Add support for a number of special directories, as defined by the
authorMatthias Clasen <mclasen@redhat.com>
Mon, 4 Jun 2007 14:54:49 +0000 (14:54 +0000)
committerMatthias Clasen <matthiasc@src.gnome.org>
Mon, 4 Jun 2007 14:54:49 +0000 (14:54 +0000)
2007-06-04  Matthias Clasen  <mclasen@redhat.com>

        Add support for a number of special directories, as
        defined by the xdg-user-dirs specification.  (#432651,
        Bastien Nocera, Emmanuele Bassi, Michael Natterer)

        * glib/glib.symbols:
        * glib/gutils.[hc]: Add the GUserDirectory enum and
        g_get_user_special_dir(), with implementations based
        on the xdg-user-dirs spec and on native interfaces
        for Win32 and Carbon.

        * configure.in: Add Carbon checks.

        * tests/tetsglib.c: Test g_get_user_special_dir().

svn path=/trunk/; revision=5528

ChangeLog
configure.in
docs/reference/ChangeLog
docs/reference/glib/glib-sections.txt
docs/reference/glib/tmpl/misc_utils.sgml
glib/glib.symbols
glib/gutils.c
glib/gutils.h
tests/testglib.c

index 78c356d7c1c3d4548fa024bbd9f5f489b73bc448..b1893ceae625b154d66e571fb43685836f281e9b 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,19 @@
+2007-06-04  Matthias Clasen  <mclasen@redhat.com>
+       
+       Add support for a number of special directories, as 
+       defined by the xdg-user-dirs specification.  (#432651,
+       Bastien Nocera, Emmanuele Bassi, Michael Natterer)
+
+       * glib/glib.symbols:
+       * glib/gutils.[hc]: Add the GUserDirectory enum and
+       g_get_user_special_dir(), with implementations based
+       on the xdg-user-dirs spec and on native interfaces
+       for Win32 and Carbon.
+       
+       * configure.in: Add Carbon checks.
+
+       * tests/tetsglib.c: Test g_get_user_special_dir().
+       
 2007-06-03  Yevgen Muntyan  <muntyan@tamu.edu>
 
        * glib/gregex.c: fixed g_regex_fetch_named* for cases when (?J)
index 68b45dce37eb4af9483e05be1769093a387d2c7f..bcdfa1047ab407d1a378ec3080b6090567ef1a1c 100644 (file)
@@ -145,11 +145,21 @@ case $host in
     ;;
 esac
 
-
 AC_MSG_RESULT([$glib_native_win32])
+
+glib_have_carbon=no
+AC_MSG_CHECKING([for Mac OS X Carbon support])
+AC_TRY_CPP([
+#include <Carbon/Carbon.h>
+#include <CoreServices/CoreServices.h>
+], glib_have_carbon=yes)
+
+AC_MSG_RESULT([$glib_have_carbon])
+
 AM_CONDITIONAL(OS_WIN32, [test "$glib_native_win32" = "yes"])
 AM_CONDITIONAL(OS_UNIX, [test "$glib_native_win32" != "yes"])
 AM_CONDITIONAL(OS_LINUX, [test "$glib_os_linux" = "yes"])
+AM_CONDITIONAL(OS_CARBON, [test "$glib_have_carbon" = "yes"])
 AC_SUBST(GLIB_DEF)
 AC_SUBST(GMODULE_DEF)
 AC_SUBST(GOBJECT_DEF)
@@ -180,6 +190,12 @@ else
 fi
 AC_SUBST(LIBTOOL_EXPORT_OPTIONS)
 
