Convert all code to new Stream API.

Also made a number of improvmements to tt, making it compatible with more
shells.
This commit is contained in:
Jordan Bancino 2023-03-18 14:32:09 +00:00
parent a97a593f21
commit 5289c16e2b
35 changed files with 714 additions and 492 deletions

View file

@ -37,7 +37,7 @@ CanonicalJsonKeyCompare(void *k1, void *k2)
} }
static void static void
CanonicalJsonEncodeValue(JsonValue * value, FILE * out) CanonicalJsonEncodeValue(JsonValue * value, Stream * out)
{ {
Array *arr; Array *arr;
size_t i, len; size_t i, len;
@ -52,7 +52,7 @@ CanonicalJsonEncodeValue(JsonValue * value, FILE * out)
arr = JsonValueAsArray(value); arr = JsonValueAsArray(value);
len = ArraySize(arr); len = ArraySize(arr);
fputc('[', out); StreamPutc(out, '[');
for (i = 0; i < len; i++) for (i = 0; i < len; i++)
{ {
@ -67,11 +67,11 @@ CanonicalJsonEncodeValue(JsonValue * value, FILE * out)
CanonicalJsonEncodeValue(aVal, out); CanonicalJsonEncodeValue(aVal, out);
if (i < len - 1) if (i < len - 1)
{ {
fputc(',', out); StreamPutc(out, ',');
} }
} }
fputc(']', out); StreamPutc(out, ']');
break; break;
default: default:
JsonEncodeValue(value, out, JSON_DEFAULT); JsonEncodeValue(value, out, JSON_DEFAULT);
@ -80,7 +80,7 @@ CanonicalJsonEncodeValue(JsonValue * value, FILE * out)
} }
int int
CanonicalJsonEncode(HashMap * object, FILE * out) CanonicalJsonEncode(HashMap * object, Stream * out)
{ {
char *key; char *key;
JsonValue *value; JsonValue *value;
@ -106,7 +106,7 @@ CanonicalJsonEncode(HashMap * object, FILE * out)
ArraySort(keys, CanonicalJsonKeyCompare); ArraySort(keys, CanonicalJsonKeyCompare);
fputc('{', out); StreamPutc(out, '{');
keyCount = ArraySize(keys); keyCount = ArraySize(keys);
for (i = 0; i < keyCount; i++) for (i = 0; i < keyCount; i++)
@ -129,16 +129,16 @@ CanonicalJsonEncode(HashMap * object, FILE * out)
} }
JsonEncodeString(key, out); JsonEncodeString(key, out);
fputc(':', out); StreamPutc(out, ':');
CanonicalJsonEncodeValue(value, out); CanonicalJsonEncodeValue(value, out);
if (i < keyCount - 1) if (i < keyCount - 1)
{ {
fputc(',', out); StreamPutc(out, ',');
} }
} }
fputc('}', out); StreamPutc(out, '}');
ArrayFree(keys); ArrayFree(keys);
return 1; return 1;

View file

