Initial commit (non-working)

This commit is contained in:
Nathan Fisher 2023-10-06 01:16:55 -04:00
commit 72679eff07
6 changed files with 356 additions and 0 deletions

8
.gitignore vendored Normal file
View file

@ -0,0 +1,8 @@
test/output/
test/*
!test/*.c
!test/Makefile
*.o
*.a
*.so
*.core

58
Makefile Normal file
View file

@ -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

3
compile_flags.txt Normal file
View file

@ -0,0 +1,3 @@
-I
include

211
gemtext-parser.c Normal file
View file

@ -0,0 +1,211 @@
#include <stddef.h> // NULL, size_t
#include <stdio.h> // fclose
#include <stdlib.h> // calloc, free
#include <string.h> // 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;
}

76
include/gemtext-parser.h Normal file
View file

@ -0,0 +1,76 @@
#ifndef GEMTEXT_PARSER_H
#define GEMTEXT_PARSER_H 1
#include <pthread.h>
#include <stddef.h>
#include <stdio.h>
#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

0
test/Makefile Normal file
View file