summary refs log tree commit diff
path: root/src/settings/plugins/ifcfg-rh/shvar.c
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2020-10-05 22:27:18 +0200
committerMichael Biebl <biebl@debian.org>2020-10-05 22:27:18 +0200
commitaafc1dbe4712c86189bbc1d4d54ad8cb4c69be7e (patch)
treea2a9bb4d007339a0b1304540388230ccedac32bd /src/settings/plugins/ifcfg-rh/shvar.c
parente7b44ef4c80907346ec7492a09c45277459924fc (diff)
New upstream version 1.27.90 upstream/1.27.90
Diffstat (limited to 'src/settings/plugins/ifcfg-rh/shvar.c')
-rw-r--r--src/settings/plugins/ifcfg-rh/shvar.c2409
1 files changed, 1220 insertions, 1189 deletions
diff --git a/src/settings/plugins/ifcfg-rh/shvar.c b/src/settings/plugins/ifcfg-rh/shvar.c
index cd63661d..8f5be2cc 100644
--- a/src/settings/plugins/ifcfg-rh/shvar.c
+++ b/src/settings/plugins/ifcfg-rh/shvar.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
 /*
  * Copyright (C) 1999, 2000 Red Hat, Inc.
  */
@@ -24,69 +24,68 @@
 /*****************************************************************************/
 
 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 *prev_shadowed;
-
-	/* There are three cases:
-	 *
-	 * 1) the line is not a valid variable assignment (that is, it doesn't
-	 *   start with a "FOO=" with possible whitespace prefix).
-	 *   In that case, @key and @key_with_prefix are %NULL, and the entire
-	 *   original line is in @line. Such entries are ignored for the most part.
-	 *
-	 * 2) if the line can be parsed with a "FOO=" assignment, then @line contains
-	 *   the part after '=', @key_with_prefix contains the key "FOO" with possible
-	 *   whitespace prefix, and @key points into @key_with_prefix skipping over the
-	 *   whitespace.
-	 *
-	 * 3) like 2, but if the value was deleted via svSetValue(), the entry is not removed,
-	 *   but only marked for deletion. That is done by clearing @line but preserving
-	 *   @key/@key_with_prefix.
-	 * */
-	char *line;
-	char *key_with_prefix;
-
-	/* svSetValue() will clear the dirty flag. */
-	bool dirty:1;
+    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 *prev_shadowed;
+
+    /* There are three cases:
+     *
+     * 1) the line is not a valid variable assignment (that is, it doesn't
+     *   start with a "FOO=" with possible whitespace prefix).
+     *   In that case, @key and @key_with_prefix are %NULL, and the entire
+     *   original line is in @line. Such entries are ignored for the most part.
+     *
+     * 2) if the line can be parsed with a "FOO=" assignment, then @line contains
+     *   the part after '=', @key_with_prefix contains the key "FOO" with possible
+     *   whitespace prefix, and @key points into @key_with_prefix skipping over the
+     *   whitespace.
+     *
+     * 3) like 2, but if the value was deleted via svSetValue(), the entry is not removed,
+     *   but only marked for deletion. That is done by clearing @line but preserving
+     *   @key/@key_with_prefix.
+     * */
+    char *line;
+    char *key_with_prefix;
+
+    /* svSetValue() will clear the dirty flag. */
+    bool dirty : 1;
 };
 
 typedef struct _shvarLine shvarLine;
 
 struct _shvarFile {
-	char *fileName;
-	CList lst_head;
-	GHashTable *lst_idx;
-	int fd;
-	bool modified:1;
+    char *      fileName;
+    CList       lst_head;
+    GHashTable *lst_idx;
+    int         fd;
+    bool        modified : 1;
 };
 
 /*****************************************************************************/
 
-static void _line_link_parse (shvarFile *s, const char *value, gsize len);
+static void _line_link_parse(shvarFile *s, const char *value, gsize len);
 
 /*****************************************************************************/
 
-#define ASSERT_key_is_well_known(key) \
-	nm_assert ( ({ \
-		const char *_key = (key); \
-		gboolean _is_wellknown = TRUE; \
-		\
-		if (!nms_ifcfg_rh_utils_is_well_known_key (_key)) { \
-			_is_wellknown = FALSE; \
-			g_critical ("ifcfg-rh key \"%s\" is not well-known", _key); \
-		} \
-		\
-		_is_wellknown; \
-	}) )
+#define ASSERT_key_is_well_known(key)                                  \
+    nm_assert(({                                                       \
+        const char *_key          = (key);                             \
+        gboolean    _is_wellknown = TRUE;                              \
+                                                                       \
+        if (!nms_ifcfg_rh_utils_is_well_known_key(_key)) {             \
+            _is_wellknown = FALSE;                                     \
+            g_critical("ifcfg-rh key \"%s\" is not well-known", _key); \
+        }                                                              \
+                                                                       \
+        _is_wellknown;                                                 \
+    }))
 
 /**
  * svParseBoolean:
@@ -100,52 +99,46 @@ static void _line_link_parse (shvarFile *s, const char *value, gsize len);
  * Returns: the parsed boolean value or @fallback.
  */
 int
-svParseBoolean (const char *value, int fallback)
+svParseBoolean(const char *value, int fallback)
 {
-	if (!value)
-		return fallback;
-
-	if (   !g_ascii_strcasecmp ("yes", value)
-	    || !g_ascii_strcasecmp ("true", value)
-	    || !g_ascii_strcasecmp ("t", value)
-	    || !g_ascii_strcasecmp ("y", value)
-	    || !g_ascii_strcasecmp ("1", value))
-		return TRUE;
-	else if (   !g_ascii_strcasecmp ("no", value)
-	         || !g_ascii_strcasecmp ("false", value)
-	         || !g_ascii_strcasecmp ("f", value)
-	         || !g_ascii_strcasecmp ("n", value)
-	         || !g_ascii_strcasecmp ("0", value))
-		return FALSE;
-
-	return fallback;
+    if (!value)
+        return fallback;
+
+    if (!g_ascii_strcasecmp("yes", value) || !g_ascii_strcasecmp("true", value)
+        || !g_ascii_strcasecmp("t", value) || !g_ascii_strcasecmp("y", value)
+        || !g_ascii_strcasecmp("1", value))
+        return TRUE;
+    else if (!g_ascii_strcasecmp("no", value) || !g_ascii_strcasecmp("false", value)
+             || !g_ascii_strcasecmp("f", value) || !g_ascii_strcasecmp("n", value)
+             || !g_ascii_strcasecmp("0", value))
+        return FALSE;
+
+    return fallback;
 }
 
 /*****************************************************************************/
 
 static gboolean
-_shell_is_name (const char *key, gssize len)
+_shell_is_name(const char *key, gssize len)
 {
-	gssize i;
-
-	/* whether @key is a valid identifier (name). */
-	if (!key || len == 0)
-		return FALSE;
-	if (   !g_ascii_isalpha (key[0])
-	    && key[0] != '_')
-		return FALSE;
-	for (i = 1; TRUE; i++) {
-		if (len < 0) {
-			if (!key[i])
-				return TRUE;
-		} else {
-			if (i >= len)
-				return TRUE;
-		}
-		if (   !g_ascii_isalnum (key[i])
-		    && key[i] != '_')
-			return FALSE;
-	}
+    gssize i;
+
+    /* whether @key is a valid identifier (name). */
+    if (!key || len == 0)
+        return FALSE;
+    if (!g_ascii_isalpha(key[0]) && key[0] != '_')
+        return FALSE;
+    for (i = 1; TRUE; i++) {
+        if (len < 0) {
+            if (!key[i])
+                return TRUE;
+        } else {
+            if (i >= len)
+                return TRUE;
+        }
+        if (!g_ascii_isalnum(key[i]) && key[i] != '_')
+            return FALSE;
+    }
 }
 
 /*****************************************************************************/
@@ -154,959 +147,964 @@ _shell_is_name (const char *key, gssize len)
  *
  * While at it, add $''. */
 static char *
-_escape_ansic (const char *source)
+_escape_ansic(const char *source)
 {
-	const char *p;
-	char *dest;
-	char *q;
-
-	nm_assert (source);
-
-	p = (const char *) source;
-	/* Each source byte needs maximally four destination chars (\777) */
-	q = dest = g_malloc (strlen (source) * 4 + 1 + 3);
-
-	*q++ = '$';
-	*q++ = '\'';
-
-	while (*p) {
-		switch (*p) {
-		case '\b':
-			*q++ = '\\';
-			*q++ = 'b';
-			break;
-		case '\f':
-			*q++ = '\\';
-			*q++ = 'f';
-			break;
-		case '\n':
-			*q++ = '\\';
-			*q++ = 'n';
-			break;
-		case '\r':
-			*q++ = '\\';
-			*q++ = 'r';
-			break;
-		case '\t':
-			*q++ = '\\';
-			*q++ = 't';
-			break;
-		case '\v':
-			*q++ = '\\';
-			*q++ = 'v';
-			break;
-		case '\\':
-		case '"':
-		case '\'':
-			*q++ = '\\';
-			*q++ = *p;
-			break;
-		default:
-			if ((*p < ' ') || (*p >= 0177)) {
-				*q++ = '\\';
-				*q++ = '0' + (((*p) >> 6) & 07);
-				*q++ = '0' + (((*p) >> 3) & 07);
-				*q++ = '0' + ((*p) & 07);
-			} else
-				*q++ = *p;
-			break;
-		}
-		p++;
-	}
-	*q++ = '\'';
-	*q++ = '\0';
-
-	nm_assert (q - dest <= strlen (source) * 4 + 1 + 3);
-
-	return dest;
+    const char *p;
+    char *      dest;
+    char *      q;
+
+    nm_assert(source);
+
+    p = (const char *) source;
+    /* Each source byte needs maximally four destination chars (\777) */
+    q = dest = g_malloc(strlen(source) * 4 + 1 + 3);
+
+    *q++ = '$';
+    *q++ = '\'';
+
+    while (*p) {
+        switch (*p) {
+        case '\b':
+            *q++ = '\\';
+            *q++ = 'b';
+            break;
+        case '\f':
+            *q++ = '\\';
+            *q++ = 'f';
+            break;
+        case '\n':
+            *q++ = '\\';
+            *q++ = 'n';
+            break;
+        case '\r':
+            *q++ = '\\';
+            *q++ = 'r';
+            break;
+        case '\t':
+            *q++ = '\\';
+            *q++ = 't';
+            break;
+        case '\v':
+            *q++ = '\\';
+            *q++ = 'v';
+            break;
+        case '\\':
+        case '"':
+        case '\'':
+            *q++ = '\\';
+            *q++ = *p;
+            break;
+        default:
+            if ((*p < ' ') || (*p >= 0177)) {
+                *q++ = '\\';
+                *q++ = '0' + (((*p) >> 6) & 07);
+                *q++ = '0' + (((*p) >> 3) & 07);
+                *q++ = '0' + ((*p) & 07);
+            } else
+                *q++ = *p;
+            break;
+        }
+        p++;
+    }
+    *q++ = '\'';
+    *q++ = '\0';
+
+    nm_assert(q - dest <= strlen(source) * 4 + 1 + 3);
+
+    return dest;
 }
 
 /*****************************************************************************/
 
