uri: add gst_filename_to_uri() that takes relative filenames
authorTim-Philipp Müller <tim.muller@collabora.co.uk>
Thu, 24 Feb 2011 15:18:43 +0000 (15:18 +0000)
committerTim-Philipp Müller <tim.muller@collabora.co.uk>
Thu, 24 Feb 2011 15:36:52 +0000 (15:36 +0000)
Add function that (unlike the GLib equivalent) also accepts paths that
aren't absolute and will clean up relative markers such as ./ and ../
before forming a URI.

Fixes warnings with e.g. filesrc location=foo ! typefind caused by the
recent switch to g_filename_to_uri(), but also actually creates valid
URIs for the first time.

Windows code paths could need some more work, e.g. we don't clean up
the relative markers there for now (because path could have \ and /
as separators).

API: gst_filename_to_uri()

docs/gst/gstreamer-sections.txt
gst/gsturi.c
gst/gsturi.h
win32/common/libgstreamer.def

index 4787245..3a87827 100644 (file)
@@ -2569,6 +2569,7 @@ gst_uri_has_protocol
 gst_uri_get_protocol
 gst_uri_get_location
 gst_uri_construct
+gst_filename_to_uri
 gst_element_make_from_uri
 gst_uri_handler_get_uri_type
 gst_uri_handler_get_protocols
index 247604c..76a1f87 100644 (file)
@@ -1,6 +1,7 @@
 /* GStreamer
  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
  *                    2000 Wim Taymans <wtay@chello.be>
+ * Copyright (C) 2011 Tim-Philipp Müller <tim centricular net>
  *
  * gsturi.c: register URI handlers
  *
@@ -782,3 +783,113 @@ gst_uri_handler_new_uri (GstURIHandler * handler, const gchar * uri)
 
   g_signal_emit (handler, gst_uri_handler_signals[NEW_URI], 0, uri);
 }
+
+static gchar *
+gst_file_utils_canonicalise_path (const gchar * path)
+{
+  gchar **parts, **p, *clean_path;
+
+  parts = g_strsplit (path, "/", -1);
+
+  p = parts;
+  while (*p != NULL) {
+    if (strcmp (*p, ".") == 0) {
+      /* just move all following parts on top of this, incl. NUL terminator */
+      g_free (*p);
+      g_memmove (p, p + 1, (g_strv_length (p + 1) + 1) * sizeof (gchar *));
+      /* re-check the new current part again in the next iteration */
+      continue;
+    } else if (strcmp (*p, "..") == 0 && p > parts) {
+      /* just move all following parts on top of the previous part, incl.
+       * NUL terminator */
+      g_free (*(p - 1));
+      g_free (*p);
+      g_memmove (p - 1, p + 1, (g_strv_length (p + 1) + 1) * sizeof (gchar *));
+      /* re-check the new current part again in the next iteration */
+      --p;
+      continue;
+    }
+    ++p;
+  }
+  if (*path == '/') {
+    guint num_parts;
+
+    num_parts = g_strv_length (parts) + 1;      /* incl. terminator */
+    parts = g_renew (gchar *, parts, num_parts + 1);
+    g_memmove (parts + 1, parts, num_parts * sizeof (gchar *));
+    parts[0] = g_strdup ("/");
+  }
+
+  clean_path = g_build_filenamev (parts);
+  g_strfreev (parts);
+  return clean_path;
+}
+
+static gboolean
+file_path_contains_relatives (const gchar * path)
+{
+  return (strstr (path, "/./") != NULL || strstr (path, "/../") != NULL ||
+      strstr (path, G_DIR_SEPARATOR_S "." G_DIR_SEPARATOR_S) != NULL ||
+      strstr (path, G_DIR_SEPARATOR_S ".." G_DIR_SEPARATOR_S) != NULL);
+}
+
+/**
+ * gst_filename_to_uri:
+ * @filename: absolute or relative file name path
+ * @error: pointer to error, or NULL
+ *
+ * Similar to g_filename_to_uri(), but attempts to handle relative file paths
+ * as well. Before converting @filename into an URI, it will be prefixed by
+ * the current working directory if it is a relative path, and then the path
+ * will be canonicalised so that it doesn't contain any './' or '../' segments.
+ *
+ * On Windows #filename should be in UTF-8 encoding.
+ *
+ * Since: 0.10.33
+ */
+gchar *
+gst_filename_to_uri (const gchar * filename, GError ** error)
+{
+  gchar *abs_location = NULL;
+  gchar *uri, *abs_clean;
+
+  g_return_val_if_fail (filename != NULL, NULL);
+  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+  if (g_path_is_absolute (filename)) {
+    if (!file_path_contains_relatives (filename)) {
+      uri = g_filename_to_uri (filename, NULL, error);
+      goto beach;
+    }
+
+    abs_location = g_strdup (filename);
+  } else {
+    gchar *cwd;
+
+    cwd = g_get_current_dir ();
+    abs_location = g_build_filename (cwd, filename, NULL);
+    g_free (cwd);
+
+    if (!file_path_contains_relatives (abs_location)) {
+      uri = g_filename_to_uri (abs_location, NULL, error);
+      goto beach;
+    }
+  }
+
+  /* path is now absolute, but contains '.' or '..' */
+#ifndef G_OS_WIN32
+  abs_clean = gst_file_utils_canonicalise_path (abs_location);
+  GST_LOG ("'%s' -> '%s' -> '%s'", filename, abs_location, abs_clean);
+  uri = g_filename_to_uri (abs_clean, NULL, error);
+  g_free (abs_clean);
+#else
+  GST_WARNING ("FIXME: canonicalise win32 path");
+  uri = g_filename_to_uri (abs_location, NULL, error);
+#endif
+
+beach:
+
+  g_free (abs_location);
+  GST_DEBUG ("'%s' -> '%s'", filename, uri);
+  return uri;
+}
index 6193cf3..48a09c0 100644 (file)
@@ -133,6 +133,9 @@ gchar *             gst_uri_get_location            (const gchar * uri);
 gchar *                gst_uri_construct               (const gchar * protocol,
                                                 const gchar * location);
 
+gchar *         gst_filename_to_uri             (const gchar * filename,
+                                                 GError     ** error);
+
 GstElement *   gst_element_make_from_uri       (const GstURIType type,
                                                 const gchar *    uri,
                                                 const gchar *    elementname);
index 1e444b9..aae5e2e 100644 (file)
@@ -420,6 +420,7 @@ EXPORTS
        gst_event_type_get_name
        gst_event_type_get_type
        gst_event_type_to_quark
+       gst_filename_to_uri
        gst_filter_run
        gst_flow_get_name
        gst_flow_return_get_type