#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
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 *
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