-#define _char_req_escape(ch)        NM_IN_SET (ch,      '"', '\\',       '$', '`')
-#define _char_req_escape_old(ch)    NM_IN_SET (ch,      '"', '\\', '\'', '$', '`', '~')
-#define _char_req_quotes(ch)        NM_IN_SET (ch, ' ',            '\'',           '~', '\t', '|', '&', ';', '(', ')', '<', '>')
+#define _char_req_escape(ch)     NM_IN_SET(ch, '"', '\\', '$', '`')
+#define _char_req_escape_old(ch) NM_IN_SET(ch, '"', '\\', '\'', '$', '`', '~')
+#define _char_req_quotes(ch)     NM_IN_SET(ch, ' ', '\'', '~', '\t', '|', '&', ';', '(', ')', '<', '>')
 
 const char *
-svEscape (const char *s, char **to_free)
+svEscape(const char *s, char **to_free)
 {
-	char *new;
-	gsize mangle = 0;
-	gboolean requires_quotes = FALSE;
-	int newlen;
-	size_t i, j, slen;
-
-	for (slen = 0; s[slen]; slen++) {
-		if (_char_req_escape (s[slen]))
-			mangle++;
-		else if (_char_req_quotes (s[slen]))
-			requires_quotes = TRUE;
-		else if (s[slen] < ' ') {
-			/* if the string contains newline we can only express it using ANSI C quotation
-			 * (as we don't support line continuation).
-			 * Additionally, ANSI control characters look odd with regular quotation, so handle
-			 * them too. */
-			return (*to_free = _escape_ansic (s));
-		}
-	}
-	if (!mangle && !requires_quotes) {
-		*to_free = NULL;
-		return s;
-	}
-
-	newlen = slen + mangle + 3; /* 3 is extra ""\0 */
-	new = g_malloc (newlen);
-
-	j = 0;
-	new[j++] = '"';
-	for (i = 0; i < slen; i++) {
-		if (_char_req_escape (s[i]))
-			new[j++] = '\\';
-		new[j++] = s[i];
-	}
-	new[j++] = '"';
-	new[j++] = '\0';
-
-	nm_assert (j == slen + mangle + 3);
-
-	*to_free = new;
-	return new;
+    char *new;
+    gsize    mangle          = 0;
+    gboolean requires_quotes = FALSE;
+    int      newlen;
+    size_t   i, j, slen;
+
+    for (slen = 0; s[slen]; slen++) {
+        if (_char_req_escape(s[slen]))
+            mangle++;
+        else if (_char_req_quotes(s[slen]))
+            requires_quotes = TRUE;
+        else if (s[slen] < ' ') {
+            /* if the string contains newline we can only express it using ANSI C quotation
+             * (as we don't support line continuation).
+             * Additionally, ANSI control characters look odd with regular quotation, so handle
+             * them too. */
+            return (*to_free = _escape_ansic(s));
+        }
+    }
+    if (!mangle && !requires_quotes) {
+        *to_free = NULL;
+        return s;
+    }
+
+    newlen = slen + mangle + 3; /* 3 is extra ""\0 */
+    new    = g_malloc(newlen);
+
+    j        = 0;
+    new[j++] = '"';
+    for (i = 0; i < slen; i++) {
+        if (_char_req_escape(s[i]))
+            new[j++] = '\\';
+        new[j++] = s[i];
+    }
+    new[j++] = '"';
+    new[j++] = '\0';
+
+    nm_assert(j == slen + mangle + 3);
+
+    *to_free = new;
+    return new;
 }
 
 static gboolean
-_looks_like_old_svescaped (const char *value)
+_looks_like_old_svescaped(const char *value)
 {
-	gsize k;
-
-	if (value[0] != '"')
-		return FALSE;
-
-	for (k = 1; ; k++) {
-		if (value[k] == '\0')
-			return FALSE;
-		if (!_char_req_escape_old (value[k]))
-			continue;
-
-		if (value[k] == '"')
-			return (value[k + 1] == '\0');
-		else if (value[k] == '\\') {
-			k++;
-			if (!_char_req_escape_old (value[k]))
-				return FALSE;
-		} else
-			return FALSE;
-	}
+    gsize k;
+
+    if (value[0] != '"')
+        return FALSE;
+
+    for (k = 1;; k++) {
+        if (value[k] == '\0')
+            return FALSE;
+        if (!_char_req_escape_old(value[k]))
+            continue;
+
+        if (value[k] == '"')
+            return (value[k + 1] == '\0');
+        else if (value[k] == '\\') {
+            k++;
+            if (!_char_req_escape_old(value[k]))
+                return FALSE;
+        } else
+            return FALSE;
+    }
 }
 
 static gboolean
-_ch_octal_is (char ch)
+_ch_octal_is(char ch)
 {
-	return ch >= '0' && ch < '8';
+    return ch >= '0' && ch < '8';
 }
 
 static guint8
-_ch_octal_get (char ch)
+_ch_octal_get(char ch)
 {
-	nm_assert (_ch_octal_is (ch));
-	return (ch - '0');
+    nm_assert(_ch_octal_is(ch));
+    return (ch - '0');
 }
 
 static gboolean
-_ch_hex_is (char ch)
+_ch_hex_is(char ch)
 {
-	return g_ascii_isxdigit (ch);
+    return g_ascii_isxdigit(ch);
 }
 
 static guint8
-_ch_hex_get (char ch)
+_ch_hex_get(char ch)
 {
-	nm_assert (_ch_hex_is (ch));
-	return ch <= '9' ? ch - '0' : (ch & 0x4F) - 'A' + 10;
+    nm_assert(_ch_hex_is(ch));
+    return ch <= '9' ? ch - '0' : (ch & 0x4F) - 'A' + 10;
 }
 
 static void
-_gstr_init (GString **str, const char *value, gsize i)
+_gstr_init(GString **str, const char *value, gsize i)
 {
-	nm_assert (str);
-	nm_assert (value);
-
-	if (!(*str)) {
-		/* if @str is not yet initialized, it allocates
-		 * a new GString and copies @i characters from
-		 * @value over.
-		 *
-		 * Unescaping usually does not extend the length of a string,
-		 * so we might be tempted to allocate a fixed buffer of length
-		 * (strlen(value)+CONST).
-		 * However, due to $'\Ux' escapes, the maximum length is some
-		 * (FACTOR*strlen(value) + CONST), which is non trivial to get
-		 * right in all cases. Also, we would have to provision for the
-		 * very unlikely extreme case.
-		 * Instead, use a GString buffer which can grow as needed. But for an
-		 * initial guess, strlen(value) is a good start */
-		*str = g_string_new_len (NULL, strlen (value) + 3);
-		if (i)
-			g_string_append_len (*str, value, i);
-	}
+    nm_assert(str);
+    nm_assert(value);
+
+    if (!(*str)) {
+        /* if @str is not yet initialized, it allocates
+         * a new GString and copies @i characters from
+         * @value over.
+         *
+         * Unescaping usually does not extend the length of a string,
+         * so we might be tempted to allocate a fixed buffer of length
+         * (strlen(value)+CONST).
+         * However, due to $'\Ux' escapes, the maximum length is some
+         * (FACTOR*strlen(value) + CONST), which is non trivial to get
+         * right in all cases. Also, we would have to provision for the
+         * very unlikely extreme case.
+         * Instead, use a GString buffer which can grow as needed. But for an
+         * initial guess, strlen(value) is a good start */
+        *str = g_string_new_len(NULL, strlen(value) + 3);
+        if (i)
+            g_string_append_len(*str, value, i);
+    }
 }
 
 const char *
