sdgui: load dictionaries from sdtui configuration
This commit is contained in:
parent
10c05a2422
commit
f147b54393
67
src/sdgui.c
67
src/sdgui.c
|
@ -53,9 +53,8 @@ g;
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
static gboolean
|
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)))
|
if (!(self->dict = stardict_dict_new (self->filename, e)))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
|
@ -69,22 +68,62 @@ dictionary_load (Dictionary *self, gchar *filename, GError **e)
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
static gboolean
|
static void
|
||||||
init (gchar **filenames, GError **e)
|
init (gchar **filenames)
|
||||||
{
|
{
|
||||||
while (filenames[g.dictionaries_len])
|
while (filenames[g.dictionaries_len])
|
||||||
g.dictionaries_len++;
|
g.dictionaries_len++;
|
||||||
|
|
||||||
g.dictionaries = g_malloc0_n (sizeof *g.dictionaries, 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++)
|
for (gsize i = 0; i < g.dictionaries_len; i++)
|
||||||
{
|
{
|
||||||
Dictionary *dict = &g.dictionaries[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;
|
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;
|
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
|
static void
|
||||||
search (Dictionary *dict)
|
search (Dictionary *dict)
|
||||||
{
|
{
|
||||||
|
@ -203,7 +242,7 @@ main (int argc, char *argv[])
|
||||||
GOptionEntry option_entries[] =
|
GOptionEntry option_entries[] =
|
||||||
{
|
{
|
||||||
{G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames,
|
{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;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!filenames)
|
if (filenames)
|
||||||
{
|
init (filenames);
|
||||||
// TODO: eventually just load all dictionaries from configuration
|
else if (!init_from_config (&error) && error)
|
||||||
die_with_dialog ("No arguments have been passed.");
|
|
||||||
}
|
|
||||||
if (!init (filenames, &error))
|
|
||||||
die_with_dialog (error->message);
|
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
|
// Some Adwaita stupidity
|
||||||
const char *style = "notebook header tab { padding: 2px 8px; margin: 0; }";
|
const char *style = "notebook header tab { padding: 2px 8px; margin: 0; }";
|
||||||
|
|
||||||
|
|
122
src/sdtui.c
122
src/sdtui.c
|
@ -28,13 +28,11 @@
|
||||||
#include <gio/gio.h>
|
#include <gio/gio.h>
|
||||||
#include <pango/pango.h>
|
#include <pango/pango.h>
|
||||||
#include <glib/gi18n.h>
|
#include <glib/gi18n.h>
|
||||||
#include <glib/gstdio.h>
|
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <pwd.h>
|
|
||||||
|
|
||||||
#include <termo.h> // input
|
#include <termo.h> // input
|
||||||
#include <ncurses.h> // output
|
#include <ncurses.h> // output
|
||||||
|
@ -96,94 +94,6 @@ add_read_watch (int fd, GIOFunc func, gpointer user_data)
|
||||||
return res;
|
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 -------------------------------------------------------------
|
// --- Application -------------------------------------------------------------
|
||||||
|
|
||||||
#define ATTRIBUTE_TABLE(XX) \
|
#define ATTRIBUTE_TABLE(XX) \
|
||||||
|
@ -574,8 +484,8 @@ app_load_config_values (Application *self, GKeyFile *kf)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Try to resolve relative paths and expand tildes
|
// Try to resolve relative paths and expand tildes
|
||||||
gchar *resolved = resolve_filename
|
gchar *resolved =
|
||||||
(path, resolve_relative_config_filename);
|
resolve_filename (path, resolve_relative_config_filename);
|
||||||
if (resolved)
|
if (resolved)
|
||||||
g_free (path);
|
g_free (path);
|
||||||
else
|
else
|
||||||
|
@ -588,30 +498,16 @@ app_load_config_values (Application *self, GKeyFile *kf)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
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;
|
// TODO: proper error handling showing all relevant information;
|
||||||
// we can afford that here since the terminal hasn't been initialized yet
|
// we can afford that here since the terminal hasn't been initialized yet
|
||||||
if (!error)
|
GKeyFile *key_file = load_project_config_file (error);
|
||||||
app_load_config_values (self, kf);
|
if (key_file)
|
||||||
else if (error->code == G_KEY_FILE_ERROR_NOT_FOUND)
|
{
|
||||||
g_error_free (error);
|
app_load_config_values (self, key_file);
|
||||||
else
|
g_key_file_free (key_file);
|
||||||
g_propagate_error (e, error);
|
}
|
||||||
|
|
||||||
g_key_file_free (kf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
118
src/utils.c
118
src/utils.c
|
@ -19,10 +19,14 @@
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include <glib/gprintf.h>
|
#include <glib/gprintf.h>
|
||||||
#include <gio/gio.h>
|
#include <gio/gio.h>
|
||||||
|
#include <glib/gstdio.h>
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
#include <pwd.h>
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
|
@ -105,3 +109,117 @@ fatal (const gchar *format, ...)
|
||||||
exit (EXIT_FAILURE);
|
exit (EXIT_FAILURE);
|
||||||
va_end (ap);
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* utils.h: miscellaneous utilities
|
* utils.h: miscellaneous utilities
|
||||||
*
|
*
|
||||||
* Copyright (c) 2013 - 2020, Přemysl Eric Janouch <p@janouch.name>
|
* Copyright (c) 2013 - 2021, Přemysl Eric Janouch <p@janouch.name>
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and/or distribute this software for any
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
* purpose with or without fee is hereby granted.
|
* 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);
|
gboolean xstrtoul (unsigned long *out, const char *s, int base);
|
||||||
void fatal (const gchar *format, ...) G_GNUC_PRINTF (1, 2) G_GNUC_NORETURN;
|
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
|
#endif // ! UTILS_H
|
||||||
|
|
Loading…
Reference in New Issue