diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 13:40:30 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 13:40:30 +0000 |
commit | ad8b856c3b0d4d6055141a74664dbea4416c198d (patch) | |
tree | 8596d1a11d20a850e3316e27e51e422baa0aa1e5 /xdg-user-dir | |
parent | Initial commit. (diff) | |
download | xdg-user-dirs-upstream.tar.xz xdg-user-dirs-upstream.zip |
Adding upstream version 0.17.upstream/0.17upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rwxr-xr-x | xdg-user-dir | 8 | ||||
-rw-r--r-- | xdg-user-dir-lookup.c | 232 | ||||
-rw-r--r-- | xdg-user-dirs-update.c | 1152 | ||||
-rw-r--r-- | xdg-user-dirs.desktop | 10 |
4 files changed, 1402 insertions, 0 deletions
diff --git a/xdg-user-dir b/xdg-user-dir new file mode 100755 index 0000000..d64fcd6 --- /dev/null +++ b/xdg-user-dir @@ -0,0 +1,8 @@ +#!/bin/sh + +test -f ${XDG_CONFIG_HOME:-~/.config}/user-dirs.dirs && . ${XDG_CONFIG_HOME:-~/.config}/user-dirs.dirs +if [ "x$1" = "xDESKTOP" ]; then + eval echo \${XDG_${1}_DIR:-$HOME/Desktop} +else + eval echo \${XDG_${1}_DIR:-$HOME} +fi diff --git a/xdg-user-dir-lookup.c b/xdg-user-dir-lookup.c new file mode 100644 index 0000000..a1a2273 --- /dev/null +++ b/xdg-user-dir-lookup.c @@ -0,0 +1,232 @@ +/* + This file is not licenced under the GPL like the rest of the code. + Its is under the MIT license, to encourage reuse by cut-and-paste. + + 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. +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +/** + * xdg_user_dir_lookup_with_fallback: + * @type: a string specifying the type of directory + * @fallback: value to use if the directory isn't specified by the user + * @returns: a newly allocated absolute pathname + * + * Looks up a XDG user directory of the specified type. + * Example of types are "DESKTOP" and "DOWNLOAD". + * + * In case the user hasn't specified any directory for the specified + * type the value returned is @fallback. + * + * The return value is newly allocated and must be freed with + * free(). The return value is never NULL if @fallback != NULL, unless + * out of memory. + **/ +static char * +xdg_user_dir_lookup_with_fallback (const char *type, const char *fallback) +{ + FILE *file; + char *home_dir, *config_home, *config_file; + char buffer[512]; + char *user_dir; + char *p, *d; + int len; + int relative; + + home_dir = getenv ("HOME"); + + if (home_dir == NULL) + goto error; + + config_home = getenv ("XDG_CONFIG_HOME"); + if (config_home == NULL || config_home[0] == 0) + { + config_file = (char*) malloc (strlen (home_dir) + strlen ("/.config/user-dirs.dirs") + 1); + if (config_file == NULL) + goto error; + + strcpy (config_file, home_dir); + strcat (config_file, "/.config/user-dirs.dirs"); + } + else + { + config_file = (char*) malloc (strlen (config_home) + strlen ("/user-dirs.dirs") + 1); + if (config_file == NULL) + goto error; + + strcpy (config_file, config_home); + strcat (config_file, "/user-dirs.dirs"); + } + + file = fopen (config_file, "r"); + free (config_file); + if (file == NULL) + goto error; + + user_dir = NULL; + while (fgets (buffer, sizeof (buffer), file)) + { + /* 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_", 4) != 0) + continue; + p += 4; + if (strncmp (p, type, strlen (type)) != 0) + continue; + p += strlen (type); + if (strncmp (p, "_DIR", 4) != 0) + continue; + p += 4; + + while (*p == ' ' || *p == '\t') + p++; + + if (*p != '=') + continue; + p++; + + while (*p == ' ' || *p == '\t') + p++; + + if (*p != '"') + continue; + p++; + + relative = 0; + if (strncmp (p, "$HOME/", 6) == 0) + { + p += 6; + relative = 1; + } + else if (*p != '/') + continue; + + if (relative) + { + user_dir = (char*) malloc (strlen (home_dir) + 1 + strlen (p) + 1); + if (user_dir == NULL) + goto error2; + + strcpy (user_dir, home_dir); + strcat (user_dir, "/"); + } + else + { + user_dir = (char*) malloc (strlen (p) + 1); + if (user_dir == NULL) + goto error2; + + *user_dir = 0; + } + + d = user_dir + strlen (user_dir); + while (*p && *p != '"') + { + if ((*p == '\\') && (*(p+1) != 0)) + p++; + *d++ = *p++; + } + *d = 0; + } +error2: + fclose (file); + + if (user_dir) + return user_dir; + + error: + if (fallback) + return strdup (fallback); + return NULL; +} + +/** + * xdg_user_dir_lookup: + * @type: a string specifying the type of directory + * @returns: a newly allocated absolute pathname + * + * Looks up a XDG user directory of the specified type. + * Example of types are "DESKTOP" and "DOWNLOAD". + * + * The return value is always != NULL (unless out of memory), + * and if a directory + * for the type is not specified by the user the default + * is the home directory. Except for DESKTOP which defaults + * to ~/Desktop. + * + * The return value is newly allocated and must be freed with + * free(). + **/ +static char * +xdg_user_dir_lookup (const char *type) +{ + char *dir, *home_dir, *user_dir; + + dir = xdg_user_dir_lookup_with_fallback (type, NULL); + if (dir != NULL) + return dir; + + home_dir = getenv ("HOME"); + + if (home_dir == NULL) + return strdup ("/tmp"); + + /* Special case desktop for historical compatibility */ + if (strcmp (type, "DESKTOP") == 0) + { + user_dir = (char*) malloc (strlen (home_dir) + strlen ("/Desktop") + 1); + if (user_dir == NULL) + return NULL; + + strcpy (user_dir, home_dir); + strcat (user_dir, "/Desktop"); + return user_dir; + } + + return strdup (home_dir); +} + +#ifdef STANDALONE_XDG_USER_DIR_LOOKUP +int +main (int argc, char *argv[]) +{ + if (argc != 2) + { + fprintf (stderr, "Usage %s <dir-type>\n", argv[0]); + exit (1); + } + + printf ("%s\n", xdg_user_dir_lookup (argv[1])); + return 0; +} +#endif diff --git a/xdg-user-dirs-update.c b/xdg-user-dirs-update.c new file mode 100644 index 0000000..6a99db0 --- /dev/null +++ b/xdg-user-dirs-update.c @@ -0,0 +1,1152 @@ +#include <config.h> + +#define _GNU_SOURCE + +#include <sys/types.h> +#include <sys/stat.h> +#include <libintl.h> +#include <locale.h> +#include <pwd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <stdarg.h> +#include <errno.h> +#include <iconv.h> +#include <langinfo.h> + +typedef struct { + char *name; + char *path; +} Directory; + +Directory backwards_compat_dirs[] = { + { "DESKTOP", "Desktop" }, + { "TEMPLATES", "Templates" }, + { "PUBLICSHARE", "Public" }, + { NULL} +}; + +static Directory *default_dirs = NULL; +static Directory *user_dirs = NULL; +static int user_dirs_changed = 0; + +/* Config: */ + +static int enabled = 1; +static char *filename_encoding = NULL; /* NULL => utf8 */ + +/* Args */ +static char *dummy_file = NULL; + +static iconv_t filename_converter = (iconv_t)(-1); + +static char * +concat_strings (const char *first, ...) +{ + char *res; + const char *next; + va_list va; + int len; + + res = strdup (first); + len = strlen (res) + 1; + + va_start (va, first); + while ( (next = va_arg(va, const char *)) != NULL) + { + res = realloc (res, len + strlen (next) + 1); + strcat (res, next); + } + + va_end(va); + + return res; +} + +static char * +strdup_end (const char *start, const char *end) +{ + char *res; + + res = malloc (end - start + 1); + memcpy (res, start, end - start); + res[end-start] = 0; + return res; +} + +static int +has_prefix (const char *str, const char *prefix) +{ + return strncmp (str, prefix, strlen (prefix)) == 0; +} + +static char +ascii_toupper (char c) +{ + if (c >= 'a' && c <= 'z') + c = (c - 'a') + 'A'; + return c; +} + +static void +ascii_str_toupper (char *c) +{ + while (*c) + { + *c = ascii_toupper (*c); + c++; + } +} + +static int +is_space (char c) +{ + return c == ' ' || c == '\t'; +} + +static void +remove_trailing_whitespace (char *s) +{ + int len; + + len = strlen (s); + while (len > 0 && is_space (s[len-1])) + { + s[len-1] = 0; + len--; + } +} + +static int +is_regular_file (char *path) +{ + struct stat statbuf; + if (stat (path, &statbuf) == -1) + return 0; + + return S_ISREG(statbuf.st_mode); +} + +static int +is_directory (char *path) +{ + struct stat statbuf; + if (stat (path, &statbuf) == -1) + return 0; + + return S_ISDIR(statbuf.st_mode); +} + + +static int +mkdir_all (char *path) +{ + char *p; + int revert; + int result; + + path = strdup (path); + + result = 1; + p = path; + while (*p) + { + /* skip initial slashes */ + while (*p == '/') + p++; + + while (*p && *p != '/') + p++; + + revert = 0; + if (*p == '/') + { + *p = 0; + revert = 1; + } + + if ((mkdir (path, 0755) == -1) && + (errno != EEXIST)) + { + result = 0; + break; + } + + if (revert) + *p = '/'; + } + + free (path); + return result; +} + +static char * +shell_unescape (char *escaped) +{ + char *unescaped; + char *d; + + unescaped = malloc (strlen (escaped) + 1); + + d = unescaped; + + while (*escaped) + { + if (*escaped == '\\' && *(escaped + 1) != 0) + escaped++; + *d++ = *escaped++; + } + *d = 0; + return unescaped; +} + +static char * +shell_escape (char *unescaped) +{ + char *escaped; + char *d; + + escaped = malloc (strlen (unescaped) * 2 + 1); + + d = escaped; + + while (*unescaped) + { + if (*unescaped == '$' || + *unescaped == '`' || + *unescaped == '\\') + *d++ = '\\'; + *d++ = *unescaped++; + } + *d = 0; + return escaped; +} + +static char * +filename_from_utf8 (const char *utf8_path) +{ + size_t res, len; + const char *in; + char *out, *outp; + size_t in_left, out_left, outbuf_size; + int done; + + if (filename_converter == (iconv_t)(-1)) + return strdup (utf8_path); + + len = strlen (utf8_path); + outbuf_size = len + 1; + + done = 0; + do + { + in = utf8_path; + in_left = len; + out = malloc (outbuf_size); + out_left = outbuf_size - 1; + outp = out; + + res = iconv (filename_converter, + (ICONV_CONST char **)&in, &in_left, + &outp, &out_left); + if (res == (size_t)(-1) && errno == E2BIG) + { + free (out); + outbuf_size *= 2; + } + else + done = 1; + } + while (!done); + + if (res == (size_t)(-1)) + { + free (out); + return NULL; + } + + /* zero terminate */ + *outp = 0; + return out; +} + +static char * +get_home_dir (void) +{ + struct passwd *pw; + static char *home_dir = NULL; + const char *home_env; + + if (home_dir != NULL) + return home_dir; + + home_env = getenv ("HOME"); + + if (home_env && *home_env) + { + home_dir = strdup (home_env); + } + else + { + setpwent (); + pw = getpwuid (getuid ()); + endpwent (); + + if (pw && pw->pw_dir) + home_dir = strdup (pw->pw_dir); + } + + return home_dir; +} + +static char * +get_user_config_file (const char *filename) +{ + char *config_home, *file; + int free_config_home; + + config_home = getenv ("XDG_CONFIG_HOME"); + + free_config_home = 0; + if (config_home == NULL || config_home[0] == 0) + { + config_home = concat_strings (get_home_dir (), "/.config", NULL); + free_config_home = 1; + } + + file = concat_strings (config_home, "/", filename, NULL); + + if (free_config_home) + free (config_home); + + return file; +} + +static void +freev (char **strs) +{ + int i; + if (strs) + { + for (i = 0; strs[i] != NULL; i++) + free (strs[i]); + free (strs); + } +} + +static char ** +parse_colon_separated_dirs (const char *dirs) +{ + int numfiles; + char **paths; + const char *p; + + numfiles = 0; + paths = malloc (sizeof (char *)); + paths[0] = NULL; + + p = dirs; + while (p != NULL && *p != 0) + { + int len; + const char *path; + char *colon; + + path = p; + colon = strchr (path, ':'); + if (colon) + { + len = colon - p; + p = colon + 1; + } + else + { + len = strlen (p); + p = NULL; + } + + paths = realloc (paths, sizeof (char *) * (numfiles + 2)); + paths[numfiles++] = strndup (path, len); + paths[numfiles] = NULL; + } + + return paths; +} + +static char ** +get_config_files (char *filename) +{ + int i; + int numfiles; + char *config_dirs; + char *file; + char **paths, **config_paths; + + numfiles = 0; + paths = malloc (sizeof (char *)); + paths[0] = NULL; + + file = get_user_config_file (filename); + if (file) + { + if (is_regular_file (file)) + { + paths = realloc (paths, sizeof (char *) * (numfiles + 2)); + paths[numfiles++] = file; + paths[numfiles] = NULL; + } + else + free (file); + } + + config_dirs = getenv ("XDG_CONFIG_DIRS"); + if (config_dirs) + config_paths = parse_colon_separated_dirs (config_dirs); + else + config_paths = parse_colon_separated_dirs (XDGCONFDIR); + + for (i = 0; config_paths[i] != NULL; i++) + { + file = concat_strings (config_paths[i], "/", filename, NULL); + if (is_regular_file (file)) + { + paths = realloc (paths, sizeof (char *) * (numfiles + 2)); + paths[numfiles++] = file; + paths[numfiles] = NULL; + } + else + free (file); + free (config_paths[i]); + } + + free (config_paths); + + return paths; +} + +static Directory * +add_directory (Directory *dirs, Directory *dir) +{ + Directory *new_dirs; + int i; + + if (dirs == NULL) + { + new_dirs = malloc (sizeof (Directory) * 2); + new_dirs[0] = *dir; + new_dirs[1].name = NULL; + } + else + { + for (i = 0; dirs[i].name != NULL; i++) + ; + new_dirs = realloc (dirs, (i + 2) * sizeof (Directory)); + new_dirs[i] = *dir; + new_dirs[i+1].name = NULL; + } + return new_dirs; +} + +static int +is_true (const char *str) +{ + while (is_space (*str)) + str++; + + if (*str == '1' || + has_prefix (str, "True") || + has_prefix (str, "true")) + return 1; + return 0; +} + +static void +load_config (char *path) +{ + FILE *file; + char buffer[512]; + char *p; + int len; + + file = fopen (path, "r"); + if (file == NULL) + return; + + while (fgets (buffer, sizeof (buffer), file)) + { + /* Remove newline at end */ + len = strlen (buffer); + if (len > 0 && buffer[len-1] == '\n') + buffer[len-1] = 0; + + p = buffer; + /* Skip whitespace */ + while (is_space (*p)) + p++; + + if (*p == '#') + continue; + + remove_trailing_whitespace (p); + + if (has_prefix (p, "enabled=")) + { + p += strlen ("enabled="); + enabled = is_true (p); + } + if (has_prefix (p, "filename_encoding=")) + { + p += strlen ("filename_encoding="); + + while (is_space (*p)) + p++; + + ascii_str_toupper (p); + remove_trailing_whitespace (p); + if (filename_encoding) + free (filename_encoding); + + if (strcmp (p, "UTF8") == 0 || + strcmp (p, "UTF-8") == 0) + filename_encoding = NULL; + else if (strcmp (p, "LOCALE") == 0) + filename_encoding = strdup (nl_langinfo (CODESET)); + else + filename_encoding = strdup (p); + } + } + + fclose (file); +} + +static void +load_all_configs (void) +{ + char **paths; + int i; + + paths = get_config_files ("user-dirs.conf"); + + /* Load config files in reverse */ + for (i = 0; paths[i] != NULL; i++) + ; + + while (--i >= 0) + load_config (paths[i]); + + freev (paths); +} + +static void +load_default_dirs (void) +{ + FILE *file; + char buffer[512]; + char *p; + char *key, *key_end, *value; + int len; + Directory dir; + char **paths; + + paths = get_config_files ("user-dirs.defaults"); + if (paths[0] == NULL) + { + fprintf (stderr, "No default user directories\n"); + exit (1); + } + + file = fopen (paths[0], "r"); + if (file == NULL) + { + fprintf (stderr, "Can't open %s\n", paths[0]); + exit (1); + } + + while (fgets (buffer, sizeof (buffer), file)) + { + /* Remove newline at end */ + len = strlen (buffer); + if (len > 0 && buffer[len-1] == '\n') + buffer[len-1] = 0; + + p = buffer; + /* Skip whitespace */ + while (is_space (*p)) + p++; + + if (*p == '#') + continue; + + key = p; + while (*p && !is_space (*p) && * p != '=') + p++; + + key_end = p; + + while (is_space (*p)) + p++; + if (*p == '=') + p++; + while (is_space (*p)) + p++; + + value = p; + + *key_end = 0; + + if (*key == 0 || *value == 0) + continue; + + dir.name = strdup (key); + dir.path = strdup (value); + default_dirs = add_directory (default_dirs, &dir); + } + + fclose (file); + freev (paths); +} + +static void +load_user_dirs (void) +{ + FILE *file; + char buffer[512]; + char *p; + char *key, *key_end, *value, *value_end; + int len; + Directory dir; + char *user_config_file; + + user_config_file = get_user_config_file ("user-dirs.dirs"); + + file = fopen (user_config_file, "r"); + free (user_config_file); + + if (file == NULL) + return; + + while (fgets (buffer, sizeof (buffer), file)) + { + /* Remove newline at end */ + len = strlen (buffer); + if (len > 0 && buffer[len-1] == '\n') + buffer[len-1] = 0; + + p = buffer; + /* Skip whitespace */ + while (is_space (*p)) + p++; + + /* Skip comment lines */ + if (*p == '#') + continue; + + if (!has_prefix(p, "XDG_")) + continue; + p += 4; + key = p; + + while (*p && !is_space (*p) && * p != '=') + p++; + + if (*p == 0) + continue; + + key_end = p - 4; + if (key_end <= key || + !has_prefix (key_end, "_DIR")) + continue; + + if (*p == '=') + p++; + + while (is_space (*p)) + p++; + + if (*p++ != '"') + continue; + + + if (has_prefix (p, "$HOME")) + { + p += 5; + if (*p == '/') + p++; + else if (*p != '"' && *p != 0) + continue; /* Not ending after $HOME, nor followed by slash. Ignore */ + } + else if (*p != '/') + continue; + value = p; + + while (*p) + { + if (*p == '"') + break; + if (*p == '\\' && *(p+1) != 0) + p++; + + p++; + } + value_end = p; + + *key_end = 0; + *value_end = 0; + + if (*key == 0) + continue; + + dir.name = strdup (key); + dir.path = shell_unescape (value); + user_dirs = add_directory (user_dirs, &dir); + } + + fclose (file); +} + +static void +save_locale (void) +{ + FILE *file; + char *user_locale_file; + char *locale, *dot; + + user_locale_file = get_user_config_file ("user-dirs.locale"); + file = fopen (user_locale_file, "w"); + free (user_locale_file); + + if (file == NULL) + { + fprintf (stderr, "Can't save user-dirs.locale\n"); + return; + } + + locale = strdup (setlocale (LC_MESSAGES, NULL)); + /* Skip encoding part */ + dot = strchr (locale, '.'); + if (dot) + *dot = 0; + fprintf (file, "%s", locale); + free (locale); + fclose (file); +} + +static int +save_user_dirs (void) +{ + FILE *file; + char *user_config_file; + char *tmp_file; + int i; + char *escaped; + int tmp_fd; + int res; + char *dir, *slash; + struct stat stat_buf; + + res = 1; + + tmp_file = NULL; + if (dummy_file) + user_config_file = strdup (dummy_file); + else + user_config_file = get_user_config_file ("user-dirs.dirs"); + + dir = strdup (user_config_file); + slash = strrchr (dir, '/'); + if (slash) + *slash = 0; + + if (stat (dir, &stat_buf) == -1 && errno == ENOENT) + { + if (mkdir (dir, 0700) == -1) + { + free (dir); + fprintf (stderr, "Can't save user-dirs.dirs, failed to create directory\n"); + res = 0; + goto out; + } + } + free (dir); + + tmp_file = malloc (strlen (user_config_file) + 6 + 1); + strcpy (tmp_file, user_config_file); + strcat (tmp_file, "XXXXXX"); + + tmp_fd = mkstemp (tmp_file); + if (tmp_fd == -1) + { + fprintf (stderr, "Can't save user-dirs.dirs\n"); + res = 0; + goto out; + } + + file = fdopen (tmp_fd, "w"); + if (file == NULL) + { + unlink (tmp_file); + fprintf (stderr, "Can't save user-dirs.dirs\n"); + res = 0; + goto out; + } + + fprintf (file, "# This file is written by xdg-user-dirs-update\n"); + fprintf (file, "# If you want to change or add directories, just edit the line you're\n"); + fprintf (file, "# interested in. All local changes will be retained on the next run.\n"); + fprintf (file, "# Format is XDG_xxx_DIR=\"$HOME/yyy\", where yyy is a shell-escaped\n"); + fprintf (file, "# homedir-relative path, or XDG_xxx_DIR=\"/yyy\", where /yyy is an\n"); + fprintf (file, "# absolute path. No other format is supported.\n"); + fprintf (file, "# \n"); + + if (user_dirs) + { + for (i = 0; user_dirs[i].name != NULL; i++) + { + escaped = shell_escape (user_dirs[i].path); + fprintf (file, "XDG_%s_DIR=\"%s%s\"\n", + user_dirs[i].name, + (*escaped == '/')?"":"$HOME/", + escaped); + free (escaped); + } + } + + fclose (file); + + if (rename (tmp_file, user_config_file) == -1) + { + unlink (tmp_file); + fprintf (stderr, "Can't save user-dirs.dirs\n"); + res = 0; + } + + out: + if (tmp_file) + free (tmp_file); + free (user_config_file); + return res; +} + + +static char * +localize_path_name (const char *path) +{ + char *res; + const char *element, *element_end; + char *element_copy; + char *translated; + int has_slash; + + res = strdup (""); + + while (*path) + { + has_slash = 0; + while (*path == '/') + { + path++; + has_slash = 1; + } + + element = path; + while (*path && *path != '/') + path++; + element_end = path; + + element_copy = strdup_end (element, element_end); + translated = gettext (element_copy); + + res = realloc (res, strlen (res) + 1 + strlen (translated) + 1); + if (has_slash) + strcat (res, "/"); + strcat (res, translated); + + free (element_copy); + } + + return res; +} + +static Directory * +lookup_backwards_compat (Directory *dir) +{ + int i; + for (i = 0; backwards_compat_dirs[i].name != NULL; i++) + { + if (strcmp (dir->name, backwards_compat_dirs[i].name) == 0) + return &backwards_compat_dirs[i]; + } + return NULL; +} + +static Directory * +find_dir (Directory *dirs, const char *name) +{ + int i; + + if (dirs == NULL) + return NULL; + + for (i = 0; dirs[i].name != NULL; i++) + { + if (strcmp (dirs[i].name, name) == 0) + return &dirs[i]; + } + return NULL; +} + +static void +create_dirs (int force) +{ + int i; + Directory dir; + Directory *user_dir, *default_dir, *compat_dir; + char *path_name, *relative_path_name, *translated_name; + + if (default_dirs == NULL) + return; + + for (i = 0; default_dirs[i].name != NULL; i++) + { + default_dir = &default_dirs[i]; + user_dir = NULL; + user_dir = find_dir (user_dirs, default_dir->name); + + if (user_dir && !force) + { + if (user_dir->path[0] == '/') + path_name = strdup (user_dir->path); + else + path_name = concat_strings (get_home_dir (), "/", user_dir->path, NULL); + if (!is_directory (path_name)) + { + fprintf (stderr, "%s was removed, reassigning %s to homedir\n", + path_name, user_dir->name); + free (user_dir->path); + user_dir->path = strdup (""); + user_dirs_changed = 1; + } + free (path_name); + continue; + } + + path_name = NULL; + relative_path_name = NULL; + if (user_dir == NULL && !force) + { + /* New default dir. Check if its an old named dir. We want to + reuse that if it exists. */ + compat_dir = lookup_backwards_compat (default_dir); + if (compat_dir) + { + path_name = concat_strings (get_home_dir (), "/", compat_dir->path, NULL); + if (!is_directory (path_name)) + { + free (path_name); + path_name = NULL; + } + else + relative_path_name = strdup (compat_dir->path); + } + } + + if (path_name == NULL) + { + translated_name = localize_path_name (default_dir->path); + relative_path_name = filename_from_utf8 (translated_name); + if (relative_path_name == NULL) + relative_path_name = strdup (translated_name); + free (translated_name); + if (relative_path_name[0] == '/') + path_name = strdup (relative_path_name); /* default path was absolute, not homedir relative */ + else + path_name = concat_strings (get_home_dir (), "/", relative_path_name, NULL); + } + + if (user_dir == NULL || strcmp (relative_path_name, user_dir->path) != 0) + { + /* Don't make the directories if we're writing a dummy output file */ + if (dummy_file == NULL && + !mkdir_all (path_name)) + { + fprintf (stderr, "Can't create dir %s\n", path_name); + } + else + { + user_dirs_changed = 1; + if (user_dir == NULL) + { + dir.name = strdup (default_dir->name); + dir.path = strdup (relative_path_name); + user_dirs = add_directory (user_dirs, &dir); + } + else + { + /* We forced an update */ + fprintf (stdout, "Moving %s directory from %s to %s\n", + default_dir->name, user_dir->path, relative_path_name); + free (user_dir->path); + user_dir->path = strdup (relative_path_name); + } + } + } + + free (relative_path_name); + free (path_name); + } +} + +int +main (int argc, char *argv[]) +{ + int force; + int i; + int was_empty; + char *set_dir = NULL; + char *set_value = NULL; + char *locale_dir = NULL; + + setlocale (LC_ALL, ""); + + if (is_directory (LOCALEDIR)) + locale_dir = strdup (LOCALEDIR); + else + { + /* In case LOCALEDIR does not exist, e.g. xdg-user-dirs is installed in + * a different location than the one determined at compile time, look + * through the XDG_DATA_DIRS environment variable for alternate locations + * of the locale files */ + char *data_dirs = getenv ("XDG_DATA_DIRS"); + if (data_dirs) + { + char **data_paths; + + data_paths = parse_colon_separated_dirs (data_dirs); + for (i = 0; data_paths[i] != NULL; i++) + { + if (!locale_dir) + { + char *dir = NULL; + dir = concat_strings (data_paths[i], "/", "locale", NULL); + if (is_directory (dir)) + locale_dir = dir; + else + free (dir); + } + free (data_paths[i]); + } + free (data_paths); + } + } + + bindtextdomain (GETTEXT_PACKAGE, locale_dir); + free (locale_dir); + + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + + force = 0; + for (i = 1; i < argc; i++) + { + if (strcmp (argv[i], "--help") == 0) + { + printf ("Usage: xdg-user-dirs-update [--force] [--dummy-output <path>] [--set DIR path]\n"); + exit (0); + } + else if (strcmp (argv[i], "--force") == 0) + force = 1; + else if (strcmp (argv[i], "--dummy-output") == 0 && i + 1 < argc) + dummy_file = argv[++i]; + else if (strcmp (argv[i], "--set") == 0 && i + 2 < argc) + { + set_dir = argv[++i]; + set_value = argv[++i]; + + if (*set_value != '/') + { + printf ("directory value must be absolute path (was %s)\n", set_value); + exit (1); + } + } + else + { + printf ("Invalid argument %s\n", argv[i]); + exit (1); + } + } + + load_all_configs (); + if (filename_encoding) + { + filename_converter = iconv_open (filename_encoding, "UTF-8"); + if (filename_converter == (iconv_t)(-1)) + { + fprintf (stderr, "Can't convert from UTF-8 to %s\n", filename_encoding); + return 1; + } + } + + if (set_dir != NULL) + { + Directory *dir; + char *path, *home; + /* Set a key */ + + load_user_dirs (); + + home = get_home_dir (); + + path = set_value; + if (has_prefix (path, home)) + { + path += strlen (home); + while (*path == '/') + path++; + } + + dir = find_dir (user_dirs, set_dir); + if (dir != NULL) + { + free (dir->path); + dir->path = strdup (path); + } + else + { + Directory new_dir; + + new_dir.name = strdup (set_dir); + new_dir.path = strdup (path); + + user_dirs = add_directory (user_dirs, &new_dir); + } + if (!save_user_dirs ()) + return 1; + } + else + { + + /* default: update */ + if (!enabled) + return 0; + + load_default_dirs (); + load_user_dirs (); + + was_empty = (user_dirs == NULL) || (user_dirs->name == NULL); + + create_dirs (force); + + if (user_dirs_changed) + { + if (!save_user_dirs ()) + return 1; + + if ((force || was_empty) && dummy_file == NULL) + save_locale (); + } + + } + return 0; +} diff --git a/xdg-user-dirs.desktop b/xdg-user-dirs.desktop new file mode 100644 index 0000000..6b969d4 --- /dev/null +++ b/xdg-user-dirs.desktop @@ -0,0 +1,10 @@ +[Desktop Entry] +Type=Application +Name=User folders update +TryExec=xdg-user-dirs-update +Exec=xdg-user-dirs-update +StartupNotify=false +NoDisplay=true + +X-GNOME-Autostart-Phase=Initialization +X-KDE-autostart-phase=1 |