@ -27,6 +27,7 @@
#include <Json.h> #include <Json.h>
#include <Util.h> #include <Util.h>
#include <Str.h> #include <Str.h>
#include <Stream.h>
#include <sys/types.h> #include <sys/types.h>
#include <dirent.h> #include <dirent.h>
@ -83,7 +84,8 @@ struct DbRef
DbRef *prev; DbRef *prev;
DbRef *next; DbRef *next;
FILE *fp; int fd;
Stream *stream;
}; };
static void static void
@ -394,9 +396,11 @@ DbLockFromArr(Db * db, Array * args)
char *file; char *file;
char *hash; char *hash;
DbRef *ref; DbRef *ref;
FILE *fp;
struct flock lock; struct flock lock;
int fd;
Stream *stream;
if (!db || !args) if (!db || !args)
{ {
return NULL; return NULL;
@ -412,9 +416,8 @@ DbLockFromArr(Db * db, Array * args)
ref = HashMapGet(db->cache, hash); ref = HashMapGet(db->cache, hash);
file = DbFileName(db, args); file = DbFileName(db, args);
/* Open the file for reading and writing so we can lock it */ fd = open(file, O_RDWR);
fp = fopen(file, "r+"); if (fd == -1)
if (!fp)
{ {
if (ref) if (ref)
{ {
@ -452,15 +455,17 @@ DbLockFromArr(Db * db, Array * args)
goto finish; goto finish;
} }
stream = StreamFd(fd);
lock.l_start = 0; lock.l_start = 0;
lock.l_len = 0; lock.l_len = 0;
lock.l_type = F_WRLCK; lock.l_type = F_WRLCK;
lock.l_whence = SEEK_SET; lock.l_whence = SEEK_SET;
/* Lock the file on the disk */ /* 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; ref = NULL;
goto finish; goto finish;
} }
@ -470,17 +475,16 @@ DbLockFromArr(Db * db, Array * args)
unsigned long diskTs = UtilLastModified(file); unsigned long diskTs = UtilLastModified(file);
pthread_mutex_lock(&ref->lock); pthread_mutex_lock(&ref->lock);
ref->fp = fp;
if (diskTs > ref->ts) if (diskTs > ref->ts)
{ {
/* File was modified on disk since it was cached */ /* File was modified on disk since it was cached */
HashMap *json = JsonDecode(fp); HashMap *json = JsonDecode(ref->stream);
if (!json) if (!json)
{ {
pthread_mutex_unlock(&ref->lock); pthread_mutex_unlock(&ref->lock);
fclose(fp); StreamClose(ref->stream);
ref = NULL; ref = NULL;
goto finish; goto finish;
} }
@ -489,7 +493,6 @@ DbLockFromArr(Db * db, Array * args)
ref->json = json; ref->json = json;
ref->ts = diskTs; ref->ts = diskTs;
ref->size = DbComputeSize(ref->json); ref->size = DbComputeSize(ref->json);
} }
/* Float this ref to mostRecent */ /* Float this ref to mostRecent */
@ -525,25 +528,26 @@ DbLockFromArr(Db * db, Array * args)
ref = Malloc(sizeof(DbRef)); ref = Malloc(sizeof(DbRef));
if (!ref) if (!ref)
{ {
fclose(fp); StreamClose(stream);
goto finish; goto finish;
} }
ref->json = JsonDecode(fp); ref->json = JsonDecode(stream);
ref->fp = fp;
if (!ref->json) if (!ref->json)
{ {
Free(ref); Free(ref);
fclose(fp); StreamClose(stream);
ref = NULL; ref = NULL;
goto finish; goto finish;
} }
ref->fd = fd;
ref->stream = stream;
pthread_mutex_init(&ref->lock, NULL); pthread_mutex_init(&ref->lock, NULL);
pthread_mutex_lock(&ref->lock); pthread_mutex_lock(&ref->lock);
for (i = 0; i < ArraySize(args); i++) for (i = 0; i < ArraySize(args); i++)
{ {
ArrayAdd(name, StrDuplicate(ArrayGet(args, i))); ArrayAdd(name, StrDuplicate(ArrayGet(args, i)));
@ -579,7 +583,7 @@ finish:
DbRef * DbRef *
DbCreate(Db * db, size_t nArgs,...) DbCreate(Db * db, size_t nArgs,...)
{ {
FILE *fp; Stream *fp;
char *file; char *file;
char *dir; char *dir;
va_list ap; va_list ap;
@ -620,7 +624,7 @@ DbCreate(Db * db, size_t nArgs,...)
Free(dir); Free(dir);
fp = fopen(file, "w"); fp = StreamOpen(file, "w");
Free(file); Free(file);
if (!fp) if (!fp)
{ {
@ -628,9 +632,8 @@ DbCreate(Db * db, size_t nArgs,...)
return NULL; return NULL;
} }
fprintf(fp, "{}"); StreamPuts(fp, "{}");
fflush(fp); StreamClose(fp);
fclose(fp);
ret = DbLockFromArr(db, args); ret = DbLockFromArr(db, args);
@ -744,17 +747,16 @@ DbUnlock(Db * db, DbRef * ref)
pthread_mutex_lock(&db->lock); pthread_mutex_lock(&db->lock);
rewind(ref->fp); lseek(ref->fd, 0L, SEEK_SET);
if (ftruncate(fileno(ref->fp), 0) < 0) if (ftruncate(ref->fd, 0) < 0)
{ {
pthread_mutex_unlock(&db->lock); pthread_mutex_unlock(&db->lock);
return 0; return 0;
} }
JsonEncode(ref->json, ref->fp, JSON_DEFAULT); JsonEncode(ref->json, ref->stream, JSON_DEFAULT);
fflush(ref->fp); StreamClose(ref->stream);
fclose(ref->fp);
if (db->cache) if (db->cache)
{ {

View file

@ -28,7 +28,7 @@
#include <Telodendria.h> #include <Telodendria.h>
void void
HtmlBegin(FILE * stream, char *title) HtmlBegin(Stream * stream, char *title)
{ {
size_t i; size_t i;
@ -37,87 +37,87 @@ HtmlBegin(FILE * stream, char *title)
return; return;
} }
fprintf(stream, StreamPrintf(stream,
"<!DOCTYPE html>" "<!DOCTYPE html>"
"<html>" "<html>"
"<head>" "<head>"
"<meta charset=\"utf-8\">" "<meta charset=\"utf-8\">"
"<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">" "<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">"
"<title>%s | Telodendria</title>" "<title>%s | Telodendria</title>"
,title ,title
); );
HtmlBeginStyle(stream); HtmlBeginStyle(stream);
fprintf(stream, StreamPuts(stream,
":root {" ":root {"
" color-scheme: dark;" " color-scheme: dark;"
" --accent: #7b8333;" " --accent: #7b8333;"
"}" "}"
"body {" "body {"
" margin: auto;" " margin: auto;"
" width: 100%%;" " width: 100%;"
" max-width: 8.5in;" " max-width: 8.5in;"
" padding: 0.25in;" " padding: 0.25in;"
" background-color: #0d1117;" " background-color: #0d1117;"
" color: white;" " color: white;"
"}" "}"
"a {" "a {"
" color: var(--accent);" " color: var(--accent);"
" text-decoration: none;" " text-decoration: none;"
"}" "}"
"h1 {" "h1 {"
" text-align: center;" " text-align: center;"
"}" "}"
".logo {" ".logo {"
" color: var(--accent);" " color: var(--accent);"
" text-align: center;" " text-align: center;"
" font-weight: bold;" " font-weight: bold;"
"}" "}"
); );
fprintf(stream, StreamPuts(stream,
".form {" ".form {"
" margin: auto;" " margin: auto;"
" width: 100%%;" " width: 100%;"
" max-width: 400px;" " max-width: 400px;"
" border-radius: 10px;" " border-radius: 10px;"
" border: 1px var(--accent) solid;" " border: 1px var(--accent) solid;"
" padding: 10px;" " padding: 10px;"
"}" "}"
"form {" "form {"
" display: block;" " display: block;"
"}" "}"
"form > input, label {" "form > input, label {"
" width: 95%%;" " width: 95%;"
" height: 25px;" " height: 25px;"
" display: block;" " display: block;"
" margin-bottom: 5px;" " margin-bottom: 5px;"
" margin-left: auto;" " margin-left: auto;"
" margin-right: auto;" " margin-right: auto;"
"}" "}"
); );
HtmlEndStyle(stream); HtmlEndStyle(stream);
fprintf(stream, StreamPuts(stream,
"</head>" "</head>"
"<body>" "<body>"
"<pre class=\"logo\">" "<pre class=\"logo\">"
); );
for (i = 0; i < TELODENDRIA_LOGO_HEIGHT; i++) for (i = 0; i < TELODENDRIA_LOGO_HEIGHT; i++)
{ {
fprintf(stream, "%s\n", TelodendriaLogo[i]); StreamPrintf(stream, "%s\n", TelodendriaLogo[i]);
} }
fprintf(stream, StreamPrintf(stream,
"</pre>" "</pre>"
"<h1>%s</h1>" "<h1>%s</h1>"
,title); ,title);
} }
void void
HtmlEnd(FILE * stream) HtmlEnd(Stream * stream)
{ {
fprintf(stream, StreamPuts(stream,
"</body>" "</body>"
"</html>"); "</html>");
} }

View file

@ -536,7 +536,7 @@ HttpParamEncode(HashMap * params)
} }
HashMap * HashMap *
HttpParseHeaders(FILE * fp) HttpParseHeaders(Stream * fp)
{ {
HashMap *headers; HashMap *headers;

View file

@ -40,7 +40,8 @@
struct HttpClientContext struct HttpClientContext
{ {
HashMap *responseHeaders; HashMap *responseHeaders;
FILE *stream; Stream *stream;
int sd;
}; };
HttpClientContext * HttpClientContext *
@ -126,7 +127,8 @@ HttpRequest(HttpRequestMethod method, int flags, unsigned short port, char *host
freeaddrinfo(res0); freeaddrinfo(res0);
context->stream = fdopen(sd, "r+"); context->sd = sd;
context->stream = StreamFd(sd);
if (!context->stream) if (!context->stream)
{ {
Free(context); Free(context);
@ -134,8 +136,8 @@ HttpRequest(HttpRequestMethod method, int flags, unsigned short port, char *host
return NULL; return NULL;
} }
fprintf(context->stream, "%s %s HTTP/1.1\r\n", StreamPrintf(context->stream, "%s %s HTTP/1.1\r\n",
HttpRequestMethodToString(method), path); HttpRequestMethodToString(method), path);
HttpRequestHeader(context, "Connection", "close"); HttpRequestHeader(context, "Connection", "close");
HttpRequestHeader(context, "User-Agent", "Telodendria/" TELODENDRIA_VERSION); HttpRequestHeader(context, "User-Agent", "Telodendria/" TELODENDRIA_VERSION);
@ -152,7 +154,7 @@ HttpRequestHeader(HttpClientContext * context, char *key, char *val)
return; return;
} }
fprintf(context->stream, "%s: %s\r\n", key, val); StreamPrintf(context->stream, "%s: %s\r\n", key, val);
} }
void void
@ -163,8 +165,8 @@ HttpRequestSendHeaders(HttpClientContext * context)
return; return;
} }
fprintf(context->stream, "\r\n"); StreamPuts(context->stream, "\r\n");
fflush(context->stream); StreamFlush(context->stream);
} }
HttpStatus HttpStatus
@ -182,8 +184,8 @@ HttpRequestSend(HttpClientContext * context)
return 0; return 0;
} }
fflush(context->stream); StreamFlush(context->stream);
shutdown(fileno(context->stream), SHUT_WR); shutdown(context->sd, SHUT_WR);
lineLen = UtilGetLine(&line, &lineSize, context->stream); lineLen = UtilGetLine(&line, &lineSize, context->stream);
@ -238,7 +240,7 @@ HttpResponseHeaders(HttpClientContext * context)
return context->responseHeaders; return context->responseHeaders;
} }
FILE * Stream *
HttpClientStream(HttpClientContext * context) HttpClientStream(HttpClientContext * context)
{ {
if (!context) if (!context)
@ -267,6 +269,6 @@ HttpClientContextFree(HttpClientContext * context)
HashMapFree(context->responseHeaders); HashMapFree(context->responseHeaders);
fclose(context->stream); StreamClose(context->stream);
Free(context); Free(context);
} }

View file

