Code

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