Code

7cc69f89df3b90a7502e9610577db60b14e5c6c3
[inkscape.git] / src / dir-util.cpp
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);
182 char *
183 inkscape_abs2rel (const char *path, const char *base, char *result, const size_t size)
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);
253 void
254 prepend_current_dir_if_relative (char **result, const gchar *uri)
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);