summaryrefslogtreecommitdiff
path: root/gio/gresourcefile.c
diff options
context:
space:
mode:
Diffstat (limited to 'gio/gresourcefile.c')
-rw-r--r--gio/gresourcefile.c125
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 *