mirror of
https://github.com/cesanta/mongoose.git
synced 2026-11-10 16:31:44 +00:00
Better unicode handling on win32
This commit is contained in:
1 parent
a2f027e798
commit
fd8db5c53a
6 files changed
+231
-69
No files matched your search
+112
-31
@@ -691,7 +691,7 @@ int mg_http_upload(struct mg_connection *c, struct mg_http_message *hm,
|
|||||||
snprintf(path, sizeof(path), "%s%c%s", dir, MG_DIRSEP, name);
|
snprintf(path, sizeof(path), "%s%c%s", dir, MG_DIRSEP, name);
|
||||||
LOG(LL_DEBUG,
|
LOG(LL_DEBUG,
|
||||||
("%p %d bytes @ %d [%s]", c->fd, (int) hm->body.len, (int) oft, name));
|
("%p %d bytes @ %d [%s]", c->fd, (int) hm->body.len, (int) oft, name));
|
||||||
if ((fp = fopen(path, oft == 0 ? "wb" : "ab")) == NULL) {
|
if ((fp = mg_fopen(path, oft == 0 ? "wb" : "ab")) == NULL) {
|
||||||
mg_http_reply(c, 400, "", "fopen(%s): %d", name, errno);
|
mg_http_reply(c, 400, "", "fopen(%s): %d", name, errno);
|
||||||
return -2;
|
return -2;
|
||||||
} else {
|
} else {
|
||||||
@@ -729,9 +729,9 @@ static const char *guess_content_type(const char *filename) {
|
|||||||
size_t ext_len;
|
size_t ext_len;
|
||||||
const char *type;
|
const char *type;
|
||||||
} * t, types[] = {
|
} * t, types[] = {
|
||||||
MIME_ENTRY("html", "text/html"),
|
MIME_ENTRY("html", "text/html; charset=utf-8"),
|
||||||
MIME_ENTRY("htm", "text/html"),
|
MIME_ENTRY("htm", "text/html; charset=utf-8"),
|
||||||
MIME_ENTRY("shtml", "text/html"),
|
MIME_ENTRY("shtml", "text/html; charset=utf-8"),
|
||||||
MIME_ENTRY("css", "text/css"),
|
MIME_ENTRY("css", "text/css"),
|
||||||
MIME_ENTRY("js", "text/javascript"),
|
MIME_ENTRY("js", "text/javascript"),
|
||||||
MIME_ENTRY("mjs", "text/javascript"),
|
MIME_ENTRY("mjs", "text/javascript"),
|
||||||
@@ -742,7 +742,7 @@ static const char *guess_content_type(const char *filename) {
|
|||||||
MIME_ENTRY("jpeg", "image/jpeg"),
|
MIME_ENTRY("jpeg", "image/jpeg"),
|
||||||
MIME_ENTRY("png", "image/png"),
|
MIME_ENTRY("png", "image/png"),
|
||||||
MIME_ENTRY("svg", "image/svg+xml"),
|
MIME_ENTRY("svg", "image/svg+xml"),
|
||||||
MIME_ENTRY("txt", "text/plain"),
|
MIME_ENTRY("txt", "text/plain; charset=utf-8"),
|
||||||
MIME_ENTRY("wav", "audio/wav"),
|
MIME_ENTRY("wav", "audio/wav"),
|
||||||
MIME_ENTRY("mp3", "audio/mpeg"),
|
MIME_ENTRY("mp3", "audio/mpeg"),
|
||||||
MIME_ENTRY("mid", "audio/mid"),
|
MIME_ENTRY("mid", "audio/mid"),
|
||||||
@@ -779,18 +779,21 @@ static const char *guess_content_type(const char *filename) {
|
|||||||
if (mg_ncasecmp(t->ext, &filename[n - t->ext_len], t->ext_len)) continue;
|
if (mg_ncasecmp(t->ext, &filename[n - t->ext_len], t->ext_len)) continue;
|
||||||
return t->type;
|
return t->type;
|
||||||
}
|
}
|
||||||
return "text/plain";
|
return "text/plain; charset=utf-8";
|
||||||
}
|
}
|
||||||
|
|
||||||
void mg_http_serve_file(struct mg_connection *c, struct mg_http_message *hm,
|
void mg_http_serve_file(struct mg_connection *c, struct mg_http_message *hm,
|
||||||
const char *path, const char *mime, const char *hdrs) {
|
const char *path, const char *mime, const char *hdrs) {
|
||||||
struct mg_str *inm = mg_http_get_header(hm, "If-None-Match");
|
struct mg_str *inm = mg_http_get_header(hm, "If-None-Match");
|
||||||
mg_stat_t st;
|
mg_stat_t st;
|
||||||
FILE *fp = fopen(path, "rb");
|
|
||||||
char etag[64];
|
char etag[64];
|
||||||
|
FILE *fp = mg_fopen(path, "rb");
|
||||||
if (fp == NULL || mg_stat(path, &st) != 0 ||
|
if (fp == NULL || mg_stat(path, &st) != 0 ||
|
||||||
mg_http_etag(etag, sizeof(etag), &st) != etag) {
|
mg_http_etag(etag, sizeof(etag), &st) != etag) {
|
||||||
|
LOG(LL_DEBUG,
|
||||||
|
("404 [%.*s] [%s] %p", (int) hm->uri.len, hm->uri.ptr, path, fp));
|
||||||
mg_http_reply(c, 404, "", "%s", "Not found\n");
|
mg_http_reply(c, 404, "", "%s", "Not found\n");
|
||||||
|
if (fp != NULL) fclose(fp);
|
||||||
} else if (inm != NULL && mg_vcasecmp(inm, etag) == 0) {
|
} else if (inm != NULL && mg_vcasecmp(inm, etag) == 0) {
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
mg_printf(c, "HTTP/1.1 304 Not Modified\r\nContent-Length: 0\r\n\r\n");
|
mg_printf(c, "HTTP/1.1 304 Not Modified\r\nContent-Length: 0\r\n\r\n");
|
||||||
@@ -950,12 +953,34 @@ struct dirent *readdir(DIR *d) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void printdirentry(struct mg_connection *c, struct mg_http_message *hm,
|
static bool mg_is_safe(int c) {
|
||||||
const char *name, mg_stat_t *stp) {
|
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') ||
|
||||||
char size[64], mod[64];
|
(c >= 'A' && c <= 'Z') || strchr("._-$,;~()/", c) != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mg_url_encode(const char *s, char *buf, size_t len) {
|
||||||
|
int i, n = 0;
|
||||||
|
for (i = 0; s[i] != '\0'; i++) {
|
||||||
|
int c = *((const unsigned char *) (s + i));
|
||||||
|
if ((size_t) n + 4 >= len) return 0;
|
||||||
|
if (mg_is_safe(c)) {
|
||||||
|
buf[n++] = s[i];
|
||||||
|
} else {
|
||||||
|
buf[n++] = '%';
|
||||||
|
mg_hex(&s[i], 1, &buf[n]);
|
||||||
|
n += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void printdirentry(struct mg_connection *c, const char *name,
|
||||||
|
mg_stat_t *stp) {
|
||||||
|
char size[64], mod[64], path[MG_PATH_MAX];
|
||||||
int is_dir = S_ISDIR(stp->st_mode);
|
int is_dir = S_ISDIR(stp->st_mode);
|
||||||
const char *slash = is_dir ? "/" : "";
|
const char *slash = is_dir ? "/" : "";
|
||||||
const char *es = hm->uri.ptr[hm->uri.len - 1] != '/' ? "/" : "";
|
// const char *es = hm->uri.ptr[hm->uri.len - 1] != '/' ? "/" : "";
|
||||||
|
int n = 0;
|
||||||
|
|
||||||
if (is_dir) {
|
if (is_dir) {
|
||||||
snprintf(size, sizeof(size), "%s", "[DIR]");
|
snprintf(size, sizeof(size), "%s", "[DIR]");
|
||||||
@@ -971,11 +996,11 @@ static void printdirentry(struct mg_connection *c, struct mg_http_message *hm,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M", localtime(&stp->st_mtime));
|
strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M", localtime(&stp->st_mtime));
|
||||||
|
n = mg_url_encode(name, path, sizeof(path));
|
||||||
mg_printf(c,
|
mg_printf(c,
|
||||||
" <tr><td><a href=\"%.*s%s%s%s\">%s%s</a></td>"
|
" <tr><td><a href=\"%.*s%s\">%s%s</a></td>"
|
||||||
"<td>%s</td><td>%s</td></tr>\n",
|
"<td>%s</td><td>%s</td></tr>\n",
|
||||||
(int) hm->uri.len, hm->uri.ptr, es, name, slash, name, slash, mod,
|
n, path, slash, name, slash, mod, size);
|
||||||
size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void listdir(struct mg_connection *c, struct mg_http_message *hm,
|
static void listdir(struct mg_connection *c, struct mg_http_message *hm,
|
||||||
@@ -983,20 +1008,52 @@ static void listdir(struct mg_connection *c, struct mg_http_message *hm,
|
|||||||
char path[MG_PATH_MAX], *p = &dir[strlen(dir) - 1], tmp[10];
|
char path[MG_PATH_MAX], *p = &dir[strlen(dir) - 1], tmp[10];
|
||||||
struct dirent *dp;
|
struct dirent *dp;
|
||||||
DIR *dirp;
|
DIR *dirp;
|
||||||
|
static const char *sort_js_code =
|
||||||
|
"<script>function srt(tb, sc, so, d) {"
|
||||||
|
"var tr = Array.prototype.slice.call(tb.rows, 0),"
|
||||||
|
"tr = tr.sort(function (a, b) { var c1 = a.cells[sc], c2 = b.cells[sc],"
|
||||||
|
"n1 = c1.getAttribute('name'), n2 = c2.getAttribute('name'), "
|
||||||
|
"t1 = a.cells[2].getAttribute('name'), "
|
||||||
|
"t2 = b.cells[2].getAttribute('name'); "
|
||||||
|
"return so * (t1 < 0 && t2 >= 0 ? -1 : t2 < 0 && t1 >= 0 ? 1 : "
|
||||||
|
"n1 ? parseInt(n2) - parseInt(n1) : "
|
||||||
|
"c1.textContent.trim().localeCompare(c2.textContent.trim())); });";
|
||||||
|
static const char *sort_js_code2 =
|
||||||
|
"for (var i = 0; i < tr.length; i++) tb.appendChild(tr[i]); "
|
||||||
|
"if (!d) window.location.hash = ('sc=' + sc + '&so=' + so); "
|
||||||
|
"};"
|
||||||
|
"window.onload = function() {"
|
||||||
|
"var tb = document.getElementById('tb');"
|
||||||
|
"var m = /sc=([012]).so=(1|-1)/.exec(window.location.hash) || [0, 2, 1];"
|
||||||
|
"var sc = m[1], so = m[2]; document.onclick = function(ev) { "
|
||||||
|
"var c = ev.target.rel; if (c) {if (c == sc) so *= -1; srt(tb, c, so); "
|
||||||
|
"sc = c; ev.preventDefault();}};"
|
||||||
|
"srt(tb, sc, so, true);"
|
||||||
|
"}"
|
||||||
|
"</script>";
|
||||||
|
|
||||||
while (p > dir && *p != '/') *p-- = '\0';
|
while (p > dir && *p != '/') *p-- = '\0';
|
||||||
if ((dirp = (opendir(dir))) != NULL) {
|
if ((dirp = (opendir(dir))) != NULL) {
|
||||||
size_t off, n;
|
size_t off, n;
|
||||||
mg_printf(c, "%s\r\n", "HTTP/1.1 200 OK\r\nContent-Length: \r\n");
|
mg_printf(c, "%s\r\n",
|
||||||
|
"HTTP/1.1 200 OK\r\n"
|
||||||
|
"Content-Type: text/html; charset=utf-8\r\n"
|
||||||
|
"Content-Length: \r\n");
|
||||||
off = c->send.len; // Start of body
|
off = c->send.len; // Start of body
|
||||||
mg_printf(c,
|
mg_printf(c,
|
||||||
"<!DOCTYPE html><html><head><title>Index of %.*s</title>"
|
"<!DOCTYPE html><html><head><title>Index of %.*s</title>%s%s"
|
||||||
"<style>th,td {text-align: left; padding-right: 1em; "
|
"<style>th,td {text-align: left; padding-right: 1em; "
|
||||||
"font-family: monospace; }</style></head>"
|
"font-family: monospace; }</style></head>"
|
||||||
"<body><h1>Index of %.*s</h1><table cellpadding=\"0\"><thead>"
|
"<body><h1>Index of %.*s</h1><table cellpadding=\"0\"><thead>"
|
||||||
"<tr><th>Name</th><th>Modified</th><th>Size</th></tr>"
|
"<tr><th><a href=\"#\" rel=\"0\">Name</a></th><th>"
|
||||||
"<tr><td colspan=\"3\"><hr></td></tr></thead><tbody>\n",
|
"<a href=\"#\" rel=\"1\">Modified</a></th>"
|
||||||
(int) hm->uri.len, hm->uri.ptr, (int) hm->uri.len, hm->uri.ptr);
|
"<th><a href=\"#\" rel=\"2\">Size</a></th></tr>"
|
||||||
|
"<tr><td colspan=\"3\"><hr></td></tr>"
|
||||||
|
"</thead>"
|
||||||
|
"<tbody id=\"tb\">\n",
|
||||||
|
(int) hm->uri.len, hm->uri.ptr, sort_js_code, sort_js_code2,
|
||||||
|
(int) hm->uri.len, hm->uri.ptr);
|
||||||
|
|
||||||
while ((dp = readdir(dirp)) != NULL) {
|
while ((dp = readdir(dirp)) != NULL) {
|
||||||
mg_stat_t st;
|
mg_stat_t st;
|
||||||
const char *sep = dp->d_name[0] == MG_DIRSEP ? "/" : "";
|
const char *sep = dp->d_name[0] == MG_DIRSEP ? "/" : "";
|
||||||
@@ -1008,7 +1065,7 @@ static void listdir(struct mg_connection *c, struct mg_http_message *hm,
|
|||||||
} else if (mg_stat(path, &st) != 0) {
|
} else if (mg_stat(path, &st) != 0) {
|
||||||
LOG(LL_ERROR, ("%lu stat(%s): %d", c->id, path, errno));
|
LOG(LL_ERROR, ("%lu stat(%s): %d", c->id, path, errno));
|
||||||
} else {
|
} else {
|
||||||
printdirentry(c, hm, dp->d_name, &st);
|
printdirentry(c, dp->d_name, &st);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
closedir(dirp);
|
closedir(dirp);
|
||||||
@@ -1042,6 +1099,7 @@ void mg_http_serve_dir(struct mg_connection *c, struct mg_http_message *hm,
|
|||||||
size_t n1 = strlen(t1), n2;
|
size_t n1 = strlen(t1), n2;
|
||||||
|
|
||||||
mg_url_decode(hm->uri.ptr, hm->uri.len, t1 + n1, sizeof(t1) - n1, 0);
|
mg_url_decode(hm->uri.ptr, hm->uri.len, t1 + n1, sizeof(t1) - n1, 0);
|
||||||
|
LOG(LL_DEBUG, ("SERVE: [%s]", t1 + n1));
|
||||||
t1[sizeof(t1) - 1] = '\0';
|
t1[sizeof(t1) - 1] = '\0';
|
||||||
n2 = strlen(t1);
|
n2 = strlen(t1);
|
||||||
while (n2 > 0 && t1[n2 - 1] == '/') t1[--n2] = 0;
|
while (n2 > 0 && t1[n2 - 1] == '/') t1[--n2] = 0;
|
||||||
@@ -1057,16 +1115,17 @@ void mg_http_serve_dir(struct mg_connection *c, struct mg_http_message *hm,
|
|||||||
|
|
||||||
if (strlen(t2) < n1 || memcmp(t1, t2, n1) != 0) {
|
if (strlen(t2) < n1 || memcmp(t1, t2, n1) != 0) {
|
||||||
// Requested file is located outside root directory, fail
|
// Requested file is located outside root directory, fail
|
||||||
mg_http_reply(c, 404, "", "Not found %.*s\n", hm->uri.len, hm->uri.ptr);
|
mg_http_reply(c, 404, "", "Not found %.*s\n", (int) hm->uri.len,
|
||||||
|
hm->uri.ptr);
|
||||||
} else {
|
} else {
|
||||||
FILE *fp = fopen(t2, "r");
|
FILE *fp = mg_fopen(t2, "r");
|
||||||
#if MG_ENABLE_SSI
|
#if MG_ENABLE_SSI
|
||||||
if (is_index && fp == NULL) {
|
if (is_index && fp == NULL) {
|
||||||
char *p = t2 + strlen(t2);
|
char *p = t2 + strlen(t2);
|
||||||
while (p > t2 && p[-1] != '/') p--;
|
while (p > t2 && p[-1] != '/') p--;
|
||||||
strncpy(p, "index.shtml", &t2[sizeof(t2)] - p - 2);
|
strncpy(p, "index.shtml", &t2[sizeof(t2)] - p - 2);
|
||||||
t2[sizeof(t2) - 1] = '\0';
|
t2[sizeof(t2) - 1] = '\0';
|
||||||
fp = fopen(t2, "r");
|
fp = mg_fopen(t2, "r");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#if MG_ENABLE_HTTP_DEBUG_ENDPOINT
|
#if MG_ENABLE_HTTP_DEBUG_ENDPOINT
|
||||||
@@ -3054,6 +3113,7 @@ void mg_mgr_poll(struct mg_mgr *mgr, int ms) {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef MG_MAX_SSI_DEPTH
|
#ifndef MG_MAX_SSI_DEPTH
|
||||||
#define MG_MAX_SSI_DEPTH 5
|
#define MG_MAX_SSI_DEPTH 5
|
||||||
#endif
|
#endif
|
||||||
@@ -3061,7 +3121,7 @@ void mg_mgr_poll(struct mg_mgr *mgr, int ms) {
|
|||||||
#if MG_ENABLE_SSI
|
#if MG_ENABLE_SSI
|
||||||
static char *mg_ssi(const char *path, const char *root, int depth) {
|
static char *mg_ssi(const char *path, const char *root, int depth) {
|
||||||
struct mg_iobuf b = {NULL, 0, 0};
|
struct mg_iobuf b = {NULL, 0, 0};
|
||||||
FILE *fp = fopen(path, "rb");
|
FILE *fp = mg_fopen(path, "rb");
|
||||||
if (fp != NULL) {
|
if (fp != NULL) {
|
||||||
char buf[BUFSIZ], arg[sizeof(buf)];
|
char buf[BUFSIZ], arg[sizeof(buf)];
|
||||||
int ch, intag = 0;
|
int ch, intag = 0;
|
||||||
@@ -3735,21 +3795,42 @@ struct mg_str mg_url_pass(const char *url) {
|
|||||||
|
|
||||||
|
|
||||||
#if MG_ENABLE_FS
|
#if MG_ENABLE_FS
|
||||||
size_t mg_file_size(const char *path) {
|
int mg_stat(const char *path, mg_stat_t *st) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
wchar_t tmp[MG_PATH_MAX];
|
||||||
|
MultiByteToWideChar(CP_UTF8, 0, path, -1, tmp, sizeof(tmp) / sizeof(tmp[0]));
|
||||||
|
return _wstati64(tmp, st);
|
||||||
|
#else
|
||||||
|
return stat(path, st);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *mg_fopen(const char *path, const char *mode) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
wchar_t b1[MG_PATH_MAX], b2[10];
|
||||||
|
MultiByteToWideChar(CP_UTF8, 0, path, -1, b1, sizeof(b1) / sizeof(b1[0]));
|
||||||
|
MultiByteToWideChar(CP_UTF8, 0, mode, -1, b2, sizeof(b2) / sizeof(b2[0]));
|
||||||
|
return _wfopen(b1, b2);
|
||||||
|
#else
|
||||||
|
return fopen(path, mode);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t mg_file_size(const char *path) {
|
||||||
#if MG_ARCH == MG_ARCH_FREERTOS
|
#if MG_ARCH == MG_ARCH_FREERTOS
|
||||||
struct FF_STAT st;
|
struct FF_STAT st;
|
||||||
return ff_stat(path, &st) == 0 ? st.st_size : 0;
|
return ff_stat(path, &st) == 0 ? st.st_size : 0;
|
||||||
#else
|
#else
|
||||||
struct stat st;
|
mg_stat_t st;
|
||||||
return stat(path, &st) == 0 ? st.st_size : 0;
|
return mg_stat(path, &st) == 0 ? st.st_size : 0;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
char *mg_file_read(const char *path) {
|
char *mg_file_read(const char *path) {
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
char *data = NULL;
|
char *data = NULL;
|
||||||
size_t size = mg_file_size(path);
|
size_t size = (size_t) mg_file_size(path);
|
||||||
if ((fp = fopen(path, "rb")) != NULL) {
|
if ((fp = mg_fopen(path, "rb")) != NULL) {
|
||||||
data = (char *) malloc(size + 1);
|
data = (char *) malloc(size + 1);
|
||||||
if (data != NULL) {
|
if (data != NULL) {
|
||||||
if (fread(data, 1, size, fp) != size) {
|
if (fread(data, 1, size, fp) != size) {
|
||||||
@@ -3769,7 +3850,7 @@ bool mg_file_write(const char *path, const void *buf, size_t len) {
|
|||||||
FILE *fp;
|
FILE *fp;
|
||||||
char tmp[MG_PATH_MAX];
|
char tmp[MG_PATH_MAX];
|
||||||
snprintf(tmp, sizeof(tmp), "%s.%d", path, rand());
|
snprintf(tmp, sizeof(tmp), "%s.%d", path, rand());
|
||||||
fp = fopen(tmp, "wb");
|
fp = mg_fopen(tmp, "wb");
|
||||||
if (fp != NULL) {
|
if (fp != NULL) {
|
||||||
result = fwrite(buf, 1, len, fp) == len;
|
result = fwrite(buf, 1, len, fp) == len;
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
@@ -3797,7 +3878,7 @@ bool mg_file_printf(const char *path, const char *fmt, ...) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void mg_random(void *buf, size_t len) {
|
void mg_random(void *buf, size_t len) {
|
||||||
FILE *fp = fopen("/dev/urandom", "rb");
|
FILE *fp = mg_fopen("/dev/urandom", "rb");
|
||||||
size_t i, n = 0;
|
size_t i, n = 0;
|
||||||
if (fp != NULL) n = fread(buf, 1, len, fp);
|
if (fp != NULL) n = fread(buf, 1, len, fp);
|
||||||
if (fp == NULL || n <= 0) {
|
if (fp == NULL || n <= 0) {
|
||||||
|
|||||||
+3
-3
@@ -428,7 +428,7 @@ void mg_timer_poll(unsigned long uptime_ms);
|
|||||||
|
|
||||||
|
|
||||||
char *mg_file_read(const char *path);
|
char *mg_file_read(const char *path);
|
||||||
size_t mg_file_size(const char *path);
|
int64_t mg_file_size(const char *path);
|
||||||
bool mg_file_write(const char *path, const void *buf, size_t len);
|
bool mg_file_write(const char *path, const void *buf, size_t len);
|
||||||
bool mg_file_printf(const char *path, const char *fmt, ...);
|
bool mg_file_printf(const char *path, const char *fmt, ...);
|
||||||
void mg_random(void *buf, size_t len);
|
void mg_random(void *buf, size_t len);
|
||||||
@@ -450,11 +450,11 @@ void mg_usleep(unsigned long usecs);
|
|||||||
#if MG_ENABLE_FS
|
#if MG_ENABLE_FS
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
typedef struct _stati64 mg_stat_t;
|
typedef struct _stati64 mg_stat_t;
|
||||||
#define mg_stat(a, b) _stati64((a), (b))
|
|
||||||
#else
|
#else
|
||||||
typedef struct stat mg_stat_t;
|
typedef struct stat mg_stat_t;
|
||||||
#define mg_stat(a, b) stat((a), (b))
|
|
||||||
#endif
|
#endif
|
||||||
|
int mg_stat(const char *path, mg_stat_t *);
|
||||||
|
FILE *mg_fopen(const char *fp, const char *mode);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define mg_htons(x) mg_ntohs(x)
|
#define mg_htons(x) mg_ntohs(x)
|
||||||
|
|||||||
+82
-23
@@ -271,7 +271,7 @@ int mg_http_upload(struct mg_connection *c, struct mg_http_message *hm,
|
|||||||
snprintf(path, sizeof(path), "%s%c%s", dir, MG_DIRSEP, name);
|
snprintf(path, sizeof(path), "%s%c%s", dir, MG_DIRSEP, name);
|
||||||
LOG(LL_DEBUG,
|
LOG(LL_DEBUG,
|
||||||
("%p %d bytes @ %d [%s]", c->fd, (int) hm->body.len, (int) oft, name));
|
("%p %d bytes @ %d [%s]", c->fd, (int) hm->body.len, (int) oft, name));
|
||||||
if ((fp = fopen(path, oft == 0 ? "wb" : "ab")) == NULL) {
|
if ((fp = mg_fopen(path, oft == 0 ? "wb" : "ab")) == NULL) {
|
||||||
mg_http_reply(c, 400, "", "fopen(%s): %d", name, errno);
|
mg_http_reply(c, 400, "", "fopen(%s): %d", name, errno);
|
||||||
return -2;
|
return -2;
|
||||||
} else {
|
} else {
|
||||||
@@ -309,9 +309,9 @@ static const char *guess_content_type(const char *filename) {
|
|||||||
size_t ext_len;
|
size_t ext_len;
|
||||||
const char *type;
|
const char *type;
|
||||||
} * t, types[] = {
|
} * t, types[] = {
|
||||||
MIME_ENTRY("html", "text/html"),
|
MIME_ENTRY("html", "text/html; charset=utf-8"),
|
||||||
MIME_ENTRY("htm", "text/html"),
|
MIME_ENTRY("htm", "text/html; charset=utf-8"),
|
||||||
MIME_ENTRY("shtml", "text/html"),
|
MIME_ENTRY("shtml", "text/html; charset=utf-8"),
|
||||||
MIME_ENTRY("css", "text/css"),
|
MIME_ENTRY("css", "text/css"),
|
||||||
MIME_ENTRY("js", "text/javascript"),
|
MIME_ENTRY("js", "text/javascript"),
|
||||||
MIME_ENTRY("mjs", "text/javascript"),
|
MIME_ENTRY("mjs", "text/javascript"),
|
||||||
@@ -322,7 +322,7 @@ static const char *guess_content_type(const char *filename) {
|
|||||||
MIME_ENTRY("jpeg", "image/jpeg"),
|
MIME_ENTRY("jpeg", "image/jpeg"),
|
||||||
MIME_ENTRY("png", "image/png"),
|
MIME_ENTRY("png", "image/png"),
|
||||||
MIME_ENTRY("svg", "image/svg+xml"),
|
MIME_ENTRY("svg", "image/svg+xml"),
|
||||||
MIME_ENTRY("txt", "text/plain"),
|
MIME_ENTRY("txt", "text/plain; charset=utf-8"),
|
||||||
MIME_ENTRY("wav", "audio/wav"),
|
MIME_ENTRY("wav", "audio/wav"),
|
||||||
MIME_ENTRY("mp3", "audio/mpeg"),
|
MIME_ENTRY("mp3", "audio/mpeg"),
|
||||||
MIME_ENTRY("mid", "audio/mid"),
|
MIME_ENTRY("mid", "audio/mid"),
|
||||||
@@ -359,18 +359,21 @@ static const char *guess_content_type(const char *filename) {
|
|||||||
if (mg_ncasecmp(t->ext, &filename[n - t->ext_len], t->ext_len)) continue;
|
if (mg_ncasecmp(t->ext, &filename[n - t->ext_len], t->ext_len)) continue;
|
||||||
return t->type;
|
return t->type;
|
||||||
}
|
}
|
||||||
return "text/plain";
|
return "text/plain; charset=utf-8";
|
||||||
}
|
}
|
||||||
|
|
||||||
void mg_http_serve_file(struct mg_connection *c, struct mg_http_message *hm,
|
void mg_http_serve_file(struct mg_connection *c, struct mg_http_message *hm,
|
||||||
const char *path, const char *mime, const char *hdrs) {
|
const char *path, const char *mime, const char *hdrs) {
|
||||||
struct mg_str *inm = mg_http_get_header(hm, "If-None-Match");
|
struct mg_str *inm = mg_http_get_header(hm, "If-None-Match");
|
||||||
mg_stat_t st;
|
mg_stat_t st;
|
||||||
FILE *fp = fopen(path, "rb");
|
|
||||||
char etag[64];
|
char etag[64];
|
||||||
|
FILE *fp = mg_fopen(path, "rb");
|
||||||
if (fp == NULL || mg_stat(path, &st) != 0 ||
|
if (fp == NULL || mg_stat(path, &st) != 0 ||
|
||||||
mg_http_etag(etag, sizeof(etag), &st) != etag) {
|
mg_http_etag(etag, sizeof(etag), &st) != etag) {
|
||||||
|
LOG(LL_DEBUG,
|
||||||
|
("404 [%.*s] [%s] %p", (int) hm->uri.len, hm->uri.ptr, path, fp));
|
||||||
mg_http_reply(c, 404, "", "%s", "Not found\n");
|
mg_http_reply(c, 404, "", "%s", "Not found\n");
|
||||||
|
if (fp != NULL) fclose(fp);
|
||||||
} else if (inm != NULL && mg_vcasecmp(inm, etag) == 0) {
|
} else if (inm != NULL && mg_vcasecmp(inm, etag) == 0) {
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
mg_printf(c, "HTTP/1.1 304 Not Modified\r\nContent-Length: 0\r\n\r\n");
|
mg_printf(c, "HTTP/1.1 304 Not Modified\r\nContent-Length: 0\r\n\r\n");
|
||||||
@@ -530,12 +533,34 @@ struct dirent *readdir(DIR *d) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void printdirentry(struct mg_connection *c, struct mg_http_message *hm,
|
static bool mg_is_safe(int c) {
|
||||||
const char *name, mg_stat_t *stp) {
|
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') ||
|
||||||
char size[64], mod[64];
|
(c >= 'A' && c <= 'Z') || strchr("._-$,;~()/", c) != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mg_url_encode(const char *s, char *buf, size_t len) {
|
||||||
|
int i, n = 0;
|
||||||
|
for (i = 0; s[i] != '\0'; i++) {
|
||||||
|
int c = *((const unsigned char *) (s + i));
|
||||||
|
if ((size_t) n + 4 >= len) return 0;
|
||||||
|
if (mg_is_safe(c)) {
|
||||||
|
buf[n++] = s[i];
|
||||||
|
} else {
|
||||||
|
buf[n++] = '%';
|
||||||
|
mg_hex(&s[i], 1, &buf[n]);
|
||||||
|
n += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void printdirentry(struct mg_connection *c, const char *name,
|
||||||
|
mg_stat_t *stp) {
|
||||||
|
char size[64], mod[64], path[MG_PATH_MAX];
|
||||||
int is_dir = S_ISDIR(stp->st_mode);
|
int is_dir = S_ISDIR(stp->st_mode);
|
||||||
const char *slash = is_dir ? "/" : "";
|
const char *slash = is_dir ? "/" : "";
|
||||||
const char *es = hm->uri.ptr[hm->uri.len - 1] != '/' ? "/" : "";
|
// const char *es = hm->uri.ptr[hm->uri.len - 1] != '/' ? "/" : "";
|
||||||
|
int n = 0;
|
||||||
|
|
||||||
if (is_dir) {
|
if (is_dir) {
|
||||||
snprintf(size, sizeof(size), "%s", "[DIR]");
|
snprintf(size, sizeof(size), "%s", "[DIR]");
|
||||||
@@ -551,11 +576,11 @@ static void printdirentry(struct mg_connection *c, struct mg_http_message *hm,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M", localtime(&stp->st_mtime));
|
strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M", localtime(&stp->st_mtime));
|
||||||
|
n = mg_url_encode(name, path, sizeof(path));
|
||||||
mg_printf(c,
|
mg_printf(c,
|
||||||
" <tr><td><a href=\"%.*s%s%s%s\">%s%s</a></td>"
|
" <tr><td><a href=\"%.*s%s\">%s%s</a></td>"
|
||||||
"<td>%s</td><td>%s</td></tr>\n",
|
"<td>%s</td><td>%s</td></tr>\n",
|
||||||
(int) hm->uri.len, hm->uri.ptr, es, name, slash, name, slash, mod,
|
n, path, slash, name, slash, mod, size);
|
||||||
size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void listdir(struct mg_connection *c, struct mg_http_message *hm,
|
static void listdir(struct mg_connection *c, struct mg_http_message *hm,
|
||||||
@@ -563,20 +588,52 @@ static void listdir(struct mg_connection *c, struct mg_http_message *hm,
|
|||||||
char path[MG_PATH_MAX], *p = &dir[strlen(dir) - 1], tmp[10];
|
char path[MG_PATH_MAX], *p = &dir[strlen(dir) - 1], tmp[10];
|
||||||
struct dirent *dp;
|
struct dirent *dp;
|
||||||
DIR *dirp;
|
DIR *dirp;
|
||||||
|
static const char *sort_js_code =
|
||||||
|
"<script>function srt(tb, sc, so, d) {"
|
||||||
|
"var tr = Array.prototype.slice.call(tb.rows, 0),"
|
||||||
|
"tr = tr.sort(function (a, b) { var c1 = a.cells[sc], c2 = b.cells[sc],"
|
||||||
|
"n1 = c1.getAttribute('name'), n2 = c2.getAttribute('name'), "
|
||||||
|
"t1 = a.cells[2].getAttribute('name'), "
|
||||||
|
"t2 = b.cells[2].getAttribute('name'); "
|
||||||
|
"return so * (t1 < 0 && t2 >= 0 ? -1 : t2 < 0 && t1 >= 0 ? 1 : "
|
||||||
|
"n1 ? parseInt(n2) - parseInt(n1) : "
|
||||||
|
"c1.textContent.trim().localeCompare(c2.textContent.trim())); });";
|
||||||
|
static const char *sort_js_code2 =
|
||||||
|
"for (var i = 0; i < tr.length; i++) tb.appendChild(tr[i]); "
|
||||||
|
"if (!d) window.location.hash = ('sc=' + sc + '&so=' + so); "
|
||||||
|
"};"
|
||||||
|
"window.onload = function() {"
|
||||||
|
"var tb = document.getElementById('tb');"
|
||||||
|
"var m = /sc=([012]).so=(1|-1)/.exec(window.location.hash) || [0, 2, 1];"
|
||||||
|
"var sc = m[1], so = m[2]; document.onclick = function(ev) { "
|
||||||
|
"var c = ev.target.rel; if (c) {if (c == sc) so *= -1; srt(tb, c, so); "
|
||||||
|
"sc = c; ev.preventDefault();}};"
|
||||||
|
"srt(tb, sc, so, true);"
|
||||||
|
"}"
|
||||||
|
"</script>";
|
||||||
|
|
||||||
while (p > dir && *p != '/') *p-- = '\0';
|
while (p > dir && *p != '/') *p-- = '\0';
|
||||||
if ((dirp = (opendir(dir))) != NULL) {
|
if ((dirp = (opendir(dir))) != NULL) {
|
||||||
size_t off, n;
|
size_t off, n;
|
||||||
mg_printf(c, "%s\r\n", "HTTP/1.1 200 OK\r\nContent-Length: \r\n");
|
mg_printf(c, "%s\r\n",
|
||||||
|
"HTTP/1.1 200 OK\r\n"
|
||||||
|
"Content-Type: text/html; charset=utf-8\r\n"
|
||||||
|
"Content-Length: \r\n");
|
||||||
off = c->send.len; // Start of body
|
off = c->send.len; // Start of body
|
||||||
mg_printf(c,
|
mg_printf(c,
|
||||||
"<!DOCTYPE html><html><head><title>Index of %.*s</title>"
|
"<!DOCTYPE html><html><head><title>Index of %.*s</title>%s%s"
|
||||||
"<style>th,td {text-align: left; padding-right: 1em; "
|
"<style>th,td {text-align: left; padding-right: 1em; "
|
||||||
"font-family: monospace; }</style></head>"
|
"font-family: monospace; }</style></head>"
|
||||||
"<body><h1>Index of %.*s</h1><table cellpadding=\"0\"><thead>"
|
"<body><h1>Index of %.*s</h1><table cellpadding=\"0\"><thead>"
|
||||||
"<tr><th>Name</th><th>Modified</th><th>Size</th></tr>"
|
"<tr><th><a href=\"#\" rel=\"0\">Name</a></th><th>"
|
||||||
"<tr><td colspan=\"3\"><hr></td></tr></thead><tbody>\n",
|
"<a href=\"#\" rel=\"1\">Modified</a></th>"
|
||||||
(int) hm->uri.len, hm->uri.ptr, (int) hm->uri.len, hm->uri.ptr);
|
"<th><a href=\"#\" rel=\"2\">Size</a></th></tr>"
|
||||||
|
"<tr><td colspan=\"3\"><hr></td></tr>"
|
||||||
|
"</thead>"
|
||||||
|
"<tbody id=\"tb\">\n",
|
||||||
|
(int) hm->uri.len, hm->uri.ptr, sort_js_code, sort_js_code2,
|
||||||
|
(int) hm->uri.len, hm->uri.ptr);
|
||||||
|
|
||||||
while ((dp = readdir(dirp)) != NULL) {
|
while ((dp = readdir(dirp)) != NULL) {
|
||||||
mg_stat_t st;
|
mg_stat_t st;
|
||||||
const char *sep = dp->d_name[0] == MG_DIRSEP ? "/" : "";
|
const char *sep = dp->d_name[0] == MG_DIRSEP ? "/" : "";
|
||||||
@@ -588,7 +645,7 @@ static void listdir(struct mg_connection *c, struct mg_http_message *hm,
|
|||||||
} else if (mg_stat(path, &st) != 0) {
|
} else if (mg_stat(path, &st) != 0) {
|
||||||
LOG(LL_ERROR, ("%lu stat(%s): %d", c->id, path, errno));
|
LOG(LL_ERROR, ("%lu stat(%s): %d", c->id, path, errno));
|
||||||
} else {
|
} else {
|
||||||
printdirentry(c, hm, dp->d_name, &st);
|
printdirentry(c, dp->d_name, &st);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
closedir(dirp);
|
closedir(dirp);
|
||||||
@@ -622,6 +679,7 @@ void mg_http_serve_dir(struct mg_connection *c, struct mg_http_message *hm,
|
|||||||
size_t n1 = strlen(t1), n2;
|
size_t n1 = strlen(t1), n2;
|
||||||
|
|
||||||
mg_url_decode(hm->uri.ptr, hm->uri.len, t1 + n1, sizeof(t1) - n1, 0);
|
mg_url_decode(hm->uri.ptr, hm->uri.len, t1 + n1, sizeof(t1) - n1, 0);
|
||||||
|
LOG(LL_DEBUG, ("SERVE: [%s]", t1 + n1));
|
||||||
t1[sizeof(t1) - 1] = '\0';
|
t1[sizeof(t1) - 1] = '\0';
|
||||||
n2 = strlen(t1);
|
n2 = strlen(t1);
|
||||||
while (n2 > 0 && t1[n2 - 1] == '/') t1[--n2] = 0;
|
while (n2 > 0 && t1[n2 - 1] == '/') t1[--n2] = 0;
|
||||||
@@ -637,16 +695,17 @@ void mg_http_serve_dir(struct mg_connection *c, struct mg_http_message *hm,
|
|||||||
|
|
||||||
if (strlen(t2) < n1 || memcmp(t1, t2, n1) != 0) {
|
if (strlen(t2) < n1 || memcmp(t1, t2, n1) != 0) {
|
||||||
// Requested file is located outside root directory, fail
|
// Requested file is located outside root directory, fail
|
||||||
mg_http_reply(c, 404, "", "Not found %.*s\n", hm->uri.len, hm->uri.ptr);
|
mg_http_reply(c, 404, "", "Not found %.*s\n", (int) hm->uri.len,
|
||||||
|
hm->uri.ptr);
|
||||||
} else {
|
} else {
|
||||||
FILE *fp = fopen(t2, "r");
|
FILE *fp = mg_fopen(t2, "r");
|
||||||
#if MG_ENABLE_SSI
|
#if MG_ENABLE_SSI
|
||||||
if (is_index && fp == NULL) {
|
if (is_index && fp == NULL) {
|
||||||
char *p = t2 + strlen(t2);
|
char *p = t2 + strlen(t2);
|
||||||
while (p > t2 && p[-1] != '/') p--;
|
while (p > t2 && p[-1] != '/') p--;
|
||||||
strncpy(p, "index.shtml", &t2[sizeof(t2)] - p - 2);
|
strncpy(p, "index.shtml", &t2[sizeof(t2)] - p - 2);
|
||||||
t2[sizeof(t2) - 1] = '\0';
|
t2[sizeof(t2) - 1] = '\0';
|
||||||
fp = fopen(t2, "r");
|
fp = mg_fopen(t2, "r");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#if MG_ENABLE_HTTP_DEBUG_ENDPOINT
|
#if MG_ENABLE_HTTP_DEBUG_ENDPOINT
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "log.h"
|
|
||||||
#include "ssi.h"
|
#include "ssi.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
#ifndef MG_MAX_SSI_DEPTH
|
#ifndef MG_MAX_SSI_DEPTH
|
||||||
#define MG_MAX_SSI_DEPTH 5
|
#define MG_MAX_SSI_DEPTH 5
|
||||||
@@ -8,7 +9,7 @@
|
|||||||
#if MG_ENABLE_SSI
|
#if MG_ENABLE_SSI
|
||||||
static char *mg_ssi(const char *path, const char *root, int depth) {
|
static char *mg_ssi(const char *path, const char *root, int depth) {
|
||||||
struct mg_iobuf b = {NULL, 0, 0};
|
struct mg_iobuf b = {NULL, 0, 0};
|
||||||
FILE *fp = fopen(path, "rb");
|
FILE *fp = mg_fopen(path, "rb");
|
||||||
if (fp != NULL) {
|
if (fp != NULL) {
|
||||||
char buf[BUFSIZ], arg[sizeof(buf)];
|
char buf[BUFSIZ], arg[sizeof(buf)];
|
||||||
int ch, intag = 0;
|
int ch, intag = 0;
|
||||||
|
|||||||
+28
-7
@@ -2,21 +2,42 @@
|
|||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
#if MG_ENABLE_FS
|
#if MG_ENABLE_FS
|
||||||
size_t mg_file_size(const char *path) {
|
int mg_stat(const char *path, mg_stat_t *st) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
wchar_t tmp[MG_PATH_MAX];
|
||||||
|
MultiByteToWideChar(CP_UTF8, 0, path, -1, tmp, sizeof(tmp) / sizeof(tmp[0]));
|
||||||
|
return _wstati64(tmp, st);
|
||||||
|
#else
|
||||||
|
return stat(path, st);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *mg_fopen(const char *path, const char *mode) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
wchar_t b1[MG_PATH_MAX], b2[10];
|
||||||
|
MultiByteToWideChar(CP_UTF8, 0, path, -1, b1, sizeof(b1) / sizeof(b1[0]));
|
||||||
|
MultiByteToWideChar(CP_UTF8, 0, mode, -1, b2, sizeof(b2) / sizeof(b2[0]));
|
||||||
|
return _wfopen(b1, b2);
|
||||||
|
#else
|
||||||
|
return fopen(path, mode);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t mg_file_size(const char *path) {
|
||||||
#if MG_ARCH == MG_ARCH_FREERTOS
|
#if MG_ARCH == MG_ARCH_FREERTOS
|
||||||
struct FF_STAT st;
|
struct FF_STAT st;
|
||||||
return ff_stat(path, &st) == 0 ? st.st_size : 0;
|
return ff_stat(path, &st) == 0 ? st.st_size : 0;
|
||||||
#else
|
#else
|
||||||
struct stat st;
|
mg_stat_t st;
|
||||||
return stat(path, &st) == 0 ? st.st_size : 0;
|
return mg_stat(path, &st) == 0 ? st.st_size : 0;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
char *mg_file_read(const char *path) {
|
char *mg_file_read(const char *path) {
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
char *data = NULL;
|
char *data = NULL;
|
||||||
size_t size = mg_file_size(path);
|
size_t size = (size_t) mg_file_size(path);
|
||||||
if ((fp = fopen(path, "rb")) != NULL) {
|
if ((fp = mg_fopen(path, "rb")) != NULL) {
|
||||||
data = (char *) malloc(size + 1);
|
data = (char *) malloc(size + 1);
|
||||||
if (data != NULL) {
|
if (data != NULL) {
|
||||||
if (fread(data, 1, size, fp) != size) {
|
if (fread(data, 1, size, fp) != size) {
|
||||||
@@ -36,7 +57,7 @@ bool mg_file_write(const char *path, const void *buf, size_t len) {
|
|||||||
FILE *fp;
|
FILE *fp;
|
||||||
char tmp[MG_PATH_MAX];
|
char tmp[MG_PATH_MAX];
|
||||||
snprintf(tmp, sizeof(tmp), "%s.%d", path, rand());
|
snprintf(tmp, sizeof(tmp), "%s.%d", path, rand());
|
||||||
fp = fopen(tmp, "wb");
|
fp = mg_fopen(tmp, "wb");
|
||||||
if (fp != NULL) {
|
if (fp != NULL) {
|
||||||
result = fwrite(buf, 1, len, fp) == len;
|
result = fwrite(buf, 1, len, fp) == len;
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
@@ -64,7 +85,7 @@ bool mg_file_printf(const char *path, const char *fmt, ...) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void mg_random(void *buf, size_t len) {
|
void mg_random(void *buf, size_t len) {
|
||||||
FILE *fp = fopen("/dev/urandom", "rb");
|
FILE *fp = mg_fopen("/dev/urandom", "rb");
|
||||||
size_t i, n = 0;
|
size_t i, n = 0;
|
||||||
if (fp != NULL) n = fread(buf, 1, len, fp);
|
if (fp != NULL) n = fread(buf, 1, len, fp);
|
||||||
if (fp == NULL || n <= 0) {
|
if (fp == NULL || n <= 0) {
|
||||||
|
|||||||
+3
-3
@@ -4,7 +4,7 @@
|
|||||||
#include "str.h"
|
#include "str.h"
|
||||||
|
|
||||||
char *mg_file_read(const char *path);
|
char *mg_file_read(const char *path);
|
||||||
size_t mg_file_size(const char *path);
|
int64_t mg_file_size(const char *path);
|
||||||
bool mg_file_write(const char *path, const void *buf, size_t len);
|
bool mg_file_write(const char *path, const void *buf, size_t len);
|
||||||
bool mg_file_printf(const char *path, const char *fmt, ...);
|
bool mg_file_printf(const char *path, const char *fmt, ...);
|
||||||
void mg_random(void *buf, size_t len);
|
void mg_random(void *buf, size_t len);
|
||||||
@@ -26,11 +26,11 @@ void mg_usleep(unsigned long usecs);
|
|||||||
#if MG_ENABLE_FS
|
#if MG_ENABLE_FS
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
typedef struct _stati64 mg_stat_t;
|
typedef struct _stati64 mg_stat_t;
|
||||||
#define mg_stat(a, b) _stati64((a), (b))
|
|
||||||
#else
|
#else
|
||||||
typedef struct stat mg_stat_t;
|
typedef struct stat mg_stat_t;
|
||||||
#define mg_stat(a, b) stat((a), (b))
|
|
||||||
#endif
|
#endif
|
||||||
|
int mg_stat(const char *path, mg_stat_t *);
|
||||||
|
FILE *mg_fopen(const char *fp, const char *mode);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define mg_htons(x) mg_ntohs(x)
|
#define mg_htons(x) mg_ntohs(x)
|
||||||
|
|||||||
Reference in new issue
Block a user