Add support for a number of special directories, as defined by the

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
This commit is contained in:
Matthias Clasen 2007-06-04 14:54:49 +00:00 committed by Matthias Clasen
parent c68dc0aef5
commit d154485bc7
9 changed files with 448 additions and 1 deletions

View File

@ -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)

View 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@:>@],

View 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

View 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

View 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>

View 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

View 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
@ -100,6 +102,10 @@
# 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

View 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
{

View 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);