-svUnescape (const char *value, char **to_free)
+svUnescape(const char *value, char **to_free)
 {
-	gsize i, j;
-	GString *str = NULL;
-	int looks_like_old_svescaped = -1;
-
-	/* we handle bash syntax here (note that ifup has #!/bin/bash.
-	 * Thus, see https://www.gnu.org/software/bash/manual/html_node/Quoting.html#Quoting */
-
-	/* @value shall start with the first character after "FOO=" */
-
-	nm_assert (value);
-	nm_assert (to_free);
-
-	/* we don't expect any newlines. They must be filtered out before-hand.
-	 * We also don't support line continuation. */
-	nm_assert (!NM_STRCHAR_ANY (value, ch, ch == '\n'));
-
-	i = 0;
-	while (TRUE) {
-
-		if (value[i] == '\0')
-			goto out_value;
-
-		if (   g_ascii_isspace (value[i])
-		    || value[i] == ';') {
-			gboolean has_semicolon = (value[i] == ';');
-
-			/* starting with space is only allowed, if the entire
-			 * string consists of spaces (possibly terminated by a comment).
-			 * This disallows for example
-			 *   LANG=C ls -1
-			 *   LANG=  ls -1
-			 * but allows
-			 *   LANG= #comment
-			 *
-			 * As a special case, we also allow one trailing semicolon, as long
-			 * it is only followed by whitespace or a #-comment.
-			 *   FOO=;
-			 *   FOO=a;
-			 *   FOO=b ; #hallo
-			 */
-			j = i + 1;
-			while (   g_ascii_isspace (value[j])
-			       || (   !has_semicolon
-			           && (has_semicolon = (value[j] == ';'))))
-				j++;
-			if (!NM_IN_SET (value[j], '\0', '#'))
-				goto out_error;
-			goto out_value;
-		}
-
-		if (value[i] == '\\') {
-			/* backslash escape */
-			_gstr_init (&str, value, i);
-			i++;
-			if (G_UNLIKELY (value[i] == '\0')) {
-				/* we don't support line continuation */
-				goto out_error;
-			}
-			g_string_append_c (str, value[i]);
-			i++;
-			goto loop1_next;
-		}
-
-		if (value[i] == '\'') {
-			/* single quotes */
-			_gstr_init (&str, value, i);
-			i++;
-			j = i;
-			while (TRUE) {
-				if (value[j] == '\0') {
-					/* unterminated single quote. We don't support line continuation */
-					goto out_error;
-				}
-				if (value[j] == '\'')
-					break;
-				j++;
-			}
-			g_string_append_len (str, &value[i], j - i);
-			i = j + 1;
-			goto loop1_next;
-		}
-
-		if (value[i] == '"') {
-			/* double quotes */
-			_gstr_init (&str, value, i);
-			i++;
-			while (TRUE) {
-				if (value[i] == '"') {
-					i++;
-					break;
-				}
-				if (value[i] == '\0') {
-					/* unterminated double quote. We don't support line continuation. */
-					goto out_error;
-				}
-				if (NM_IN_SET (value[i], '`', '$')) {
-					/* we don't support shell expansion. */
-					goto out_error;
-				}
-				if (value[i] == '\\') {
-					i++;
-					if (value[i] == '\0') {
-						/* we don't support line continuation */
-						goto out_error;
-					}
-					if (NM_IN_SET (value[i], '$', '`', '"', '\\')) {
-						/* Drop the backslash. */
-					} else if (NM_IN_SET (value[i], '\'', '~')) {
-						/* '\'' and '~' in double quotes are not handled special by shell.
-						 * However, old versions of svEscape() would wrongly use double-quoting
-						 * with backslash escaping for these characters (expecting svUnescape()
-						 * to remove the backslash).
-						 *
-						 * In order to preserve previous behavior, we continue to read such
-						 * strings different then shell does. */
-
-						/* Actually, we can relax this. Old svEscape() escaped the entire value
-						 * in a particular way with double quotes.
-						 * If the value doesn't exactly look like something as created by svEscape(),
-						 * don't do the compat hack and preserve the backslash. */
-						if (looks_like_old_svescaped < 0)
-							looks_like_old_svescaped = _looks_like_old_svescaped (value);
-						if (!looks_like_old_svescaped)
-							g_string_append_c (str, '\\');
-					} else
-						g_string_append_c (str, '\\');
-				}
-				g_string_append_c (str, value[i]);
-				i++;
-			}
-			goto loop1_next;
-		}
-
-		if (   value[i] == '$'
-		    && value[i + 1] == '\'') {
-			/* ANSI-C Quoting */
-			_gstr_init (&str, value, i);
-			i += 2;
-			while (TRUE) {
-				char ch;
-
-				if (value[i] == '\'') {
-					i++;
-					break;
-				}
-				if (value[i] == '\0') {
-					/* unterminated double quote. We don't support line continuation. */
-					goto out_error;
-				}
-				if (value[i] == '\\') {
-
-					i++;
-					if (value[i] == '\0') {
-						/* we don't support line continuation */
-						goto out_error;
-					}
-					switch (value[i]) {
-					case 'a':  ch = '\a';  break;
-					case 'b':  ch = '\b';  break;
-					case 'e':  ch = '\e';  break;
-					case 'E':  ch = '\E';  break;
-					case 'f':  ch = '\f';  break;
-					case 'n':  ch = '\n';  break;
-					case 'r':  ch = '\r';  break;
-					case 't':  ch = '\t';  break;
-					case 'v':  ch = '\v';  break;
-					case '?':  ch = '\?';  break;
-					case '"':  ch = '"';   break;
-					case '\\': ch = '\\';  break;
-					case '\'': ch = '\'';  break;
-					default:
-						if (_ch_octal_is (value[i])) {
-							guint v;
-
-							v = _ch_octal_get (value[i]);
-							i++;
-							if (_ch_octal_is (value[i])) {
-								v = (v * 8) + _ch_octal_get (value[i]);
-								i++;
-								if (_ch_octal_is (value[i])) {
-									v = (v * 8) + _ch_octal_get (value[i]);
-									i++;
-								}
-							}
-							/* like bash, we cut too large numbers off. E.g. A=$'\772' becomes 0xfa  */
-							g_string_append_c (str, (guint8) v);
-						} else if (NM_IN_SET (value[i], 'x', 'u', 'U')) {
-							const char escape_type = value[i];
-							int max_digits = escape_type == 'x' ? 2 : escape_type == 'u' ? 4 : 8;
-							guint64 v;
-
-							i++;
-							if (!_ch_hex_is (value[i])) {
-								/* missing hex value after "\x" escape. This is treated like no escaping. */
-								g_string_append_c (str, '\\');
-								g_string_append_c (str, escape_type);
-							} else {
-								v = _ch_hex_get (value[i]);
-								i++;
-
-								while (--max_digits > 0) {
-									if (!_ch_hex_is (value[i]))
-										break;
-									v = v * 16 + _ch_hex_get (value[i]);
-									i++;
-								}
-								if (escape_type == 'x')
-									g_string_append_c (str, v);
-								else {
-									/* we treat the unicode escapes as utf-8 encoded values. */
-									g_string_append_unichar (str, v);
-								}
-							}
-						} else {
-							g_string_append_c (str, '\\');
-							g_string_append_c (str, value[i]);
-							i++;
-						}
-						goto loop_ansic_next;
-					}
-				} else
-					ch = value[i];
-				g_string_append_c (str, ch);
-				i++;
-loop_ansic_next: ;
-			}
-			goto loop1_next;
-		}
-
-		if (NM_IN_SET (value[i], '|', '&', '(', ')', '<', '>')) {
-			/* shell metacharacters are not supported without quoting.
-			 * Note that ';' is already handled above. */
-			goto out_error;
-		}
-
-		/* an unquoted, regular character. Just consume it directly. */
-		if (str)
-			g_string_append_c (str, value[i]);
-		i++;
-
-loop1_next: ;
-	}
-
-	nm_assert_not_reached ();
+    gsize    i, j;
+    GString *str                      = NULL;
+    int      looks_like_old_svescaped = -1;
+
+    /* we handle bash syntax here (note that ifup has #!/bin/bash.
+     * Thus, see https://www.gnu.org/software/bash/manual/html_node/Quoting.html#Quoting */
+
+    /* @value shall start with the first character after "FOO=" */
+
+    nm_assert(value);
+    nm_assert(to_free);
+
+    /* we don't expect any newlines. They must be filtered out before-hand.
+     * We also don't support line continuation. */
+    nm_assert(!NM_STRCHAR_ANY(value, ch, ch == '\n'));
+
+    i = 0;
+    while (TRUE) {
+        if (value[i] == '\0')
+            goto out_value;
+
+        if (g_ascii_isspace(value[i]) || value[i] == ';') {
+            gboolean has_semicolon = (value[i] == ';');
+
+            /* starting with space is only allowed, if the entire
+             * string consists of spaces (possibly terminated by a comment).
+             * This disallows for example
+             *   LANG=C ls -1
+             *   LANG=  ls -1
+             * but allows
+             *   LANG= #comment
+             *
+             * As a special case, we also allow one trailing semicolon, as long
+             * it is only followed by whitespace or a #-comment.
+             *   FOO=;
+             *   FOO=a;
+             *   FOO=b ; #hallo
+             */
+            j = i + 1;
+            while (g_ascii_isspace(value[j])
+                   || (!has_semicolon && (has_semicolon = (value[j] == ';'))))
+                j++;
+            if (!NM_IN_SET(value[j], '\0', '#'))
+                goto out_error;
+            goto out_value;
+        }
+
+        if (value[i] == '\\') {
+            /* backslash escape */
+            _gstr_init(&str, value, i);
+            i++;
+            if (G_UNLIKELY(value[i] == '\0')) {
+                /* we don't support line continuation */
+                goto out_error;
+            }
+            g_string_append_c(str, value[i]);
+            i++;
+            goto loop1_next;
+        }
+
+        if (value[i] == '\'') {
+            /* single quotes */
+            _gstr_init(&str, value, i);
+            i++;
+            j = i;
+            while (TRUE) {
+                if (value[j] == '\0') {
+                    /* unterminated single quote. We don't support line continuation */
+                    goto out_error;
+                }
+                if (value[j] == '\'')
+                    break;
+                j++;
+            }
+            g_string_append_len(str, &value[i], j - i);
+            i = j + 1;
+            goto loop1_next;
+        }
+
+        if (value[i] == '"') {
+            /* double quotes */
+            _gstr_init(&str, value, i);
+            i++;
+            while (TRUE) {
+                if (value[i] == '"') {
+                    i++;
+                    break;
+                }
+                if (value[i] == '\0') {
+                    /* unterminated double quote. We don't support line continuation. */
+                    goto out_error;
+                }
+                if (NM_IN_SET(value[i], '`', '$')) {
+                    /* we don't support shell expansion. */
+                    goto out_error;
+                }
+                if (value[i] == '\\') {
+                    i++;
+                    if (value[i] == '\0') {
+                        /* we don't support line continuation */
+                        goto out_error;
+                    }
+                    if (NM_IN_SET(value[i], '$', '`', '"', '\\')) {
+                        /* Drop the backslash. */
+                    } else if (NM_IN_SET(value[i], '\'', '~')) {
+                        /* '\'' and '~' in double quotes are not handled special by shell.
+                         * However, old versions of svEscape() would wrongly use double-quoting
+                         * with backslash escaping for these characters (expecting svUnescape()
+                         * to remove the backslash).
+                         *
+                         * In order to preserve previous behavior, we continue to read such
+                         * strings different then shell does. */
+
+                        /* Actually, we can relax this. Old svEscape() escaped the entire value
+                         * in a particular way with double quotes.
+                         * If the value doesn't exactly look like something as created by svEscape(),
+                         * don't do the compat hack and preserve the backslash. */
+                        if (looks_like_old_svescaped < 0)
+                            looks_like_old_svescaped = _looks_like_old_svescaped(value);
+                        if (!looks_like_old_svescaped)
+                            g_string_append_c(str, '\\');
+                    } else
+                        g_string_append_c(str, '\\');
+                }
+                g_string_append_c(str, value[i]);
+                i++;
+            }
+            goto loop1_next;
+        }
+
+        if (value[i] == '$' && value[i + 1] == '\'') {
+            /* ANSI-C Quoting */
+            _gstr_init(&str, value, i);
+            i += 2;
+            while (TRUE) {
+                char ch;
+
+                if (value[i] == '\'') {
+                    i++;
+                    break;
+                }
+                if (value[i] == '\0') {
+                    /* unterminated double quote. We don't support line continuation. */
+                    goto out_error;
+                }
+                if (value[i] == '\\') {
+                    i++;
+                    if (value[i] == '\0') {
+                        /* we don't support line continuation */
+                        goto out_error;
+                    }
+                    switch (value[i]) {
+                    case 'a':
+                        ch = '\a';
+                        break;
+                    case 'b':
+                        ch = '\b';
+                        break;
+                    case 'e':
+                        ch = '\e';
+                        break;
+                    case 'E':
+                        ch = '\E';
+                        break;
+                    case 'f':
+                        ch = '\f';
+                        break;
+                    case 'n':
+                        ch = '\n';
+                        break;
+                    case 'r':
+                        ch = '\r';
+                        break;
+                    case 't':
+                        ch = '\t';
+                        break;
+                    case 'v':
+                        ch = '\v';
+                        break;
+                    case '?':
+                        ch = '\?';
+                        break;
+                    case '"':
+                        ch = '"';
+                        break;
+                    case '\\':
+                        ch = '\\';
+                        break;
+                    case '\'':
+                        ch = '\'';
+                        break;
+                    default:
+                        if (_ch_octal_is(value[i])) {
+                            guint v;
+
+                            v = _ch_octal_get(value[i]);
+                            i++;
+                            if (_ch_octal_is(value[i])) {
+                                v = (v * 8) + _ch_octal_get(value[i]);
+                                i++;
+                                if (_ch_octal_is(value[i])) {
+                                    v = (v * 8) + _ch_octal_get(value[i]);
+                                    i++;
+                                }
+                            }
+                            /* like bash, we cut too large numbers off. E.g. A=$'\772' becomes 0xfa  */
+                            g_string_append_c(str, (guint8) v);
+                        } else if (NM_IN_SET(value[i], 'x', 'u', 'U')) {
+                            const char escape_type = value[i];
+                            int max_digits = escape_type == 'x' ? 2 : escape_type == 'u' ? 4 : 8;
+                            guint64 v;
+
+                            i++;
+                            if (!_ch_hex_is(value[i])) {
+                                /* missing hex value after "\x" escape. This is treated like no escaping. */
+                                g_string_append_c(str, '\\');
+                                g_string_append_c(str, escape_type);
+                            } else {
+                                v = _ch_hex_get(value[i]);
+                                i++;
+
+                                while (--max_digits > 0) {
+                                    if (!_ch_hex_is(value[i]))
+                                        break;
+                                    v = v * 16 + _ch_hex_get(value[i]);
+                                    i++;
+                                }
+                                if (escape_type == 'x')
+                                    g_string_append_c(str, v);
+                                else {
+                                    /* we treat the unicode escapes as utf-8 encoded values. */
+                                    g_string_append_unichar(str, v);
+                                }
+                            }
+                        } else {
+                            g_string_append_c(str, '\\');
+                            g_string_append_c(str, value[i]);
+                            i++;
+                        }
+                        goto loop_ansic_next;
+                    }
+                } else
+                    ch = value[i];
+                g_string_append_c(str, ch);
+                i++;
+loop_ansic_next:;
+            }
+            goto loop1_next;
+        }
+
+        if (NM_IN_SET(value[i], '|', '&', '(', ')', '<', '>')) {
+            /* shell metacharacters are not supported without quoting.
+             * Note that ';' is already handled above. */
+            goto out_error;
+        }
+
+        /* an unquoted, regular character. Just consume it directly. */
+        if (str)
+            g_string_append_c(str, value[i]);
+        i++;
+
+loop1_next:;
+    }
+
+    nm_assert_not_reached();
 
 out_value:
-	if (i == 0) {
-		nm_assert (!str);
-		*to_free = NULL;
-		return "";
-	}
-
-	if (str) {
-		if (str->len == 0 || str->str[0] == '\0') {
-			g_string_free (str, TRUE);
-			*to_free = NULL;
-			return "";
-		} else {
-			*to_free = g_string_free (str, FALSE);
-			return *to_free;
-		}
-	}
-
-	if (value[i] != '\0') {
-		*to_free = g_strndup (value, i);
-		return *to_free;
-	}
-
-	*to_free = NULL;
-	return value;
+    if (i == 0) {
+        nm_assert(!str);
+        *to_free = NULL;
+        return "";
+    }
+
+    if (str) {
+        if (str->len == 0 || str->str[0] == '\0') {
+            g_string_free(str, TRUE);
+            *to_free = NULL;
+            return "";
+        } else {
+            *to_free = g_string_free(str, FALSE);
+            return *to_free;
+        }
+    }
+
+    if (value[i] != '\0') {
+        *to_free = g_strndup(value, i);
+        return *to_free;
+    }
+
+    *to_free = NULL;
+    return value;
 
 out_error:
-	if (str)
-		g_string_free (str, TRUE);
-	*to_free = NULL;
-	return NULL;
+    if (str)
+        g_string_free(str, TRUE);
+    *to_free = NULL;
+    return NULL;
 }
 
 /*****************************************************************************/
 
 shvarFile *
