Просмотр исходного кода

Provide mg_match() as public interface

Add a new (experimental) public interface for pattern matching.
For details: see #499.
The documentation is currently in this issue and in unit test code.
An example has to be added.
bel2125 3 лет назад
Родитель
Сommit
14182bd4f4

+ 1 - 0
VisualStudio/civetweb_lua/civetweb_lua.vcxproj

@@ -218,6 +218,7 @@
     <None Include="..\..\src\sha1.inl" />
     <None Include="..\..\src\mod_duktape.inl" />
     <None Include="..\..\src\mod_lua.inl" />
+    <None Include="..\..\src\sort.inl" />
     <None Include="..\..\src\timer.inl" />
   </ItemGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />

+ 1 - 0
VisualStudio/civetweb_lua/civetweb_lua.vcxproj.filters

@@ -28,5 +28,6 @@
     <None Include="..\..\src\mod_mbedtls.inl" />
     <None Include="..\..\src\openssl_dl.inl" />
     <None Include="..\..\src\match.inl" />
+    <None Include="..\..\src\sort.inl" />
   </ItemGroup>
 </Project>

+ 1 - 0
docs/Embedding.md

@@ -31,6 +31,7 @@ but all functions required to run a HTTP server.
     - src/sha1.inl (SHA calculation)
     - src/handle\_form.inl (HTML form handling functions)
     - src/response.inl (helper for generating HTTP response headers)
+    - src/sort.inl (sorting, qsort_r alternative)
     - src/match.inl (pattern matching)
     - src/timer.inl (optional timer support)
     - src/http2.inl (optional HTTP2 support)

+ 1 - 0
format.bat

@@ -5,6 +5,7 @@ clang-format -i src/CivetServer.cpp
 clang-format -i src/civetweb_private_lua.h
 clang-format -i src/md5.inl
 clang-format -i src/sha1.inl
+clang-format -i src/sort.inl
 clang-format -i src/match.inl
 clang-format -i src/mod_lua.inl
 clang-format -i src/mod_lua_shared.inl

+ 44 - 10
include/civetweb.h

@@ -1366,16 +1366,50 @@ CIVETWEB_API int mg_base64_decode(const char *src,
 CIVETWEB_API char *mg_md5(char buf[33], ...);
 
 
-/* Print error message to the opened error log stream.
-   This utilizes the provided logging configuration.
-     conn: connection (not used for sending data, but to get perameters)
-     fmt: format string without the line return
-     ...: variable argument list
-   Example:
-     mg_cry(conn,"i like %s", "logging"); */
-CIVETWEB_API void mg_cry(const struct mg_connection *conn,
-                         PRINTF_FORMAT_STRING(const char *fmt),
-                         ...) PRINTF_ARGS(2, 3);
+#if !defined(MG_MATCH_CONTEXT_MAX_MATCHES)
+#define MG_MATCH_CONTEXT_MAX_MATCHES (32)
+#endif
+
+struct mg_match_element {
+	const char *str; /* First character matching wildcard */
+	size_t len;      /* Number of character matching wildcard */
+};
+
+struct mg_match_context {
+	int case_sensitive; /* Input: 1 (case sensitive) or 0 (insensitive) */
+	size_t num_matches; /* Output: Number of wildcard matches returned. */
+	struct mg_match_element match[MG_MATCH_CONTEXT_MAX_MATCHES]; /* Output */
+};
+
+
+#if defined(MG_EXPERIMENTAL_INTERFACES)
+/* Pattern matching and extraction function.
+   Parameters:
+     pat: Pattern string (see UserManual.md)
+     str: String to search for match patterns.
+     mcx: Match context (optional, can be NULL).
+
+   Return:
+     Number of characters matched.
+     -1 if no valid match was found.
+     Note: 0 characters might be a valid match for some patterns.
+*/
+CIVETWEB_API ptrdiff_t mg_match(const char *pat,
+                                const char *str,
+                                struct mg_match_context *mcx)
+#endif
+
+
+    /* Print error message to the opened error log stream.
+       This utilizes the provided logging configuration.
+         conn: connection (not used for sending data, but to get perameters)
+         fmt: format string without the line return
+         ...: variable argument list
+       Example:
+         mg_cry(conn,"i like %s", "logging"); */
+    CIVETWEB_API void mg_cry(const struct mg_connection *conn,
+                             PRINTF_FORMAT_STRING(const char *fmt),
+                             ...) PRINTF_ARGS(2, 3);
 
 
 /* utility methods to compare two buffers, case insensitive. */

+ 3 - 45
src/civetweb.c

@@ -3968,6 +3968,9 @@ header_has_option(const char *header, const char *option)
 }
 
 
