Periodic save
This commit is contained in:
parent
5d3e09a6ae
commit
626ebaeb30
2 changed files with 247 additions and 145 deletions
383
gemtext-parser.c
383
gemtext-parser.c
|
@ -6,6 +6,16 @@
|
||||||
|
|
||||||
#include "gemtext-parser.h"
|
#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* gemtextParserInit(FILE *stream) {
|
||||||
gemtextParser *parser = calloc(1, sizeof(gemtextParser));
|
gemtextParser *parser = calloc(1, sizeof(gemtextParser));
|
||||||
if (parser == NULL)
|
if (parser == NULL)
|
||||||
|
@ -13,11 +23,20 @@ gemtextParser* gemtextParserInit(FILE *stream) {
|
||||||
parser->stream = stream;
|
parser->stream = stream;
|
||||||
parser->mode = normalMode;
|
parser->mode = normalMode;
|
||||||
parser->state = lineStart;
|
parser->state = lineStart;
|
||||||
|
if (lineBufferInit(&parser->buffer) != 0) {
|
||||||
|
free(parser);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
parser->linkUrl = NULL;
|
||||||
return parser;
|
return parser;
|
||||||
}
|
}
|
||||||
|
|
||||||
void gemtextParserDeinit(gemtextParser *parser) {
|
void gemtextParserDeinit(gemtextParser *parser) {
|
||||||
fclose(parser->stream);
|
fclose(parser->stream);
|
||||||
|
free(parser->buffer.buf);
|
||||||
|
if (parser->linkUrl != NULL) {
|
||||||
|
free(parser->linkUrl);
|
||||||
|
}
|
||||||
free(parser);
|
free(parser);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,38 +84,6 @@ gemtextLine* gemtextLineQueuePop(gemtextLineQueue *lq) {
|
||||||
return line;
|
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) {
|
int lineBufferExtend(lineBuffer *lb, size_t len) {
|
||||||
char *buf = calloc(1, lb->capacity + len);
|
char *buf = calloc(1, lb->capacity + len);
|
||||||
if (buf == NULL) return 2;
|
if (buf == NULL) return 2;
|
||||||
|
@ -152,6 +139,27 @@ void lineBufferReset(lineBuffer *lb) {
|
||||||
lb->cursor = lb->buf;
|
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) {
|
gemtextLink* readLink(FILE *stream, lineBuffer *lb) {
|
||||||
char c;
|
char c;
|
||||||
char *buf;
|
char *buf;
|
||||||
|
@ -204,141 +212,228 @@ gemtextLink* readLink(FILE *stream, lineBuffer *lb) {
|
||||||
return link;
|
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
|
// todo
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int parseQuote(gemtextParser *parser, gemtextLineQueue *lq) {
|
int parsePreformatted(gemtextParser *parser, char c, gemtextLineQueue *lq) {
|
||||||
// todo
|
// todo
|
||||||
return 0;
|
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;
|
char c;
|
||||||
int ret;
|
int ret;
|
||||||
gemtextLine *line;
|
gemtextLine *line;
|
||||||
gemtextLink *link;
|
|
||||||
lineBuffer lb;
|
|
||||||
char *buf;
|
|
||||||
|
|
||||||
ret = lineBufferInit(&lb);
|
for (;;) {
|
||||||
if (ret != 0) return ret;
|
|
||||||
while (1) {
|
|
||||||
ret = fread(&c, 1, 1, parser->stream);
|
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));
|
line = calloc(1, sizeof(gemtextLine));
|
||||||
if (line == NULL) return errno;
|
if (line == NULL) return errno;
|
||||||
line->lineType = endOfStream;
|
line->lineType = endOfStream;
|
||||||
line->prev = line->next = NULL;
|
line->prev = line->next = NULL;
|
||||||
line->str = NULL;
|
line->str = NULL;
|
||||||
gemtextLineQueuePush(lq, line);
|
gemtextLineQueuePush(lq, line);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
switch (parser->state) {
|
switch (parser->mode) {
|
||||||
case lineStart:
|
case normalMode:
|
||||||
switch (c) {
|
ret = parseNormal(parser, c, lq);
|
||||||
case '=':
|
|
||||||
ret = lineBufferAppendChar(&lb, c);
|
|
||||||
if (ret != 0) {
|
|
||||||
free(lb.buf);
|
|
||||||
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;
|
break;
|
||||||
case firstLinkChar:
|
case preformattedMode:
|
||||||
ret = lineBufferAppendChar(&lb, c);
|
ret = parsePreformatted(parser, c, lq);
|
||||||
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;
|
break;
|
||||||
case firstHashChar:
|
case quoteMode:
|
||||||
case secondHashChar:
|
ret = parseQuote(parser, c, lq);
|
||||||
case thirdHashChar:
|
break;
|
||||||
case firstBacktickChar:
|
case linkMode:
|
||||||
case secondBacktickChar:
|
ret = parseLink(parser, c, lq);
|
||||||
case thirdBacktickChar:
|
break;
|
||||||
case normalState:
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int parseGemtext(gemtextParser *parser, gemtextLineQueue *lq) {
|
|
||||||
// todo
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -11,12 +11,17 @@ typedef enum {
|
||||||
normalMode,
|
normalMode,
|
||||||
preformattedMode,
|
preformattedMode,
|
||||||
quoteMode,
|
quoteMode,
|
||||||
|
linkMode,
|
||||||
|
h1Mode,
|
||||||
|
h2Mode,
|
||||||
|
h3Mode,
|
||||||
|
listMode,
|
||||||
} gemtextParserMode;
|
} gemtextParserMode;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
lineStart,
|
lineStart,
|
||||||
|
lineEnd,
|
||||||
firstLinkChar,
|
firstLinkChar,
|
||||||
secondLinkChar,
|
|
||||||
firstHashChar,
|
firstHashChar,
|
||||||
secondHashChar,
|
secondHashChar,
|
||||||
thirdHashChar,
|
thirdHashChar,
|
||||||
|
@ -54,6 +59,8 @@ typedef struct {
|
||||||
FILE *stream;
|
FILE *stream;
|
||||||
gemtextParserMode mode;
|
gemtextParserMode mode;
|
||||||
gemtextParserState state;
|
gemtextParserState state;
|
||||||
|
lineBuffer buffer;
|
||||||
|
char *linkUrl;
|
||||||
} gemtextParser;
|
} gemtextParser;
|
||||||
|
|
||||||
struct _gemtextLine {
|
struct _gemtextLine {
|
||||||
|
|
Loading…
Add table
Reference in a new issue