-svFile_new (const char *name,
-            int fd,
-            const char *content)
+svFile_new(const char *name, int fd, const char *content)
 {
-	shvarFile *s;
-	const char *p;
-	const char *q;
-
-	nm_assert (name);
-	nm_assert (fd >= -1);
-
-	s = g_slice_new (shvarFile);
-	*s = (shvarFile) {
-		.fileName = g_strdup (name),
-		.fd       = fd,
-		.lst_head = C_LIST_INIT (s->lst_head),
-		.lst_idx  = g_hash_table_new (nm_pstr_hash, nm_pstr_equal),
-	};
-
-	if (content) {
-		for (p = content; (q = strchr (p, '\n')) != NULL; p = q + 1)
-			_line_link_parse (s, p, q - p);
-		if (p[0])
-			_line_link_parse (s, p, strlen (p));
-	}
-
-	return s;
+    shvarFile * s;
+    const char *p;
+    const char *q;
+
+    nm_assert(name);
+    nm_assert(fd >= -1);
+
+    s  = g_slice_new(shvarFile);
+    *s = (shvarFile){
+        .fileName = g_strdup(name),
+        .fd       = fd,
+        .lst_head = C_LIST_INIT(s->lst_head),
+        .lst_idx  = g_hash_table_new(nm_pstr_hash, nm_pstr_equal),
+    };
+
+    if (content) {
+        for (p = content; (q = strchr(p, '\n')) != NULL; p = q + 1)
+            _line_link_parse(s, p, q - p);
+        if (p[0])
+            _line_link_parse(s, p, strlen(p));
+    }
+
+    return s;
 }
 
 const char *
-svFileGetName (const shvarFile *s)
+svFileGetName(const shvarFile *s)
 {
-	nm_assert (s);
+    nm_assert(s);
 
-	return s->fileName;
+    return s->fileName;
 }
 
 void
-_nmtst_svFileSetName (shvarFile *s, const char *fileName)
+_nmtst_svFileSetName(shvarFile *s, const char *fileName)
 {
-	/* changing the file name is not supported for regular
-	 * operation. Only allowed to use in tests, otherwise,
-	 * the filename is immutable. */
-	g_free (s->fileName);
-	s->fileName = g_strdup (fileName);
+    /* changing the file name is not supported for regular
+     * operation. Only allowed to use in tests, otherwise,
+     * the filename is immutable. */
+    g_free(s->fileName);
+    s->fileName = g_strdup(fileName);
 }
 
 void
-_nmtst_svFileSetModified (shvarFile *s)
+_nmtst_svFileSetModified(shvarFile *s)
 {
-	/* marking a file as modified is only for testing. */
-	s->modified = TRUE;
+    /* marking a file as modified is only for testing. */
+    s->modified = TRUE;
 }
 
 /*****************************************************************************/
 
 static void
-ASSERT_shvarLine (const shvarLine *line)
+ASSERT_shvarLine(const shvarLine *line)
 {
 #if NM_MORE_ASSERTS > 5
-	const char *s, *s2;
-
-	nm_assert (line);
-	if (!line->key) {
-		nm_assert (line->line);
-		nm_assert (!line->key_with_prefix);
-		s = nm_str_skip_leading_spaces (line->line);
-		s2 = strchr (s, '=');
-		nm_assert (!s2 || !_shell_is_name (s, s2 - s));
-	} else {
-		nm_assert (line->key_with_prefix);
-		nm_assert (line->key == nm_str_skip_leading_spaces (line->key_with_prefix));
-		nm_assert (_shell_is_name (line->key, -1));
-	}
+    const char *s, *s2;
+
+    nm_assert(line);
+    if (!line->key) {
+        nm_assert(line->line);
+        nm_assert(!line->key_with_prefix);
+        s  = nm_str_skip_leading_spaces(line->line);
+        s2 = strchr(s, '=');
+        nm_assert(!s2 || !_shell_is_name(s, s2 - s));
+    } else {
+        nm_assert(line->key_with_prefix);
+        nm_assert(line->key == nm_str_skip_leading_spaces(line->key_with_prefix));
+        nm_assert(_shell_is_name(line->key, -1));
+    }
 #endif
 }
 
 static shvarLine *
-line_new_parse (const char *value, gsize len)
+line_new_parse(const char *value, gsize len)
 {
-	shvarLine *line;
-	gsize k, e;
-
-	nm_assert (value);
-
-	line = g_slice_new (shvarLine);
-	*line = (shvarLine) {
-		.lst      = C_LIST_INIT (line->lst),
-		.dirty    = TRUE,
-	};
-
-	for (k = 0; k < len; k++) {
-		if (g_ascii_isspace (value[k]))
-			continue;
-
-		if (   g_ascii_isalpha (value[k])
-		    || value[k] == '_') {
-			for (e = k + 1; e < len; e++) {
-				if (value[e] == '=') {
-					nm_assert (_shell_is_name (&value[k], e - k));
-					line->line = g_strndup (&value[e + 1], len - e - 1);
-					line->key_with_prefix = g_strndup (value, e);
-					line->key = &line->key_with_prefix[k];
-					ASSERT_shvarLine (line);
-					return line;
-				}
-				if (   !g_ascii_isalnum (value[e])
-				    && value[e] != '_')
-					break;
-			}
-		}
-		break;
-	}
-	line->line = g_strndup (value, len);
-	ASSERT_shvarLine (line);
-	return line;
+    shvarLine *line;
+    gsize      k, e;
+
+    nm_assert(value);
+
+    line  = g_slice_new(shvarLine);
+    *line = (shvarLine){
+        .lst   = C_LIST_INIT(line->lst),
+        .dirty = TRUE,
+    };
+
+    for (k = 0; k < len; k++) {
+        if (g_ascii_isspace(value[k]))
+            continue;
+
+        if (g_ascii_isalpha(value[k]) || value[k] == '_') {
+            for (e = k + 1; e < len; e++) {
+                if (value[e] == '=') {
+                    nm_assert(_shell_is_name(&value[k], e - k));
+                    line->line            = g_strndup(&value[e + 1], len - e - 1);
+                    line->key_with_prefix = g_strndup(value, e);
+                    line->key             = &line->key_with_prefix[k];
+                    ASSERT_shvarLine(line);
+                    return line;
+                }
+                if (!g_ascii_isalnum(value[e]) && value[e] != '_')
+                    break;
+            }
+        }
+        break;
+    }
+    line->line = g_strndup(value, len);
+    ASSERT_shvarLine(line);
+    return line;
 }
 
 static shvarLine *
-line_new_build (const char *key, const char *value)
+line_new_build(const char *key, const char *value)
 {
-	char *value_escaped = NULL;
-	shvarLine *line;
-	char *new_key;
-
-	value = svEscape (value, &value_escaped);
-
-	line = g_slice_new (shvarLine);
-	new_key = g_strdup (key),
-	*line = (shvarLine) {
-		.lst             = C_LIST_INIT (line->lst),
-		.line            = value_escaped ?: g_strdup (value),
-		.key_with_prefix = new_key,
-		.key             = new_key,
-		.dirty           = FALSE,
-	};
-	ASSERT_shvarLine (line);
-	return line;
+    char *     value_escaped = NULL;
+    shvarLine *line;
+    char *     new_key;
+
+    value = svEscape(value, &value_escaped);
+
+    line    = g_slice_new(shvarLine);
+    new_key = g_strdup(key), *line = (shvarLine){
+                                 .lst             = C_LIST_INIT(line->lst),
+                                 .line            = value_escaped ?: g_strdup(value),
+                                 .key_with_prefix = new_key,
+                                 .key             = new_key,
+                                 .dirty           = FALSE,
+                             };
+    ASSERT_shvarLine(line);
+    return line;
 }
 
 static gboolean
