aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHugo Hörnquist <hugo@lysator.liu.se>2019-02-23 22:18:24 +0100
committerHugo Hörnquist <hugo@lysator.liu.se>2019-02-23 22:18:24 +0100
commitfbb83b0d183f1c8d09ff10fc10017eb2f53ea60d (patch)
treed915017b469f8b7146dba913ca93bf3ac6d7cbbc
parentFix c++ compile warnings. (diff)
downloadcalp-fbb83b0d183f1c8d09ff10fc10017eb2f53ea60d.tar.gz
calp-fbb83b0d183f1c8d09ff10fc10017eb2f53ea60d.tar.xz
Comments and generalizations.
-rw-r--r--parse.c229
-rw-r--r--parse.h54
-rw-r--r--vcal.h10
3 files changed, 194 insertions, 99 deletions
diff --git a/parse.c b/parse.c
index 8f7a9102..227fd6cc 100644
--- a/parse.c
+++ b/parse.c
@@ -26,9 +26,13 @@
int parse_file(char* filename, FILE* f, vcomponent* root) {
part_context p_ctx = p_key;
- SNEW(parse_ctx, ctx, filename);
+ SNEW(parse_ctx, ctx, f, filename);
PUSH(LLIST(vcomponent))(&ctx.comp_stack, root);
+ /*
+ * Create a content_line which we use as storage while we are
+ * parsing. This object is constantly broken down and rebuilt.
+ */
SNEW(content_line, cline);
char c;
@@ -37,70 +41,25 @@ int parse_file(char* filename, FILE* f, vcomponent* root) {
/* We have a linebreak */
if (c == '\r' || c == '\n') {
- if (fold(f, &ctx, c) > 0) {
+ if (fold(&ctx, c) > 0) {
/* Actuall end of line, handle value */
-
- strbuf* target = CLINE_CUR_VAL(&cline);
-
- DEEP_COPY(strbuf)(target, &ctx.str);
- strbuf_cap(target);
- strbuf_soft_reset(&ctx.str);
-
+ TRANSFER(CLINE_CUR_VAL(&cline), &ctx.str);
handle_kv(&cline, &ctx);
-
p_ctx = p_key;
} /* Else continue on current line */
/* We have an escaped character */
} else if (c == '\\') {
- char esc = fgetc(f);
- char target;
-
- /*
- * An actuall linebreak after the backslash. Unfold the
- * line and find the next "real" character, and escape
- * that.
- */
- if (esc == '\r' || esc == '\n') {
- int ret;
- if ( (ret = fold(f, &ctx, esc)) != 0) {
- if (ret == 1) ERR_P(&ctx, "ESC before not folded line");
- else ERR_P(&ctx, "other error: val = %i", ret);
- exit (2);
- } else {
- esc = fgetc(f);
- }
- }
-
- /* Escaped new_line */
- if (esc == 'n' || esc == 'N') {
- target = '\n';
-
- /* "Standard" escaped character */
- } else if (esc == ';' || esc == ',' || esc == '\\') {
- target = esc;
-
- /* Invalid escaped character */
- } else {
- ERR_P(&ctx, "Non escapable character '%c' (%i)", esc, esc);
- }
-
- /* save escapade character as a normal character */
- strbuf_append(&ctx.str, target);
-
- ++ctx.column;
- ++ctx.pcolumn;
-
+ handle_escape (&ctx);
/* Border between param {key, value} */
} else if (p_ctx == p_param_name && c == '=') {
- LLIST(param_set)* params = CLINE_CUR_PARAMS(&cline);
+ /* Create a new parameter set and push the current string
+ * as its key */
NEW(param_set, ps);
- DEEP_COPY(strbuf)(&ps->key, &ctx.str);
- strbuf_cap(&ps->key);
- strbuf_soft_reset(&ctx.str);
- PUSH(LLIST(param_set))(params, ps);
+ TRANSFER (&ps->key, &ctx.str);
+ PUSH(LLIST(param_set))(CLINE_CUR_PARAMS(&cline), ps);
p_ctx = p_param_value;
@@ -113,24 +72,27 @@ int parse_file(char* filename, FILE* f, vcomponent* root) {
*/
} else if ((p_ctx == p_key || p_ctx == p_param_value) && (c == ':' || c == ';')) {
+ /* We got a parameter value, push the current string to
+ * the current parameter set. */
if (p_ctx == p_param_value) {
/* push kv pair */
NEW(strbuf, s);
-
- DEEP_COPY(strbuf)(s, &ctx.str);
- strbuf_cap(s);
- strbuf_soft_reset(&ctx.str);
-
- LLIST(strbuf)* ls = & CLINE_CUR_PARAMS(&cline)->cur->value->val;
- PUSH(LLIST(strbuf))(ls, s);
-
+ TRANSFER(s, &ctx.str);
+ PUSH(LLIST(strbuf))(
+ & CLINE_CUR_PARAMS(&cline)->cur->value->val,
+ s);
}
+ /*
+ * Top level key.
+ * Copy the key into the current cline, and create a
+ * content_set for the upcomming value and (possible)
+ * parameters.
+ */
if (p_ctx == p_key) {
- DEEP_COPY(strbuf)(&cline.key, &ctx.str);
- strbuf_cap(&cline.key);
- strbuf_soft_reset(&ctx.str);
+
+ TRANSFER(&cline.key, &ctx.str);
NEW(content_set, p);
PUSH(LLIST(content_set))(&cline.val, p);
@@ -139,6 +101,10 @@ int parse_file(char* filename, FILE* f, vcomponent* root) {
if (c == ':') p_ctx = p_value;
else if (c == ';') p_ctx = p_param_name;
+ /*
+ * Nothing interesting happened, append the read character to
+ * the current string.
+ */
} else {
strbuf_append(&ctx.str, c);
@@ -158,14 +124,7 @@ int parse_file(char* filename, FILE* f, vcomponent* root) {
* the end here.
*/
- strbuf* target = CLINE_CUR_VAL(&cline);
- DEEP_COPY(strbuf)(target, &ctx.str);
- strbuf_cap(target);
- strbuf_soft_reset(&ctx.str);
-
- ++ctx.line;
- ctx.column = 0;
-
+ TRANSFER(CLINE_CUR_VAL(&cline), &ctx.str);
handle_kv(&cline, &ctx);
}
@@ -181,52 +140,90 @@ int parse_file(char* filename, FILE* f, vcomponent* root) {
return 0;
}
+/*
+ * We have a complete key value pair.
+ */
int handle_kv (
content_line* cline,
parse_ctx* ctx
) {
+ /*
+ * The key being BEGIN means that we decend into a new component.
+ */
if (strbuf_c(&cline->key, "BEGIN")) {
- /* should be one of:
- * VCALENDAR, VEVENT, VALARM, VTODO, VTIMEZONE,
- * and possibly some others I forget.
- */
+ /* key \in { VCALENDAR, VEVENT, VALARM, VTODO, VTIMEZONE, ... } */
+ /*
+ * Take a copy of the name of the entered component, and store
+ * it on the stack of component names.
+ */
NEW(strbuf, s);
- strbuf* type = CLINE_CUR_VAL(cline);
- DEEP_COPY(strbuf)(s, type);
+ DEEP_COPY(strbuf)(s, CLINE_CUR_VAL(cline));
PUSH(LLIST(strbuf))(&ctx->key_stack, s);
+ /* Clear the value list in the parse content_line */
RESET(LLIST(content_set))(&cline->val);
+ /*
+ * Create the new curent component, link it with the current
+ * component in a parent/child relationship.
+ * Finally push the new component on to the top of the
+ * component stack.
+ */
NEW(vcomponent, e,
s->mem,
ctx->filename);
- e->parent = PEEK(LLIST(vcomponent))(&ctx->comp_stack);
+ vcomponent* parent = PEEK(LLIST(vcomponent))(&ctx->comp_stack);
+ e->parent = parent;
+ PUSH(vcomponent)(parent, e);
+
PUSH(LLIST(vcomponent))(&ctx->comp_stack, e);
+ /*
+ * The end of a component, go back along the stack to the previous
+ * component.
+ */
} else if (strbuf_c(&cline->key, "END")) {
- strbuf* s = POP(LLIST(strbuf))(&ctx->key_stack);
- if (strbuf_cmp(s, CLINE_CUR_VAL(cline)) != 0) {
+ strbuf* expected_key = POP(LLIST(strbuf))(&ctx->key_stack);
+
+ if (strbuf_cmp(expected_key, CLINE_CUR_VAL(cline)) != 0) {
+
ERR_P(ctx, "Expected END:%s, got END:%s.\n%s line",
- s->mem,
+ expected_key->mem,
CLINE_CUR_VAL(cline)->mem,
PEEK(LLIST(vcomponent))(&ctx->comp_stack)->filename);
- PUSH(LLIST(strbuf))(&ctx->key_stack, s);
+ PUSH(LLIST(strbuf))(&ctx->key_stack, expected_key);
+
return -1;
} else {
- FFREE(strbuf, s);
- /* Received propper end, push cur into parent */
- vcomponent* cur = POP(LLIST(vcomponent))(&ctx->comp_stack);
-
- // TODO should self instead be done at creation time?
- PUSH(vcomponent)(PEEK(LLIST(vcomponent))(&ctx->comp_stack), cur);
+ FFREE(strbuf, expected_key);
+ POP(LLIST(vcomponent))(&ctx->comp_stack);
}
+
+ /*
+ * A regular key, value pair. Push it into to the current
+ * component.
+ */
} else {
+
+ /*
+ * cline is the value store used during parsing, meaning that
+ * its values WILL mutate at a later point. Therefore we take
+ * a copy of it here.
+ */
NEW(content_line, c);
DEEP_COPY(content_line)(c, cline);
+ /*
+ * The PUSH(TRIE(T)) method handles collisions by calling
+ * RESOLVE(T). content_line resolves by merging the new value
+ * into the old value, and freeing the new value's container.
+ *
+ * This means that |c| declared above might be destroyed
+ * here.
+ */
PUSH(TRIE(content_line))(
&PEEK(LLIST(vcomponent))(&ctx->comp_stack)->clines,
c->key.mem, c);
@@ -237,12 +234,12 @@ int handle_kv (
return 0;
}
-int fold(FILE* f, parse_ctx* ctx, char c) {
+int fold(parse_ctx* ctx, char c) {
int retval;
char buf[2] = {
- (c == '\n' ? '\n' : (char) fgetc(f)),
- (char) fgetc(f)
+ (c == '\n' ? '\n' : (char) fgetc(ctx->f)),
+ (char) fgetc(ctx->f)
};
ctx->pcolumn = 1;
@@ -255,7 +252,7 @@ int fold(FILE* f, parse_ctx* ctx, char c) {
retval = 0;
ctx->pcolumn++;
- } else if (ungetc(buf[1], f) != buf[1]) {
+ } else if (ungetc(buf[1], ctx->f) != buf[1]) {
ERR_P(ctx, "Failed to put character back on FILE");
retval = -2;
@@ -271,11 +268,12 @@ int fold(FILE* f, parse_ctx* ctx, char c) {
}
-INIT_F(parse_ctx, char* filename) {
+INIT_F(parse_ctx, FILE* f, char* filename) {
INIT(LLIST(strbuf), &self->key_stack);
INIT(LLIST(vcomponent), &self->comp_stack);
self->filename = (char*) calloc(sizeof(*filename), strlen(filename) + 1);
strcpy(self->filename, filename);
+ self->f = f;
self->line = 0;
self->column = 0;
@@ -300,3 +298,46 @@ FREE_F(parse_ctx) {
return 0;
}
+
+int handle_escape (parse_ctx* ctx) {
+ char esc = fgetc(ctx->f);
+ char target;
+
+ /*
+ * Escape character '\' and escaped token sepparated by a newline
+ * (since the standard for some reason allows that (!!!))
+ * We are at least guaranteed that it's a folded line, so just
+ * unfold it and continue trying to find a token to escape.
+ */
+ if (esc == '\r' || esc == '\n') {
+ int ret;
+ if ( (ret = fold(ctx, esc)) != 0) {
+ if (ret == 1) ERR_P(ctx, "ESC before not folded line");
+ else ERR_P(ctx, "other error: val = %i", ret);
+ exit (2);
+ } else {
+ esc = fgetc(ctx->f);
+ }
+ }
+
+ /* Escaped new_line */
+ if (esc == 'n' || esc == 'N') {
+ target = '\n';
+
+ /* "Standard" escaped character */
+ } else if (esc == ';' || esc == ',' || esc == '\\') {
+ target = esc;
+
+ /* Invalid escaped character */
+ } else {
+ ERR_P(ctx, "Non escapable character '%c' (%i)", esc, esc);
+ }
+
+ /* save escapade character as a normal character */
+ strbuf_append(&ctx->str, target);
+
+ ++ctx->column;
+ ++ctx->pcolumn;
+
+ return 0;
+}
diff --git a/parse.h b/parse.h
index b69bcfa5..891936b7 100644
--- a/parse.h
+++ b/parse.h
@@ -18,6 +18,20 @@
*/
#define SEGSIZE 75
+/*
+ * Transfers a strbuf from src to target.
+ * Does this first copying the contents, followed by capping the
+ * target and reseting the src.
+ */
+#define TRANSFER(target, src) do { \
+ DEEP_COPY(strbuf)((target), (src)); \
+ strbuf_cap(target); \
+ strbuf_soft_reset(src); \
+} while (0)
+
+/*
+ * Current context for the character consumer (parse_file).
+ */
typedef enum {
p_key, p_value, p_param_name, p_param_value, p_escape
} part_context;
@@ -27,11 +41,22 @@ typedef enum {
* Kept together for simplicity.
*/
typedef struct {
+ /* Which file we are parsing, copied to all components to allow
+ * writebacks later */
char* filename;
+
+ FILE* f;
+
+ /*
+ * context stacks used since ICS files form a tree. key_stack is
+ * only for sequrity purposes.
+ */
LLIST(strbuf) key_stack;
LLIST(vcomponent) comp_stack;
- /* Number for unfolded lines */
+ /* Number for unfolded lines
+ * TODO remove this
+ * */
int line;
int column;
@@ -39,19 +64,36 @@ typedef struct {
int pline;
int pcolumn;
+ /*
+ * String which we write everything read into.
+ * Later copied to appropiate places.
+ */
strbuf str;
} parse_ctx;
-INIT_F(parse_ctx, char* filename);
+INIT_F(parse_ctx, FILE* f, char* filename);
FREE_F(parse_ctx);
+
+/*
+ * Character consumer. Reads characters from stdin until end of file.
+ * Whenever it finds a token with a special value (such as ':', ';',
+ * ...) it saves it away.
+ * Once It has parsed a full line it calls handel_kv. Which build my
+ * actuall datastructure.
+ */
+int parse_file(char* filename, FILE* f, vcomponent* cal);
+
+/*
+ * Called whenever parse_file finishes a line. Copies the contents of
+ * ctx and the current content_line into the object stack, stored in
+ * ctx.
+ */
int handle_kv(
content_line* cline,
parse_ctx* ctx
);
-int parse_file(char* filename, FILE* f, vcomponent* cal);
-
/*
* Input
* f: file to get characters from
@@ -72,6 +114,8 @@ int parse_file(char* filename, FILE* f, vcomponent* cal);
* first of NL or CR, and then ensures that the program thinks
* it got the expected CRNL.
*/
-int fold(FILE* f, parse_ctx* ctx, char c);
+int fold(parse_ctx* ctx, char c);
+
+int handle_escape (parse_ctx* ctx);
#endif /* PARSE_H */
diff --git a/vcal.h b/vcal.h
index 95811a7b..02a51ff5 100644
--- a/vcal.h
+++ b/vcal.h
@@ -10,6 +10,16 @@
// #include "trie.h"
#undef TYPE
+/*
+ * content_line:
+ * a mapping between a top level key, and everything it contains.
+ * content_set:
+ * A top level value, along with a list of kv pairs for all its
+ * possible parameters.
+ * param_set:
+ * A parameter key, along with a list of all its values.
+ */
+
#define T strbuf
#define V LLIST(strbuf)
#include "pair.h"