diff --git a/src/sdgui.c b/src/sdgui.c index c547760..cc2a042 100644 --- a/src/sdgui.c +++ b/src/sdgui.c @@ -53,9 +53,8 @@ g; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - static gboolean -dictionary_load (Dictionary *self, gchar *filename, GError **e) +dictionary_load (Dictionary *self, GError **e) { - self->filename = filename; if (!(self->dict = stardict_dict_new (self->filename, e))) return FALSE; @@ -69,22 +68,62 @@ dictionary_load (Dictionary *self, gchar *filename, GError **e) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -static gboolean -init (gchar **filenames, GError **e) +static void +init (gchar **filenames) { while (filenames[g.dictionaries_len]) g.dictionaries_len++; g.dictionaries = g_malloc0_n (sizeof *g.dictionaries, g.dictionaries_len); + for (gsize i = 0; i < g.dictionaries_len; i++) + g.dictionaries[i].filename = filenames[i]; +} + +// TODO: try to deduplicate, similar to app_load_config_values() +static gboolean +init_from_key_file (GKeyFile *kf, GError **error) +{ + const gchar *dictionaries = "Dictionaries"; + gchar **names = + g_key_file_get_keys (kf, dictionaries, &g.dictionaries_len, NULL); + if (!names) + return TRUE; + + g.dictionaries = g_malloc0_n (sizeof *g.dictionaries, g.dictionaries_len); + for (gsize i = 0; i < g.dictionaries_len; i++) + g.dictionaries[i].name = names[i]; + g_free (names); + for (gsize i = 0; i < g.dictionaries_len; i++) { Dictionary *dict = &g.dictionaries[i]; - if (!dictionary_load (dict, filenames[i], e)) + gchar *path = + g_key_file_get_string (kf, dictionaries, dict->name, error); + if (!path) return FALSE; + + // Try to resolve relative paths and expand tildes + if (!(dict->filename = + resolve_filename (path, resolve_relative_config_filename))) + dict->filename = path; + else + g_free (path); } return TRUE; } +static gboolean +init_from_config (GError **error) +{ + GKeyFile *key_file = load_project_config_file (error); + if (!key_file) + return FALSE; + + gboolean result = init_from_key_file (key_file, error); + g_key_file_free (key_file); + return result; +} + static void search (Dictionary *dict) { @@ -203,7 +242,7 @@ main (int argc, char *argv[]) GOptionEntry option_entries[] = { {G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, - NULL, N_("FILE...")}, + NULL, N_("[FILE]...")}, {}, }; @@ -217,14 +256,18 @@ main (int argc, char *argv[]) return 1; } - if (!filenames) - { - // TODO: eventually just load all dictionaries from configuration - die_with_dialog ("No arguments have been passed."); - } - if (!init (filenames, &error)) + if (filenames) + init (filenames); + else if (!init_from_config (&error) && error) die_with_dialog (error->message); + for (gsize i = 0; i < g.dictionaries_len; i++) + if (!dictionary_load (&g.dictionaries[i], &error)) + die_with_dialog (error->message); + if (!g.dictionaries_len) + die_with_dialog (_("No dictionaries found either in " + "the configuration or on the command line")); + // Some Adwaita stupidity const char *style = "notebook header tab { padding: 2px 8px; margin: 0; }"; diff --git a/src/sdtui.c b/src/sdtui.c index 1158d05..caf08e6 100644 --- a/src/sdtui.c +++ b/src/sdtui.c @@ -28,13 +28,11 @@ #include #include #include -#include #include #include #include #include -#include #include // input #include // output @@ -96,94 +94,6 @@ add_read_watch (int fd, GIOFunc func, gpointer user_data) return res; } -// At times, GLib even with its sheer size is surprisingly useless, -// and I need to port some code over from "liberty". - -static gchar ** -get_xdg_config_dirs (void) -{ - GPtrArray *paths = g_ptr_array_new (); - g_ptr_array_add (paths, (gpointer) g_get_user_config_dir ()); - for (const gchar *const *system = g_get_system_config_dirs (); - *system; system++) - g_ptr_array_add (paths, (gpointer) *system); - g_ptr_array_add (paths, NULL); - return (gchar **) g_ptr_array_free (paths, FALSE); -} - -static gchar * -resolve_relative_filename_generic - (gchar **paths, const gchar *tail, const gchar *filename) -{ - for (; *paths; paths++) - { - // As per XDG spec, relative paths are ignored - if (**paths != '/') - continue; - - gchar *file = g_build_filename (*paths, tail, filename, NULL); - GStatBuf st; - if (!g_stat (file, &st)) - return file; - g_free (file); - } - return NULL; -} - -static gchar * -resolve_relative_config_filename (const gchar *filename) -{ - gchar **paths = get_xdg_config_dirs (); - gchar *result = resolve_relative_filename_generic - (paths, PROJECT_NAME, filename); - g_strfreev (paths); - return result; -} - -static gchar * -try_expand_tilde (const gchar *filename) -{ - size_t until_slash = strcspn (filename, "/"); - if (!until_slash) - return g_build_filename (g_get_home_dir () ?: "", filename, NULL); - - long buf_len = sysconf (_SC_GETPW_R_SIZE_MAX); - if (buf_len < 0) - buf_len = 1024; - struct passwd pwd, *success = NULL; - - gchar *user = g_strndup (filename, until_slash); - gchar *buf = g_malloc (buf_len); - while (getpwnam_r (user, &pwd, buf, buf_len, &success) == ERANGE) - buf = g_realloc (buf, buf_len <<= 1); - g_free (user); - - gchar *result = NULL; - if (success) - result = g_strdup_printf ("%s%s", pwd.pw_dir, filename + until_slash); - g_free (buf); - return result; -} - -static gchar * -resolve_filename (const gchar *filename, gchar *(*relative_cb) (const char *)) -{ - // Absolute path is absolute - if (*filename == '/') - return g_strdup (filename); - - // We don't want to use wordexp() for this as it may execute /bin/sh - if (*filename == '~') - { - // Paths to home directories ought to be absolute - char *expanded = try_expand_tilde (filename + 1); - if (expanded) - return expanded; - g_debug ("failed to expand the home directory in `%s'", filename); - } - return relative_cb (filename); -} - // --- Application ------------------------------------------------------------- #define ATTRIBUTE_TABLE(XX) \ @@ -574,8 +484,8 @@ app_load_config_values (Application *self, GKeyFile *kf) continue; // Try to resolve relative paths and expand tildes - gchar *resolved = resolve_filename - (path, resolve_relative_config_filename); + gchar *resolved = + resolve_filename (path, resolve_relative_config_filename); if (resolved) g_free (path); else @@ -588,30 +498,16 @@ app_load_config_values (Application *self, GKeyFile *kf) } static void -app_load_config (Application *self, GError **e) +app_load_config (Application *self, GError **error) { - GKeyFile *kf = g_key_file_new (); - gchar **paths = get_xdg_config_dirs (); - - // XXX: if there are dashes in the final path component, - // the function tries to replace them with directory separators, - // which is completely undocumented - GError *error = NULL; - g_key_file_load_from_dirs (kf, - PROJECT_NAME G_DIR_SEPARATOR_S PROJECT_NAME ".conf", - (const gchar **) paths, NULL, 0, &error); - g_strfreev (paths); - // TODO: proper error handling showing all relevant information; // we can afford that here since the terminal hasn't been initialized yet - if (!error) - app_load_config_values (self, kf); - else if (error->code == G_KEY_FILE_ERROR_NOT_FOUND) - g_error_free (error); - else - g_propagate_error (e, error); - - g_key_file_free (kf); + GKeyFile *key_file = load_project_config_file (error); + if (key_file) + { + app_load_config_values (self, key_file); + g_key_file_free (key_file); + } } static void diff --git a/src/utils.c b/src/utils.c index 3bba022..612eaaa 100644 --- a/src/utils.c +++ b/src/utils.c @@ -19,10 +19,14 @@ #include #include #include +#include + #include #include #include +#include + #include "config.h" #include "utils.h" @@ -105,3 +109,117 @@ fatal (const gchar *format, ...) exit (EXIT_FAILURE); va_end (ap); } + +// At times, GLib even with its sheer size is surprisingly useless, +// and I need to port some code over from "liberty". + +static gchar ** +get_xdg_config_dirs (void) +{ + GPtrArray *paths = g_ptr_array_new (); + g_ptr_array_add (paths, (gpointer) g_get_user_config_dir ()); + for (const gchar *const *system = g_get_system_config_dirs (); + *system; system++) + g_ptr_array_add (paths, (gpointer) *system); + g_ptr_array_add (paths, NULL); + return (gchar **) g_ptr_array_free (paths, FALSE); +} + +gchar * +resolve_relative_filename_generic + (gchar **paths, const gchar *tail, const gchar *filename) +{ + for (; *paths; paths++) + { + // As per XDG spec, relative paths are ignored + if (**paths != '/') + continue; + + gchar *file = g_build_filename (*paths, tail, filename, NULL); + GStatBuf st; + if (!g_stat (file, &st)) + return file; + g_free (file); + } + return NULL; +} + +gchar * +resolve_relative_config_filename (const gchar *filename) +{ + gchar **paths = get_xdg_config_dirs (); + gchar *result = resolve_relative_filename_generic + (paths, PROJECT_NAME, filename); + g_strfreev (paths); + return result; +} + +static gchar * +try_expand_tilde (const gchar *filename) +{ + size_t until_slash = strcspn (filename, "/"); + if (!until_slash) + return g_build_filename (g_get_home_dir () ?: "", filename, NULL); + + long buf_len = sysconf (_SC_GETPW_R_SIZE_MAX); + if (buf_len < 0) + buf_len = 1024; + struct passwd pwd, *success = NULL; + + gchar *user = g_strndup (filename, until_slash); + gchar *buf = g_malloc (buf_len); + while (getpwnam_r (user, &pwd, buf, buf_len, &success) == ERANGE) + buf = g_realloc (buf, buf_len <<= 1); + g_free (user); + + gchar *result = NULL; + if (success) + result = g_strdup_printf ("%s%s", pwd.pw_dir, filename + until_slash); + g_free (buf); + return result; +} + +gchar * +resolve_filename (const gchar *filename, gchar *(*relative_cb) (const char *)) +{ + // Absolute path is absolute + if (*filename == '/') + return g_strdup (filename); + + // We don't want to use wordexp() for this as it may execute /bin/sh + if (*filename == '~') + { + // Paths to home directories ought to be absolute + char *expanded = try_expand_tilde (filename + 1); + if (expanded) + return expanded; + g_debug ("failed to expand the home directory in `%s'", filename); + } + return relative_cb (filename); +} + +GKeyFile * +load_project_config_file (GError **error) +{ + GKeyFile *key_file = g_key_file_new (); + gchar **paths = get_xdg_config_dirs (); + GError *e = NULL; + + // XXX: if there are dashes in the final path component, + // the function tries to replace them with directory separators, + // which is completely undocumented + g_key_file_load_from_dirs (key_file, + PROJECT_NAME G_DIR_SEPARATOR_S PROJECT_NAME ".conf", + (const gchar **) paths, NULL, 0, &e); + g_strfreev (paths); + if (!e) + return key_file; + + if (e->code == G_KEY_FILE_ERROR_NOT_FOUND) + g_error_free (e); + else + g_propagate_error (error, e); + + g_key_file_free (key_file); + return NULL; +} diff --git a/src/utils.h b/src/utils.h index b47daa9..1394f08 100644 --- a/src/utils.h +++ b/src/utils.h @@ -1,7 +1,7 @@ /* * utils.h: miscellaneous utilities * - * Copyright (c) 2013 - 2020, Přemysl Eric Janouch + * Copyright (c) 2013 - 2021, Přemysl Eric Janouch * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted. @@ -43,4 +43,11 @@ gchar *stream_read_string (GDataInputStream *dis, GError **error); gboolean xstrtoul (unsigned long *out, const char *s, int base); void fatal (const gchar *format, ...) G_GNUC_PRINTF (1, 2) G_GNUC_NORETURN; +gchar *resolve_relative_filename_generic + (gchar **paths, const gchar *tail, const gchar *filename); +gchar *resolve_relative_config_filename (const gchar *filename); +gchar *resolve_filename + (const gchar *filename, gchar *(*relative_cb) (const char *)); +GKeyFile *load_project_config_file (GError **error); + #endif // ! UTILS_H