-line_set (shvarLine *line, const char *value)
+line_set(shvarLine *line, const char *value)
 {
-	char *value_escaped = NULL;
-	gboolean changed = FALSE;
-
-	ASSERT_shvarLine (line);
-	nm_assert (line->key);
-
-	line->dirty = FALSE;
-
-	if (line->key != line->key_with_prefix) {
-		memmove (line->key_with_prefix, line->key, strlen (line->key) + 1);
-		line->key = line->key_with_prefix;
-		changed = TRUE;
-		ASSERT_shvarLine (line);
-	}
-
-	value = svEscape (value, &value_escaped);
-
-	if (line->line) {
-		if (nm_streq (value, line->line)) {
-			g_free (value_escaped);
-			return changed;
-		}
-		g_free (line->line);
-	}
-
-	line->line = value_escaped ?: g_strdup (value);
-	ASSERT_shvarLine (line);
-	return TRUE;
+    char *   value_escaped = NULL;
+    gboolean changed       = FALSE;
+
+    ASSERT_shvarLine(line);
+    nm_assert(line->key);
+
+    line->dirty = FALSE;
+
+    if (line->key != line->key_with_prefix) {
+        memmove(line->key_with_prefix, line->key, strlen(line->key) + 1);
+        line->key = line->key_with_prefix;
+        changed   = TRUE;
+        ASSERT_shvarLine(line);
+    }
+
+    value = svEscape(value, &value_escaped);
+
+    if (line->line) {
+        if (nm_streq(value, line->line)) {
+            g_free(value_escaped);
+            return changed;
+        }
+        g_free(line->line);
+    }
+
+    line->line = value_escaped ?: g_strdup(value);
+    ASSERT_shvarLine(line);
+    return TRUE;
 }
 
 static void
-line_free (shvarLine *line)
+line_free(shvarLine *line)
 {
-	ASSERT_shvarLine (line);
-	c_list_unlink_stale (&line->lst);
-	g_free (line->line);
-	g_free (line->key_with_prefix);
-	g_slice_free (shvarLine, line);
+    ASSERT_shvarLine(line);
+    c_list_unlink_stale(&line->lst);
+    g_free(line->line);
+    g_free(line->key_with_prefix);
+    g_slice_free(shvarLine, line);
 }
 
 /*****************************************************************************/
 
 static void
-_line_link_parse (shvarFile *s, const char *value, gsize len)
+_line_link_parse(shvarFile *s, const char *value, gsize len)
 {
-	shvarLine *line;
+    shvarLine *line;
 
-	line = line_new_parse (value, len);
-	if (!line->key)
-		goto do_link;
+    line = line_new_parse(value, len);
+    if (!line->key)
+        goto do_link;
 
-	if (G_UNLIKELY (!g_hash_table_insert (s->lst_idx, line, line))) {
-		shvarLine *existing_key;
-		shvarLine *existing_val;
+    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 an extra
-		 * array to track the keys/values differently. I wish there was an
-		 * GHashTable API to add a key only if it does not exist yet. */
+        /* Slow-path: we have duplicate keys. Fix the mess we created.
+         * Unfortunately, g_hash_table_insert() now had to allocate an extra
+         * array to track the keys/values differently. I wish there was an
+         * GHashTable 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 ();
+        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->prev_shadowed = existing_key;
-		g_hash_table_replace (s->lst_idx, line, line);
-	}
+        nm_assert(existing_val == line);
+        nm_assert(existing_key != line);
+        line->prev_shadowed = existing_key;
+        g_hash_table_replace(s->lst_idx, line, line);
+    }
 
 do_link:
-	c_list_link_tail (&s->lst_head, &line->lst);
+    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.
  */
 static shvarFile *
-svOpenFileInternal (const char *name, gboolean create, GError **error)
+svOpenFileInternal(const char *name, gboolean create, GError **error)
 {
-	gboolean closefd = FALSE;
-	int errsv = 0;
-	gs_free char *content = NULL;
-	gs_free_error GError *local = NULL;
-	nm_auto_close int fd = -1;
-
-	if (create)
-		fd = open (name, O_RDWR | O_CLOEXEC); /* NOT O_CREAT */
-	if (fd < 0) {
-		/* try read-only */
-		fd = open (name, O_RDONLY | O_CLOEXEC); /* NOT O_CREAT */
-		if (fd < 0)
-			errsv = errno;
-		else
-			closefd = TRUE;
-	}
-
-	if (fd < 0) {
-		if (create)
-			return svFile_new (name, -1, NULL);
-
-		g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errsv),
-		             "Could not read file '%s': %s",
-		             name, nm_strerror_native (errsv));
-		return NULL;
-	}
-
-	if (!nm_utils_fd_get_contents (closefd ? nm_steal_fd (&fd) : fd,
-	                               closefd,
-	                               10 * 1024 * 1024,
-	                               NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE,
-	                               &content,
-	                               NULL,
-	                               NULL,
-	                               &local)) {
-		if (create)
-			return svFile_new (name, -1, NULL);
-
-		g_set_error (error, G_FILE_ERROR,
-		             local->domain == G_FILE_ERROR ? local->code : G_FILE_ERROR_FAILED,
-		             "Could not read file '%s': %s",
-		             name, local->message);
-		return NULL;
-	}
-
-	/* closefd is set if we opened the file read-only, so go ahead and
-	 * close it, because we can't write to it anyway */
-	nm_assert (closefd || fd >= 0);
-	return svFile_new (name,
-	                     !closefd
-	                   ? nm_steal_fd (&fd)
-	                   : -1,
-	                   content);
+    gboolean      closefd       = FALSE;
+    int           errsv         = 0;
+    gs_free char *content       = NULL;
+    gs_free_error GError *local = NULL;
+    nm_auto_close int     fd    = -1;
+
+    if (create)
+        fd = open(name, O_RDWR | O_CLOEXEC); /* NOT O_CREAT */
+    if (fd < 0) {
+        /* try read-only */
+        fd = open(name, O_RDONLY | O_CLOEXEC); /* NOT O_CREAT */
+        if (fd < 0)
+            errsv = errno;
+        else
+            closefd = TRUE;
+    }
+
+    if (fd < 0) {
+        if (create)
+            return svFile_new(name, -1, NULL);
+
+        g_set_error(error,
+                    G_FILE_ERROR,
+                    g_file_error_from_errno(errsv),
+                    "Could not read file '%s': %s",
+                    name,
+                    nm_strerror_native(errsv));
+        return NULL;
+    }
+
+    if (!nm_utils_fd_get_contents(closefd ? nm_steal_fd(&fd) : fd,
+                                  closefd,
+                                  10 * 1024 * 1024,
+                                  NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE,
+                                  &content,
+                                  NULL,
+                                  NULL,
+                                  &local)) {
+        if (create)
+            return svFile_new(name, -1, NULL);
+
+        g_set_error(error,
+                    G_FILE_ERROR,
+                    local->domain == G_FILE_ERROR ? local->code : G_FILE_ERROR_FAILED,
+                    "Could not read file '%s': %s",
+                    name,
+                    local->message);
+        return NULL;
+    }
+
+    /* closefd is set if we opened the file read-only, so go ahead and
+     * close it, because we can't write to it anyway */
+    nm_assert(closefd || fd >= 0);
+    return svFile_new(name, !closefd ? nm_steal_fd(&fd) : -1, content);
 }
 
 /* Open the file <name>, return shvarFile on success, NULL on failure */
 shvarFile *
-svOpenFile (const char *name, GError **error)
+svOpenFile(const char *name, GError **error)
 {
-	return svOpenFileInternal (name, FALSE, error);
+    return svOpenFileInternal(name, FALSE, error);
 }
 
 /* Create a new file structure, returning actual data if the file exists,
  * and a suitable starting point if it doesn't.
  */
 shvarFile *
-svCreateFile (const char *name)
+svCreateFile(const char *name)
 {
-	return svOpenFileInternal (name, TRUE, NULL);
+    return svOpenFileInternal(name, TRUE, NULL);
 }
 
 /*****************************************************************************/
 
 static gboolean
-_svKeyMatchesType (const char *key, SvKeyType match_key_type)
+_svKeyMatchesType(const char *key, SvKeyType match_key_type)
 {
-	if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_ANY))
-		return TRUE;
-
-#define _IS_NUMBERED(key, tag) \
-	({ \
-		gint64 _idx; \
-		\
-		   NMS_IFCFG_RH_UTIL_IS_NUMBERED_TAG (key, tag, &_idx) \
-		&& _idx >= 0; \
-	})
-
-	if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_ROUTE_SVFORMAT)) {
-		if (   _IS_NUMBERED (key, "ADDRESS")
-		    || _IS_NUMBERED (key, "NETMASK")
-		    || _IS_NUMBERED (key, "GATEWAY")
-		    || _IS_NUMBERED (key, "METRIC")
-		    || _IS_NUMBERED (key, "OPTIONS"))
-			return TRUE;
-	}
-	if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_IP4_ADDRESS)) {
-		if (   _IS_NUMBERED (key, "IPADDR")
-		    || _IS_NUMBERED (key, "PREFIX")
-		    || _IS_NUMBERED (key, "NETMASK")
-		    || _IS_NUMBERED (key, "GATEWAY"))
-			return TRUE;
-	}
-	if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_USER)) {
-		if (g_str_has_prefix (key, "NM_USER_"))
-			return TRUE;
-	}
-	if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_TC)) {
-		if (   _IS_NUMBERED (key, "QDISC")
-		    || _IS_NUMBERED (key, "FILTER"))
-			return TRUE;
-	}
-	if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_SRIOV_VF)) {
-		if (_IS_NUMBERED (key, "SRIOV_VF"))
-			return TRUE;
-	}
-	if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_ROUTING_RULE4)) {
-		if (_IS_NUMBERED (key, "ROUTING_RULE_"))
-			return TRUE;
-	}
-	if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_ROUTING_RULE6)) {
-		if (_IS_NUMBERED (key, "ROUTING_RULE6_"))
-			return TRUE;
-	}
-
-	return FALSE;
+    if (NM_FLAGS_HAS(match_key_type, SV_KEY_TYPE_ANY))
+        return TRUE;
+
+#define _IS_NUMBERED(key, tag)                             \
+    ({                                                     \
+        gint64 _idx;                                       \
+                                                           \
+        NMS_IFCFG_RH_UTIL_IS_NUMBERED_TAG(key, tag, &_idx) \
+        &&_idx >= 0;                                       \
+    })
+
+    if (NM_FLAGS_HAS(match_key_type, SV_KEY_TYPE_ROUTE_SVFORMAT)) {
+        if (_IS_NUMBERED(key, "ADDRESS") || _IS_NUMBERED(key, "NETMASK")
+            || _IS_NUMBERED(key, "GATEWAY") || _IS_NUMBERED(key, "METRIC")
+            || _IS_NUMBERED(key, "OPTIONS"))
+            return TRUE;
+    }
+    if (NM_FLAGS_HAS(match_key_type, SV_KEY_TYPE_IP4_ADDRESS)) {
+        if (_IS_NUMBERED(key, "IPADDR") || _IS_NUMBERED(key, "PREFIX")
+            || _IS_NUMBERED(key, "NETMASK") || _IS_NUMBERED(key, "GATEWAY"))
+            return TRUE;
+    }
+    if (NM_FLAGS_HAS(match_key_type, SV_KEY_TYPE_USER)) {
+        if (g_str_has_prefix(key, "NM_USER_"))
+            return TRUE;
+    }
+    if (NM_FLAGS_HAS(match_key_type, SV_KEY_TYPE_TC)) {
+        if (_IS_NUMBERED(key, "QDISC") || _IS_NUMBERED(key, "FILTER"))
+            return TRUE;
+    }
+    if (NM_FLAGS_HAS(match_key_type, SV_KEY_TYPE_SRIOV_VF)) {
+        if (_IS_NUMBERED(key, "SRIOV_VF"))
+            return TRUE;
+    }
+    if (NM_FLAGS_HAS(match_key_type, SV_KEY_TYPE_ROUTING_RULE4)) {
+        if (_IS_NUMBERED(key, "ROUTING_RULE_"))
+            return TRUE;
+    }
+    if (NM_FLAGS_HAS(match_key_type, SV_KEY_TYPE_ROUTING_RULE6)) {
+        if (_IS_NUMBERED(key, "ROUTING_RULE6_"))
+            return TRUE;
+    }
+
+    return FALSE;
 }
 
 gint64
