From 6cfb307c475231441891c27e85d7374b1d416d69 Mon Sep 17 00:00:00 2001
From: Simon McVittie <smcv@collabora.com>
Date: Mon, 22 Jun 2020 17:21:56 +0100
Subject: [PATCH] glib-backports: Add an implementation of
 g_canonicalize_filename()

Signed-off-by: Simon McVittie <smcv@collabora.com>
---
 src/glib-backports.c | 139 +++++++++++++++++++++++++++++++++++++++++++
 src/glib-backports.h |   6 ++
 2 files changed, 145 insertions(+)

diff --git a/src/glib-backports.c b/src/glib-backports.c
index c7bfc1f9c..ce4990ee1 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 0e0474125..78f7fe164 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
-- 
GitLab