svn commit: r1928796 - in apr/apr/trunk: . include strings test
[email protected] Sat, 27 Sep 2025 17:19:11 -0000
| Newsgroups | gmane.comp.apache.apr.cvs |
|---|---|
| Message-ID | <[email protected]> |
Author: minfrin
Date: Sat Sep 27 17:19:11 2025
New Revision: 1928796
Log:
Add apr_strqtok() function to tokenise quoted strings.
Modified:
apr/apr/trunk/CHANGES
apr/apr/trunk/include/apr_strings.h
apr/apr/trunk/strings/apr_strtok.c
apr/apr/trunk/test/teststr.c
Modified: apr/apr/trunk/CHANGES
==============================================================================
--- apr/apr/trunk/CHANGES Sat Sep 27 16:59:32 2025 (r1928795)
+++ apr/apr/trunk/CHANGES Sat Sep 27 17:19:11 2025 (r1928796)
@@ -1,6 +1,9 @@
-*- coding: utf-8 -*-
Changes for APR 2.0.0
+ *) apr_strings: Add apr_strqtok() function to tokenise quoted strings.
+ [Graham Leggett]
+
*) apr_uri: Update apr_uri_parse() and apr_uri_unparse() to decode / encode
a % separating a zone identifier in an IPv6 literal if detected following
RFC6874. PR69754. [Jens Finkhaeuser <[email protected]>, Ruediger Pluem]
Modified: apr/apr/trunk/include/apr_strings.h
==============================================================================
--- apr/apr/trunk/include/apr_strings.h Sat Sep 27 16:59:32 2025 (r1928795)
+++ apr/apr/trunk/include/apr_strings.h Sat Sep 27 17:19:11 2025 (r1928796)
@@ -291,13 +291,13 @@ APR_DECLARE(apr_status_t) apr_tokenize_t
* argument.
* @param str The string to separate; this should be specified on the
* first call to apr_strtok() for a given string, and NULL
- * on subsequent calls.
+ * on subsequent calls. This string is modified in place.
* @param sep The set of delimiters
* @param last State saved by apr_strtok() between calls.
* @return The next token from the string
* @note the 'last' state points to the trailing NUL char of the final
* token, otherwise it points to the character following the current
- * token (all successive or empty occurances of sep are skiped on the
+ * token (all successive or empty occurances of sep are skipped on the
* subsequent call to apr_strtok). Therefore it is possible to avoid
* a strlen() determination, with the following logic;
* toklen = last - retval; if (*last) --toklen;
@@ -305,6 +305,27 @@ APR_DECLARE(apr_status_t) apr_tokenize_t
APR_DECLARE(char *) apr_strtok(char *str, const char *sep, char **last);
/**
+ * Split a string into separate null-terminated possibly quoted tokens.
+ * The tokens are delimited in the string by one or more characters
+ * from the sep argument. A quoted token may be separated by single or
+ * double quotes, and quoted sections may appear more than once in each
+ * token. The backslash character escapes each quote. The apr_strqtok
+ * function can be used interchangeably with the apr_strtok function
+ * using the same state variable.
+ * @param str The string to separate; this should be specified on the
+ * first call to apr_strtok() for a given string, and NULL
+ * on subsequent calls. This string is modified in place.
+ * @param sep The set of delimiters
+ * @param last State saved by apr_strqtok() between calls.
+ * @return The next token from the string
+ * @note while the 'last' state points to the trailing NUL char of the
+ * final token, otherwise it points to the character following the
+ * current token, no string length can be inferred as quoted characters
+ * and backslash escape characters are removed from the final token.
+ */
+APR_DECLARE(char *) apr_strqtok(char *str, const char *sep, char **last);
+
+/**
* @defgroup APR_Strings_Snprintf snprintf implementations
* @warning
* These are snprintf implementations based on apr_vformatter().
Modified: apr/apr/trunk/strings/apr_strtok.c
==============================================================================
--- apr/apr/trunk/strings/apr_strtok.c Sat Sep 27 16:59:32 2025 (r1928795)
+++ apr/apr/trunk/strings/apr_strtok.c Sat Sep 27 17:19:11 2025 (r1928796)
@@ -54,3 +54,85 @@ APR_DECLARE(char *) apr_strtok(char *str
return token;
}
+
+APR_DECLARE(char *) apr_strqtok(char *str, const char *sep, char **last)
+{
+ char *token;
+ apr_size_t rewind = 0;
+ char c, q = 0, s = 0;
+
+ if (!str) { /* subsequent call */
+ str = *last; /* start where we left off */
+ }
+
+ /* skip characters in sep (will terminate at '\0') */
+ while (*str && strchr(sep, *str)) {
+ ++str;
+ }
+
+ if (!*str) { /* no more tokens */
+ return NULL;
+ }
+
+ token = str;
+
+ /* skip quoted sections */
+ while ((c = *str)) {
+
+ if (!q) {
+ if ('\'' == c) {
+ q = '\'';
+ rewind++;
+ }
+ else if ('\"' == c) {
+ q = '\"';
+ rewind++;
+ }
+ else if (strchr(sep, c)) {
+ break;
+ }
+ else if (rewind) {
+ str[-rewind] = c;
+ }
+ }
+ else {
+ if (!s) {
+ if ('\\' == c) {
+ s = c;
+ rewind++;
+ }
+ else if (!s && q == c) {
+ rewind++;
+ q = 0;
+ }
+ else if (rewind) {
+ str[-rewind] = c;
+ }
+ }
+ else {
+ s = 0;
+ if (rewind) {
+ str[-rewind] = c;
+ }
+ }
+ }
+
+ str++;
+ }
+
+ if (rewind) {
+ str[-rewind] = '\0';
+ }
+
+ /* prepare for the next call (will terminate at '\0)
+ */
+ *last = str;
+
+ if (**last) {
+ **last = '\0';
+ ++*last;
+ }
+
+ return token;
+}
+
Modified: apr/apr/trunk/test/teststr.c
==============================================================================
--- apr/apr/trunk/test/teststr.c Sat Sep 27 16:59:32 2025 (r1928795)
+++ apr/apr/trunk/test/teststr.c Sat Sep 27 17:19:11 2025 (r1928796)
@@ -94,6 +94,100 @@ static void test_strtok(abts_case *tc, v
}
}
+static void test_strqtok(abts_case *tc, void *data)
+{
+ char *retval1, *retval2;
+ char *str1, *str2;
+ char *state1, *state2;
+
+ /* test empty string */
+ str1 = str2 = "";
+ str1 = apr_pstrdup(p, str1);
+ str2 = apr_pstrdup(p, str2);
+
+ retval1 = apr_strtok(str1, ",", &state1);
+ retval2 = apr_strqtok(str2, ",", &state2);
+
+ ABTS_TRUE(tc, retval1 == NULL);
+ ABTS_TRUE(tc, retval2 == NULL);
+
+ /* test delimiters only */
+ str1 = str2 = ",";
+ str1 = apr_pstrdup(p, str1);
+ str2 = apr_pstrdup(p, str2);
+
+ retval1 = apr_strtok(str1, ",", &state1);
+ retval2 = apr_strqtok(str2, ",", &state2);
+
+ /* test unquoted string */
+ str1 = str2 = "key";
+ str1 = apr_pstrdup(p, str1);
+ str2 = apr_pstrdup(p, str2);
+
+ retval1 = apr_strtok(str1, "=", &state1);
+ retval2 = apr_strqtok(str2, "=", &state2);
+
+ ABTS_STR_EQUAL(tc, retval1, "key");
+ ABTS_STR_EQUAL(tc, retval2, "key");
+
+ /* test quoted string */
+ str1 = str2 = "\"key\"";
+ str1 = apr_pstrdup(p, str1);
+ str2 = apr_pstrdup(p, str2);
+
+ retval1 = apr_strtok(str1, "=", &state1);
+ retval2 = apr_strqtok(str2, "=", &state2);
+
+ ABTS_STR_EQUAL(tc, retval1, "\"key\"");
+ ABTS_STR_EQUAL(tc, retval2, "key");
+
+ /* test quoted key value pair */
+ str1 = str2 = "\"key\"='value'";
+ str1 = apr_pstrdup(p, str1);
+ str2 = apr_pstrdup(p, str2);
+
+ retval1 = apr_strtok(str1, "=", &state1);
+ retval2 = apr_strqtok(str2, "=", &state2);
+
+ ABTS_STR_EQUAL(tc, retval1, "\"key\"");
+ ABTS_STR_EQUAL(tc, retval2, "key");
+
+ retval1 = apr_strtok(NULL, "=", &state1);
+ retval2 = apr_strqtok(NULL, "=", &state2);
+
+ ABTS_STR_EQUAL(tc, retval1, "'value'");
+ ABTS_STR_EQUAL(tc, retval2, "value");
+
+ retval1 = apr_strtok(NULL, "=", &state1);
+ retval2 = apr_strqtok(NULL, "=", &state2);
+
+ ABTS_TRUE(tc, retval1 == NULL);
+ ABTS_TRUE(tc, retval2 == NULL);
+
+ /* test quoted against quoted */
+ str1 = str2 = "\"k\"'ey'";
+ str1 = apr_pstrdup(p, str1);
+ str2 = apr_pstrdup(p, str2);
+
+ retval1 = apr_strtok(str1, "=", &state1);
+ retval2 = apr_strqtok(str2, "=", &state2);
+
+ ABTS_STR_EQUAL(tc, retval1, "\"k\"'ey'");
+ ABTS_STR_EQUAL(tc, retval2, "key");
+
+ /* test escapes, unquoted against quoted */
+ str1 = str2 = "outside\\'in\\'sid=e'";
+ str1 = apr_pstrdup(p, str1);
+ str2 = apr_pstrdup(p, str2);
+
+ retval1 = apr_strtok(str1, "=", &state1);
+ retval2 = apr_strqtok(str2, "=", &state2);
+
+ ABTS_STR_EQUAL(tc, retval1, "outside\\'in\\'sid");
+ ABTS_STR_EQUAL(tc, retval2, "outside\\in'sid=e");
+
+}
+
static void snprintf_noNULL(abts_case *tc, void *data)
{
char buff[100];
@@ -512,6 +606,7 @@ abts_suite *teststr(abts_suite *suite)
abts_run_test(suite, snprintf_noNULL, NULL);
abts_run_test(suite, snprintf_underflow, NULL);
abts_run_test(suite, test_strtok, NULL);
+ abts_run_test(suite, test_strqtok, NULL);
abts_run_test(suite, string_error, NULL);
abts_run_test(suite, string_long, NULL);
abts_run_test(suite, string_strtoi64, NULL);