-svNumberedParseKey (const char *key)
+svNumberedParseKey(const char *key)
 {
-	gint64 idx;
+    gint64 idx;
 
-	if (   NMS_IFCFG_RH_UTIL_IS_NUMBERED_TAG (key, "ROUTING_RULE_", &idx)
-	    || NMS_IFCFG_RH_UTIL_IS_NUMBERED_TAG (key, "ROUTING_RULE6_", &idx))
-		return idx;
-	return -1;
+    if (NMS_IFCFG_RH_UTIL_IS_NUMBERED_TAG(key, "ROUTING_RULE_", &idx)
+        || NMS_IFCFG_RH_UTIL_IS_NUMBERED_TAG(key, "ROUTING_RULE6_", &idx))
+        return idx;
+    return -1;
 }
 
 GHashTable *
-svGetKeys (shvarFile *s, SvKeyType match_key_type)
+svGetKeys(shvarFile *s, SvKeyType match_key_type)
 {
-	GHashTable *keys = NULL;
-	CList *current;
-	const shvarLine *line;
-
-	nm_assert (s);
-
-	c_list_for_each (current, &s->lst_head) {
-		line = c_list_entry (current, shvarLine, lst);
-		if (   line->key
-		    && line->line
-		    && _svKeyMatchesType (line->key, match_key_type)) {
-			/* we don't clone the keys. The keys are only valid
-			 * until @s gets modified. */
-			if (!keys)
-				keys = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, NULL);
-			g_hash_table_add (keys, (gpointer) line->key);
-		}
-	}
-	return keys;
+    GHashTable *     keys = NULL;
+    CList *          current;
+    const shvarLine *line;
+
+    nm_assert(s);
+
+    c_list_for_each (current, &s->lst_head) {
+        line = c_list_entry(current, shvarLine, lst);
+        if (line->key && line->line && _svKeyMatchesType(line->key, match_key_type)) {
+            /* we don't clone the keys. The keys are only valid
+             * until @s gets modified. */
+            if (!keys)
+                keys = g_hash_table_new_full(nm_str_hash, g_str_equal, NULL, NULL);
+            g_hash_table_add(keys, (gpointer) line->key);
+        }
+    }
+    return keys;
 }
 
 static int
-_get_keys_sorted_cmp (gconstpointer a,
-                      gconstpointer b,
-                      gpointer user_data)
+_get_keys_sorted_cmp(gconstpointer a, gconstpointer b, gpointer user_data)
 {
-	const char *k_a = *((const char *const*) a);
-	const char *k_b = *((const char *const*) b);
-	gint64 n_a;
-	gint64 n_b;
-
-	n_a = svNumberedParseKey (k_a);
-	n_b = svNumberedParseKey (k_b);
-	NM_CMP_DIRECT (n_a, n_b);
-	NM_CMP_RETURN (strcmp (k_a, k_b));
-	nm_assert_not_reached ();
-	return 0;
+    const char *k_a = *((const char *const *) a);
+    const char *k_b = *((const char *const *) b);
+    gint64      n_a;
+    gint64      n_b;
+
+    n_a = svNumberedParseKey(k_a);
+    n_b = svNumberedParseKey(k_b);
+    NM_CMP_DIRECT(n_a, n_b);
+    NM_CMP_RETURN(strcmp(k_a, k_b));
+    nm_assert_not_reached();
+    return 0;
 }
 
 const char **
-svGetKeysSorted (shvarFile *s,
-                 SvKeyType match_key_type,
-                 guint *out_len)
+svGetKeysSorted(shvarFile *s, SvKeyType match_key_type, guint *out_len)
 {
-	gs_unref_hashtable GHashTable *keys_hash = NULL;
-
-	keys_hash = svGetKeys (s, match_key_type);
-	if (!keys_hash) {
-		NM_SET_OUT (out_len, 0);
-		return NULL;
-	}
-	return (const char **) nm_utils_hash_keys_to_array (keys_hash,
-	                                                    _get_keys_sorted_cmp,
-	                                                    NULL,
-	                                                    out_len);
+    gs_unref_hashtable GHashTable *keys_hash = NULL;
+
+    keys_hash = svGetKeys(s, match_key_type);
+    if (!keys_hash) {
+        NM_SET_OUT(out_len, 0);
+        return NULL;
+    }
+    return (
+        const char **) nm_utils_hash_keys_to_array(keys_hash, _get_keys_sorted_cmp, NULL, out_len);
 }
 
 /*****************************************************************************/
 
 const char *
-svFindFirstNumberedKey (shvarFile *s, const char *key_prefix)
+svFindFirstNumberedKey(shvarFile *s, const char *key_prefix)
 {
-	const shvarLine *line;
+    const shvarLine *line;
 
-	g_return_val_if_fail (s, NULL);
-	g_return_val_if_fail (key_prefix, NULL);
+    g_return_val_if_fail(s, NULL);
+    g_return_val_if_fail(key_prefix, NULL);
 
-	c_list_for_each_entry (line, &s->lst_head, lst) {
-		if (   line->key
-		    && line->line
-		    && nms_ifcfg_rh_utils_is_numbered_tag (line->key, key_prefix, NULL))
-			return line->key;
-	}
+    c_list_for_each_entry (line, &s->lst_head, lst) {
+        if (line->key && line->line
+            && nms_ifcfg_rh_utils_is_numbered_tag(line->key, key_prefix, NULL))
+            return line->key;
+    }
 
-	return NULL;
+    return NULL;
 }
 
 /*****************************************************************************/
 
 static const char *
-_svGetValue (shvarFile *s, const char *key, char **to_free)
+_svGetValue(shvarFile *s, const char *key, char **to_free)
 {
-	const shvarLine *line;
-	const char *v;
-
-	nm_assert (s);
-	nm_assert (_shell_is_name (key, -1));
-	nm_assert (to_free);
-
-	ASSERT_key_is_well_known (key);
-
-	line = g_hash_table_lookup (s->lst_idx, &key);
-
-	if (line && line->line) {
-		v = svUnescape (line->line, to_free);
-		if (!v) {
-			/* a wrongly quoted value is treated like the empty string.
-			 * See also svWriteFile(), which handles unparsable values
-			 * that way. */
-			nm_assert (!*to_free);
-			return "";
-		}
-		return v;
-	}
-	*to_free = NULL;
-	return NULL;
+    const shvarLine *line;
+    const char *     v;
+
+    nm_assert(s);
+    nm_assert(_shell_is_name(key, -1));
+    nm_assert(to_free);
+
+    ASSERT_key_is_well_known(key);
+
+    line = g_hash_table_lookup(s->lst_idx, &key);
+
+    if (line && line->line) {
+        v = svUnescape(line->line, to_free);
+        if (!v) {
+            /* a wrongly quoted value is treated like the empty string.
+             * See also svWriteFile(), which handles unparsable values
+             * that way. */
+            nm_assert(!*to_free);
+            return "";
+        }
+        return v;
+    }
+    *to_free = NULL;
+    return NULL;
 }
 
 /* Returns the value for key. The value is either owned by @s
@@ -1116,13 +1114,13 @@ _svGetValue (shvarFile *s, const char *key, char **to_free)
  * - like svGetValueStr(), but does not ignore empty string values.
  */
 const char *
-svGetValue (shvarFile *s, const char *key, char **to_free)
+svGetValue(shvarFile *s, const char *key, char **to_free)
 {
-	g_return_val_if_fail (s, NULL);
-	g_return_val_if_fail (key, NULL);
-	g_return_val_if_fail (to_free, NULL);
+    g_return_val_if_fail(s, NULL);
+    g_return_val_if_fail(key, NULL);
+    g_return_val_if_fail(to_free, NULL);
 
-	return _svGetValue (s, key, to_free);
+    return _svGetValue(s, key, to_free);
 }
 
 /* Returns the value for key. The value is either owned by @s
@@ -1132,20 +1130,20 @@ svGetValue (shvarFile *s, const char *key, char **to_free)
  * - like svGetValueStr_cp(), but avoids cloning the value if possible.
  */
 const char *
-svGetValueStr (shvarFile *s, const char *key, char **to_free)
+svGetValueStr(shvarFile *s, const char *key, char **to_free)
 {
-	const char *value;
-
-	g_return_val_if_fail (s, NULL);
-	g_return_val_if_fail (key, NULL);
-	g_return_val_if_fail (to_free, NULL);
-
-	value = _svGetValue (s, key, to_free);
-	if (!value || !value[0]) {
-		nm_assert (!*to_free);
-		return NULL;
-	}
-	return value;
+    const char *value;
+
+    g_return_val_if_fail(s, NULL);
+    g_return_val_if_fail(key, NULL);
+    g_return_val_if_fail(to_free, NULL);
+
+    value = _svGetValue(s, key, to_free);
+    if (!value || !value[0]) {
+        nm_assert(!*to_free);
+        return NULL;
+    }
+    return value;
 }
 
 /* Returns the value for key. The returned value must be freed
@@ -1155,20 +1153,20 @@ svGetValueStr (shvarFile *s, const char *key, char **to_free)
  * - like svGetValueStr_cp(), but does not ignore an empty string.
  */
 char *
