g_build_path/filename



The following patch adds what I think is a pretty
important function we missed: 
 
 gchar *g_build_filename (const gchar *first_element, ...);

And as a "free" side effect of implementation:

 gchar *g_build_path (const gchar *separator, ...);

Which is not as important but fairly useful.

The reasons for adding g_build_filename() are:

 - It gets used a lot (there are ~10 places in GTK+ that 
   use g_strconcat ( ... G_DIR_SEPARATOR_S ... ) 

 - Doing it yourself is ugly on Unix, but fatal on
   Win32, where \\ in a filename is illegal.

This is basically a replacement for g_concat_dir_and_file()
in gnome-libs, which was one of the more commonly used
convenience functions from gnome-libs.

I think it would be worth slipping this in, even at this
point ... I've discussed it with various people in the 
past, and got favorable reactions but never actually got around 
to writing anything.

Regards,
                                        Owen

(There is some missing optimization here that could be done ;
somethign of the form:

  va_start (args, separator);
  len = g_build_pathv (separator, NULL, args, NULL);
  va_end (args);

  result = g_malloc (len + 1);

  va_start (args, separator);
  len = g_build_pathv (separator, NULL, args, result);
  va_end (args);

The simple implementation here should be fine for most uses.)


Index: glib/gfileutils.c
===================================================================
RCS file: /cvs/gnome/glib/glib/gfileutils.c,v
retrieving revision 1.21
diff -u -r1.21 gfileutils.c
--- glib/gfileutils.c	2001/07/19 20:17:03	1.21
+++ glib/gfileutils.c	2001/09/08 21:32:20
@@ -28,6 +28,7 @@
 #endif
 #include <stdio.h>
 #include <stdlib.h>
+#include <stdarg.h>
 #include <string.h>
 #include <errno.h>
 #include <sys/types.h>
@@ -696,4 +697,120 @@
     g_free (fulltemplate);
 
   return retval;
+}
+
+static gchar *
+g_build_pathv (const gchar *separator,
+	       const gchar *first,
+	       va_list      args)
+{
+  GString *result;
+  gint separator_len = strlen (separator);
+  gboolean is_first = TRUE;
+  const gchar *next_element;
+
+  result = g_string_new (NULL);
+
+  if (first)
+    next_element = first;
+  else
+    next_element = va_arg (args, gchar *);
+
+  while (TRUE)
+    {
+      const gchar *element;
+      const gchar *start;
+      const gchar *end;
+
+      if (next_element)
+	{
+	  element = next_element;
+	  next_element = va_arg (args, gchar *);
+	}
+      else
+	break;
+
+      start = element;
+      
+      if (is_first)
+	is_first = FALSE;
+      else if (separator_len)
+	{
+	  while (start &&
+		 strncmp (start, separator, separator_len) == 0)
+	    start += separator_len;
+	}
+
+      end = start + strlen (start);
+      
+      if (next_element && separator_len)
+	{
+	  while (end > start + separator_len &&
+		 strncmp (end - separator_len, separator, separator_len) == 0)
+	    end -= separator_len;
+	}
+
+      if (end > start)
+	{
+	  if (result->len > 0)
+	    g_string_append (result, separator);
+
+	  g_string_append_len (result, start, end - start);
+	}
+    }
+  
+  return g_string_free (result, FALSE);
+}
+
+/**
+ * g_build_path:
+ * @separator: a string used to separator the elements of the path.
+ * 
+ * Create a path from a series of elements using @separator as the
+ * separator between elements. At the boundary between two elements,
+ * any trailing occurrences of separator in the first element, or
+ * leading occurrences of separator in the second element are removed
+ * and exactly one copy of the separator is inserted.
+ * 
+ * Return value: a newly allocated string that must be freed with g_free().
+ **/
+gchar *
+g_build_path (const gchar *separator, ...)
+{
+  gchar *str;
+  va_list args;
+
+  g_return_val_if_fail (separator != NULL, NULL);
+
+  va_start (args, separator);
+  str = g_build_pathv (separator, NULL, args);
+  va_end (args);
+
+  return str;
+}
+
+/**
+ * g_build_filename:
+ * @first_element: the first element in the path
+ * 
+ * Create a filename from a series of elements using the correct
+ * separator for filenames. This function behaves identically
+ * to g_build_path (G_DIR_SEPARATOR_S, first_element, ....)
+ * 
+ * Return value: a newly allocated string that must be freed with g_free().
+ **/
+gchar *
+g_build_filename (const gchar *first_element, ...)
+{
+  gchar *str;
+  va_list args;
+
+  if (first_element == NULL)
+    return g_strdup ("");
+  
+  va_start (args, first_element);
+  str = g_build_pathv (G_DIR_SEPARATOR_S, first_element, args);
+  va_end (args);
+
+  return str;
 }
Index: glib/gfileutils.h
===================================================================
RCS file: /cvs/gnome/glib/glib/gfileutils.h,v
retrieving revision 1.8
diff -u -r1.8 gfileutils.h
--- glib/gfileutils.h	2001/06/26 16:01:14	1.8
+++ glib/gfileutils.h	2001/09/08 21:32:20
@@ -88,6 +88,10 @@
 			      char        **name_used,
 			      GError      **error);
 
+gchar *g_build_path (const gchar *separator, ...);
+gchar *g_build_filename (const gchar *first_element, ...);
+
+
 G_END_DECLS
 
 #endif /* __G_FILEUTILS_H__ */
