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);
182 }
184 char *
185 inkscape_abs2rel (const char *path, const char *base, char *result, const size_t size)
186 {
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);
253 }
255 void
256 prepend_current_dir_if_relative (char **result, const gchar *uri)
257 {
258 if (!uri) {
259 *(result) = NULL;
260 return;
261 }
263 char *full_path = (char *) g_malloc (1001);
264 char *cwd = g_get_current_dir();
266 gsize bytesRead = 0;
267 gsize bytesWritten = 0;
268 GError* error = NULL;
269 gchar* cwd_utf8 = g_filename_to_utf8 ( cwd,
270 -1,
271 &bytesRead,
272 &bytesWritten,
273 &error);
275 inkscape_rel2abs (uri, cwd_utf8, full_path, 1000);
276 *(result) = g_strdup (full_path);
277 g_free (full_path);
278 g_free (cwd);
279 }