-svGetValue_cp (shvarFile *s, const char *key)
+svGetValue_cp(shvarFile *s, const char *key)
 {
-	char *to_free;
-	const char *value;
-
-	g_return_val_if_fail (s, NULL);
-	g_return_val_if_fail (key, NULL);
-
-	value = _svGetValue (s, key, &to_free);
-	if (!value) {
-		nm_assert (!to_free);
-		return NULL;
-	}
-	return to_free ?: g_strdup (value);
+    char *      to_free;
+    const char *value;
+
+    g_return_val_if_fail(s, NULL);
+    g_return_val_if_fail(key, NULL);
+
+    value = _svGetValue(s, key, &to_free);
+    if (!value) {
+        nm_assert(!to_free);
+        return NULL;
+    }
+    return to_free ?: g_strdup(value);
 }
 
 /* Returns the value for key. The returned value must be freed
@@ -1179,20 +1177,20 @@ svGetValue_cp (shvarFile *s, const char *key)
  * - like svGetValue_cp(), but returns NULL instead of an empty string.
  */
 char *
-svGetValueStr_cp (shvarFile *s, const char *key)
+svGetValueStr_cp(shvarFile *s, const char *key)
 {
-	char *to_free;
-	const char *value;
-
-	g_return_val_if_fail (s, NULL);
-	g_return_val_if_fail (key, NULL);
-
-	value = _svGetValue (s, key, &to_free);
-	if (!value || !value[0]) {
-		nm_assert (!to_free);
-		return NULL;
-	}
-	return to_free ?: g_strdup (value);
+    char *      to_free;
+    const char *value;
+
+    g_return_val_if_fail(s, NULL);
+    g_return_val_if_fail(key, NULL);
+
+    value = _svGetValue(s, key, &to_free);
+    if (!value || !value[0]) {
+        nm_assert(!to_free);
+        return NULL;
+    }
+    return to_free ?: g_strdup(value);
 }
 
 /* svGetValueBoolean:
@@ -1205,13 +1203,27 @@ svGetValueStr_cp (shvarFile *s, const char *key)
  * Returns: the parsed boolean value or @fallback.
  */
 int
-svGetValueBoolean (shvarFile *s, const char *key, int fallback)
+svGetValueBoolean(shvarFile *s, const char *key, int fallback)
 {
-	gs_free char *to_free = NULL;
-	const char *value;
+    gs_free char *to_free = NULL;
+    const char *  value;
 
-	value = _svGetValue (s, key, &to_free);
-	return svParseBoolean (value, fallback);
+    value = _svGetValue(s, key, &to_free);
+    return svParseBoolean(value, fallback);
+}
+
+/* svGetValueTernary:
+ * @s: fhe file
+ * @key: the name of the key to read
+ *
+ * Reads a value @key and converts it to a NMTernary value.
+ *
+ * Returns: the parsed NMTernary
+ */
+NMTernary
+svGetValueTernary(shvarFile *s, const char *key)
+{
+    return svGetValueBoolean(s, key, NM_TERNARY_DEFAULT);
 }
 
 /* svGetValueInt64:
@@ -1225,173 +1237,171 @@ svGetValueBoolean (shvarFile *s, const char *key, int fallback)
  * Reads a value @key and converts it to an integer using _nm_utils_ascii_str_to_int64().
  * In case of error, @errno will be set and @fallback returned. */
 gint64
-svGetValueInt64 (shvarFile *s, const char *key, guint base, gint64 min, gint64 max, gint64 fallback)
+svGetValueInt64(shvarFile *s, const char *key, guint base, gint64 min, gint64 max, gint64 fallback)
 {
-	char *to_free;
-	const char *value;
-	gint64 result;
-	int errsv;
-
-	value = _svGetValue (s, key, &to_free);
-	if (!value) {
-		nm_assert (!to_free);
-		/* indicate that the key does not exist (or has a syntax error
-		 * and svUnescape() failed). */
-		errno = ENOKEY;
-		return fallback;
-	}
-
-	result = _nm_utils_ascii_str_to_int64 (value, base, min, max, fallback);
-	if (to_free) {
-		errsv = errno;
-		g_free (to_free);
-		errno = errsv;
-	}
-	return result;
+    char *      to_free;
+    const char *value;
+    gint64      result;
+    int         errsv;
+
+    value = _svGetValue(s, key, &to_free);
+    if (!value) {
+        nm_assert(!to_free);
+        /* indicate that the key does not exist (or has a syntax error
+         * and svUnescape() failed). */
+        errno = ENOKEY;
+        return fallback;
+    }
+
+    result = _nm_utils_ascii_str_to_int64(value, base, min, max, fallback);
+    if (to_free) {
+        errsv = errno;
+        g_free(to_free);
+        errno = errsv;
+    }
+    return result;
 }
 
 gboolean
-svGetValueEnum (shvarFile *s, const char *key,
-                GType gtype, int *out_value,
-                GError **error)
+svGetValueEnum(shvarFile *s, const char *key, GType gtype, int *out_value, GError **error)
 {
-	gs_free char *to_free = NULL;
-	const char *svalue;
-	gs_free char *err_token = NULL;
-	int value;
-
-	svalue = _svGetValue (s, key, &to_free);
-	if (!svalue) {
-		/* don't touch out_value. The caller is supposed
-		 * to initialize it with the default value. */
-		return TRUE;
-	}
-
-	if (!nm_utils_enum_from_str (gtype, svalue, &value, &err_token)) {
-		g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
-		             "Invalid token \"%s\" in \"%s\" for %s",
-		             err_token, svalue, key);
-		return FALSE;
-	}
-
-	NM_SET_OUT (out_value, value);
-	return TRUE;
+    gs_free char *to_free = NULL;
+    const char *  svalue;
+    gs_free char *err_token = NULL;
+    int           value;
+
+    svalue = _svGetValue(s, key, &to_free);
+    if (!svalue) {
+        /* don't touch out_value. The caller is supposed
+         * to initialize it with the default value. */
+        return TRUE;
+    }
+
+    if (!nm_utils_enum_from_str(gtype, svalue, &value, &err_token)) {
+        g_set_error(error,
+                    NM_UTILS_ERROR,
+                    NM_UTILS_ERROR_UNKNOWN,
+                    "Invalid token \"%s\" in \"%s\" for %s",
+                    err_token,
+                    svalue,
+                    key);
+        return FALSE;
+    }
+
+    NM_SET_OUT(out_value, value);
+    return TRUE;
 }
 
 /*****************************************************************************/
 
 gboolean
-svUnsetAll (shvarFile *s, SvKeyType match_key_type)
+svUnsetAll(shvarFile *s, SvKeyType match_key_type)
 {
-	shvarLine *line;
-	gboolean changed = FALSE;
-
-	g_return_val_if_fail (s, FALSE);
-
-	c_list_for_each_entry (line, &s->lst_head, lst) {
-		ASSERT_shvarLine (line);
-		if (   line->key
-		    && _svKeyMatchesType (line->key, match_key_type)) {
-			if (nm_clear_g_free (&line->line)) {
-				ASSERT_shvarLine (line);
-				changed = TRUE;
-			}
-		}
-	}
-
-	if (changed)
-		s->modified = TRUE;
-	return changed;
+    shvarLine *line;
+    gboolean   changed = FALSE;
+
+    g_return_val_if_fail(s, FALSE);
+
+    c_list_for_each_entry (line, &s->lst_head, lst) {
+        ASSERT_shvarLine(line);
+        if (line->key && _svKeyMatchesType(line->key, match_key_type)) {
+            if (nm_clear_g_free(&line->line)) {
+                ASSERT_shvarLine(line);
+                changed = TRUE;
+            }
+        }
+    }
+
+    if (changed)
+        s->modified = TRUE;
+    return changed;
 }
 
 gboolean
-svUnsetDirtyWellknown (shvarFile *s, NMTernary new_dirty_value)
+svUnsetDirtyWellknown(shvarFile *s, NMTernary new_dirty_value)
 {
-	shvarLine *line;
-	gboolean changed = FALSE;
-
-	g_return_val_if_fail (s, FALSE);
-
-	c_list_for_each_entry (line, &s->lst_head, lst) {
-		const NMSIfcfgKeyTypeInfo *ti;
-
-		ASSERT_shvarLine (line);
-
-		if (   line->dirty
-		    && line->key
-		    && line->line
-		    && (ti = nms_ifcfg_rh_utils_is_well_known_key (line->key))
-		    && !NM_FLAGS_HAS (ti->key_flags, NMS_IFCFG_KEY_TYPE_KEEP_WHEN_DIRTY)) {
-			if (nm_clear_g_free (&line->line)) {
-				ASSERT_shvarLine (line);
-				changed = TRUE;
-			}
-		}
-
-		if (new_dirty_value != NM_TERNARY_DEFAULT)
-			line->dirty = (new_dirty_value != NM_TERNARY_FALSE);
-	}
-
-	if (changed)
-		s->modified = TRUE;
-	return changed;
+    shvarLine *line;
+    gboolean   changed = FALSE;
+
+    g_return_val_if_fail(s, FALSE);
+
+    c_list_for_each_entry (line, &s->lst_head, lst) {
+        const NMSIfcfgKeyTypeInfo *ti;
+
+        ASSERT_shvarLine(line);
+
+        if (line->dirty && line->key && line->line
+            && (ti = nms_ifcfg_rh_utils_is_well_known_key(line->key))
+            && !NM_FLAGS_HAS(ti->key_flags, NMS_IFCFG_KEY_TYPE_KEEP_WHEN_DIRTY)) {
+            if (nm_clear_g_free(&line->line)) {
+                ASSERT_shvarLine(line);
+                changed = TRUE;
+            }
+        }
+
+        if (new_dirty_value != NM_TERNARY_DEFAULT)
+            line->dirty = (new_dirty_value != NM_TERNARY_FALSE);
+    }
+
+    if (changed)
+        s->modified = TRUE;
+    return changed;
 }
 
 /* Same as svSetValueStr() but it preserves empty @value -- contrary to
  * svSetValueStr() for which "" effectively means to remove the value. */
 gboolean
