diff --git a/gemtext-parser.c b/gemtext-parser.c index f873883..28dc1ad 100644 --- a/gemtext-parser.c +++ b/gemtext-parser.c @@ -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); - 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; - } + switch (parser->mode) { + case normalMode: + ret = parseNormal(parser, c, lq); 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); + case preformattedMode: + ret = parsePreformatted(parser, c, lq); break; - case firstHashChar: - case secondHashChar: - case thirdHashChar: - case firstBacktickChar: - case secondBacktickChar: - case thirdBacktickChar: - case normalState: + 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; } } return 0; -} - -int parseGemtext(gemtextParser *parser, gemtextLineQueue *lq) { - // todo - return 0; } \ No newline at end of file diff --git a/include/gemtext-parser.h b/include/gemtext-parser.h index c8d741f..c9d87f9 100644 --- a/include/gemtext-parser.h +++ b/include/gemtext-parser.h @@ -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 {