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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -72,7 +72,7 @@ main(int argc, char **argv)
char *configArg = NULL;
/* Config file */
FILE *configFile = NULL;
Stream *configFile = NULL;
HashMap *config = NULL;
/* Program configuration */
@ -155,11 +155,11 @@ main(int argc, char **argv)
}
else if (strcmp(configArg, "-") == 0)
{
configFile = stdin;
configFile = StreamStdin();
}
else
{
fclose(stdin);
StreamClose(StreamStdin());
#ifdef __OpenBSD__
if (unveil(configArg, "r") != 0)
{
@ -168,7 +168,7 @@ main(int argc, char **argv)
goto finish;
}
#endif
configFile = fopen(configArg, "r");
configFile = StreamOpen(configArg, "r");
if (!configFile)
{
Log(lc, LOG_ERR, "Unable to open configuration file '%s' for reading.", configArg);
@ -180,7 +180,7 @@ main(int argc, char **argv)
Log(lc, LOG_NOTICE, "Processing configuration file '%s'.", configArg);
config = JsonDecode(configFile);
fclose(configFile);
StreamClose(configFile);
if (!config)
{
@ -250,7 +250,7 @@ main(int argc, char **argv)
if (tConfig->flags & TELODENDRIA_LOG_FILE)
{
FILE *logFile = fopen("telodendria.log", "a");
Stream *logFile = StreamOpen("telodendria.log", "a");
if (!logFile)
{
@ -476,9 +476,9 @@ finish:
* If we're not logging to standard output, then we can close it. Otherwise,
* if we are logging to stdout, LogConfigFree() will close it for us.
*/
if (!tConfig || !(tConfig->flags & TELODENDRIA_LOG_STDOUT))
if (tConfig && !(tConfig->flags & TELODENDRIA_LOG_STDOUT))
{
fclose(stdout);
StreamClose(StreamStdout());
}
DbClose(matrixArgs.db);
@ -486,15 +486,26 @@ finish:
LogConfigTimeStampFormatSet(lc, NULL);
TelodendriaConfigFree(tConfig);
Log(lc, LOG_DEBUG, "");
MemoryIterate(TelodendriaMemoryIterator, lc);
Log(lc, LOG_DEBUG, "");
Log(lc, LOG_DEBUG, "Exiting with code '%d'.", exit);
/*
* Uninstall the memory hook because it uses the Log
* API, whose configuration is being freed now, so it
* won't work anymore.
*/
MemoryHook(NULL, NULL);
LogConfigFree(lc);
MemoryFreeAll();
/* Standard error should never have been opened, but just in case
* it was, this doesn't hurt anything. */
StreamClose(StreamStderr());
fclose(stderr);
/* Generate a memory report if any leaks occurred. At this point no
* memory should be allocated. */
TelodendriaGenerateMemReport();
MemoryFreeAll();
return exit;
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -28,6 +28,7 @@
#include <Util.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
@ -42,23 +43,26 @@
#define STREAM_EOF (1 << 0)
#define STREAM_ERR (1 << 1)
#define STREAM_TTY (1 << 2)
struct Stream
{
Io *io;
int *rBuf;
char *rBuf;
size_t rLen;
size_t rOff;
int *wBuf;
char *wBuf;
size_t wLen;
int *ugBuf;
char *ugBuf;
size_t ugSize;
size_t ugLen;
int flags:2;
int flags;
int fd;
};
Stream *
@ -79,6 +83,7 @@ StreamIo(Io * io)
memset(stream, 0, sizeof(Stream));
stream->io = io;
stream->fd = -1;
return stream;
}
@ -87,26 +92,54 @@ Stream *
StreamFd(int fd)
{
Io *io = IoFd(fd);
Stream *stream;
if (!io)
{
return NULL;
}
return StreamIo(io);
stream = StreamIo(io);
if (!stream)
{
return NULL;
}
stream->fd = fd;
if (isatty(stream->fd))
{
stream->flags |= STREAM_TTY;
}
return stream;
}
Stream *
StreamFile(FILE * fp)
{
Io *io = IoFile(fp);
Stream *stream;
if (!io)
{
return NULL;
}
return StreamIo(io);
stream = StreamIo(io);
if (!stream)
{
return NULL;
}
stream->fd = fileno(fp);
if (isatty(stream->fd))
{
stream->flags |= STREAM_TTY;
}
return stream;
}
Stream *
@ -122,6 +155,45 @@ StreamOpen(const char *path, const char *mode)
return StreamFile(fp);
}
Stream *
StreamStdout(void)
{
static Stream *stdOut = NULL;
if (!stdOut)
{
stdOut = StreamFd(STDOUT_FILENO);
}
return stdOut;
}
Stream *
StreamStderr(void)
{
static Stream *stdErr = NULL;
if (!stdErr)
{
stdErr = StreamFd(STDERR_FILENO);
}
return stdErr;
}
Stream *
StreamStdin(void)
{
static Stream *stdIn = NULL;
if (!stdIn)
{
stdIn = StreamFd(STDIN_FILENO);
}
return stdIn;
}
int
StreamClose(Stream * stream)
{
@ -171,8 +243,10 @@ StreamVprintf(Stream * stream, const char *fmt, va_list ap)
* buffered. It therefore allows us to finish filling the buffer
* and then only flush it when necessary, preventing superfluous
* writes. */
char *buf;
ssize_t len;
char *buf = NULL;
size_t bufSize = 0;
FILE *fp;
int ret;
@ -181,38 +255,22 @@ StreamVprintf(Stream * stream, const char *fmt, va_list ap)
return -1;
}
buf = Malloc(IO_BUFFER);
if (!buf)
fp = open_memstream(&buf, &bufSize);
if (!fp)
{
return -1;
}
len = vsnprintf(buf, IO_BUFFER, fmt, ap);
ret = vfprintf(fp, fmt, ap);
fclose(fp);
if (len < 0)
if (ret >= 0)
{
Free(buf);
return len;
ret = StreamPuts(stream, buf);
}
if (len >= IO_BUFFER)
{
char *new = Realloc(buf, len + 1);
if (!new)
{
Free(buf);
return -1;
}
buf = new;
vsnprintf(buf, len, fmt, ap);
}
ret = StreamPuts(stream, buf);
Free(buf);
free(buf); /* Allocated by stdlib, not Memory
* API */
return ret;
}
@ -257,7 +315,7 @@ StreamGetc(Stream * stream)
if (!stream->rBuf)
{
/* No buffer allocated yet */
stream->rBuf = Malloc(IO_BUFFER * sizeof(int));
stream->rBuf = Malloc(IO_BUFFER);
if (!stream->rBuf)
{
stream->flags |= STREAM_ERR;
@ -319,7 +377,7 @@ StreamUngetc(Stream * stream, int c)
if (stream->ugLen >= stream->ugSize)
{
int *new;
char *new;
stream->ugSize += IO_BUFFER;
new = Realloc(stream->ugBuf, stream->ugSize);
@ -335,7 +393,7 @@ StreamUngetc(Stream * stream, int c)
stream->ugBuf = new;
}
stream->ugBuf[stream->ugLen - 1] = c;
stream->ugBuf[stream->ugLen] = c;
stream->ugLen++;
return c;
@ -352,7 +410,7 @@ StreamPutc(Stream * stream, int c)
if (!stream->wBuf)
{
stream->wBuf = Malloc(IO_BUFFER * sizeof(int));
stream->wBuf = Malloc(IO_BUFFER);
if (!stream->wBuf)
{
stream->flags |= STREAM_ERR;
@ -377,6 +435,24 @@ StreamPutc(Stream * stream, int c)
stream->wBuf[stream->wLen] = c;
stream->wLen++;
if (stream->flags & STREAM_TTY && c == '\n')
{
/* Newline encountered on a TTY; write now. This fixes some
* strange behavior on certain TTYs where a newline is written
* to the screen upon flush even when no newline exists in the
* stream. We just flush on newlines, but only if we're
* directly writing to a TTY. */
ssize_t writeRes = IoWrite(stream->io, stream->wBuf, stream->wLen);
if (writeRes == -1)
{
stream->flags |= STREAM_ERR;
return EOF;
}
stream->wLen = 0;
}
return c;
}
@ -405,6 +481,46 @@ StreamPuts(Stream * stream, char *str)
return ret;
}
char *
StreamGets(Stream * stream, char *str, int size)
{
int i;
if (!stream)
{
errno = EBADF;
return NULL;
}
if (size <= 0)
{
errno = EINVAL;
return NULL;
}
for (i = 0; i < size - 1; i++)
{
int c = StreamGetc(stream);
if (StreamEof(stream) || StreamError(stream))
{
break;
}
str[i] = c;
if (c == '\n')
{
i++;
break;
}
}
str[i] = '\0';
return str;
}
int
StreamEof(Stream * stream)
{
@ -505,3 +621,9 @@ StreamCopy(Stream * in, Stream * out)
StreamFlush(out);
return nBytes;
}
int
StreamFileno(Stream * stream)
{
return stream ? stream->fd : -1;
}

View file

@ -26,6 +26,8 @@
#include <Memory.h>
#include <Log.h>
#include <time.h>
const char
TelodendriaLogo[TELODENDRIA_LOGO_HEIGHT][TELODENDRIA_LOGO_WIDTH] = {
" .= -=- ",
@ -62,21 +64,6 @@ const char
"======================================================="
};
void
TelodendriaHexDump(size_t off, char *hexBuf, char *asciiBuf, void *args)
{
LogConfig *lc = args;
if (hexBuf && asciiBuf)
{
Log(lc, LOG_DEBUG, "%04x: %s | %s |", off, hexBuf, asciiBuf);
}
else
{
Log(lc, LOG_DEBUG, "%04x", off);
}
}
void
TelodendriaMemoryHook(MemoryAction a, MemoryInfo * i, void *args)
{
@ -109,20 +96,78 @@ TelodendriaMemoryHook(MemoryAction a, MemoryInfo * i, void *args)
MemoryInfoGetPointer(i));
}
void
TelodendriaMemoryIterator(MemoryInfo * i, void *args)
static void
HexDump(size_t off, char *hexBuf, char *asciiBuf, void *args)
{
LogConfig *lc = (LogConfig *) args;
FILE *report = args;
/* We haven't freed the logger memory yet */
if (MemoryInfoGetPointer(i) != lc)
if (hexBuf && asciiBuf)
{
Log(lc, LOG_WARNING, "%s:%d: %lu bytes of memory at %p leaked.",
fprintf(report, "%04lx: %s | %s |\n", off, hexBuf, asciiBuf);
}
else
{
fprintf(report, "%04lx\n", off);
}
}
static void
MemoryIterator(MemoryInfo * i, void *args)
{
FILE *report = args;
fprintf(report, "%s:%d: %lu bytes at %p\n",
MemoryInfoGetFile(i), MemoryInfoGetLine(i),
MemoryInfoGetSize(i), MemoryInfoGetPointer(i));
MemoryHexDump(i, TelodendriaHexDump, lc);
MemoryHexDump(i, HexDump, report);
fprintf(report, "\n");
}
void
TelodendriaGenerateMemReport(void)
{
static const char *reportName = "Memory.txt";
/*
* Use C standard IO instead of the Stream or Io APIs, because
* those use the Memory API, and that's exactly what we're trying
* to assess, so using it would generate false positives. None of
* this code should leak memory.
*/
FILE *report;
time_t currentTime;
struct tm *timeInfo;
char tsBuffer[1024];
if (!MemoryAllocated())
{
/* No memory leaked, no need to write the report. This is the
* ideal situation; we only want the report to show up if leaks
* occurred. */
return;
}
report = fopen(reportName, "a");
if (!report)
{
return;
}
currentTime = time(NULL);
timeInfo = localtime(&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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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