diff --git a/docs/README.md b/docs/README.md index 302ec11b..31ff3544 100644 --- a/docs/README.md +++ b/docs/README.md @@ -580,14 +580,17 @@ Usage example: mg_send(c, "hi", 2); // Append string "hi" to the output buffer ``` -### mg\_printf() +### mg\_printf(), mg\_vprintf() ```c int mg_printf(struct mg_connection *, const char *fmt, ...); +int mg_vprintf(struct mg_connection *, const char *fmt, va_list ap); ``` Same as `mg_send()`, but formats data using `printf()` semantics. Return number of bytes appended to the output buffer. +NOTE: See [mg\_snprintf](#mg_snprintf-mg_vsnprintf) +for the list of supported format specifiers Parameters: - `c` - a connection pointer @@ -601,32 +604,6 @@ Usage example: mg_printf(c, "Hello, %s!", "world"); // Add "Hello, world!" to output buffer ``` -### mg\_vprintf() - -```c -int mg_vprintf(struct mg_connection *, const char *fmt, va_list ap); -``` - -Same as `mg_printf()`, but takes `va_list` argument as a parameter. - -Parameters: -- `c` - A connection pointer -- `fmt` - A format string in `printf` semantics -- `ap` - An arguments list - -Return value: Number of bytes appended to the output buffer. - -Usage example: - -```c -void foo(struct mg_connection *c, const char *fmt, ...) { - va_list ap; - va_start(ap, fmt); - mg_vprintf(c, fmt, ap); - va_end(ap); -} -``` - ### mg\_straddr ```c @@ -2385,10 +2362,11 @@ unsigned long val = mg_unhex(data, sizeof(data) - 1); // val is now 123 ``` -### mg\_asprintf() +### mg\_asprintf(), mg\_vasprintf() ```c int mg_asprintf(char **buf, size_t size, const char *fmt, ...); +int mg_vasprintf(char **buf, size_t size, const char *fmt, va_list ap); ``` Print message specified by printf-like format string `fmt` into a buffer @@ -2411,38 +2389,42 @@ char buf[1024], *pbuf = &buf; mg_asprintf(&pbuf, sizeof(buf), "Hello, %s!", "world"); // buf is now "Hello, world!" ``` -### mg\_vasprintf() - +### mg\_snprintf(), mg\_vsnprintf() ```c -int mg_vasprintf(char **buf, size_t size, const char *fmt, va_list ap); +size_t mg_snprintf(char *buf, size_t len, const char *fmt, ...); +size_t mg_vsnprintf(char *buf, size_t len, const char *fmt, va_list ap); +size_t mg_asprintf(char **buf, size_t len, const char *fmt, ...); +size_t mg_vasprintf(char **buf, size_t size, const char *fmt, va_list ap); ``` -Same as `mg_asprintf()` but uses `va_list` argument. +Print formatted string into a string buffer, just like `snprintf()` +standard function does, but in a predictable way that does not depend on +the C library or the build environment. The return value can be larger +than the buffer length `len`, in which case the overflow bytes are not printed. Parameters: - `buf` - Pointer to pointer to output buffer -- `size` - Pre-allocated buffer size +- `len` - Buffer size - `fmt` - printf-like format string -- `ap` - Parameters list + +Supported format specifiers: +- `hhd`, `hd`, `d`, `ld`, `lld` - for `char`, `short`, `int`, `long`, `int64_t` +- `hhu`, `hu`, `u`, `lu`, `llu` - same but for unsigned variants +- `hhx`, `hx`, `x`, `lx`, `llx` - same, unsigned and hex output +- `s` - `for char *` +- `p` - for any pointer, prints `0x.....` hex value +- `%X.Y` - optional width and precision modifiers +- `%.*` - optional precision modifier specified as `int` argument Return value: Number of bytes printed Usage example: + ```c -void foo(const char *fmt, ...) { - va_list ap; - va_start(ap, fmt); - char buf[1024], *pbuf = buf; - mg_vasprintf(&pbuf, sizeof(buf), fmt, ap); - va_end(ap); - - printf("%s\n", buf); -} - -// ... - -foo("Hello, %s!", "world"); // Print "Hello, world! - +mg_snprintf(buf, sizeof(buf), "%lld", (int64_t) 123); // 123 +mg_snprintf(buf, sizeof(buf), "%.2s", "abcdef"); // ab +mg_snprintf(buf, sizeof(buf), "%.*s", 2, "abcdef"); // ab +mg_snprintf(buf, sizeof(buf), "%05x", 123); // 00123 ``` ### mg\_to64() diff --git a/examples/http-server/main.c b/examples/http-server/main.c index a674897b..c6b2676f 100644 --- a/examples/http-server/main.c +++ b/examples/http-server/main.c @@ -5,7 +5,7 @@ #include "mongoose.h" static const char *s_debug_level = "2"; -static const char *s_root_dir = NULL; +static const char *s_root_dir = "."; static const char *s_listening_address = "http://0.0.0.0:8000"; static const char *s_enable_hexdump = "no"; static const char *s_ssi_pattern = "#.html"; diff --git a/mongoose.c b/mongoose.c index d01b72af..37e11363 100644 --- a/mongoose.c +++ b/mongoose.c @@ -472,7 +472,7 @@ bool mg_file_write(struct mg_fs *fs, const char *path, const void *buf, bool mg_file_printf(struct mg_fs *fs, const char *path, const char *fmt, ...) { char tmp[256], *buf = tmp; bool result; - int len; + size_t len; va_list ap; va_start(ap, fmt); len = mg_vasprintf(&buf, sizeof(tmp), fmt, ap); @@ -1246,8 +1246,8 @@ int mg_http_parse(const char *s, size_t len, struct mg_http_message *hm) { static void mg_http_vprintf_chunk(struct mg_connection *c, const char *fmt, va_list ap) { char mem[256], *buf = mem; - int len = mg_vasprintf(&buf, sizeof(mem), fmt, ap); - mg_printf(c, "%X\r\n", len); + size_t len = mg_vasprintf(&buf, sizeof(mem), fmt, ap); + mg_printf(c, "%lx\r\n", (unsigned long) len); mg_send(c, buf, len > 0 ? (size_t) len : 0); mg_send(c, "\r\n", 2); if (buf != mem) free(buf); @@ -1261,7 +1261,7 @@ void mg_http_printf_chunk(struct mg_connection *c, const char *fmt, ...) { } void mg_http_write_chunk(struct mg_connection *c, const char *buf, size_t len) { - mg_printf(c, "%lX\r\n", (unsigned long) len); + mg_printf(c, "%lx\r\n", (unsigned long) len); mg_send(c, buf, len); mg_send(c, "\r\n", 2); } @@ -1341,13 +1341,13 @@ void mg_http_reply(struct mg_connection *c, int code, const char *headers, const char *fmt, ...) { char mem[256], *buf = mem; va_list ap; - int len; + size_t len; va_start(ap, fmt); len = mg_vasprintf(&buf, sizeof(mem), fmt, ap); va_end(ap); mg_printf(c, "HTTP/1.1 %d %s\r\n%sContent-Length: %d\r\n\r\n", code, mg_http_status_code_str(code), headers == NULL ? "" : headers, len); - mg_send(c, buf, len > 0 ? (size_t) len : 0); + mg_send(c, buf, len > 0 ? len : 0); if (buf != mem) free(buf); } @@ -1481,7 +1481,7 @@ void mg_http_serve_file(struct mg_connection *c, struct mg_http_message *hm, mg_printf(c, "HTTP/1.1 304 Not Modified\r\nContent-Length: 0\r\n\r\n"); } else { int n, status = 200; - char range[100] = "", tmp[50]; + char range[100] = ""; int64_t r1 = 0, r2 = 0, cl = (int64_t) size; struct mg_str mime = guess_content_type(mg_str(path), opts->mime_types); @@ -1504,13 +1504,14 @@ void mg_http_serve_file(struct mg_connection *c, struct mg_http_message *hm, fs->sk(fd->fd, (size_t) r1); } } - mg_snprintf(tmp, sizeof(tmp), "Content-Length: %lld\r\n", cl); - LOG(LL_INFO, ("TMP: [%s]", tmp)); mg_printf(c, - "HTTP/1.1 %d %s\r\nContent-Type: %.*s\r\n" - "Etag: %s\r\n%s%s%s\r\n", + "HTTP/1.1 %d %s\r\n" + "Content-Type: %.*s\r\n" + "Etag: %s\r\n" + "Content-Length: %llu\r\n" + "%s%s\r\n", status, mg_http_status_code_str(status), (int) mime.len, mime.ptr, - etag, tmp, range, opts->extra_headers ? opts->extra_headers : ""); + etag, cl, range, opts->extra_headers ? opts->extra_headers : ""); if (mg_vcasecmp(&hm->method, "HEAD") == 0) { c->is_draining = 1; mg_fs_close(fd); @@ -1547,13 +1548,13 @@ static void printdirentry(const char *name, void *userdata) { if (flags & MG_FS_DIR) { mg_snprintf(sz, sizeof(sz), "%s", "[DIR]"); } else { - mg_snprintf(sz, sizeof(sz), "%llx", (uint64_t) size); + mg_snprintf(sz, sizeof(sz), "%lld", (uint64_t) size); } - mg_snprintf(mod, sizeof(mod), "%lx", (unsigned long) t); + mg_snprintf(mod, sizeof(mod), "%ld", (unsigned long) t); n = (int) mg_url_encode(name, strlen(name), path, sizeof(path)); mg_printf(d->c, " %s%s" - "%s%s\n", + "%s%s\n", n, path, slash, name, slash, (unsigned long) t, mod, flags & MG_FS_DIR ? (int64_t) -1 : (int64_t) size, sz); } @@ -1602,7 +1603,7 @@ static void listdir(struct mg_connection *c, struct mg_http_message *hm, "Index of %.*s%s%s" "" - "