Index: tests/strfunc-test.c
===================================================================
RCS file: /cvs/gnome/glib/tests/strfunc-test.c,v
retrieving revision 1.12
diff -u -r1.12 strfunc-test.c
--- tests/strfunc-test.c	2001/08/25 22:29:40	1.12
+++ tests/strfunc-test.c	2001/09/08 21:32:20
@@ -81,6 +81,17 @@
 }
 
 static gboolean
+str_check (gchar *str,
+	   gchar *expected)
+{
+  gboolean ok = (strcmp (str, expected) == 0);
+
+  g_free (str);
+
+  return ok;
+}
+
+static gboolean
 test_isalnum (gchar c)
 {
   return g_ascii_isalnum (c);
@@ -353,6 +364,59 @@
   TEST_DIGIT (xdigit);
 
   #undef TEST_DIGIT
+
+  /* Tests for g_build_path, g_build_filename */
+
+  TEST (NULL, str_check (g_build_path ("", NULL), ""));
+  TEST (NULL, str_check (g_build_path ("", "", NULL), ""));
+  TEST (NULL, str_check (g_build_path ("", "x", NULL), "x"));
+  TEST (NULL, str_check (g_build_path ("", "x", "y",  NULL), "xy"));
+  TEST (NULL, str_check (g_build_path ("", "x", "y", "z", NULL), "xyz"));
+
+  TEST (NULL, str_check (g_build_path (":", NULL), ""));
+  TEST (NULL, str_check (g_build_path (":", ":", NULL), ":"));
+  TEST (NULL, str_check (g_build_path (":", ":x", NULL), ":x"));
+  TEST (NULL, str_check (g_build_path (":", "x:", NULL), "x:"));
+  TEST (NULL, str_check (g_build_path (":", "x", "y",  NULL), "x:y"));
+  TEST (NULL, str_check (g_build_path (":", ":x", "y", NULL), ":x:y"));
+  TEST (NULL, str_check (g_build_path (":", "x", "y:", NULL), "x:y:"));
+  TEST (NULL, str_check (g_build_path (":", ":x:", ":y:", NULL), ":x:y:"));
+  TEST (NULL, str_check (g_build_path (":", ":x::", "::y:", NULL), ":x:y:"));
+  TEST (NULL, str_check (g_build_path (":", "x", "y", "z", NULL), "x:y:z"));
+  TEST (NULL, str_check (g_build_path (":", ":x:", ":y:", ":z:", NULL), ":x:y:z:"));
+  TEST (NULL, str_check (g_build_path (":", "::x::", "::y::", "::z::", NULL), "::x:y:z::"));
+
+  TEST (NULL, str_check (g_build_path ("::", NULL), ""));
+  TEST (NULL, str_check (g_build_path ("::", "::", NULL), "::"));
+  TEST (NULL, str_check (g_build_path ("::", "::x", NULL), "::x"));
+  TEST (NULL, str_check (g_build_path ("::", "x::", NULL), "x::"));
+  TEST (NULL, str_check (g_build_path ("::", "x", "y",  NULL), "x::y"));
+  TEST (NULL, str_check (g_build_path ("::", "::x", "y", NULL), "::x::y"));
+  TEST (NULL, str_check (g_build_path ("::", "x", "y::", NULL), "x::y::"));
+  TEST (NULL, str_check (g_build_path ("::", "::x::", "::y::", NULL), "::x::y::"));
+  TEST (NULL, str_check (g_build_path ("::", "::x:::", ":::y::", NULL), "::x::::y::"));
+  TEST (NULL, str_check (g_build_path ("::", "::x::::", "::::y::", NULL), "::x::y::"));
+  TEST (NULL, str_check (g_build_path ("::", "x", "y", "z", NULL), "x::y::z"));
+  TEST (NULL, str_check (g_build_path ("::", "::x::", "::y::", "::z::", NULL), "::x::y::z::"));
+  TEST (NULL, str_check (g_build_path ("::", ":::x:::", ":::y:::", ":::z:::", NULL), ":::x::::y::::z:::"));
+  TEST (NULL, str_check (g_build_path ("::", "::::x::::", "::::y::::", "::::z::::", NULL), "::::x::y::z::::"));
+
+#define S G_DIR_SEPARATOR_S
+
+  TEST (NULL, str_check (g_build_filename (NULL), ""));
+  TEST (NULL, str_check (g_build_filename (S, NULL), S));
+  TEST (NULL, str_check (g_build_filename (S"x", NULL), S"x"));
+  TEST (NULL, str_check (g_build_filename ("x"S, NULL), "x"S));
+  TEST (NULL, str_check (g_build_filename ("x", "y",  NULL), "x"S"y"));
+  TEST (NULL, str_check (g_build_filename (S"x", "y", NULL), S"x"S"y"));
+  TEST (NULL, str_check (g_build_filename ("x", "y"S, NULL), "x"S"y"S));
+  TEST (NULL, str_check (g_build_filename (S"x"S, S"y"S, NULL), S"x"S"y"S));
+  TEST (NULL, str_check (g_build_filename (S"x"S S, S S"y"S, NULL), S"x"S"y"S));
+  TEST (NULL, str_check (g_build_filename ("x", "y", "z", NULL), "x"S"y"S"z"));
+  TEST (NULL, str_check (g_build_filename (S"x"S, S"y"S, S"z"S, NULL), S"x"S"y"S"z"S));
+  TEST (NULL, str_check (g_build_filename (S S"x"S S, S S"y"S S, S S"z"S S, NULL), S S"x"S"y"S"z"S S));
+
+#undef S
 
   return any_failed;
 }




[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]