diff options
Diffstat (limited to 'gio/gresourcefile.c')
-rw-r--r-- | gio/gresourcefile.c | 125 |
1 files changed, 74 insertions, 51 deletions
diff --git a/gio/gresourcefile.c b/gio/gresourcefile.c index 739c6e2f1..429e9ef49 100644 --- a/gio/gresourcefile.c +++ b/gio/gresourcefile.c @@ -138,69 +138,92 @@ g_resource_file_init (GResourceFile *resource) { } -static char * -canonicalize_filename (const char *filename) +static inline gchar * +scan_backwards (const gchar *begin, + const gchar *end, + gchar c) { - char *canon, *start, *p, *q; + while (end >= begin) + { + if (*end == c) + return (gchar *)end; + end--; + } - /* Skip multiple inital slashes */ - while (filename[0] == '/' && filename[1] == '/') - filename++; + return NULL; +} - if (*filename != '/') - canon = g_strconcat ("/", filename, NULL); - else - canon = g_strdup (filename); +static inline void +pop_to_previous_part (const gchar *begin, + gchar **out) +{ + if (*out > begin) + *out = scan_backwards (begin, *out - 1, '/'); +} + +/* + * canonicalize_filename: + * @in: the path to be canonicalized + * + * The path @in may contain non-canonical path pieces such as "../" + * or duplicated "/". This will resolve those into a form that only + * contains a single / at a time and resolves all "../". The resulting + * path must also start with a /. + * + * Returns: the canonical form of the path + */ +static char * +canonicalize_filename (const char *in) +{ + gchar *bptr; + char *out; - start = canon + 1; + bptr = out = g_malloc (strlen (in) + 2); + *out = '/'; - p = start; - while (*p != 0) + while (*in != 0) { - if (p[0] == '.' && (p[1] == 0 || p[1] == '/')) - { - memmove (p, p+1, strlen (p+1)+1); - } - else if (p[0] == '.' && p[1] == '.' && (p[2] == 0 || p[2] == '/')) - { - q = p + 2; - /* Skip previous separator */ - p = p - 2; - if (p < start) - p = start; - while (p > start && *p != '/') - p--; - if (*p == '/') - *p++ = '/'; - memmove (p, q, strlen (q)+1); - } - else - { - /* Skip until next separator */ - while (*p != 0 && *p != '/') - p++; + g_assert (*out == '/'); - if (*p != 0) - { - /* Canonicalize one separator */ - *p++ = '/'; - } - } + /* move past slashes */ + while (*in == '/') + in++; + + /* Handle ./ ../ .\0 ..\0 */ + if (*in == '.') + { + /* If this is ../ or ..\0 move up */ + if (in[1] == '.' && (in[2] == '/' || in[2] == 0)) + { + pop_to_previous_part (bptr, &out); + in += 2; + continue; + } + + /* If this is ./ skip past it */ + if (in[1] == '/' || in[1] == 0) + { + in += 1; + continue; + } + } - /* Remove additional separators */ - q = p; - while (*q && *q == '/') - q++; + /* Scan to the next path piece */ + while (*in != 0 && *in != '/') + *(++out) = *(in++); - if (p != q) - memmove (p, q, strlen (q)+1); + /* Add trailing /, compress the rest on the next go round. */ + if (*in == '/') + *(++out) = *(in++); } - /* Remove trailing slashes */ - if (p > start && *(p-1) == '/') - *(p-1) = 0; + /* Trim trailing / from path */ + if (out > bptr && *out == '/') + *out = 0; + else + *(++out) = 0; - return canon; + return bptr; } static GFile * |