From 5289c16e2b30b7cc2d60ba834e8b2bc1dc61e532 Mon Sep 17 00:00:00 2001
From: Jordan Bancino
Date: Sat, 18 Mar 2023 14:32:09 +0000
Subject: [PATCH] Convert all code to new Stream API.
Also made a number of improvmements to tt, making it compatible with more
shells.
---
src/CanonicalJson.c | 18 ++--
src/Db.c | 54 +++++-----
src/Html.c | 134 +++++++++++------------
src/Http.c | 2 +-
src/HttpClient.c | 24 +++--
src/HttpServer.c | 42 ++++----
src/Io.c | 2 +-
src/Json.c | 106 ++++++++++---------
src/Log.c | 38 +++----
src/Main.c | 37 ++++---
src/Matrix.c | 4 +-
src/Routes/RouteStatic.c | 2 +-
src/Static/StaticError.c | 6 +-
src/Static/StaticItWorks.c | 36 +++----
src/Static/StaticLogin.c | 188 ++++++++++++++++----------------
src/Stream.c | 194 +++++++++++++++++++++++++++-------
src/Telodendria.c | 89 ++++++++++++----
src/Util.c | 20 ++--
src/include/CanonicalJson.h | 4 +-
src/include/Html.h | 14 +--
src/include/Http.h | 3 +-
src/include/HttpClient.h | 2 +-
src/include/HttpServer.h | 3 +-
src/include/Io.h | 1 +
src/include/Json.h | 9 +-
src/include/Log.h | 4 +-
src/include/Static.h | 6 +-
src/include/Stream.h | 15 +++
src/include/Telodendria.h | 5 +-
src/include/Util.h | 8 +-
tools/bin/td | 2 +-
tools/bin/tt | 44 ++++----
tools/src/http-debug-server.c | 29 ++---
tools/src/http.c | 37 ++++---
tools/src/json.c | 24 +++--
35 files changed, 714 insertions(+), 492 deletions(-)
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,
- ""
);
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;
}