Innex of %.*s

" + "

Index of %.*s

" "" "" @@ -1611,6 +1612,9 @@ static void listdir(struct mg_connection *c, struct mg_http_message *hm, "\n", (int) uri.len, uri.ptr, sort_js_code, sort_js_code2, (int) uri.len, uri.ptr); + mg_printf(c, "%s", + " " + "\n"); fs->ls(dir, printdirentry, &d); mg_printf(c, @@ -2077,11 +2081,11 @@ bool mg_log_prefix(int level, const char *file, int line, const char *fname) { void mg_log(const char *fmt, ...) { char mem[256], *buf = mem; va_list ap; - int len = 0; + size_t len = 0; va_start(ap, fmt); len = mg_vasprintf(&buf, sizeof(mem), fmt, ap); va_end(ap); - s_fn(buf, len > 0 ? (size_t) len : 0, s_fn_param); + s_fn(buf, len > 0 ? len : 0, s_fn_param); s_fn("\n", 1, s_fn_param); if (buf != mem) free(buf); } @@ -2584,16 +2588,16 @@ struct mg_connection *mg_mqtt_listen(struct mg_mgr *mgr, const char *url, -int mg_vprintf(struct mg_connection *c, const char *fmt, va_list ap) { +size_t mg_vprintf(struct mg_connection *c, const char *fmt, va_list ap) { char mem[256], *buf = mem; - int len = mg_vasprintf(&buf, sizeof(mem), fmt, ap); - len = mg_send(c, buf, len > 0 ? (size_t) len : 0); + size_t len = mg_vasprintf(&buf, sizeof(mem), fmt, ap); + len = mg_send(c, buf, len); if (buf != mem) free(buf); return len; } -int mg_printf(struct mg_connection *c, const char *fmt, ...) { - int len = 0; +size_t mg_printf(struct mg_connection *c, const char *fmt, ...) { + size_t len = 0; va_list ap; va_start(ap, fmt); len = mg_vprintf(c, fmt, ap); @@ -3202,7 +3206,7 @@ static void iolog(struct mg_connection *c, char *buf, long n, bool r) { (char) ('0' + c->is_writable), '\0'}; LOG(log_level, - ("%-3lu %s %d:%d %ld err %d (%s)", c->id, flags, (int) c->send.len, + ("%3lu %s %d:%d %ld err %d (%s)", c->id, flags, (int) c->send.len, (int) c->recv.len, n, MG_SOCK_ERRNO, strerror(errno))); if (n == 0) { // Do nothing @@ -3981,7 +3985,8 @@ size_t mg_lld(char *buf, int64_t val, bool is_signed, bool is_hex) { static size_t mg_copys(char *buf, size_t len, size_t n, char *p, size_t k) { size_t j = 0; - for (j = 0; j < k && j + n < len && p[j]; j++) buf[n + j] = p[j]; + for (j = 0; j < k && p[j]; j++) + if (j + n < len) buf[n + j] = p[j]; return j; } @@ -3989,8 +3994,9 @@ size_t mg_vsnprintf(char *buf, size_t len, const char *fmt, va_list ap) { size_t i = 0, n = 0; while (fmt[i] != '\0') { if (fmt[i] == '%') { - size_t j, k, is_long = 0, w = 0 /* width */, pr = 0 /* precision */; + size_t j, k, x = 0, is_long = 0, w = 0 /* width */, pr = 0 /* prec */; char pad = ' ', c = fmt[++i]; + if (c == '#') x++, c = fmt[++i]; if (c == '0') pad = '0', c = fmt[++i]; while (isdigit(c)) w *= 10, w += (size_t) (c - '0'), c = fmt[++i]; if (c == '.') { @@ -4002,12 +4008,14 @@ size_t mg_vsnprintf(char *buf, size_t len, const char *fmt, va_list ap) { while (isdigit(c)) pr *= 10, pr += (size_t) (c - '0'), c = fmt[++i]; } } + while (c == 'h') c = fmt[++i]; // Treat h and hh as int if (c == 'l') { is_long++, c = fmt[++i]; if (c == 'l') is_long++, c = fmt[++i]; } - if (c == 'd' || c == 'u' || c == 'x') { - bool s = (c == 'd'), h = (c == 'x'); + if (c == 'p') x = 1, is_long = 1; + if (c == 'd' || c == 'u' || c == 'x' || c == 'X' || c == 'p') { + bool s = (c == 'd'), h = (c == 'x' || c == 'X' || c == 'p'); char tmp[30]; if (is_long == 2) { int64_t v = va_arg(ap, int64_t); @@ -4019,7 +4027,9 @@ size_t mg_vsnprintf(char *buf, size_t len, const char *fmt, va_list ap) { int v = va_arg(ap, int); k = mg_lld(tmp, s ? (int64_t) v : (int64_t) (unsigned) v, s, h); } - for (j = 0; n < len && k < w && j + k < w; j++) buf[n++] = pad; + for (j = 0; k < w && j + k < w; j++) + mg_copys(buf, len, n, &pad, 1), n++; + if (x) mg_copys(buf, len, n, (char *) "0x", 2), n += 2; mg_copys(buf, len, n, tmp, k); n += k; } else if (c == 'c') { @@ -4029,8 +4039,14 @@ size_t mg_vsnprintf(char *buf, size_t len, const char *fmt, va_list ap) { } else if (c == 's') { char *p = va_arg(ap, char *); if (pr == 0) pr = p == NULL ? 0 : strlen(p); - for (j = 0; n < len && pr < w && j + pr < w; j++) buf[n++] = pad; + for (j = 0; pr < w && j + pr < w; j++) + mg_copys(buf, len, n, &pad, 1), n++; n += mg_copys(buf, len, n, p, pr); + } else if (c == '%') { + if (n < len) buf[n] = '%'; + n++; + } else { + abort(); // Unsupported format specifier } i++; } else { @@ -4109,43 +4125,21 @@ void mg_unhex(const char *buf, size_t len, unsigned char *to) { } } -int mg_vasprintf(char **buf, size_t size, const char *fmt, va_list ap) { +size_t mg_vasprintf(char **buf, size_t size, const char *fmt, va_list ap) { va_list ap_copy; - int len; + size_t len; va_copy(ap_copy, ap); - len = vsnprintf(*buf, size, fmt, ap_copy); + len = mg_vsnprintf(*buf, size, fmt, ap_copy); va_end(ap_copy); - if (len < 0) { - // eCos and Windows are not standard-compliant and return -1 when - // the buffer is too small. Keep allocating larger buffers until we - // succeed or out of memory. - // LCOV_EXCL_START - *buf = NULL; - while (len < 0) { - free(*buf); - if (size == 0) size = 5; - size *= 2; - if ((*buf = (char *) calloc(1, size)) == NULL) { - len = -1; - break; - } + if (len >= size) { + // Allocate a buffer that is large enough + if ((*buf = (char *) calloc(1, len + 1)) == NULL) { + len = 0; + } else { va_copy(ap_copy, ap); - len = vsnprintf(*buf, size - 1, fmt, ap_copy); - va_end(ap_copy); - } - // Microsoft version of vsnprintf() is not always null-terminated, so put - // the terminator manually - if (*buf != NULL) (*buf)[len] = 0; - // LCOV_EXCL_STOP - } else if (len >= (int) size) { - /// Standard-compliant code path. Allocate a buffer that is large enough - if ((*buf = (char *) calloc(1, (size_t) len + 1)) == NULL) { - len = -1; // LCOV_EXCL_LINE - } else { // LCOV_EXCL_LINE - va_copy(ap_copy, ap); - len = vsnprintf(*buf, (size_t) len + 1, fmt, ap_copy); + len = mg_vsnprintf(*buf, len + 1, fmt, ap_copy); va_end(ap_copy); } } @@ -4153,8 +4147,8 @@ int mg_vasprintf(char **buf, size_t size, const char *fmt, va_list ap) { return len; } -int mg_asprintf(char **buf, size_t size, const char *fmt, ...) { - int ret; +size_t mg_asprintf(char **buf, size_t size, const char *fmt, ...) { + size_t ret; va_list ap; va_start(ap, fmt); ret = mg_vasprintf(buf, size, fmt, ap); @@ -5036,7 +5030,7 @@ struct mg_connection *mg_ws_connect(struct mg_mgr *mgr, const char *url, if (c != NULL) { char nonce[16], key[30], mem1[128], mem2[256], *buf1 = mem1, *buf2 = mem2; struct mg_str host = mg_url_host(url); - int n1 = 0, n2 = 0; + size_t n1 = 0, n2 = 0; if (fmt != NULL) { va_list ap; va_start(ap, fmt); @@ -5058,7 +5052,7 @@ struct mg_connection *mg_ws_connect(struct mg_mgr *mgr, const char *url, "Sec-WebSocket-Key: %s\r\n" "\r\n", mg_url_uri(url), (int) host.len, host.ptr, n1, buf1, key); - mg_send(c, buf2, n2 > 0 ? (size_t) n2 : 0); + mg_send(c, buf2, n2 > 0 ? n2 : 0); if (buf1 != mem1) free(buf1); if (buf2 != mem2) free(buf2); c->pfn = mg_ws_cb; diff --git a/mongoose.h b/mongoose.h index 7d367e25..4c705203 100644 --- a/mongoose.h +++ b/mongoose.h @@ -54,13 +54,11 @@ extern "C" { #endif #endif // !defined(MG_ARCH) -#if !defined(PRINTF_LIKE) -#if defined(__GNUC__) || defined(__clang__) || defined(__TI_COMPILER_VERSION__) +#if defined(__GNUC__) && defined(__arm__) #define PRINTF_LIKE(f, a) __attribute__((format(printf, f, a))) #else #define PRINTF_LIKE(f, a) #endif -#endif #if MG_ARCH == MG_ARCH_CUSTOM #include @@ -401,8 +399,6 @@ typedef int socklen_t; #define S_ISDIR(x) (((x) &_S_IFMT) == _S_IFDIR) #endif -#define MG_INT64_FMT "%I64d" - #ifndef MG_ENABLE_DIRLIST #define MG_ENABLE_DIRLIST 1 #endif @@ -505,10 +501,6 @@ typedef int socklen_t; #define MG_DIRSEP '/' #endif -#ifndef MG_INT64_FMT -#define MG_INT64_FMT "%lld" -#endif - #ifndef MG_ENABLE_FILE #if defined(FOPEN_MAX) #define MG_ENABLE_FILE 1 @@ -550,13 +542,13 @@ bool mg_globmatch(const char *pattern, size_t plen, const char *s, size_t n); bool mg_commalist(struct mg_str *s, struct mg_str *k, struct mg_str *v); bool mg_commalist(struct mg_str *s, struct mg_str *k, struct mg_str *v); size_t mg_vsnprintf(char *buf, size_t len, const char *fmt, va_list ap); -size_t mg_snprintf(char *buf, size_t len, const char *fmt, ...); +size_t mg_snprintf(char *, size_t, const char *fmt, ...) PRINTF_LIKE(3, 4); char *mg_hexdump(const void *buf, size_t len); char *mg_hex(const void *buf, size_t len, char *dst); void mg_unhex(const char *buf, size_t len, unsigned char *to); unsigned long mg_unhexn(const char *s, size_t len); -int mg_asprintf(char **buf, size_t size, const char *fmt, ...); -int mg_vasprintf(char **buf, size_t size, const char *fmt, va_list ap); +size_t mg_asprintf(char **, size_t, const char *fmt, ...) PRINTF_LIKE(3, 4); +size_t mg_vasprintf(char **buf, size_t size, const char *fmt, va_list ap); int mg_check_ip_acl(struct mg_str acl, uint32_t remote_ip); int64_t mg_to64(struct mg_str str); @@ -865,8 +857,8 @@ struct mg_connection *mg_connect(struct mg_mgr *, const char *url, mg_event_handler_t fn, void *fn_data); void mg_connect_resolved(struct mg_connection *); bool mg_send(struct mg_connection *, const void *, size_t); -int mg_printf(struct mg_connection *, const char *fmt, ...); -int mg_vprintf(struct mg_connection *, const char *fmt, va_list ap); +size_t mg_printf(struct mg_connection *, const char *fmt, ...); +size_t mg_vprintf(struct mg_connection *, const char *fmt, va_list ap); char *mg_straddr(struct mg_addr *, char *, size_t); bool mg_aton(struct mg_str str, struct mg_addr *addr); char *mg_ntoa(const struct mg_addr *addr, char *buf, size_t len); diff --git a/src/arch.h b/src/arch.h index 1d7d0c0b..fee7671d 100644 --- a/src/arch.h +++ b/src/arch.h @@ -29,13 +29,11 @@ #endif #endif // !defined(MG_ARCH) -#if !defined(PRINTF_LIKE) -#if defined(__GNUC__) || defined(__clang__) || defined(__TI_COMPILER_VERSION__) +#if defined(__GNUC__) && defined(__arm__) #define PRINTF_LIKE(f, a) __attribute__((format(printf, f, a))) #else #define PRINTF_LIKE(f, a) #endif -#endif #if MG_ARCH == MG_ARCH_CUSTOM #include diff --git a/src/arch_win32.h b/src/arch_win32.h index 33407a84..ea796a7b 100644 --- a/src/arch_win32.h +++ b/src/arch_win32.h @@ -84,8 +84,6 @@ typedef int socklen_t; #define S_ISDIR(x) (((x) &_S_IFMT) == _S_IFDIR) #endif -#define MG_INT64_FMT "%I64d" - #ifndef MG_ENABLE_DIRLIST #define MG_ENABLE_DIRLIST 1 #endif diff --git a/src/config.h b/src/config.h index d1c637e6..51d2a00e 100644 --- a/src/config.h +++ b/src/config.h @@ -95,10 +95,6 @@ #define MG_DIRSEP '/' #endif -#ifndef MG_INT64_FMT -#define MG_INT64_FMT "%lld" -#endif - #ifndef MG_ENABLE_FILE #if defined(FOPEN_MAX) #define MG_ENABLE_FILE 1 diff --git a/src/fs.c b/src/fs.c index d3f601f0..ad329f2b 100644 --- a/src/fs.c +++ b/src/fs.c @@ -64,7 +64,7 @@ bool mg_file_write(struct mg_fs *fs, const char *path, const void *buf, bool mg_file_printf(struct mg_fs *fs, const char *path, const char *fmt, ...) { char tmp[256], *buf = tmp; bool result; - int len; + size_t len; va_list ap; va_start(ap, fmt); len = mg_vasprintf(&buf, sizeof(tmp), fmt, ap); diff --git a/src/http.c b/src/http.c index 2562b6d0..64f56f9c 100644 --- a/src/http.c +++ b/src/http.c @@ -258,8 +258,8 @@ int mg_http_parse(const char *s, size_t len, struct mg_http_message *hm) { static void mg_http_vprintf_chunk(struct mg_connection *c, const char *fmt, va_list ap) { char mem[256], *buf = mem; - int len = mg_vasprintf(&buf, sizeof(mem), fmt, ap); - mg_printf(c, "%X\r\n", len); + size_t len = mg_vasprintf(&buf, sizeof(mem), fmt, ap); + mg_printf(c, "%lx\r\n", (unsigned long) len); mg_send(c, buf, len > 0 ? (size_t) len : 0); mg_send(c, "\r\n", 2); if (buf != mem) free(buf); @@ -273,7 +273,7 @@ void mg_http_printf_chunk(struct mg_connection *c, const char *fmt, ...) { } void mg_http_write_chunk(struct mg_connection *c, const char *buf, size_t len) { - mg_printf(c, "%lX\r\n", (unsigned long) len); + mg_printf(c, "%lx\r\n", (unsigned long) len); mg_send(c, buf, len); mg_send(c, "\r\n", 2); } @@ -353,13 +353,13 @@ void mg_http_reply(struct mg_connection *c, int code, const char *headers, const char *fmt, ...) { char mem[256], *buf = mem; va_list ap; - int len; + size_t len; va_start(ap, fmt); len = mg_vasprintf(&buf, sizeof(mem), fmt, ap); va_end(ap); mg_printf(c, "HTTP/1.1 %d %s\r\n%sContent-Length: %d\r\n\r\n", code, mg_http_status_code_str(code), headers == NULL ? "" : headers, len); - mg_send(c, buf, len > 0 ? (size_t) len : 0); + mg_send(c, buf, len > 0 ? len : 0); if (buf != mem) free(buf); } @@ -493,7 +493,7 @@ void mg_http_serve_file(struct mg_connection *c, struct mg_http_message *hm, mg_printf(c, "HTTP/1.1 304 Not Modified\r\nContent-Length: 0\r\n\r\n"); } else { int n, status = 200; - char range[100] = "", tmp[50]; + char range[100] = ""; int64_t r1 = 0, r2 = 0, cl = (int64_t) size; struct mg_str mime = guess_content_type(mg_str(path), opts->mime_types); @@ -516,13 +516,14 @@ void mg_http_serve_file(struct mg_connection *c, struct mg_http_message *hm, fs->sk(fd->fd, (size_t) r1); } } - mg_snprintf(tmp, sizeof(tmp), "Content-Length: %lld\r\n", cl); - LOG(LL_INFO, ("TMP: [%s]", tmp)); mg_printf(c, - "HTTP/1.1 %d %s\r\nContent-Type: %.*s\r\n" - "Etag: %s\r\n%s%s%s\r\n", + "HTTP/1.1 %d %s\r\n" + "Content-Type: %.*s\r\n" + "Etag: %s\r\n" + "Content-Length: %llu\r\n" + "%s%s\r\n", status, mg_http_status_code_str(status), (int) mime.len, mime.ptr, - etag, tmp, range, opts->extra_headers ? opts->extra_headers : ""); + etag, cl, range, opts->extra_headers ? opts->extra_headers : ""); if (mg_vcasecmp(&hm->method, "HEAD") == 0) { c->is_draining = 1; mg_fs_close(fd); @@ -559,13 +560,13 @@ static void printdirentry(const char *name, void *userdata) { if (flags & MG_FS_DIR) { mg_snprintf(sz, sizeof(sz), "%s", "[DIR]"); } else { - mg_snprintf(sz, sizeof(sz), "%llx", (uint64_t) size); + mg_snprintf(sz, sizeof(sz), "%lld", (uint64_t) size); } - mg_snprintf(mod, sizeof(mod), "%lx", (unsigned long) t); + mg_snprintf(mod, sizeof(mod), "%ld", (unsigned long) t); n = (int) mg_url_encode(name, strlen(name), path, sizeof(path)); mg_printf(d->c, " " - "\n", + "\n", n, path, slash, name, slash, (unsigned long) t, mod, flags & MG_FS_DIR ? (int64_t) -1 : (int64_t) size, sz); } @@ -614,7 +615,7 @@ static void listdir(struct mg_connection *c, struct mg_http_message *hm, "Index of %.*s%s%s" "" - "

