forked from lda/telodendria
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:
parent
a97a593f21
commit
5289c16e2b
35 changed files with 714 additions and 492 deletions
|
@ -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;
|
||||||
|
|
54
src/Db.c
54
src/Db.c
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
134
src/Html.c
134
src/Html.c
|
@ -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>");
|
||||||
}
|
}
|
||||||
|
|
|
@ -536,7 +536,7 @@ HttpParamEncode(HashMap * params)
|
||||||
}
|
}
|
||||||
|
|
||||||
HashMap *
|
HashMap *
|
||||||
HttpParseHeaders(FILE * fp)
|
HttpParseHeaders(Stream * fp)
|
||||||
{
|
{
|
||||||
HashMap *headers;
|
HashMap *headers;
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
2
src/Io.c
2
src/Io.c
|
@ -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;
|
||||||
|
|
||||||
|
|
106
src/Json.c
106
src/Json.c
|
@ -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;
|
||||||
|
|
||||||
|
|
38
src/Log.c
38
src/Log.c
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
37
src/Main.c
37
src/Main.c
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
194
src/Stream.c
194
src/Stream.c
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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(¤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
|
void
|
||||||
|
|
20
src/Util.c
20
src/Util.c
|
@ -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++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 *);
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,...);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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 *);
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
44
tools/bin/tt
44
tools/bin/tt
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue