Do not damage utf8 string on escape.
authorDaniel Carl <danielcarl@gmx.de>
Wed, 13 Jun 2018 22:29:24 +0000 (00:29 +0200)
committerDaniel Carl <danielcarl@gmx.de>
Wed, 13 Jun 2018 22:29:24 +0000 (00:29 +0200)
This fixes #492.

src/input.c
src/util.c
src/util.h
tests/test-util.c

index c02f185..220cbb1 100644 (file)
@@ -211,7 +211,7 @@ static void resume_editor(GPid pid, int status, EditorData *data)
         text = util_get_file_contents(data->file, NULL);
 
         if (text) {
-            escaped = g_strescape(text, NULL);
+            escaped = util_strescape(text, NULL);
 
             /* put the text back into the element */
             if (data->element_id && strlen(data->element_id) > 0) {
index bef3800..7c94dcc 100644 (file)
@@ -796,6 +796,52 @@ char *util_str_replace(const char *search, const char *replace, const char *stri
     return ret;
 }
 
+/**
+ * Escapes some special characters in the source string by inserting a '\'
+ * before them. Acts like g_strescape() but does not demage utf8 chars.
+ * Returns a newly allocated string.
+ */
+char *util_strescape(const char *source, const char *exceptions)
+{
+    GString *result = g_string_new(NULL);
+    while (TRUE) {
+        char c = *source++;
+        if ('\0' == c) {
+            goto done;
+        }
+        if (exceptions && !strchr(exceptions, c)) {
+            continue;
+        }
+        switch (c) {
+            case '\n':
+                g_string_append(result, "\\n");
+                break;
+            case '\"':
+                g_string_append(result, "\\\"");
+                break;
+            case '\\':
+                g_string_append(result, "\\\\");
+                break;
+            case '\b':
+                g_string_append(result, "\\b");
+                break;
+            case '\f':
+                g_string_append(result, "\\f");
+                break;
+            case '\r':
+                g_string_append(result, "\\r");
+                break;
+            case '\t':
+                g_string_append(result, "\\t");
+                break;
+            default:
+                g_string_append_c(result, c);
+        }
+    }
+done:
+    return g_string_free(result, FALSE);
+}
+
 static void create_dir_if_not_exists(const char *dirpath)
 {
     if (!g_file_test(dirpath, G_FILE_TEST_IS_DIR)) {
index 14dc3bb..5c6aba9 100644 (file)
@@ -56,6 +56,7 @@ char *util_sanitize_filename(char *filename);
 char *util_sanitize_uri(const char *uri_str);
 char *util_strcasestr(const char *haystack, const char *needle);
 char *util_str_replace(const char* search, const char* replace, const char* string);
+char *util_strescape(const char *source, const char *exceptions);
 gboolean util_wildmatch(const char *pattern, const char *subject);
 
 #endif /* end of include guard: _UTIL_H */
index d03ac57..ad3f1cb 100644 (file)
@@ -262,6 +262,35 @@ static void test_wildmatch_multi(void)
     g_assert_false(util_wildmatch("foo,?", "fo"));
 }
 
+static void test_strescape(void)
+{
+    unsigned int i;
+    char *value;
+    struct {
+        char *in;
+        char *expected;
+        char *excludes;
+    } data[] = {
+        {"", "", NULL},
+        {"foo", "foo", NULL},
+        {"a\nb\nc", "a\\nb\\nc", NULL},
+        {"foo\"bar", "foo\\bar", NULL},
+        {"s\\t", "s\\\\t", NULL},
+        {"u\bv", "u\\bv", NULL},
+        {"w\fx", "w\\fx", NULL},
+        {"y\rz", "y\\rz", NULL},
+        {"tab\tdi\t", "tab\\tdi\\t", NULL},
+        {"❧äüö\n@foo\t\"bar\"", "❧äüö\\n@foo\\t\\\"bar\\\"", NULL},
+        {"❧äüö\n@foo\t\"bar\"", "❧äüö\\n@foo\\t\"bar\"", "\""},
+        {"❧äüö\n@foo\t\"bar\"", "❧äüö\n@foo\t\\\"bar\\\"", "\n\t"},
+    };
+    for (i = 0; i < LENGTH(data); i++) {
+        value = util_strescape(data->in, data->excludes);
+        g_assert_cmpstr(value, ==, data->expected);
+        g_free(value);
+    }
+}
+
 int main(int argc, char *argv[])
 {
     g_test_init(&argc, &argv, NULL);
@@ -279,6 +308,7 @@ int main(int argc, char *argv[])
     g_test_add_func("/test-util/wildmatch-curlybraces", test_wildmatch_curlybraces);
     g_test_add_func("/test-util/wildmatch-complete", test_wildmatch_complete);
     g_test_add_func("/test-util/wildmatch-multi", test_wildmatch_multi);
+    g_test_add_func("/test-util/strescape", test_strescape);
 
     return g_test_run();
 }