commit 72679eff07eb15a2639abf14ecc0707ff4e0ce39 Author: Nathan Fisher Date: Fri Oct 6 01:16:55 2023 -0400 Initial commit (non-working) diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6c6dc34 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +test/output/ +test/* +!test/*.c +!test/Makefile +*.o +*.a +*.so +*.core diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..2f08020 --- /dev/null +++ b/Makefile @@ -0,0 +1,58 @@ +# _,.---._ .-._ .--.-. ,--.--------. +# _,..---._ ,-.' , - `. /==/ \ .-._/==/ //==/, - , -\ +# /==/, - \ /==/_, , - \|==|, \/ /, |==\ -\\==\.-. - ,-./ +# |==| _ _\==| .=. |==|- \| | \==\- \`--`\==\- \ +# |==| .=. |==|_ : ;=: - |==| , | -| `--`-' \==\_ \ +# |==|,| | -|==| , '=' |==| - _ | |==|- | +# |==| '=' /\==\ - ,_ /|==| /\ , | |==|, | +# |==|-, _`/ '.='. - .' /==/, | |- | /==/ -/ +# `-.`.____.' `--`--'' `--`./ `--` `--`--` +# _ __ ,---. .-._ .=-.-. _,.----. +# .-`.' ,`..--.' \ /==/ \ .-._ /==/_ /.' .' - \ +# /==/, - \==\-/\ \ |==|, \/ /, /==|, |/==/ , ,-' +# |==| _ .=. /==/-|_\ | |==|- \| ||==| ||==|- | . +# |==| , '=',\==\, - \ |==| , | -||==|- ||==|_ `-' \ +# |==|- '..'/==/ - ,| |==| - _ ||==| ,||==| _ , | +# |==|, | /==/- /\ - \|==| /\ , ||==|- |\==\. / +# /==/ - | \==\ _.\=\.-'/==/, | |- |/==/. / `-.`.___.-' +# `--`---' `--` `--`./ `--``--`-` +# +# @(#)Copyright (c) 2023, Nathan D. Fisher. +# +# This is free software. It comes with NO WARRANTY. +# Permission to use, modify and distribute this source code +# is granted subject to the following conditions. +# 1/ that the above copyright notice and this notice +# are preserved in all copies and that due credit be given +# to the author. +# 2/ that any changes to this code are clearly commented +# as such so that the author does not get blamed for bugs +# other than his own. +# + +.SUFFIXES: +.SUFFIXES: .o .c + +CFLAGS += -Wall -Werror +CFLAGS += -Iinclude + +hdrs += include/gemtext-parser.h + +srcs += gemtext-parser.c + +objs = $(srcs:.c=.o) + +libname = libgemtext-parser +staticlib = $(libname).a + +all: static + +static: $(staticlib) + +$(staticlib): $(objs) + $(AR) rcs $@ $? + +clean: + rm -rf $(objs) $(staticlib) + +.PHONY: all clean diff --git a/compile_flags.txt b/compile_flags.txt new file mode 100644 index 0000000..49d52dd --- /dev/null +++ b/compile_flags.txt @@ -0,0 +1,3 @@ +-I +include + diff --git a/gemtext-parser.c b/gemtext-parser.c new file mode 100644 index 0000000..fa162d9 --- /dev/null +++ b/gemtext-parser.c @@ -0,0 +1,211 @@ +#include // NULL, size_t +#include // fclose +#include // calloc, free +#include // memcpy + +#include "gemtext-parser.h" + +gemtextParser* gemtextParserInit(FILE *stream) { + gemtextParser *parser = calloc(1, sizeof(gemtextParser)); + if (parser == NULL) + return NULL; + parser->stream = stream; + parser->mode = normalMode; + parser->state = lineStart; + return parser; +} + +void gemtextParserDeinit(gemtextParser *parser) { + fclose(parser->stream); + free(parser); +} + +int gemtextLineQueueInit(gemtextLineQueue *queue) { + int ret; + + queue->head = NULL; + queue->tail = NULL; + ret = pthread_mutex_init(&queue->mutex, NULL); + if (ret != 0) + return ret; + return pthread_cond_init(&queue->cond, NULL); +} + +void gemtextLineQueuePush(gemtextLineQueue *queue, gemtextLine *line) { + pthread_mutex_lock(&queue->mutex); + if (queue->tail == NULL) { + queue->tail = queue->head = line; + } else { + line->next = queue->tail; + queue->tail->prev = line; + queue->tail = line; + } + queue->count++; + pthread_mutex_unlock(&queue->mutex); +} + +gemtextLine* gemtextLineQueuePop(gemtextLineQueue *lq) { + gemtextLine *line; + + while (lq->count == 0) + pthread_cond_wait(&lq->cond, &lq->mutex); + pthread_mutex_lock(&lq->mutex); + lq->count++; + line = lq->head; + if (line->lineType == endOfStream) + return line; + if (lq->tail == lq->head) { + lq->tail = lq->head = NULL; + } else { + lq->head = lq->head->prev; + } + pthread_mutex_unlock(&lq->mutex); + line->prev = line->next = NULL; + return line; +} + +lineBuffer* lineBufferInit() { + 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; +} + +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; + memcpy(buf, lb->buf, lb->len); + free(lb->buf); + lb->buf = buf; + lb->cursor = buf + lb->len; + return 0; +} + +int lineBufferAppendChar(lineBuffer *lb, char c) { + int ret = 0; + + if (lb->len >= lb->capacity - 1) { + ret = lineBufferExtend(lb, LBUF_SIZE); + if (ret != 0) return ret; + } + *lb->cursor = c; + lb->cursor++; + lb->len++; + return ret; +} + +void lineBufferAppendCharUnchecked(lineBuffer *lb, char c) { + *lb->cursor = c; + lb->cursor++; + lb->len++; +} + +int lineBufferAppendString(lineBuffer *lb, char *c, size_t len) { + int ret = 0, i = 0; + size_t rem = 0; + + // Find the remaining length + rem = lb->capacity - lb->len; + // if the length won't fit our string, extend the buffer. + // We do len - rem + LBUF_SIZE for a safety margin + if (rem < len) { + ret = lineBufferExtend(lb, len - rem + LBUF_SIZE); + if (ret != 0) return ret; + } + + for (i = 0; i < len; i++) { + // We use 'unchecked' because we did our checks above + lineBufferAppendCharUnchecked(lb, *c); + c++; + } + return ret; +} + +void lineBufferReset(lineBuffer *lb) { + lb->len = 0; + lb->cursor = lb->buf; +} + +gemtextLink* readLink(FILE *stream, lineBuffer *lb) { + char c; + int ret = 0; + gemtextLink *link = calloc(1, sizeof(gemtextLink)); + + if (link == NULL) return NULL; + while (1) { + fread(&c, 1, 1, stream); + switch (c) { + case ' ': + case '\t': + if (lb->len == 0) + continue; + link->url = strndup(lb->buf, lb->len); + break; + case '\n': + if (lb->len == 0) { + free(link); + return NULL; + } + link->url = strndup(lb->buf, lb->len); + return link; + default: + ret = lineBufferAppendChar(lb, c); + if (ret != 0) { + free(link); + return NULL; + } + } + } + lineBufferReset(lb); + while (1) { + fread(&c, 1, 1, stream); + switch (c) { + case '\n': + link->display = strndup(lb->buf, lb->len); + break; + default: + ret = lineBufferAppendChar(lb, c); + if (ret != 0) { + free(link->url); + free(link); + return NULL; + } + } + } + return link; +} + +int parseNormal(gemtextParser *parser, gemtextLineQueue *lq) { + // todo + return 0; +} + +int parsePreformatted(gemtextParser *parser, gemtextLineQueue *lq) { + // todo + return 0; +} + +int parseQuote(gemtextParser *parser, gemtextLineQueue *lq) { + // todo + 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 new file mode 100644 index 0000000..e2a6567 --- /dev/null +++ b/include/gemtext-parser.h @@ -0,0 +1,76 @@ +#ifndef GEMTEXT_PARSER_H +#define GEMTEXT_PARSER_H 1 + +#include +#include +#include + +#define LBUF_SIZE 512 + +typedef enum { + normalMode, + preformattedMode, + quoteMode, +} gemtextParserMode; + +typedef enum { + lineStart, + firstLinkChar, + firstLinkWord, + firstHashChar, + secondHashChar, + thirdHashChar, + firstBacktickChar, + secondBacktickChar, + thirdBacktickChar, + normalState, +} gemtextParserState; + +typedef enum { + normalLine, + linkLine, + listLine, + h1Line, + h2Line, + h3Line, + preformattedLine, + quoteLine, + endOfStream, +} gemtextLineType; + +typedef struct { + size_t capacity; + size_t len; + char *cursor; + char *buf; +} lineBuffer; + +typedef struct { + char *url; + char *display; +} gemtextLink; + +typedef struct { + FILE *stream; + gemtextParserMode mode; + gemtextParserState state; +} gemtextParser; + +struct _gemtextLine { + struct _gemtextLine *next; + struct _gemtextLine *prev; + gemtextLineType lineType; + char *line; +}; + +typedef struct _gemtextLine gemtextLine; + +typedef struct { + pthread_cond_t cond; + size_t count; + pthread_mutex_t mutex; + gemtextLine *head; + gemtextLine *tail; +} gemtextLineQueue; + +#endif \ No newline at end of file diff --git a/test/Makefile b/test/Makefile new file mode 100644 index 0000000..e69de29