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