@ -74,7 +74,7 @@ struct HttpServerContext
HashMap *responseHeaders; HashMap *responseHeaders;
HttpStatus responseStatus; HttpStatus responseStatus;
FILE *stream; Stream *stream;
}; };
typedef struct HttpServerWorkerThreadArgs typedef struct HttpServerWorkerThreadArgs
@ -86,7 +86,7 @@ typedef struct HttpServerWorkerThreadArgs
static HttpServerContext * static HttpServerContext *
HttpServerContextCreate(HttpRequestMethod requestMethod, HttpServerContextCreate(HttpRequestMethod requestMethod,
char *requestPath, HashMap * requestParams, FILE * stream) char *requestPath, HashMap * requestParams, Stream * stream)
{ {
HttpServerContext *c; HttpServerContext *c;
@ -158,7 +158,7 @@ HttpServerContextFree(HttpServerContext * c)
HashMapFree(c->requestParams); HashMapFree(c->requestParams);
Free(c->requestPath); Free(c->requestPath);
fclose(c->stream); StreamClose(c->stream);
Free(c); Free(c);
} }
@ -229,7 +229,7 @@ HttpResponseStatus(HttpServerContext * c, HttpStatus status)
c->responseStatus = status; c->responseStatus = status;
} }
FILE * Stream *
HttpServerStream(HttpServerContext * c) HttpServerStream(HttpServerContext * c)
{ {
if (!c) if (!c)
@ -243,25 +243,25 @@ HttpServerStream(HttpServerContext * c)
void void
HttpSendHeaders(HttpServerContext * c) HttpSendHeaders(HttpServerContext * c)
{ {
FILE *fp = c->stream; Stream *fp = c->stream;
char *key; char *key;
char *val; 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)) 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) DequeueConnection(HttpServer * server)
{ {
FILE *fp; Stream *fp;
if (!server) if (!server)
{ {
@ -409,7 +409,7 @@ HttpServerWorkerThread(void *args)
while (!server->stop) while (!server->stop)
{ {
FILE *fp; Stream *fp;
HttpServerContext *context; HttpServerContext *context;
char *line = NULL; char *line = NULL;
@ -450,7 +450,7 @@ HttpServerWorkerThread(void *args)
while ((lineLen = UtilGetLine(&line, &lineSize, fp)) == -1 while ((lineLen = UtilGetLine(&line, &lineSize, fp)) == -1
&& errno == EAGAIN) && errno == EAGAIN)
{ {
clearerr(fp); StreamClearError(fp);
/* If the server is stopped, or it's been a while, just /* If the server is stopped, or it's been a while, just
* give up so we aren't wasting a thread on this client. */ * give up so we aren't wasting a thread on this client. */
@ -541,24 +541,24 @@ HttpServerWorkerThread(void *args)
HttpServerContextFree(context); HttpServerContextFree(context);
fp = NULL; /* The above call will close this fp = NULL; /* The above call will close this
* FILE */ * Stream */
goto finish; goto finish;
internal_error: internal_error:
fprintf(fp, "HTTP/1.0 500 Internal Server Error\n"); StreamPuts(fp, "HTTP/1.0 500 Internal Server Error\n");
fprintf(fp, "Connection: close\n"); StreamPuts(fp, "Connection: close\n");
goto finish; goto finish;
bad_request: bad_request:
fprintf(fp, "HTTP/1.0 400 Bad Request\n"); StreamPuts(fp, "HTTP/1.0 400 Bad Request\n");
fprintf(fp, "Connection: close\n"); StreamPuts(fp, "Connection: close\n");
goto finish; goto finish;
finish: finish:
Free(line); Free(line);
if (fp) if (fp)
{ {
fclose(fp); StreamClose(fp);
} }
} }
@ -570,7 +570,7 @@ HttpServerEventThread(void *args)
{ {
HttpServer *server = (HttpServer *) args; HttpServer *server = (HttpServer *) args;
struct pollfd pollFds[1]; struct pollfd pollFds[1];
FILE *fp; Stream *fp;
size_t i; size_t i;
server->isRunning = 1; server->isRunning = 1;
@ -632,7 +632,7 @@ HttpServerEventThread(void *args)
continue; continue;
} }
fp = fdopen(connFd, "r+"); fp = StreamFd(connFd);
if (!fp) if (!fp)
{ {
pthread_mutex_unlock(&server->connQueueMutex); pthread_mutex_unlock(&server->connQueueMutex);
@ -655,7 +655,7 @@ HttpServerEventThread(void *args)
while ((fp = DequeueConnection(server))) while ((fp = DequeueConnection(server)))
{ {
fclose(fp); StreamClose(fp);
} }
server->isRunning = 0; server->isRunning = 0;

View file

@ -134,7 +134,7 @@ int
IoVprintf(Io * io, const char *fmt, va_list ap) IoVprintf(Io * io, const char *fmt, va_list ap)
{ {
char *buf; char *buf;
size_t write; int write;
int ret; int ret;

View file

@ -67,7 +67,7 @@ typedef enum JsonToken
typedef struct JsonParserState typedef struct JsonParserState
{ {
FILE *stream; Stream *stream;
JsonToken tokenType; JsonToken tokenType;
char *token; char *token;
@ -334,12 +334,12 @@ JsonValueFree(JsonValue * value)
} }
void void
JsonEncodeString(const char *str, FILE * out) JsonEncodeString(const char *str, Stream * out)
{ {
size_t i; size_t i;
char c; char c;
fputc('"', out); StreamPutc(out, '"');
i = 0; i = 0;
while ((c = str[i]) != '\0') while ((c = str[i]) != '\0')
@ -349,23 +349,23 @@ JsonEncodeString(const char *str, FILE * out)
case '\\': case '\\':
case '"': case '"':
case '/': case '/':
fputc('\\', out); StreamPutc(out, '\\');
fputc(c, out); StreamPutc(out, c);
break; break;
case '\b': case '\b':
fputs("\\b", out); StreamPuts(out, "\\b");
break; break;
case '\t': case '\t':
fputs("\\t", out); StreamPuts(out, "\\t");
break; break;
case '\n': case '\n':
fputs("\\n", out); StreamPuts(out, "\\n");
break; break;
case '\f': case '\f':
fputs("\\f", out); StreamPuts(out, "\\f");
break; break;
case '\r': case '\r':
fputs("\\r", out); StreamPuts(out, "\\r");
break; break;
default: /* Assume UTF-8 input */ default: /* Assume UTF-8 input */
/* /*
@ -381,22 +381,22 @@ JsonEncodeString(const char *str, FILE * out)
*/ */
if (c <= 0x001F) if (c <= 0x001F)
{ {
fprintf(out, "\\u%04x", c); StreamPrintf(out, "\\u%04x", c);
} }
else else
{ {
fputc(c, out); StreamPutc(out, c);
} }
break; break;
} }
i++; i++;
} }
fputc('"', out); StreamPutc(out, '"');
} }
static char * static char *
JsonDecodeString(FILE * in) JsonDecodeString(Stream * in)
{ {
const size_t strBlockSize = 16; const size_t strBlockSize = 16;
@ -419,7 +419,7 @@ JsonDecodeString(FILE * in)
return NULL; return NULL;
} }
while ((c = fgetc(in)) != EOF) while ((c = StreamGetc(in)) != EOF)
{ {
if (c <= 0x001F) if (c <= 0x001F)
{ {
@ -449,7 +449,7 @@ JsonDecodeString(FILE * in)
return str; return str;
break; break;
case '\\': case '\\':
c = fgetc(in); c = StreamGetc(in);
switch (c) switch (c)
{ {
case '\\': case '\\':
@ -479,8 +479,14 @@ JsonDecodeString(FILE * in)
a[1] = '\0'; a[1] = '\0';
break; break;
case 'u': case 'u':
/* Read \uXXXX point into a 4-byte buffer */ /* Read 4 characters into a */
if (fscanf(in, "%04lx", &utf8) != 1) 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 */ /* Bad hex value */
Free(str); Free(str);
@ -562,7 +568,7 @@ JsonDecodeString(FILE * in)
} }
void void
JsonEncodeValue(JsonValue * value, FILE * out, int level) JsonEncodeValue(JsonValue * value, Stream * out, int level)
{ {
size_t i; size_t i;
size_t len; size_t len;
@ -577,47 +583,47 @@ JsonEncodeValue(JsonValue * value, FILE * out, int level)
arr = value->as.array; arr = value->as.array;
len = ArraySize(arr); len = ArraySize(arr);
fputc('[', out); StreamPutc(out, '[');
for (i = 0; i < len; i++) for (i = 0; i < len; i++)
{ {
if (level >= 0) 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); JsonEncodeValue(ArrayGet(arr, i), out, level >= 0 ? level + 2 : level);
if (i < len - 1) if (i < len - 1)
{ {
fputc(',', out); StreamPutc(out, ',');
} }
} }
if (level >= 0) if (level >= 0)
{ {
fprintf(out, "\n%*s", level, ""); StreamPrintf(out, "\n%*s", level, "");
} }
fputc(']', out); StreamPutc(out, ']');
break; break;
case JSON_STRING: case JSON_STRING:
JsonEncodeString(value->as.string, out); JsonEncodeString(value->as.string, out);
break; break;
case JSON_INTEGER: case JSON_INTEGER:
fprintf(out, "%ld", value->as.integer); StreamPrintf(out, "%ld", value->as.integer);
break; break;
case JSON_FLOAT: case JSON_FLOAT:
fprintf(out, "%f", value->as.floating); StreamPrintf(out, "%f", value->as.floating);
break; break;
case JSON_BOOLEAN: case JSON_BOOLEAN:
if (value->as.boolean) if (value->as.boolean)
{ {
fputs("true", out); StreamPuts(out, "true");
} }
else else
{ {
fputs("false", out); StreamPuts(out, "false");
} }
break; break;
case JSON_NULL: case JSON_NULL:
fputs("null", out); StreamPuts(out, "null");
break; break;
default: default:
return; return;
@ -625,7 +631,7 @@ JsonEncodeValue(JsonValue * value, FILE * out, int level)
} }
int int
JsonEncode(HashMap * object, FILE * out, int level) JsonEncode(HashMap * object, Stream * out, int level)
{ {
size_t index; size_t index;
size_t count; size_t count;
@ -643,10 +649,10 @@ JsonEncode(HashMap * object, FILE * out, int level)
count++; count++;
} }
fputc('{', out); StreamPutc(out, '{');
if (level >= 0) if (level >= 0)
{ {
fputc('\n', out); StreamPutc(out, '\n');
} }
index = 0; index = 0;
@ -654,27 +660,27 @@ JsonEncode(HashMap * object, FILE * out, int level)
{ {
if (level >= 0) if (level >= 0)
{ {
fprintf(out, "%*s", level + 2, ""); StreamPrintf(out, "%*s", level + 2, "");
} }
JsonEncodeString(key, out); JsonEncodeString(key, out);
fputc(':', out); StreamPutc(out, ':');
if (level >= 0) if (level >= 0)
{ {
fputc(' ', out); StreamPutc(out, ' ');
} }
JsonEncodeValue(value, out, level >= 0 ? level + 2 : level); JsonEncodeValue(value, out, level >= 0 ? level + 2 : level);
if (index < count - 1) if (index < count - 1)
{ {
fputc(',', out); StreamPutc(out, ',');
} }
if (level >= 0) if (level >= 0)
{ {
fputc('\n', out); StreamPutc(out, '\n');
} }
index++; index++;
@ -682,9 +688,9 @@ JsonEncode(HashMap * object, FILE * out, int level)
if (level >= 0) if (level >= 0)
{ {
fprintf(out, "%*s", level, ""); StreamPrintf(out, "%*s", level, "");
} }
fputc('}', out); StreamPutc(out, '}');
return 1; return 1;
} }
@ -715,18 +721,18 @@ JsonConsumeWhitespace(JsonParserState * state)
while (1) while (1)
{ {
c = fgetc(state->stream); c = StreamGetc(state->stream);
if (feof(state->stream)) if (StreamEof(state->stream))
{ {
break; break;
} }
if (ferror(state->stream)) if (StreamError(state->stream))
{ {
if (errno == EAGAIN) if (errno == EAGAIN)
{ {
clearerr(state->stream); StreamClearError(state->stream);
tries++; tries++;
if (tries >= nRetries || readFlg) if (tries >= nRetries || readFlg)
@ -765,7 +771,7 @@ JsonTokenSeek(JsonParserState * state)
{ {
int c = JsonConsumeWhitespace(state); int c = JsonConsumeWhitespace(state);
if (feof(state->stream)) if (StreamEof(state->stream))
{ {
state->tokenType = TOKEN_EOF; state->tokenType = TOKEN_EOF;
return; return;
@ -822,7 +828,7 @@ JsonTokenSeek(JsonParserState * state)
} }
state->token[0] = c; state->token[0] = c;
while ((c = fgetc(state->stream)) != EOF) while ((c = StreamGetc(state->stream)) != EOF)
{ {
if (c == '.') if (c == '.')
{ {
@ -838,7 +844,7 @@ JsonTokenSeek(JsonParserState * state)
} }
else if (!isdigit(c)) else if (!isdigit(c))
{ {
ungetc(c, state->stream); StreamUngetc(state->stream, c);
break; break;
} }
@ -893,7 +899,7 @@ JsonTokenSeek(JsonParserState * state)
switch (c) switch (c)
{ {
case 't': case 't':
if (!fgets(state->token + 1, 4, state->stream)) if (!StreamGets(state->stream, state->token + 1, 4))
{ {
state->tokenType = TOKEN_EOF; state->tokenType = TOKEN_EOF;
Free(state->token); Free(state->token);
@ -914,7 +920,7 @@ JsonTokenSeek(JsonParserState * state)
} }
break; break;
case 'f': case 'f':
if (!fgets(state->token + 1, 5, state->stream)) if (!StreamGets(state->stream, state->token + 1, 5))
{ {
state->tokenType = TOKEN_EOF; state->tokenType = TOKEN_EOF;
Free(state->token); Free(state->token);
@ -935,7 +941,7 @@ JsonTokenSeek(JsonParserState * state)
} }
break; break;
case 'n': case 'n':
if (!fgets(state->token + 1, 4, state->stream)) if (!StreamGets(state->stream, state->token + 1, 4))
{ {
state->tokenType = TOKEN_EOF; state->tokenType = TOKEN_EOF;
Free(state->token); Free(state->token);
@ -1155,7 +1161,7 @@ error:
} }
HashMap * HashMap *
JsonDecode(FILE * stream) JsonDecode(Stream * stream)
{ {
HashMap *result; HashMap *result;

View file

@ -38,7 +38,7 @@ struct LogConfig
{ {
int level; int level;
size_t indent; size_t indent;
FILE *out; Stream *out;
int flags; int flags;
char *tsFmt; char *tsFmt;
@ -109,7 +109,7 @@ LogConfigFree(LogConfig * config)
return; return;
} }
fclose(config->out); StreamClose(config->out);
Free(config); Free(config);
} }
@ -165,7 +165,7 @@ LogConfigLevelSet(LogConfig * config, int level)
} }
void void
LogConfigOutputSet(LogConfig * config, FILE * out) LogConfigOutputSet(LogConfig * config, Stream * out)
{ {
if (!config) if (!config)
{ {
@ -178,7 +178,7 @@ LogConfigOutputSet(LogConfig * config, FILE * out)
} }
else 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) doColor = LogConfigFlagGet(config, LOG_FLAG_COLOR)
&& isatty(fileno(config->out)); && isatty(StreamFileno(config->out));
if (doColor) if (doColor)
{ {
@ -276,10 +276,10 @@ Log(LogConfig * config, int level, const char *msg,...)
break; break;
} }
fputs(ansi, config->out); StreamPuts(config->out, ansi);
} }
fputc('[', config->out); StreamPutc(config->out, '[');
if (config->tsFmt) if (config->tsFmt)
{ {
@ -292,10 +292,10 @@ Log(LogConfig * config, int level, const char *msg,...)
if (tsLength) if (tsLength)
{ {
fputs(tsBuffer, config->out); StreamPuts(config->out, tsBuffer);
if (!isspace((unsigned char) tsBuffer[tsLength - 1])) 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; break;
} }
fprintf(config->out, "%c]", indicator); StreamPrintf(config->out, "%c]", indicator);
if (doColor) if (doColor)
{ {
/* ANSI Reset */ /* 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++) for (i = 0; i < config->indent; i++)
{ {
fputc(' ', config->out); StreamPutc(config->out, ' ');
} }
va_start(argp, msg); va_start(argp, msg);
vfprintf(config->out, msg, argp); StreamVprintf(config->out, msg, argp);
fputc('\n', config->out); StreamPutc(config->out, '\n');
va_end(argp); va_end(argp);
/* If we are debugging, there might be something that's going to StreamFlush(config->out);
* segfault the program coming up, so flush the output stream
* immediately. */
if (config->level == LOG_DEBUG)
{
fflush(config->out);
}
pthread_mutex_unlock(&config->lock); pthread_mutex_unlock(&config->lock);
} }

View file

@ -72,7 +72,7 @@ main(int argc, char **argv)
char *configArg = NULL; char *configArg = NULL;
/* Config file */ /* Config file */
FILE *configFile = NULL; Stream *configFile = NULL;
HashMap *config = NULL; HashMap *config = NULL;
/* Program configuration */ /* Program configuration */
@ -155,11 +155,11 @@ main(int argc, char **argv)
} }
else if (strcmp(configArg, "-") == 0) else if (strcmp(configArg, "-") == 0)
{ {
configFile = stdin; configFile = StreamStdin();
} }
else else
{ {
fclose(stdin); StreamClose(StreamStdin());
#ifdef __OpenBSD__ #ifdef __OpenBSD__
if (unveil(configArg, "r") != 0) if (unveil(configArg, "r") != 0)
{ {
@ -168,7 +168,7 @@ main(int argc, char **argv)
goto finish; goto finish;
} }
#endif #endif
configFile = fopen(configArg, "r"); configFile = StreamOpen(configArg, "r");
if (!configFile) if (!configFile)
{ {
Log(lc, LOG_ERR, "Unable to open configuration file '%s' for reading.", configArg); 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); Log(lc, LOG_NOTICE, "Processing configuration file '%s'.", configArg);
config = JsonDecode(configFile); config = JsonDecode(configFile);
fclose(configFile); StreamClose(configFile);
if (!config) if (!config)
{ {
@ -250,7 +250,7 @@ main(int argc, char **argv)
if (tConfig->flags & TELODENDRIA_LOG_FILE) if (tConfig->flags & TELODENDRIA_LOG_FILE)
{ {
FILE *logFile = fopen("telodendria.log", "a"); Stream *logFile = StreamOpen("telodendria.log", "a");
if (!logFile) if (!logFile)
{ {
@ -476,9 +476,9 @@ finish:
* If we're not logging to standard output, then we can close it. Otherwise, * 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 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); DbClose(matrixArgs.db);
@ -486,15 +486,26 @@ finish:
LogConfigTimeStampFormatSet(lc, NULL); LogConfigTimeStampFormatSet(lc, NULL);
TelodendriaConfigFree(tConfig); TelodendriaConfigFree(tConfig);
Log(lc, LOG_DEBUG, "");
MemoryIterate(TelodendriaMemoryIterator, lc);
Log(lc, LOG_DEBUG, "");
Log(lc, LOG_DEBUG, "Exiting with code '%d'.", exit); 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); 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; return exit;
} }

View file

@ -39,7 +39,7 @@ MatrixHttpHandler(HttpServerContext * context, void *argp)
{ {
MatrixHttpHandlerArgs *args = (MatrixHttpHandlerArgs *) argp; MatrixHttpHandlerArgs *args = (MatrixHttpHandlerArgs *) argp;
LogConfig *lc = args->lc; LogConfig *lc = args->lc;
FILE *stream; Stream *stream;
HashMap *response = NULL; HashMap *response = NULL;
char *key; char *key;
@ -133,7 +133,7 @@ MatrixHttpHandler(HttpServerContext * context, void *argp)
JsonEncode(response, stream, JSON_DEFAULT); JsonEncode(response, stream, JSON_DEFAULT);
JsonFree(response); JsonFree(response);
fprintf(stream, "\n"); StreamPrintf(stream, "\n");
} }
/* /*

View file

@ -27,7 +27,7 @@
ROUTE_IMPL(RouteStatic, args) ROUTE_IMPL(RouteStatic, args)
{ {
FILE *stream = HttpServerStream(args->context); Stream *stream = HttpServerStream(args->context);
char *pathPart = MATRIX_PATH_POP(args->path); char *pathPart = MATRIX_PATH_POP(args->path);
HttpResponseHeader(args->context, "Content-Type", "text/html"); HttpResponseHeader(args->context, "Content-Type", "text/html");

View file

@ -26,7 +26,7 @@
#include <Http.h> #include <Http.h>
void void
StaticError(FILE * stream, HttpStatus error) StaticError(Stream * stream, HttpStatus error)
{ {
char title[10]; char title[10];
@ -34,8 +34,8 @@ StaticError(FILE * stream, HttpStatus error)
HtmlBegin(stream, title); HtmlBegin(stream, title);
fprintf(stream, "<h2 style=\"text-align: center\">%s</h2>", StreamPrintf(stream, "<h2 style=\"text-align: center\">%s</h2>",
HttpStatusToString(error)); HttpStatusToString(error));
HtmlEnd(stream); HtmlEnd(stream);
} }

View file

@ -25,30 +25,30 @@
#include <Html.h> #include <Html.h>
void void
StaticItWorks(FILE * stream) StaticItWorks(Stream * stream)
{ {
HtmlBegin(stream, "It works! Telodendria is running."); HtmlBegin(stream, "It works! Telodendria is running.");
fprintf(stream, StreamPuts(stream,
"<style>" "<style>"
"p {" "p {"
" text-align: center;" " text-align: center;"
"}" "}"
"</style>" "</style>"
); );
fprintf(stream, StreamPuts(stream,
"<p>" "<p>"
"Your Telodendria server is listening on this port and is ready " "Your Telodendria server is listening on this port and is ready "
"for messages." "for messages."
"</p>" "</p>"
"<p>" "<p>"
"To use this server, you'll need <a href=\"https://matrix.org/clients\">" "To use this server, you'll need <a href=\"https://matrix.org/clients\">"
"a Matrix client</a>." "a Matrix client</a>."
"</p>" "</p>"
"<p>" "<p>"
"Welcome to the Matrix universe :)" "Welcome to the Matrix universe :)"
"</p>" "</p>"
); );
HtmlEnd(stream); HtmlEnd(stream);

View file

@ -25,121 +25,121 @@
#include <Html.h> #include <Html.h>
void void
StaticLogin(FILE * stream) StaticLogin(Stream * stream)
{ {
HtmlBegin(stream, "Log In"); HtmlBegin(stream, "Log In");
fprintf(stream, StreamPuts(stream,
"<div class=\"form\">" "<div class=\"form\">"
"<form id=\"login-form\">" "<form id=\"login-form\">"
"<label for=\"user\">Username:</label>" "<label for=\"user\">Username:</label>"
"<input type=\"text\" id=\"user\">" "<input type=\"text\" id=\"user\">"
"<label for=\"password\">Password:</label>" "<label for=\"password\">Password:</label>"
"<input type=\"password\" id=\"password\">" "<input type=\"password\" id=\"password\">"
"<br>" "<br>"
"<input type=\"submit\" value=\"Log In\">" "<input type=\"submit\" value=\"Log In\">"
"</form>"); "</form>");
HtmlBeginStyle(stream); HtmlBeginStyle(stream);
fprintf(stream, StreamPuts(stream,
"#error-msg {" "#error-msg {"
" display: none;" " display: none;"
" color: red;" " color: red;"
" text-align: center;" " text-align: center;"
" font-weight: bold;" " font-weight: bold;"
" font-size: larger;" " font-size: larger;"
"}"); "}");
HtmlEndStyle(stream); HtmlEndStyle(stream);
fprintf(stream, StreamPuts(stream,
"<p id=\"error-msg\"></p>" "<p id=\"error-msg\"></p>"
"</div>" "</div>"
); );
HtmlBeginJs(stream); HtmlBeginJs(stream);
fprintf(stream, StreamPuts(stream,
"function findGetParameter(parameterName) {" "function findGetParameter(parameterName) {"
" var result = null;" " var result = null;"
" var tmp = [];" " var tmp = [];"
" var items = location.search.substr(1).split(\"&\");" " var items = location.search.substr(1).split(\"&\");"
" for (var index = 0; index < items.length; index++) {" " for (var index = 0; index < items.length; index++) {"
" tmp = items[index].split(\"=\");" " tmp = items[index].split(\"=\");"
" if (tmp[0] === parameterName) result = decodeURIComponent(tmp[1]);" " if (tmp[0] === parameterName) result = decodeURIComponent(tmp[1]);"
" }" " }"
" return result;" " return result;"
"}" "}"
"function setError(msg) {" "function setError(msg) {"
" var err = document.getElementById('error-msg');" " var err = document.getElementById('error-msg');"
" if (msg) {" " if (msg) {"
" err.style.display = 'block';" " err.style.display = 'block';"
" err.innerHTML = msg;" " err.innerHTML = msg;"
" } else {" " } else {"
" err.style.display = 'none';" " err.style.display = 'none';"
" }" " }"
"}" "}"
); );
fprintf(stream, StreamPuts(stream,
"function buildRequest(user, pass) {" "function buildRequest(user, pass) {"
" var d = findGetParameter('device_id');" " var d = findGetParameter('device_id');"
" var i = findGetParameter('initial_device_display_name');" " var i = findGetParameter('initial_device_display_name');"
" var r = findGetParameter('refresh_token') === 'true';" " var r = findGetParameter('refresh_token') === 'true';"
" var request = {};" " var request = {};"
" request['type'] = 'm.login.password';" " request['type'] = 'm.login.password';"
" request['identifier'] = {" " request['identifier'] = {"
" type: 'm.id.user'," " type: 'm.id.user',"
" user: user" " user: user"
" };" " };"
" request['password'] = pass;" " request['password'] = pass;"
" if (d) request['device_id'] = d;" " if (d) request['device_id'] = d;"
" if (i) request['initial_device_display_name'] = i;" " if (i) request['initial_device_display_name'] = i;"
" if (r) request['refresh_token'] = r;" " if (r) request['refresh_token'] = r;"
" return request;" " return request;"
"}" "}"
); );
fprintf(stream, StreamPuts(stream,
"function processResponse(xhr) {" "function processResponse(xhr) {"
" if (xhr.readyState == 4) {" " if (xhr.readyState == 4) {"
" var r = JSON.parse(xhr.responseText);" " var r = JSON.parse(xhr.responseText);"
" if (!r.error) {" " if (!r.error) {"
" if (window.onLogin) {" " if (window.onLogin) {"
" window.onLogin(r);" " window.onLogin(r);"
" } else {" " } else {"
" setError('Client malfunction. Your client should have defined window.onLogin()');" " setError('Client malfunction. Your client should have defined window.onLogin()');"
" }" " }"
" } else {" " } else {"
" setError(r.errcode + ': ' + r.error);" " setError(r.errcode + ': ' + r.error);"
" }" " }"
" }" " }"
"}" "}"
); );
fprintf(stream, StreamPuts(stream,
"function sendRequest(request) {" "function sendRequest(request) {"
" var xhr = new XMLHttpRequest();" " var xhr = new XMLHttpRequest();"
" xhr.open('POST', '/_matrix/client/v3/login');" " xhr.open('POST', '/_matrix/client/v3/login');"
" xhr.setRequestHeader('Content-Type', 'application/json');" " xhr.setRequestHeader('Content-Type', 'application/json');"
" xhr.onreadystatechange = () => processResponse(xhr);" " xhr.onreadystatechange = () => processResponse(xhr);"
" xhr.send(JSON.stringify(request));" " xhr.send(JSON.stringify(request));"
"}" "}"
); );
fprintf(stream, StreamPuts(stream,
"window.addEventListener('load', () => {" "window.addEventListener('load', () => {"
" document.getElementById('login-form').addEventListener('submit', (e) => {" " document.getElementById('login-form').addEventListener('submit', (e) => {"
" e.preventDefault();" " e.preventDefault();"
" var user = document.getElementById('user').value;" " var user = document.getElementById('user').value;"
" var pass = document.getElementById('password').value;" " var pass = document.getElementById('password').value;"
" if (!user || !pass) {" " if (!user || !pass) {"
" setError('Please provide a username and password.');" " setError('Please provide a username and password.');"
" return;" " return;"
" }" " }"
" setError(null);" " setError(null);"
" var request = buildRequest(user, pass);" " var request = buildRequest(user, pass);"
" sendRequest(request);" " sendRequest(request);"
" });" " });"
"});" "});"
); );
HtmlEndJs(stream); HtmlEndJs(stream);

View file

@ -28,6 +28,7 @@
#include <Util.h> #include <Util.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <errno.h> #include <errno.h>
#include <string.h> #include <string.h>
#include <sys/stat.h> #include <sys/stat.h>
@ -42,23 +43,26 @@
#define STREAM_EOF (1 << 0) #define STREAM_EOF (1 << 0)
#define STREAM_ERR (1 << 1) #define STREAM_ERR (1 << 1)
#define STREAM_TTY (1 << 2)
struct Stream struct Stream
{ {
Io *io; Io *io;
int *rBuf; char *rBuf;
size_t rLen; size_t rLen;
size_t rOff; size_t rOff;
int *wBuf; char *wBuf;
size_t wLen; size_t wLen;
int *ugBuf; char *ugBuf;
size_t ugSize; size_t ugSize;
size_t ugLen; size_t ugLen;
int flags:2; int flags;
int fd;
}; };
Stream * Stream *
@ -79,6 +83,7 @@ StreamIo(Io * io)
memset(stream, 0, sizeof(Stream)); memset(stream, 0, sizeof(Stream));
stream->io = io; stream->io = io;
stream->fd = -1;
return stream; return stream;
} }
@ -87,26 +92,54 @@ Stream *
StreamFd(int fd) StreamFd(int fd)
{ {
Io *io = IoFd(fd); Io *io = IoFd(fd);
Stream *stream;
if (!io) if (!io)
{ {
return NULL; 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 * Stream *
StreamFile(FILE * fp) StreamFile(FILE * fp)
{ {
Io *io = IoFile(fp); Io *io = IoFile(fp);
Stream *stream;
if (!io) if (!io)
{ {
return NULL; 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 * Stream *
@ -122,6 +155,45 @@ StreamOpen(const char *path, const char *mode)
return StreamFile(fp); 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 int
StreamClose(Stream * stream) 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 * buffered. It therefore allows us to finish filling the buffer
* and then only flush it when necessary, preventing superfluous * and then only flush it when necessary, preventing superfluous
* writes. */ * writes. */
char *buf;
ssize_t len; char *buf = NULL;
size_t bufSize = 0;
FILE *fp;
int ret; int ret;
@ -181,38 +255,22 @@ StreamVprintf(Stream * stream, const char *fmt, va_list ap)
return -1; return -1;
} }
buf = Malloc(IO_BUFFER); fp = open_memstream(&buf, &bufSize);
if (!buf) if (!fp)
{ {
return -1; return -1;
} }
len = vsnprintf(buf, IO_BUFFER, fmt, ap); ret = vfprintf(fp, fmt, ap);
fclose(fp);
if (len < 0) if (ret >= 0)
{ {
Free(buf); ret = StreamPuts(stream, buf);
return len;
} }
if (len >= IO_BUFFER) free(buf); /* Allocated by stdlib, not Memory
{ * API */
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);
return ret; return ret;
} }
@ -257,7 +315,7 @@ StreamGetc(Stream * stream)
if (!stream->rBuf) if (!stream->rBuf)
{ {
/* No buffer allocated yet */ /* No buffer allocated yet */
stream->rBuf = Malloc(IO_BUFFER * sizeof(int)); stream->rBuf = Malloc(IO_BUFFER);
if (!stream->rBuf) if (!stream->rBuf)
{ {
stream->flags |= STREAM_ERR; stream->flags |= STREAM_ERR;
@ -319,7 +377,7 @@ StreamUngetc(Stream * stream, int c)
if (stream->ugLen >= stream->ugSize) if (stream->ugLen >= stream->ugSize)
{ {
int *new; char *new;
stream->ugSize += IO_BUFFER; stream->ugSize += IO_BUFFER;
new = Realloc(stream->ugBuf, stream->ugSize); new = Realloc(stream->ugBuf, stream->ugSize);
@ -335,7 +393,7 @@ StreamUngetc(Stream * stream, int c)
stream->ugBuf = new; stream->ugBuf = new;
} }
stream->ugBuf[stream->ugLen - 1] = c; stream->ugBuf[stream->ugLen] = c;
stream->ugLen++; stream->ugLen++;
return c; return c;
@ -352,7 +410,7 @@ StreamPutc(Stream * stream, int c)
if (!stream->wBuf) if (!stream->wBuf)
{ {
stream->wBuf = Malloc(IO_BUFFER * sizeof(int)); stream->wBuf = Malloc(IO_BUFFER);
if (!stream->wBuf) if (!stream->wBuf)
{ {
stream->flags |= STREAM_ERR; stream->flags |= STREAM_ERR;
@ -377,6 +435,24 @@ StreamPutc(Stream * stream, int c)
stream->wBuf[stream->wLen] = c; stream->wBuf[stream->wLen] = c;
stream->wLen++; 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; return c;
} }
@ -405,6 +481,46 @@ StreamPuts(Stream * stream, char *str)
return ret; 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 int
StreamEof(Stream * stream) StreamEof(Stream * stream)
{ {
@ -505,3 +621,9 @@ StreamCopy(Stream * in, Stream * out)
StreamFlush(out); StreamFlush(out);
return nBytes; return nBytes;
} }
int
StreamFileno(Stream * stream)
{
return stream ? stream->fd : -1;
}

View file

@ -26,6 +26,8 @@
#include <Memory.h> #include <Memory.h>
#include <Log.h> #include <Log.h>
#include <time.h>
const char const char
TelodendriaLogo[TELODENDRIA_LOGO_HEIGHT][TELODENDRIA_LOGO_WIDTH] = { 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 void
TelodendriaMemoryHook(MemoryAction a, MemoryInfo * i, void *args) TelodendriaMemoryHook(MemoryAction a, MemoryInfo * i, void *args)
{ {
@ -109,20 +96,78 @@ TelodendriaMemoryHook(MemoryAction a, MemoryInfo * i, void *args)
MemoryInfoGetPointer(i)); MemoryInfoGetPointer(i));
} }
void static void
TelodendriaMemoryIterator(MemoryInfo * i, void *args) 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 (hexBuf && asciiBuf)
if (MemoryInfoGetPointer(i) != lc)
{ {
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), MemoryInfoGetFile(i), MemoryInfoGetLine(i),
MemoryInfoGetSize(i), MemoryInfoGetPointer(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(&currentTime);
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 void

View file

@ -219,7 +219,7 @@ UtilParseBytes(char *str)
} }
ssize_t 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; char *curPos, *newLinePtr;
size_t newLinePtrLen; size_t newLinePtrLen;
@ -246,9 +246,9 @@ UtilGetDelim(char **linePtr, size_t * n, int delim, FILE * stream)
while (1) while (1)
{ {
c = fgetc(stream); c = StreamGetc(stream);
if (ferror(stream) || (c == EOF && curPos == *linePtr)) if (StreamError(stream) || (c == EOF && curPos == *linePtr))
{ {
return -1; return -1;
} }
@ -296,31 +296,31 @@ UtilGetDelim(char **linePtr, size_t * n, int delim, FILE * stream)
} }
ssize_t ssize_t
UtilGetLine(char **linePtr, size_t * n, FILE * stream) UtilGetLine(char **linePtr, size_t * n, Stream * stream)
{ {
return UtilGetDelim(linePtr, n, '\n', stream); return UtilGetDelim(linePtr, n, '\n', stream);
} }
size_t size_t
UtilStreamCopy(FILE * in, FILE * out) UtilStreamCopy(Stream * in, Stream * out)
{ {
size_t bytes = 0; size_t bytes = 0;
int c; int c;
while (1) while (1)
{ {
c = fgetc(in); c = StreamGetc(in);
if (feof(in)) if (StreamEof(in))
{ {
break; break;
} }
if (ferror(in)) if (StreamError(in))
{ {
if (errno == EAGAIN) if (errno == EAGAIN)
{ {
clearerr(in); StreamClearError(in);
continue; continue;
} }
else else
@ -329,7 +329,7 @@ UtilStreamCopy(FILE * in, FILE * out)
} }
} }
fputc(c, out); StreamPutc(out, c);
bytes++; bytes++;
} }

View file

@ -26,10 +26,12 @@
#define TELODENDRIA_CANONICALJSON_H #define TELODENDRIA_CANONICALJSON_H
#include <stdio.h> #include <stdio.h>
#include <HashMap.h> #include <HashMap.h>
#include <Stream.h>
extern int extern int
CanonicalJsonEncode(HashMap *, FILE *); CanonicalJsonEncode(HashMap *, Stream *);
extern char * extern char *
CanonicalJsonEncodeToString(HashMap *); CanonicalJsonEncodeToString(HashMap *);

View file

@ -24,23 +24,23 @@
#ifndef TELODENDRIA_HTML_H #ifndef TELODENDRIA_HTML_H
#define TELODENDRIA_HTML_H #define TELODENDRIA_HTML_H
#include <stdio.h> #include <Stream.h>
#define HtmlBeginJs(stream) fprintf(stream, \ #define HtmlBeginJs(stream) StreamPuts(stream, \
"<script>" \ "<script>" \
"\n// @license magnet:?xt=" \ "\n// @license magnet:?xt=" \
"urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&dn=expat.txt " \ "urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&dn=expat.txt " \
"Expat\n") "Expat\n")
#define HtmlEndJs(stream) fprintf(stream, "\n// @license-end\n</script>") #define HtmlEndJs(stream) StreamPuts(stream, "\n// @license-end\n</script>")
#define HtmlBeginStyle(stream) fprintf(stream, "<style>") #define HtmlBeginStyle(stream) StreamPuts(stream, "<style>")
#define HtmlEndStyle(stream) fprintf(stream, "</style>") #define HtmlEndStyle(stream) StreamPuts(stream, "</style>")
extern void extern void
HtmlBegin(FILE *, char *); HtmlBegin(Stream *, char *);
extern void extern void
HtmlEnd(FILE *); HtmlEnd(Stream *);
#endif /* TELODENDRIA_HTML_H */ #endif /* TELODENDRIA_HTML_H */

View file

@ -27,6 +27,7 @@
#include <stdio.h> #include <stdio.h>
#include <HashMap.h> #include <HashMap.h>
#include <Stream.h>
typedef enum HttpRequestMethod typedef enum HttpRequestMethod
{ {
@ -126,6 +127,6 @@ extern char *
HttpParamEncode(HashMap *); HttpParamEncode(HashMap *);
extern HashMap * extern HashMap *
HttpParseHeaders(FILE *); HttpParseHeaders(Stream *);
#endif #endif

View file

@ -49,7 +49,7 @@ extern HttpStatus
extern HashMap * extern HashMap *
HttpResponseHeaders(HttpClientContext *); HttpResponseHeaders(HttpClientContext *);
extern FILE * extern Stream *
HttpClientStream(HttpClientContext *); HttpClientStream(HttpClientContext *);
extern void extern void

View file

@ -29,6 +29,7 @@
#include <stdio.h> #include <stdio.h>
#include <HashMap.h> #include <HashMap.h>
#include <Stream.h>
typedef struct HttpServer HttpServer; typedef struct HttpServer HttpServer;
@ -68,7 +69,7 @@ extern char *
extern void extern void
HttpResponseStatus(HttpServerContext *, HttpStatus); HttpResponseStatus(HttpServerContext *, HttpStatus);
extern FILE * extern Stream *
HttpServerStream(HttpServerContext *); HttpServerStream(HttpServerContext *);
extern void extern void

View file

@ -27,6 +27,7 @@
#include <stdio.h> #include <stdio.h>
#include <stdarg.h> #include <stdarg.h>
#include <unistd.h> #include <unistd.h>
#include <sys/types.h>
#ifndef IO_BUFFER #ifndef IO_BUFFER
#define IO_BUFFER 4096 #define IO_BUFFER 4096

View file

@ -27,6 +27,7 @@
#include <HashMap.h> #include <HashMap.h>
#include <Array.h> #include <Array.h>
#include <Stream.h>
#include <stdio.h> #include <stdio.h>
#include <stddef.h> #include <stddef.h>
@ -96,16 +97,16 @@ extern void
JsonFree(HashMap *); JsonFree(HashMap *);
extern void extern void
JsonEncodeString(const char *, FILE *); JsonEncodeString(const char *, Stream *);
extern void extern void
JsonEncodeValue(JsonValue * value, FILE * out, int); JsonEncodeValue(JsonValue * value, Stream * out, int);
extern int extern int
JsonEncode(HashMap *, FILE *, int); JsonEncode(HashMap *, Stream *, int);
extern HashMap * extern HashMap *
JsonDecode(FILE *); JsonDecode(Stream *);
extern JsonValue * extern JsonValue *
JsonGet(HashMap *, size_t,...); JsonGet(HashMap *, size_t,...);

View file

@ -29,6 +29,8 @@
#include <stddef.h> #include <stddef.h>
#include <syslog.h> #include <syslog.h>
#include <Stream.h>
#define LOG_FLAG_COLOR (1 << 0) #define LOG_FLAG_COLOR (1 << 0)
#define LOG_FLAG_SYSLOG (1 << 1) #define LOG_FLAG_SYSLOG (1 << 1)
@ -53,7 +55,7 @@ extern void
LogConfigIndentSet(LogConfig *, size_t); LogConfigIndentSet(LogConfig *, size_t);
extern void extern void
LogConfigOutputSet(LogConfig *, FILE *); LogConfigOutputSet(LogConfig *, Stream *);
extern void extern void
LogConfigFlagSet(LogConfig *, int); LogConfigFlagSet(LogConfig *, int);

View file

@ -29,12 +29,12 @@
#include <Http.h> #include <Http.h>
extern void extern void
StaticItWorks(FILE *); StaticItWorks(Stream *);
extern void extern void
StaticLogin(FILE *); StaticLogin(Stream *);
extern void extern void
StaticError(FILE *, HttpStatus); StaticError(Stream *, HttpStatus);
#endif /* TELODENDRIA_STATIC_H */ #endif /* TELODENDRIA_STATIC_H */

View file

@ -42,6 +42,15 @@ extern Stream *
extern Stream * extern Stream *
StreamOpen(const char *, const char *); StreamOpen(const char *, const char *);
extern Stream *
StreamStdout(void);
extern Stream *
StreamStderr(void);
extern Stream *
StreamStdin(void);
extern int extern int
StreamClose(Stream *); StreamClose(Stream *);
@ -63,6 +72,9 @@ extern int
extern int extern int
StreamPuts(Stream *, char *); StreamPuts(Stream *, char *);
extern char *
StreamGets(Stream *, char *, int);
extern int extern int
StreamEof(Stream *); StreamEof(Stream *);
@ -78,4 +90,7 @@ extern int
extern ssize_t extern ssize_t
StreamCopy(Stream *, Stream *); StreamCopy(Stream *, Stream *);
extern int
StreamFileno(Stream *);
#endif /* TELODENDRIA_STREAM_H */ #endif /* TELODENDRIA_STREAM_H */

View file

@ -39,14 +39,11 @@ extern const char
extern const char extern const char
TelodendriaHeader[TELODENDRIA_HEADER_HEIGHT][TELODENDRIA_HEADER_WIDTH]; TelodendriaHeader[TELODENDRIA_HEADER_HEIGHT][TELODENDRIA_HEADER_WIDTH];
extern void
TelodendriaHexDump(size_t, char *, char *, void *);
extern void extern void
TelodendriaMemoryHook(MemoryAction, MemoryInfo *, void *); TelodendriaMemoryHook(MemoryAction, MemoryInfo *, void *);
extern void extern void
TelodendriaMemoryIterator(MemoryInfo *, void *); TelodendriaGenerateMemReport(void);
extern void extern void
TelodendriaPrintHeader(LogConfig *); TelodendriaPrintHeader(LogConfig *);

View file

@ -29,6 +29,8 @@
#include <stddef.h> #include <stddef.h>
#include <sys/types.h> #include <sys/types.h>
#include <Stream.h>
extern unsigned long extern unsigned long
UtilServerTs(void); UtilServerTs(void);
@ -45,12 +47,12 @@ extern size_t
UtilParseBytes(char *); UtilParseBytes(char *);
extern ssize_t extern ssize_t
UtilGetDelim(char **, size_t *, int, FILE *); UtilGetDelim(char **, size_t *, int, Stream *);
extern ssize_t extern ssize_t
UtilGetLine(char **, size_t *, FILE *); UtilGetLine(char **, size_t *, Stream *);
extern size_t extern size_t
UtilStreamCopy(FILE *, FILE *); UtilStreamCopy(Stream *, Stream *);
#endif /* TELODENDRIA_UTIL_H */ #endif /* TELODENDRIA_UTIL_H */

View file

@ -39,7 +39,7 @@ MAIN="Main"
if [ "$DEBUG" = "1" ]; then if [ "$DEBUG" = "1" ]; then
CFLAGS="$CFLAGS -O0 -g -pg" CFLAGS="$CFLAGS -O0 -g -pg"
LDFLAGS="-lm -pthread -v" LDFLAGS="-lm -pthread"
PROG="$PROG-debug" PROG="$PROG-debug"
fi fi

View file

@ -8,25 +8,19 @@ ENDPOINT="$1"
: "${METH:=GET}" : "${METH:=GET}"
# Check if user is available. If it is, register it. register_payload() {
user_available=$(http "$BASE/_matrix/client/v3/register/available?username=$USERNAME" | json -s "available") printf '{'
if [ "$user_available" = "true" ]; then printf ' "auth": {'
session=$(echo '{}' | http -X POST "$BASE/_matrix/client/v3/register" | json -s "session->@decode") printf ' "type": "m.login.dummy",'
( printf ' "session": %s' "$(json -e "$session")"
printf '{' printf ' },'
printf ' "auth": {' printf ' "username": %s,' "$(json -e "$USERNAME")"
printf ' "type": "m.login.dummy",' printf ' "password": %s,' "$PASSWORD"
printf ' "session": %s' "$(json -e "$session")" printf ' "inhibit_login": true '
printf ' },' 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
# Log in login_payload() {
RESPONSE=$((
printf '{' printf '{'
printf ' "identifier": {' printf ' "identifier": {'
printf ' "type": "m.id.user",' printf ' "type": "m.id.user",'
@ -35,7 +29,17 @@ RESPONSE=$((
printf ' "type": "m.login.password",' printf ' "type": "m.login.password",'
printf ' "password": %s' "$PASSWORD" printf ' "password": %s' "$PASSWORD"
printf '}' 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") ACCESS_TOKEN=$(echo "$RESPONSE" | json -s "access_token->@decode")
@ -53,4 +57,4 @@ fi
# Log out # Log out
http -X POST -H "Authorization: Bearer $ACCESS_TOKEN" \ http -X POST -H "Authorization: Bearer $ACCESS_TOKEN" \
"$BASE/_matrix/client/v3/logout" > /dev/null "$BASE/_matrix/client/v3/logout/all" > /dev/null

View file

@ -49,24 +49,24 @@ HttpHandle(HttpServerContext * cx, void *args)
(void) args; (void) args;
printf("%s %s\n", HttpRequestMethodToString(method), StreamPrintf(StreamStdout(), "%s %s\n", HttpRequestMethodToString(method),
HttpRequestPath(cx)); HttpRequestPath(cx));
while (HashMapIterate(headers, &key, (void **) &val)) 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"); StreamPutc(StreamStdout(), '\n');
printf("(%lu bytes)\n", bytes); StreamPrintf(StreamStdout(), "(%lu bytes)\n", bytes);
HttpSendHeaders(cx); HttpSendHeaders(cx);
fprintf(HttpServerStream(cx), "{}\n"); StreamPuts(HttpServerStream(cx), "{}\n");
} }
int int
@ -77,12 +77,12 @@ main(void)
server = HttpServerCreate(8008, 1, 1, HttpHandle, NULL); server = HttpServerCreate(8008, 1, 1, HttpHandle, NULL);
if (!HttpServerStart(server)) if (!HttpServerStart(server))
{ {
fprintf(stderr, "Unable to start HTTP server.\n"); StreamPuts(StreamStderr(), "Unable to start HTTP server.\n");
HttpServerFree(server); HttpServerFree(server);
return 1; return 1;
} }
printf("Listening on port 8008.\n"); StreamPuts(StreamStdout(), "Listening on port 8008.\n");
sa.sa_handler = SignalHandle; sa.sa_handler = SignalHandle;
sigfillset(&sa.sa_mask); sigfillset(&sa.sa_mask);
@ -90,7 +90,7 @@ main(void)
if (sigaction(SIGINT, &sa, NULL) < 0) if (sigaction(SIGINT, &sa, NULL) < 0)
{ {
fprintf(stderr, "Unable to install signal handler.\n"); StreamPuts(StreamStderr(), "Unable to install signal handler.\n");
HttpServerStop(server); HttpServerStop(server);
HttpServerJoin(server); HttpServerJoin(server);
HttpServerFree(server); HttpServerFree(server);
@ -99,7 +99,12 @@ main(void)
HttpServerJoin(server); HttpServerJoin(server);
printf("Shutting down.\n"); StreamPuts(StreamStdout(), "Shutting down.\n");
HttpServerStop(server); HttpServerStop(server);
StreamClose(StreamStdout());
StreamClose(StreamStderr());
StreamClose(StreamStdin());
return 0; return 0;
} }

View file

@ -40,7 +40,7 @@
static void static void
usage(char *prog) 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 int
@ -72,7 +72,7 @@ main(int argc, char **argv)
method = HttpRequestMethodFromString(optarg); method = HttpRequestMethodFromString(optarg);
if (!method) if (!method)
{ {
fprintf(stderr, "Unknown request method: %s\n", optarg); StreamPrintf(StreamStderr(), "Unknown request method: %s\n", optarg);
return 1; return 1;
} }
break; break;
@ -113,7 +113,7 @@ main(int argc, char **argv)
uri = UriParse(argv[optind]); uri = UriParse(argv[optind]);
if (!uri) if (!uri)
{ {
fprintf(stderr, "Failed to parse URI: %s\n", argv[optind]); StreamPrintf(StreamStderr(), "Failed to parse URI: %s\n", argv[optind]);
return 1; return 1;
} }
@ -131,7 +131,7 @@ main(int argc, char **argv)
if (!uri->port) if (!uri->port)
{ {
fprintf(stderr, "Unknown protocol: %s\n", uri->proto); StreamPrintf(StreamStderr(), "Unknown protocol: %s\n", uri->proto);
UriFree(uri); UriFree(uri);
return 1; return 1;
} }
@ -145,7 +145,7 @@ main(int argc, char **argv)
if (!cx) if (!cx)
{ {
fprintf(stderr, "Failed to connect.\n"); StreamPuts(StreamStderr(), "Failed to connect.\n");
UriFree(uri); UriFree(uri);
return 1; return 1;
} }
@ -163,32 +163,32 @@ main(int argc, char **argv)
{ {
if (*data == '@') if (*data == '@')
{ {
FILE *in; Stream *in;
data++; data++;
if (strcmp(data, "-") == 0) if (strcmp(data, "-") == 0)
{ {
in = stdin; in = StreamStdin();
} }
else else
{ {
in = fopen(data, "r"); in = StreamOpen(data, "r");
} }
if (!in) if (!in)
{ {
fprintf(stderr, "%s: %s\n", data, strerror(errno)); StreamPrintf(StreamStderr(), "%s: %s\n", data, strerror(errno));
return 1; return 1;
} }
UtilStreamCopy(in, HttpClientStream(cx)); UtilStreamCopy(in, HttpClientStream(cx));
fclose(in); StreamClose(in);
} }
else else
{ {
fprintf(HttpClientStream(cx), "%s", data); StreamPuts(HttpClientStream(cx), data);
} }
} }
@ -196,7 +196,7 @@ main(int argc, char **argv)
if (!res) if (!res)
{ {
fprintf(stderr, "Failed to send request.\n"); StreamPuts(StreamStderr(), "Failed to send request.\n");
HttpClientContextFree(cx); HttpClientContextFree(cx);
UriFree(uri); UriFree(uri);
return 1; return 1;
@ -206,20 +206,25 @@ main(int argc, char **argv)
{ {
HashMap *responseHeaders = HttpResponseHeaders(cx); 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)) 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); HttpClientContextFree(cx);
UriFree(uri); UriFree(uri);
StreamClose(StreamStdout());
StreamClose(StreamStderr());
StreamClose(StreamStdin());
return !(res == HTTP_OK); return !(res == HTTP_OK);
} }

View file

@ -36,7 +36,7 @@
static void static void
usage(char *prog) 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 static void
@ -89,7 +89,7 @@ query(char *select, HashMap * json)
} }
else if (JsonValueType(val) == JSON_STRING && strcmp(keyName + 1, "decode") == 0) else if (JsonValueType(val) == JSON_STRING && strcmp(keyName + 1, "decode") == 0)
{ {
printf("%s\n", JsonValueAsString(val)); StreamPrintf(StreamStdout(), "%s\n", JsonValueAsString(val));
val = NULL; val = NULL;
break; break;
} }
@ -160,8 +160,8 @@ query(char *select, HashMap * json)
if (val) if (val)
{ {
JsonEncodeValue(val, stdout, JSON_PRETTY); JsonEncodeValue(val, StreamStdout(), JSON_PRETTY);
printf("\n"); StreamPutc(StreamStdout(), '\n');
} }
} }
@ -170,9 +170,9 @@ encode(char *str)
{ {
JsonValue *val = JsonValueString(str); JsonValue *val = JsonValueString(str);
JsonEncodeValue(val, stdout, JSON_DEFAULT); JsonEncodeValue(val, StreamStdout(), JSON_DEFAULT);
JsonValueFree(val); JsonValueFree(val);
printf("\n"); StreamPutc(StreamStdout(), '\n');
} }
int int
@ -203,11 +203,11 @@ main(int argc, char **argv)
if (flag != FLAG_ENCODE) if (flag != FLAG_ENCODE)
{ {
json = JsonDecode(stdin); json = JsonDecode(StreamStdin());
if (!json) if (!json)
{ {
fprintf(stderr, "Malformed JSON.\n"); StreamPuts(StreamStderr(), "Malformed JSON.\n");
return 1; return 1;
} }
} }
@ -221,11 +221,15 @@ main(int argc, char **argv)
encode(input); encode(input);
break; break;
default: default:
JsonEncode(json, stdout, JSON_PRETTY); JsonEncode(json, StreamStdout(), JSON_PRETTY);
printf("\n"); StreamPutc(StreamStdout(), '\n');
break; break;
} }
StreamClose(StreamStdout());
StreamClose(StreamStderr());
StreamClose(StreamStdin());
MemoryFreeAll(); MemoryFreeAll();
return 0; return 0;
} }