shared: add nm_utils_escaped_tokens_escape()

This escapes strings so that they can be concatenated with a delimiter
and without loss tokenized with nm_utils_escaped_tokens_split().

Note that this is similar to _nm_utils_escape_plain() and
_nm_utils_escape_spaces(). The difference is that
nm_utils_escaped_tokens_escape() also escapes the last trailing
whitespace. That means, if delimiters contains all NM_ASCII_SPACES, then
it is identical to _nm_utils_escape_spaces(). Otherwise, the trailing
space is treated specially. That is, because
nm_utils_escaped_tokens_split() uses NM_UTILS_STRSPLIT_SET_FLAGS_STRSTRIP,
to strip leading and trailing whitespace. To still express a trailing
whitespace, the last whitespace must be escaped. Note that
NM_UTILS_STRSPLIT_SET_FLAGS_STRSTRIP also honors escaping any whitespace
(not only at the last position), but when escaping we don't need to
escape them, because unescaped (non-trailing) whitespace are taken just
fine.

The pair nm_utils_escaped_tokens_split() and
nm_utils_escaped_tokens_escape() are proposed as default way of
tokenizing a list of items. For example, with

  $ nmcli connection modify "$PROFILE" +ipv4.routing-rules 'priority 5 from 192.168.7.5/32 table 5, priority 6 iif a\, from 192.168.7.5/32 table 6'

Here we implement a to/from string function to handle one item
(nm_ip_routing_rule_{from,to}_string()). When such elements are combined with ',',
then we need to support an additional layer of escaping on top of that.
The advantage is that the indvidual to/from string functions are agnostic
to this second layer of escaping/tokenizing that nmcli employs to handle
a list of these items.
The disadvantage is that we possibly get multiple layers of backslash
escapings. That is only mitigated by the fact that nm_utils_escaped_tokens_*()
supports a syntax for which *most* characters don't need any special escaping.
Only delimiters, backslash, and the trailing space needs escaping, and
these are cases are expected to be few.
This commit is contained in:
Thomas Haller 2019-04-11 21:20:11 +02:00
parent 9522aaf226
commit e206e3d4d8
2 changed files with 107 additions and 0 deletions

View file

@ -1216,6 +1216,82 @@ done2:
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

View file

@ -409,6 +409,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