ifcfg-rh: support storing newline '\n' and other ANSI control caracters

This is especially important for the team config JSON, which is expected
to contain newlines.

ANSI C quotation is bash specific, but initscripts already use #!/bin/bash.

Unfortunately, g_strescape() doesn't escape '\'' and can thus not be
used.

Also add a test that svEscape() and svUnescape() do a round-trip.

Not only consider \r and \n as candidates for ANSI C quotation, but all
ANSI control characters.
This commit is contained in:
Thomas Haller 2016-10-31 22:39:20 +01:00
parent 7b548fb9a8
commit c55b7e866e
2 changed files with 101 additions and 10 deletions

View file

@ -99,15 +99,88 @@ _shell_is_name (const char *key)
/*****************************************************************************/
/* like g_strescape(), except that it also escapes '\''' *sigh*.
*
* While at it, add $''. */
static char *
_escape_ansic (const char *source)
{
const char *p;
gchar *dest;
gchar *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 ESC_ESCAPEES "\"'\\$~`" /* must be escaped */
#define ESC_SPACES " \t|&;()<>" /* only require "" */
#define ESC_NEWLINES "\n\r" /* will be removed */
const char *
svEscape (const char *s, char **to_free)
{
char *new;
int mangle = 0, space = 0, newline = 0;
gsize mangle = 0;
gboolean has_space = FALSE;
int newlen;
size_t i, j, slen;
@ -117,23 +190,26 @@ svEscape (const char *s, char **to_free)
if (strchr (ESC_ESCAPEES, s[i]))
mangle++;
if (strchr (ESC_SPACES, s[i]))
space++;
if (strchr (ESC_NEWLINES, s[i]))
newline++;
has_space = TRUE;
if (s[i] < ' ') {
/* 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 && !space && !newline) {
if (!mangle && !has_space) {
*to_free = NULL;
return s;
}
newlen = slen + mangle - newline + 3; /* 3 is extra ""\0 */
newlen = slen + mangle + 3; /* 3 is extra ""\0 */
new = g_malloc (newlen);
j = 0;
new[j++] = '"';
for (i = 0; i < slen; i++) {
if (strchr (ESC_NEWLINES, s[i]))
continue;
if (strchr (ESC_ESCAPEES, s[i])) {
new[j++] = '\\';
}
@ -142,7 +218,7 @@ svEscape (const char *s, char **to_free)
new[j++] = '"';
new[j++] = '\0';
nm_assert (j == slen + mangle - newline + 3);
nm_assert (j == slen + mangle + 3);
*to_free = new;
return new;

View file

@ -8730,6 +8730,21 @@ do_svUnescape_assert (const char *str, const char *expected)
s = _svUnescape (str, &to_free);
g_assert_cmpstr (s, ==, expected);
/* check we can make a round-trip */
if (expected) {
gs_free char *s1_free = NULL;
gs_free char *s2_free = NULL;
const char *s1, *s2;
s1 = svEscape (expected, &s1_free);
g_assert (s1);
s2 = _svUnescape (s1, &s2_free);
g_assert (s2);
g_assert_cmpstr (s2, ==, expected);
}
}
static void