+if test "x$glib_have_carbon" = "xyes"; then
+  AC_DEFINE(HAVE_CARBON, 1, [define to 1 if Carbon is available])
+  LDFLAGS="$LDFLAGS -framework Carbon"
+fi
+
+
 dnl declare --enable-* args and collect ac_help strings
 AC_ARG_ENABLE(debug,
               AC_HELP_STRING([--enable-debug=@<:@no/minimum/yes@:>@],
index 58b8c400576c6b04cb611f06c818a54068684813..da22463b0f8ed2bd8633230ec2c6c2925f6ea4ee 100644 (file)
@@ -1,3 +1,9 @@
+2007-06-04  Matthias Clasen  <mclasen@redhat.com>
+
+       * glib/glib-sections.txt:
+       * glib/tmpl/misc_utils.sgml: Add g_get_special_user_dir()
+       and GUserDirectory.
+
 2007-05-30  Matthias Clasen  <mclasen@redhat.com>
 
        * glib/tmpl/i18n.sgml: Add some hints about
index 04a5542f50b3213a3217d34ec2c02a3574b255da..2f50e2de8e932e0c7aac7c6d1bc200d2efc77b0c 100644 (file)
@@ -1388,6 +1388,8 @@ g_get_real_name
 g_get_user_cache_dir
 g_get_user_data_dir
 g_get_user_config_dir
+GUserDirectory
+g_get_user_special_dir
 g_get_system_data_dirs
 g_get_system_config_dirs
 
index 7fc5f9839e3e5ddacabbd5c3057b539f2c5930ef..f0f3223898cbf63e22d68384b28f73596f4baab6 100644 (file)
@@ -125,6 +125,30 @@ These are portable utility functions.
 @Returns: 
 
 
+<!-- ##### ENUM GUserDirectory ##### -->
+<para>
+
+</para>
+
+@G_USER_DIRECTORY_DESKTOP: 
+@G_USER_DIRECTORY_DOCUMENTS: 
+@G_USER_DIRECTORY_DOWNLOAD: 
+@G_USER_DIRECTORY_MUSIC: 
+@G_USER_DIRECTORY_PICTURES: 
+@G_USER_DIRECTORY_PUBLIC_SHARE: 
+@G_USER_DIRECTORY_TEMPLATES: 
+@G_USER_DIRECTORY_VIDEOS: 
+@G_USER_N_DIRECTORIES: 
+
+<!-- ##### FUNCTION g_get_user_special_dir ##### -->
+<para>
+
+</para>
+
+@directory: 
+@Returns: 
+
+
 <!-- ##### FUNCTION g_get_system_data_dirs ##### -->
 <para>
 
index 9725f20ce0220b43fd3f3da4e2e38f966d7625e1..a924b6ddd61849500aeeb9f5af7ab60099d4408d 100644 (file)
@@ -1397,6 +1397,7 @@ g_get_tmp_dir_utf8
 g_get_user_cache_dir
 g_get_user_config_dir
 g_get_user_data_dir
+g_get_user_special_dir
 g_get_user_name PRIVATE
 #ifdef G_OS_WIN32
 g_get_user_name_utf8
index a91abb8a2c426a7f83e87f61e6f8cdd426572564..89413494c8422fc2b61aedfa051293972cdc0cc5 100644 (file)
@@ -40,6 +40,8 @@
 #include <string.h>
 #include <ctype.h>             /* For tolower() */
 #include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
 #ifdef HAVE_PWD_H
 #include <pwd.h>
 #endif
 #  include <process.h>
 #endif
 
+#ifdef HAVE_CARBON
+#include <CoreServices/CoreServices.h>
+#endif
+
 #ifdef HAVE_CODESET
 #include <langinfo.h>
 #endif
@@ -1387,6 +1393,13 @@ static  gchar   *g_user_cache_dir = NULL;
 static  gchar   *g_user_config_dir = NULL;
 static  gchar  **g_system_config_dirs = NULL;
 
+static  gchar  **g_user_special_dirs = NULL;
+static  time_t   g_user_special_dirs_mtime = (time_t) -1;
+static  time_t   g_user_special_dirs_stat_time = (time_t) -1;
+
+/* fifteen minutes of fame for everybody */
+#define G_USER_DIRS_EXPIRE      15 * 60
+
 #ifdef G_OS_WIN32
 
 static gchar *
@@ -2095,6 +2108,330 @@ g_get_user_cache_dir (void)
   return cache_dir;
 }
 
+#ifdef HAVE_CARBON
+
+static gchar *
+find_folder (OSType type)
+{
+  gchar *filename = NULL;
+  FSRef  found;
+
+  if (FSFindFolder (kUserDomain, type, kDontCreateFolder, &found) == noErr)
+    {
+      CFURLRef url = CFURLCreateFromFSRef (kCFAllocatorSystemDefault, &found);
+
+      if (url)
+       {
+         CFStringRef path = CFURLCopyFileSystemPath (url, kCFURLPOSIXPathStyle);
+
+         if (path)
+           {
+             filename = g_strdup (CFStringGetCStringPtr (path, kCFStringEncodingUTF8));
+
+             if (! filename)
+               {
+                 filename = g_new0 (gchar, CFStringGetLength (path) * 3 + 1);
+
+                 CFStringGetCString (path, filename,
+                                     CFStringGetLength (path) * 3 + 1,
+                                     kCFStringEncodingUTF8);
+               }
+
+             CFRelease (path);
+           }
+
+         CFRelease (url);
+       }
+    }
+
+  return filename;
+}
+
+static void
+load_user_special_dirs (void)
+{
+  g_user_special_dirs[G_USER_DIRECTORY_DESKTOP] = find_folder (kDesktopFolderType);
+  g_user_special_dirs[G_USER_DIRECTORY_DOCUMENTS] = find_folder (kDocumentsFolderType);
+  g_user_special_dirs[G_USER_DIRECTORY_DOWNLOAD] = find_folder (kDesktopFolderType); /* XXX correct ? */
+  g_user_special_dirs[G_USER_DIRECTORY_MUSIC] = find_folder (kMusicDocumentsFolderType);
+  g_user_special_dirs[G_USER_DIRECTORY_PICTURES] = find_folder (kPictureDocumentsFolderType);
+  g_user_special_dirs[G_USER_DIRECTORY_PUBLIC_SHARE] = NULL;
+  g_user_special_dirs[G_USER_DIRECTORY_TEMPLATES] = NULL;
+  g_user_special_dirs[G_USER_DIRECTORY_VIDEOS] = find_folder (kMovieDocumentsFolderType);
+}
+
+#endif /* HAVE_CARBON */
+
+#if defined(G_OS_WIN32)
+static void
+load_user_special_dirs (void)
+{
+  g_user_special_dirs[G_USER_DIRECTORY_DESKTOP] = get_special_folder (CSIDL_DESKTOPDIRECTORY);
+  g_user_special_dirs[G_USER_DIRECTORY_DOCUMENTS] = get_special_folder (CSIDL_MYDOCUMENTS);
+  g_user_special_dirs[G_USER_DIRECTORY_DOWNLOAD] = get_special_folder (CSIDL_DESKTOPDIRECTORY); /* XXX correct ? */
+  g_user_special_dirs[G_USER_DIRECTORY_MUSIC] = get_special_folder (CSIDL_MYMUSIC);
+  g_user_special_dirs[G_USER_DIRECTORY_PICTURES] = get_special_folder (CSIDL_MYPICTURES);
+  g_user_special_dirs[G_USER_DIRECTORY_PUBLIC_SHARE] = get_special_folder (CSIDL_COMMON_DOCUMENTS);  /* XXX correct ? */
+  g_user_special_dirs[G_USER_DIRECTORY_TEMPLATES] = get_special_folder (CSIDL_TEMPLATES);
+  g_user_special_dirs[G_USER_DIRECTORY_VIDEOS] = get_special_folder (CSIDL_MYVIDEO);
+}
+#endif /* G_OS_WIN32 */
+
+#if defined(G_OS_WIN32) || defined(HAVE_CARBON)
+static void 
+maybe_expire_user_special_dirs (void)
+{
+  /* expire the user dirs after G_USER_DIRS_EXPIRE seconds */
+  time_t now;
+
+  time (&now);
+  if (now > g_user_special_dirs_mtime + G_USER_DIRS_EXPIRE &&
+      g_user_special_dirs)
+    {
+      gint i;
+
+      for (i = 0; i < G_USER_N_DIRECTORIES; i++)
+        g_free (g_user_special_dirs[i]);
+
+      g_free (g_user_special_dirs);
+      g_user_special_dirs = NULL;
+    }
+
+  if (g_user_special_dirs == NULL)
+    g_user_special_dirs_mtime = now;
+}
+#endif
+
+#if defined(G_OS_UNIX) && !defined(HAVE_CARBON)
+
+/* expire g_user_special_dirs if the config file
+ * was modified between different reads
+ */
+static void
+maybe_expire_user_special_dirs (void)
+{
+  gchar *config_file;
+  struct stat stat_buf;
+  time_t now;
+
+  /* don't stat() the file more often than necessary */
+  time (&now);
+  if (now < g_user_special_dirs_stat_time + 5)
+    return;
+
+  g_user_special_dirs_stat_time = now;
+
+  config_file = g_build_filename (g_get_user_config_dir (),
+                                  "user-dirs.dirs",
+                                  NULL);
+  
+  if (stat (config_file, &stat_buf) < 0)
+    goto out;
+
+  if (stat_buf.st_mtime != g_user_special_dirs_mtime &&
+      g_user_special_dirs != NULL)
+    {
+      gint i;
+
+      for (i = 0; i < G_USER_N_DIRECTORIES; i++)
+        g_free (g_user_special_dirs[i]);
+
+      g_free (g_user_special_dirs);
+      g_user_special_dirs = NULL;
+    }
+
+    g_user_special_dirs_mtime = stat_buf.st_mtime;
+
+out:
+  g_free (config_file);
+}
+
+/* adapted from xdg-user-dir-lookup.c
+ *
+ * Copyright (C) 2007 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions: 
+ *
+ * The above copyright notice and this permission notice shall be
+/* included in all copies or substantial portions of the Software. 
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+static void
+load_user_special_dirs (void)
+{
+  gchar *config_file;
+  gchar *data;
+  gchar **lines;
+  gint n_lines, i;
+  
+  config_file = g_build_filename (g_get_user_config_dir (),
+                                  "user-dirs.dirs",
+                                  NULL);
+  
+  if (!g_file_get_contents (config_file, &data, NULL, NULL))
+    {
+      g_free (config_file);
+      return;
+    }
+
+  lines = g_strsplit (data, "\n", -1);
+  n_lines = g_strv_length (lines);
+  g_free (data);
+  
+  for (i = 0; i < n_lines; i++)
+    {
+      gchar *buffer = lines[i];
+      gchar *d, *p;
+      gint len;
+      gboolean is_relative = FALSE;
+      GUserDirectory directory;
+
+      /* Remove newline at end */
+      len = strlen (buffer);
+      if (len > 0 && buffer[len - 1] == '\n')
+       buffer[len - 1] = 0;
+      
+      p = buffer;
+      while (*p == ' ' || *p == '\t')
+       p++;
+      
+      if (strncmp (p, "XDG_DESKTOP_DIR", strlen ("XDG_DESKTOP_DIR")) == 0)
+        {
+          directory = G_USER_DIRECTORY_DESKTOP;
+          p += strlen ("XDG_DESKTOP_DIR");
+        }
+      else if (strncmp (p, "XDG_DOCUMENTS_DIR", strlen ("XDG_DOCUMENTS_DIR")) == 0)
+        {
+          directory = G_USER_DIRECTORY_DOCUMENTS;
+          p += strlen ("XDG_DOCUMENTS_DIR");
+        }
+      else if (strncmp (p, "XDG_DOWNLOAD_DIR", strlen ("XDG_DOWNLOAD_DIR")) == 0)
+        {
+          directory = G_USER_DIRECTORY_DOWNLOAD;
+          p += strlen ("XDG_DOWNLOAD_DIR");
+        }
+      else if (strncmp (p, "XDG_MUSIC_DIR", strlen ("XDG_MUSIC_DIR")) == 0)
+        {
+          directory = G_USER_DIRECTORY_MUSIC;
+          p += strlen ("XDG_MUSIC_DIR");
+        }
+      else if (strncmp (p, "XDG_PICTURES_DIR", strlen ("XDG_PICTURES_DIR")) == 0)
+        {
+          directory = G_USER_DIRECTORY_PICTURES;
+          p += strlen ("XDG_PICTURES_DIR");
+        }
+      else if (strncmp (p, "XDG_PUBLICSHARE_DIR", strlen ("XDG_PUBLICSHARE_DIR")) == 0)
+        {
+          directory = G_USER_DIRECTORY_PUBLIC_SHARE;
+          p += strlen ("XDG_PUBLICSHARE_DIR");
+        }
+      else if (strncmp (p, "XDG_TEMPLATES_DIR", strlen ("XDG_TEMPLATES_DIR")) == 0)
+        {
+          directory = G_USER_DIRECTORY_TEMPLATES;
+          p += strlen ("XDG_TEMPLATES_DIR");
+        }
+      else if (strncmp (p, "XDG_VIDEOS_DIR", strlen ("XDG_VIDEOS_DIR")) == 0)
+        {
+          directory = G_USER_DIRECTORY_VIDEOS;
+          p += strlen ("XDG_VIDEOS_DIR");
+        }
+      else
+       continue;
+
+      while (*p == ' ' || *p == '\t')
+       p++;
+
+      if (*p != '=')
+       continue;
+      p++;
+
+      while (*p == ' ' || *p == '\t')
+       p++;
+
+      if (*p != '"')
+       continue;
+      p++;
+
+      if (strncmp (p, "$HOME", 5) == 0)
+       {
+         p += 5;
+         is_relative = TRUE;
+       }
+      else if (*p != '/')
+       continue;
+
+      d = strrchr (p, '"');
+      if (!d)
+        continue;
+      *d = 0;
+
+      d = p;
+      
+      /* remove trailing slashes */
+      len = strlen (d);
+      if (d[len - 1] == '/')
+        d[len - 1] = 0;
+      
+      if (is_relative)
+        g_user_special_dirs[directory] = g_build_filename (g_get_home_dir (), d, NULL);
+      else
+       g_user_special_dirs[directory] = g_strdup (d);
+    }
+
+  g_strfreev (lines);
+  g_free (config_file);
+}
+
+#endif /* G_OS_UNIX && !HAVE_CARBON */
+
+/**
+ * g_get_user_special_dir:
+ * @directory: the logical id of special directory
+ *
+ * Returns the full path of a special directory using its logical id.
+ *
+ * On Unix this is done using the XDG special user directories.
+ *
+ * Return value: the path to the specified special directory, or %NULL
+ *   if the logical id was not found. The returned string is owned by
+ *   GLib and should not be modified or freed.
+ *
+ * Since: 2.14
+ */
+G_CONST_RETURN gchar *
+g_get_user_special_dir (GUserDirectory directory)
+{
+  g_return_val_if_fail (directory >= G_USER_DIRECTORY_DESKTOP &&
+                        directory < G_USER_N_DIRECTORIES, NULL);
+
+  G_LOCK (g_utils_global);
+
+  maybe_expire_user_special_dirs ();
+  if (g_user_special_dirs == NULL)
+    {
+      g_user_special_dirs = g_new0 (gchar *, G_USER_N_DIRECTORIES);
+      load_user_special_dirs ();
+    }
+
+  G_UNLOCK (g_utils_global);
+
+  return g_user_special_dirs[directory];
+}
+
 #ifdef G_OS_WIN32
 
 #undef g_get_system_data_dirs
