mirror of
https://github.com/VCMP-SqMod/SqMod.git
synced 2024-11-08 00:37:15 +01:00
Update CivetWeb library.
This commit is contained in:
parent
5d5d5b7920
commit
aca50cab7c
6
vendor/CivetWeb/CMakeLists.txt
vendored
6
vendor/CivetWeb/CMakeLists.txt
vendored
@ -10,7 +10,7 @@ add_library(CivetWeb STATIC
|
|||||||
wolfssl_extras.inl
|
wolfssl_extras.inl
|
||||||
handle_form.inl
|
handle_form.inl
|
||||||
md5.inl
|
md5.inl
|
||||||
mod_http2.inl
|
http2.inl
|
||||||
)
|
)
|
||||||
# Configure include folders
|
# Configure include folders
|
||||||
target_include_directories(CivetWeb PRIVATE ${CMAKE_CURRENT_LIST_DIR})
|
target_include_directories(CivetWeb PRIVATE ${CMAKE_CURRENT_LIST_DIR})
|
||||||
@ -25,14 +25,14 @@ target_compile_definitions(CivetWeb PUBLIC USE_TIMERS=1 USE_WEBSOCKET=1 USE_IPV6
|
|||||||
find_package(OpenSSL)
|
find_package(OpenSSL)
|
||||||
# Check SSL status
|
# Check SSL status
|
||||||
if (OPENSSL_FOUND)
|
if (OPENSSL_FOUND)
|
||||||
message(STATUS "CivetWeb: OpenSSL was found")
|
message(STATUS "CivetWeb: OpenSSL was found ${OPENSSL_VERSION}")
|
||||||
target_link_libraries(CivetWeb PUBLIC OpenSSL::Crypto OpenSSL::SSL)
|
target_link_libraries(CivetWeb PUBLIC OpenSSL::Crypto OpenSSL::SSL)
|
||||||
string(REPLACE "." ";" OPENSSL_VERSION_LIST ${OPENSSL_VERSION})
|
string(REPLACE "." ";" OPENSSL_VERSION_LIST ${OPENSSL_VERSION})
|
||||||
list(GET OPENSSL_VERSION_LIST 0 OPENSSL_VERSION_MAJOR)
|
list(GET OPENSSL_VERSION_LIST 0 OPENSSL_VERSION_MAJOR)
|
||||||
list(GET OPENSSL_VERSION_LIST 1 OPENSSL_VERSION_MINOR)
|
list(GET OPENSSL_VERSION_LIST 1 OPENSSL_VERSION_MINOR)
|
||||||
list(GET OPENSSL_VERSION_LIST 2 OPENSSL_VERSION_PATCH)
|
list(GET OPENSSL_VERSION_LIST 2 OPENSSL_VERSION_PATCH)
|
||||||
# Tell the library what SSL version to expect
|
# Tell the library what SSL version to expect
|
||||||
target_compile_definitions(CivetWeb PUBLIC "OPENSSL_API_${OPENSSL_VERSION_MAJOR}_${OPENSSL_VERSION_MINOR}")
|
target_compile_definitions(CivetWeb PRIVATE "OPENSSL_API_${OPENSSL_VERSION_MAJOR}_${OPENSSL_VERSION_MINOR}")
|
||||||
message(STATUS "CivetWeb: OPENSSL_API_${OPENSSL_VERSION_MAJOR}_${OPENSSL_VERSION_MINOR}")
|
message(STATUS "CivetWeb: OPENSSL_API_${OPENSSL_VERSION_MAJOR}_${OPENSSL_VERSION_MINOR}")
|
||||||
else()
|
else()
|
||||||
target_compile_definitions(CivetWeb PUBLIC NO_SSL=1)
|
target_compile_definitions(CivetWeb PUBLIC NO_SSL=1)
|
||||||
|
2590
vendor/CivetWeb/civetweb.c
vendored
2590
vendor/CivetWeb/civetweb.c
vendored
File diff suppressed because it is too large
Load Diff
2
vendor/CivetWeb/handle_form.inl
vendored
2
vendor/CivetWeb/handle_form.inl
vendored
@ -220,7 +220,7 @@ mg_handle_form_request(struct mg_connection *conn,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* GET request: form data is in the query string. */
|
/* GET request: form data is in the query string. */
|
||||||
/* The entire data has already been loaded, so there is no nead to
|
/* The entire data has already been loaded, so there is no need to
|
||||||
* call mg_read. We just need to split the query string into key-value
|
* call mg_read. We just need to split the query string into key-value
|
||||||
* pairs. */
|
* pairs. */
|
||||||
data = conn->request_info.query_string;
|
data = conn->request_info.query_string;
|
||||||
|
@ -654,7 +654,7 @@ hpack_getnum(const uint8_t *buf,
|
|||||||
* the encoded string.
|
* the encoded string.
|
||||||
*/
|
*/
|
||||||
static char *
|
static char *
|
||||||
hpack_decode(const uint8_t *buf, int *i, struct mg_context *ctx)
|
hpack_decode(const uint8_t *buf, int *i, int max_i, struct mg_context *ctx)
|
||||||
{
|
{
|
||||||
uint64_t byte_len64;
|
uint64_t byte_len64;
|
||||||
int byte_len;
|
int byte_len;
|
||||||
@ -670,6 +670,11 @@ hpack_decode(const uint8_t *buf, int *i, struct mg_context *ctx)
|
|||||||
byte_len = (int)byte_len64;
|
byte_len = (int)byte_len64;
|
||||||
bit_len = byte_len * 8;
|
bit_len = byte_len * 8;
|
||||||
|
|
||||||
|
/* check size */
|
||||||
|
if ((*i) + byte_len > max_i) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/* Now read the string */
|
/* Now read the string */
|
||||||
if (!is_huff) {
|
if (!is_huff) {
|
||||||
/* Not huffman encoded: Copy directly */
|
/* Not huffman encoded: Copy directly */
|
||||||
@ -718,6 +723,10 @@ hpack_decode(const uint8_t *buf, int *i, struct mg_context *ctx)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (bytesStored == sizeof(str)) {
|
||||||
|
/* too long */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -820,7 +829,7 @@ hpack_encode(uint8_t *store, const char *load, int lower)
|
|||||||
|
|
||||||
|
|
||||||
static const char http2_pri[] = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
|
static const char http2_pri[] = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
|
||||||
static unsigned char http2_pri_len = 24; /* = strlen(http2_pri) */
|
static const unsigned char http2_pri_len = 24; /* = strlen(http2_pri) */
|
||||||
|
|
||||||
|
|
||||||
/* Read and check the HTTP/2 primer/preface:
|
/* Read and check the HTTP/2 primer/preface:
|
||||||
@ -829,17 +838,19 @@ static int
|
|||||||
is_valid_http2_primer(struct mg_connection *conn)
|
is_valid_http2_primer(struct mg_connection *conn)
|
||||||
{
|
{
|
||||||
size_t pri_len = http2_pri_len;
|
size_t pri_len = http2_pri_len;
|
||||||
char buf[32];
|
char buf[32]; /* Buffer must hold 24 bytes primer */
|
||||||
|
|
||||||
if (pri_len > sizeof(buf)) {
|
|
||||||
/* Should never be reached - the RFC primer has 24 bytes */
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
int read_pri_len = mg_read(conn, buf, pri_len);
|
int read_pri_len = mg_read(conn, buf, pri_len);
|
||||||
if ((read_pri_len != (int)pri_len)
|
if (read_pri_len != (int)pri_len) {
|
||||||
|| (0 != memcmp(buf, http2_pri, pri_len))) {
|
/* Size does not match.
|
||||||
|
* This includes cases where mg_read returns error codes */
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
if (0 != memcmp(buf, http2_pri, pri_len)) {
|
||||||
|
/* Primer does not match */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/* Primer does match */
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -850,7 +861,7 @@ is_valid_http2_primer(struct mg_connection *conn)
|
|||||||
(conn)->client.sock, \
|
(conn)->client.sock, \
|
||||||
(conn)->ssl, \
|
(conn)->ssl, \
|
||||||
(const char *)(data), \
|
(const char *)(data), \
|
||||||
(int)(len));
|
(int)(len))
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -949,7 +960,7 @@ http2_send_response_headers(struct mg_connection *conn)
|
|||||||
uint16_t header_len = 0;
|
uint16_t header_len = 0;
|
||||||
int has_date = 0;
|
int has_date = 0;
|
||||||
int has_connection_header = 0;
|
int has_connection_header = 0;
|
||||||
int i;
|
int i, ok;
|
||||||
|
|
||||||
if ((conn->status_code < 100) || (conn->status_code > 999)) {
|
if ((conn->status_code < 100) || (conn->status_code > 999)) {
|
||||||
/* Invalid status: Set status to "Internal Server Error" */
|
/* Invalid status: Set status to "Internal Server Error" */
|
||||||
@ -1058,16 +1069,23 @@ http2_send_response_headers(struct mg_connection *conn)
|
|||||||
http2_header_frame[8] = (conn->http2.stream_id & 0xFFu);
|
http2_header_frame[8] = (conn->http2.stream_id & 0xFFu);
|
||||||
|
|
||||||
/* Send header frame */
|
/* Send header frame */
|
||||||
mg_xwrite(conn, http2_header_frame, 9);
|
ok = 1;
|
||||||
mg_xwrite(conn, header_bin, header_len);
|
if (mg_xwrite(conn, http2_header_frame, 9) != 9) {
|
||||||
|
ok = 0;
|
||||||
DEBUG_TRACE("HTTP2 response header sent: stream %u", conn->http2.stream_id);
|
} else if (mg_xwrite(conn, header_bin, header_len) != header_len) {
|
||||||
|
ok = 0;
|
||||||
|
}
|
||||||
|
if (ok) {
|
||||||
|
DEBUG_TRACE("HTTP2 response header sent: stream %u",
|
||||||
|
conn->http2.stream_id);
|
||||||
|
} else {
|
||||||
|
DEBUG_TRACE("HTTP2 response header sending error: stream %u",
|
||||||
|
conn->http2.stream_id);
|
||||||
|
}
|
||||||
|
|
||||||
(void)has_connection_header; /* ignore for the moment */
|
(void)has_connection_header; /* ignore for the moment */
|
||||||
|
|
||||||
|
return ok;
|
||||||
return 42; /* TODO */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1420,8 +1438,13 @@ handle_http2(struct mg_connection *conn)
|
|||||||
/* Get Header name "key" */
|
/* Get Header name "key" */
|
||||||
if (idx == 0) {
|
if (idx == 0) {
|
||||||
/* Index 0: Header name encoded in following bytes */
|
/* Index 0: Header name encoded in following bytes */
|
||||||
key = hpack_decode(buf, &i, conn->phys_ctx);
|
key =
|
||||||
|
hpack_decode(buf, &i, (int)bytes_read, conn->phys_ctx);
|
||||||
CHECK_LEAK_HDR_ALLOC(key);
|
CHECK_LEAK_HDR_ALLOC(key);
|
||||||
|
if (!key) {
|
||||||
|
DEBUG_TRACE("HTTP2 key decoding error");
|
||||||
|
goto clean_http2;
|
||||||
|
}
|
||||||
} else if (/*(idx >= 15) &&*/ (idx <= 61)) {
|
} else if (/*(idx >= 15) &&*/ (idx <= 61)) {
|
||||||
/* Take key name from predefined header table */
|
/* Take key name from predefined header table */
|
||||||
key = mg_strdup_ctx(hpack_predefined[idx].name,
|
key = mg_strdup_ctx(hpack_predefined[idx].name,
|
||||||
@ -1480,8 +1503,16 @@ handle_http2(struct mg_connection *conn)
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
/* Read value from HTTP2 stream */
|
/* Read value from HTTP2 stream */
|
||||||
val = hpack_decode(buf, &i, conn->phys_ctx); /* leak? */
|
val = hpack_decode(buf,
|
||||||
|
&i,
|
||||||
|
(int)bytes_read,
|
||||||
|
conn->phys_ctx); /* leak? */
|
||||||
CHECK_LEAK_HDR_ALLOC(val);
|
CHECK_LEAK_HDR_ALLOC(val);
|
||||||
|
if (!val) {
|
||||||
|
DEBUG_TRACE("HTTP2 value decoding error");
|
||||||
|
mg_free((void *)key);
|
||||||
|
goto clean_http2;
|
||||||
|
}
|
||||||
|
|
||||||
if (indexing) {
|
if (indexing) {
|
||||||
/* Add to index */
|
/* Add to index */
|
||||||
@ -1546,6 +1577,7 @@ handle_http2(struct mg_connection *conn)
|
|||||||
} else if (!strcmp(":path", key)) {
|
} else if (!strcmp(":path", key)) {
|
||||||
conn->request_info.local_uri = val;
|
conn->request_info.local_uri = val;
|
||||||
conn->request_info.request_uri = val;
|
conn->request_info.request_uri = val;
|
||||||
|
conn->request_info.local_uri_raw = val;
|
||||||
} else if (!strcmp(":status", key)) {
|
} else if (!strcmp(":status", key)) {
|
||||||
conn->status_code = atoi(val);
|
conn->status_code = atoi(val);
|
||||||
}
|
}
|
||||||
@ -1803,7 +1835,7 @@ HPACK_TABLE_TEST()
|
|||||||
|
|
||||||
for (i = 0; i < 256; i++) {
|
for (i = 0; i < 256; i++) {
|
||||||
if (reverse_map[i] == -1) {
|
if (reverse_map[i] == -1) {
|
||||||
ck_abort_msg("reverse map at %i mising", i);
|
ck_abort_msg("reverse map at %i missing", i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
137
vendor/CivetWeb/include/civetweb.h
vendored
137
vendor/CivetWeb/include/civetweb.h
vendored
@ -23,9 +23,9 @@
|
|||||||
#ifndef CIVETWEB_HEADER_INCLUDED
|
#ifndef CIVETWEB_HEADER_INCLUDED
|
||||||
#define CIVETWEB_HEADER_INCLUDED
|
#define CIVETWEB_HEADER_INCLUDED
|
||||||
|
|
||||||
#define CIVETWEB_VERSION "1.15"
|
#define CIVETWEB_VERSION "1.16"
|
||||||
#define CIVETWEB_VERSION_MAJOR (1)
|
#define CIVETWEB_VERSION_MAJOR (1)
|
||||||
#define CIVETWEB_VERSION_MINOR (15)
|
#define CIVETWEB_VERSION_MINOR (16)
|
||||||
#define CIVETWEB_VERSION_PATCH (0)
|
#define CIVETWEB_VERSION_PATCH (0)
|
||||||
|
|
||||||
#ifndef CIVETWEB_API
|
#ifndef CIVETWEB_API
|
||||||
@ -654,7 +654,7 @@ CIVETWEB_API void *mg_get_thread_pointer(const struct mg_connection *conn);
|
|||||||
or write to the connection. */
|
or write to the connection. */
|
||||||
/* Note: An alternative is to use the init_connection callback
|
/* Note: An alternative is to use the init_connection callback
|
||||||
instead to initialize the user connection data pointer. It is
|
instead to initialize the user connection data pointer. It is
|
||||||
reccomended to supply a pointer to some user defined data structure
|
recommended to supply a pointer to some user defined data structure
|
||||||
as conn_data initializer in init_connection. In case it is required
|
as conn_data initializer in init_connection. In case it is required
|
||||||
to change some data after the init_connection call, store another
|
to change some data after the init_connection call, store another
|
||||||
data pointer in the user defined data structure and modify that
|
data pointer in the user defined data structure and modify that
|
||||||
@ -1128,7 +1128,7 @@ CIVETWEB_API int mg_get_var2(const char *data,
|
|||||||
required to increase this value at compile time.
|
required to increase this value at compile time.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
data: form encoded iput string. Will be modified by this function.
|
data: form encoded input string. Will be modified by this function.
|
||||||
form_fields: output list of name/value-pairs. A buffer with a size
|
form_fields: output list of name/value-pairs. A buffer with a size
|
||||||
specified by num_form_fields must be provided by the
|
specified by num_form_fields must be provided by the
|
||||||
caller.
|
caller.
|
||||||
@ -1341,6 +1341,22 @@ CIVETWEB_API int mg_url_decode(const char *src,
|
|||||||
CIVETWEB_API int mg_url_encode(const char *src, char *dst, size_t dst_len);
|
CIVETWEB_API int mg_url_encode(const char *src, char *dst, size_t dst_len);
|
||||||
|
|
||||||
|
|
||||||
|
/* BASE64-encode input buffer into destination buffer.
|
||||||
|
returns -1 on OK. */
|
||||||
|
CIVETWEB_API int mg_base64_encode(const unsigned char *src,
|
||||||
|
size_t src_len,
|
||||||
|
char *dst,
|
||||||
|
size_t *dst_len);
|
||||||
|
|
||||||
|
|
||||||
|
/* BASE64-decode input buffer into destination buffer.
|
||||||
|
returns -1 on OK. */
|
||||||
|
CIVETWEB_API int mg_base64_decode(const char *src,
|
||||||
|
size_t src_len,
|
||||||
|
unsigned char *dst,
|
||||||
|
size_t *dst_len);
|
||||||
|
|
||||||
|
|
||||||
/* MD5 hash given strings.
|
/* MD5 hash given strings.
|
||||||
Buffer 'buf' must be 33 bytes long. Varargs is a NULL terminated list of
|
Buffer 'buf' must be 33 bytes long. Varargs is a NULL terminated list of
|
||||||
ASCIIz strings. When function returns, buf will contain human-readable
|
ASCIIz strings. When function returns, buf will contain human-readable
|
||||||
@ -1350,6 +1366,40 @@ CIVETWEB_API int mg_url_encode(const char *src, char *dst, size_t dst_len);
|
|||||||
CIVETWEB_API char *mg_md5(char buf[33], ...);
|
CIVETWEB_API char *mg_md5(char buf[33], ...);
|
||||||
|
|
||||||
|
|
||||||
|
#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.
|
/* Print error message to the opened error log stream.
|
||||||
This utilizes the provided logging configuration.
|
This utilizes the provided logging configuration.
|
||||||
conn: connection (not used for sending data, but to get perameters)
|
conn: connection (not used for sending data, but to get perameters)
|
||||||
@ -1505,6 +1555,7 @@ CIVETWEB_API int mg_get_response(struct mg_connection *conn,
|
|||||||
* -1: parameter error
|
* -1: parameter error
|
||||||
* -2: invalid connection type
|
* -2: invalid connection type
|
||||||
* -3: invalid connection status
|
* -3: invalid connection status
|
||||||
|
* -4: network error (only if built with NO_RESPONSE_BUFFERING)
|
||||||
*/
|
*/
|
||||||
CIVETWEB_API int mg_response_header_start(struct mg_connection *conn,
|
CIVETWEB_API int mg_response_header_start(struct mg_connection *conn,
|
||||||
int status);
|
int status);
|
||||||
@ -1557,6 +1608,7 @@ CIVETWEB_API int mg_response_header_add_lines(struct mg_connection *conn,
|
|||||||
* -1: parameter error
|
* -1: parameter error
|
||||||
* -2: invalid connection type
|
* -2: invalid connection type
|
||||||
* -3: invalid connection status
|
* -3: invalid connection status
|
||||||
|
* -4: sending failed (network error)
|
||||||
*/
|
*/
|
||||||
CIVETWEB_API int mg_response_header_send(struct mg_connection *conn);
|
CIVETWEB_API int mg_response_header_send(struct mg_connection *conn);
|
||||||
|
|
||||||
@ -1600,7 +1652,7 @@ CIVETWEB_API unsigned mg_check_feature(unsigned feature);
|
|||||||
buffer: Store system information as string here.
|
buffer: Store system information as string here.
|
||||||
buflen: Length of buffer (including a byte required for a terminating 0).
|
buflen: Length of buffer (including a byte required for a terminating 0).
|
||||||
Return:
|
Return:
|
||||||
Available size of system information, exluding a terminating 0.
|
Available size of system information, excluding a terminating 0.
|
||||||
The information is complete, if the return value is smaller than buflen.
|
The information is complete, if the return value is smaller than buflen.
|
||||||
The result is a JSON formatted string, the exact content may vary.
|
The result is a JSON formatted string, the exact content may vary.
|
||||||
Note:
|
Note:
|
||||||
@ -1617,7 +1669,7 @@ CIVETWEB_API int mg_get_system_info(char *buffer, int buflen);
|
|||||||
buffer: Store context information here.
|
buffer: Store context information here.
|
||||||
buflen: Length of buffer (including a byte required for a terminating 0).
|
buflen: Length of buffer (including a byte required for a terminating 0).
|
||||||
Return:
|
Return:
|
||||||
Available size of system information, exluding a terminating 0.
|
Available size of system information, excluding a terminating 0.
|
||||||
The information is complete, if the return value is smaller than buflen.
|
The information is complete, if the return value is smaller than buflen.
|
||||||
The result is a JSON formatted string, the exact content may vary.
|
The result is a JSON formatted string, the exact content may vary.
|
||||||
Note:
|
Note:
|
||||||
@ -1646,7 +1698,7 @@ CIVETWEB_API void mg_disable_connection_keep_alive(struct mg_connection *conn);
|
|||||||
buffer: Store context information here.
|
buffer: Store context information here.
|
||||||
buflen: Length of buffer (including a byte required for a terminating 0).
|
buflen: Length of buffer (including a byte required for a terminating 0).
|
||||||
Return:
|
Return:
|
||||||
Available size of system information, exluding a terminating 0.
|
Available size of system information, excluding a terminating 0.
|
||||||
The information is complete, if the return value is smaller than buflen.
|
The information is complete, if the return value is smaller than buflen.
|
||||||
The result is a JSON formatted string, the exact content may vary.
|
The result is a JSON formatted string, the exact content may vary.
|
||||||
Note:
|
Note:
|
||||||
@ -1669,11 +1721,80 @@ CIVETWEB_API int mg_get_connection_info(const struct mg_context *ctx,
|
|||||||
Note: Experimental interfaces may change
|
Note: Experimental interfaces may change
|
||||||
*/
|
*/
|
||||||
struct mg_error_data {
|
struct mg_error_data {
|
||||||
unsigned *code; /* error code (number) */
|
unsigned code; /* error code (number) */
|
||||||
|
unsigned code_sub; /* error sub code (number) */
|
||||||
char *text; /* buffer for error text */
|
char *text; /* buffer for error text */
|
||||||
size_t text_buffer_size; /* size of buffer of "text" */
|
size_t text_buffer_size; /* size of buffer of "text" */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* Values for error "code" in mg_error_data */
|
||||||
|
enum {
|
||||||
|
/* No error */
|
||||||
|
MG_ERROR_DATA_CODE_OK = 0u,
|
||||||
|
|
||||||
|
/* Caller provided invalid parameter */
|
||||||
|
MG_ERROR_DATA_CODE_INVALID_PARAM = 1u,
|
||||||
|
|
||||||
|
/* "configuration_option" contains invalid element */
|
||||||
|
MG_ERROR_DATA_CODE_INVALID_OPTION = 2u,
|
||||||
|
|
||||||
|
/* Initializen TLS / SSL library failed */
|
||||||
|
MG_ERROR_DATA_CODE_INIT_TLS_FAILED = 3u,
|
||||||
|
|
||||||
|
/* Mandatory "configuration_option" missing */
|
||||||
|
MG_ERROR_DATA_CODE_MISSING_OPTION = 4u,
|
||||||
|
|
||||||
|
/* Duplicate "authentication_domain" option */
|
||||||
|
MG_ERROR_DATA_CODE_DUPLICATE_DOMAIN = 5u,
|
||||||
|
|
||||||
|
/* Not enough memory */
|
||||||
|
MG_ERROR_DATA_CODE_OUT_OF_MEMORY = 6u,
|
||||||
|
|
||||||
|
/* Server already stopped */
|
||||||
|
MG_ERROR_DATA_CODE_SERVER_STOPPED = 7u,
|
||||||
|
|
||||||
|
/* mg_init_library must be called first */
|
||||||
|
MG_ERROR_DATA_CODE_INIT_LIBRARY_FAILED = 8u,
|
||||||
|
|
||||||
|
/* Operating system function failed */
|
||||||
|
MG_ERROR_DATA_CODE_OS_ERROR = 9u,
|
||||||
|
|
||||||
|
/* Failed to bind to server ports */
|
||||||
|
MG_ERROR_DATA_CODE_INIT_PORTS_FAILED = 10u,
|
||||||
|
|
||||||
|
/* Failed to switch user (option "run_as_user") */
|
||||||
|
MG_ERROR_DATA_CODE_INIT_USER_FAILED = 11u,
|
||||||
|
|
||||||
|
/* Access Control List error */
|
||||||
|
MG_ERROR_DATA_CODE_INIT_ACL_FAILED = 12u,
|
||||||
|
|
||||||
|
/* Global password file error */
|
||||||
|
MG_ERROR_DATA_CODE_INVALID_PASS_FILE = 13u,
|
||||||
|
|
||||||
|
/* Lua background script init error */
|
||||||
|
MG_ERROR_DATA_CODE_SCRIPT_ERROR = 14u,
|
||||||
|
|
||||||
|
/* Client: Host not found, invalid IP to connect */
|
||||||
|
MG_ERROR_DATA_CODE_HOST_NOT_FOUND = 15u,
|
||||||
|
|
||||||
|
/* Client: TCP connect timeout */
|
||||||
|
MG_ERROR_DATA_CODE_CONNECT_TIMEOUT = 16u,
|
||||||
|
|
||||||
|
/* Client: TCP connect failed */
|
||||||
|
MG_ERROR_DATA_CODE_CONNECT_FAILED = 17u,
|
||||||
|
|
||||||
|
/* Error using TLS client certificate */
|
||||||
|
MG_ERROR_DATA_CODE_TLS_CLIENT_CERT_ERROR = 18u,
|
||||||
|
|
||||||
|
/* Error setting trusted TLS server certificate for client connection */
|
||||||
|
MG_ERROR_DATA_CODE_TLS_SERVER_CERT_ERROR = 19u,
|
||||||
|
|
||||||
|
/* Error establishing TLS connection to HTTPS server */
|
||||||
|
MG_ERROR_DATA_CODE_TLS_CONNECT_ERROR = 20u
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
struct mg_init_data {
|
struct mg_init_data {
|
||||||
const struct mg_callbacks *callbacks; /* callback function pointer */
|
const struct mg_callbacks *callbacks; /* callback function pointer */
|
||||||
void *user_data; /* data */
|
void *user_data; /* data */
|
||||||
|
262
vendor/CivetWeb/match.inl
vendored
Normal file
262
vendor/CivetWeb/match.inl
vendored
Normal file
@ -0,0 +1,262 @@
|
|||||||
|
/* Reimplementation of pattern matching */
|
||||||
|
/* This file is part of the CivetWeb web server.
|
||||||
|
* See https://github.com/civetweb/civetweb/
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/* Initialize structure with 0 matches */
|
||||||
|
static void
|
||||||
|
match_context_reset(struct mg_match_context *mcx)
|
||||||
|
{
|
||||||
|
mcx->num_matches = 0;
|
||||||
|
memset(mcx->match, 0, sizeof(mcx->match));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Add a new match to the list of matches */
|
||||||
|
static void
|
||||||
|
match_context_push(const char *str, size_t len, struct mg_match_context *mcx)
|
||||||
|
{
|
||||||
|
if (mcx->num_matches < MG_MATCH_CONTEXT_MAX_MATCHES) {
|
||||||
|
mcx->match[mcx->num_matches].str = str;
|
||||||
|
mcx->match[mcx->num_matches].len = len;
|
||||||
|
mcx->num_matches++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static ptrdiff_t
|
||||||
|
mg_match_impl(const char *pat,
|
||||||
|
size_t pat_len,
|
||||||
|
const char *str,
|
||||||
|
struct mg_match_context *mcx)
|
||||||
|
{
|
||||||
|
/* Parse string */
|
||||||
|
size_t i_pat = 0; /* Pattern index */
|
||||||
|
size_t i_str = 0; /* Pattern index */
|
||||||
|
|
||||||
|
int case_sensitive = ((mcx != NULL) ? mcx->case_sensitive : 0); /* 0 or 1 */
|
||||||
|
|
||||||
|
while (i_pat < pat_len) {
|
||||||
|
|
||||||
|
/* Pattern ? matches one character, except / and NULL character */
|
||||||
|
if ((pat[i_pat] == '?') && (str[i_str] != '\0')
|
||||||
|
&& (str[i_str] != '/')) {
|
||||||
|
size_t i_str_start = i_str;
|
||||||
|
do {
|
||||||
|
/* Advance as long as there are ? */
|
||||||
|
i_pat++;
|
||||||
|
i_str++;
|
||||||
|
} while ((pat[i_pat] == '?') && (str[i_str] != '\0')
|
||||||
|
&& (str[i_str] != '/') && (i_pat < pat_len));
|
||||||
|
|
||||||
|
/* If we have a match context, add the substring we just found */
|
||||||
|
if (mcx) {
|
||||||
|
match_context_push(str + i_str_start, i_str - i_str_start, mcx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reached end of pattern ? */
|
||||||
|
if (i_pat == pat_len) {
|
||||||
|
return (ptrdiff_t)i_str;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Pattern $ matches end of string */
|
||||||
|
if (pat[i_pat] == '$') {
|
||||||
|
return (str[i_str] == '\0') ? (ptrdiff_t)i_str : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Pattern * or ** matches multiple characters */
|
||||||
|
if (pat[i_pat] == '*') {
|
||||||
|
size_t len; /* length matched by "*" or "**" */
|
||||||
|
ptrdiff_t ret;
|
||||||
|
|
||||||
|
i_pat++;
|
||||||
|
if ((pat[i_pat] == '*') && (i_pat < pat_len)) {
|
||||||
|
/* Pattern ** matches all */
|
||||||
|
i_pat++;
|
||||||
|
len = strlen(str + i_str);
|
||||||
|
} else {
|
||||||
|
/* Pattern * matches all except / character */
|
||||||
|
len = strcspn(str + i_str, "/");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i_pat == pat_len) {
|
||||||
|
/* End of pattern reached. Add all to match context. */
|
||||||
|
if (mcx) {
|
||||||
|
match_context_push(str + i_str, len, mcx);
|
||||||
|
}
|
||||||
|
return (i_str + len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This loop searches for the longest possible match */
|
||||||
|
do {
|
||||||
|
ret = mg_match_impl(pat + i_pat,
|
||||||
|
(pat_len - (size_t)i_pat),
|
||||||
|
str + i_str + len,
|
||||||
|
mcx);
|
||||||
|
} while ((ret == -1) && (len-- > 0));
|
||||||
|
|
||||||
|
/* If we have a match context, add the substring we just found */
|
||||||
|
if (ret >= 0) {
|
||||||
|
if (mcx) {
|
||||||
|
match_context_push(str + i_str, len, mcx);
|
||||||
|
}
|
||||||
|
return (i_str + ret + len);
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Single character compare */
|
||||||
|
if (case_sensitive) {
|
||||||
|
if (pat[i_pat] != str[i_str]) {
|
||||||
|
/* case sensitive compare: mismatch */
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
} else if (lowercase(&pat[i_pat]) != lowercase(&str[i_str])) {
|
||||||
|
/* case insensitive compare: mismatch */
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
i_pat++;
|
||||||
|
i_str++;
|
||||||
|
}
|
||||||
|
return (ptrdiff_t)i_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static ptrdiff_t
|
||||||
|
mg_match_alternatives(const char *pat,
|
||||||
|
size_t pat_len,
|
||||||
|
const char *str,
|
||||||
|
struct mg_match_context *mcx)
|
||||||
|
{
|
||||||
|
const char *match_alternative = (const char *)memchr(pat, '|', pat_len);
|
||||||
|
|
||||||
|
if (mcx != NULL) {
|
||||||
|
match_context_reset(mcx);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (match_alternative != NULL) {
|
||||||
|
/* Split at | for alternative match */
|
||||||
|
size_t left_size = (size_t)(match_alternative - pat);
|
||||||
|
|
||||||
|
/* Try left string first */
|
||||||
|
ptrdiff_t ret = mg_match_impl(pat, left_size, str, mcx);
|
||||||
|
if (ret >= 0) {
|
||||||
|
/* A 0-byte match is also valid */
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reset possible incomplete match data */
|
||||||
|
if (mcx != NULL) {
|
||||||
|
match_context_reset(mcx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If no match: try right side */
|
||||||
|
pat += left_size + 1;
|
||||||
|
pat_len -= left_size + 1;
|
||||||
|
match_alternative = (const char *)memchr(pat, '|', pat_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handled all | operators. This is the final string. */
|
||||||
|
return mg_match_impl(pat, pat_len, str, mcx);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
match_compare(const void *p1, const void *p2, void *user)
|
||||||
|
{
|
||||||
|
const struct mg_match_element *e1 = (const struct mg_match_element *)p1;
|
||||||
|
const struct mg_match_element *e2 = (const struct mg_match_element *)p2;
|
||||||
|
|
||||||
|
/* unused */
|
||||||
|
(void)user;
|
||||||
|
|
||||||
|
if (e1->str > e2->str) {
|
||||||
|
return +1;
|
||||||
|
}
|
||||||
|
if (e1->str < e2->str) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#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) {
|
||||||
|
/* Remove possible incomplete data */
|
||||||
|
match_context_reset(mcx);
|
||||||
|
} else {
|
||||||
|
/* Join "?*" to one pattern. */
|
||||||
|
size_t i, j;
|
||||||
|
|
||||||
|
/* Use difference of two array elements instead of sizeof, since
|
||||||
|
* there may be some additional padding bytes. */
|
||||||
|
size_t elmsize =
|
||||||
|
(size_t)(&mcx->match[1]) - (size_t)(&mcx->match[0]);
|
||||||
|
|
||||||
|
/* First sort the matches by address ("str" begin to end) */
|
||||||
|
mg_sort(mcx->match, mcx->num_matches, elmsize, match_compare, NULL);
|
||||||
|
|
||||||
|
/* Join consecutive matches */
|
||||||
|
i = 1;
|
||||||
|
while (i < mcx->num_matches) {
|
||||||
|
if ((mcx->match[i - 1].str + mcx->match[i - 1].len)
|
||||||
|
== mcx->match[i].str) {
|
||||||
|
/* Two matches are consecutive. Join length. */
|
||||||
|
mcx->match[i - 1].len += mcx->match[i].len;
|
||||||
|
|
||||||
|
/* Shift all list elements. */
|
||||||
|
for (j = i + 1; j < mcx->num_matches; j++) {
|
||||||
|
mcx->match[j - 1].len = mcx->match[j].len;
|
||||||
|
mcx->match[j - 1].str = mcx->match[j].str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove/blank last list element. */
|
||||||
|
mcx->num_matches--;
|
||||||
|
mcx->match[mcx->num_matches].str = NULL;
|
||||||
|
mcx->match[mcx->num_matches].len = 0;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static ptrdiff_t
|
||||||
|
match_prefix(const char *pattern, size_t pattern_len, const char *str)
|
||||||
|
{
|
||||||
|
if (pattern == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return mg_match_alternatives(pattern, pattern_len, str, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static ptrdiff_t
|
||||||
|
match_prefix_strlen(const char *pattern, const char *str)
|
||||||
|
{
|
||||||
|
if (pattern == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return mg_match_alternatives(pattern, strlen(pattern), str, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* End of match.inl */
|
3
vendor/CivetWeb/md5.inl
vendored
3
vendor/CivetWeb/md5.inl
vendored
@ -131,6 +131,7 @@ MD5_STATIC void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#if !defined(MD5_STATIC)
|
#if !defined(MD5_STATIC)
|
||||||
|
#include <stdint.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -239,7 +240,7 @@ md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
|
|||||||
* On little-endian machines, we can process properly aligned
|
* On little-endian machines, we can process properly aligned
|
||||||
* data without copying it.
|
* data without copying it.
|
||||||
*/
|
*/
|
||||||
if (!((data - (const md5_byte_t *)0) & 3)) {
|
if (!(((uintptr_t)data) & 3)) {
|
||||||
/* data are properly aligned, a direct assignment is possible */
|
/* data are properly aligned, a direct assignment is possible */
|
||||||
/* cast through a (void *) should avoid a compiler warning,
|
/* cast through a (void *) should avoid a compiler warning,
|
||||||
see
|
see
|
||||||
|
2
vendor/CivetWeb/mod_zlib.inl
vendored
2
vendor/CivetWeb/mod_zlib.inl
vendored
@ -12,7 +12,7 @@ zalloc(void *opaque, uInt items, uInt size)
|
|||||||
{
|
{
|
||||||
struct mg_connection *conn = (struct mg_connection *)opaque;
|
struct mg_connection *conn = (struct mg_connection *)opaque;
|
||||||
void *ret = mg_calloc_ctx(items, size, conn->phys_ctx);
|
void *ret = mg_calloc_ctx(items, size, conn->phys_ctx);
|
||||||
(void)conn; /* mg_calloc_ctx makro might not need it */
|
(void)conn; /* mg_calloc_ctx macro might not need it */
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
28
vendor/CivetWeb/response.inl
vendored
28
vendor/CivetWeb/response.inl
vendored
@ -37,7 +37,7 @@ free_buffered_response_header_list(struct mg_connection *conn)
|
|||||||
|
|
||||||
|
|
||||||
/* Send first line of HTTP/1.x response */
|
/* Send first line of HTTP/1.x response */
|
||||||
static void
|
static int
|
||||||
send_http1_response_status_line(struct mg_connection *conn)
|
send_http1_response_status_line(struct mg_connection *conn)
|
||||||
{
|
{
|
||||||
const char *status_txt;
|
const char *status_txt;
|
||||||
@ -55,7 +55,13 @@ send_http1_response_status_line(struct mg_connection *conn)
|
|||||||
/* mg_get_response_code_text will never return NULL */
|
/* mg_get_response_code_text will never return NULL */
|
||||||
status_txt = mg_get_response_code_text(conn, conn->status_code);
|
status_txt = mg_get_response_code_text(conn, conn->status_code);
|
||||||
|
|
||||||
mg_printf(conn, "HTTP/%s %i %s\r\n", http_version, status_code, status_txt);
|
if (mg_printf(
|
||||||
|
conn, "HTTP/%s %i %s\r\n", http_version, status_code, status_txt)
|
||||||
|
< 10) {
|
||||||
|
/* Network sending failed */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -68,10 +74,12 @@ send_http1_response_status_line(struct mg_connection *conn)
|
|||||||
* -1: parameter error
|
* -1: parameter error
|
||||||
* -2: invalid connection type
|
* -2: invalid connection type
|
||||||
* -3: invalid connection status
|
* -3: invalid connection status
|
||||||
|
* -4: network error (only if built with NO_RESPONSE_BUFFERING)
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
mg_response_header_start(struct mg_connection *conn, int status)
|
mg_response_header_start(struct mg_connection *conn, int status)
|
||||||
{
|
{
|
||||||
|
int ret = 0;
|
||||||
if ((conn == NULL) || (status < 100) || (status > 999)) {
|
if ((conn == NULL) || (status < 100) || (status > 999)) {
|
||||||
/* Parameter error */
|
/* Parameter error */
|
||||||
return -1;
|
return -1;
|
||||||
@ -93,11 +101,13 @@ mg_response_header_start(struct mg_connection *conn, int status)
|
|||||||
#if !defined(NO_RESPONSE_BUFFERING)
|
#if !defined(NO_RESPONSE_BUFFERING)
|
||||||
free_buffered_response_header_list(conn);
|
free_buffered_response_header_list(conn);
|
||||||
#else
|
#else
|
||||||
send_http1_response_status_line(conn);
|
if (!send_http1_response_status_line(conn)) {
|
||||||
|
ret = -4;
|
||||||
|
};
|
||||||
conn->request_state = 1; /* Reset from 10 to 1 */
|
conn->request_state = 1; /* Reset from 10 to 1 */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return 0;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -254,6 +264,7 @@ static int http2_send_response_headers(struct mg_connection *conn);
|
|||||||
* -1: parameter error
|
* -1: parameter error
|
||||||
* -2: invalid connection type
|
* -2: invalid connection type
|
||||||
* -3: invalid connection status
|
* -3: invalid connection status
|
||||||
|
* -4: network send failed
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
mg_response_header_send(struct mg_connection *conn)
|
mg_response_header_send(struct mg_connection *conn)
|
||||||
@ -285,12 +296,16 @@ mg_response_header_send(struct mg_connection *conn)
|
|||||||
#if defined(USE_HTTP2)
|
#if defined(USE_HTTP2)
|
||||||
if (conn->protocol_type == PROTOCOL_TYPE_HTTP2) {
|
if (conn->protocol_type == PROTOCOL_TYPE_HTTP2) {
|
||||||
int ret = http2_send_response_headers(conn);
|
int ret = http2_send_response_headers(conn);
|
||||||
return ret ? 0 : 0; /* todo */
|
free_buffered_response_header_list(conn);
|
||||||
|
return (ret ? 0 : -4);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Send */
|
/* Send */
|
||||||
send_http1_response_status_line(conn);
|
if (!send_http1_response_status_line(conn)) {
|
||||||
|
free_buffered_response_header_list(conn);
|
||||||
|
return -4;
|
||||||
|
};
|
||||||
for (i = 0; i < conn->response_info.num_headers; i++) {
|
for (i = 0; i < conn->response_info.num_headers; i++) {
|
||||||
mg_printf(conn,
|
mg_printf(conn,
|
||||||
"%s: %s\r\n",
|
"%s: %s\r\n",
|
||||||
@ -322,5 +337,6 @@ mg_response_header_send(struct mg_connection *conn)
|
|||||||
conn->request_state = 3;
|
conn->request_state = 3;
|
||||||
|
|
||||||
/* ok */
|
/* ok */
|
||||||
|
free_buffered_response_header_list(conn);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
48
vendor/CivetWeb/sort.inl
vendored
Normal file
48
vendor/CivetWeb/sort.inl
vendored
Normal file
@ -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 */
|
Loading…
Reference in New Issue
Block a user