1152 lines
21 KiB
C
1152 lines
21 KiB
C
#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;
|
|
}
|