summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/settings/plugins/ifcfg-rh/shvar.c136
1 files changed, 88 insertions, 48 deletions
diff --git a/src/settings/plugins/ifcfg-rh/shvar.c b/src/settings/plugins/ifcfg-rh/shvar.c
index cb0eebcc3a..f9f3550909 100644
--- a/src/settings/plugins/ifcfg-rh/shvar.c
+++ b/src/settings/plugins/ifcfg-rh/shvar.c
@@ -25,8 +25,17 @@
struct _shvarLine {
+ const char *key;
+
CList lst;
+ /* We index variables by their key in shvarFile.lst_idx. One shell variable might
+ * occur multiple times in a file (in which case the last occurrence wins).
+ * Hence, we need to keep a list of all the same keys.
+ *
+ * This is a pointer to the next shadowed line. */
+ struct _shvarLine *next_shadowed;
+
/* There are three cases:
*
* 1) the line is not a valid variable assignment (that is, it doesn't
@@ -44,7 +53,6 @@ struct _shvarLine {
* @key/@key_with_prefix.
* */
char *line;
- const char *key;
char *key_with_prefix;
/* svSetValue() will clear the dirty flag. */
@@ -54,10 +62,11 @@ struct _shvarLine {
typedef struct _shvarLine shvarLine;
struct _shvarFile {
- char *fileName;
- int fd;
- CList lst_head;
- gboolean modified;
+ char *fileName;
+ CList lst_head;
+ GHashTable *lst_idx;
+ int fd;
+ bool modified:1;
};
/*****************************************************************************/
@@ -629,6 +638,7 @@ svFile_new (const char *name)
s->fd = -1;
s->fileName = g_strdup (name);
c_list_init (&s->lst_head);
+ s->lst_idx = g_hash_table_new (nm_pstr_hash, nm_pstr_equal);
return s;
}
@@ -690,8 +700,8 @@ line_new_parse (const char *value, gsize len)
line = g_slice_new (shvarLine);
*line = (shvarLine) {
- .lst = C_LIST_INIT (line->lst),
- .dirty = TRUE,
+ .lst = C_LIST_INIT (line->lst),
+ .dirty = TRUE,
};
for (k = 0; k < len; k++) {
@@ -780,14 +790,49 @@ static void
line_free (shvarLine *line)
{
ASSERT_shvarLine (line);
+ c_list_unlink_stale (&line->lst);
g_free (line->line);
g_free (line->key_with_prefix);
- c_list_unlink_stale (&line->lst);
g_slice_free (shvarLine, line);
}
/*****************************************************************************/
+static void
+_line_link_parse (shvarFile *s, const char *value, gsize len)
+{
+ shvarLine *line;
+
+ line = line_new_parse (value, len);
+ if (!line->key) {
+ c_list_link_tail (&s->lst_head, &line->lst);
+ return;
+ }
+
+ if (G_UNLIKELY (!g_hash_table_insert (s->lst_idx, line, line))) {
+ shvarLine *existing_key;
+ shvarLine *existing_val;
+
+ /* Slow-path: we have duplicate keys. Fix the mess we created.
+ * Unfortunately, g_hash_table_insert() now had to allocate a special
+ * array to track the keys/values differently. I wish there was an
+ * API to add a key only if it does not exist yet. */
+
+ if (!g_hash_table_lookup_extended (s->lst_idx, line, (gpointer *) &existing_key, (gpointer *) &existing_val))
+ nm_assert_not_reached ();
+
+ nm_assert (existing_val == line);
+ nm_assert (existing_key != line);
+ line->next_shadowed = existing_key;
+ g_hash_table_replace (s->lst_idx, line, line);
+ }
+
+ c_list_link_tail (&s->lst_head, &line->lst);
+}
+
+/*****************************************************************************/
+
+
/* Open the file <name>, returning a shvarFile on success and NULL on failure.
* Add a wrinkle to let the caller specify whether or not to create the file
* (actually, return a structure anyway) if it doesn't exist.
@@ -845,9 +890,9 @@ svOpenFileInternal (const char *name, gboolean create, GError **error)
s = svFile_new (name);
for (p = arena; (q = strchr (p, '\n')) != NULL; p = q + 1)
- c_list_link_tail (&s->lst_head, &line_new_parse (p, q - p)->lst);
+ _line_link_parse (s, p, q - p);
if (p[0])
- c_list_link_tail (&s->lst_head, &line_new_parse (p, strlen (p))->lst);
+ _line_link_parse (s, p, strlen (p));
/* closefd is set if we opened the file read-only, so go ahead and
* close it, because we can't write to it anyway */
@@ -1000,16 +1045,16 @@ svGetKeysSorted (shvarFile *s,
const char *
svFindFirstNumberedKey (shvarFile *s, const char *key_prefix)
{
- const shvarLine *l;
+ const shvarLine *line;
g_return_val_if_fail (s, NULL);
g_return_val_if_fail (key_prefix, NULL);
- c_list_for_each_entry (l, &s->lst_head, lst) {
- if ( l->key
- && l->line
- && nms_ifcfg_rh_util_is_numbered_tag (l->key, key_prefix, NULL))
- return l->key;
+ c_list_for_each_entry (line, &s->lst_head, lst) {
+ if ( line->key
+ && line->line
+ && nms_ifcfg_rh_util_is_numbered_tag (line->key, key_prefix, NULL))
+ return line->key;
}
return NULL;
@@ -1020,8 +1065,7 @@ svFindFirstNumberedKey (shvarFile *s, const char *key_prefix)
static const char *
_svGetValue (shvarFile *s, const char *key, char **to_free)
{
- CList *current;
- const shvarLine *line, *l;
+ const shvarLine *line;
const char *v;
nm_assert (s);
@@ -1030,12 +1074,7 @@ _svGetValue (shvarFile *s, const char *key, char **to_free)
ASSERT_key_is_well_known (key);
- line = NULL;
- c_list_for_each (current, &s->lst_head) {
- l = c_list_entry (current, shvarLine, lst);
- if (l->key && nm_streq (l->key, key))
- line = l;
- }
+ line = g_hash_table_lookup (s->lst_idx, &key);
if (line && line->line) {
v = svUnescape (line->line, to_free);
@@ -1226,19 +1265,15 @@ svGetValueEnum (shvarFile *s, const char *key,
gboolean
svUnsetAll (shvarFile *s, SvKeyType match_key_type)
{
- CList *current;
shvarLine *line;
gboolean changed = FALSE;
g_return_val_if_fail (s, FALSE);
- c_list_for_each (current, &s->lst_head) {
- line = c_list_entry (current, shvarLine, lst);
+ c_list_for_each_entry (line, &s->lst_head, lst) {
ASSERT_shvarLine (line);
- if (!line->key)
- continue;
-
- if (_svKeyMatchesType (line->key, match_key_type)) {
+ if ( line->key
+ && _svKeyMatchesType (line->key, match_key_type)) {
if (nm_clear_g_free (&line->line)) {
ASSERT_shvarLine (line);
changed = TRUE;
@@ -1289,8 +1324,7 @@ svUnsetDirtyWellknown (shvarFile *s, NMTernary new_dirty_value)
gboolean
svSetValue (shvarFile *s, const char *key, const char *value)
{
- CList *current;
- shvarLine *line, *l;
+ shvarLine *line;
gboolean changed = FALSE;
g_return_val_if_fail (s, FALSE);
@@ -1300,17 +1334,19 @@ svSetValue (shvarFile *s, const char *key, const char *value)
ASSERT_key_is_well_known (key);
- line = NULL;
- c_list_for_each (current, &s->lst_head) {
- l = c_list_entry (current, shvarLine, lst);
- if (l->key && nm_streq (l->key, key)) {
- if (line) {
- /* if we find multiple entries for the same key, we can
- * delete all but the last. */
- line_free (line);
- changed = TRUE;
- }
- line = l;
+ line = g_hash_table_lookup (s->lst_idx, &key);
+ if (line) {
+ shvarLine *l_curr;
+
+ /* if we find multiple entries for the same key, we can
+ * delete all but the last. */
+ l_curr = g_steal_pointer (&line->next_shadowed);
+ while (l_curr) {
+ shvarLine *l = l_curr;
+
+ l_curr = l_curr->next_shadowed;
+ line_free (l);
+ changed = TRUE;
}
}
@@ -1324,7 +1360,10 @@ svSetValue (shvarFile *s, const char *key, const char *value)
}
} else {
if (!line) {
- c_list_link_tail (&s->lst_head, &line_new_build (key, value)->lst);
+ line = line_new_build (key, value);
+ if (!g_hash_table_add (s->lst_idx, line))
+ nm_assert_not_reached ();
+ c_list_link_tail (&s->lst_head, &line->lst);
changed = TRUE;
} else {
if (line_set (line, value))
@@ -1476,14 +1515,15 @@ svWriteFile (shvarFile *s, int mode, GError **error)
void
svCloseFile (shvarFile *s)
{
- CList *current, *safe;
+ shvarLine *line;
g_return_if_fail (s != NULL);
if (s->fd >= 0)
nm_close (s->fd);
g_free (s->fileName);
- c_list_for_each_safe (current, safe, &s->lst_head)
- line_free (c_list_entry (current, shvarLine, lst));
+ g_hash_table_destroy (s->lst_idx);
+ while ((line = c_list_first_entry (&s->lst_head, shvarLine, lst)))
+ line_free (line);
g_slice_free (shvarFile, s);
}