+/* Sorting function implemented in a separate file */
+#include "sort.inl"
+
 /* Pattern matching has been reimplemented in a new file */
 #include "match.inl"
 
@@ -9819,51 +9822,6 @@ dir_scan_callback(struct de *de, void *data)
 
 
 static void
-mg_sort(void *data,
-        size_t elemcount,
-        size_t elemsize,
-        int (*compfunc)(const void *data1, const void *data2, void *userarg),
-        void *userarg)
-{
-	/* We cannot use qsort_r here. For a detailed reason, see
-	 * https://github.com/civetweb/civetweb/issues/1048#issuecomment-1047093014
-	 * https://stackoverflow.com/questions/39560773/different-declarations-of-qsort-r-on-mac-and-linux
-	 */
-
-	/* We use ShellSort here with this gap sequence: https://oeis.org/A102549 */
-	int A102549[9] = {1, 4, 10, 23, 57, 132, 301, 701, 1750};
-	int Aidx, gap, i, j, k;
-	void *tmp = alloca(elemsize);
-
-	for (Aidx = 8; Aidx >= 0; Aidx--) {
-		gap = A102549[Aidx];
-		if (gap > ((int)elemcount / 2)) {
-			continue;
-		}
-		for (i = 0; i < gap; i++) {
-			for (j = i; j < (int)elemcount; j += gap) {
-				memcpy(tmp, (void *)((ptrdiff_t)data + elemsize * j), elemsize);
-
-				for (k = j; k >= gap; k -= gap) {
-					void *cmp =
-					    (void *)((ptrdiff_t)data + elemsize * (k - gap));
-					int cmpres = compfunc(cmp, tmp, userarg);
-					if (cmpres > 0) {
-						memcpy((void *)((ptrdiff_t)data + elemsize * k),
-						       cmp,
-						       elemsize);
-					} else {
-						break;
-					}
-				}
-				memcpy((void *)((ptrdiff_t)data + elemsize * k), tmp, elemsize);
-			}
-		}
-	}
-}
-
-
-static void
 handle_directory_request(struct mg_connection *conn, const char *dir)
 {
 	size_t i;

+ 8 - 21
src/match.inl

@@ -3,21 +3,6 @@
  * See https://github.com/civetweb/civetweb/
  */
 
-#if !defined(MG_MATCH_CONTEXT_MAX_MATCHES)
-#define MG_MATCH_CONTEXT_MAX_MATCHES (32)
-#endif
-
-struct mg_match_element {
-	const char *str;
-	size_t len;
-};
-
-struct mg_match_context {
-	int case_sensitive;
-	size_t num_matches;
-	struct mg_match_element match[MG_MATCH_CONTEXT_MAX_MATCHES];
-};
-
 
 /* Initialize structure with 0 matches */
 static void
@@ -195,13 +180,15 @@ match_compare(const void *p1, const void *p2, void *user)
 }
 
 
