138 lines
4.5 KiB
C
138 lines
4.5 KiB
C
/* Copyright 2008, 2012 Neil Edelman, distributed under the terms of the
|
|
GNU General Public License, see copying.txt */
|
|
|
|
/* Parsing of strings.
|
|
* Created by Neil Edelman on 2008-03-21. */
|
|
|
|
#include <stdio.h> /* [f]printf FILE */
|
|
#include <stdlib.h> /* malloc */
|
|
#include <string.h> /* strstr, strpbrk */
|
|
#include "Widget.h"
|
|
#include "Parser.h"
|
|
|
|
/* private */
|
|
static const int maxRecursion = 16;
|
|
|
|
/* public */
|
|
struct Parser {
|
|
char *str;
|
|
char *pos;
|
|
char *rew;
|
|
int recursion;
|
|
};
|
|
/* private - this is the list of 'widgets', see Widget.c - add widgets to here
|
|
to make them recognised - ASCIIbetical */
|
|
struct Symbol {
|
|
char *symbol;
|
|
int (*handler)(const struct Files *, FILE *fp);
|
|
int onlyInRoot; /* fixme: not used, just trust the users? haha */
|
|
} static const sym[] = {
|
|
{ "content", &WidgetContent, 0 }, /* index */
|
|
{ "date", &WidgetDate, 0 }, /* news */
|
|
{ "filealt", &WidgetFilealt, 0 }, /* files */
|
|
{ "filedesc", &WidgetFiledesc, 0 }, /* files */
|
|
{ "filehref", &WidgetFilehref, 0 }, /* files */
|
|
{ "fileicon", &WidgetFileicon, 0 }, /* files */
|
|
{ "filename", &WidgetFilename, 0 }, /* files */
|
|
{ "files", &WidgetFiles, -1 }, /* index */
|
|
{ "filesize", &WidgetFilesize, 0 }, /* files */
|
|
/*{ "folder", 0, -1 }, *//* replaced by ~ - scetchy */
|
|
{ "htmlcontent",&WidgetContent,0 }, /* index */
|
|
{ "news", &WidgetNews, 0 }, /* news */
|
|
{ "newsname", &WidgetNewsname, 0 }, /* news */
|
|
{ "now", &WidgetNow, 0 }, /* any */
|
|
{ "pwd", &WidgetPwd, -1 }, /* don't put it where it doesn't make sense */
|
|
{ "root", &WidgetRoot, -1 }, /* like pwd exept up instead of dn */
|
|
{ "title", &WidgetTitle, 0 } /* news */
|
|
};
|
|
|
|
/* private */
|
|
int parse(struct Parser *p, int invisible, FILE *fp);
|
|
|
|
struct Parser *Parser(const char *str) {
|
|
struct Parser *p;
|
|
p = malloc(sizeof(struct Parser));
|
|
p->str = (char *)str;
|
|
p->pos = p->str;
|
|
p->rew = p->str;
|
|
p->recursion = 0;
|
|
return p;
|
|
}
|
|
void Parser_(struct Parser *p) {
|
|
if(!p) return;
|
|
if(p->recursion) fprintf(stderr, "Parser~: a file was closed with recursion level of %d; syntax error?\n", p->recursion);
|
|
free(p);
|
|
}
|
|
|
|
/** binary search */
|
|
const struct Symbol *match(const char *str, const char *end) {
|
|
const int N = sizeof(sym) / sizeof(struct Symbol);
|
|
int a, lenMatch, lenComp, lo = 0, mid, hi = N - 1;
|
|
lenMatch = end - str;
|
|
while(lo <= hi) {
|
|
mid = (lo + hi) >> 1;
|
|
/* this is highly inefficient */
|
|
lenComp = strlen(sym[mid].symbol);
|
|
a = strncmp(str, sym[mid].symbol, lenMatch > lenComp ? lenMatch : lenComp);
|
|
if (a < 0) hi = mid - 1;
|
|
else if(a > 0) lo = mid + 1;
|
|
else return &sym[mid];
|
|
}
|
|
return 0;
|
|
}
|
|
void ParserRewind(struct Parser *p) {
|
|
if(!p) return;
|
|
p->pos = p->rew;
|
|
}
|
|
/** parse, called recusively (invisible, hack) fixme: this fn needs rewriting, messy */
|
|
int ParserParse(struct Parser *p, const struct Files *f, int invisible, FILE *fp) {
|
|
char *mark;
|
|
if(!p || !fp || !p->pos) return 0;
|
|
if(++p->recursion > maxRecursion) fprintf(stderr, "Parser::parse: %d recursion levels reached (Ctrl-C to stop!)\n", p->recursion);
|
|
mark = p->pos;
|
|
if(p->recursion == 1) p->rew = mark;
|
|
for( ; ; ) {
|
|
p->pos = strpbrk(p->pos, "@}~");
|
|
if(!p->pos) {
|
|
if(!invisible) fprintf(fp, "%.*s", (int)(p->pos - mark), mark);
|
|
break;
|
|
} else if(*p->pos == '}') {
|
|
if(!invisible) fprintf(fp, "%.*s", (int)(p->pos - mark), mark);
|
|
p->pos++;
|
|
break;
|
|
} else if(*p->pos == '~') {
|
|
/* a tilde on a line by itself */
|
|
if(p->recursion == 1 &&
|
|
((p->pos <= p->str || *(p->pos - 1) == '\n') &&
|
|
*(p->pos + 1) == '\n')) {
|
|
if(!invisible) fprintf(fp, "%.*s", (int)(p->pos - mark), mark);
|
|
p->pos += 2; /* "~\n" */
|
|
p->recursion = 0;
|
|
return -1;
|
|
}
|
|
p->pos++;
|
|
} else if(*(p->pos + 1) == '(') { /* the only one left is @ */
|
|
const struct Symbol *m;
|
|
int over = 0, open;
|
|
char *start = p->pos + 2, *end;
|
|
if(!invisible) fprintf(fp, "%.*s", (int)(p->pos - mark), mark);
|
|
if(!(end = strpbrk(start, ")"))) break; /* syntax error */
|
|
if(!(m = match(start, end))) fprintf(stderr, "Parser::parse: symbol not reconised, '%.*s.'\n", (int)(end - start), start);
|
|
/* if(p->recursion == 1 && m && m->onlyInRoot) return -1; ? */
|
|
open = *(end + 1) == '{' ? -1 : 0;
|
|
do {
|
|
/* -> widget.c */
|
|
if(m && m->handler && !invisible) over = m->handler(f, fp);
|
|
p->pos = end + (open ? 2 : 1);
|
|
/* recurse between {} */
|
|
if(open) ParserParse(p, f, invisible || !over, fp);
|
|
} while(over);
|
|
mark = p->pos;
|
|
} else { /* @ by itself */
|
|
p->pos++;
|
|
}
|
|
}
|
|
p->recursion--;
|
|
return 0;
|
|
}
|