Initial commit (non-working)
This commit is contained in:
commit
72679eff07
6 changed files with 356 additions and 0 deletions
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
test/output/
|
||||||
|
test/*
|
||||||
|
!test/*.c
|
||||||
|
!test/Makefile
|
||||||
|
*.o
|
||||||
|
*.a
|
||||||
|
*.so
|
||||||
|
*.core
|
58
Makefile
Normal file
58
Makefile
Normal 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
3
compile_flags.txt
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
-I
|
||||||
|
include
|
||||||
|
|
211
gemtext-parser.c
Normal file
211
gemtext-parser.c
Normal 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
76
include/gemtext-parser.h
Normal 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
0
test/Makefile
Normal file
Loading…
Add table
Reference in a new issue