Innex of %.*s

Name" "ModifiedSize
..[DIR]
%s%s%s%s
%s%s
" + "

Index of %.*s

" "" "" @@ -623,6 +624,9 @@ static void listdir(struct mg_connection *c, struct mg_http_message *hm, "\n", (int) uri.len, uri.ptr, sort_js_code, sort_js_code2, (int) uri.len, uri.ptr); + mg_printf(c, "%s", + " " + "\n"); fs->ls(dir, printdirentry, &d); mg_printf(c, diff --git a/src/log.c b/src/log.c index 0ef06924..1e7921cf 100644 --- a/src/log.c +++ b/src/log.c @@ -50,11 +50,11 @@ bool mg_log_prefix(int level, const char *file, int line, const char *fname) { void mg_log(const char *fmt, ...) { char mem[256], *buf = mem; va_list ap; - int len = 0; + size_t len = 0; va_start(ap, fmt); len = mg_vasprintf(&buf, sizeof(mem), fmt, ap); va_end(ap); - s_fn(buf, len > 0 ? (size_t) len : 0, s_fn_param); + s_fn(buf, len > 0 ? len : 0, s_fn_param); s_fn("\n", 1, s_fn_param); if (buf != mem) free(buf); } diff --git a/src/net.c b/src/net.c index f8f7c660..ea35de34 100644 --- a/src/net.c +++ b/src/net.c @@ -2,16 +2,16 @@ #include "log.h" #include "util.h" -int mg_vprintf(struct mg_connection *c, const char *fmt, va_list ap) { +size_t mg_vprintf(struct mg_connection *c, const char *fmt, va_list ap) { char mem[256], *buf = mem; - int len = mg_vasprintf(&buf, sizeof(mem), fmt, ap); - len = mg_send(c, buf, len > 0 ? (size_t) len : 0); + size_t len = mg_vasprintf(&buf, sizeof(mem), fmt, ap); + len = mg_send(c, buf, len); if (buf != mem) free(buf); return len; } -int mg_printf(struct mg_connection *c, const char *fmt, ...) { - int len = 0; +size_t mg_printf(struct mg_connection *c, const char *fmt, ...) { + size_t len = 0; va_list ap; va_start(ap, fmt); len = mg_vprintf(c, fmt, ap); diff --git a/src/net.h b/src/net.h index 41f50956..da43809f 100644 --- a/src/net.h +++ b/src/net.h @@ -69,8 +69,8 @@ struct mg_connection *mg_connect(struct mg_mgr *, const char *url, mg_event_handler_t fn, void *fn_data); void mg_connect_resolved(struct mg_connection *); bool mg_send(struct mg_connection *, const void *, size_t); -int mg_printf(struct mg_connection *, const char *fmt, ...); -int mg_vprintf(struct mg_connection *, const char *fmt, va_list ap); +size_t mg_printf(struct mg_connection *, const char *fmt, ...); +size_t mg_vprintf(struct mg_connection *, const char *fmt, va_list ap); char *mg_straddr(struct mg_addr *, char *, size_t); bool mg_aton(struct mg_str str, struct mg_addr *addr); char *mg_ntoa(const struct mg_addr *addr, char *buf, size_t len); diff --git a/src/sock.c b/src/sock.c index 5dd7932b..367a0a83 100644 --- a/src/sock.c +++ b/src/sock.c @@ -117,7 +117,7 @@ static void iolog(struct mg_connection *c, char *buf, long n, bool r) { (char) ('0' + c->is_writable), '\0'}; LOG(log_level, - ("%-3lu %s %d:%d %ld err %d (%s)", c->id, flags, (int) c->send.len, + ("%3lu %s %d:%d %ld err %d (%s)", c->id, flags, (int) c->send.len, (int) c->recv.len, n, MG_SOCK_ERRNO, strerror(errno))); if (n == 0) { // Do nothing diff --git a/src/str.c b/src/str.c index a3666293..4fdd142e 100644 --- a/src/str.c +++ b/src/str.c @@ -170,7 +170,8 @@ size_t mg_lld(char *buf, int64_t val, bool is_signed, bool is_hex) { static size_t mg_copys(char *buf, size_t len, size_t n, char *p, size_t k) { size_t j = 0; - for (j = 0; j < k && j + n < len && p[j]; j++) buf[n + j] = p[j]; + for (j = 0; j < k && p[j]; j++) + if (j + n < len) buf[n + j] = p[j]; return j; } @@ -178,8 +179,9 @@ size_t mg_vsnprintf(char *buf, size_t len, const char *fmt, va_list ap) { size_t i = 0, n = 0; while (fmt[i] != '\0') { if (fmt[i] == '%') { - size_t j, k, is_long = 0, w = 0 /* width */, pr = 0 /* precision */; + size_t j, k, x = 0, is_long = 0, w = 0 /* width */, pr = 0 /* prec */; char pad = ' ', c = fmt[++i]; + if (c == '#') x++, c = fmt[++i]; if (c == '0') pad = '0', c = fmt[++i]; while (isdigit(c)) w *= 10, w += (size_t) (c - '0'), c = fmt[++i]; if (c == '.') { @@ -191,12 +193,14 @@ size_t mg_vsnprintf(char *buf, size_t len, const char *fmt, va_list ap) { while (isdigit(c)) pr *= 10, pr += (size_t) (c - '0'), c = fmt[++i]; } } + while (c == 'h') c = fmt[++i]; // Treat h and hh as int if (c == 'l') { is_long++, c = fmt[++i]; if (c == 'l') is_long++, c = fmt[++i]; } - if (c == 'd' || c == 'u' || c == 'x') { - bool s = (c == 'd'), h = (c == 'x'); + if (c == 'p') x = 1, is_long = 1; + if (c == 'd' || c == 'u' || c == 'x' || c == 'X' || c == 'p') { + bool s = (c == 'd'), h = (c == 'x' || c == 'X' || c == 'p'); char tmp[30]; if (is_long == 2) { int64_t v = va_arg(ap, int64_t); @@ -208,7 +212,9 @@ size_t mg_vsnprintf(char *buf, size_t len, const char *fmt, va_list ap) { int v = va_arg(ap, int); k = mg_lld(tmp, s ? (int64_t) v : (int64_t) (unsigned) v, s, h); } - for (j = 0; n < len && k < w && j + k < w; j++) buf[n++] = pad; + for (j = 0; k < w && j + k < w; j++) + mg_copys(buf, len, n, &pad, 1), n++; + if (x) mg_copys(buf, len, n, (char *) "0x", 2), n += 2; mg_copys(buf, len, n, tmp, k); n += k; } else if (c == 'c') { @@ -218,8 +224,14 @@ size_t mg_vsnprintf(char *buf, size_t len, const char *fmt, va_list ap) { } else if (c == 's') { char *p = va_arg(ap, char *); if (pr == 0) pr = p == NULL ? 0 : strlen(p); - for (j = 0; n < len && pr < w && j + pr < w; j++) buf[n++] = pad; + for (j = 0; pr < w && j + pr < w; j++) + mg_copys(buf, len, n, &pad, 1), n++; n += mg_copys(buf, len, n, p, pr); + } else if (c == '%') { + if (n < len) buf[n] = '%'; + n++; + } else { + abort(); // Unsupported format specifier } i++; } else { @@ -298,43 +310,21 @@ void mg_unhex(const char *buf, size_t len, unsigned char *to) { } } -int mg_vasprintf(char **buf, size_t size, const char *fmt, va_list ap) { +size_t mg_vasprintf(char **buf, size_t size, const char *fmt, va_list ap) { va_list ap_copy; - int len; + size_t len; va_copy(ap_copy, ap); - len = vsnprintf(*buf, size, fmt, ap_copy); + len = mg_vsnprintf(*buf, size, fmt, ap_copy); va_end(ap_copy); - if (len < 0) { - // eCos and Windows are not standard-compliant and return -1 when - // the buffer is too small. Keep allocating larger buffers until we - // succeed or out of memory. - // LCOV_EXCL_START - *buf = NULL; - while (len < 0) { - free(*buf); - if (size == 0) size = 5; - size *= 2; - if ((*buf = (char *) calloc(1, size)) == NULL) { - len = -1; - break; - } + if (len >= size) { + // Allocate a buffer that is large enough + if ((*buf = (char *) calloc(1, len + 1)) == NULL) { + len = 0; + } else { va_copy(ap_copy, ap); - len = vsnprintf(*buf, size - 1, fmt, ap_copy); - va_end(ap_copy); - } - // Microsoft version of vsnprintf() is not always null-terminated, so put - // the terminator manually - if (*buf != NULL) (*buf)[len] = 0; - // LCOV_EXCL_STOP - } else if (len >= (int) size) { - /// Standard-compliant code path. Allocate a buffer that is large enough - if ((*buf = (char *) calloc(1, (size_t) len + 1)) == NULL) { - len = -1; // LCOV_EXCL_LINE - } else { // LCOV_EXCL_LINE - va_copy(ap_copy, ap); - len = vsnprintf(*buf, (size_t) len + 1, fmt, ap_copy); + len = mg_vsnprintf(*buf, len + 1, fmt, ap_copy); va_end(ap_copy); } } @@ -342,8 +332,8 @@ int mg_vasprintf(char **buf, size_t size, const char *fmt, va_list ap) { return len; } -int mg_asprintf(char **buf, size_t size, const char *fmt, ...) { - int ret; +size_t mg_asprintf(char **buf, size_t size, const char *fmt, ...) { + size_t ret; va_list ap; va_start(ap, fmt); ret = mg_vasprintf(buf, size, fmt, ap); diff --git a/src/str.h b/src/str.h index 57018c33..53348b31 100644 --- a/src/str.h +++ b/src/str.h @@ -32,12 +32,12 @@ bool mg_globmatch(const char *pattern, size_t plen, const char *s, size_t n); bool mg_commalist(struct mg_str *s, struct mg_str *k, struct mg_str *v); bool mg_commalist(struct mg_str *s, struct mg_str *k, struct mg_str *v); size_t mg_vsnprintf(char *buf, size_t len, const char *fmt, va_list ap); -size_t mg_snprintf(char *buf, size_t len, const char *fmt, ...); +size_t mg_snprintf(char *, size_t, const char *fmt, ...) PRINTF_LIKE(3, 4); char *mg_hexdump(const void *buf, size_t len); char *mg_hex(const void *buf, size_t len, char *dst); void mg_unhex(const char *buf, size_t len, unsigned char *to); unsigned long mg_unhexn(const char *s, size_t len); -int mg_asprintf(char **buf, size_t size, const char *fmt, ...); -int mg_vasprintf(char **buf, size_t size, const char *fmt, va_list ap); +size_t mg_asprintf(char **, size_t, const char *fmt, ...) PRINTF_LIKE(3, 4); +size_t mg_vasprintf(char **buf, size_t size, const char *fmt, va_list ap); int mg_check_ip_acl(struct mg_str acl, uint32_t remote_ip); int64_t mg_to64(struct mg_str str); diff --git a/src/ws.c b/src/ws.c index 1f0c01e8..b9386694 100644 --- a/src/ws.c +++ b/src/ws.c @@ -216,7 +216,7 @@ struct mg_connection *mg_ws_connect(struct mg_mgr *mgr, const char *url, if (c != NULL) { char nonce[16], key[30], mem1[128], mem2[256], *buf1 = mem1, *buf2 = mem2; struct mg_str host = mg_url_host(url); - int n1 = 0, n2 = 0; + size_t n1 = 0, n2 = 0; if (fmt != NULL) { va_list ap; va_start(ap, fmt); @@ -238,7 +238,7 @@ struct mg_connection *mg_ws_connect(struct mg_mgr *mgr, const char *url, "Sec-WebSocket-Key: %s\r\n" "\r\n", mg_url_uri(url), (int) host.len, host.ptr, n1, buf1, key); - mg_send(c, buf2, n2 > 0 ? (size_t) n2 : 0); + mg_send(c, buf2, n2 > 0 ? n2 : 0); if (buf1 != mem1) free(buf1); if (buf2 != mem2) free(buf2); c->pfn = mg_ws_cb; diff --git a/test/unit_test.c b/test/unit_test.c index fc7c4200..9f566933 100644 --- a/test/unit_test.c +++ b/test/unit_test.c @@ -1194,6 +1194,7 @@ static void test_timer(void) { static void test_str(void) { char buf[100]; + size_t n = sizeof(buf); struct mg_str s = mg_strdup(mg_str("a")); ASSERT(mg_strcmp(s, mg_str("a")) == 0); free((void *) s.ptr); @@ -1204,37 +1205,47 @@ static void test_str(void) { ASSERT(mg_strstr(mg_str("abc"), mg_str("b")) != NULL); ASSERT(mg_strcmp(mg_str("hi"), mg_strstrip(mg_str(" \thi\r\n"))) == 0); - ASSERT(mg_snprintf(buf, sizeof(buf), "%d", 0) == 1 && !strcmp(buf, "0")); - ASSERT(mg_snprintf(buf, sizeof(buf), "%d", 1) == 1 && !strcmp(buf, "1")); - ASSERT(mg_snprintf(buf, sizeof(buf), "%d", -1) == 2 && !strcmp(buf, "-1")); - ASSERT(mg_snprintf(buf, sizeof(buf), "%.*s", 1, "ab") == 1 && - !strcmp(buf, "a")); - ASSERT(mg_snprintf(buf, sizeof(buf), "%.1s", "ab") == 1 && !strcmp(buf, "a")); - ASSERT(mg_snprintf(buf, sizeof(buf), "%.99s", "a") == 1 && !strcmp(buf, "a")); - ASSERT(mg_snprintf(buf, sizeof(buf), "%11s", "a") == 11 && - !strcmp(buf, " a")); - ASSERT(mg_snprintf(buf, sizeof(buf), "%s", "a\0b") == 1 && !strcmp(buf, "a")); - ASSERT(mg_snprintf(buf, sizeof(buf), "%2s", "a") == 2 && !strcmp(buf, " a")); - ASSERT(mg_snprintf(buf, sizeof(buf), "%.*s", 3, "a\0b") == 1 && - !strcmp(buf, "a")); - memset(buf, 'x', sizeof(buf)); - ASSERT(mg_snprintf(buf, sizeof(buf), "%d", 7) == 1 && !strcmp(buf, "7")); - memset(buf, 0, sizeof(buf)); + ASSERT(mg_snprintf(buf, n, "%d", 0) == 1 && !strcmp(buf, "0")); + ASSERT(mg_snprintf(buf, n, "%d", 1) == 1 && !strcmp(buf, "1")); + ASSERT(mg_snprintf(buf, n, "%d", -1) == 2 && !strcmp(buf, "-1")); + ASSERT(mg_snprintf(buf, n, "%.*s", 1, "ab") == 1 && !strcmp(buf, "a")); + ASSERT(mg_snprintf(buf, n, "%.1s", "ab") == 1 && !strcmp(buf, "a")); + ASSERT(mg_snprintf(buf, n, "%.99s", "a") == 1 && !strcmp(buf, "a")); + ASSERT(mg_snprintf(buf, n, "%11s", "a") == 11 && !strcmp(buf, " a")); + ASSERT(mg_snprintf(buf, n, "%s", "a\0b") == 1 && !strcmp(buf, "a")); + ASSERT(mg_snprintf(buf, n, "%2s", "a") == 2 && !strcmp(buf, " a")); + ASSERT(mg_snprintf(buf, n, "%.*s", 3, "a\0b") == 1 && !strcmp(buf, "a")); + memset(buf, 'x', n); + ASSERT(mg_snprintf(buf, n, "%d", 7) == 1 && !strcmp(buf, "7")); + memset(buf, 0, n); ASSERT(mg_snprintf(buf, 0, "%d", 123) == 3 && buf[0] == '\0'); - ASSERT(mg_snprintf(buf, sizeof(buf), "%lld", (uint64_t) 0xffffffffff) == 13 && + ASSERT(mg_snprintf(buf, n, "%lld", (uint64_t) 0xffffffffff) == 13 && !strcmp(buf, "1099511627775")); - ASSERT(mg_snprintf(buf, sizeof(buf), "%llx", (uint64_t) 0xffffffffff) == 10 && + ASSERT(mg_snprintf(buf, n, "%llx", (uint64_t) 0xffffffffff) == 10 && !strcmp(buf, "ffffffffff")); - ASSERT(mg_snprintf(buf, sizeof(buf), "%lx", (unsigned long) 0x6204d754) == - 8 && + ASSERT(mg_snprintf(buf, n, "%lx", (unsigned long) 0x6204d754) == 8 && !strcmp(buf, "6204d754")); - ASSERT(mg_snprintf(buf, sizeof(buf), "ab") == 2 && !strcmp(buf, "ab")); - ASSERT(mg_snprintf(buf, sizeof(buf), "%dx", 1) == 2 && !strcmp(buf, "1x")); - ASSERT(mg_snprintf(buf, sizeof(buf), "%sx", "a") == 2 && !strcmp(buf, "ax")); - ASSERT(mg_snprintf(buf, sizeof(buf), "%cx", 32) == 2 && !strcmp(buf, " x")); - ASSERT(mg_snprintf(buf, sizeof(buf), "%x", 15) == 1 && !strcmp(buf, "f")); - ASSERT(mg_snprintf(buf, sizeof(buf), "%2x", 15) == 2 && !strcmp(buf, " f")); - ASSERT(mg_snprintf(buf, sizeof(buf), "%02x", 15) == 2 && !strcmp(buf, "0f")); + ASSERT(mg_snprintf(buf, n, "ab") == 2 && !strcmp(buf, "ab")); + ASSERT(mg_snprintf(buf, n, "%dx", 1) == 2 && !strcmp(buf, "1x")); + ASSERT(mg_snprintf(buf, n, "%sx", "a") == 2 && !strcmp(buf, "ax")); + ASSERT(mg_snprintf(buf, n, "%cx", 32) == 2 && !strcmp(buf, " x")); + ASSERT(mg_snprintf(buf, n, "%x", 15) == 1 && !strcmp(buf, "f")); + ASSERT(mg_snprintf(buf, n, "%2x", 15) == 2 && !strcmp(buf, " f")); + ASSERT(mg_snprintf(buf, n, "%02x", 15) == 2 && !strcmp(buf, "0f")); + ASSERT(mg_snprintf(buf, n, "%p", (void *) (size_t) 7) == 3 && + !strcmp(buf, "0x7")); + ASSERT(mg_snprintf(buf, n, "%hx:%hhx", (short) 1, (char) 2) == 3 && + !strcmp(buf, "1:2")); + ASSERT(mg_snprintf(buf, n, "%hx:%hhx", (short) 1, (char) 2) == 3 && + !strcmp(buf, "1:2")); + ASSERT(mg_snprintf(buf, n, "%%") == 1 && !strcmp(buf, "%")); + ASSERT(mg_snprintf(buf, 10, "%s %s", "a", "b") == 3 && !strcmp(buf, "a b")); + ASSERT(mg_snprintf(buf, 10, "%s %s", "a", "b") == 3 && !strcmp(buf, "a b")); + buf[0] = '\0'; + ASSERT(mg_snprintf(0, 0, "ab%dc", 123) == 6 && buf[0] == '\0'); + ASSERT(mg_snprintf(0, 0, "%s ", "a") == 2 && buf[0] == '\0'); + ASSERT(mg_snprintf(0, 0, "%s %s", "a", "b") == 3 && buf[0] == '\0'); + ASSERT(mg_snprintf(0, 0, "%2s %s", "a", "b") == 4 && buf[0] == '\0'); } static void fn1(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { @@ -1371,9 +1382,9 @@ static void test_util(void) { { s = buf; - mg_asprintf(&s, sizeof(buf), "%s", "%3d", 123); + mg_asprintf(&s, sizeof(buf), "%3d", 123); ASSERT(s == buf); - ASSERT(strcmp(buf, "%3d") == 0); + ASSERT(strcmp(buf, "123") == 0); mg_asprintf(&s, sizeof(buf), "%.*s", 7, "a%40b.c"); ASSERT(s == buf); ASSERT(strcmp(buf, "a%40b.c") == 0); @@ -1729,7 +1740,10 @@ static void test_get_header_var(void) { } int main(void) { - mg_log_set("3"); + const char *debug_level = getenv("V"); + if (debug_level == NULL) debug_level = "3"; + mg_log_set(debug_level); + test_str(); test_globmatch(); test_get_header_var();
Name" "ModifiedSize
..[DIR]