summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2019-04-17 11:13:17 +0200
committerThomas Haller <thaller@redhat.com>2019-04-17 11:13:17 +0200
commitb5a86c10a06584cc64bab9e74226b48f12c9d5b1 (patch)
treec9715998824e3196a0879a89ef09ad081e68c1f8
parentf4afb38bd90f2d96d5f8f4e60d50d7d65d24cbbe (diff)
parenta7d1e14e6deb33dc51902bf76c51755e9915bb1a (diff)
downloadNetworkManager-b5a86c10a06584cc64bab9e74226b48f12c9d5b1.tar.gz
all: merge branch 'th/strsplit-pt3'
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/merge_requests/107
-rw-r--r--clients/common/nm-meta-setting-desc.c46
-rw-r--r--clients/common/nm-meta-setting-desc.h2
-rw-r--r--libnm-core/nm-setting-ip-config.c38
-rw-r--r--libnm-core/tests/test-general.c64
-rw-r--r--libnm-core/tests/test-setting.c6
-rw-r--r--shared/nm-utils/nm-shared-utils.c228
-rw-r--r--shared/nm-utils/nm-shared-utils.h71
7 files changed, 334 insertions, 121 deletions
diff --git a/clients/common/nm-meta-setting-desc.c b/clients/common/nm-meta-setting-desc.c
index 1de51dc191..2a4e5b8269 100644
--- a/clients/common/nm-meta-setting-desc.c
+++ b/clients/common/nm-meta-setting-desc.c
@@ -167,12 +167,16 @@ _value_str_as_index_list (const char *value, gsize *out_len)
#define MULTILIST_WITH_ESCAPE_CHARS NM_ASCII_SPACES","
+#define ESCAPED_TOKENS_DELIMTER ','
+#define ESCAPED_TOKENS_DELIMTERS ","
+
typedef enum {
VALUE_STRSPLIT_MODE_STRIPPED,
VALUE_STRSPLIT_MODE_OBJLIST,
VALUE_STRSPLIT_MODE_OBJLIST_WITH_ESCAPE,
VALUE_STRSPLIT_MODE_MULTILIST,
VALUE_STRSPLIT_MODE_MULTILIST_WITH_ESCAPE,
+ VALUE_STRSPLIT_MODE_ESCAPED_TOKENS,
} ValueStrsplitMode;
static const char *
@@ -211,6 +215,10 @@ _value_strsplit (const char *value,
case VALUE_STRSPLIT_MODE_MULTILIST_WITH_ESCAPE:
strv = nm_utils_strsplit_set_full (value, MULTILIST_WITH_ESCAPE_CHARS, NM_UTILS_STRSPLIT_SET_FLAGS_ALLOW_ESCAPING);
break;
+ case VALUE_STRSPLIT_MODE_ESCAPED_TOKENS:
+ strv = nm_utils_escaped_tokens_split (value, ESCAPED_TOKENS_DELIMTERS);
+ NM_SET_OUT (out_len, NM_PTRARRAY_LEN (strv));
+ return g_steal_pointer (&strv);
default:
nm_assert_not_reached ();
break;
@@ -1882,9 +1890,11 @@ _set_fcn_multilist (ARGS_SET_FCN)
}
strv = _value_strsplit (value,
- property_info->property_typ_data->subtype.multilist.strsplit_with_escape
- ? VALUE_STRSPLIT_MODE_MULTILIST_WITH_ESCAPE
- : VALUE_STRSPLIT_MODE_MULTILIST,
+ property_info->property_typ_data->subtype.multilist.strsplit_escaped_tokens
+ ? VALUE_STRSPLIT_MODE_ESCAPED_TOKENS
+ : ( property_info->property_typ_data->subtype.multilist.strsplit_with_escape
+ ? VALUE_STRSPLIT_MODE_OBJLIST_WITH_ESCAPE
+ : VALUE_STRSPLIT_MODE_OBJLIST),
&nstrv);
j = 0;
@@ -3053,9 +3063,7 @@ _get_fcn_objlist (ARGS_GET_FCN)
num = property_info->property_typ_data->subtype.objlist.get_num_fcn (setting);
for (idx = 0; idx < num; idx++) {
-#if NM_MORE_ASSERTS
gsize start_offset;
-#endif
if (!str)
str = g_string_new (NULL);
@@ -3063,19 +3071,27 @@ _get_fcn_objlist (ARGS_GET_FCN)
if ( get_type == NM_META_ACCESSOR_GET_TYPE_PRETTY
&& property_info->property_typ_data->subtype.objlist.delimit_pretty_with_semicolon)
g_string_append (str, "; ");
- else
+ else {
+ G_STATIC_ASSERT_EXPR (ESCAPED_TOKENS_DELIMTER == ',');
g_string_append (str, ", ");
+ }
}
-#if NM_MORE_ASSERTS
start_offset = str->len;
-#endif
property_info->property_typ_data->subtype.objlist.obj_to_str_fcn (get_type,
setting,
idx,
str);
+ if (start_offset == str->len) {
+ /* nothing was appended. Remove the delimiter again. */
+ nm_assert_not_reached ();
+ if (str->len > 0)
+ g_string_truncate (str, str->len - 2);
+ continue;
+ }
+
#if NM_MORE_ASSERTS
nm_assert (start_offset < str->len);
if ( property_info->property_typ_data->subtype.objlist.strsplit_with_escape
@@ -3267,9 +3283,11 @@ _set_fcn_objlist (ARGS_SET_FCN)
}
strv = _value_strsplit (value,
- property_info->property_typ_data->subtype.objlist.strsplit_with_escape
- ? VALUE_STRSPLIT_MODE_OBJLIST_WITH_ESCAPE
- : VALUE_STRSPLIT_MODE_OBJLIST,
+ property_info->property_typ_data->subtype.objlist.strsplit_escaped_tokens
+ ? VALUE_STRSPLIT_MODE_ESCAPED_TOKENS
+ : ( property_info->property_typ_data->subtype.objlist.strsplit_with_escape
+ ? VALUE_STRSPLIT_MODE_OBJLIST_WITH_ESCAPE
+ : VALUE_STRSPLIT_MODE_OBJLIST),
&nstrv);
if (_SET_FCN_DO_SET_ALL (modifier, value)) {
@@ -3382,7 +3400,7 @@ _objlist_obj_to_str_fcn_ip_config_routing_rules (NMMetaAccessorGetType get_type,
NULL,
NULL);
if (s)
- g_string_append (str, s);
+ nm_utils_escaped_tokens_escape_gstr (s, ESCAPED_TOKENS_DELIMTERS, str);
}
static gboolean
@@ -5652,7 +5670,7 @@ static const NMMetaPropertyInfo *const property_infos_IP4_CONFIG[] = {
.obj_to_str_fcn = _objlist_obj_to_str_fcn_ip_config_routing_rules,
.set_fcn = _objlist_set_fcn_ip_config_routing_rules,
.remove_by_idx_fcn_u = OBJLIST_REMOVE_BY_IDX_FCN_U (NMSettingIPConfig, nm_setting_ip_config_remove_routing_rule),
- .strsplit_with_escape = TRUE,
+ .strsplit_escaped_tokens = TRUE,
),
),
),
@@ -5860,7 +5878,7 @@ static const NMMetaPropertyInfo *const property_infos_IP6_CONFIG[] = {
.obj_to_str_fcn = _objlist_obj_to_str_fcn_ip_config_routing_rules,
.set_fcn = _objlist_set_fcn_ip_config_routing_rules,
.remove_by_idx_fcn_u = OBJLIST_REMOVE_BY_IDX_FCN_U (NMSettingIPConfig, nm_setting_ip_config_remove_routing_rule),
- .strsplit_with_escape = TRUE,
+ .strsplit_escaped_tokens = TRUE,
),
),
),
diff --git a/clients/common/nm-meta-setting-desc.h b/clients/common/nm-meta-setting-desc.h
index 15c0e81eb0..44a6827b7e 100644
--- a/clients/common/nm-meta-setting-desc.h
+++ b/clients/common/nm-meta-setting-desc.h
@@ -281,6 +281,7 @@ struct _NMMetaPropertyTypData {
void (*remove_by_idx_fcn_s) (NMSetting *setting, int idx);
gboolean (*remove_by_value_fcn) (NMSetting *setting, const char *item);
bool strsplit_with_escape:1;
+ bool strsplit_escaped_tokens:1;
} multilist;
struct {
guint (*get_num_fcn) (NMSetting *setting);
@@ -297,6 +298,7 @@ struct _NMMetaPropertyTypData {
void (*remove_by_idx_fcn_s) (NMSetting *setting, int idx);
bool delimit_pretty_with_semicolon:1;
bool strsplit_with_escape:1;
+ bool strsplit_escaped_tokens:1;
} objlist;
struct {
gboolean (*set_fcn) (NMSetting *setting,
diff --git a/libnm-core/nm-setting-ip-config.c b/libnm-core/nm-setting-ip-config.c
index 48815c3484..d4a38161dd 100644
--- a/libnm-core/nm-setting-ip-config.c
+++ b/libnm-core/nm-setting-ip-config.c
@@ -2954,9 +2954,8 @@ nm_ip_routing_rule_from_string (const char *str,
GError **error)
{
nm_auto_unref_ip_routing_rule NMIPRoutingRule *self = NULL;
- gs_free char *str_clone = NULL;
- char *str_remainder;
- char *str_word;
+ gs_free const char **tokens = NULL;
+ gsize i_token;
gboolean any_words = FALSE;
char *word0 = NULL;
char *word1 = NULL;
@@ -3022,10 +3021,9 @@ nm_ip_routing_rule_from_string (const char *str,
addr_family = _rr_string_addr_family_from_flags (to_string_flags);
- str_clone = g_strdup (str);
- str_remainder = str_clone;
-
- while ((str_word = nm_utils_str_simpletokens_extract_next (&str_remainder))) {
+ tokens = nm_utils_escaped_tokens_split (str, NM_ASCII_SPACES);
+ for (i_token = 0; tokens && tokens[i_token]; i_token++) {
+ char *str_word = (char *) tokens[i_token];
any_words = TRUE;
if (!word0)
@@ -3325,24 +3323,6 @@ _rr_string_append_inet_addr (GString *str,
}
}
-static void
-_rr_string_append_escaped (GString *str,
- const char *s)
-{
- for (; s[0]; s++) {
- /* We need to escape spaces and '\\', because that
- * is what nm_utils_str_simpletokens_extract_next() uses to split
- * words.
- * We also escape ',' because nmcli uses that to concatenate values.
- * We also escape ';', in case somebody wants to use ';' instead of ','.
- */
- if ( NM_IN_SET (s[0], '\\', ',', ';')
- || g_ascii_isspace (s[0]))
- g_string_append_c (str, '\\');
- g_string_append_c (str, s[0]);
- }
-}
-
/**
* nm_ip_routing_rule_to_string:
* @self: the #NMIPRoutingRule instance to convert to string.
@@ -3486,13 +3466,17 @@ nm_ip_routing_rule_to_string (const NMIPRoutingRule *self,
if (self->iifname) {
g_string_append (nm_gstring_add_space_delimiter (str),
"iif ");
- _rr_string_append_escaped (str, self->iifname);
+ nm_utils_escaped_tokens_escape_gstr (self->iifname,
+ NM_ASCII_SPACES,
+ str);
}
if (self->oifname) {
g_string_append (nm_gstring_add_space_delimiter (str),
"oif ");
- _rr_string_append_escaped (str, self->oifname);
+ nm_utils_escaped_tokens_escape_gstr (self->oifname,
+ NM_ASCII_SPACES,
+ str);
}
if (self->table != 0) {
diff --git a/libnm-core/tests/test-general.c b/libnm-core/tests/test-general.c
index f6bbd626c3..dfaba1cac5 100644
--- a/libnm-core/tests/test-general.c
+++ b/libnm-core/tests/test-general.c
@@ -264,8 +264,8 @@ _do_test_nm_utils_strsplit_set_f_one (NMUtilsStrsplitSetFlags flags,
gsize words_len,
const char *const*exp_words)
{
- const char *DELIMITERS = " \t\n";
-#define DELIMITERS_C ' ', '\t', '\n'
+#define DELIMITERS " \n"
+#define DELIMITERS_C ' ', '\n'
gs_free const char **words = NULL;
gsize i, j, k;
@@ -510,6 +510,47 @@ _do_test_nm_utils_strsplit_set_f (NMUtilsStrsplitSetFlags flags,
##__VA_ARGS__)
static void
+_do_test_nm_utils_strsplit_set_simple (NMUtilsStrsplitSetFlags flags,
+ const char *str,
+ gsize words_len,
+ const char *const*exp_words)
+{
+ gs_free const char **tokens = NULL;
+ gsize n_tokens;
+
+ tokens = nm_utils_strsplit_set_full (str, DELIMITERS, flags);
+
+ if (!tokens) {
+ g_assert_cmpint (words_len, ==, 0);
+ return;
+ }
+
+ g_assert (str && str[0]);
+ g_assert_cmpint (words_len, >, 0);
+ n_tokens = NM_PTRARRAY_LEN (tokens);
+
+ if (_nm_utils_strv_cmp_n (exp_words, words_len, tokens, -1) != 0) {
+ gsize i;
+
+ g_print (">>> split \"%s\" (flags %x) got %zu tokens (%zu expected)\n", str, (guint) flags, n_tokens, words_len);
+ for (i = 0; i < NM_MAX (n_tokens, words_len); i++) {
+ const char *s1 = i < n_tokens ? tokens[i] : NULL;
+ const char *s2 = i < words_len ? exp_words[i] : NULL;
+
+ g_print (">>> [%zu]: %s - %s%s%s vs. %s%s%s\n",
+ i,
+ nm_streq0 (s1, s2) ? "same" : "diff",
+ NM_PRINT_FMT_QUOTE_STRING (s1),
+ NM_PRINT_FMT_QUOTE_STRING (s2));
+ }
+ g_assert_not_reached ();
+ }
+ g_assert_cmpint (words_len, ==, NM_PTRARRAY_LEN (tokens));
+}
+#define do_test_nm_utils_strsplit_set_simple(flags, str, ...) \
+ _do_test_nm_utils_strsplit_set_simple ((flags), (str), NM_NARG (__VA_ARGS__), NM_MAKE_STRV (__VA_ARGS__))
+
+static void
test_nm_utils_strsplit_set (void)
{
gs_unref_ptrarray GPtrArray *words_exp = NULL;
@@ -534,8 +575,8 @@ test_nm_utils_strsplit_set (void)
do_test_nm_utils_strsplit_set (FALSE, NULL);
do_test_nm_utils_strsplit_set (FALSE, "");
- do_test_nm_utils_strsplit_set (FALSE, "\t");
- do_test_nm_utils_strsplit_set (FALSE, " \t\n");
+ do_test_nm_utils_strsplit_set (FALSE, "\n");
+ do_test_nm_utils_strsplit_set (TRUE, " \t\n", "\t");
do_test_nm_utils_strsplit_set (FALSE, "a", "a");
do_test_nm_utils_strsplit_set (FALSE, "a b", "a", "b");
do_test_nm_utils_strsplit_set (FALSE, "a\rb", "a\rb");
@@ -610,6 +651,21 @@ test_nm_utils_strsplit_set (void)
words_len,
(const char *const*) words_exp->pdata);
}
+
+ do_test_nm_utils_strsplit_set_simple (NM_UTILS_STRSPLIT_SET_FLAGS_ESCAPED, "\t", "\t");
+ do_test_nm_utils_strsplit_set_simple (NM_UTILS_STRSPLIT_SET_FLAGS_ESCAPED | NM_UTILS_STRSPLIT_SET_FLAGS_STRSTRIP, "\t");
+ do_test_nm_utils_strsplit_set_simple (NM_UTILS_STRSPLIT_SET_FLAGS_ESCAPED | NM_UTILS_STRSPLIT_SET_FLAGS_STRSTRIP | NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY,
+ "\t", "");
+ do_test_nm_utils_strsplit_set_simple (NM_UTILS_STRSPLIT_SET_FLAGS_ESCAPED | NM_UTILS_STRSPLIT_SET_FLAGS_STRSTRIP | NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY,
+ "\t\\\t\t\t\\\t", "\t\t\t\t");
+
+ do_test_nm_utils_strsplit_set_simple (NM_UTILS_STRSPLIT_SET_FLAGS_ESCAPED, "\ta", "\ta");
+ do_test_nm_utils_strsplit_set_simple (NM_UTILS_STRSPLIT_SET_FLAGS_ESCAPED | NM_UTILS_STRSPLIT_SET_FLAGS_STRSTRIP, "\ta", "a");
+ do_test_nm_utils_strsplit_set_simple (NM_UTILS_STRSPLIT_SET_FLAGS_ESCAPED, "\ta\\ b\t\\ ", "\ta b\t ");
+ do_test_nm_utils_strsplit_set_simple (NM_UTILS_STRSPLIT_SET_FLAGS_ESCAPED | NM_UTILS_STRSPLIT_SET_FLAGS_STRSTRIP, "\ta\\ b\t\\ \t", "a b\t ");
+ do_test_nm_utils_strsplit_set_simple (NM_UTILS_STRSPLIT_SET_FLAGS_ESCAPED, "a\\ b", "a ", "b");
+ do_test_nm_utils_strsplit_set_simple (NM_UTILS_STRSPLIT_SET_FLAGS_ESCAPED, "\ta\\ b", "\ta ", "b");
+ do_test_nm_utils_strsplit_set_simple (NM_UTILS_STRSPLIT_SET_FLAGS_ESCAPED | NM_UTILS_STRSPLIT_SET_FLAGS_STRSTRIP, "\ta\\ b", "a ", "b");
}
/*****************************************************************************/
diff --git a/libnm-core/tests/test-setting.c b/libnm-core/tests/test-setting.c
index 70c0d6a4c6..57af014021 100644
--- a/libnm-core/tests/test-setting.c
+++ b/libnm-core/tests/test-setting.c
@@ -2886,7 +2886,7 @@ test_routing_rule (gconstpointer test_data)
char ifname_buf[16];
_rr_from_str ("priority 5 from 0.0.0.0 table 1",
- " from 0.0.0\\.0 \\priority 5 lookup 1 ");
+ " from 0.0.0.0 priority 5 lookup 1 ");
_rr_from_str ("priority 5 from 0.0.0.0/0 table 4");
_rr_from_str ("priority 5 to 0.0.0.0 table 6");
_rr_from_str ("priority 5 to 0.0.0.0 table 254",
@@ -2905,7 +2905,7 @@ test_routing_rule (gconstpointer test_data)
"priority 5 from :: to ::/0 table 0x19 fwmark 0x00/4294967295");
_rr_from_str ("priority 5 from :: iif aab table 25");
_rr_from_str ("priority 5 from :: iif aab oif er table 25",
- "priority 5 from :: table 0x19 dev \\a\\a\\b oif er");
+ "priority 5 from :: table 0x19 dev aab oif er");
_rr_from_str ("priority 5 from :: iif a\\\\303b table 25");
_rr_from_str ("priority 5 to 0.0.0.0 sport 10 table 6",
"priority 5 to 0.0.0.0 sport 10-10 table 6");
@@ -2952,7 +2952,7 @@ test_routing_rule (gconstpointer test_data)
g_assert_cmpint (0x10, ==, nm_ip_routing_rule_get_tos (rr1));
nm_clear_pointer (&rr1, nm_ip_routing_rule_unref);
- rr1 = _rr_from_str_get ("priority 5 from :: iif a\\\\303\\\\261\\,x\\;b table 254",
+ rr1 = _rr_from_str_get ("priority 5 from :: iif a\\\\303\\\\261,x;b table 254",
"priority 5 from :: iif a\\\\303\\\\261,x;b table 254");
g_assert_cmpstr (nm_ip_routing_rule_get_iifname (rr1), ==, "a\\303\\261,x;b");
success = nm_ip_routing_rule_get_xifname_bin (rr1, FALSE, ifname_buf);
diff --git a/shared/nm-utils/nm-shared-utils.c b/shared/nm-utils/nm-shared-utils.c
index 879d1e7b9e..06cd481ebb 100644
--- a/shared/nm-utils/nm-shared-utils.c
+++ b/shared/nm-utils/nm-shared-utils.c
@@ -1027,8 +1027,10 @@ nm_utils_strsplit_set_full (const char *str,
const char *c_str;
char *s;
guint8 ch_lookup[256];
- const gboolean f_allow_escaping = NM_FLAGS_HAS (flags, NM_UTILS_STRSPLIT_SET_FLAGS_ALLOW_ESCAPING);
- const gboolean f_preseve_empty = NM_FLAGS_HAS (flags, NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY);
+ const gboolean f_escaped = NM_FLAGS_HAS (flags, NM_UTILS_STRSPLIT_SET_FLAGS_ESCAPED);
+ const gboolean f_allow_escaping = f_escaped || NM_FLAGS_HAS (flags, NM_UTILS_STRSPLIT_SET_FLAGS_ALLOW_ESCAPING);
+ const gboolean f_preserve_empty = NM_FLAGS_HAS (flags, NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY);
+ const gboolean f_strstrip = NM_FLAGS_HAS (flags, NM_UTILS_STRSPLIT_SET_FLAGS_STRSTRIP);
if (!str)
return NULL;
@@ -1042,7 +1044,7 @@ nm_utils_strsplit_set_full (const char *str,
nm_assert ( !f_allow_escaping
|| !_char_lookup_has (ch_lookup, '\\'));
- if (!f_preseve_empty) {
+ if (!f_preserve_empty) {
while (_char_lookup_has (ch_lookup, str[0]))
str++;
}
@@ -1055,6 +1057,17 @@ nm_utils_strsplit_set_full (const char *str,
return NULL;
}
+#define _char_is_escaped(str_start, str_cur) \
+ ({ \
+ const char *const _str_start = (str_start); \
+ const char *const _str_cur = (str_cur); \
+ const char *_str_i = (_str_cur); \
+ \
+ while ( _str_i > _str_start \
+ && _str_i[-1] == '\\') \
+ _str_i--; \
+ (((_str_cur - _str_i) % 2) != 0); \
+ })
num_tokens = 1;
c_str = str;
@@ -1069,23 +1082,17 @@ nm_utils_strsplit_set_full (const char *str,
/* we assume escapings are not frequent. After we found
* this delimiter, check whether it was escaped by counting
* the backslashed before. */
- if (f_allow_escaping) {
- const char *c2 = c_str;
-
- while ( c2 > str
- && c2[-1] == '\\')
- c2--;
- if (((c_str - c2) % 2) != 0) {
- /* the delimiter is escaped. This was not an accepted delimiter. */
- c_str++;
- continue;
- }
+ if ( f_allow_escaping
+ && _char_is_escaped (str, c_str)) {
+ /* the delimiter is escaped. This was not an accepted delimiter. */
+ c_str++;
+ continue;
}
c_str++;
/* if we drop empty tokens, then we now skip over all consecutive delimiters. */
- if (!f_preseve_empty) {
+ if (!f_preserve_empty) {
while (_char_lookup_has (ch_lookup, c_str[0]))
c_str++;
if (c_str[0] == '\0')
@@ -1115,10 +1122,10 @@ done1:
ptr[i_token++] = s;
if (s[0] == '\0') {
- nm_assert (f_preseve_empty);
+ nm_assert (f_preserve_empty);
goto done2;
}
- nm_assert ( f_preseve_empty
+ nm_assert ( f_preserve_empty
|| !_char_lookup_has (ch_lookup, s[0]));
while (!_char_lookup_has (ch_lookup, s[0])) {
@@ -1138,7 +1145,7 @@ done1:
s[0] = '\0';
s++;
- if (!f_preseve_empty) {
+ if (!f_preserve_empty) {
while (_char_lookup_has (ch_lookup, s[0]))
s++;
if (s[0] == '\0')
@@ -1150,9 +1157,141 @@ done2:
nm_assert (i_token == num_tokens);
ptr[i_token] = NULL;
+ if (f_strstrip) {
+ gsize i;
+
+ i_token = 0;
+ for (i = 0; ptr[i]; i++) {
+
+ s = (char *) nm_str_skip_leading_spaces (ptr[i]);
+ if (s[0] != '\0') {
+ char *s_last;
+
+ s_last = &s[strlen (s) - 1];
+ while ( s_last > s
+ && g_ascii_isspace (s_last[0])
+ && ( ! f_allow_escaping
+ || !_char_is_escaped (s, s_last)))
+ (s_last--)[0] = '\0';
+ }
+
+ if ( !f_preserve_empty
+ && s[0] == '\0')
+ continue;
+
+ ptr[i_token++] = s;
+ }
+
+ if (i_token == 0) {
+ g_free (ptr);
+ return NULL;
+ }
+ ptr[i_token] = NULL;
+ }
+
+ if (f_escaped) {
+ gsize i, j;
+
+ /* We no longer need ch_lookup for its original purpose. Modify it, so it
+ * can detect the delimiters, '\\', and (optionally) whitespaces. */
+ ch_lookup[((guint8) '\\')] = 1;
+ if (f_strstrip) {
+ for (i = 0; NM_ASCII_SPACES[i]; i++)
+ ch_lookup[((guint8) (NM_ASCII_SPACES[i]))] = 1;
+ }
+
+ for (i_token = 0; ptr[i_token]; i_token++) {
+ s = (char *) ptr[i_token];
+ j = 0;
+ for (i = 0; s[i] != '\0'; ) {
+ if ( s[i] == '\\'
+ && _char_lookup_has (ch_lookup, s[i + 1]))
+ i++;
+ s[j++] = s[i++];
+ }
+ s[j] = '\0';
+ }
+ }
+
return ptr;
}
+/*****************************************************************************/
+
+const char *
+nm_utils_escaped_tokens_escape (const char *str,
+ const char *delimiters,
+ char **out_to_free)
+{
+ guint8 ch_lookup[256];
+ char *ret;
+ gsize str_len;
+ gsize alloc_len;
+ gsize n_escapes;
+ gsize i, j;
+ gboolean escape_trailing_space;
+
+ if (!delimiters) {
+ nm_assert (delimiters);
+ delimiters = NM_ASCII_SPACES;
+ }
+
+ if (!str || str[0] == '\0') {
+ *out_to_free = NULL;
+ return str;
+ }
+
+ _char_lookup_table_init (ch_lookup, delimiters);
+
+ /* also mark '\\' as requiring escaping. */
+ ch_lookup[((guint8) '\\')] = 1;
+
+ n_escapes = 0;
+ for (i = 0; str[i] != '\0'; i++) {
+ if (_char_lookup_has (ch_lookup, str[i]))
+ n_escapes++;
+ }
+
+ str_len = i;
+ nm_assert (str_len > 0 && strlen (str) == str_len);
+
+ escape_trailing_space = !_char_lookup_has (ch_lookup, str[str_len - 1])
+ && g_ascii_isspace (str[str_len - 1]);
+
+ if ( n_escapes == 0
+ && !escape_trailing_space) {
+ *out_to_free = NULL;
+ return str;
+ }
+
+ alloc_len = str_len + n_escapes + ((gsize) escape_trailing_space) + 1;
+ ret = g_new (char, alloc_len);
+
+ j = 0;
+ for (i = 0; str[i] != '\0'; i++) {
+ if (_char_lookup_has (ch_lookup, str[i])) {
+ nm_assert (j < alloc_len);
+ ret[j++] = '\\';
+ }
+ nm_assert (j < alloc_len);
+ ret[j++] = str[i];
+ }
+ if (escape_trailing_space) {
+ nm_assert (!_char_lookup_has (ch_lookup, ret[j - 1]) && g_ascii_isspace (ret[j - 1]));
+ ret[j] = ret[j - 1];
+ ret[j - 1] = '\\';
+ j++;
+ }
+
+ nm_assert (j == alloc_len - 1);
+ ret[j] = '\0';
+
+ *out_to_free = ret;
+ return ret;
+}
+
+/*****************************************************************************/
+
/**
* nm_utils_strv_find_first:
* @list: the strv list to search
@@ -2552,59 +2691,6 @@ _nm_utils_unescape_plain (char *str, const char *candidates, gboolean do_strip)
return str;
}
-char *
-nm_utils_str_simpletokens_extract_next (char **p_line_start)
-{
- char *s_next;
- char *s_start;
- gsize j;
-
- s_start = *p_line_start;
- if (!s_start)
- return NULL;
-
- s_start = nm_str_skip_leading_spaces (s_start);
-
- if (s_start[0] == '\0') {
- *p_line_start = s_start;
- return NULL;
- }
-
- s_next = s_start;
- j = 0;
- while (TRUE) {
- if (s_next[0] == '\0') {
- s_start[j] = '\0';
- *p_line_start = s_next;
- return s_start;
- }
- if (s_next[0] == '\\') {
- s_next++;
- if (s_next[0] == '\0') {
- /* trailing backslash at end of word. That's an error,
- * but we silently drop the backslash and signal success. */
- *p_line_start = s_next;
- if (j == 0)
- return NULL;
- s_start[j] = '\0';
- return s_start;
- }
-
- s_start[j++] = (s_next++)[0];
- continue;
- }
- if (!g_ascii_isspace (s_next[0])) {
- s_start[j++] = (s_next++)[0];
- continue;
- }
-
- nm_assert (j > 0);
- s_start[j] = '\0';
- *p_line_start = nm_str_skip_leading_spaces (&s_next[1]);
- return s_start;
- }
-}
-
/*****************************************************************************/
typedef struct {
diff --git a/shared/nm-utils/nm-shared-utils.h b/shared/nm-utils/nm-shared-utils.h
index 8ec6fa2f5a..4591cc8e9a 100644
--- a/shared/nm-utils/nm-shared-utils.h
+++ b/shared/nm-utils/nm-shared-utils.h
@@ -336,6 +336,44 @@ typedef enum {
NM_UTILS_STRSPLIT_SET_FLAGS_NONE = 0,
NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY = (1u << 0),
NM_UTILS_STRSPLIT_SET_FLAGS_ALLOW_ESCAPING = (1u << 1),
+
+ /* If flag is set, does the same as g_strstrip() on the returned tokens.
+ * This will remove leading and trailing ascii whitespaces (g_ascii_isspace()
+ * and NM_ASCII_SPACES).
+ *
+ * - when combined with !%NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY,
+ * empty tokens will be removed (and %NULL will be returned if that
+ * results in an empty string array).
+ * - when combined with %NM_UTILS_STRSPLIT_SET_FLAGS_ALLOW_ESCAPING,
+ * trailing whitespace escaped by backslash are not stripped. */
+ NM_UTILS_STRSPLIT_SET_FLAGS_STRSTRIP = (1u << 2),
+
+ /* This implies %NM_UTILS_STRSPLIT_SET_FLAGS_ALLOW_ESCAPING.
+ *
+ * This will do a final run over all tokens and remove all backslash
+ * escape characters that
+ * - precede a delimiter.
+ * - precede a backslash.
+ * - preceed a whitespace (with %NM_UTILS_STRSPLIT_SET_FLAGS_STRSTRIP).
+ *
+ * Note that with %NM_UTILS_STRSPLIT_SET_FLAGS_STRSTRIP, it is only
+ * necessary to escape the very last whitespace (if the delimiters
+ * are not whitespace themself). So, technically, it would be sufficient
+ * to only unescape a backslash before the last whitespace and the user
+ * still could express everything. However, such a rule would be complicated
+ * to understand, so when using backslash escaping with nm_utils_strsplit_set_full(),
+ * then all characters (including backslash) are treated verbatim, except:
+ *
+ * - "\\$DELIMITER" (escaped delimiter)
+ * - "\\\\" (escaped backslash)
+ * - "\\$SPACE" (escaped space) (with %NM_UTILS_STRSPLIT_SET_FLAGS_STRSTRIP).
+ *
+ * Note that all other escapes like "\\n" or "\\001" are left alone.
+ * That makes the escaping/unescaping rules simple. Also, for the most part
+ * a text is just taken as-is, with little additional rules. Only backslashes
+ * need extra care, and then only if they proceed one of the relevant characters.
+ */
+ NM_UTILS_STRSPLIT_SET_FLAGS_ESCAPED = (1u << 3),
} NMUtilsStrsplitSetFlags;
const char **nm_utils_strsplit_set_full (const char *str,
@@ -360,8 +398,6 @@ nm_utils_strsplit_set (const char *str,
return nm_utils_strsplit_set_full (str, delimiters, NM_UTILS_STRSPLIT_SET_FLAGS_NONE);
}
-char *nm_utils_str_simpletokens_extract_next (char **p_line_start);
-
gssize nm_utils_strv_find_first (char **list, gssize len, const char *needle);
char **_nm_utils_strv_cleanup (char **strv,
@@ -371,6 +407,37 @@ char **_nm_utils_strv_cleanup (char **strv,
/*****************************************************************************/
+static inline const char **
+nm_utils_escaped_tokens_split (const char *str,
+ const char *delimiters)
+{
+ return nm_utils_strsplit_set_full (str,
+ delimiters,
+ NM_UTILS_STRSPLIT_SET_FLAGS_ESCAPED
+ | NM_UTILS_STRSPLIT_SET_FLAGS_STRSTRIP);
+}
+
+const char *nm_utils_escaped_tokens_escape (const char *str,
+ const char *delimiters,
+ char **out_to_free);
+
+static inline GString *
+nm_utils_escaped_tokens_escape_gstr (const char *str,
+ const char *delimiters,
+ GString *gstring)
+{
+ gs_free char *str_to_free = NULL;
+
+ nm_assert (str);
+ nm_assert (gstring);
+
+ g_string_append (gstring,
+ nm_utils_escaped_tokens_escape (str, delimiters, &str_to_free));
+ return gstring;
+}
+
+/*****************************************************************************/
+
#define NM_UTILS_CHECKSUM_LENGTH_MD5 16
#define NM_UTILS_CHECKSUM_LENGTH_SHA1 20
#define NM_UTILS_CHECKSUM_LENGTH_SHA256 32