]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib: Implement t_strsplit_tabescaped_inplace()
authorTimo Sirainen <timo.sirainen@dovecot.fi>
Fri, 3 Nov 2017 23:39:38 +0000 (01:39 +0200)
committerAki Tuomi <aki.tuomi@dovecot.fi>
Mon, 6 Nov 2017 07:34:31 +0000 (09:34 +0200)
This is a more efficient version of t_strsplit_tabescaped(), which modifies
the input string instead of duplicating it.

src/lib/strescape.c
src/lib/strescape.h
src/lib/test-strescape.c

index 0fb45cd20a9a954accee1fe69320cabde5079458..3be173291c386d91264d305d5e27ef7be4c3e820 100644 (file)
@@ -249,6 +249,50 @@ const char *t_str_tabunescape(const char *str)
                return str_tabunescape(t_strdup_noconst(str));
 }
 
+const char *const *t_strsplit_tabescaped_inplace(char *data)
+{
+       /* @UNSAFE */
+       char **array;
+       unsigned int count, new_alloc_count, alloc_count;
+
+       if (*data == '\0')
+               return t_new(const char *, 1);
+
+       alloc_count = 32;
+       array = t_malloc_no0(sizeof(char *) * alloc_count);
+
+       array[0] = data; count = 1;
+       bool need_unescape = FALSE;
+       while ((data = strpbrk(data, "\t\001")) != NULL) {
+               /* separator or escape char found */
+               if (*data == '\001') {
+                       need_unescape = TRUE;
+                       data++;
+                       continue;
+               }
+               if (count+1 >= alloc_count) {
+                       new_alloc_count = nearest_power(alloc_count+1);
+                       array = p_realloc(unsafe_data_stack_pool, array,
+                                         sizeof(char *) * alloc_count,
+                                         sizeof(char *) *
+                                         new_alloc_count);
+                       alloc_count = new_alloc_count;
+               }
+               *data++ = '\0';
+               if (need_unescape) {
+                       str_tabunescape(array[count-1]);
+                       need_unescape = FALSE;
+               }
+               array[count++] = data;
+       }
+       if (need_unescape)
+               str_tabunescape(array[count-1]);
+       i_assert(count < alloc_count);
+       array[count] = NULL;
+
+       return (const char *const *)array;
+}
+
 char **p_strsplit_tabescaped(pool_t pool, const char *str)
 {
        char **args;
index acf7cba67d5b13d7c2c899f8d90ae80ab806b902..c910a2682574325d0df2f7616f1cf28de80c965d 100644 (file)
@@ -28,5 +28,8 @@ const char *t_str_tabunescape(const char *str);
 
 char **p_strsplit_tabescaped(pool_t pool, const char *str);
 const char *const *t_strsplit_tabescaped(const char *str);
+/* Same as t_strsplit_tabescaped(), but the input string is modified and the
+   returned pointers inside the array point to the original string. */
+const char *const *t_strsplit_tabescaped_inplace(char *str);
 
 #endif
index 2314956e43217675aee2fa04486f1ab541dcd854..07c52476b351ecebc597c34fc54994ed23c68940 100644 (file)
@@ -155,6 +155,20 @@ static void test_strsplit_tabescaped(void)
        test_end();
 }
 
+static void test_strsplit_tabescaped_inplace(void)
+{
+       const char *const *args;
+
+       test_begin("*_strsplit_tabescaped_inplace()");
+       for (unsigned int i = 0; i < N_ELEMENTS(strsplit_tests); i++) {
+               char *input = t_strdup_noconst(strsplit_tests[i].input);
+               args = t_strsplit_tabescaped_inplace(input);
+               for (unsigned int j = 0; strsplit_tests[i].output[j] != NULL; j++)
+                       test_assert_idx(null_strcmp(strsplit_tests[i].output[j], args[j]) == 0, i);
+       }
+       test_end();
+}
+
 void test_strescape(void)
 {
        strsplit_tests[0].input = t_strdup_printf("%s\t%s\t%s\t",
@@ -162,4 +176,5 @@ void test_strescape(void)
        test_str_escape();
        test_tabescape();
        test_strsplit_tabescaped();
+       test_strsplit_tabescaped_inplace();
 }