-/* Export as public API? */
-static ptrdiff_t
-mg_match(const char *pat,
-         size_t pat_len,
-         const char *str,
-         struct mg_match_context *mcx)
+#if defined(MG_EXPERIMENTAL_INTERFACES)
+CIVETWEB_API
+#else
+static
+#endif
+ptrdiff_t
+mg_match(const char *pat, const char *str, struct mg_match_context *mcx)
 {
+	size_t pat_len = strlen(pat);
 	ptrdiff_t ret = mg_match_alternatives(pat, pat_len, str, mcx);
 	if (mcx != NULL) {
 		if (ret < 0) {

+ 48 - 0
src/sort.inl

@@ -0,0 +1,48 @@
+/* Sort function. */
+/* from https://github.com/bel2125/sort_r */
+
+static void
+mg_sort(void *data,
+        size_t elemcount,
+        size_t elemsize,
+        int (*compfunc)(const void *data1, const void *data2, void *userarg),
+        void *userarg)
+{
+	/* We cannot use qsort_r here. For a detailed reason, see
+	 * https://github.com/civetweb/civetweb/issues/1048#issuecomment-1047093014
+	 * https://stackoverflow.com/questions/39560773/different-declarations-of-qsort-r-on-mac-and-linux
+	 */
+
+	/* We use ShellSort here with this gap sequence: https://oeis.org/A102549 */
+	int A102549[9] = {1, 4, 10, 23, 57, 132, 301, 701, 1750};
+	int Aidx, gap, i, j, k;
+	void *tmp = alloca(elemsize);
+
+	for (Aidx = 8; Aidx >= 0; Aidx--) {
+		gap = A102549[Aidx];
+		if (gap > ((int)elemcount / 2)) {
+			continue;
+		}
+		for (i = 0; i < gap; i++) {
+			for (j = i; j < (int)elemcount; j += gap) {
+				memcpy(tmp, (void *)((ptrdiff_t)data + elemsize * j), elemsize);
+
+				for (k = j; k >= gap; k -= gap) {
+					void *cmp =
+					    (void *)((ptrdiff_t)data + elemsize * (k - gap));
+					int cmpres = compfunc(cmp, tmp, userarg);
+					if (cmpres > 0) {
+						memcpy((void *)((ptrdiff_t)data + elemsize * k),
+						       cmp,
+						       elemsize);
+					} else {
+						break;
+					}
+				}
+				memcpy((void *)((ptrdiff_t)data + elemsize * k), tmp, elemsize);
+			}
+		}
+	}
+}
+
+/* end if sort.inl */

+ 23 - 23
unittest/private.c

@@ -657,57 +657,57 @@ START_TEST(test_mg_match)
 	/* Copyright (c) 2022 the CivetWeb developers */
 	struct mg_match_context mcx;
 
-	ck_assert_int_eq(4, mg_match("a*D", 3, "abcde", NULL));
+	ck_assert_int_eq(4, mg_match_alternatives("a*D", 3, "abcde", NULL));
 
 	memset(&mcx, 0, sizeof(mcx));
 	mcx.case_sensitive = 0;
-	ck_assert_int_eq(4, mg_match("a*D", 3, "abcde", &mcx));
+	ck_assert_int_eq(4, mg_match_alternatives("a*D", 3, "abcde", &mcx));
 	ck_assert_int_eq(1, (int)mcx.num_matches);
-	ck_assert_int_eq(2, (int)mcx.match_len[0]);
-	ck_assert(!memcmp(mcx.match_str[0], "bc", 2));
+	ck_assert_int_eq(2, (int)mcx.match[0].len);
+	ck_assert(!memcmp(mcx.match[0].str, "bc", 2));
 
 	memset(&mcx, 0, sizeof(mcx));
 	mcx.case_sensitive = 1;
-	ck_assert_int_eq(-1, mg_match("a*D", 3, "abcde", &mcx));
+	ck_assert_int_eq(-1, mg_match_alternatives("a*D", 3, "abcde", &mcx));
 	ck_assert_int_eq(0, (int)mcx.num_matches);
 
 	memset(&mcx, 0, sizeof(mcx));
 	mcx.case_sensitive = 1;
-	ck_assert_int_eq(4, mg_match("a??d", 4, "abcde", &mcx));
+	ck_assert_int_eq(4, mg_match_alternatives("a??d", 4, "abcde", &mcx));
 	ck_assert_int_eq(1, (int)mcx.num_matches);
-	ck_assert_int_eq(2, (int)mcx.match_len[0]);
-	ck_assert(!memcmp(mcx.match_str[0], "bc", 2));
+	ck_assert_int_eq(2, (int)mcx.match[0].len);
+	ck_assert(!memcmp(mcx.match[0].str, "bc", 2));
 
 	memset(&mcx, 0, sizeof(mcx));
 	mcx.case_sensitive = 1;
-	ck_assert_int_eq(5, mg_match("a??d*", 5, "abcde", &mcx));
+	ck_assert_int_eq(5, mg_match_alternatives("a??d*", 5, "abcde", &mcx));
 	ck_assert_int_eq(2, (int)mcx.num_matches);
-	ck_assert_int_eq(2, (int)mcx.match_len[0]);
-	ck_assert(!memcmp(mcx.match_str[0], "bc", 2));
-	ck_assert_int_eq(1, (int)mcx.match_len[1]);
-	ck_assert(!memcmp(mcx.match_str[1], "e", 1));
+	ck_assert_int_eq(2, (int)mcx.match[0].len);
+	ck_assert(!memcmp(mcx.match[0].str, "bc", 2));
+	ck_assert_int_eq(1, (int)mcx.match[1].len);
+	ck_assert(!memcmp(mcx.match[1].str, "e", 1));
 
 	memset(&mcx, 0, sizeof(mcx));
 	mcx.case_sensitive = 1;
-	ck_assert_int_eq(4, mg_match("a??d*", 5, "abcd", &mcx));
+	ck_assert_int_eq(4, mg_match_alternatives("a??d*", 5, "abcd", &mcx));
 	ck_assert_int_eq(2, (int)mcx.num_matches);
-	ck_assert_int_eq(2, (int)mcx.match_len[0]);
-	ck_assert(!memcmp(mcx.match_str[0], "bc", 2));
-	ck_assert_int_eq(0, (int)mcx.match_len[1]);
+	ck_assert_int_eq(2, (int)mcx.match[0].len);
+	ck_assert(!memcmp(mcx.match[0].str, "bc", 2));
+	ck_assert_int_eq(0, (int)mcx.match[1].len);
 
 	memset(&mcx, 0, sizeof(mcx));
 	mcx.case_sensitive = 0;
-	ck_assert_int_eq(2, mg_match("a?|?B", 5, "ABC", &mcx));
+	ck_assert_int_eq(2, mg_match_alternatives("a?|?B", 5, "ABC", &mcx));
 	ck_assert_int_eq(1, (int)mcx.num_matches);
-	ck_assert_int_eq(1, (int)mcx.match_len[0]);
-	ck_assert(!memcmp(mcx.match_str[0], "B", 1));
+	ck_assert_int_eq(1, (int)mcx.match[0].len);
+	ck_assert(!memcmp(mcx.match[0].str, "B", 1));
 
 	memset(&mcx, 0, sizeof(mcx));
 	mcx.case_sensitive = 1;
-	ck_assert_int_eq(2, mg_match("a?|?B", 5, "ABC", &mcx));
+	ck_assert_int_eq(2, mg_match_alternatives("a?|?B", 5, "ABC", &mcx));
 	ck_assert_int_eq(1, (int)mcx.num_matches);
-	ck_assert_int_eq(1, (int)mcx.match_len[0]);
-	ck_assert(!memcmp(mcx.match_str[0], "A", 1));
+	ck_assert_int_eq(1, (int)mcx.match[0].len);
+	ck_assert(!memcmp(mcx.match[0].str, "A", 1));
 }
 END_TEST