summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGeorge Joseph <george.joseph@fairview5.com>2014-09-18 19:23:39 +0000
committerGeorge Joseph <george.joseph@fairview5.com>2014-09-18 19:23:39 +0000
commitad8ef9175ad92f90d5ad1c8429fc71d791dae42f (patch)
treed2b7961aca136b4db7de7bde117a0a86743efc07
parentde72f3edbc6385a5ff438484a6b31d5548de1caf (diff)
utils: Create ast_strsep function that ignores separators inside 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. Tested by: George Joseph Review: https://reviewboard.asterisk.org/r/3989/ ........ Merged revisions 423476 from http://svn.asterisk.org/svn/asterisk/branches/12 ........ Merged revisions 423478 from http://svn.asterisk.org/svn/asterisk/branches/13 git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@423480 65c4cc65-6c06-0410-ace0-fbb531ad65f3
-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;
}