diff options
Diffstat (limited to '')
-rw-r--r-- | Makefile | 20 | ||||
-rw-r--r-- | parse.c | 220 | ||||
-rw-r--r-- | strbuf.c | 104 | ||||
-rw-r--r-- | strbuf.h | 65 | ||||
-rw-r--r-- | vcal.c | 45 | ||||
-rw-r--r-- | vcal.h | 29 |
6 files changed, 423 insertions, 60 deletions
@@ -1,3 +1,5 @@ +.PHONY: all clean + CC := gcc LEX := flex @@ -5,14 +7,18 @@ CFLAGS = -Wall -ggdb #LFLAGS = #LDFLAGS = -#%.yy.c : %.yy -# $(LEX) -o $@ ${LFLAGS} $< +C_FILES = $(wildcard *.c) +O_FILES = $(C_FILES:.c=.o) +H_FILES = $(wildcard *.h) -%.o : %.c - $(CC) -c -o $@ $^ ${CFLAGS} +all: parse -parse: parse.o - $(CC) -o $@ $^ ${LDFLAGS} +%.o : %.c $(H_FILES) + $(CC) -c -o $@ $< ${CFLAGS} -test: test.yy.o +parse: $(O_FILES) $(CC) -o $@ $^ ${LDFLAGS} + +clean: + -rm parse + -rm *.o @@ -1,15 +1,20 @@ +#define SAFE_STR + +/* + * TODO currently not all pointers inside strings are reset correctly, + * leading to old garbage data being read way to much. + * + * A better ERR macro would solve most problems, + * along with the introduction of a MSG macro. + */ + #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> -typedef enum { - key, value -} context; - -typedef struct { - char *key, *value; -} kvpair; +#include "strbuf.h" +#include "vcal.h" /* * Max length of a line. @@ -18,28 +23,44 @@ typedef struct { */ #define SEGSIZE 75 -int ii = 0; +#define ERR(x, line) do { \ + fprintf(stderr, "ERR %i: %s (cal %i)\n", __LINE__, (x), (line)); \ +} while(0) -#define MARK do { printf("%i -- %i\n", __LINE__, ii++); } while (0) +typedef enum { + p_key, p_value +} part_context; -#define ERR(x) do { \ - fprintf(stderr, "ERR %i: %s", __LINE__, (x)); \ -} while(0) +typedef enum { + s_none, s_calendar, s_event +} scope_context; +int handle_kv(vcalendar* cal, vevent* ev, string* key, string* val, int line, scope_context* s_ctx); -int main (int argc, char* argv[argc]) { - FILE* F = fopen(argv[1], "r"); +int parse_file(char* fname, vcalendar* cal) { + FILE* f = fopen(fname, "r"); int segments = 1; - char* str = malloc(segments * SEGSIZE); - int i = 0; + string str; + init_string (&str, segments * SEGSIZE); - context ctx = key; - kvpair kvs[100]; - int ki = 0; + part_context p_ctx = p_key; + scope_context s_ctx = s_none; + + int keylen = 100; + int vallen = 100; + + string key, val; + + init_string(&key, keylen); + init_string(&val, vallen); + + int line = 0; + + vevent ev; char c; - while ( (c = fgetc(F)) != EOF) { + while ( (c = fgetc(f)) != EOF) { /* * A carrige return means that the current line is at an * end. The following character should always be \n. @@ -49,62 +70,155 @@ int main (int argc, char* argv[argc]) { if (c == '\r') { char s[2]; - s[0] = fgetc(F); - s[1] = fgetc(F); + s[0] = fgetc(f); + s[1] = fgetc(f); - if (s[0] != '\n') { ERR("expected newline after CR"); } + if (s[0] != '\n') { ERR("expected newline after CR", line); } else if (s[1] == ' ' || s[1] == '\t') { - MARK; // TODO check return value // TODO segments is always incremented here, meaning // that segment grows larger for every multi line // encountered. - str = realloc(str, ++segments * SEGSIZE); - if (str == NULL) { /* TODO signal error */ +#if 0 + if (realloc_string(&str, ++segments * SEGSIZE) != 0) { /* TODO signal error */ + ERR("Failed to realloc string", line); exit (1); } +#endif continue; } else { - MARK; - if (ungetc(s[1], F) != s[1]) { /* TODO signal error */ + if (ungetc(s[1], f) != s[1]) { /* TODO signal error */ exit (2); } /* At TRUE end of line */ - kvs[ki].value = malloc(i + 1); - memcpy(kvs[ki].value, str, i); - kvs[ki].value[i] = 0; - ki++; - i = 0; - ctx = key; + if (str.ptr + 1 > vallen) { + vallen = str.ptr + 1; + realloc_string(&val, vallen); + } + copy_strbuf(&val, &str); + *strbuf_cur(&val) = 0; + + ++line; + + /* We just got a value */ + /* TODO for some reason both key and val is empty here */ + handle_kv(cal, &ev, &key, &val, line, &s_ctx); + strbuf_soft_reset(&str); + p_ctx = p_key; + continue; } - } else if (ctx == key && c == ':') { - kvs[ki].key = malloc(i + 1); - memcpy(kvs[ki].key, str, i); - kvs[ki].key[i] = 0; - printf("key := %s\n", kvs[ki].key); - i = 0; - ctx = value; + } else if (p_ctx == p_key && c == ':') { + /* + if (str.ptr + 1 > keylen) { + keylen = str.ptr + 1; + // TODO this might break everything + realloc_string(&key, keylen); + } + */ + copy_strbuf(&key, &str); + *strbuf_end(&key) = 0; + strbuf_soft_reset(&str); + p_ctx = p_value; continue; } - str[i] = c; - ++i; + strbuf_append(&str, c); } if (errno != 0) { - printf("Error parsing, errno = %i\n", errno); + ERR("Error parsing", -1); + } else { + /* + * Close last pair if the file is lacking trailing whitespace. + * A file with trailing whitespace would however fail. + * TODO check the spec and adjust accordingly + */ + if (str.ptr + 1 > vallen) { + vallen = str.ptr + 1; + realloc_string(&val, vallen); + } + copy_strbuf(&val, &str); + *strbuf_cur(&val) = 0; } - puts("File parsed"); - free(str); - + // TODO this segfaults /* - * Just print and free all collected data - */ - for (int i = 0; i < ki; i++) { - printf("[%s] := [%s]\n", kvs[i].key, kvs[i].value); - free(kvs[i].key); - free(kvs[i].value); + free_vevent(&ev); + free_string(&str); + free_string(&key); + free_string(&val); + */ + + // fclose(f); + + return 0; +} + +int main (int argc, char* argv[argc]) { + if (argc < 2) { + puts("Please give a ics file as first argument"); + exit (1); + } + vcalendar cal; + init_vcalendar(&cal); + parse_file(argv[1], &cal); + + printf("Parsed calendar file containing [%lu] events\n\n", cal.n_events); + for (size_t i = 0; i < cal.n_events; i++) { + printf("%lu: %s\n", i, cal.events[i].summary.mem); + } + + free_vcalendar(&cal); +} + +int handle_kv(vcalendar* cal, vevent* ev, string* key, string* val, int line, scope_context* s_ctx) { + switch (*s_ctx) { + case s_none: + /* Both key and val is null here */ + if (! (strbuf_c(key, "BEGIN") && strbuf_c(val, "VCALENDAR"))) { + ERR("Invalid start of calendar", line); + return 1; + } + *s_ctx = s_calendar; + break; + case s_calendar: + if (strbuf_c(key, "BEGIN")) { + if (strbuf_c(val, "VEVENT")) { + *s_ctx = s_event; + break; + } else { + ERR("Unsupported start", line); + return 2; + } + } else if (strbuf_c(key, "END")) { + if (strbuf_c(val, "VCALENDAR")) { + *s_ctx = s_none; + /* GOTO cleanup */ + break; + } + } + break; + case s_event: + /* */ if (strbuf_c(key, "DTSTART")) { + strbuf_init_copy(&ev->dtstart, val); + } else if (strbuf_c(key, "DTEND")) { + strbuf_init_copy(&ev->dtend, val); + } else if (strbuf_c(key, "SUMMARY")) { + strbuf_init_copy(&ev->summary, val); + } else if (strbuf_c(key, "DESCRIPTION")) { + strbuf_init_copy(&ev->description, val); + } else if (strbuf_c(key, "END")) { + if (strbuf_c(val, "VEVENT")) { + push_event(cal, ev); + *s_ctx = s_calendar; + } else { + ERR("Trying to end something, expected VEVENT", line); + return 3; + } + } + + break; } + return 0; } diff --git a/strbuf.c b/strbuf.c new file mode 100644 index 00000000..2e25cf71 --- /dev/null +++ b/strbuf.c @@ -0,0 +1,104 @@ +#include "strbuf.h" + +#include <string.h> + +int init_string(string* str, size_t len) { + str->mem = malloc(len); + str->alloc = len; + str->ptr = 0; + str->len = 0; + return 0; +} + +int realloc_string(string* str, size_t len) { +#ifdef SAFE_STR + if (str->mem == NULL || str->alloc == 0) { + ERR("String memory not initialized"); + return 1; + } +#endif + str->mem = realloc(str->mem, len); + str->alloc = len; + return 0; +} + +int free_string(string* str) { +#ifdef SAFE_STR + if (str->alloc == 0) { + ERR("String not allocated"); + return 1; + } +#endif + free (str->mem); + str->alloc = 0; + return 0; +} + +int strbuf_append(string* s, char c) { + s->mem[s->len] = c; + s->ptr = ++s->len; + return 0; +} + +int copy_strbuf(string* dest, string* src) { +#ifdef SAFE_STR + if (dest->alloc < src->len) { + ERR("Not enough memmory allocated"); + return 1; + } +#endif + dest->len = src->len; + memcpy(dest->mem, src->mem, src->len); + return 0; +} + +int strbuf_cmp(string* a, string* b) { + return strcmp(a->mem, b->mem); +} + +int strbuf_c(string* a, char* b) { + return strcmp(a->mem, b) == 0; +} + +char* charat(string* s, int idx) { +#ifdef SAFE_STR + if (idx > s->len) { + ERR("Index out of bounds"); + return -1; + } +#endif + return &s->mem[idx]; +} + +char* strbuf_cur(string* s) { + return &s->mem[s->ptr]; +} + +int strbuf_reset(string* s) +{ + s->ptr = 0; + return 0; +} + +int strbuf_init_copy(string* dest, string* src) { +#ifdef SAFE_STR + if (dest->alloc != 0) { + ERR("Dest already allocated", -1); + return 1; + } +#endif + + init_string(dest, src->alloc); + copy_strbuf(dest, src); + + return 0; +} + +char* strbuf_end(string* s) { + return &s->mem[s->len]; +} + +int strbuf_soft_reset(string* s) { + s->ptr = s->len = 0; + return 0; +} diff --git a/strbuf.h b/strbuf.h new file mode 100644 index 00000000..ef6ea61f --- /dev/null +++ b/strbuf.h @@ -0,0 +1,65 @@ +#ifndef STRBUF_H +#define STRBUF_H + +#include <stdlib.h> + +typedef struct { + char* mem; + size_t ptr; + size_t alloc; + size_t len; +} string; + +/* + * TODO rename everything to be on the form + * strbuf_.* + * + * TODO Check memmory allocation for last +1 byte for null. + */ + +/* + * Constructor + */ +int init_string(string* str, size_t len); + +/* + * Like realloc, but for strbuf + */ +int realloc_string(string* str, size_t len); + +/* + * Free's contents of str, but keeps str. + */ +int free_string(string* str); + +/* + * Copy contents from src to dest. + * Assumes that dest is already initialized. + * + * also see: strbuf_alloc_copy + */ +int copy_strbuf(string* dest, string* src); +int strbuf_cmp(string* a, string* b); +int strbuf_c(string* a, char* b); +int strbuf_append(string* a, char c); +int strbuf_reset(string* s); +char* charat(string* s, int idx); +char* strbuf_cur(string* s); + +/* + * Sets the length and seek ptr to 0, but doesn't touch the memmory. + */ +int strbuf_soft_reset(string* s); + +/* + * Returns the character after the last, so where null hopefully is. + */ +char* strbuf_end(string* s); + +/* + * Copies contents from src to dest, also allocating dest in the + * process. dest should not be initialized before this call. + */ +int strbuf_init_copy(string* dest, string* src); + +#endif /* STRBUF_H */ @@ -0,0 +1,45 @@ +#include "vcal.h" + +#include <string.h> + +int copy_vevent(vevent* dest, vevent* src) { + copy_strbuf(&dest->dtstart , &src->dtstart); + copy_strbuf(&dest->dtend , &src->dtend); + copy_strbuf(&dest->summary , &src->summary); + copy_strbuf(&dest->description , &src->description); + return 0; +} + +int free_vevent (vevent* ev) { + free_string(&ev->dtstart); + free_string(&ev->dtend); + free_string(&ev->summary); + free_string(&ev->description); + return 0; +} + +int push_event(vcalendar* cal, vevent* ev) { + if (cal->n_events + 1> cal->alloc) { + cal->alloc <<= 1; + } + cal->events = realloc(cal->events, cal->alloc); + copy_vevent(&cal->events[cal->n_events], ev); + cal->n_events++; + return 0; +} + +int init_vcalendar(vcalendar* cal) { + cal->events = malloc(sizeof(*cal->events)); + cal->alloc = 1; + cal->n_events = 0; + return 0; +} + +int free_vcalendar (vcalendar* cal) { + for (size_t i = 0; i < cal->n_events; i++) { + vevent* v = & cal->events[i]; + free_vevent(v); + free(v); + } + return 0; +} @@ -0,0 +1,29 @@ +#ifndef VCAL_H +#define VCAL_H + +#include <stdlib.h> + +#include "strbuf.h" + +typedef struct { + string dtstart; + string dtend; + string summary; + string description; +} vevent; + +int copy_vevent(vevent* dest, vevent* src); +int free_vevent(vevent* ev); + +typedef struct { + size_t n_events; + size_t alloc; + vevent* events; +} vcalendar; + +int init_vcalendar(vcalendar* cal); +int free_vcalendar(vcalendar* cal); + +int push_event(vcalendar* cal, vevent* ev); + +#endif /* VCAL_H */ |