From c9e241e2f9ad65abf90dd446d21423bf650fa9f1 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 24 Jan 2014 22:12:42 +0100 Subject: [PATCH] ifcfg-rh: change algorithm for svUnescape The previous algorithm had runtime complexity O(n^2). Change it to O(2*n). Signed-off-by: Thomas Haller --- src/settings/plugins/ifcfg-rh/shvar.c | 71 ++++++++---- .../plugins/ifcfg-rh/tests/test-ifcfg-rh.c | 106 ++++++++++++++++++ 2 files changed, 158 insertions(+), 19 deletions(-) diff --git a/src/settings/plugins/ifcfg-rh/shvar.c b/src/settings/plugins/ifcfg-rh/shvar.c index 14969c84ba..cf03c6dab4 100644 --- a/src/settings/plugins/ifcfg-rh/shvar.c +++ b/src/settings/plugins/ifcfg-rh/shvar.c @@ -113,30 +113,63 @@ svCreateFile(const char *name) /* remove escaped characters in place */ void -svUnescape(char *s) { - int len, i; +svUnescape (char *s) +{ + size_t len, idx_rd = 0, idx_wr = 0; + char c; - len = strlen(s); - if (len >= 2 && (s[0] == '"' || s[0] == '\'') && s[0] == s[len-1]) { - i = len - 2; - if (i == 0) - s[0] = '\0'; - else { - memmove(s, s+1, i); - s[i+1] = '\0'; - len = i; + len = strlen(s); + if (len < 2) { + if (s[0] == '\\') + s[0] = '\0'; + return; } - } - for (i = 0; i < len; i++) { - if (s[i] == '\\') { - memmove(s+i, s+i+1, len-(i+1)); - len--; + + if ((s[0] == '"' || s[0] == '\'') && s[0] == s[len-1]) { + if (len == 2) { + s[0] = '\0'; + return; + } + if (len == 3) { + if (s[1] == '\\') { + s[0] = '\0'; + } else { + s[0] = s[1]; + s[1] = '\0'; + } + return; + } + s[--len] = '\0'; + idx_rd = 1; + } else { + /* seek for the first escape... */ + char *p = strchr (s, '\\'); + + if (!p) + return; + if (p[1] == '\0') { + p[0] = '\0'; + return; + } + idx_wr = idx_rd = (p - s); } - s[len] = '\0'; - } + + /* idx_rd points to the first escape. Walk the string and shift the + * characters from idx_rd to idx_wr. */ + while ((c = s[idx_rd++])) { + if (c == '\\') { + if (s[idx_rd] == '\0') { + s[idx_wr] = '\0'; + return; + } + s[idx_wr++] = s[idx_rd++]; + continue; + } + s[idx_wr++] = c; + } + s[idx_wr] = '\0'; } - /* create a new string with all necessary characters escaped. * caller must free returned string */ diff --git a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c index 271e24a598..3691c5f379 100644 --- a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c +++ b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c @@ -13475,6 +13475,110 @@ test_write_team_port (void) g_object_unref (reread); } + +/* Old algorithm for "remove escaped characters in place". + * + * This function is obsolete because it has O(n^2) runtime + * complexity and got replaced. Keep it here for testing, + * that both functions behave identical. + **/ +static void +svUnescape_On2 (char *s) +{ + int len, i; + + len = strlen(s); + if (len >= 2 && (s[0] == '"' || s[0] == '\'') && s[0] == s[len-1]) { + i = len - 2; + if (i == 0) + s[0] = '\0'; + else { + memmove(s, s+1, i); + s[i+1] = '\0'; + len = i; + } + } + for (i = 0; i < len; i++) { + if (s[i] == '\\') { + memmove(s+i, s+i+1, len-(i+1)); + len--; + } + s[len] = '\0'; + } +} + +static void +test_svUnescape_assert (const char *str) +{ + char *s1 = g_strdup (str); + char *s2 = g_strdup (str); + + svUnescape (s1); + svUnescape_On2 (s2); + + g_assert_cmpstr (s1, ==, s2); + + g_free (s1); + g_free (s2); +} + +static void +test_svUnescape () +{ + int len, repeat, i, k; + GRand *rand = g_rand_new (); + guint32 seed = g_random_int (); + + g_rand_set_seed (rand, seed); + + test_svUnescape_assert (""); + test_svUnescape_assert ("'"); + test_svUnescape_assert ("\""); + test_svUnescape_assert ("\\"); + test_svUnescape_assert ("x"); + test_svUnescape_assert (" "); + test_svUnescape_assert ("' '"); + test_svUnescape_assert ("'x'"); + test_svUnescape_assert ("\'some string\'"); + test_svUnescape_assert ("Bob outside LAN"); + test_svUnescape_assert ("{ \"device\": \"team0\", \"link_watch\": { \"name\": \"ethtool\" } }"); + + for (len = 1; len < 25; len++) { + char *s = g_new0 (char, len+1); + + for (repeat = 0; repeat < MAX (4*len, 20); repeat++) { + + /* fill the entire string with random. */ + for (i = 0; i < len; i++) + s[i] = g_rand_int (rand); + + /* randomly place escape characters into the string */ + k = g_rand_int (rand) % (len); + while (k-- > 0) + s[g_rand_int (rand) % len] = '\\'; + + if (len > 1) { + /* quote the string. */ + k = g_rand_int (rand) % (10); + if (k < 4) { + char quote = k < 2 ? '"' : '\''; + + s[0] = quote; + s[len-1] = quote; + } + } + + /*g_message (">>%s<<", s);*/ + test_svUnescape_assert (s); + } + + g_free (s); + } + + g_rand_free (rand); +} + + #define TEST_IFCFG_WIFI_OPEN_SSID_BAD_HEX TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wifi-open-ssid-bad-hex" #define TEST_IFCFG_WIFI_OPEN_SSID_LONG_QUOTED TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wifi-open-ssid-long-quoted" #define TEST_IFCFG_WIFI_OPEN_SSID_LONG_HEX TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wifi-open-ssid-long-hex" @@ -13506,6 +13610,8 @@ int main (int argc, char **argv) g_log_set_always_fatal (G_LOG_LEVEL_CRITICAL); + g_test_add_func (TPATH "svUnescape", test_svUnescape); + g_test_add_func (TPATH "unmanaged", test_read_unmanaged); g_test_add_func (TPATH "unmanaged-unrecognized", test_read_unmanaged_unrecognized); g_test_add_func (TPATH "unrecognized", test_read_unrecognized);