diff --git a/src/CanonicalJson.c b/src/CanonicalJson.c index fae0ef1..c1e74f5 100644 --- a/src/CanonicalJson.c +++ b/src/CanonicalJson.c @@ -37,7 +37,7 @@ CanonicalJsonKeyCompare(void *k1, void *k2) } static void -CanonicalJsonEncodeValue(JsonValue * value, FILE * out) +CanonicalJsonEncodeValue(JsonValue * value, Stream * out) { Array *arr; size_t i, len; @@ -52,7 +52,7 @@ CanonicalJsonEncodeValue(JsonValue * value, FILE * out) arr = JsonValueAsArray(value); len = ArraySize(arr); - fputc('[', out); + StreamPutc(out, '['); for (i = 0; i < len; i++) { @@ -67,11 +67,11 @@ CanonicalJsonEncodeValue(JsonValue * value, FILE * out) CanonicalJsonEncodeValue(aVal, out); if (i < len - 1) { - fputc(',', out); + StreamPutc(out, ','); } } - fputc(']', out); + StreamPutc(out, ']'); break; default: JsonEncodeValue(value, out, JSON_DEFAULT); @@ -80,7 +80,7 @@ CanonicalJsonEncodeValue(JsonValue * value, FILE * out) } int -CanonicalJsonEncode(HashMap * object, FILE * out) +CanonicalJsonEncode(HashMap * object, Stream * out) { char *key; JsonValue *value; @@ -106,7 +106,7 @@ CanonicalJsonEncode(HashMap * object, FILE * out) ArraySort(keys, CanonicalJsonKeyCompare); - fputc('{', out); + StreamPutc(out, '{'); keyCount = ArraySize(keys); for (i = 0; i < keyCount; i++) @@ -129,16 +129,16 @@ CanonicalJsonEncode(HashMap * object, FILE * out) } JsonEncodeString(key, out); - fputc(':', out); + StreamPutc(out, ':'); CanonicalJsonEncodeValue(value, out); if (i < keyCount - 1) { - fputc(',', out); + StreamPutc(out, ','); } } - fputc('}', out); + StreamPutc(out, '}'); ArrayFree(keys); return 1; diff --git a/src/Db.c b/src/Db.c index b922767..ea2d395 100644 --- a/src/Db.c +++ b/src/Db.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -83,7 +84,8 @@ struct DbRef DbRef *prev; DbRef *next; - FILE *fp; + int fd; + Stream *stream; }; static void @@ -394,9 +396,11 @@ DbLockFromArr(Db * db, Array * args) char *file; char *hash; DbRef *ref; - FILE *fp; struct flock lock; + int fd; + Stream *stream; + if (!db || !args) { return NULL; @@ -412,9 +416,8 @@ DbLockFromArr(Db * db, Array * args) ref = HashMapGet(db->cache, hash); file = DbFileName(db, args); - /* Open the file for reading and writing so we can lock it */ - fp = fopen(file, "r+"); - if (!fp) + fd = open(file, O_RDWR); + if (fd == -1) { if (ref) { @@ -452,15 +455,17 @@ DbLockFromArr(Db * db, Array * args) goto finish; } + stream = StreamFd(fd); + lock.l_start = 0; lock.l_len = 0; lock.l_type = F_WRLCK; lock.l_whence = SEEK_SET; /* Lock the file on the disk */ - if (fcntl(fileno(fp), F_SETLK, &lock) < 0) + if (fcntl(fd, F_SETLK, &lock) < 0) { - fclose(fp); + StreamClose(stream); ref = NULL; goto finish; } @@ -470,17 +475,16 @@ DbLockFromArr(Db * db, Array * args) unsigned long diskTs = UtilLastModified(file); pthread_mutex_lock(&ref->lock); - ref->fp = fp; if (diskTs > ref->ts) { /* File was modified on disk since it was cached */ - HashMap *json = JsonDecode(fp); + HashMap *json = JsonDecode(ref->stream); if (!json) { pthread_mutex_unlock(&ref->lock); - fclose(fp); + StreamClose(ref->stream); ref = NULL; goto finish; } @@ -489,7 +493,6 @@ DbLockFromArr(Db * db, Array * args) ref->json = json; ref->ts = diskTs; ref->size = DbComputeSize(ref->json); - } /* Float this ref to mostRecent */ @@ -525,25 +528,26 @@ DbLockFromArr(Db * db, Array * args) ref = Malloc(sizeof(DbRef)); if (!ref) { - fclose(fp); + StreamClose(stream); goto finish; } - ref->json = JsonDecode(fp); - ref->fp = fp; + ref->json = JsonDecode(stream); if (!ref->json) { Free(ref); - fclose(fp); + StreamClose(stream); ref = NULL; goto finish; } + ref->fd = fd; + ref->stream = stream; + pthread_mutex_init(&ref->lock, NULL); pthread_mutex_lock(&ref->lock); - for (i = 0; i < ArraySize(args); i++) { ArrayAdd(name, StrDuplicate(ArrayGet(args, i))); @@ -579,7 +583,7 @@ finish: DbRef * DbCreate(Db * db, size_t nArgs,...) { - FILE *fp; + Stream *fp; char *file; char *dir; va_list ap; @@ -620,7 +624,7 @@ DbCreate(Db * db, size_t nArgs,...) Free(dir); - fp = fopen(file, "w"); + fp = StreamOpen(file, "w"); Free(file); if (!fp) { @@ -628,9 +632,8 @@ DbCreate(Db * db, size_t nArgs,...) return NULL; } - fprintf(fp, "{}"); - fflush(fp); - fclose(fp); + StreamPuts(fp, "{}"); + StreamClose(fp); ret = DbLockFromArr(db, args); @@ -744,17 +747,16 @@ DbUnlock(Db * db, DbRef * ref) pthread_mutex_lock(&db->lock); - rewind(ref->fp); - if (ftruncate(fileno(ref->fp), 0) < 0) + lseek(ref->fd, 0L, SEEK_SET); + if (ftruncate(ref->fd, 0) < 0) { pthread_mutex_unlock(&db->lock); return 0; } - JsonEncode(ref->json, ref->fp, JSON_DEFAULT); + JsonEncode(ref->json, ref->stream, JSON_DEFAULT); - fflush(ref->fp); - fclose(ref->fp); + StreamClose(ref->stream); if (db->cache) { diff --git a/src/Html.c b/src/Html.c index 9d59c0f..25ea8ce 100644 --- a/src/Html.c +++ b/src/Html.c @@ -28,7 +28,7 @@ #include void -HtmlBegin(FILE * stream, char *title) +HtmlBegin(Stream * stream, char *title) { size_t i; @@ -37,87 +37,87 @@ HtmlBegin(FILE * stream, char *title) return; } - fprintf(stream, - "" - "" - "" - "" - "" - "%s | Telodendria" - ,title + StreamPrintf(stream, + "" + "" + "" + "" + "" + "%s | Telodendria" + ,title ); HtmlBeginStyle(stream); - fprintf(stream, - ":root {" - " color-scheme: dark;" - " --accent: #7b8333;" - "}" - "body {" - " margin: auto;" - " width: 100%%;" - " max-width: 8.5in;" - " padding: 0.25in;" - " background-color: #0d1117;" - " color: white;" - "}" - "a {" - " color: var(--accent);" - " text-decoration: none;" - "}" - "h1 {" - " text-align: center;" - "}" - ".logo {" - " color: var(--accent);" - " text-align: center;" - " font-weight: bold;" - "}" + StreamPuts(stream, + ":root {" + " color-scheme: dark;" + " --accent: #7b8333;" + "}" + "body {" + " margin: auto;" + " width: 100%;" + " max-width: 8.5in;" + " padding: 0.25in;" + " background-color: #0d1117;" + " color: white;" + "}" + "a {" + " color: var(--accent);" + " text-decoration: none;" + "}" + "h1 {" + " text-align: center;" + "}" + ".logo {" + " color: var(--accent);" + " text-align: center;" + " font-weight: bold;" + "}" ); - fprintf(stream, - ".form {" - " margin: auto;" - " width: 100%%;" - " max-width: 400px;" - " border-radius: 10px;" - " border: 1px var(--accent) solid;" - " padding: 10px;" - "}" - "form {" - " display: block;" - "}" - "form > input, label {" - " width: 95%%;" - " height: 25px;" - " display: block;" - " margin-bottom: 5px;" - " margin-left: auto;" - " margin-right: auto;" - "}" + StreamPuts(stream, + ".form {" + " margin: auto;" + " width: 100%;" + " max-width: 400px;" + " border-radius: 10px;" + " border: 1px var(--accent) solid;" + " padding: 10px;" + "}" + "form {" + " display: block;" + "}" + "form > input, label {" + " width: 95%;" + " height: 25px;" + " display: block;" + " margin-bottom: 5px;" + " margin-left: auto;" + " margin-right: auto;" + "}" ); HtmlEndStyle(stream); - fprintf(stream, - "" - "" - "
"
+    StreamPuts(stream,
+               ""
+               ""
+               "
"
             );
 
     for (i = 0; i < TELODENDRIA_LOGO_HEIGHT; i++)
     {
-        fprintf(stream, "%s\n", TelodendriaLogo[i]);
+        StreamPrintf(stream, "%s\n", TelodendriaLogo[i]);
     }
 
-    fprintf(stream,
-            "
" - "

%s

" - ,title); + StreamPrintf(stream, + "
" + "

%s

" + ,title); } void -HtmlEnd(FILE * stream) +HtmlEnd(Stream * stream) { - fprintf(stream, - "" - ""); + StreamPuts(stream, + "" + ""); } diff --git a/src/Http.c b/src/Http.c index 653de82..63084d2 100644 --- a/src/Http.c +++ b/src/Http.c @@ -536,7 +536,7 @@ HttpParamEncode(HashMap * params) } HashMap * -HttpParseHeaders(FILE * fp) +HttpParseHeaders(Stream * fp) { HashMap *headers; diff --git a/src/HttpClient.c b/src/HttpClient.c index 00d64aa..50fa155 100644 --- a/src/HttpClient.c +++ b/src/HttpClient.c @@ -40,7 +40,8 @@ struct HttpClientContext { HashMap *responseHeaders; - FILE *stream; + Stream *stream; + int sd; }; HttpClientContext * @@ -126,7 +127,8 @@ HttpRequest(HttpRequestMethod method, int flags, unsigned short port, char *host freeaddrinfo(res0); - context->stream = fdopen(sd, "r+"); + context->sd = sd; + context->stream = StreamFd(sd); if (!context->stream) { Free(context); @@ -134,8 +136,8 @@ HttpRequest(HttpRequestMethod method, int flags, unsigned short port, char *host return NULL; } - fprintf(context->stream, "%s %s HTTP/1.1\r\n", - HttpRequestMethodToString(method), path); + StreamPrintf(context->stream, "%s %s HTTP/1.1\r\n", + HttpRequestMethodToString(method), path); HttpRequestHeader(context, "Connection", "close"); HttpRequestHeader(context, "User-Agent", "Telodendria/" TELODENDRIA_VERSION); @@ -152,7 +154,7 @@ HttpRequestHeader(HttpClientContext * context, char *key, char *val) return; } - fprintf(context->stream, "%s: %s\r\n", key, val); + StreamPrintf(context->stream, "%s: %s\r\n", key, val); } void @@ -163,8 +165,8 @@ HttpRequestSendHeaders(HttpClientContext * context) return; } - fprintf(context->stream, "\r\n"); - fflush(context->stream); + StreamPuts(context->stream, "\r\n"); + StreamFlush(context->stream); } HttpStatus @@ -182,8 +184,8 @@ HttpRequestSend(HttpClientContext * context) return 0; } - fflush(context->stream); - shutdown(fileno(context->stream), SHUT_WR); + StreamFlush(context->stream); + shutdown(context->sd, SHUT_WR); lineLen = UtilGetLine(&line, &lineSize, context->stream); @@ -238,7 +240,7 @@ HttpResponseHeaders(HttpClientContext * context) return context->responseHeaders; } -FILE * +Stream * HttpClientStream(HttpClientContext * context) { if (!context) @@ -267,6 +269,6 @@ HttpClientContextFree(HttpClientContext * context) HashMapFree(context->responseHeaders); - fclose(context->stream); + StreamClose(context->stream); Free(context); } diff --git a/src/HttpServer.c b/src/HttpServer.c index 9660dcf..5c08522 100644 --- a/src/HttpServer.c +++ b/src/HttpServer.c @@ -74,7 +74,7 @@ struct HttpServerContext HashMap *responseHeaders; HttpStatus responseStatus; - FILE *stream; + Stream *stream; }; typedef struct HttpServerWorkerThreadArgs @@ -86,7 +86,7 @@ typedef struct HttpServerWorkerThreadArgs static HttpServerContext * HttpServerContextCreate(HttpRequestMethod requestMethod, - char *requestPath, HashMap * requestParams, FILE * stream) + char *requestPath, HashMap * requestParams, Stream * stream) { HttpServerContext *c; @@ -158,7 +158,7 @@ HttpServerContextFree(HttpServerContext * c) HashMapFree(c->requestParams); Free(c->requestPath); - fclose(c->stream); + StreamClose(c->stream); Free(c); } @@ -229,7 +229,7 @@ HttpResponseStatus(HttpServerContext * c, HttpStatus status) c->responseStatus = status; } -FILE * +Stream * HttpServerStream(HttpServerContext * c) { if (!c) @@ -243,25 +243,25 @@ HttpServerStream(HttpServerContext * c) void HttpSendHeaders(HttpServerContext * c) { - FILE *fp = c->stream; + Stream *fp = c->stream; char *key; char *val; - fprintf(fp, "HTTP/1.0 %d %s\n", c->responseStatus, HttpStatusToString(c->responseStatus)); + StreamPrintf(fp, "HTTP/1.0 %d %s\n", c->responseStatus, HttpStatusToString(c->responseStatus)); while (HashMapIterate(c->responseHeaders, &key, (void **) &val)) { - fprintf(fp, "%s: %s\n", key, val); + StreamPrintf(fp, "%s: %s\n", key, val); } - fprintf(fp, "\n"); + StreamPuts(fp, "\n"); } -static FILE * +static Stream * DequeueConnection(HttpServer * server) { - FILE *fp; + Stream *fp; if (!server) { @@ -409,7 +409,7 @@ HttpServerWorkerThread(void *args) while (!server->stop) { - FILE *fp; + Stream *fp; HttpServerContext *context; char *line = NULL; @@ -450,7 +450,7 @@ HttpServerWorkerThread(void *args) while ((lineLen = UtilGetLine(&line, &lineSize, fp)) == -1 && errno == EAGAIN) { - clearerr(fp); + StreamClearError(fp); /* If the server is stopped, or it's been a while, just * give up so we aren't wasting a thread on this client. */ @@ -541,24 +541,24 @@ HttpServerWorkerThread(void *args) HttpServerContextFree(context); fp = NULL; /* The above call will close this - * FILE */ + * Stream */ goto finish; internal_error: - fprintf(fp, "HTTP/1.0 500 Internal Server Error\n"); - fprintf(fp, "Connection: close\n"); + StreamPuts(fp, "HTTP/1.0 500 Internal Server Error\n"); + StreamPuts(fp, "Connection: close\n"); goto finish; bad_request: - fprintf(fp, "HTTP/1.0 400 Bad Request\n"); - fprintf(fp, "Connection: close\n"); + StreamPuts(fp, "HTTP/1.0 400 Bad Request\n"); + StreamPuts(fp, "Connection: close\n"); goto finish; finish: Free(line); if (fp) { - fclose(fp); + StreamClose(fp); } } @@ -570,7 +570,7 @@ HttpServerEventThread(void *args) { HttpServer *server = (HttpServer *) args; struct pollfd pollFds[1]; - FILE *fp; + Stream *fp; size_t i; server->isRunning = 1; @@ -632,7 +632,7 @@ HttpServerEventThread(void *args) continue; } - fp = fdopen(connFd, "r+"); + fp = StreamFd(connFd); if (!fp) { pthread_mutex_unlock(&server->connQueueMutex); @@ -655,7 +655,7 @@ HttpServerEventThread(void *args) while ((fp = DequeueConnection(server))) { - fclose(fp); + StreamClose(fp); } server->isRunning = 0; diff --git a/src/Io.c b/src/Io.c index 16fc846..86c203c 100644 --- a/src/Io.c +++ b/src/Io.c @@ -134,7 +134,7 @@ int IoVprintf(Io * io, const char *fmt, va_list ap) { char *buf; - size_t write; + int write; int ret; diff --git a/src/Json.c b/src/Json.c index 90c6c3d..af4159b 100644 --- a/src/Json.c +++ b/src/Json.c @@ -67,7 +67,7 @@ typedef enum JsonToken typedef struct JsonParserState { - FILE *stream; + Stream *stream; JsonToken tokenType; char *token; @@ -334,12 +334,12 @@ JsonValueFree(JsonValue * value) } void -JsonEncodeString(const char *str, FILE * out) +JsonEncodeString(const char *str, Stream * out) { size_t i; char c; - fputc('"', out); + StreamPutc(out, '"'); i = 0; while ((c = str[i]) != '\0') @@ -349,23 +349,23 @@ JsonEncodeString(const char *str, FILE * out) case '\\': case '"': case '/': - fputc('\\', out); - fputc(c, out); + StreamPutc(out, '\\'); + StreamPutc(out, c); break; case '\b': - fputs("\\b", out); + StreamPuts(out, "\\b"); break; case '\t': - fputs("\\t", out); + StreamPuts(out, "\\t"); break; case '\n': - fputs("\\n", out); + StreamPuts(out, "\\n"); break; case '\f': - fputs("\\f", out); + StreamPuts(out, "\\f"); break; case '\r': - fputs("\\r", out); + StreamPuts(out, "\\r"); break; default: /* Assume UTF-8 input */ /* @@ -381,22 +381,22 @@ JsonEncodeString(const char *str, FILE * out) */ if (c <= 0x001F) { - fprintf(out, "\\u%04x", c); + StreamPrintf(out, "\\u%04x", c); } else { - fputc(c, out); + StreamPutc(out, c); } break; } i++; } - fputc('"', out); + StreamPutc(out, '"'); } static char * -JsonDecodeString(FILE * in) +JsonDecodeString(Stream * in) { const size_t strBlockSize = 16; @@ -419,7 +419,7 @@ JsonDecodeString(FILE * in) return NULL; } - while ((c = fgetc(in)) != EOF) + while ((c = StreamGetc(in)) != EOF) { if (c <= 0x001F) { @@ -449,7 +449,7 @@ JsonDecodeString(FILE * in) return str; break; case '\\': - c = fgetc(in); + c = StreamGetc(in); switch (c) { case '\\': @@ -479,8 +479,14 @@ JsonDecodeString(FILE * in) a[1] = '\0'; break; case 'u': - /* Read \uXXXX point into a 4-byte buffer */ - if (fscanf(in, "%04lx", &utf8) != 1) + /* Read 4 characters into a */ + if (!StreamGets(in, a, sizeof(a))) + { + Free(str); + return NULL; + } + /* Interpret characters as a hex number */ + if (sscanf(a, "%04lx", &utf8) != 1) { /* Bad hex value */ Free(str); @@ -562,7 +568,7 @@ JsonDecodeString(FILE * in) } void -JsonEncodeValue(JsonValue * value, FILE * out, int level) +JsonEncodeValue(JsonValue * value, Stream * out, int level) { size_t i; size_t len; @@ -577,47 +583,47 @@ JsonEncodeValue(JsonValue * value, FILE * out, int level) arr = value->as.array; len = ArraySize(arr); - fputc('[', out); + StreamPutc(out, '['); for (i = 0; i < len; i++) { if (level >= 0) { - fprintf(out, "\n%*s", level + 2, ""); + StreamPrintf(out, "\n%*s", level + 2, ""); } JsonEncodeValue(ArrayGet(arr, i), out, level >= 0 ? level + 2 : level); if (i < len - 1) { - fputc(',', out); + StreamPutc(out, ','); } } if (level >= 0) { - fprintf(out, "\n%*s", level, ""); + StreamPrintf(out, "\n%*s", level, ""); } - fputc(']', out); + StreamPutc(out, ']'); break; case JSON_STRING: JsonEncodeString(value->as.string, out); break; case JSON_INTEGER: - fprintf(out, "%ld", value->as.integer); + StreamPrintf(out, "%ld", value->as.integer); break; case JSON_FLOAT: - fprintf(out, "%f", value->as.floating); + StreamPrintf(out, "%f", value->as.floating); break; case JSON_BOOLEAN: if (value->as.boolean) { - fputs("true", out); + StreamPuts(out, "true"); } else { - fputs("false", out); + StreamPuts(out, "false"); } break; case JSON_NULL: - fputs("null", out); + StreamPuts(out, "null"); break; default: return; @@ -625,7 +631,7 @@ JsonEncodeValue(JsonValue * value, FILE * out, int level) } int -JsonEncode(HashMap * object, FILE * out, int level) +JsonEncode(HashMap * object, Stream * out, int level) { size_t index; size_t count; @@ -643,10 +649,10 @@ JsonEncode(HashMap * object, FILE * out, int level) count++; } - fputc('{', out); + StreamPutc(out, '{'); if (level >= 0) { - fputc('\n', out); + StreamPutc(out, '\n'); } index = 0; @@ -654,27 +660,27 @@ JsonEncode(HashMap * object, FILE * out, int level) { if (level >= 0) { - fprintf(out, "%*s", level + 2, ""); + StreamPrintf(out, "%*s", level + 2, ""); } JsonEncodeString(key, out); - fputc(':', out); + StreamPutc(out, ':'); if (level >= 0) { - fputc(' ', out); + StreamPutc(out, ' '); } JsonEncodeValue(value, out, level >= 0 ? level + 2 : level); if (index < count - 1) { - fputc(',', out); + StreamPutc(out, ','); } if (level >= 0) { - fputc('\n', out); + StreamPutc(out, '\n'); } index++; @@ -682,9 +688,9 @@ JsonEncode(HashMap * object, FILE * out, int level) if (level >= 0) { - fprintf(out, "%*s", level, ""); + StreamPrintf(out, "%*s", level, ""); } - fputc('}', out); + StreamPutc(out, '}'); return 1; } @@ -715,18 +721,18 @@ JsonConsumeWhitespace(JsonParserState * state) while (1) { - c = fgetc(state->stream); + c = StreamGetc(state->stream); - if (feof(state->stream)) + if (StreamEof(state->stream)) { break; } - if (ferror(state->stream)) + if (StreamError(state->stream)) { if (errno == EAGAIN) { - clearerr(state->stream); + StreamClearError(state->stream); tries++; if (tries >= nRetries || readFlg) @@ -765,7 +771,7 @@ JsonTokenSeek(JsonParserState * state) { int c = JsonConsumeWhitespace(state); - if (feof(state->stream)) + if (StreamEof(state->stream)) { state->tokenType = TOKEN_EOF; return; @@ -822,7 +828,7 @@ JsonTokenSeek(JsonParserState * state) } state->token[0] = c; - while ((c = fgetc(state->stream)) != EOF) + while ((c = StreamGetc(state->stream)) != EOF) { if (c == '.') { @@ -838,7 +844,7 @@ JsonTokenSeek(JsonParserState * state) } else if (!isdigit(c)) { - ungetc(c, state->stream); + StreamUngetc(state->stream, c); break; } @@ -893,7 +899,7 @@ JsonTokenSeek(JsonParserState * state) switch (c) { case 't': - if (!fgets(state->token + 1, 4, state->stream)) + if (!StreamGets(state->stream, state->token + 1, 4)) { state->tokenType = TOKEN_EOF; Free(state->token); @@ -914,7 +920,7 @@ JsonTokenSeek(JsonParserState * state) } break; case 'f': - if (!fgets(state->token + 1, 5, state->stream)) + if (!StreamGets(state->stream, state->token + 1, 5)) { state->tokenType = TOKEN_EOF; Free(state->token); @@ -935,7 +941,7 @@ JsonTokenSeek(JsonParserState * state) } break; case 'n': - if (!fgets(state->token + 1, 4, state->stream)) + if (!StreamGets(state->stream, state->token + 1, 4)) { state->tokenType = TOKEN_EOF; Free(state->token); @@ -1155,7 +1161,7 @@ error: } HashMap * -JsonDecode(FILE * stream) +JsonDecode(Stream * stream) { HashMap *result; diff --git a/src/Log.c b/src/Log.c index 0d97522..f1188c1 100644 --- a/src/Log.c +++ b/src/Log.c @@ -38,7 +38,7 @@ struct LogConfig { int level; size_t indent; - FILE *out; + Stream *out; int flags; char *tsFmt; @@ -109,7 +109,7 @@ LogConfigFree(LogConfig * config) return; } - fclose(config->out); + StreamClose(config->out); Free(config); } @@ -165,7 +165,7 @@ LogConfigLevelSet(LogConfig * config, int level) } void -LogConfigOutputSet(LogConfig * config, FILE * out) +LogConfigOutputSet(LogConfig * config, Stream * out) { if (!config) { @@ -178,7 +178,7 @@ LogConfigOutputSet(LogConfig * config, FILE * out) } else { - config->out = stdout; + config->out = StreamStdout(); } } @@ -240,7 +240,7 @@ Log(LogConfig * config, int level, const char *msg,...) } doColor = LogConfigFlagGet(config, LOG_FLAG_COLOR) - && isatty(fileno(config->out)); + && isatty(StreamFileno(config->out)); if (doColor) { @@ -276,10 +276,10 @@ Log(LogConfig * config, int level, const char *msg,...) break; } - fputs(ansi, config->out); + StreamPuts(config->out, ansi); } - fputc('[', config->out); + StreamPutc(config->out, '['); if (config->tsFmt) { @@ -292,10 +292,10 @@ Log(LogConfig * config, int level, const char *msg,...) if (tsLength) { - fputs(tsBuffer, config->out); + StreamPuts(config->out, tsBuffer); if (!isspace((unsigned char) tsBuffer[tsLength - 1])) { - fputc(' ', config->out); + StreamPutc(config->out, ' '); } } } @@ -331,32 +331,26 @@ Log(LogConfig * config, int level, const char *msg,...) break; } - fprintf(config->out, "%c]", indicator); + StreamPrintf(config->out, "%c]", indicator); if (doColor) { /* ANSI Reset */ - fputs("\033[0m", config->out); + StreamPuts(config->out, "\033[0m"); } - fputc(' ', config->out); + StreamPutc(config->out, ' '); for (i = 0; i < config->indent; i++) { - fputc(' ', config->out); + StreamPutc(config->out, ' '); } va_start(argp, msg); - vfprintf(config->out, msg, argp); - fputc('\n', config->out); + StreamVprintf(config->out, msg, argp); + StreamPutc(config->out, '\n'); va_end(argp); - /* If we are debugging, there might be something that's going to - * segfault the program coming up, so flush the output stream - * immediately. */ - if (config->level == LOG_DEBUG) - { - fflush(config->out); - } + StreamFlush(config->out); pthread_mutex_unlock(&config->lock); } diff --git a/src/Main.c b/src/Main.c index f99bf18..b425f67 100644 --- a/src/Main.c +++ b/src/Main.c @@ -72,7 +72,7 @@ main(int argc, char **argv) char *configArg = NULL; /* Config file */ - FILE *configFile = NULL; + Stream *configFile = NULL; HashMap *config = NULL; /* Program configuration */ @@ -155,11 +155,11 @@ main(int argc, char **argv) } else if (strcmp(configArg, "-") == 0) { - configFile = stdin; + configFile = StreamStdin(); } else { - fclose(stdin); + StreamClose(StreamStdin()); #ifdef __OpenBSD__ if (unveil(configArg, "r") != 0) { @@ -168,7 +168,7 @@ main(int argc, char **argv) goto finish; } #endif - configFile = fopen(configArg, "r"); + configFile = StreamOpen(configArg, "r"); if (!configFile) { Log(lc, LOG_ERR, "Unable to open configuration file '%s' for reading.", configArg); @@ -180,7 +180,7 @@ main(int argc, char **argv) Log(lc, LOG_NOTICE, "Processing configuration file '%s'.", configArg); config = JsonDecode(configFile); - fclose(configFile); + StreamClose(configFile); if (!config) { @@ -250,7 +250,7 @@ main(int argc, char **argv) if (tConfig->flags & TELODENDRIA_LOG_FILE) { - FILE *logFile = fopen("telodendria.log", "a"); + Stream *logFile = StreamOpen("telodendria.log", "a"); if (!logFile) { @@ -476,9 +476,9 @@ finish: * If we're not logging to standard output, then we can close it. Otherwise, * if we are logging to stdout, LogConfigFree() will close it for us. */ - if (!tConfig || !(tConfig->flags & TELODENDRIA_LOG_STDOUT)) + if (tConfig && !(tConfig->flags & TELODENDRIA_LOG_STDOUT)) { - fclose(stdout); + StreamClose(StreamStdout()); } DbClose(matrixArgs.db); @@ -486,15 +486,26 @@ finish: LogConfigTimeStampFormatSet(lc, NULL); TelodendriaConfigFree(tConfig); - Log(lc, LOG_DEBUG, ""); - MemoryIterate(TelodendriaMemoryIterator, lc); - Log(lc, LOG_DEBUG, ""); Log(lc, LOG_DEBUG, "Exiting with code '%d'.", exit); + + /* + * Uninstall the memory hook because it uses the Log + * API, whose configuration is being freed now, so it + * won't work anymore. + */ + MemoryHook(NULL, NULL); + LogConfigFree(lc); - MemoryFreeAll(); + /* Standard error should never have been opened, but just in case + * it was, this doesn't hurt anything. */ + StreamClose(StreamStderr()); - fclose(stderr); + /* Generate a memory report if any leaks occurred. At this point no + * memory should be allocated. */ + TelodendriaGenerateMemReport(); + + MemoryFreeAll(); return exit; } diff --git a/src/Matrix.c b/src/Matrix.c index 9733add..2d6cd2b 100644 --- a/src/Matrix.c +++ b/src/Matrix.c @@ -39,7 +39,7 @@ MatrixHttpHandler(HttpServerContext * context, void *argp) { MatrixHttpHandlerArgs *args = (MatrixHttpHandlerArgs *) argp; LogConfig *lc = args->lc; - FILE *stream; + Stream *stream; HashMap *response = NULL; char *key; @@ -133,7 +133,7 @@ MatrixHttpHandler(HttpServerContext * context, void *argp) JsonEncode(response, stream, JSON_DEFAULT); JsonFree(response); - fprintf(stream, "\n"); + StreamPrintf(stream, "\n"); } /* diff --git a/src/Routes/RouteStatic.c b/src/Routes/RouteStatic.c index 82716c3..55755d4 100644 --- a/src/Routes/RouteStatic.c +++ b/src/Routes/RouteStatic.c @@ -27,7 +27,7 @@ ROUTE_IMPL(RouteStatic, args) { - FILE *stream = HttpServerStream(args->context); + Stream *stream = HttpServerStream(args->context); char *pathPart = MATRIX_PATH_POP(args->path); HttpResponseHeader(args->context, "Content-Type", "text/html"); diff --git a/src/Static/StaticError.c b/src/Static/StaticError.c index 12cfd7a..7bdc535 100644 --- a/src/Static/StaticError.c +++ b/src/Static/StaticError.c @@ -26,7 +26,7 @@ #include void -StaticError(FILE * stream, HttpStatus error) +StaticError(Stream * stream, HttpStatus error) { char title[10]; @@ -34,8 +34,8 @@ StaticError(FILE * stream, HttpStatus error) HtmlBegin(stream, title); - fprintf(stream, "

%s

", - HttpStatusToString(error)); + StreamPrintf(stream, "

%s

", + HttpStatusToString(error)); HtmlEnd(stream); } diff --git a/src/Static/StaticItWorks.c b/src/Static/StaticItWorks.c index 6f87e08..d49c43e 100644 --- a/src/Static/StaticItWorks.c +++ b/src/Static/StaticItWorks.c @@ -25,30 +25,30 @@ #include void -StaticItWorks(FILE * stream) +StaticItWorks(Stream * stream) { HtmlBegin(stream, "It works! Telodendria is running."); - fprintf(stream, - "" + StreamPuts(stream, + "" ); - fprintf(stream, - "

" + StreamPuts(stream, + "

" "Your Telodendria server is listening on this port and is ready " - "for messages." - "

" - "

" - "To use this server, you'll need " - "a Matrix client." - "

" - "

" - "Welcome to the Matrix universe :)" - "

" + "for messages." + "

" + "

" + "To use this server, you'll need " + "a Matrix client." + "

" + "

" + "Welcome to the Matrix universe :)" + "

" ); HtmlEnd(stream); diff --git a/src/Static/StaticLogin.c b/src/Static/StaticLogin.c index f59d7c0..81c1492 100644 --- a/src/Static/StaticLogin.c +++ b/src/Static/StaticLogin.c @@ -25,121 +25,121 @@ #include void -StaticLogin(FILE * stream) +StaticLogin(Stream * stream) { HtmlBegin(stream, "Log In"); - fprintf(stream, - "
" - "
" - "" - "" - "" - "" - "
" - "" - "
"); + StreamPuts(stream, + "
" + "
" + "" + "" + "" + "" + "
" + "" + "
"); HtmlBeginStyle(stream); - fprintf(stream, - "#error-msg {" - " display: none;" - " color: red;" - " text-align: center;" - " font-weight: bold;" - " font-size: larger;" - "}"); + StreamPuts(stream, + "#error-msg {" + " display: none;" + " color: red;" + " text-align: center;" + " font-weight: bold;" + " font-size: larger;" + "}"); HtmlEndStyle(stream); - fprintf(stream, - "

" - "
" + StreamPuts(stream, + "

" + "
" ); HtmlBeginJs(stream); - fprintf(stream, - "function findGetParameter(parameterName) {" - " var result = null;" - " var tmp = [];" - " var items = location.search.substr(1).split(\"&\");" - " for (var index = 0; index < items.length; index++) {" - " tmp = items[index].split(\"=\");" - " if (tmp[0] === parameterName) result = decodeURIComponent(tmp[1]);" - " }" - " return result;" - "}" - "function setError(msg) {" - " var err = document.getElementById('error-msg');" - " if (msg) {" - " err.style.display = 'block';" - " err.innerHTML = msg;" - " } else {" - " err.style.display = 'none';" - " }" - "}" + StreamPuts(stream, + "function findGetParameter(parameterName) {" + " var result = null;" + " var tmp = [];" + " var items = location.search.substr(1).split(\"&\");" + " for (var index = 0; index < items.length; index++) {" + " tmp = items[index].split(\"=\");" + " if (tmp[0] === parameterName) result = decodeURIComponent(tmp[1]);" + " }" + " return result;" + "}" + "function setError(msg) {" + " var err = document.getElementById('error-msg');" + " if (msg) {" + " err.style.display = 'block';" + " err.innerHTML = msg;" + " } else {" + " err.style.display = 'none';" + " }" + "}" ); - fprintf(stream, - "function buildRequest(user, pass) {" - " var d = findGetParameter('device_id');" + StreamPuts(stream, + "function buildRequest(user, pass) {" + " var d = findGetParameter('device_id');" " var i = findGetParameter('initial_device_display_name');" - " var r = findGetParameter('refresh_token') === 'true';" - " var request = {};" - " request['type'] = 'm.login.password';" - " request['identifier'] = {" - " type: 'm.id.user'," - " user: user" - " };" - " request['password'] = pass;" - " if (d) request['device_id'] = d;" - " if (i) request['initial_device_display_name'] = i;" - " if (r) request['refresh_token'] = r;" - " return request;" - "}" + " var r = findGetParameter('refresh_token') === 'true';" + " var request = {};" + " request['type'] = 'm.login.password';" + " request['identifier'] = {" + " type: 'm.id.user'," + " user: user" + " };" + " request['password'] = pass;" + " if (d) request['device_id'] = d;" + " if (i) request['initial_device_display_name'] = i;" + " if (r) request['refresh_token'] = r;" + " return request;" + "}" ); - fprintf(stream, - "function processResponse(xhr) {" - " if (xhr.readyState == 4) {" - " var r = JSON.parse(xhr.responseText);" - " if (!r.error) {" - " if (window.onLogin) {" - " window.onLogin(r);" - " } else {" - " setError('Client malfunction. Your client should have defined window.onLogin()');" - " }" - " } else {" - " setError(r.errcode + ': ' + r.error);" - " }" - " }" - "}" + StreamPuts(stream, + "function processResponse(xhr) {" + " if (xhr.readyState == 4) {" + " var r = JSON.parse(xhr.responseText);" + " if (!r.error) {" + " if (window.onLogin) {" + " window.onLogin(r);" + " } else {" + " setError('Client malfunction. Your client should have defined window.onLogin()');" + " }" + " } else {" + " setError(r.errcode + ': ' + r.error);" + " }" + " }" + "}" ); - fprintf(stream, - "function sendRequest(request) {" - " var xhr = new XMLHttpRequest();" - " xhr.open('POST', '/_matrix/client/v3/login');" + StreamPuts(stream, + "function sendRequest(request) {" + " var xhr = new XMLHttpRequest();" + " xhr.open('POST', '/_matrix/client/v3/login');" " xhr.setRequestHeader('Content-Type', 'application/json');" - " xhr.onreadystatechange = () => processResponse(xhr);" - " xhr.send(JSON.stringify(request));" - "}" + " xhr.onreadystatechange = () => processResponse(xhr);" + " xhr.send(JSON.stringify(request));" + "}" ); - fprintf(stream, - "window.addEventListener('load', () => {" - " document.getElementById('login-form').addEventListener('submit', (e) => {" - " e.preventDefault();" - " var user = document.getElementById('user').value;" - " var pass = document.getElementById('password').value;" - " if (!user || !pass) {" + StreamPuts(stream, + "window.addEventListener('load', () => {" + " document.getElementById('login-form').addEventListener('submit', (e) => {" + " e.preventDefault();" + " var user = document.getElementById('user').value;" + " var pass = document.getElementById('password').value;" + " if (!user || !pass) {" " setError('Please provide a username and password.');" - " return;" - " }" - " setError(null);" - " var request = buildRequest(user, pass);" - " sendRequest(request);" - " });" - "});" + " return;" + " }" + " setError(null);" + " var request = buildRequest(user, pass);" + " sendRequest(request);" + " });" + "});" ); HtmlEndJs(stream); diff --git a/src/Stream.c b/src/Stream.c index adc744b..c87c117 100644 --- a/src/Stream.c +++ b/src/Stream.c @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -42,23 +43,26 @@ #define STREAM_EOF (1 << 0) #define STREAM_ERR (1 << 1) +#define STREAM_TTY (1 << 2) struct Stream { Io *io; - int *rBuf; + char *rBuf; size_t rLen; size_t rOff; - int *wBuf; + char *wBuf; size_t wLen; - int *ugBuf; + char *ugBuf; size_t ugSize; size_t ugLen; - int flags:2; + int flags; + + int fd; }; Stream * @@ -79,6 +83,7 @@ StreamIo(Io * io) memset(stream, 0, sizeof(Stream)); stream->io = io; + stream->fd = -1; return stream; } @@ -87,26 +92,54 @@ Stream * StreamFd(int fd) { Io *io = IoFd(fd); + Stream *stream; if (!io) { return NULL; } - return StreamIo(io); + stream = StreamIo(io); + if (!stream) + { + return NULL; + } + + stream->fd = fd; + + if (isatty(stream->fd)) + { + stream->flags |= STREAM_TTY; + } + + return stream; } Stream * StreamFile(FILE * fp) { Io *io = IoFile(fp); + Stream *stream; if (!io) { return NULL; } - return StreamIo(io); + stream = StreamIo(io); + if (!stream) + { + return NULL; + } + + stream->fd = fileno(fp); + + if (isatty(stream->fd)) + { + stream->flags |= STREAM_TTY; + } + + return stream; } Stream * @@ -122,6 +155,45 @@ StreamOpen(const char *path, const char *mode) return StreamFile(fp); } +Stream * +StreamStdout(void) +{ + static Stream *stdOut = NULL; + + if (!stdOut) + { + stdOut = StreamFd(STDOUT_FILENO); + } + + return stdOut; +} + +Stream * +StreamStderr(void) +{ + static Stream *stdErr = NULL; + + if (!stdErr) + { + stdErr = StreamFd(STDERR_FILENO); + } + + return stdErr; +} + +Stream * +StreamStdin(void) +{ + static Stream *stdIn = NULL; + + if (!stdIn) + { + stdIn = StreamFd(STDIN_FILENO); + } + + return stdIn; +} + int StreamClose(Stream * stream) { @@ -171,8 +243,10 @@ StreamVprintf(Stream * stream, const char *fmt, va_list ap) * buffered. It therefore allows us to finish filling the buffer * and then only flush it when necessary, preventing superfluous * writes. */ - char *buf; - ssize_t len; + + char *buf = NULL; + size_t bufSize = 0; + FILE *fp; int ret; @@ -181,38 +255,22 @@ StreamVprintf(Stream * stream, const char *fmt, va_list ap) return -1; } - buf = Malloc(IO_BUFFER); - if (!buf) + fp = open_memstream(&buf, &bufSize); + if (!fp) { return -1; } - len = vsnprintf(buf, IO_BUFFER, fmt, ap); + ret = vfprintf(fp, fmt, ap); + fclose(fp); - if (len < 0) + if (ret >= 0) { - Free(buf); - return len; + ret = StreamPuts(stream, buf); } - if (len >= IO_BUFFER) - { - char *new = Realloc(buf, len + 1); - - if (!new) - { - Free(buf); - return -1; - } - - buf = new; - - vsnprintf(buf, len, fmt, ap); - } - - ret = StreamPuts(stream, buf); - - Free(buf); + free(buf); /* Allocated by stdlib, not Memory + * API */ return ret; } @@ -257,7 +315,7 @@ StreamGetc(Stream * stream) if (!stream->rBuf) { /* No buffer allocated yet */ - stream->rBuf = Malloc(IO_BUFFER * sizeof(int)); + stream->rBuf = Malloc(IO_BUFFER); if (!stream->rBuf) { stream->flags |= STREAM_ERR; @@ -319,7 +377,7 @@ StreamUngetc(Stream * stream, int c) if (stream->ugLen >= stream->ugSize) { - int *new; + char *new; stream->ugSize += IO_BUFFER; new = Realloc(stream->ugBuf, stream->ugSize); @@ -335,7 +393,7 @@ StreamUngetc(Stream * stream, int c) stream->ugBuf = new; } - stream->ugBuf[stream->ugLen - 1] = c; + stream->ugBuf[stream->ugLen] = c; stream->ugLen++; return c; @@ -352,7 +410,7 @@ StreamPutc(Stream * stream, int c) if (!stream->wBuf) { - stream->wBuf = Malloc(IO_BUFFER * sizeof(int)); + stream->wBuf = Malloc(IO_BUFFER); if (!stream->wBuf) { stream->flags |= STREAM_ERR; @@ -377,6 +435,24 @@ StreamPutc(Stream * stream, int c) stream->wBuf[stream->wLen] = c; stream->wLen++; + if (stream->flags & STREAM_TTY && c == '\n') + { + /* Newline encountered on a TTY; write now. This fixes some + * strange behavior on certain TTYs where a newline is written + * to the screen upon flush even when no newline exists in the + * stream. We just flush on newlines, but only if we're + * directly writing to a TTY. */ + ssize_t writeRes = IoWrite(stream->io, stream->wBuf, stream->wLen); + + if (writeRes == -1) + { + stream->flags |= STREAM_ERR; + return EOF; + } + + stream->wLen = 0; + } + return c; } @@ -405,6 +481,46 @@ StreamPuts(Stream * stream, char *str) return ret; } +char * +StreamGets(Stream * stream, char *str, int size) +{ + int i; + + if (!stream) + { + errno = EBADF; + return NULL; + } + + if (size <= 0) + { + errno = EINVAL; + return NULL; + } + + for (i = 0; i < size - 1; i++) + { + int c = StreamGetc(stream); + + if (StreamEof(stream) || StreamError(stream)) + { + break; + } + + str[i] = c; + + if (c == '\n') + { + i++; + break; + } + } + + str[i] = '\0'; + + return str; +} + int StreamEof(Stream * stream) { @@ -505,3 +621,9 @@ StreamCopy(Stream * in, Stream * out) StreamFlush(out); return nBytes; } + +int +StreamFileno(Stream * stream) +{ + return stream ? stream->fd : -1; +} diff --git a/src/Telodendria.c b/src/Telodendria.c index 12e0b2c..8a6f868 100644 --- a/src/Telodendria.c +++ b/src/Telodendria.c @@ -26,6 +26,8 @@ #include #include +#include + const char TelodendriaLogo[TELODENDRIA_LOGO_HEIGHT][TELODENDRIA_LOGO_WIDTH] = { " .= -=- ", @@ -62,21 +64,6 @@ const char "=======================================================" }; -void -TelodendriaHexDump(size_t off, char *hexBuf, char *asciiBuf, void *args) -{ - LogConfig *lc = args; - - if (hexBuf && asciiBuf) - { - Log(lc, LOG_DEBUG, "%04x: %s | %s |", off, hexBuf, asciiBuf); - } - else - { - Log(lc, LOG_DEBUG, "%04x", off); - } -} - void TelodendriaMemoryHook(MemoryAction a, MemoryInfo * i, void *args) { @@ -109,20 +96,78 @@ TelodendriaMemoryHook(MemoryAction a, MemoryInfo * i, void *args) MemoryInfoGetPointer(i)); } -void -TelodendriaMemoryIterator(MemoryInfo * i, void *args) +static void +HexDump(size_t off, char *hexBuf, char *asciiBuf, void *args) { - LogConfig *lc = (LogConfig *) args; + FILE *report = args; - /* We haven't freed the logger memory yet */ - if (MemoryInfoGetPointer(i) != lc) + if (hexBuf && asciiBuf) { - Log(lc, LOG_WARNING, "%s:%d: %lu bytes of memory at %p leaked.", + fprintf(report, "%04lx: %s | %s |\n", off, hexBuf, asciiBuf); + } + else + { + fprintf(report, "%04lx\n", off); + } +} + + +static void +MemoryIterator(MemoryInfo * i, void *args) +{ + FILE *report = args; + + fprintf(report, "%s:%d: %lu bytes at %p\n", MemoryInfoGetFile(i), MemoryInfoGetLine(i), MemoryInfoGetSize(i), MemoryInfoGetPointer(i)); - MemoryHexDump(i, TelodendriaHexDump, lc); + MemoryHexDump(i, HexDump, report); + + fprintf(report, "\n"); +} + +void +TelodendriaGenerateMemReport(void) +{ + static const char *reportName = "Memory.txt"; + + /* + * Use C standard IO instead of the Stream or Io APIs, because + * those use the Memory API, and that's exactly what we're trying + * to assess, so using it would generate false positives. None of + * this code should leak memory. + */ + FILE *report; + time_t currentTime; + struct tm *timeInfo; + char tsBuffer[1024]; + + if (!MemoryAllocated()) + { + /* No memory leaked, no need to write the report. This is the + * ideal situation; we only want the report to show up if leaks + * occurred. */ + return; } + + report = fopen(reportName, "a"); + if (!report) + { + return; + } + + currentTime = time(NULL); + timeInfo = localtime(¤tTime); + strftime(tsBuffer, sizeof(tsBuffer), "%c", timeInfo); + + fprintf(report, "---------- Telodendria Memory Report ----------\n"); + fprintf(report, "Date: %s\n", tsBuffer); + fprintf(report, "Total Bytes: %lu\n", MemoryAllocated()); + fprintf(report, "\n"); + + MemoryIterate(MemoryIterator, report); + + fclose(report); } void diff --git a/src/Util.c b/src/Util.c index 9bf20d9..b32e18b 100644 --- a/src/Util.c +++ b/src/Util.c @@ -219,7 +219,7 @@ UtilParseBytes(char *str) } ssize_t -UtilGetDelim(char **linePtr, size_t * n, int delim, FILE * stream) +UtilGetDelim(char **linePtr, size_t * n, int delim, Stream * stream) { char *curPos, *newLinePtr; size_t newLinePtrLen; @@ -246,9 +246,9 @@ UtilGetDelim(char **linePtr, size_t * n, int delim, FILE * stream) while (1) { - c = fgetc(stream); + c = StreamGetc(stream); - if (ferror(stream) || (c == EOF && curPos == *linePtr)) + if (StreamError(stream) || (c == EOF && curPos == *linePtr)) { return -1; } @@ -296,31 +296,31 @@ UtilGetDelim(char **linePtr, size_t * n, int delim, FILE * stream) } ssize_t -UtilGetLine(char **linePtr, size_t * n, FILE * stream) +UtilGetLine(char **linePtr, size_t * n, Stream * stream) { return UtilGetDelim(linePtr, n, '\n', stream); } size_t -UtilStreamCopy(FILE * in, FILE * out) +UtilStreamCopy(Stream * in, Stream * out) { size_t bytes = 0; int c; while (1) { - c = fgetc(in); + c = StreamGetc(in); - if (feof(in)) + if (StreamEof(in)) { break; } - if (ferror(in)) + if (StreamError(in)) { if (errno == EAGAIN) { - clearerr(in); + StreamClearError(in); continue; } else @@ -329,7 +329,7 @@ UtilStreamCopy(FILE * in, FILE * out) } } - fputc(c, out); + StreamPutc(out, c); bytes++; } diff --git a/src/include/CanonicalJson.h b/src/include/CanonicalJson.h index a37684e..a96e251 100644 --- a/src/include/CanonicalJson.h +++ b/src/include/CanonicalJson.h @@ -26,10 +26,12 @@ #define TELODENDRIA_CANONICALJSON_H #include + #include +#include extern int - CanonicalJsonEncode(HashMap *, FILE *); + CanonicalJsonEncode(HashMap *, Stream *); extern char * CanonicalJsonEncodeToString(HashMap *); diff --git a/src/include/Html.h b/src/include/Html.h index a5a6f1a..8919295 100644 --- a/src/include/Html.h +++ b/src/include/Html.h @@ -24,23 +24,23 @@ #ifndef TELODENDRIA_HTML_H #define TELODENDRIA_HTML_H -#include +#include -#define HtmlBeginJs(stream) fprintf(stream, \ +#define HtmlBeginJs(stream) StreamPuts(stream, \ "") +#define HtmlEndJs(stream) StreamPuts(stream, "\n// @license-end\n") -#define HtmlBeginStyle(stream) fprintf(stream, "") +#define HtmlBeginStyle(stream) StreamPuts(stream, "") extern void - HtmlBegin(FILE *, char *); + HtmlBegin(Stream *, char *); extern void - HtmlEnd(FILE *); + HtmlEnd(Stream *); #endif /* TELODENDRIA_HTML_H */ diff --git a/src/include/Http.h b/src/include/Http.h index 3a0036b..e307bfd 100644 --- a/src/include/Http.h +++ b/src/include/Http.h @@ -27,6 +27,7 @@ #include #include +#include typedef enum HttpRequestMethod { @@ -126,6 +127,6 @@ extern char * HttpParamEncode(HashMap *); extern HashMap * - HttpParseHeaders(FILE *); + HttpParseHeaders(Stream *); #endif diff --git a/src/include/HttpClient.h b/src/include/HttpClient.h index 6f4b180..8917098 100644 --- a/src/include/HttpClient.h +++ b/src/include/HttpClient.h @@ -49,7 +49,7 @@ extern HttpStatus extern HashMap * HttpResponseHeaders(HttpClientContext *); -extern FILE * +extern Stream * HttpClientStream(HttpClientContext *); extern void diff --git a/src/include/HttpServer.h b/src/include/HttpServer.h index 2ea382d..6703939 100644 --- a/src/include/HttpServer.h +++ b/src/include/HttpServer.h @@ -29,6 +29,7 @@ #include #include +#include typedef struct HttpServer HttpServer; @@ -68,7 +69,7 @@ extern char * extern void HttpResponseStatus(HttpServerContext *, HttpStatus); -extern FILE * +extern Stream * HttpServerStream(HttpServerContext *); extern void diff --git a/src/include/Io.h b/src/include/Io.h index af52903..5c6421c 100644 --- a/src/include/Io.h +++ b/src/include/Io.h @@ -27,6 +27,7 @@ #include #include #include +#include #ifndef IO_BUFFER #define IO_BUFFER 4096 diff --git a/src/include/Json.h b/src/include/Json.h index b34816b..dfedba5 100644 --- a/src/include/Json.h +++ b/src/include/Json.h @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -96,16 +97,16 @@ extern void JsonFree(HashMap *); extern void - JsonEncodeString(const char *, FILE *); + JsonEncodeString(const char *, Stream *); extern void - JsonEncodeValue(JsonValue * value, FILE * out, int); + JsonEncodeValue(JsonValue * value, Stream * out, int); extern int - JsonEncode(HashMap *, FILE *, int); + JsonEncode(HashMap *, Stream *, int); extern HashMap * - JsonDecode(FILE *); + JsonDecode(Stream *); extern JsonValue * JsonGet(HashMap *, size_t,...); diff --git a/src/include/Log.h b/src/include/Log.h index 190aefc..932e894 100644 --- a/src/include/Log.h +++ b/src/include/Log.h @@ -29,6 +29,8 @@ #include #include +#include + #define LOG_FLAG_COLOR (1 << 0) #define LOG_FLAG_SYSLOG (1 << 1) @@ -53,7 +55,7 @@ extern void LogConfigIndentSet(LogConfig *, size_t); extern void - LogConfigOutputSet(LogConfig *, FILE *); + LogConfigOutputSet(LogConfig *, Stream *); extern void LogConfigFlagSet(LogConfig *, int); diff --git a/src/include/Static.h b/src/include/Static.h index 3a4f13a..ccba81e 100644 --- a/src/include/Static.h +++ b/src/include/Static.h @@ -29,12 +29,12 @@ #include extern void - StaticItWorks(FILE *); + StaticItWorks(Stream *); extern void - StaticLogin(FILE *); + StaticLogin(Stream *); extern void - StaticError(FILE *, HttpStatus); + StaticError(Stream *, HttpStatus); #endif /* TELODENDRIA_STATIC_H */ diff --git a/src/include/Stream.h b/src/include/Stream.h index ecd2903..ec22ee3 100644 --- a/src/include/Stream.h +++ b/src/include/Stream.h @@ -42,6 +42,15 @@ extern Stream * extern Stream * StreamOpen(const char *, const char *); +extern Stream * + StreamStdout(void); + +extern Stream * + StreamStderr(void); + +extern Stream * + StreamStdin(void); + extern int StreamClose(Stream *); @@ -63,6 +72,9 @@ extern int extern int StreamPuts(Stream *, char *); +extern char * + StreamGets(Stream *, char *, int); + extern int StreamEof(Stream *); @@ -78,4 +90,7 @@ extern int extern ssize_t StreamCopy(Stream *, Stream *); +extern int + StreamFileno(Stream *); + #endif /* TELODENDRIA_STREAM_H */ diff --git a/src/include/Telodendria.h b/src/include/Telodendria.h index f4ce9f3..dde0a39 100644 --- a/src/include/Telodendria.h +++ b/src/include/Telodendria.h @@ -39,14 +39,11 @@ extern const char extern const char TelodendriaHeader[TELODENDRIA_HEADER_HEIGHT][TELODENDRIA_HEADER_WIDTH]; -extern void - TelodendriaHexDump(size_t, char *, char *, void *); - extern void TelodendriaMemoryHook(MemoryAction, MemoryInfo *, void *); extern void - TelodendriaMemoryIterator(MemoryInfo *, void *); + TelodendriaGenerateMemReport(void); extern void TelodendriaPrintHeader(LogConfig *); diff --git a/src/include/Util.h b/src/include/Util.h index 8006fbc..2e2a692 100644 --- a/src/include/Util.h +++ b/src/include/Util.h @@ -29,6 +29,8 @@ #include #include +#include + extern unsigned long UtilServerTs(void); @@ -45,12 +47,12 @@ extern size_t UtilParseBytes(char *); extern ssize_t - UtilGetDelim(char **, size_t *, int, FILE *); + UtilGetDelim(char **, size_t *, int, Stream *); extern ssize_t - UtilGetLine(char **, size_t *, FILE *); + UtilGetLine(char **, size_t *, Stream *); extern size_t - UtilStreamCopy(FILE *, FILE *); + UtilStreamCopy(Stream *, Stream *); #endif /* TELODENDRIA_UTIL_H */ diff --git a/tools/bin/td b/tools/bin/td index 9d7427a..bbfd642 100644 --- a/tools/bin/td +++ b/tools/bin/td @@ -39,7 +39,7 @@ MAIN="Main" if [ "$DEBUG" = "1" ]; then CFLAGS="$CFLAGS -O0 -g -pg" - LDFLAGS="-lm -pthread -v" + LDFLAGS="-lm -pthread" PROG="$PROG-debug" fi diff --git a/tools/bin/tt b/tools/bin/tt index 2af9ffa..5efa83e 100755 --- a/tools/bin/tt +++ b/tools/bin/tt @@ -8,25 +8,19 @@ ENDPOINT="$1" : "${METH:=GET}" -# Check if user is available. If it is, register it. -user_available=$(http "$BASE/_matrix/client/v3/register/available?username=$USERNAME" | json -s "available") -if [ "$user_available" = "true" ]; then - session=$(echo '{}' | http -X POST "$BASE/_matrix/client/v3/register" | json -s "session->@decode") - ( - printf '{' - printf ' "auth": {' - printf ' "type": "m.login.dummy",' - printf ' "session": %s' "$(json -e "$session")" - printf ' },' - printf ' "username": %s,' "$(json -e "$USERNAME")" - printf ' "password": %s,' "$PASSWORD" - printf ' "inhibit_login": true ' - printf '}' - ) | http -X POST -d @- "$BASE/_matrix/client/v3/register" > /dev/null -fi +register_payload() { + printf '{' + printf ' "auth": {' + printf ' "type": "m.login.dummy",' + printf ' "session": %s' "$(json -e "$session")" + printf ' },' + printf ' "username": %s,' "$(json -e "$USERNAME")" + printf ' "password": %s,' "$PASSWORD" + printf ' "inhibit_login": true ' + printf '}' +} -# Log in -RESPONSE=$(( +login_payload() { printf '{' printf ' "identifier": {' printf ' "type": "m.id.user",' @@ -35,7 +29,17 @@ RESPONSE=$(( printf ' "type": "m.login.password",' printf ' "password": %s' "$PASSWORD" printf '}' -) | http -X POST -d @- "$BASE/_matrix/client/v3/login") +} + +# Check if user is available. If it is, register it. +user_available=$(http "$BASE/_matrix/client/v3/register/available?username=$USERNAME" | json -s "available") +if [ "$user_available" = "true" ]; then + session=$(echo '{}' | http -X POST "$BASE/_matrix/client/v3/register" | json -s "session->@decode") + register_payload | http -X POST -d @- "$BASE/_matrix/client/v3/register" > /dev/null +fi + +# Log in +RESPONSE=$(login_payload | http -X POST -d @- "$BASE/_matrix/client/v3/login") ACCESS_TOKEN=$(echo "$RESPONSE" | json -s "access_token->@decode") @@ -53,4 +57,4 @@ fi # Log out http -X POST -H "Authorization: Bearer $ACCESS_TOKEN" \ - "$BASE/_matrix/client/v3/logout" > /dev/null + "$BASE/_matrix/client/v3/logout/all" > /dev/null diff --git a/tools/src/http-debug-server.c b/tools/src/http-debug-server.c index 550f8d0..109abbb 100644 --- a/tools/src/http-debug-server.c +++ b/tools/src/http-debug-server.c @@ -49,24 +49,24 @@ HttpHandle(HttpServerContext * cx, void *args) (void) args; - printf("%s %s\n", HttpRequestMethodToString(method), - HttpRequestPath(cx)); + StreamPrintf(StreamStdout(), "%s %s\n", HttpRequestMethodToString(method), + HttpRequestPath(cx)); while (HashMapIterate(headers, &key, (void **) &val)) { - printf("%s: %s\n", key, val); + StreamPrintf(StreamStdout(), "%s: %s\n", key, val); } - printf("\n"); + StreamPutc(StreamStdout(), '\n'); - bytes = UtilStreamCopy(HttpServerStream(cx), stdout); + bytes = UtilStreamCopy(HttpServerStream(cx), StreamStdout()); - printf("\n"); - printf("(%lu bytes)\n", bytes); + StreamPutc(StreamStdout(), '\n'); + StreamPrintf(StreamStdout(), "(%lu bytes)\n", bytes); HttpSendHeaders(cx); - fprintf(HttpServerStream(cx), "{}\n"); + StreamPuts(HttpServerStream(cx), "{}\n"); } int @@ -77,12 +77,12 @@ main(void) server = HttpServerCreate(8008, 1, 1, HttpHandle, NULL); if (!HttpServerStart(server)) { - fprintf(stderr, "Unable to start HTTP server.\n"); + StreamPuts(StreamStderr(), "Unable to start HTTP server.\n"); HttpServerFree(server); return 1; } - printf("Listening on port 8008.\n"); + StreamPuts(StreamStdout(), "Listening on port 8008.\n"); sa.sa_handler = SignalHandle; sigfillset(&sa.sa_mask); @@ -90,7 +90,7 @@ main(void) if (sigaction(SIGINT, &sa, NULL) < 0) { - fprintf(stderr, "Unable to install signal handler.\n"); + StreamPuts(StreamStderr(), "Unable to install signal handler.\n"); HttpServerStop(server); HttpServerJoin(server); HttpServerFree(server); @@ -99,7 +99,12 @@ main(void) HttpServerJoin(server); - printf("Shutting down.\n"); + StreamPuts(StreamStdout(), "Shutting down.\n"); HttpServerStop(server); + + StreamClose(StreamStdout()); + StreamClose(StreamStderr()); + StreamClose(StreamStdin()); + return 0; } diff --git a/tools/src/http.c b/tools/src/http.c index db41bcc..e46dc01 100644 --- a/tools/src/http.c +++ b/tools/src/http.c @@ -40,7 +40,7 @@ static void usage(char *prog) { - fprintf(stderr, "Usage: %s [-i -X method -H header -d data] url\n", prog); + StreamPrintf(StreamStderr(), "Usage: %s [-i -X method -H header -d data] url\n", prog); } int @@ -72,7 +72,7 @@ main(int argc, char **argv) method = HttpRequestMethodFromString(optarg); if (!method) { - fprintf(stderr, "Unknown request method: %s\n", optarg); + StreamPrintf(StreamStderr(), "Unknown request method: %s\n", optarg); return 1; } break; @@ -113,7 +113,7 @@ main(int argc, char **argv) uri = UriParse(argv[optind]); if (!uri) { - fprintf(stderr, "Failed to parse URI: %s\n", argv[optind]); + StreamPrintf(StreamStderr(), "Failed to parse URI: %s\n", argv[optind]); return 1; } @@ -131,7 +131,7 @@ main(int argc, char **argv) if (!uri->port) { - fprintf(stderr, "Unknown protocol: %s\n", uri->proto); + StreamPrintf(StreamStderr(), "Unknown protocol: %s\n", uri->proto); UriFree(uri); return 1; } @@ -145,7 +145,7 @@ main(int argc, char **argv) if (!cx) { - fprintf(stderr, "Failed to connect.\n"); + StreamPuts(StreamStderr(), "Failed to connect.\n"); UriFree(uri); return 1; } @@ -163,32 +163,32 @@ main(int argc, char **argv) { if (*data == '@') { - FILE *in; + Stream *in; data++; if (strcmp(data, "-") == 0) { - in = stdin; + in = StreamStdin(); } else { - in = fopen(data, "r"); + in = StreamOpen(data, "r"); } if (!in) { - fprintf(stderr, "%s: %s\n", data, strerror(errno)); + StreamPrintf(StreamStderr(), "%s: %s\n", data, strerror(errno)); return 1; } UtilStreamCopy(in, HttpClientStream(cx)); - fclose(in); + StreamClose(in); } else { - fprintf(HttpClientStream(cx), "%s", data); + StreamPuts(HttpClientStream(cx), data); } } @@ -196,7 +196,7 @@ main(int argc, char **argv) if (!res) { - fprintf(stderr, "Failed to send request.\n"); + StreamPuts(StreamStderr(), "Failed to send request.\n"); HttpClientContextFree(cx); UriFree(uri); return 1; @@ -206,20 +206,25 @@ main(int argc, char **argv) { HashMap *responseHeaders = HttpResponseHeaders(cx); - printf("HTTP/1.0 %d %s\n", res, HttpStatusToString(res)); + StreamPrintf(StreamStdout(), "HTTP/1.0 %d %s\n", res, HttpStatusToString(res)); while (HashMapIterate(responseHeaders, &key, (void **) &val)) { - printf("%s: %s\n", key, val); + StreamPrintf(StreamStdout(), "%s: %s\n", key, val); } - printf("\n"); + StreamPutc(StreamStdout(), '\n'); } - UtilStreamCopy(HttpClientStream(cx), stdout); + UtilStreamCopy(HttpClientStream(cx), StreamStdout()); + StreamClose(StreamStdout()); HttpClientContextFree(cx); UriFree(uri); + StreamClose(StreamStdout()); + StreamClose(StreamStderr()); + StreamClose(StreamStdin()); + return !(res == HTTP_OK); } diff --git a/tools/src/json.c b/tools/src/json.c index f52d1aa..64e4e1b 100644 --- a/tools/src/json.c +++ b/tools/src/json.c @@ -36,7 +36,7 @@ static void usage(char *prog) { - fprintf(stderr, "Usage: %s [-s query|-e str]\n", prog); + StreamPrintf(StreamStderr(), "Usage: %s [-s query|-e str]\n", prog); } static void @@ -89,7 +89,7 @@ query(char *select, HashMap * json) } else if (JsonValueType(val) == JSON_STRING && strcmp(keyName + 1, "decode") == 0) { - printf("%s\n", JsonValueAsString(val)); + StreamPrintf(StreamStdout(), "%s\n", JsonValueAsString(val)); val = NULL; break; } @@ -160,8 +160,8 @@ query(char *select, HashMap * json) if (val) { - JsonEncodeValue(val, stdout, JSON_PRETTY); - printf("\n"); + JsonEncodeValue(val, StreamStdout(), JSON_PRETTY); + StreamPutc(StreamStdout(), '\n'); } } @@ -170,9 +170,9 @@ encode(char *str) { JsonValue *val = JsonValueString(str); - JsonEncodeValue(val, stdout, JSON_DEFAULT); + JsonEncodeValue(val, StreamStdout(), JSON_DEFAULT); JsonValueFree(val); - printf("\n"); + StreamPutc(StreamStdout(), '\n'); } int @@ -203,11 +203,11 @@ main(int argc, char **argv) if (flag != FLAG_ENCODE) { - json = JsonDecode(stdin); + json = JsonDecode(StreamStdin()); if (!json) { - fprintf(stderr, "Malformed JSON.\n"); + StreamPuts(StreamStderr(), "Malformed JSON.\n"); return 1; } } @@ -221,11 +221,15 @@ main(int argc, char **argv) encode(input); break; default: - JsonEncode(json, stdout, JSON_PRETTY); - printf("\n"); + JsonEncode(json, StreamStdout(), JSON_PRETTY); + StreamPutc(StreamStdout(), '\n'); break; } + StreamClose(StreamStdout()); + StreamClose(StreamStderr()); + StreamClose(StreamStdin()); + MemoryFreeAll(); return 0; }