-svSetValue (shvarFile *s, const char *key, const char *value)
+svSetValue(shvarFile *s, const char *key, const char *value)
 {
-	shvarLine *line;
-	shvarLine *l_shadowed;
-	gboolean changed = FALSE;
-
-	g_return_val_if_fail (s, FALSE);
-	g_return_val_if_fail (key, FALSE);
-
-	nm_assert (_shell_is_name (key, -1));
-
-	ASSERT_key_is_well_known (key);
-
-	line = g_hash_table_lookup (s->lst_idx, &key);
-	if (   line
-	    && (l_shadowed = line->prev_shadowed)) {
-		/* if we find multiple entries for the same key, we can
-		 * delete the shadowed ones. */
-		line->prev_shadowed = NULL;
-		changed = TRUE;
-		do {
-			shvarLine *l = l_shadowed;
-
-			l_shadowed = l_shadowed->prev_shadowed;
-			line_free (l);
-		} while (l_shadowed);
-	}
-
-	if (!value) {
-		if (line) {
-			/* We only clear the value, but leave the line entry. This way, if we
-			 * happen to re-add the value, we write it to the same line again. */
-			if (nm_clear_g_free (&line->line)) {
-				changed = TRUE;
-			}
-		}
-	} else {
-		if (!line) {
-			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))
-				changed = TRUE;
-		}
-	}
-
-	if (changed)
-		s->modified = TRUE;
-	return changed;
+    shvarLine *line;
+    shvarLine *l_shadowed;
+    gboolean   changed = FALSE;
+
+    g_return_val_if_fail(s, FALSE);
+    g_return_val_if_fail(key, FALSE);
+
+    nm_assert(_shell_is_name(key, -1));
+
+    ASSERT_key_is_well_known(key);
+
+    line = g_hash_table_lookup(s->lst_idx, &key);
+    if (line && (l_shadowed = line->prev_shadowed)) {
+        /* if we find multiple entries for the same key, we can
+         * delete the shadowed ones. */
+        line->prev_shadowed = NULL;
+        changed             = TRUE;
+        do {
+            shvarLine *l = l_shadowed;
+
+            l_shadowed = l_shadowed->prev_shadowed;
+            line_free(l);
+        } while (l_shadowed);
+    }
+
+    if (!value) {
+        if (line) {
+            /* We only clear the value, but leave the line entry. This way, if we
+             * happen to re-add the value, we write it to the same line again. */
+            if (nm_clear_g_free(&line->line)) {
+                changed = TRUE;
+            }
+        }
+    } else {
+        if (!line) {
+            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))
+                changed = TRUE;
+        }
+    }
+
+    if (changed)
+        s->modified = TRUE;
+    return changed;
 }
 
 /* Set the variable <key> equal to the value <value>.
@@ -1400,53 +1410,62 @@ svSetValue (shvarFile *s, const char *key, const char *value)
  * to the bottom of the file.
  */
 gboolean
-svSetValueStr (shvarFile *s, const char *key, const char *value)
+svSetValueStr(shvarFile *s, const char *key, const char *value)
 {
-	return svSetValue (s, key, value && value[0] ? value : NULL);
+    return svSetValue(s, key, value && value[0] ? value : NULL);
 }
 
 gboolean
-svSetValueInt64 (shvarFile *s, const char *key, gint64 value)
+svSetValueInt64(shvarFile *s, const char *key, gint64 value)
 {
-	char buf[NM_DECIMAL_STR_MAX (value)];
+    char buf[NM_DECIMAL_STR_MAX(value)];
+
+    return svSetValue(s, key, nm_sprintf_buf(buf, "%" G_GINT64_FORMAT, value));
+}
 
-	return svSetValue (s, key, nm_sprintf_buf (buf, "%"G_GINT64_FORMAT, value));
+gboolean
+svSetValueInt64_cond(shvarFile *s, const char *key, gboolean do_set, gint64 value)
+{
+    if (do_set)
+        return svSetValueInt64(s, key, value);
+    else
+        return svUnsetValue(s, key);
 }
 
 gboolean
-svSetValueInt64_cond (shvarFile *s, const char *key, gboolean do_set, gint64 value)
+svSetValueBoolean(shvarFile *s, const char *key, gboolean value)
 {
-	if (do_set)
-		return svSetValueInt64 (s, key, value);
-	else
-		return svUnsetValue (s, key);
+    return svSetValue(s, key, value ? "yes" : "no");
 }
 
 gboolean
-svSetValueBoolean (shvarFile *s, const char *key, gboolean value)
+svSetValueTernary(shvarFile *s, const char *key, NMTernary value)
 {
-	return svSetValue (s, key, value ? "yes" : "no");
+    if (NM_IN_SET(value, NM_TERNARY_TRUE, NM_TERNARY_FALSE))
+        return svSetValueBoolean(s, key, (gboolean) value);
+    else
+        return svUnsetValue(s, key);
 }
 
 gboolean
-svSetValueBoolean_cond_true (shvarFile *s, const char *key, gboolean value)
+svSetValueBoolean_cond_true(shvarFile *s, const char *key, gboolean value)
 {
-	return svSetValue (s, key, value ? "yes" : NULL);
+    return svSetValue(s, key, value ? "yes" : NULL);
 }
 
 gboolean
-svSetValueEnum (shvarFile *s, const char *key, GType gtype, int value)
+svSetValueEnum(shvarFile *s, const char *key, GType gtype, int value)
 {
-	gs_free char *v = NULL;
+    gs_free char *v = NULL;
 
-	v = _nm_utils_enum_to_str_full (gtype, value, " ", NULL);
-	return svSetValueStr (s, key, v);
+    v = _nm_utils_enum_to_str_full(gtype, value, " ", NULL);
+    return svSetValueStr(s, key, v);
 }
 
 gboolean
-svUnsetValue (shvarFile *s, const char *key)
+svUnsetValue(shvarFile *s, const char *key)
 {
-	return svSetValue (s, key, NULL);
+    return svSetValue(s, key, NULL);
 }
 
 /*****************************************************************************/
@@ -1458,97 +1477,109 @@ svUnsetValue (shvarFile *s, const char *key)
  * open() syscall.
  */
 gboolean
-svWriteFile (shvarFile *s, int mode, GError **error)
+svWriteFile(shvarFile *s, int mode, GError **error)
 {
-	FILE *f;
-	int tmpfd;
-	CList *current;
-	int errsv;
-
-	if (s->modified) {
-		if (s->fd == -1)
-			s->fd = open (s->fileName, O_WRONLY | O_CREAT | O_CLOEXEC, mode);
-		if (s->fd == -1) {
-			errsv = errno;
-			g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errsv),
-			             "Could not open file '%s' for writing: %s",
-			             s->fileName, nm_strerror_native (errsv));
-			return FALSE;
-		}
-		if (ftruncate (s->fd, 0) < 0) {
-			errsv = errno;
-			g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errsv),
-			             "Could not overwrite file '%s': %s",
-			             s->fileName, nm_strerror_native (errsv));
-			return FALSE;
-		}
-
-		tmpfd = fcntl (s->fd, F_DUPFD_CLOEXEC, 0);
-		if (tmpfd == -1) {
-			errsv = errno;
-			g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errsv),
-			             "Internal error writing file '%s': %s",
-			             s->fileName, nm_strerror_native (errsv));
-			return FALSE;
-		}
-		f = fdopen (tmpfd, "w");
-		if (!f) {
-			errsv = errno;
-			g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errsv),
-			             "Internal error writing file '%s': %s",
-			             s->fileName, nm_strerror_native (errsv));
-			return FALSE;
-		}
-		fseek (f, 0, SEEK_SET);
-		c_list_for_each (current, &s->lst_head) {
-			const shvarLine *line = c_list_entry (current, shvarLine, lst);
-			const char *str;
-			char *s_tmp;
-			gboolean valid_value;
-
-			ASSERT_shvarLine (line);
-
-			if (!line->key) {
-				str = nm_str_skip_leading_spaces (line->line);
-				if (NM_IN_SET (str[0], '\0', '#'))
-					fprintf (f, "%s\n", line->line);
-				else
-					fprintf (f, "#NM: %s\n", line->line);
-				continue;
-			}
-
-			if (!line->line)
-				continue;
-
-			/* we check that the assignment can be properly unescaped. */
-			valid_value = !!svUnescape (line->line, &s_tmp);
-			g_free (s_tmp);
-
-			if (valid_value)
-				fprintf (f, "%s=%s\n", line->key_with_prefix, line->line);
-			else {
-				fprintf (f, "%s=\n", line->key);
-				fprintf (f, "#NM: %s=%s\n", line->key_with_prefix, line->line);
-			}
-		}
-		fclose (f);
-	}
-
-	return TRUE;
+    FILE * f;
+    int    tmpfd;
+    CList *current;
+    int    errsv;
+
+    if (s->modified) {
+        if (s->fd == -1)
+            s->fd = open(s->fileName, O_WRONLY | O_CREAT | O_CLOEXEC, mode);
+        if (s->fd == -1) {
+            errsv = errno;
+            g_set_error(error,
+                        G_FILE_ERROR,
+                        g_file_error_from_errno(errsv),
+                        "Could not open file '%s' for writing: %s",
+                        s->fileName,
+                        nm_strerror_native(errsv));
+            return FALSE;
+        }
+        if (ftruncate(s->fd, 0) < 0) {
+            errsv = errno;
+            g_set_error(error,
+                        G_FILE_ERROR,
+                        g_file_error_from_errno(errsv),
+                        "Could not overwrite file '%s': %s",
+                        s->fileName,
+                        nm_strerror_native(errsv));
+            return FALSE;
+        }
+
+        tmpfd = fcntl(s->fd, F_DUPFD_CLOEXEC, 0);
+        if (tmpfd == -1) {
+            errsv = errno;
+            g_set_error(error,
+                        G_FILE_ERROR,
+                        g_file_error_from_errno(errsv),
+                        "Internal error writing file '%s': %s",
+                        s->fileName,
+                        nm_strerror_native(errsv));
+            return FALSE;
+        }
+        f = fdopen(tmpfd, "w");
+        if (!f) {
+            errsv = errno;
+            g_set_error(error,
+                        G_FILE_ERROR,
+                        g_file_error_from_errno(errsv),
+                        "Internal error writing file '%s': %s",
+                        s->fileName,
+                        nm_strerror_native(errsv));
+            return FALSE;
+        }
+        fseek(f, 0, SEEK_SET);
+        c_list_for_each (current, &s->lst_head) {
+            const shvarLine *line = c_list_entry(current, shvarLine, lst);
+            const char *     str;
+            char *           s_tmp;
+            gboolean         valid_value;
+
+            ASSERT_shvarLine(line);
+
+            if (!line->key) {
+                str = nm_str_skip_leading_spaces(line->line);
+                if (NM_IN_SET(str[0], '\0', '#'))
+                    fprintf(f, "%s\n", line->line);
+                else
+                    fprintf(f, "#NM: %s\n", line->line);
+                continue;
+            }
+
+            if (!line->line)
+                continue;
+
+            /* we check that the assignment can be properly unescaped. */
+            valid_value = !!svUnescape(line->line, &s_tmp);
+            g_free(s_tmp);
+
+            if (valid_value)
+                fprintf(f, "%s=%s\n", line->key_with_prefix, line->line);
+            else {
+                fprintf(f, "%s=\n", line->key);
+                fprintf(f, "#NM: %s=%s\n", line->key_with_prefix, line->line);
+            }
+        }
+        fclose(f);
+    }
+
+    return TRUE;
 }
 
 void
-svCloseFile (shvarFile *s)
+svCloseFile(shvarFile *s)
 {
-	shvarLine *line;
+    shvarLine *line;
 
-	g_return_if_fail (s != NULL);
+    g_return_if_fail(s != NULL);
 
-	if (s->fd >= 0)
-		nm_close (s->fd);
-	g_free (s->fileName);
-	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);
+    if (s->fd >= 0)
+        nm_close(s->fd);
+    g_free(s->fileName);
+    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);
 }