index 03df0761382619877505ecb338f544feab63018a..4e64b71c95b1f697fcc667b9ec067a4b5af5bf4f 100644 (file)
@@ -145,6 +145,43 @@ G_CONST_RETURN gchar* G_CONST_RETURN * g_get_system_config_dirs (void);
 
 G_CONST_RETURN gchar* G_CONST_RETURN * g_get_language_names (void);
 
+/**
+ * GUserDirectory:
+ *
+ * These are logical ids for special directories which are defined
+ * depending on the platform used. You should use g_get_user_special_dir()
+ * to retrieve the full path associated to the logical id.
+ *
+ * The #GUserDirectory enumeration can be extended at later date. Not
+ * every platform has a directory for every logical id in this
+ * enumeration.
+ *
+ * @G_USER_DIRECTORY_DESKTOP: the user's Desktop directory
+ * @G_USER_DIRECTORY_DOCUMENTS: the user's Documents directory
+ * @G_USER_DIRECTORY_DOWNLOAD: the user's Downloads directory
+ * @G_USER_DIRECTORY_MUSIC: the user's Music directory
+ * @G_USER_DIRECTORY_PICTURES: the user's Pictures directory
+ * @G_USER_DIRECTORY_PUBLIC_SHARE: the user's shared directory
+ * @G_USER_DIRECTORY_TEMPLATES: the user's Templates directory
+ * @G_USER_DIRECTORY_VIDEOS: the user's Movies directory
+ *
+ * Since: 2.14
+ */
+typedef enum {
+  G_USER_DIRECTORY_DESKTOP,
+  G_USER_DIRECTORY_DOCUMENTS,
+  G_USER_DIRECTORY_DOWNLOAD,
+  G_USER_DIRECTORY_MUSIC,
+  G_USER_DIRECTORY_PICTURES,
+  G_USER_DIRECTORY_PUBLIC_SHARE,
+  G_USER_DIRECTORY_TEMPLATES,
+  G_USER_DIRECTORY_VIDEOS,
+
+  G_USER_N_DIRECTORIES
+} GUserDirectory;
+
+G_CONST_RETURN gchar* g_get_user_special_dir (GUserDirectory directory);
+
 typedef struct _GDebugKey      GDebugKey;
 struct _GDebugKey
 {
index 0a846653cd1143b9c63e01f4d9acc307e61a235c..9105321fb995d77700bb28bf4b02699fbe92756d 100644 (file)
@@ -596,6 +596,14 @@ main (int   argc,
   sv = (gchar **) g_get_language_names ();
   g_print ("languages: %s\n", s ? g_strjoinv (":", sv) : "NULL!");
 
+  /* special dirs */
+  s = g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP);
+  g_print ("user_special[DESKTOP]: %s\n", s ? s : "NULL!");
+  s = g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS);
+  g_print ("user_special[DOCUMENTS]: %s\n", s ? s : "NULL!");
+  s = g_get_user_special_dir (G_USER_DIRECTORY_PUBLIC_SHARE);
+  g_print ("user_special[PUBLIC_SHARE]: %s\n", s ? s : "NULL!");
+
   /* type sizes */
   g_print ("checking size of gint8: %"    G_GSIZE_FORMAT, sizeof (gint8));
   TEST (NULL, sizeof (gint8) == 1);