summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/asterisk/strings.h60
-rw-r--r--main/utils.c60
-rw-r--r--tests/test_strings.c80
3 files changed, 200 insertions, 0 deletions
diff --git a/include/asterisk/strings.h b/include/asterisk/strings.h
index 0b98a2bb0..79a2e49c8 100644
--- a/include/asterisk/strings.h
+++ b/include/asterisk/strings.h
@@ -236,6 +236,66 @@ char *ast_strip(char *s),
char *ast_strip_quoted(char *s, const char *beg_quotes, const char *end_quotes);
/*!
+ \brief Flags for ast_strsep
+ */
+enum ast_strsep_flags {
+ AST_STRSEP_STRIP = 0x01, /*!< Trim, then strip quotes. You may want to trim again */
+ AST_STRSEP_TRIM = 0x02, /*!< Trim leading and trailing whitespace */
+ AST_STRSEP_UNESCAPE = 0x04, /*!< Unescape '\' */
+ AST_STRSEP_ALL = 0x07, /*!< Trim, strip, unescape */
+};
+
+/*!
+ \brief Act like strsep but ignore separators inside quotes.
+ \param s Pointer to address of the the string to be processed.
+ Will be modified and can't be constant.
+ \param sep A single character delimiter.
+ \param flags Controls post-processing of the result.
+ AST_STRSEP_TRIM trims all leading and trailing whitespace from the result.
+ AST_STRSEP_STRIP does a trim then strips the outermost quotes. You may want
+ to trim again after the strip. Just OR both the TRIM and STRIP flags.
+ AST_STRSEP_UNESCAPE unescapes '\' sequences.
+ AST_STRSEP_ALL does all of the above processing.
+ \return The next token or NULL if done or if there are more than 8 levels of
+ nested quotes.
+
+ This function acts like strsep with three exceptions...
+ The separator is a single character instead of a string.
+ Separators inside quotes are treated literally instead of like separators.
+ You can elect to have leading and trailing whitespace and quotes
+ stripped from the result and have '\' sequences unescaped.
+
+ Like strsep, ast_strsep maintains no internal state and you can call it
+ recursively using different separators on the same storage.
+
+ Also like strsep, for consistent results, consecutive separators are not
+ collapsed so you may get an empty string as a valid result.
+
+ Examples:
+ \code
+ char *mystr = ast_strdupa("abc=def,ghi='zzz=yyy,456',jkl");
+ char *token, *token2, *token3;
+
+ while((token = ast_strsep(&mystr, ',', AST_SEP_STRIP))) {
+ // 1st token will be aaa=def
+ // 2nd token will be ghi='zzz=yyy,456'
+ while((token2 = ast_strsep(&token, '=', AST_SEP_STRIP))) {
+ // 1st token2 will be ghi
+ // 2nd token2 will be zzz=yyy,456
+ while((token3 = ast_strsep(&token2, ',', AST_SEP_STRIP))) {
+ // 1st token3 will be zzz=yyy
+ // 2nd token3 will be 456
+ // and so on
+ }
+ }
+ // 3rd token will be jkl
+ }
+
+ \endcode
+ */
+char *ast_strsep(char **s, const char sep, uint32_t flags);
+
+/*!
\brief Strip backslash for "escaped" semicolons,
the string to be stripped (will be modified).
\return The stripped string.
diff --git a/main/utils.c b/main/utils.c
index 229080b83..40818c37a 100644
--- a/main/utils.c
+++ b/main/utils.c
@@ -1473,6 +1473,66 @@ char *ast_strip_quoted(char *s, const char *beg_quotes, const char *end_quotes)
return s;
}
+char *ast_strsep(char **iss, const char sep, uint32_t flags)
+{
+ char *st = *iss;
+ char *is;
+ int inquote = 0;
+ int found = 0;
+ char stack[8];
+
+ if (iss == NULL || *iss == '\0') {
+ return NULL;
+ }
+
+ memset(stack, 0, sizeof(stack));
+
+ for(is = st; *is; is++) {
+ if (*is == '\\') {
+ if (*++is != '\0') {
+ is++;
+ } else {
+ break;
+ }
+ }
+
+ if (*is == '\'' || *is == '"') {
+ if (*is == stack[inquote]) {
+ stack[inquote--] = '\0';
+ } else {
+ if (++inquote >= sizeof(stack)) {
+ return NULL;
+ }
+ stack[inquote] = *is;
+ }
+ }
+
+ if (*is == sep && !inquote) {
+ *is = '\0';
+ found = 1;
+ *iss = is + 1;
+ break;
+ }
+ }
+ if (!found) {
+ *iss = NULL;
+ }
+
+ if (flags & AST_STRSEP_STRIP) {
+ st = ast_strip_quoted(st, "'\"", "'\"");
+ }
+
+ if (flags & AST_STRSEP_TRIM) {
+ st = ast_strip(st);
+ }
+
+ if (flags & AST_STRSEP_UNESCAPE) {
+ ast_unescape_quoted(st);
+ }
+
+ return st;
+}
+
char *ast_unescape_semicolon(char *s)
{
char *e;
diff --git a/tests/test_strings.c b/tests/test_strings.c
index 5e5a17d27..127ee789d 100644
--- a/tests/test_strings.c
+++ b/tests/test_strings.c
@@ -310,11 +310,90 @@ AST_TEST_DEFINE(ends_with_test)
return AST_TEST_PASS;
}
+AST_TEST_DEFINE(strsep_test)
+{
+ char *test1, *test2, *test3;
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = "strsep";
+ info->category = "/main/strings/";
+ info->summary = "Test ast_strsep";
+ info->description = "Test ast_strsep";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ test1 = ast_strdupa("ghi=jkl,mno='pqr,stu',abc=def, vwx = yz1 , vwx = yz1 , '"
+ " vwx = yz1 ' , ' vwx , yz1 ',v\"w\"x, '\"x,v\",\"x\"' , \" i\\'m a test\""
+ ", \" i\\'m a, test\", \" i\\'m a, test\", e\\,nd, end\\");
+
+ test2 = ast_strsep(&test1, ',', 0);
+ ast_test_validate(test, 0 == strcmp("ghi=jkl", test2));
+
+ test3 = ast_strsep(&test2, '=', 0);
+ ast_test_validate(test, 0 == strcmp("ghi", test3));
+
+ test3 = ast_strsep(&test2, '=', 0);
+ ast_test_validate(test, 0 == strcmp("jkl", test3));
+
+ test2 = ast_strsep(&test1, ',', 0);
+ ast_test_validate(test, 0 == strcmp("mno='pqr,stu'", test2));
+
+ test3 = ast_strsep(&test2, '=', 0);
+ ast_test_validate(test, 0 == strcmp("mno", test3));
+
+ test3 = ast_strsep(&test2, '=', 0);
+ ast_test_validate(test, 0 == strcmp("'pqr,stu'", test3));
+
+ test2 = ast_strsep(&test1, ',', 0);
+ ast_test_validate(test, 0 == strcmp("abc=def", test2));
+
+ test2 = ast_strsep(&test1, ',', 0);
+ ast_test_validate(test, 0 == strcmp(" vwx = yz1 ", test2));
+
+ test2 = ast_strsep(&test1, ',', AST_STRSEP_TRIM);
+ ast_test_validate(test, 0 == strcmp("vwx = yz1", test2));
+
+ test2 = ast_strsep(&test1, ',', AST_STRSEP_STRIP);
+ ast_test_validate(test, 0 == strcmp(" vwx = yz1 ", test2));
+
+ test2 = ast_strsep(&test1, ',', AST_STRSEP_STRIP | AST_STRSEP_TRIM);
+ ast_test_validate(test, 0 == strcmp("vwx , yz1", test2));
+
+ test2 = ast_strsep(&test1, ',', AST_STRSEP_STRIP | AST_STRSEP_TRIM);
+ ast_test_validate(test, 0 == strcmp("v\"w\"x", test2));
+
+ test2 = ast_strsep(&test1, ',', AST_STRSEP_TRIM);
+ ast_test_validate(test, 0 == strcmp("'\"x,v\",\"x\"'", test2));
+
+ test2 = ast_strsep(&test1, ',', AST_STRSEP_TRIM);
+ ast_test_validate(test, 0 == strcmp("\" i\\'m a test\"", test2));
+
+ test2 = ast_strsep(&test1, ',', AST_STRSEP_TRIM | AST_STRSEP_UNESCAPE);
+ ast_test_validate(test, 0 == strcmp("\" i'm a, test\"", test2));
+
+ test2 = ast_strsep(&test1, ',', AST_STRSEP_ALL);
+ ast_test_validate(test, 0 == strcmp("i'm a, test", test2));
+
+ test2 = ast_strsep(&test1, ',', AST_STRSEP_TRIM | AST_STRSEP_UNESCAPE);
+ ast_test_validate(test, 0 == strcmp("e,nd", test2));
+
+ test2 = ast_strsep(&test1, ',', AST_STRSEP_TRIM | AST_STRSEP_UNESCAPE);
+ ast_test_validate(test, 0 == strcmp("end", test2));
+
+ // nothing failed; we're all good!
+ return AST_TEST_PASS;
+}
+
+
static int unload_module(void)
{
AST_TEST_UNREGISTER(str_test);
AST_TEST_UNREGISTER(begins_with_test);
AST_TEST_UNREGISTER(ends_with_test);
+ AST_TEST_UNREGISTER(strsep_test);
return 0;
}
@@ -323,6 +402,7 @@ static int load_module(void)
AST_TEST_REGISTER(str_test);
AST_TEST_REGISTER(begins_with_test);
AST_TEST_REGISTER(ends_with_test);
+ AST_TEST_REGISTER(strsep_test);
return AST_MODULE_LOAD_SUCCESS;
}