Periodic save

This commit is contained in:
Nathan Fisher 2023-10-08 11:59:17 -04:00
parent 5d3e09a6ae
commit 626ebaeb30
2 changed files with 247 additions and 145 deletions

View file

@ -6,6 +6,16 @@
#include "gemtext-parser.h"
int lineBufferInit(lineBuffer *lb) {
char *buf = calloc(1, LBUF_SIZE);
if (buf == NULL) return 2;
lb->len = 0;
lb->capacity = LBUF_SIZE;
lb->buf = buf;
lb->cursor = buf;
return 0;
}
gemtextParser* gemtextParserInit(FILE *stream) {
gemtextParser *parser = calloc(1, sizeof(gemtextParser));
if (parser == NULL)
@ -13,11 +23,20 @@ gemtextParser* gemtextParserInit(FILE *stream) {
parser->stream = stream;
parser->mode = normalMode;
parser->state = lineStart;
if (lineBufferInit(&parser->buffer) != 0) {
free(parser);
return NULL;
}
parser->linkUrl = NULL;
return parser;
}
void gemtextParserDeinit(gemtextParser *parser) {
fclose(parser->stream);
free(parser->buffer.buf);
if (parser->linkUrl != NULL) {
free(parser->linkUrl);
}
free(parser);
}
@ -65,38 +84,6 @@ gemtextLine* gemtextLineQueuePop(gemtextLineQueue *lq) {
return line;
}
lineBuffer* lineBufferNew() {
char *buf;
lineBuffer *lb = calloc(1, sizeof(lineBuffer));
if (lb == NULL) return NULL;
lb->capacity = LBUF_SIZE;
lb->len = 0;
buf = calloc(1, LBUF_SIZE);
if (buf == NULL) {
free(lb);
return NULL;
}
lb->buf = buf;
lb->cursor = lb->buf;
return lb;
}
int lineBufferInit(lineBuffer *lb) {
char *buf = calloc(1, LBUF_SIZE);
if (buf == NULL) return 2;
lb->len = 0;
lb->capacity = LBUF_SIZE;
lb->buf = buf;
lb->cursor = buf;
return 0;
}
void lineBufferDeinit(lineBuffer *lb) {
free(lb->buf);
free(lb);
}
int lineBufferExtend(lineBuffer *lb, size_t len) {
char *buf = calloc(1, lb->capacity + len);
if (buf == NULL) return 2;
@ -152,6 +139,27 @@ void lineBufferReset(lineBuffer *lb) {
lb->cursor = lb->buf;
}
int gemtextParserSend(gemtextParser *parser, gemtextLineType lt, gemtextLineQueue *lq) {
gemtextLine *line;
char *buf;
line = calloc(1, sizeof(gemtextLine));
if (line == NULL) return errno;
line->lineType = lt;
buf = strndup(parser->buffer.buf, parser->buffer.len);
if (buf == NULL) return errno;
line->str = buf;
gemtextLineQueuePush(lq, line);
lineBufferReset(&parser->buffer);
parser->state = lineStart;
parser->mode = normalMode;
return 0;
}
void logParseError(int err) {
//todo
}
gemtextLink* readLink(FILE *stream, lineBuffer *lb) {
char c;
char *buf;
@ -204,141 +212,228 @@ gemtextLink* readLink(FILE *stream, lineBuffer *lb) {
return link;
}
int parsePreformatted(gemtextParser *parser, gemtextLineQueue *lq) {
enterH1Mode(gemtextParser *parser, char c) {
lineBufferReset(&parser->buffer);
switch (c) {
case ' ':
case '\t':
break;
default:
lineBufferReset(&parser->buffer);
lineBufferAppendCharUnchecked(&parser->buffer, c);
}
parser->mode = h1Mode;
parser->state = normalState;
}
enterH2Mode(gemtextParser *parser, char c) {
lineBufferReset(&parser->buffer);
switch (c) {
case ' ':
case '\t':
break;
default:
lineBufferReset(&parser->buffer);
lineBufferAppendCharUnchecked(&parser->buffer, c);
}
parser->mode = h2Mode;
parser->state = normalState;
}
enterH3Mode(gemtextParser *parser, char c) {
lineBufferReset(&parser->buffer);
switch (c) {
case ' ':
case '\t':
break;
default:
lineBufferReset(&parser->buffer);
lineBufferAppendCharUnchecked(&parser->buffer, c);
}
parser->mode = h3Mode;
parser->state = normalState;
}
int parseLink(gemtextParser *parser, char c, gemtextLineQueue *lq) {
// todo
return 0;
}
int parseQuote(gemtextParser *parser, gemtextLineQueue *lq) {
int parsePreformatted(gemtextParser *parser, char c, gemtextLineQueue *lq) {
// todo
return 0;
}
int parseNormal(gemtextParser *parser, gemtextLineQueue *lq) {
int parseQuote(gemtextParser *parser, char c, gemtextLineQueue *lq) {
// todo
return 0;
}
int parseH1(gemtextParser *parser, char c, gemtextLineQueue *lq) {
// todo
return 0;
}
int parseH2(gemtextParser *parser, char c, gemtextLineQueue *lq) {
// todo
return 0;
}
int parseH3(gemtextParser *parser, char c, gemtextLineQueue *lq) {
// todo
return 0;
}
int parseList(gemtextParser *parser, char c, gemtextLineQueue *lq) {
// todo
return 0;
}
int parseNormal(gemtextParser *parser, char c, gemtextLineQueue *lq) {
int ret;
switch (parser->state) {
case lineStart:
switch (c) {
case '=':
parser->state = firstLinkChar;
break;
case '>':
parser->mode = quoteMode;
parser->state = lineStart;
break;
case '*':
parser->mode = listMode;
parser->state = normalState;
case '#':
parser->state = firstHashChar;
break;
case '`':
parser->state = firstBacktickChar;
break;
case '\n':
ret = gemtextParserSend(parser, normalLine, lq);
if (ret) return ret;
break;
default:
break;
}
break;
case firstLinkChar:
if (c == '>') {
parser->state = lineStart;
parser->mode = linkMode;
} else if (c == '\n') {
ret = gemtextParserSend(parser, normalLine, lq);
if (ret) return ret;
} else {
parser->state = normalState;
}
break;
case firstHashChar:
if (c == '#') {
parser->state = secondHashChar;
} else if (c == '\n') {
ret = gemtextParserSend(parser, normalLine, lq);
if (ret) return ret;
} else {
enterH1Mode(parser, c);
}
break;
case secondHashChar:
if (c == '#') {
parser->state = thirdHashChar;
} else if (c == '\n') {
ret = gemtextParserSend(parser, normalLine, lq);
if (ret) return ret;
} else {
enterH2Mode(parser, c);
}
break;
case thirdHashChar:
if (c == '\n') {
ret = gemtextParserSend(parser, normalLine, lq);
if (ret) return ret;
} else {
enterH3Mode(parser, c);
}
break;
case firstBacktickChar:
if (c == '\n') {
ret = gemtextParserSend(parser, normalLine, lq);
if (ret) return ret;
} else if (c == '`') {
parser->state = secondBacktickChar;
} else {
parser->state = normalState;
parser->mode = normalMode;
}
case secondBacktickChar:
if (c == '`') {
parser->state = thirdBacktickChar;
} else if (c == '\n') {
ret = gemtextParserSend(parser, normalLine, lq);
if (ret) return ret;
}
case thirdBacktickChar:
case normalState:
break;
}
return 0;
}
int parseGemtext(gemtextParser *parser, gemtextLineQueue *lq) {
char c;
int ret;
gemtextLine *line;
gemtextLink *link;
lineBuffer lb;
char *buf;
ret = lineBufferInit(&lb);
if (ret != 0) return ret;
while (1) {
for (;;) {
ret = fread(&c, 1, 1, parser->stream);
if (c != 1) {
if (ret == 1) {
ret = lineBufferAppendChar(&parser->buffer, c);
if (ret) {
logParseError(ret);
return ret;
}
} else {
line = calloc(1, sizeof(gemtextLine));
if (line == NULL) return errno;
line->lineType = endOfStream;
line->prev = line->next = NULL;
line->str = NULL;
gemtextLineQueuePush(lq, line);
break;
}
switch (parser->state) {
case lineStart:
switch (c) {
case '=':
ret = lineBufferAppendChar(&lb, c);
if (ret != 0) {
free(lb.buf);
switch (parser->mode) {
case normalMode:
ret = parseNormal(parser, c, lq);
break;
case preformattedMode:
ret = parsePreformatted(parser, c, lq);
break;
case quoteMode:
ret = parseQuote(parser, c, lq);
break;
case linkMode:
ret = parseLink(parser, c, lq);
break;
case h1Mode:
ret = parseH1(parser, c, lq);
break;
case h2Mode:
ret = parseH2(parser, c, lq);
break;
case h3Mode:
ret = parseH3(parser, c, lq);
break;
case listMode:
ret = parseList(parser, c, lq);
break;
}
if (ret) {
logParseError(ret);
return ret;
}
parser->state = firstLinkChar;
break;
case '>':
parser->mode = quoteMode;
ret = parseQuote(parser, lq);
if (ret != 0) {
free(lb.buf);
return ret;
}
break;
case '#':
ret = lineBufferAppendChar(&lb, c);
if (ret != 0) {
free(lb.buf);
return ret;
}
parser->state = firstHashChar;
break;
case '`':
ret = lineBufferAppendChar(&lb, c);
if (ret != 0) {
free(lb.buf);
return ret;
}
parser->state = firstBacktickChar;
break;
case '\n':
line = calloc(1, sizeof(gemtextLine));
if (line == NULL) return errno;
line->lineType = normalLine;
buf = strndup("\n", 1);
if (buf == NULL) {
free(lb.buf);
return errno;
}
line->str = buf;
gemtextLineQueuePush(lq, line);
break;
default:
ret = lineBufferAppendChar(&lb, c);
if (ret != 0) {
free(lb.buf);
return ret;
}
parser->state = normalState;
break;
}
break;
case firstLinkChar:
ret = lineBufferAppendChar(&lb, c);
if (ret != 0) {
free(lb.buf);
return ret;
}
if (c == '>') {
parser->state = secondLinkChar;
} else if (c == '\n') {
// todo - send a 'normal' line to the lineQueue and reset the
// lineBuffer
} else {
parser->state = normalState;
}
case secondLinkChar:
line = calloc(1, sizeof(gemtextLine));
if (line == NULL) return errno;
link = readLink(parser->stream, &lb);
if (link == NULL) {
line->lineType = normalLine;
buf = strndup(lb.buf, lb.len);
if (buf == NULL) {
free(lb.buf);
free(line);
return errno;
}
line->str = buf;
gemtextLineQueuePush(lq, line);
} else {
line->lineType = linkLine;
line->link = link;
gemtextLineQueuePush(lq, line);
}
lineBufferReset(&lb);
break;
case firstHashChar:
case secondHashChar:
case thirdHashChar:
case firstBacktickChar:
case secondBacktickChar:
case thirdBacktickChar:
case normalState:
}
}
return 0;
}
int parseGemtext(gemtextParser *parser, gemtextLineQueue *lq) {
// todo
return 0;
}

View file

@ -11,12 +11,17 @@ typedef enum {
normalMode,
preformattedMode,
quoteMode,
linkMode,
h1Mode,
h2Mode,
h3Mode,
listMode,
} gemtextParserMode;
typedef enum {
lineStart,
lineEnd,
firstLinkChar,
secondLinkChar,
firstHashChar,
secondHashChar,
thirdHashChar,
@ -54,6 +59,8 @@ typedef struct {
FILE *stream;
gemtextParserMode mode;
gemtextParserState state;
lineBuffer buffer;
char *linkUrl;
} gemtextParser;
struct _gemtextLine {