1 /** \file Some utility functions for filenames. */
3 #define DIR_UTIL_C
5 #include <errno.h>
6 #include <string>
7 #include <cstring>
8 #include <glib/gutils.h>
9 #include <glib/gmem.h>
10 #include <glib/gerror.h>
11 #include <glib/gconvert.h>
12 #include <glib/gstrfuncs.h>
14 /** Returns a form of \a path relative to \a base if that is easy to construct (e.g. if \a path
15 appears to be in the directory specified by \a base), otherwise returns \a path.
17 N.B. The return value is a pointer into the \a path string.
19 \a base is expected to be either NULL or the absolute path of a directory.
21 \a path is expected to be an absolute path.
23 \see inkscape_abs2rel for a more sophisticated version.
24 \see prepend_current_dir_if_relative.
25 */
26 char const *
27 sp_relative_path_from_path(char const *const path, char const *const base)
28 {
29 if (base == NULL || path == NULL) {
30 return path;
31 }
33 size_t base_len = strlen(base);
34 while (base_len != 0
35 && (base[base_len - 1] == G_DIR_SEPARATOR))
36 {
37 --base_len;
38 }
40 if ((memcmp(path, base, base_len) == 0)
41 && (path[base_len] == G_DIR_SEPARATOR))
42 {
43 char const *ret = path + base_len + 1;
44 while (*ret == G_DIR_SEPARATOR) {
45 ++ret;
46 }
47 if (*ret != '\0') {
48 return ret;
49 }
50 }
52 return path;
53 }
55 char const *
56 sp_extension_from_path(char const *const path)
57 {
58 if (path == NULL) {
59 return NULL;
60 }
62 char const *p = path;
63 while (*p != '\0') p++;
65 while ((p >= path) && (*p != G_DIR_SEPARATOR) && (*p != '.')) p--;
66 if (* p != '.') return NULL;
67 p++;
69 return p;
70 }
73 /* current == "./", parent == "../" */
74 static char const dots[] = {'.', '.', G_DIR_SEPARATOR, '\0'};
75 static char const *const parent = dots;
76 static char const *const current = dots + 1;
78 /**
79 * \brief Convert a relative path name into absolute. If path is already absolute, does nothing except copying path to result.
80 *
81 * \param path relative path
82 * \param base base directory (must be absolute path)
83 * \param result result buffer
84 * \param size size of result buffer
85 * \return != NULL: absolute path
86 * == NULL: error
88 \comment
89 based on functions by Shigio Yamaguchi.
90 FIXME:TODO: force it to also do path normalization of the entire resulting path,
91 i.e. get rid of any .. and . in any place, even if 'path' is already absolute
92 (now it returns it unchanged in this case)
94 */
95 char *
96 inkscape_rel2abs (const char *path, const char *base, char *result, const size_t size)
97 {
98 const char *pp, *bp;
99 /* endp points the last position which is safe in the result buffer. */
100 const char *endp = result + size - 1;
101 char *rp;
102 int length;
103 if (*path == G_DIR_SEPARATOR)
104 {
105 if (strlen (path) >= size)
106 goto erange;
107 strcpy (result, path);
108 goto finish;
109 }
110 else if (*base != G_DIR_SEPARATOR || !size)
111 {
112 errno = EINVAL;
113 return (NULL);
114 }
115 else if (size == 1)
116 goto erange;
117 if (!strcmp (path, ".") || !strcmp (path, current))
118 {
119 if (strlen (base) >= size)
120 goto erange;
121 strcpy (result, base);
122 /* rp points the last char. */
123 rp = result + strlen (base) - 1;
124 if (*rp == G_DIR_SEPARATOR)
125 *rp = 0;
126 else
127 rp++;
128 /* rp point NULL char */
129 if (*++path == G_DIR_SEPARATOR)
130 {
131 /* Append G_DIR_SEPARATOR to the tail of path name. */
132 *rp++ = G_DIR_SEPARATOR;
133 if (rp > endp)
134 goto erange;
135 *rp = 0;
136 }
137 goto finish;
138 }
139 bp = base + strlen (base);
140 if (*(bp - 1) == G_DIR_SEPARATOR)
141 --bp;
142 /* up to root. */
143 for (pp = path; *pp && *pp == '.';)
144 {
145 if (!strncmp (pp, parent, 3))
146 {
147 pp += 3;
148 while (bp > base && *--bp != G_DIR_SEPARATOR)
149 ;
150 }
151 else if (!strncmp (pp, current, 2))
152 {
153 pp += 2;
154 }
155 else if (!strncmp (pp, "..\0", 3))
156 {
157 pp += 2;
158 while (bp > base && *--bp != G_DIR_SEPARATOR)
159 ;
160 }
161 else
162 break;
163 }
164 /* down to leaf. */
165 length = bp - base;
166 if (length >= static_cast<int>(size))
167 goto erange;
168 strncpy (result, base, length);
169 rp = result + length;
170 if (*pp || *(pp - 1) == G_DIR_SEPARATOR || length == 0)
171 *rp++ = G_DIR_SEPARATOR;
172 if (rp + strlen (pp) > endp)
173 goto erange;
174 strcpy (rp, pp);
175 finish:
176 return result;
177 erange:
178 errno = ERANGE;
179 return (NULL);
180 }
182 char *
183 inkscape_abs2rel (const char *path, const char *base, char *result, const size_t size)
184 {
185 const char *pp, *bp, *branch;
186 /* endp points the last position which is safe in the result buffer. */
187 const char *endp = result + size - 1;
188 char *rp;
190 if (*path != G_DIR_SEPARATOR)
191 {
192 if (strlen (path) >= size)
193 goto erange;
194 strcpy (result, path);
195 goto finish;
196 }
197 else if (*base != G_DIR_SEPARATOR || !size)
198 {
199 errno = EINVAL;
200 return (NULL);
201 }
202 else if (size == 1)
203 goto erange;
204 /* seek to branched point. */
205 branch = path;
206 for (pp = path, bp = base; *pp && *bp && *pp == *bp; pp++, bp++)
207 if (*pp == G_DIR_SEPARATOR)
208 branch = pp;
209 if ((*pp == 0 || *pp == G_DIR_SEPARATOR && *(pp + 1) == 0) &&
210 (*bp == 0 || *bp == G_DIR_SEPARATOR && *(bp + 1) == 0))
211 {
212 rp = result;
213 *rp++ = '.';
214 if (*pp == G_DIR_SEPARATOR || *(pp - 1) == G_DIR_SEPARATOR)
215 *rp++ = G_DIR_SEPARATOR;
216 if (rp > endp)
217 goto erange;
218 *rp = 0;
219 goto finish;
220 }
221 if (*pp == 0 && *bp == G_DIR_SEPARATOR || *pp == G_DIR_SEPARATOR && *bp == 0)
222 branch = pp;
223 /* up to root. */
224 rp = result;
225 for (bp = base + (branch - path); *bp; bp++)
226 if (*bp == G_DIR_SEPARATOR && *(bp + 1) != 0)
227 {
228 if (rp + 3 > endp)
229 goto erange;
230 *rp++ = '.';
231 *rp++ = '.';
232 *rp++ = G_DIR_SEPARATOR;
233 }
234 if (rp > endp)
235 goto erange;
236 *rp = 0;
237 /* down to leaf. */
238 if (*branch)
239 {
240 if (rp + strlen (branch + 1) > endp)
241 goto erange;
242 strcpy (rp, branch + 1);
243 }
244 else
245 *--rp = 0;
246 finish:
247 return result;
248 erange:
249 errno = ERANGE;
250 return (NULL);
251 }
253 void
254 prepend_current_dir_if_relative (char **result, const gchar *uri)
255 {
256 if (!uri) {
257 *(result) = NULL;
258 return;
259 }
261 char *full_path = (char *) g_malloc (1001);
262 char *cwd = g_get_current_dir();
264 gsize bytesRead = 0;
265 gsize bytesWritten = 0;
266 GError* error = NULL;
267 gchar* cwd_utf8 = g_filename_to_utf8 ( cwd,
268 -1,
269 &bytesRead,
270 &bytesWritten,
271 &error);
273 inkscape_rel2abs (uri, cwd_utf8, full_path, 1000);
274 *(result) = g_strdup (full_path);
275 g_free (full_path);
276 g_free (cwd);
277 }