all: merge branch 'th/strsplit-pt3'

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/merge_requests/107
This commit is contained in:
Thomas Haller 2019-04-17 11:13:17 +02:00
commit b5a86c10a0
7 changed files with 334 additions and 121 deletions

View file

@ -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,
),
),
),

View file

@ -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,

View file

@ -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) {

View file

@ -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;
@ -509,6 +509,47 @@ _do_test_nm_utils_strsplit_set_f (NMUtilsStrsplitSetFlags flags,
str, \
##__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)
{
@ -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");
}
/*****************************************************************************/

View file

@ -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);

View file

@ -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 {

View file

@ -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