diff options
author | 2012-09-05 01:29:46 -0400 | |
---|---|---|
committer | 2012-09-05 01:33:18 -0400 | |
commit | a0e3c463acbda2d81081e223ef999fdab59a0304 (patch) | |
tree | 1f5e363ed7898caa5db90d2eacc880b6cc9da506 | |
parent | Respect --debug switch when using >=glib-2.31.2 (diff) | |
download | openrc-settingsd-a0e3c463acbda2d81081e223ef999fdab59a0304.tar.gz openrc-settingsd-a0e3c463acbda2d81081e223ef999fdab59a0304.tar.bz2 openrc-settingsd-a0e3c463acbda2d81081e223ef999fdab59a0304.zip |
Finish SetVConsoleKeyboard() and SetX11Keyboard() implementation
Also, fix xorg.conf.d parser so it works as intended.
-rw-r--r-- | Makefile.am | 5 | ||||
-rw-r--r-- | TODO | 2 | ||||
-rw-r--r-- | data/kbd-model-map | 73 | ||||
-rw-r--r-- | src/localed.c | 730 | ||||
-rw-r--r-- | src/shell-utils.c | 80 | ||||
-rw-r--r-- | src/shell-utils.h | 14 |
6 files changed, 828 insertions, 76 deletions
diff --git a/Makefile.am b/Makefile.am index 7ae61ed..b0694bf 100644 --- a/Makefile.am +++ b/Makefile.am @@ -5,10 +5,15 @@ EXTRA_DIST = \ data/locale1.xml \ $(NULL) +pkgdata_DATA = \ + data/kbd-model-map \ + $(NULL) + AM_CPPFLAGS = \ -include $(top_builddir)/config.h \ -DSYSCONFDIR=\""$(sysconfdir)"\" \ -DLIBEXECDIR=\""$(libexecdir)"\" \ + -DPKGDATADIR=\""$(pkgdatadir)"\" \ $(OPENRC_SETTINGSD_CFLAGS) \ -I$(top_srcdir)/src \ -I$(top_builddir)/src \ @@ -9,7 +9,7 @@ document that this case is not supported? Write an init.d file to read RC_SYS so that we can piggyback on openrc's built-in virtualization detection. -Implement localed and timedated. +Implement timedated. Do something about runtime dependency on systemd's org.freedesktop.hostname1.policy, org.freedesktop.locale1.policy, and org.freedesktop.timedate1.policy files. diff --git a/data/kbd-model-map b/data/kbd-model-map new file mode 100644 index 0000000..5876801 --- /dev/null +++ b/data/kbd-model-map @@ -0,0 +1,73 @@ +# Shamelessly stolen from systemd-44 source (src/locale/kbd-model-map) +# Generated from system-config-keyboard's model list +# consolelayout xlayout xmodel xvariant xoptions +sg ch pc105 de_nodeadkeys terminate:ctrl_alt_bksp +nl nl pc105 - terminate:ctrl_alt_bksp +mk-utf mkd,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll +trq tr pc105 - terminate:ctrl_alt_bksp +guj in,us pc105 guj terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll +uk gb pc105 - terminate:ctrl_alt_bksp +is-latin1 is pc105 - terminate:ctrl_alt_bksp +de de pc105 - terminate:ctrl_alt_bksp +gur gur,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll +la-latin1 latam pc105 - terminate:ctrl_alt_bksp +us us pc105+inet - terminate:ctrl_alt_bksp +ko kr pc105 - terminate:ctrl_alt_bksp +ro-std ro pc105 std terminate:ctrl_alt_bksp +de-latin1 de pc105 - terminate:ctrl_alt_bksp +tml-inscript in,us pc105 tam terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll +slovene si pc105 - terminate:ctrl_alt_bksp +hu101 hu pc105 qwerty terminate:ctrl_alt_bksp +jp106 jp jp106 - terminate:ctrl_alt_bksp +croat hr pc105 - terminate:ctrl_alt_bksp +ben-probhat in,us pc105 ben_probhat terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll +fi-latin1 fi pc105 - terminate:ctrl_alt_bksp +it2 it pc105 - terminate:ctrl_alt_bksp +hu hu pc105 - terminate:ctrl_alt_bksp +sr-latin rs pc105 latin terminate:ctrl_alt_bksp +fi fi pc105 - terminate:ctrl_alt_bksp +fr_CH ch pc105 fr terminate:ctrl_alt_bksp +dk-latin1 dk pc105 - terminate:ctrl_alt_bksp +fr fr pc105 - terminate:ctrl_alt_bksp +it it pc105 - terminate:ctrl_alt_bksp +tml-uni in,us pc105 tam_TAB terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll +ua-utf ua,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll +fr-latin1 fr pc105 - terminate:ctrl_alt_bksp +sg-latin1 ch pc105 de_nodeadkeys terminate:ctrl_alt_bksp +be-latin1 be pc105 - terminate:ctrl_alt_bksp +dk dk pc105 - terminate:ctrl_alt_bksp +fr-pc fr pc105 - terminate:ctrl_alt_bksp +bg_pho-utf8 bg,us pc105 ,phonetic terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll +it-ibm it pc105 - terminate:ctrl_alt_bksp +cz-us-qwertz cz,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll +ar-digits ara,us pc105 digits terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll +br-abnt2 br abnt2 - terminate:ctrl_alt_bksp +ro ro pc105 - terminate:ctrl_alt_bksp +us-acentos us pc105 intl terminate:ctrl_alt_bksp +pt-latin1 pt pc105 - terminate:ctrl_alt_bksp +ro-std-cedilla ro pc105 std_cedilla terminate:ctrl_alt_bksp +tj tj pc105 - terminate:ctrl_alt_bksp +ar-qwerty ara,us pc105 qwerty terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll +ar-azerty-digits ara,us pc105 azerty_digits terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll +ben in,us pc105 ben terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll +de-latin1-nodeadkeys de pc105 nodeadkeys terminate:ctrl_alt_bksp +no no pc105 - terminate:ctrl_alt_bksp +bg_bds-utf8 bg,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll +dvorak us pc105 dvorak terminate:ctrl_alt_bksp +ru ru,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll +cz-lat2 cz pc105 qwerty terminate:ctrl_alt_bksp +pl2 pl pc105 - terminate:ctrl_alt_bksp +es es pc105 - terminate:ctrl_alt_bksp +ro-cedilla ro pc105 cedilla terminate:ctrl_alt_bksp +ie ie pc105 - terminate:ctrl_alt_bksp +ar-azerty ara,us pc105 azerty terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll +ar-qwerty-digits ara,us pc105 qwerty_digits terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll +et ee pc105 - terminate:ctrl_alt_bksp +sk-qwerty sk pc105 - terminate:ctrl_alt_bksp,qwerty +dev dev,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll +fr-latin9 fr pc105 latin9 terminate:ctrl_alt_bksp +fr_CH-latin1 ch pc105 fr terminate:ctrl_alt_bksp +cf ca(fr) pc105 - terminate:ctrl_alt_bksp +sv-latin1 se pc105 - terminate:ctrl_alt_bksp +sr-cy rs pc105 - terminate:ctrl_alt_bksp +gr gr,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll diff --git a/src/localed.c b/src/localed.c index 706a8ed..2e8e44e 100644 --- a/src/localed.c +++ b/src/localed.c @@ -16,6 +16,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#include <limits.h> #include <stdlib.h> #include <string.h> @@ -58,6 +59,211 @@ static GFile *x11_gentoo_file = NULL; static GFile *x11_systemd_file = NULL; G_LOCK_DEFINE_STATIC (xorg_conf); +/* keyboard model map file parser */ + +static GFile *kbd_model_map_file = NULL; + +GRegex *kbd_model_map_line_comment_re = NULL; +GRegex *kbd_model_map_line_re = NULL; + +struct kbd_model_map_entry { + gchar *vconsole_keymap; + gchar *x11_layout; + gchar *x11_model; + gchar *x11_variant; + gchar *x11_options; +}; + +static void +kbd_model_map_regex_destroy () +{ + if (kbd_model_map_line_comment_re != NULL) { + g_regex_unref (kbd_model_map_line_comment_re); + kbd_model_map_line_comment_re = NULL; + } + if (kbd_model_map_line_re != NULL) { + g_regex_unref (kbd_model_map_line_re); + kbd_model_map_line_re = NULL; + } +} + +static void +kbd_model_map_regex_init () +{ + if (kbd_model_map_line_comment_re == NULL) { + kbd_model_map_line_comment_re = g_regex_new ("^\\s*(?:#.*)?$", G_REGEX_ANCHORED, 0, NULL); + g_assert (kbd_model_map_line_comment_re != NULL); + } + if (kbd_model_map_line_re == NULL) { + kbd_model_map_line_re = g_regex_new ("^\\s*(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)", G_REGEX_ANCHORED, 0, NULL); + g_assert (kbd_model_map_line_re != NULL); + } +} + +static gboolean +kbd_model_map_entry_matches_vconsole (const struct kbd_model_map_entry *entry, + const gchar *vconsole_keymap) +{ + return !g_strcmp0 (vconsole_keymap, entry->vconsole_keymap); +} + +static gboolean +matches_delimeted (const gchar *left, + const gchar *right, + const gchar *delimeter, + unsigned int *failure_score) +{ + gboolean ret = FALSE; + gchar **leftv = NULL, **rightv = NULL; + gchar **leftcur = NULL, **rightcur = NULL; + + if (left == NULL || left[0] == 0) + leftv = g_new0 (gchar *, 1); + else + leftv = g_strsplit (left, delimeter, 0); + + if (right == NULL || right[0] == 0) + rightv = g_new0 (gchar *, 1); + else + rightv = g_strsplit (right, delimeter, 0); + + if (failure_score != NULL) + *failure_score = 0; + + for (leftcur = leftv; *leftcur != NULL; leftcur++) { + gboolean found = FALSE; + for (rightcur = rightv; *rightcur != NULL; rightcur++) + if (!g_strcmp0 (*leftcur, *rightcur)) { + found = TRUE; + break; + } + if (found) + ret = TRUE; + else if (failure_score != NULL) + (*failure_score)++; + } + + for (rightcur = rightv; *rightcur != NULL; rightcur++) { + gboolean found = FALSE; + for (leftcur = leftv; *leftcur != NULL; leftcur++) + if (!g_strcmp0 (*rightcur, *leftcur)) { + found = TRUE; + break; + } + if (found) + ret = TRUE; + else if (failure_score != NULL) + (*failure_score)++; + } + + g_strfreev (leftv); + g_strfreev (rightv); + return ret; +} + +static gboolean +kbd_model_map_entry_matches_x11 (const struct kbd_model_map_entry *entry, + const gchar *_x11_layout, + const gchar *_x11_model, + const gchar *_x11_variant, + const gchar *_x11_options, + unsigned int *failure_score) +{ + unsigned int x11_layout_failures; + gboolean ret = FALSE; + + ret = matches_delimeted (_x11_layout, entry->x11_layout, ",", &x11_layout_failures); + if (failure_score != NULL) + *failure_score = 10000 * !ret + + 100 * x11_layout_failures + + (g_strcmp0 (_x11_model, entry->x11_model) ? 1 : 0) + + 10 * (g_strcmp0 (_x11_variant, entry->x11_variant) ? 1 : 0) + + !matches_delimeted (_x11_options, entry->x11_options, ",", NULL); + return ret; +} + +static void +kbd_model_map_entry_free (struct kbd_model_map_entry *entry) +{ + if (entry == NULL) + return; + + g_free (entry->vconsole_keymap); + g_free (entry->x11_layout); + g_free (entry->x11_model); + g_free (entry->x11_variant); + g_free (entry->x11_options); + + g_free (entry); +} + +static GList* +kbd_model_map_load (GError **error) +{ + GList *ret = NULL; + gchar *filename = NULL, *filebuf = NULL, *line = NULL, *newline = NULL; + struct kbd_model_map_entry *entry = NULL; + + filename = g_file_get_path (kbd_model_map_file); + g_debug ("Parsing keyboard model map file file: '%s'", filename); + + if (!g_file_load_contents (kbd_model_map_file, NULL, &filebuf, NULL, NULL, error)) { + g_prefix_error (error, "Unable to read '%s':", filename); + goto out; + } + + for (line = filebuf; *line != 0; line = newline + 1) { + struct kbd_model_map_entry *entry = NULL; + GMatchInfo *match_info = NULL; + + if ((newline = strstr (line, "\n")) != NULL) + *newline = 0; + else + newline = line + strlen (line) - 1; + + if (g_regex_match (kbd_model_map_line_comment_re, line, 0, &match_info)) { + g_match_info_free (match_info); + continue; + } + + if (!g_regex_match (kbd_model_map_line_re, line, 0, &match_info)) { + g_propagate_error (error, + g_error_new (G_FILE_ERROR, G_FILE_ERROR_FAILED, + "Failed to parse line '%s' in '%s'", line, filename)); + g_match_info_free (match_info); + if (ret != NULL) { + g_list_free_full (ret, (GDestroyNotify)kbd_model_map_entry_free); + ret = NULL; + } + goto out; + } + entry = g_new0 (struct kbd_model_map_entry, 1); + entry->vconsole_keymap = g_match_info_fetch (match_info, 1); + entry->x11_layout = g_match_info_fetch (match_info, 2); + entry->x11_model = g_match_info_fetch (match_info, 3); + entry->x11_variant = g_match_info_fetch (match_info, 4); + entry->x11_options = g_match_info_fetch (match_info, 5); + + // "-" in the map file stands for an empty string + if (!g_strcmp0 (entry->x11_model, "-")) + entry->x11_model[0] = 0; + if (!g_strcmp0 (entry->x11_variant, "-")) + entry->x11_variant[0] = 0; + if (!g_strcmp0 (entry->x11_options, "-")) + entry->x11_options[0] = 0; + + ret = g_list_prepend (ret, entry); + g_match_info_free (match_info); + } + out: + if (ret != NULL) + ret = g_list_reverse (ret); + + g_free (filename); + g_free (filebuf); + return ret; +} + /* Trivial /etc/X11/xorg.conf.d/30-keyboard.conf parser */ enum XORG_CONFD_LINE_TYPE { @@ -141,7 +347,7 @@ static void xorg_confd_regex_init () { if (xorg_confd_line_comment_re == NULL) { - xorg_confd_line_comment_re = g_regex_new ("^\\s*#", G_REGEX_ANCHORED|G_REGEX_CASELESS, 0, NULL); + xorg_confd_line_comment_re = g_regex_new ("^\\s*#", G_REGEX_ANCHORED, 0, NULL); g_assert (xorg_confd_line_comment_re != NULL); } if (xorg_confd_line_section_input_class_re == NULL) { @@ -190,7 +396,6 @@ xorg_confd_line_entry_free (struct xorg_confd_line_entry *entry) g_free (entry); } -/* Note that string and value are not duplicated */ static struct xorg_confd_line_entry * xorg_confd_line_entry_new (const gchar *string, const gchar *value, @@ -202,6 +407,7 @@ xorg_confd_line_entry_new (const gchar *string, entry->string = g_strdup (string); entry->value = g_strdup (value); entry->type = type; + return entry; } static void @@ -226,9 +432,7 @@ xorg_confd_parser_new (GFile *xorg_confd_file, GError **error) { struct xorg_confd_parser *parser = NULL; - gchar *filebuf = NULL; - gchar **linebuf = NULL; - gchar **lines = NULL; + gchar *filebuf = NULL, *line = NULL, *newline = NULL; GList *input_class_section_start = NULL; gboolean in_section = FALSE, in_xkb_section = FALSE; @@ -244,63 +448,64 @@ xorg_confd_parser_new (GFile *xorg_confd_file, goto fail; } - lines = g_strsplit (filebuf, "\n", 0); - if (lines == NULL) - goto out; - - for (linebuf = lines; *linebuf != NULL; linebuf++) { + for (line = filebuf; *line != 0; line = newline + 1) { struct xorg_confd_line_entry *entry = NULL; GMatchInfo *match_info = NULL; gboolean matched = FALSE; - entry = xorg_confd_line_entry_new (*linebuf, NULL, XORG_CONFD_LINE_TYPE_UNKNOWN); + if ((newline = strstr (line, "\n")) != NULL) + *newline = 0; + else + newline = line + strlen (line) - 1; + + entry = xorg_confd_line_entry_new (line, NULL, XORG_CONFD_LINE_TYPE_UNKNOWN); - if (g_regex_match (xorg_confd_line_comment_re, *linebuf, 0, &match_info)) { - g_debug ("Parsed line '%s' as comment", *linebuf); + if (g_regex_match (xorg_confd_line_comment_re, line, 0, &match_info)) { + g_debug ("Parsed line '%s' as comment", line); entry->type = XORG_CONFD_LINE_TYPE_COMMENT; - } else if (g_regex_match (xorg_confd_line_section_input_class_re, *linebuf, 0, &match_info)) { - g_debug ("Parsed line '%s' as InputClass section", *linebuf); + } else if (g_regex_match (xorg_confd_line_section_input_class_re, line, 0, &match_info)) { + g_debug ("Parsed line '%s' as InputClass section", line); if (in_section) goto no_match; in_section = TRUE; entry->type = XORG_CONFD_LINE_TYPE_SECTION_INPUT_CLASS; - } else if (g_regex_match (xorg_confd_line_section_re, *linebuf, 0, &match_info)) { - g_debug ("Parsed line '%s' as non-InputClass section", *linebuf); + } else if (g_regex_match (xorg_confd_line_section_re, line, 0, &match_info)) { + g_debug ("Parsed line '%s' as non-InputClass section", line); if (in_section) goto no_match; in_section = TRUE; entry->type = XORG_CONFD_LINE_TYPE_SECTION_OTHER; - } else if (g_regex_match (xorg_confd_line_end_section_re, *linebuf, 0, &match_info)) { - g_debug ("Parsed line '%s' as end of section", *linebuf); + } else if (g_regex_match (xorg_confd_line_end_section_re, line, 0, &match_info)) { + g_debug ("Parsed line '%s' as end of section", line); if (!in_section) goto no_match; entry->type = XORG_CONFD_LINE_TYPE_END_SECTION; - } else if (g_regex_match (xorg_confd_line_match_is_keyboard_re, *linebuf, 0, &match_info)) { - g_debug ("Parsed line '%s' as MatchIsKeyboard declaration", *linebuf); + } else if (g_regex_match (xorg_confd_line_match_is_keyboard_re, line, 0, &match_info)) { + g_debug ("Parsed line '%s' as MatchIsKeyboard declaration", line); if (!in_section) goto no_match; entry->type = XORG_CONFD_LINE_TYPE_MATCH_IS_KEYBOARD; in_xkb_section = TRUE; - } else if (g_regex_match (xorg_confd_line_xkb_layout_re, *linebuf, 0, &match_info)) { - g_debug ("Parsed line '%s' as XkbLayout option", *linebuf); + } else if (g_regex_match (xorg_confd_line_xkb_layout_re, line, 0, &match_info)) { + g_debug ("Parsed line '%s' as XkbLayout option", line); if (!in_section) goto no_match; entry->type = XORG_CONFD_LINE_TYPE_XKB_LAYOUT; entry->value = g_match_info_fetch (match_info, 2); - } else if (g_regex_match (xorg_confd_line_xkb_model_re, *linebuf, 0, &match_info)) { - g_debug ("Parsed line '%s' as XkbModel option", *linebuf); + } else if (g_regex_match (xorg_confd_line_xkb_model_re, line, 0, &match_info)) { + g_debug ("Parsed line '%s' as XkbModel option", line); if (!in_section) goto no_match; entry->type = XORG_CONFD_LINE_TYPE_XKB_MODEL; entry->value = g_match_info_fetch (match_info, 2); - } else if (g_regex_match (xorg_confd_line_xkb_variant_re, *linebuf, 0, &match_info)) { - g_debug ("Parsed line '%s' as XkbVariant option", *linebuf); + } else if (g_regex_match (xorg_confd_line_xkb_variant_re, line, 0, &match_info)) { + g_debug ("Parsed line '%s' as XkbVariant option", line); if (!in_section) goto no_match; entry->type = XORG_CONFD_LINE_TYPE_XKB_VARIANT; entry->value = g_match_info_fetch (match_info, 2); - } else if (g_regex_match (xorg_confd_line_xkb_options_re, *linebuf, 0, &match_info)) { - g_debug ("Parsed line '%s' as XkbOptions option", *linebuf); + } else if (g_regex_match (xorg_confd_line_xkb_options_re, line, 0, &match_info)) { + g_debug ("Parsed line '%s' as XkbOptions option", line); if (!in_section) goto no_match; entry->type = XORG_CONFD_LINE_TYPE_XKB_OPTIONS; @@ -308,7 +513,7 @@ xorg_confd_parser_new (GFile *xorg_confd_file, } if (entry->type == XORG_CONFD_LINE_TYPE_UNKNOWN) - g_debug ("Parsing line '%s' as unknown", *linebuf); + g_debug ("Parsing line '%s' as unknown", line); g_match_info_free (match_info); parser->line_list = g_list_prepend (parser->line_list, entry); @@ -342,7 +547,6 @@ xorg_confd_parser_new (GFile *xorg_confd_file, out: g_free (filebuf); - g_strfreev (lines); return parser; parse_fail: @@ -351,7 +555,6 @@ xorg_confd_parser_new (GFile *xorg_confd_file, "Unable to parse '%s'", parser->filename)); fail: g_free (filebuf); - g_strfreev (lines); xorg_confd_parser_free (parser); return NULL; } @@ -402,17 +605,24 @@ xorg_confd_parser_line_set_or_delete (GList *line, if (value == NULL || !g_strcmp0 (value, "")) { /* If value is null, we delete the line and return previous one */ - GList *prev = line->prev; - prev->next = line->next; - prev->next->prev = prev; + g_debug ("Deleting entry '%s'", entry->string); + GList *prev, *next; + + prev = line->prev; + next = line->next; line->prev = NULL; line->next = NULL; g_list_free_full (line, (GDestroyNotify)xorg_confd_line_entry_free); + if (prev != NULL) + prev->next = next; + if (next != NULL) + next->prev = prev; return prev; } entry->value = g_strdup (value); - replacement = g_strdup_printf ("\1\"%s\"", value); - replaced = g_regex_replace (re, entry->string, 0, 0, replacement, 0, NULL); + replacement = g_strdup_printf ("\\1\"%s\"", value); + replaced = g_regex_replace (re, entry->string, -1, 0, replacement, 0, NULL); + g_debug ("Setting entry '%s' to new value '%s' i.e. '%s'", entry->string, value, replaced); g_free (replacement); g_free (entry->string); entry->string = replaced; @@ -429,24 +639,25 @@ xorg_confd_parser_set_xkb (struct xorg_confd_parser *parser, { GList *curr = NULL, *end = NULL; gboolean layout_found = FALSE, model_found = FALSE, variant_found = FALSE, options_found = FALSE; + struct xorg_confd_line_entry *entry = NULL; + gchar *string = NULL; if (parser == NULL) return; if (parser->section == NULL) { - struct xorg_confd_line_entry *entry = NULL; GList *section = NULL; - entry = xorg_confd_line_entry_new ("Section \"InputClass\"\n", NULL, XORG_CONFD_LINE_TYPE_SECTION_INPUT_CLASS); + entry = xorg_confd_line_entry_new ("Section \"InputClass\"", NULL, XORG_CONFD_LINE_TYPE_SECTION_INPUT_CLASS); section = g_list_prepend (section, entry); - entry = xorg_confd_line_entry_new (" Identifier \"keyboard-all\"\n", NULL, XORG_CONFD_LINE_TYPE_UNKNOWN); + entry = xorg_confd_line_entry_new (" Identifier \"keyboard-all\"", NULL, XORG_CONFD_LINE_TYPE_UNKNOWN); section = g_list_prepend (section, entry); - entry = entry = xorg_confd_line_entry_new (" MatchIsKeyboard \"on\"\n", NULL, XORG_CONFD_LINE_TYPE_MATCH_IS_KEYBOARD); + entry = entry = xorg_confd_line_entry_new (" MatchIsKeyboard \"on\"", NULL, XORG_CONFD_LINE_TYPE_MATCH_IS_KEYBOARD); section = g_list_prepend (section, entry); - entry = entry = xorg_confd_line_entry_new ("EndSection\n", NULL, XORG_CONFD_LINE_TYPE_END_SECTION); + entry = entry = xorg_confd_line_entry_new ("EndSection", NULL, XORG_CONFD_LINE_TYPE_END_SECTION); section = g_list_prepend (section, entry); section = g_list_reverse (section); @@ -455,7 +666,7 @@ xorg_confd_parser_set_xkb (struct xorg_confd_parser *parser, } for (curr = parser->section; curr != NULL; curr = curr->next) { - struct xorg_confd_line_entry *entry = (struct xorg_confd_line_entry *) curr->data; + entry = (struct xorg_confd_line_entry *) curr->data; if (entry->type == XORG_CONFD_LINE_TYPE_END_SECTION) { end = curr; @@ -474,38 +685,31 @@ xorg_confd_parser_set_xkb (struct xorg_confd_parser *parser, curr = xorg_confd_parser_line_set_or_delete (curr, options, xorg_confd_line_xkb_options_re); } } - if (!layout_found && layout != NULL && g_strcmp0 (layout, "")) { - struct xorg_confd_line_entry *entry; - gchar *string; + if (!layout_found && layout != NULL && g_strcmp0 (layout, "")) { string = g_strdup_printf (" Option \"XkbLayout\" \"%s\"", layout); + g_debug ("Inserting new entry: '%s'", string); entry = xorg_confd_line_entry_new (string, layout, XORG_CONFD_LINE_TYPE_XKB_LAYOUT); parser->line_list = g_list_insert_before (parser->line_list, end, entry); g_free (string); } if (!model_found && model != NULL && g_strcmp0 (model, "")) { - struct xorg_confd_line_entry *entry; - gchar *string; - string = g_strdup_printf (" Option \"XkbModel\" \"%s\"", model); + g_debug ("Inserting new entry: '%s'", string); entry = xorg_confd_line_entry_new (string, model, XORG_CONFD_LINE_TYPE_XKB_MODEL); parser->line_list = g_list_insert_before (parser->line_list, end, entry); g_free (string); } if (!variant_found && variant != NULL && g_strcmp0 (variant, "")) { - struct xorg_confd_line_entry *entry; - gchar *string; - string = g_strdup_printf (" Option \"XkbVariant\" \"%s\"", variant); + g_debug ("Inserting new entry: '%s'", string); entry = xorg_confd_line_entry_new (string, variant, XORG_CONFD_LINE_TYPE_XKB_VARIANT); parser->line_list = g_list_insert_before (parser->line_list, end, entry); g_free (string); } if (!options_found && options != NULL && g_strcmp0 (options, "")) { - struct xorg_confd_line_entry *entry; - gchar *string; - string = g_strdup_printf (" Option \"XkbOptions\" \"%s\"", options); + g_debug ("Inserting new entry: '%s'", string); entry = xorg_confd_line_entry_new (string, options, XORG_CONFD_LINE_TYPE_XKB_OPTIONS); parser->line_list = g_list_insert_before (parser->line_list, end, entry); g_free (string); @@ -556,19 +760,268 @@ xorg_confd_parser_save (const struct xorg_confd_parser *parser, /* End of trivial /etc/X11/xorg.conf.d/30-keyboard.conf parser */ static gboolean +locale_name_is_valid (gchar *name) +{ + return g_regex_match_simple ("^[a-zA-Z0-9_.@-]*$", name, G_REGEX_MULTILINE, 0); +} + +struct invoked_locale { + GDBusMethodInvocation *invocation; + gchar **locale; /* newly allocated */ +}; + +static void +invoked_locale_free (struct invoked_locale *data) +{ + if (data == NULL) + return; + g_strfreev (data->locale); + g_free (data); +} + +static void +on_handle_set_locale_authorized_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GError *err = NULL; + struct invoked_locale *data; + gchar **loc, **var, **val, **locale_values = NULL; + ShellUtilsTrivial *locale_file_parsed = NULL; + + data = (struct invoked_locale *) user_data; + if (!check_polkit_finish (res, &err)) { + g_dbus_method_invocation_return_gerror (data->invocation, err); + goto out; + } + + G_LOCK (locale); + locale_values = g_new0 (gchar *, g_strv_length (locale_variables) + 1); + /* Don't allow unknown locale variables or invalid values */ + if (data->locale != NULL) { + for (loc = data->locale; *loc != NULL; loc++) { + gboolean found = FALSE; + for (val = locale_values, var = locale_variables; *var != NULL; val++, var++) { + size_t varlen; + gchar *unquoted = NULL; + + varlen = strlen (*var); + if (g_str_has_prefix (*loc, *var) && (*loc)[varlen] == '=' && + (unquoted = g_shell_unquote (*loc + varlen + 1, NULL)) != NULL && + locale_name_is_valid (unquoted)) { + found = TRUE; + if (*val != NULL) + g_free (*val); + *val = unquoted; + } else + g_free (unquoted); + } + if (!found) { + g_dbus_method_invocation_return_dbus_error (data->invocation, DBUS_ERROR_INVALID_ARGS, + "Invalid locale variable name or value"); + G_UNLOCK (locale); + goto out; + } + } + } + + if ((locale_file_parsed = shell_utils_trivial_new (locale_file, &err)) == NULL) { + g_dbus_method_invocation_return_gerror (data->invocation, err); + G_UNLOCK (locale); + goto out; + } + + if (shell_utils_trivial_is_empty (locale_file_parsed)) { + /* Simply write the new env file */ + shell_utils_trivial_free (locale_file_parsed); + if ((locale_file_parsed = shell_utils_trivial_new_from_string (locale_file, "# Configuration file for eselect\n# This file has been automatically generated\n", &err)) == NULL) { + g_dbus_method_invocation_return_gerror (data->invocation, err); + G_UNLOCK (locale); + goto out; + } + } + + for (val = locale_values, var = locale_variables; *var != NULL; val++, var++) { + if (*val == NULL) + shell_utils_trivial_clear_variable (locale_file_parsed, *var); + else + shell_utils_trivial_set_variable (locale_file_parsed, *var, *val, TRUE); + } + + if (!shell_utils_trivial_save (locale_file_parsed, &err)) { + g_dbus_method_invocation_return_gerror (data->invocation, err); + G_UNLOCK (locale); + goto out; + } + + g_strfreev (locale); + locale = g_new0 (gchar *, g_strv_length (locale_variables) + 1); + loc = locale; + for (val = locale_values, var = locale_variables; *var != NULL; val++, var++) { + if (*val != NULL) { + *loc = g_strdup_printf ("%s=%s", *var, *val); + loc++; + } + } + + openrc_settingsd_localed_locale1_complete_set_locale (locale1, data->invocation); + openrc_settingsd_localed_locale1_set_locale (locale1, (const gchar * const *) locale); + G_UNLOCK (locale); + + out: + shell_utils_trivial_free (locale_file_parsed); + g_strfreev (locale_values); + invoked_locale_free (data); + if (err != NULL) + g_error_free (err); +} + +static gboolean on_handle_set_locale (OpenrcSettingsdLocaledLocale1 *locale1, GDBusMethodInvocation *invocation, const gchar * const *_locale, const gboolean user_interaction, gpointer user_data) { - g_dbus_method_invocation_return_dbus_error (invocation, - DBUS_ERROR_NOT_SUPPORTED, - SERVICE_NAME " is in read-only mode"); + if (read_only) + g_dbus_method_invocation_return_dbus_error (invocation, + DBUS_ERROR_NOT_SUPPORTED, + SERVICE_NAME " is in read-only mode"); + else { + struct invoked_locale *data; + data = g_new0 (struct invoked_locale, 1); + data->invocation = invocation; + data->locale = g_strdupv ((gchar**)_locale); + check_polkit_async (g_dbus_method_invocation_get_sender (invocation), "org.freedesktop.locale1.set-locale", user_interaction, on_handle_set_locale_authorized_cb, data); + } return TRUE; } +struct invoked_vconsole_keyboard { + GDBusMethodInvocation *invocation; + gchar *vconsole_keymap; /* newly allocated */ + gchar *vconsole_keymap_toggle; /* newly allocated */ + gboolean convert; +}; + +static void +invoked_vconsole_keyboard_free (struct invoked_vconsole_keyboard *data) +{ + if (data == NULL) + return; + g_free (data->vconsole_keymap); + g_free (data->vconsole_keymap_toggle); + g_free (data); +} + +static void +on_handle_set_vconsole_keyboard_authorized_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GError *err = NULL; + struct invoked_vconsole_keyboard *data; + GList *kbd_model_map = NULL; + struct kbd_model_map_entry *best_entry = NULL; + struct xorg_confd_parser *x11_parser = NULL; + + data = (struct invoked_vconsole_keyboard *) user_data; + if (!check_polkit_finish (res, &err)) { + g_dbus_method_invocation_return_gerror (data->invocation, err); + goto out; + } + + G_LOCK (keymaps); + if (data->convert) { + GList *cur; + + G_LOCK (xorg_conf); + kbd_model_map = kbd_model_map_load (&err); + if (err != NULL) { + g_dbus_method_invocation_return_gerror (data->invocation, err); + goto unlock; + } + + for (cur = kbd_model_map; cur->next != NULL; cur = cur->next) { + struct kbd_model_map_entry *cur_entry = NULL; + cur_entry = (struct kbd_model_map_entry *) cur->data; + if (kbd_model_map_entry_matches_vconsole (cur_entry, data->vconsole_keymap)) { + best_entry = cur_entry; + break; + } + } + } + + /* We do not set vconsole_keymap_toggle because there is no good equivalent for it in OpenRC */ + if (!shell_utils_trivial_set_and_save (keymaps_file, &err, "keymap", NULL, data->vconsole_keymap, NULL)) { + g_dbus_method_invocation_return_gerror (data->invocation, err); + goto unlock; + } + + g_free (vconsole_keymap); + vconsole_keymap = g_strdup (data->vconsole_keymap); + openrc_settingsd_localed_locale1_set_vconsole_keymap (locale1, vconsole_keymap); + + if (data->convert) { + if (best_entry == NULL) { + gchar *filename; + filename = g_file_get_path (kbd_model_map_file); + g_printerr ("Failed to find conversion entry for console keymap '%s' in '%s'\n", data->vconsole_keymap, filename); + g_free (filename); + G_UNLOCK (xorg_conf); + } else { + unsigned int failure_score = 0; + + kbd_model_map_entry_matches_x11 (best_entry, x11_layout, x11_model, x11_variant, x11_options, &failure_score); + if (failure_score > 0) { + /* The xkb data has changed, so we want to update it */ + if (!g_file_query_exists (x11_gentoo_file, NULL) && g_file_query_exists (x11_systemd_file, NULL)) + x11_parser = xorg_confd_parser_new (x11_systemd_file, &err); + else + x11_parser = xorg_confd_parser_new (x11_gentoo_file, &err); + + if (x11_parser == NULL) { + g_dbus_method_invocation_return_gerror (data->invocation, err); + goto unlock; + } + xorg_confd_parser_set_xkb (x11_parser, best_entry->x11_layout, best_entry->x11_model, best_entry->x11_variant, best_entry->x11_options); + if (!xorg_confd_parser_save (x11_parser, &err)) { + g_dbus_method_invocation_return_gerror (data->invocation, err); + goto unlock; + } + g_free (x11_layout); + g_free (x11_model); + g_free (x11_variant); + g_free (x11_options); + x11_layout = g_strdup (best_entry->x11_layout); + x11_model = g_strdup (best_entry->x11_model); + x11_variant = g_strdup (best_entry->x11_variant); + x11_options = g_strdup (best_entry->x11_options); + openrc_settingsd_localed_locale1_set_x11_layout (locale1, x11_layout); + openrc_settingsd_localed_locale1_set_x11_model (locale1, x11_model); + openrc_settingsd_localed_locale1_set_x11_variant (locale1, x11_variant); + openrc_settingsd_localed_locale1_set_x11_options (locale1, x11_options); + } + } + } + /* We do not modify vconsole_keymap_toggle because there is no good equivalent for it in OpenRC */ + openrc_settingsd_localed_locale1_complete_set_vconsole_keyboard (locale1, data->invocation); + + unlock: + if (data->convert) + G_UNLOCK (xorg_conf); + G_UNLOCK (keymaps); + + out: + if (kbd_model_map != NULL) + g_list_free_full (kbd_model_map, (GDestroyNotify)kbd_model_map_entry_free); + xorg_confd_parser_free (x11_parser); + invoked_vconsole_keyboard_free (data); + if (err != NULL) + g_error_free (err); +} + static gboolean on_handle_set_vconsole_keyboard (OpenrcSettingsdLocaledLocale1 *locale1, GDBusMethodInvocation *invocation, @@ -578,13 +1031,146 @@ on_handle_set_vconsole_keyboard (OpenrcSettingsdLocaledLocale1 *locale1, const gboolean user_interaction, gpointer user_data) { - g_dbus_method_invocation_return_dbus_error (invocation, - DBUS_ERROR_NOT_SUPPORTED, - SERVICE_NAME " is in read-only mode"); + if (read_only) + g_dbus_method_invocation_return_dbus_error (invocation, + DBUS_ERROR_NOT_SUPPORTED, + SERVICE_NAME " is in read-only mode"); + else { + struct invoked_vconsole_keyboard *data; + data = g_new0 (struct invoked_vconsole_keyboard, 1); + data->invocation = invocation; + data->vconsole_keymap = g_strdup (keymap); + data->vconsole_keymap_toggle = g_strdup (keymap_toggle); + data->convert = convert; + check_polkit_async (g_dbus_method_invocation_get_sender (invocation), "org.freedesktop.locale1.set-keyboard", user_interaction, on_handle_set_vconsole_keyboard_authorized_cb, data); + } return TRUE; } +struct invoked_x11_keyboard { + GDBusMethodInvocation *invocation; + gchar *x11_layout; /* newly allocated */ + gchar *x11_model; /* newly allocated */ + gchar *x11_variant; /* newly allocated */ + gchar *x11_options; /* newly allocated */ + gboolean convert; +}; + +static void +invoked_x11_keyboard_free (struct invoked_x11_keyboard *data) +{ + if (data == NULL) + return; + g_free (data->x11_layout); + g_free (data->x11_model); + g_free (data->x11_variant); + g_free (data->x11_options); + g_free (data); +} + +static void +on_handle_set_x11_keyboard_authorized_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GError *err = NULL; + struct invoked_x11_keyboard *data; + GList *kbd_model_map = NULL; + struct kbd_model_map_entry *best_entry = NULL; + unsigned int best_failure_score = UINT_MAX; + struct xorg_confd_parser *x11_parser = NULL; + + data = (struct invoked_x11_keyboard *) user_data; + if (!check_polkit_finish (res, &err)) { + g_dbus_method_invocation_return_gerror (data->invocation, err); + goto out; + } + + G_LOCK (xorg_conf); + if (data->convert) { + GList *cur; + + G_LOCK (keymaps); + kbd_model_map = kbd_model_map_load (&err); + if (err != NULL) { + g_dbus_method_invocation_return_gerror (data->invocation, err); + goto unlock; + } + + for (cur = kbd_model_map; cur->next != NULL; cur = cur->next) { + struct kbd_model_map_entry *cur_entry = NULL; + unsigned int cur_failure_score = 0; + + cur_entry = (struct kbd_model_map_entry *) cur->data; + if (kbd_model_map_entry_matches_x11 (cur_entry, data->x11_layout, data->x11_model, data->x11_variant, data->x11_options, &cur_failure_score)) + if (cur_failure_score < best_failure_score) { + best_entry = cur_entry; + best_failure_score = cur_failure_score; + } + } + } + + if (!g_file_query_exists (x11_gentoo_file, NULL) && g_file_query_exists (x11_systemd_file, NULL)) + x11_parser = xorg_confd_parser_new (x11_systemd_file, &err); + else + x11_parser = xorg_confd_parser_new (x11_gentoo_file, &err); + + if (x11_parser == NULL) { + g_dbus_method_invocation_return_gerror (data->invocation, err); + goto unlock; + } + xorg_confd_parser_set_xkb (x11_parser, data->x11_layout, data->x11_model, data->x11_variant, data->x11_options); + if (!xorg_confd_parser_save (x11_parser, &err)) { + g_dbus_method_invocation_return_gerror (data->invocation, err); + goto unlock; + } + g_free (x11_layout); + g_free (x11_model); + g_free (x11_variant); + g_free (x11_options); + x11_layout = g_strdup (data->x11_layout); + x11_model = g_strdup (data->x11_model); + x11_variant = g_strdup (data->x11_variant); + x11_options = g_strdup (data->x11_options); + openrc_settingsd_localed_locale1_set_x11_layout (locale1, x11_layout); + openrc_settingsd_localed_locale1_set_x11_model (locale1, x11_model); + openrc_settingsd_localed_locale1_set_x11_variant (locale1, x11_variant); + openrc_settingsd_localed_locale1_set_x11_options (locale1, x11_options); + + if (data->convert) { + if (best_entry == NULL) { + gchar *filename; + filename = g_file_get_path (kbd_model_map_file); + g_printerr ("Failed to find conversion entry for x11 layout '%s' in '%s'\n", data->x11_layout, filename); + g_free (filename); + } else { + if (!shell_utils_trivial_set_and_save (keymaps_file, &err, "keymap", NULL, best_entry->vconsole_keymap, NULL)) { + g_dbus_method_invocation_return_gerror (data->invocation, err); + goto unlock; + } + g_free (vconsole_keymap); + vconsole_keymap = g_strdup (best_entry->vconsole_keymap); + openrc_settingsd_localed_locale1_set_vconsole_keymap (locale1, vconsole_keymap); + } + } + + openrc_settingsd_localed_locale1_complete_set_x11_keyboard (locale1, data->invocation); + + unlock: + if (data->convert) + G_UNLOCK (keymaps); + G_UNLOCK (xorg_conf); + + out: + if (kbd_model_map != NULL) + g_list_free_full (kbd_model_map, (GDestroyNotify)kbd_model_map_entry_free); + xorg_confd_parser_free (x11_parser); + invoked_x11_keyboard_free (data); + if (err != NULL) + g_error_free (err); +} + static gboolean on_handle_set_x11_keyboard (OpenrcSettingsdLocaledLocale1 *locale1, GDBusMethodInvocation *invocation, @@ -596,9 +1182,21 @@ on_handle_set_x11_keyboard (OpenrcSettingsdLocaledLocale1 *locale1, const gboolean user_interaction, gpointer user_data) { - g_dbus_method_invocation_return_dbus_error (invocation, - DBUS_ERROR_NOT_SUPPORTED, - SERVICE_NAME " is in read-only mode"); + if (read_only) + g_dbus_method_invocation_return_dbus_error (invocation, + DBUS_ERROR_NOT_SUPPORTED, + SERVICE_NAME " is in read-only mode"); + else { + struct invoked_x11_keyboard *data; + data = g_new0 (struct invoked_x11_keyboard, 1); + data->invocation = invocation; + data->x11_layout = g_strdup (layout); + data->x11_model = g_strdup (model); + data->x11_variant = g_strdup (variant); + data->x11_options = g_strdup (options); + data->convert = convert; + check_polkit_async (g_dbus_method_invocation_get_sender (invocation), "org.freedesktop.locale1.set-keyboard", user_interaction, on_handle_set_x11_keyboard_authorized_cb, data); + } return TRUE; } @@ -666,6 +1264,7 @@ localed_init (gboolean _read_only) struct xorg_confd_parser *x11_parser = NULL; read_only = _read_only; + kbd_model_map_file = g_file_new_for_path (PKGDATADIR "/kbd-model-map"); locale_file = g_file_new_for_path (SYSCONFDIR "/env.d/02locale"); keymaps_file = g_file_new_for_path (SYSCONFDIR "/conf.d/keymaps"); @@ -703,6 +1302,7 @@ localed_init (gboolean _read_only) /* We don't have a good equivalent for this in openrc at the moment */ vconsole_keymap_toggle = g_strdup (""); + kbd_model_map_regex_init (); xorg_confd_regex_init (); if (!g_file_query_exists (x11_gentoo_file, NULL) && g_file_query_exists (x11_systemd_file, NULL)) @@ -735,6 +1335,7 @@ localed_destroy (void) bus_id = 0; read_only = FALSE; g_strfreev (locale); + kbd_model_map_regex_destroy (); xorg_confd_regex_destroy (); g_free (vconsole_keymap); g_free (vconsole_keymap_toggle); @@ -747,4 +1348,5 @@ localed_destroy (void) g_object_unref (keymaps_file); g_object_unref (x11_gentoo_file); g_object_unref (x11_systemd_file); + g_object_unref (kbd_model_map_file); } diff --git a/src/shell-utils.c b/src/shell-utils.c index db67303..545c5dd 100644 --- a/src/shell-utils.c +++ b/src/shell-utils.c @@ -131,31 +131,51 @@ shell_utils_trivial_new (GFile *file, GError **error) { gchar *filebuf = NULL; - ShellUtilsTrivial *ret = NULL; GError *local_err = NULL; - gchar *s; if (file == NULL) return NULL; - ret = g_new0 (ShellUtilsTrivial, 1); - g_object_ref (file); - ret->file = file; - ret->filename = g_file_get_path (file); - if (!g_file_load_contents (file, NULL, &filebuf, NULL, NULL, &local_err)) { if (local_err != NULL) { /* Inability to parse or open is a failure; file not existing at all is *not* a failure */ if (local_err->code == G_IO_ERROR_NOT_FOUND) { + ShellUtilsTrivial *ret = NULL; + g_error_free (local_err); + ret = g_new0 (ShellUtilsTrivial, 1); + g_object_ref (file); + ret->file = file; + ret->filename = g_file_get_path (file); return ret; + } else { + gchar *filename; + filename = g_file_get_path (file); + g_propagate_prefixed_error (error, local_err, "Unable to read '%s':", filename); + g_free (filename); } - - g_propagate_prefixed_error (error, local_err, "Unable to read '%s':", ret->filename); } - shell_utils_trivial_free (ret); return NULL; } + return shell_utils_trivial_new_from_string (file, filebuf, error); +} + +ShellUtilsTrivial * +shell_utils_trivial_new_from_string (GFile *file, + gchar *filebuf, + GError **error) +{ + ShellUtilsTrivial *ret = NULL; + GError *local_err = NULL; + gchar *s; + + if (file == NULL || filebuf == NULL) + return NULL; + + ret = g_new0 (ShellUtilsTrivial, 1); + g_object_ref (file); + ret->file = file; + ret->filename = g_file_get_path (file); gboolean want_separator = FALSE; /* Do we expect the next entry to be a separator or comment? */ s = filebuf; @@ -308,6 +328,14 @@ no_match: } gboolean +shell_utils_trivial_is_empty (ShellUtilsTrivial *trivial) +{ + if (trivial == NULL || trivial->entry_list == NULL) + return TRUE; + return FALSE; +} + +gboolean shell_utils_trivial_set_variable (ShellUtilsTrivial *trivial, const gchar *variable, const gchar *value, @@ -350,6 +378,38 @@ shell_utils_trivial_set_variable (ShellUtilsTrivial *trivial, return ret; } +void +shell_utils_trivial_clear_variable (ShellUtilsTrivial *trivial, + const gchar *variable) +{ + GList *curr = NULL; + gboolean ret = FALSE; + + g_assert (trivial != NULL); + g_assert (variable != NULL); + + for (curr = trivial->entry_list; curr != NULL; ) { + struct ShellEntry *entry; + + entry = (struct ShellEntry *)(curr->data); + if (entry->type == SHELL_ENTRY_TYPE_ASSIGNMENT && g_strcmp0 (variable, entry->variable) == 0) { + GList *prev, *next; + + prev = curr->prev; + next = curr->next; + curr->prev = NULL; + curr->next = NULL; + g_list_free_full (curr, (GDestroyNotify)shell_entry_free); + if (prev != NULL) + prev->next = next; + if (next != NULL) + next->prev = prev; + curr = next; + } else + curr = curr->next; + } +} + gboolean shell_utils_trivial_save (ShellUtilsTrivial *trivial, GError **error) diff --git a/src/shell-utils.h b/src/shell-utils.h index 9ca40e9..629d017 100644 --- a/src/shell-utils.h +++ b/src/shell-utils.h @@ -22,7 +22,7 @@ #include <glib.h> #include <gio/gio.h> -typedef struct _ShellUtilsTrivial ShellUtilsTrivial; +typedef struct _ShellUtilsTrivial ShellUtilsTrivial; struct _ShellUtilsTrivial { @@ -46,15 +46,27 @@ ShellUtilsTrivial * shell_utils_trivial_new (GFile *file, GError **error); +ShellUtilsTrivial * +shell_utils_trivial_new_from_string (GFile *file, + gchar *filebuf, + GError **error); + void shell_utils_trivial_free (ShellUtilsTrivial *trivial); gboolean +shell_utils_trivial_is_empty (ShellUtilsTrivial *trivial); + +gboolean shell_utils_trivial_set_variable (ShellUtilsTrivial *trivial, const gchar *variable, const gchar *value, gboolean add_if_unset); +void +shell_utils_trivial_clear_variable (ShellUtilsTrivial *trivial, + const gchar *variable); + gboolean shell_utils_trivial_save (ShellUtilsTrivial *trivial, GError **error); |