diff --git a/src/glib-backports.c b/src/glib-backports.c index c7bfc1f9c8f460ac343a5e423f4c63a43cdb6984..ce4990ee15acbd95fbe213285a2e28c35370ea39 100644 --- a/src/glib-backports.c +++ b/src/glib-backports.c @@ -5,6 +5,8 @@ * Copyright 1997-2000 GLib team * Copyright 2000 Red Hat, Inc. * Copyright 2013-2019 Collabora Ltd. + * Copyright 2018 Georges Basile Stavracas Neto + * Copyright 2018 Philip Withnall * g_execvpe implementation based on GNU libc execvp: * Copyright 1991, 92, 95, 96, 97, 98, 99 Free Software Foundation, Inc. * @@ -351,3 +353,140 @@ my_g_unix_fd_add_full (int priority, return ret; } #endif + +#if !GLIB_CHECK_VERSION(2, 58, 0) +/** + * g_canonicalize_filename: + * @filename: (type filename): the name of the file + * @relative_to: (type filename) (nullable): the relative directory, or %NULL + * to use the current working directory + * + * Gets the canonical file name from @filename. All triple slashes are turned into + * single slashes, and all `..` and `.`s resolved against @relative_to. + * + * Symlinks are not followed, and the returned path is guaranteed to be absolute. + * + * If @filename is an absolute path, @relative_to is ignored. Otherwise, + * @relative_to will be prepended to @filename to make it absolute. @relative_to + * must be an absolute path, or %NULL. If @relative_to is %NULL, it'll fallback + * to g_get_current_dir(). + * + * This function never fails, and will canonicalize file paths even if they don't + * exist. + * + * No file system I/O is done. + * + * Returns: (type filename) (transfer full): a newly allocated string with the + * canonical file path + * Since: 2.58 + */ +gchar * +my_g_canonicalize_filename (const gchar *filename, + const gchar *relative_to) +{ + gchar *canon, *start, *p, *q; + guint i; + + g_return_val_if_fail (relative_to == NULL || g_path_is_absolute (relative_to), NULL); + + if (!g_path_is_absolute (filename)) + { + gchar *cwd_allocated = NULL; + const gchar *cwd; + + if (relative_to != NULL) + cwd = relative_to; + else + cwd = cwd_allocated = g_get_current_dir (); + + canon = g_build_filename (cwd, filename, NULL); + g_free (cwd_allocated); + } + else + { + canon = g_strdup (filename); + } + + start = (char *)g_path_skip_root (canon); + + if (start == NULL) + { + /* This shouldn't really happen, as g_get_current_dir() should + return an absolute pathname, but bug 573843 shows this is + not always happening */ + g_free (canon); + return g_build_filename (G_DIR_SEPARATOR_S, filename, NULL); + } + + /* POSIX allows double slashes at the start to + * mean something special (as does windows too). + * So, "//" != "/", but more than two slashes + * is treated as "/". + */ + i = 0; + for (p = start - 1; + (p >= canon) && + G_IS_DIR_SEPARATOR (*p); + p--) + i++; + if (i > 2) + { + i -= 1; + start -= i; + memmove (start, start+i, strlen (start+i) + 1); + } + + /* Make sure we're using the canonical dir separator */ + p++; + while (p < start && G_IS_DIR_SEPARATOR (*p)) + *p++ = G_DIR_SEPARATOR; + + p = start; + while (*p != 0) + { + if (p[0] == '.' && (p[1] == 0 || G_IS_DIR_SEPARATOR (p[1]))) + { + memmove (p, p+1, strlen (p+1)+1); + } + else if (p[0] == '.' && p[1] == '.' && (p[2] == 0 || G_IS_DIR_SEPARATOR (p[2]))) + { + q = p + 2; + /* Skip previous separator */ + p = p - 2; + if (p < start) + p = start; + while (p > start && !G_IS_DIR_SEPARATOR (*p)) + p--; + if (G_IS_DIR_SEPARATOR (*p)) + *p++ = G_DIR_SEPARATOR; + memmove (p, q, strlen (q)+1); + } + else + { + /* Skip until next separator */ + while (*p != 0 && !G_IS_DIR_SEPARATOR (*p)) + p++; + + if (*p != 0) + { + /* Canonicalize one separator */ + *p++ = G_DIR_SEPARATOR; + } + } + + /* Remove additional separators */ + q = p; + while (*q && G_IS_DIR_SEPARATOR (*q)) + q++; + + if (p != q) + memmove (p, q, strlen (q) + 1); + } + + /* Remove trailing slashes */ + if (p > start && G_IS_DIR_SEPARATOR (*(p-1))) + *(p-1) = 0; + + return canon; +} +#endif diff --git a/src/glib-backports.h b/src/glib-backports.h index 0e047412531ef71e032762e4e044c300657aeb10..78f7fe164f4b3566c4a3b69ee79255144234b766 100644 --- a/src/glib-backports.h +++ b/src/glib-backports.h @@ -94,3 +94,9 @@ guint my_g_unix_fd_add_full (int priority, GSource *my_g_unix_fd_source_new (int fd, GIOCondition condition); #endif + +#if !GLIB_CHECK_VERSION(2, 58, 0) +#define g_canonicalize_filename(f, r) my_g_canonicalize_filename (f, r) +gchar *my_g_canonicalize_filename (const gchar *filename, + const gchar *relative_to); +#endif