You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by br...@apache.org on 2012/11/10 16:14:47 UTC
svn commit: r1407831 - in /subversion/branches/wc-collate-path/subversion:
include/private/svn_utf_private.h include/svn_error_codes.h
libsvn_subr/utf8proc.c tests/libsvn_subr/utf-test.c
Author: brane
Date: Sat Nov 10 15:14:46 2012
New Revision: 1407831
URL: http://svn.apache.org/viewvc?rev=1407831&view=rev
Log:
On the wc-collate-path branch: Implement sqlite's LIKE and GLOB operators.
* subversion/include/svn_error_codes.h (SVN_ERR_UTF8_GLOB): New error code.
* subversion/include/private/svn_utf_private.h (svn_utf__glob):
Change prototype; add another temporary buffer parameter.
* subversion/libsvn_subr/utf8proc.c: Imclude apr_fnmatch.h.
(encode_ucs4): Reverse parameters to be more consistent with stringbuf API.
(svn_utf__glob): Update prototype and implement.
* subversion/tests/libsvn_subr/utf-test.c (test_utf_pattern_match) New test.
(test_funcs): Register test_utf_pattern_match.
Modified:
subversion/branches/wc-collate-path/subversion/include/private/svn_utf_private.h
subversion/branches/wc-collate-path/subversion/include/svn_error_codes.h
subversion/branches/wc-collate-path/subversion/libsvn_subr/utf8proc.c
subversion/branches/wc-collate-path/subversion/tests/libsvn_subr/utf-test.c
Modified: subversion/branches/wc-collate-path/subversion/include/private/svn_utf_private.h
URL: http://svn.apache.org/viewvc/subversion/branches/wc-collate-path/subversion/include/private/svn_utf_private.h?rev=1407831&r1=1407830&r2=1407831&view=diff
==============================================================================
--- subversion/branches/wc-collate-path/subversion/include/private/svn_utf_private.h (original)
+++ subversion/branches/wc-collate-path/subversion/include/private/svn_utf_private.h Sat Nov 10 15:14:46 2012
@@ -110,7 +110,7 @@ svn_utf__normcmp(const char *str1, apr_s
* null-terminated; otherwise, consider the string only up to the
* given length.
*
- * Use buffers BUF1 and BUF2 for temporary storage.
+ * Use buffers PATTERN_BUF, STRING_BUF and TEMP_BUF for temporary storage.
*
* If SQL_LIKE is true, interpret PATTERN as a pattern used by the SQL
* LIKE operator and notice ESCAPE. Otherwise it's a Unix fileglob
@@ -122,8 +122,11 @@ svn_error_t *
svn_utf__glob(const char *pattern, apr_size_t pattern_len,
const char *string, apr_size_t string_len,
const char *escape, apr_size_t escape_len,
- svn_stringbuf_t *buf1, svn_stringbuf_t *buf2,
- svn_boolean_t sql_like, svn_boolean_t *match);
+ svn_boolean_t sql_like,
+ svn_stringbuf_t *pattern_buf,
+ svn_stringbuf_t *string_buf,
+ svn_stringbuf_t *temp_buf,
+ svn_boolean_t *match);
/* Return the version of the wrapped utf8proc library. */
const char *
Modified: subversion/branches/wc-collate-path/subversion/include/svn_error_codes.h
URL: http://svn.apache.org/viewvc/subversion/branches/wc-collate-path/subversion/include/svn_error_codes.h?rev=1407831&r1=1407830&r2=1407831&view=diff
==============================================================================
--- subversion/branches/wc-collate-path/subversion/include/svn_error_codes.h (original)
+++ subversion/branches/wc-collate-path/subversion/include/svn_error_codes.h Sat Nov 10 15:14:46 2012
@@ -1435,6 +1435,11 @@ SVN_ERROR_START
SVN_ERR_MISC_CATEGORY_START + 39,
"utf8proc library error")
+ /** @since New in 1.8. */
+ SVN_ERRDEF(SVN_ERR_UTF8_GLOB,
+ SVN_ERR_MISC_CATEGORY_START + 40,
+ "Bad arguments to SQL operators GLOB or LIKE")
+
/* command-line client errors */
SVN_ERRDEF(SVN_ERR_CL_ARG_PARSING_ERROR,
Modified: subversion/branches/wc-collate-path/subversion/libsvn_subr/utf8proc.c
URL: http://svn.apache.org/viewvc/subversion/branches/wc-collate-path/subversion/libsvn_subr/utf8proc.c?rev=1407831&r1=1407830&r2=1407831&view=diff
==============================================================================
--- subversion/branches/wc-collate-path/subversion/libsvn_subr/utf8proc.c (original)
+++ subversion/branches/wc-collate-path/subversion/libsvn_subr/utf8proc.c Sat Nov 10 15:14:46 2012
@@ -26,11 +26,17 @@
#define UTF8PROC_INLINE
#include "utf8proc/utf8proc.c"
+#include <apr_fnmatch.h>
+
#include "private/svn_string_private.h"
#include "private/svn_utf_private.h"
#include "svn_private_config.h"
#define UNUSED(x) ((void)(x))
+#ifdef SVN_DEBUG
+#include "private/svn_debug.h"
+#endif
+
const char *svn_utf__utf8proc_version(void)
{
@@ -142,7 +148,7 @@ svn_utf__normcmp(const char *str1, apr_s
* A returned error indicates that the codepoint is invalud.
*/
static svn_error_t *
-encode_ucs4(apr_int32_t ucs4chr, svn_stringbuf_t *buffer)
+encode_ucs4(svn_stringbuf_t *buffer, apr_int32_t ucs4chr)
{
char utf8buf[8]; /* The longest UTF-8 sequence has 4 bytes */
const apr_size_t utf8len = utf8proc_encode_char(ucs4chr, (void *)utf8buf);
@@ -163,8 +169,110 @@ svn_error_t *
svn_utf__glob(const char *pattern, apr_size_t pattern_len,
const char *string, apr_size_t string_len,
const char *escape, apr_size_t escape_len,
- svn_stringbuf_t *buf1, svn_stringbuf_t *buf2,
- svn_boolean_t sql_like, svn_boolean_t *match)
-{
+ svn_boolean_t sql_like,
+ svn_stringbuf_t *pattern_buf,
+ svn_stringbuf_t *string_buf,
+ svn_stringbuf_t *temp_buf,
+ svn_boolean_t *match)
+{
+ SVN_DBG((">ptn : %s\n", pattern));
+ SVN_DBG((">str : %s\n", string));
+ SVN_DBG((">esc : %s\n", (escape ? escape : "(null)")));
+
+ /* If we're in LIKE mode, we don't do custom escape chars. */
+ if (escape && !sql_like)
+ return svn_error_create(SVN_ERR_UTF8_GLOB, NULL,
+ "The GLOB operator does not allow"
+ " a custom escape character");
+
+ /* Convert the patern to NFD UTF-8. We can't use the UCS-4 result
+ because apr_fnmatch can't handle it.*/
+ SVN_ERR(decompose_normalized(pattern, pattern_len, temp_buf));
+ svn_stringbuf_setempty(pattern_buf);
+ if (!sql_like)
+ {
+ const apr_int32_t *const glob = (void *)temp_buf->data;
+ apr_size_t i;
+ for (i = 0; i < temp_buf->len; ++i)
+ SVN_ERR(encode_ucs4(pattern_buf, glob[i]));
+ }
+ else
+ {
+ /* Convert a LIKE pattern to a GLOB pattern that apr_fnmatch can use. */
+ const apr_int32_t *like = (void *)temp_buf->data;
+ apr_int32_t ucs4esc;
+ svn_boolean_t escaped;
+ apr_size_t i;
+
+ if (!escape)
+ ucs4esc = -1; /* Definitely an invalid UCS-4 character. */
+ else
+ {
+ const int nullterm = (escape_len == SVN_UTF__UNKNOWN_LENGTH
+ ? UTF8PROC_NULLTERM : 0);
+ ssize_t result =
+ utf8proc_decompose((const void*) escape, escape_len, &ucs4esc, 1,
+ UTF8PROC_DECOMPOSE | UTF8PROC_STABLE | nullterm);
+ if (result < 0)
+ return svn_error_create(SVN_ERR_UTF8PROC_ERROR, NULL,
+ gettext(utf8proc_errmsg(result)));
+ if (result > 1)
+ return svn_error_create(SVN_ERR_UTF8_GLOB, NULL,
+ "The ESCAPE parameter is too long");
+ if ((ucs4esc & 0xFF) != ucs4esc)
+ return svn_error_createf(SVN_ERR_UTF8_GLOB, NULL,
+ "Invalid ESCAPE character U+%04lX",
+ (long)ucs4esc);
+ SVN_DBG(("<esc : %c (U+%04lX)\n", (char)(ucs4esc & 0xFF), (long)ucs4esc));
+ }
+
+ for (i = 0, escaped = FALSE; i < temp_buf->len; ++i, ++like)
+ {
+ if (*like == ucs4esc && !escaped)
+ {
+ svn_stringbuf_appendbyte(pattern_buf, '\\');
+ escaped = TRUE;
+ }
+ else if (escaped)
+ {
+ SVN_ERR(encode_ucs4(pattern_buf, *like));
+ escaped = FALSE;
+ }
+ else
+ {
+ if ((*like == '[' || *like == '\\') && !escaped)
+ {
+ /* Escape brackets and backslashes which are always
+ literals in LIKE patterns. */
+ svn_stringbuf_appendbyte(pattern_buf, '\\');
+ escaped = TRUE;
+ --i; --like;
+ continue;
+ }
+
+ /* Replace LIKE wildcards with their GLOB equivalents. */
+ if (*like == '%')
+ svn_stringbuf_appendbyte(pattern_buf, '*');
+ else if (*like == '_')
+ svn_stringbuf_appendbyte(pattern_buf, '?');
+ else
+ SVN_ERR(encode_ucs4(pattern_buf, *like));
+ }
+ }
+ }
+ SVN_DBG(("glob : %s\n", pattern_buf->data));
+
+ /* Now normalize the string */
+ SVN_ERR(decompose_normalized(string, string_len, temp_buf));
+ svn_stringbuf_setempty(string_buf);
+ {
+ const apr_int32_t *const ucs4nfd = (void *)temp_buf->data;
+ apr_size_t i;
+ for (i = 0; i < temp_buf->len; ++i)
+ SVN_ERR(encode_ucs4(string_buf, ucs4nfd[i]));
+ }
+ SVN_DBG(("string: %s\n", string_buf->data));
+
+ *match = !apr_fnmatch(pattern_buf->data, string_buf->data, 0);
return SVN_NO_ERROR;
}
Modified: subversion/branches/wc-collate-path/subversion/tests/libsvn_subr/utf-test.c
URL: http://svn.apache.org/viewvc/subversion/branches/wc-collate-path/subversion/tests/libsvn_subr/utf-test.c?rev=1407831&r1=1407830&r2=1407831&view=diff
==============================================================================
--- subversion/branches/wc-collate-path/subversion/tests/libsvn_subr/utf-test.c (original)
+++ subversion/branches/wc-collate-path/subversion/tests/libsvn_subr/utf-test.c Sat Nov 10 15:14:46 2012
@@ -27,6 +27,10 @@
#include "private/svn_utf_private.h"
+#ifdef SVN_DEBUG
+#include "private/svn_debug.h"
+#endif
+
/* Random number seed. Yes, it's global, just pretend you can't see it. */
static apr_uint32_t diff_diff3_seed;
@@ -481,6 +485,93 @@ test_utf_collated_compare(apr_pool_t *po
}
+
+static svn_error_t *
+test_utf_pattern_match(apr_pool_t *pool)
+{
+ static const struct glob_test_t {
+ svn_boolean_t sql_like;
+ svn_boolean_t matches;
+ const char *pattern;
+ const char *string;
+ const char *escape;
+ } glob_tests[] = {
+#define LIKE_MATCH TRUE, TRUE
+#define LIKE_FAIL TRUE, FALSE
+#define GLOB_MATCH FALSE, TRUE
+#define GLOB_FAIL FALSE, FALSE
+
+ {LIKE_FAIL, "", "test", NULL},
+ {GLOB_FAIL, "", "test", NULL},
+ {LIKE_FAIL, "", "%", NULL},
+ {GLOB_FAIL, "", "*", NULL},
+ {LIKE_FAIL, "test", "%", NULL},
+ {GLOB_FAIL, "test", "*", NULL},
+ {LIKE_MATCH, "test", "test", NULL},
+ {GLOB_MATCH, "test", "test", NULL},
+
+ {LIKE_MATCH, "t\xe1\xb8\x9dst", "te\xcc\xa7\xcc\x86st", NULL},
+ {GLOB_MATCH, "te\xcc\xa7\xcc\x86st", "t\xe1\xb8\x9dst", NULL},
+#undef LIKE_MATCH
+#undef LIKE_FAIL
+#undef GLOB_MATCH
+#undef GLOB_FAIL
+
+ {FALSE, FALSE, NULL, NULL, NULL}
+ };
+
+ svn_stringbuf_t *bufa = svn_stringbuf_create_empty(pool);
+ svn_stringbuf_t *bufb = svn_stringbuf_create_empty(pool);
+ svn_stringbuf_t *bufc = svn_stringbuf_create_empty(pool);
+ const struct glob_test_t *gt;
+
+ srand(79);
+ for (gt = glob_tests; gt->pattern; ++gt)
+ {
+ const svn_boolean_t implicit_size = (rand() % 13) & 1;
+ const apr_size_t lenptn = (implicit_size
+ ? SVN_UTF__UNKNOWN_LENGTH
+ : strlen(gt->pattern));
+ const apr_size_t lenstr = (implicit_size
+ ? SVN_UTF__UNKNOWN_LENGTH
+ : strlen(gt->string));
+ const apr_size_t lenesc = (implicit_size
+ ? SVN_UTF__UNKNOWN_LENGTH
+ : (gt->escape ? strlen(gt->escape) : 0));
+ svn_boolean_t match;
+ svn_error_t *err;
+
+
+ SVN_DBG(("--- %d ----------\n", (int)(gt - glob_tests)));
+
+ err = svn_utf__glob(gt->pattern, lenptn,
+ gt->string, lenstr,
+ gt->escape, lenesc,
+ gt->sql_like, bufa, bufb, bufc, &match);
+ if (gt->sql_like && gt->escape && !err)
+ return svn_error_create
+ (SVN_ERR_TEST_FAILED, NULL, "Failed to detect GLOB ESCAPE");
+
+ if (!match != !gt->matches)
+ {
+ if (gt->sql_like)
+ return svn_error_createf
+ (SVN_ERR_TEST_FAILED, NULL,
+ "Wrong result: %s'%s' LIKE '%s'%s%s%s%s",
+ (gt->matches ? "NOT " : ""), gt->string, gt->pattern,
+ (gt->escape ? " ESCAPE " : ""), (gt->escape ? "'" : ""),
+ (gt->escape ? gt->escape : ""), (gt->escape ? "'" : ""));
+ else
+ return svn_error_createf
+ (SVN_ERR_TEST_FAILED, NULL, "Wrong result: %s%s GLOB %s",
+ (gt->matches ? "NOT " : ""), gt->string, gt->pattern);
+ }
+ }
+
+ return SVN_NO_ERROR;
+}
+
+
/* The test table. */
@@ -497,5 +588,7 @@ struct svn_test_descriptor_t test_funcs[
"test svn_utf_cstring_from_utf8_ex2"),
SVN_TEST_PASS2(test_utf_collated_compare,
"test svn_utf__normcmp"),
+ SVN_TEST_PASS2(test_utf_pattern_match,
+ "test svn_utf__match"),
SVN_TEST_NULL
};