aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorHugo Hörnquist <hugo@lysator.liu.se>2019-03-22 20:11:11 +0100
committerHugo Hörnquist <hugo@lysator.liu.se>2019-03-22 20:17:52 +0100
commitd46183860c1f3f10095e95023adcb79b1896ab0e (patch)
treedd331a0efe9777bfe84160139da1e39df3226b71 /src
parentAdd stuff to test.scm. (diff)
downloadcalp-d46183860c1f3f10095e95023adcb79b1896ab0e.tar.gz
calp-d46183860c1f3f10095e95023adcb79b1896ab0e.tar.xz
Move C and Scheme code into subdirs.
Diffstat (limited to 'src')
-rw-r--r--src/calendar.c140
-rw-r--r--src/calendar.h41
-rw-r--r--src/err.h42
-rw-r--r--src/graphs.c144
-rw-r--r--src/graphs.h15
-rw-r--r--src/guile_interface.h28
-rw-r--r--src/guile_interface.scm.c220
-rw-r--r--src/guile_type_helpers.c13
-rw-r--r--src/guile_type_helpers.h13
-rw-r--r--src/linked_list.h92
-rw-r--r--src/linked_list.inc.h176
-rw-r--r--src/macro.h134
-rw-r--r--src/main.c91
-rw-r--r--src/pair.h19
-rw-r--r--src/pair.inc.h34
-rw-r--r--src/parse.c351
-rw-r--r--src/parse.h122
-rw-r--r--src/strbuf.c151
-rw-r--r--src/strbuf.h109
-rw-r--r--src/termios.scm.c44
-rw-r--r--src/trie.h54
-rw-r--r--src/trie.inc.h228
-rw-r--r--src/vcal.c152
-rw-r--r--src/vcal.h118
24 files changed, 2531 insertions, 0 deletions
diff --git a/src/calendar.c b/src/calendar.c
new file mode 100644
index 00000000..edc1151e
--- /dev/null
+++ b/src/calendar.c
@@ -0,0 +1,140 @@
+#include "calendar.h"
+
+#include <sys/stat.h>
+#include <dirent.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include "parse.h"
+#include "err.h"
+
+int read_vcalendar(vcomponent* cal, char* path) {
+
+ struct stat statbuf;
+ if (stat (path, &statbuf) != 0) {
+ fprintf(stderr,
+ "Error stating file or directory, errno = %i\npath = [%s]\n",
+ errno, path);
+ }
+
+ int type = statbuf.st_mode & 0777000;
+ int chmod = statbuf.st_mode & 0777;
+ INFO_F("file has mode 0%o, with chmod = 0%o", type, chmod);
+
+ switch (type) {
+ case S_IFREG: handle_file(cal, path); break;
+ case S_IFDIR: handle_dir (cal, path); break;
+ case S_IFLNK:
+ ERR("Found symlink, can't be bothered to check it further.");
+ break;
+
+ default: ;
+ }
+
+ return 0;
+}
+
+int handle_file(vcomponent* cal, char* path) {
+ INFO("Parsing a single file");
+
+ vcomponent_push_val(cal, "NAME", path);
+ vcomponent_push_val(cal, "TYPE", "file");
+ char* resolved_path = realpath(path, NULL);
+ open_ics (resolved_path, cal);
+ free (resolved_path);
+
+ return 0;
+}
+
+
+int handle_dir(vcomponent* cal, char* path) {
+ INFO("Parsing a directory");
+ DIR* dir = opendir(path);
+
+ /* Buffer for holding search path and filename */
+ char buf[PATH_MAX] = { [0 ... PATH_MAX - 1] = '\0' };
+ strcpy(buf, path);
+ int path_len = strlen(path) + 1;
+
+ /* Slash to guarantee we have at least one */
+ buf[path_len - 1] = '/';
+
+
+ vcomponent_push_val(cal, "NAME", path);
+ vcomponent_push_val(cal, "TYPE", "vdir");
+
+ struct dirent* d;
+ while ((d = readdir(dir)) != NULL) {
+ /* Check that it's a regular file */
+ if (d->d_type != DT_REG) continue;
+
+ /* Append filename with currentt searchpath */
+ strcat(buf, d->d_name);
+ char* resolved_path = realpath(buf, NULL);
+ /* Remove file part from combined path */
+ buf[path_len] = '\0';
+
+ FILE* f;
+ char info_buf[0x100];
+ if (strcmp (d->d_name, "color") == 0) {
+ f = fopen(resolved_path, "r");
+ fgets(info_buf, 0x100, f);
+ fclose(f);
+ vcomponent_push_val(cal, "COLOR", info_buf);
+ } else if (strcmp (d->d_name, "displayname") == 0) {
+ f = fopen(resolved_path, "r");
+ fgets(info_buf, 0x100, f);
+ fclose(f);
+ // TODO make sure that this replaces
+ vcomponent_push_val(cal, "NAME", info_buf);
+ } else {
+ open_ics (resolved_path, cal);
+ }
+
+ free (resolved_path);
+ }
+
+ closedir(dir);
+ return 0;
+}
+
+int get_extension(const char* filename, char* ext, ssize_t max_len) {
+ int ext_idx = -1;
+ ext[0] = '\0';
+ for (int i = 0; filename[i] != '\0'; i++) {
+ if (filename[i] == '.') ext_idx = i + 1;
+ if (filename[i] == '/') ext_idx = -1;
+ }
+
+ if (ext_idx == -1) return 0;
+
+ int ext_len = 0;
+ for (int i = 0; i < max_len; i++, ext_len++) {
+ char c = filename[i + ext_idx];
+ if (c == '\0') break;
+ ext[i] = c;
+ }
+ ext[ext_len] = '\0';
+ return ext_len;
+}
+
+int check_ext (const char* path, const char* ext) {
+ char buf[10];
+ int has_ext = get_extension(path, buf, 9);
+
+ return has_ext && strcmp(buf, ext) == 0;
+}
+
+int open_ics (char* resolved_path, vcomponent* cal) {
+ if (! check_ext(resolved_path, "ics") ) return 2;
+
+ FILE* f = fopen(resolved_path, "r");
+
+ if (f == NULL) return 1;
+
+ parse_file(resolved_path, f, cal);
+ fclose(f);
+
+ return 0;
+}
diff --git a/src/calendar.h b/src/calendar.h
new file mode 100644
index 00000000..20b78a9f
--- /dev/null
+++ b/src/calendar.h
@@ -0,0 +1,41 @@
+#ifndef CALENDAR_H
+#define CALENDAR_H
+
+#include "vcal.h"
+
+/*
+ * Reads all ics flies in path into the given vcomponent. The
+ * component is assumed to be a abstract ROOT element, whose first
+ * component will most likely become a VCALENDAR.
+ *
+ * path should either be a single .ics file (vcalendar), or a
+ * directory directly containing .ics files (vdir).
+ */
+int read_vcalendar(vcomponent* cal, char* path);
+
+/*
+ * Gets extension from filename. Writes output to ext.
+ * Assumes that the extension is the text between the last dot and
+ * the end of the string, and that no slashes can occur between the
+ * dot and the end.
+ *
+ * Returns the length of the extension, 0 if no extension.
+ */
+int get_extension(const char* filename, char* ext, ssize_t max_len);
+
+/* Returns 1 if path has extension ext, 0 otherwise */
+int check_ext (const char* path, const char* ext);
+
+/* Handle a lone ics file */
+int handle_file(vcomponent* cal, char* path);
+
+/* Handle a directory of ics files */
+int handle_dir(vcomponent* cal, char* path);
+
+/*
+ * Helper for opening a single ICS file. Handles file internally, and
+ * writes output to cal.
+ */
+int open_ics (char* resolved_path, vcomponent* cal);
+
+#endif /* CALENDAR_H */
diff --git a/src/err.h b/src/err.h
new file mode 100644
index 00000000..d9d19ec7
--- /dev/null
+++ b/src/err.h
@@ -0,0 +1,42 @@
+#ifndef ERR_H
+#define ERR_H
+
+#include <stdio.h>
+
+#include "macro.h"
+
+#define _RESET "\x1b[m"
+#define _BLACK "\x1B[0;30m"
+#define _RED "\x1B[0;31m"
+#define _GREEN "\x1B[0;32m"
+#define _YELLOW "\x1B[0;33m"
+#define _BLUE "\x1B[0;34m"
+#define _PURPLE "\x1B[0;35m"
+#define _CYAN "\x1B[0;36m"
+#define _WHITE "\x1B[0;37m"
+
+#define ERR(msg) fprintf(stderr, _RED "ERR" _RESET " (%s:%i) %s\n", __FILE__, __LINE__, #msg)
+#define ERR_F(fmt, ...) fprintf(stderr, _RED "ERR" _RESET " (%s:%i) " fmt "\n", \
+ __FILE__, __LINE__, ##__VA_ARGS__)
+
+/* Parse error */
+#define ERR_P(ctx, fmt, ...) fprintf(stderr, _RED "PARSE" _RESET " (%s:%i) %i:%i " fmt "\n", \
+ __FILE__, __LINE__, (ctx)->pline, (ctx)->pcolumn, ##__VA_ARGS__)
+
+#define INFO(msg) fprintf(stderr, _BLUE "INFO" _RESET " (%s:%i) %s\n", __FILE__, __LINE__, #msg)
+#define INFO_F(fmt, ...) fprintf(stderr, _BLUE "INFO" _RESET " (%s:%i) " fmt "\n", \
+ __FILE__, __LINE__, ##__VA_ARGS__)
+
+#define LINE(len) do { \
+ printf(_GREEN); \
+ FOR(int, i, len) printf("_"); \
+ printf("\n"); \
+} while (0)
+
+#define PRINT(T, v) do { \
+ char buf[0x1000]; \
+ FMT(T)(v, buf); \
+ INFO_F("%s", buf); \
+} while (0)
+
+#endif /* ERR_H */
diff --git a/src/graphs.c b/src/graphs.c
new file mode 100644
index 00000000..51a26117
--- /dev/null
+++ b/src/graphs.c
@@ -0,0 +1,144 @@
+#include "graphs.h"
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include "err.h"
+
+// #define TYPE strbuf
+// #include "linked_list.h"
+// #include "linked_list.inc.h"
+// #undef TYPE
+
+int create_graph_trie (vcomponent* ev, char* filename) {
+ FILE* f = fopen(filename, "w");
+
+ fputs("digraph {\n rankdir=LR;", f);
+ trie_to_dot(&ev->clines, f);
+ fputs("}", f);
+
+ fclose(f);
+
+ INFO_F("Wrote '%s' to '%s'", vcomponent_get_val(ev, "X-HNH-FILENAME"), filename);
+
+ return 0;
+}
+
+int helper_vcomponent (vcomponent* root, FILE* f) {
+ fprintf(f, "subgraph \"cluster_root\" { label=File; \"%p\" [label=%s] }\n", root, root->type);
+
+ TRIE(content_line)* trie = &root->clines;
+ TRIE_NODE(content_line)* n = trie->root->child;
+
+ if (! EMPTY(TRIE(content_line))(trie)) {
+ fprintf(f, "subgraph \"cluster_%p\" {\n", root);
+ fprintf(f, "\"%p\" [label=trie fontcolor=gray, color=gray];", trie);
+ fprintf(f, "\"%p\" -> \"%p\" [color=red]\n", root, trie);
+ while (n != NULL) {
+ fprintf(f, "\"%p\" -> \"%p\" [color=gray]\n",
+ (void*) trie,
+ (void*) n);
+ fprintf(f, "subgraph \"cluster_%c_%p\" {\ncolor=red; \n",
+ n->c, root);
+ trie_to_dot_helper( n, f );
+
+
+ fputs("}", f);
+ n = n->next;
+ }
+ fputs("}", f);
+ }
+
+ FOR(LLIST, vcomponent, child, &root->components) {
+ fprintf(f, "\"%p\" -> \"%p\"\n", root, child);
+ helper_vcomponent(child, f);
+ }
+ return 0;
+}
+
+int create_graph_vcomponent (vcomponent* root, char* outfile) {
+ FILE* f = fopen(outfile, "w");
+ if (f == NULL) {
+ ERR_F("Error opening file %s, errno = %i", outfile, errno);
+ return 1;
+ }
+ vcomponent* c = root;
+ fputs("digraph {", f);
+ helper_vcomponent(c, f);
+ fputs("}", f);
+ fclose(f);
+ return 0;
+}
+
+#define T content_line
+
+int trie_to_dot ( TRIE(T)* trie, FILE* f ) {
+ TRIE_NODE(T)* n = trie->root->child;
+ fprintf(f, "\"%p\" [label=root fontcolor=gray, color=gray];", trie);
+ while (n != NULL) {
+ fprintf(f, "\"%p\" -> \"%p\" [color=gray]\n",
+ (void*) trie,
+ (void*) n);
+ fprintf(f, "subgraph \"cluster_%c\" {\n",
+ n->c);
+ trie_to_dot_helper( n, f );
+ fputs("}", f);
+ n = n->next;
+ }
+ return 0;
+}
+
+int trie_to_dot_helper ( TRIE_NODE(T)* root, FILE* f ) {
+ if (L(root) == NULL) {
+ fprintf(f, "\"%p\"[label = \"%c\" style=filled fillcolor=white];\n",
+ (void*) root, root->c);
+ } else {
+ fprintf(f, "\"%p\"[label = \"%c [%i]\" style=filled fillcolor=green];\n",
+ (void*) root, root->c,
+ SIZE(LLIST(content_set))(L(root))
+ );
+ }
+ TRIE_NODE(T)* child = root->child;
+
+ // ----------------------------------------
+#if 1 /* Toggle values */
+ if (L(root) != NULL) {
+
+ FOR(LLIST, content_set, v, L(root)) {
+ char buf[0x100];
+ FMT(strbuf)(&v->key, buf);
+ fprintf(f, "\"%p\" [label=\"%s\" shape=rectangle color=darkgreen];\n",
+ v, buf);
+ /* Edge between TRIE char node and data node */
+ fprintf(f, "\"%p\" -> \"%p\";\n", root, v);
+
+ /* Parameters */
+ LLIST(strbuf)* keys = KEYS(TRIE(param_set))(&v->val);
+ FOR(LLIST, strbuf, key, keys) {
+ param_set* p = GET(TRIE(param_set))(&v->val, key->mem);
+
+ fprintf(f, "\"%p\" [label=\"%s\" color=blue];\n",
+ key, key->mem);
+ /* Edge between data node and param key node */
+ fprintf(f, "\"%p\" -> \"%p\";", v, key);
+
+ FOR(LLIST, strbuf, str, p) {
+ fprintf(f, "\"%p\" [label=\"%s\" color=orange];",
+ str, str->mem);
+ /* Edge between param key node and param value node */
+ fprintf(f, "\"%p\" -> \"%p\";", key, str);
+ }
+ }
+ }
+ }
+#endif
+ // ----------------------------------------
+
+ while (child != NULL) {
+ fprintf(f, "\"%p\" -> \"%p\";\n",
+ (void*) root, (void*) child);
+ trie_to_dot_helper(child, f);
+ child = child->next;
+ }
+ return 0;
+}
diff --git a/src/graphs.h b/src/graphs.h
new file mode 100644
index 00000000..fe521003
--- /dev/null
+++ b/src/graphs.h
@@ -0,0 +1,15 @@
+#ifndef GRAPHS_H
+#define GRAPHS_H
+
+#include "vcal.h"
+
+int create_graph_trie (vcomponent* ev, char* filename);
+
+int create_graph_vcomponent (vcomponent* root, char* outfile);
+
+int helper_vcomponent (vcomponent* root, FILE* f);
+
+int trie_to_dot ( TRIE(content_line)*, FILE* );
+int trie_to_dot_helper ( TRIE_NODE(content_line)*, FILE* );
+
+#endif /* GRAPHS_H */
diff --git a/src/guile_interface.h b/src/guile_interface.h
new file mode 100644
index 00000000..76ec24d3
--- /dev/null
+++ b/src/guile_interface.h
@@ -0,0 +1,28 @@
+#ifndef GUILE_INTERFACE_H
+#define GUILE_INTERFACE_H
+
+#include <libguile.h>
+#include "vcal.h"
+
+/*
+ * At a number of places scm_gc_{un,}protect_object is called.
+ * This is needed since most of my structures are allocated with the
+ * regular malloc, instead of the scm_gc_malloc variants.
+ * This leads to the garbage collector not realizing that I still have
+ * the components, and deletes them.
+ *
+ * The protection markers stop the GC from doing its thing.
+ */
+
+void init_lib (void);
+void init_vcomponent_type (void);
+
+SCM make_vcomponent (SCM);
+SCM vcomponent_get_attribute (SCM, SCM);
+SCM vcomponent_child_count (SCM);
+SCM vcomponent_children (SCM);
+SCM vcomponent_typeof (SCM);
+
+SCM scm_from_vcomponent (vcomponent*);
+
+#endif /* GUILE_INTERFACE_H */
diff --git a/src/guile_interface.scm.c b/src/guile_interface.scm.c
new file mode 100644
index 00000000..3d0bff1e
--- /dev/null
+++ b/src/guile_interface.scm.c
@@ -0,0 +1,220 @@
+#include "guile_interface.h"
+
+#include "calendar.h"
+#include "guile_type_helpers.h"
+
+static SCM vcomponent_type;
+
+void init_vcomponent_type (void) {
+ SCM name = scm_from_utf8_symbol("vcomponent");
+ SCM slots = scm_list_1(scm_from_utf8_symbol("data"));
+
+ vcomponent_type = scm_make_foreign_object_type(name, slots, NULL);
+}
+
+SCM_DEFINE (make_vcomponent, "%vcomponent-make", 1, 0, 0,
+ (SCM path),
+ "Loads a vdir iCalendar from the given path.")
+{
+ vcomponent* cal =
+ (vcomponent*) scm_gc_malloc (
+ sizeof(*cal), "vcomponent");
+ INIT(vcomponent, cal, "ROOT");
+
+ char* p = scm_to_utf8_stringn(path, NULL);
+ read_vcalendar(cal, p);
+ free(p);
+
+ return scm_from_vcomponent (cal);
+}
+
+/*
+ * Returns a line from a component.
+ */
+SCM_DEFINE (vcomponent_get_attribute, "%vcomponent-get-attribute", 2, 0, 0,
+ (SCM calendar, SCM attr),
+ "Retuns the given attribute from the vevent object at index in calendar.")
+{
+ scm_assert_foreign_object_type (vcomponent_type, calendar);
+ vcomponent* cal = scm_foreign_object_ref (calendar, 0);
+
+ char* key = scm_to_utf8_stringn(scm_string_upcase(attr), NULL);
+ content_line* c = get_property (cal, key);
+ free(key);
+
+ if (c == NULL) return SCM_BOOL_F;
+
+ SCM llist = SCM_EOL;
+ FOR (LLIST, content_set, v, c) {
+ llist = scm_cons(scm_from_strbuf(&v->key), llist);
+ }
+
+ /* returns the car of list if list is one long. */
+ if (scm_to_int(scm_length(llist)) == 1) {
+ return SCM_CAR(llist);
+ } else {
+ return llist;
+ }
+}
+
+SCM_DEFINE (vcomponent_set_attr_x, "%vcomponent-set-attribute!", 3, 0, 0,
+ (SCM component, SCM attr, SCM new_value),
+ "")
+{
+ scm_assert_foreign_object_type (vcomponent_type, component);
+ vcomponent* com = scm_foreign_object_ref (component, 0);
+
+ char* key = scm_to_utf8_stringn(scm_string_upcase(attr), NULL);
+ content_line* c = get_property (com, key);
+
+ /* Create the position in the TRIE if it doesn't already exist */
+ if (c == NULL) {
+ /* Insert empty key since this allows me to use the helper
+ * function */
+ vcomponent_push_val(com, key, "");
+ c = get_property (com, key);
+ } else {
+ /* If the object already exists it should be protected,
+ * so unprotect it
+ */
+ scm_gc_unprotect_object(c->cur->value->key.scm);
+ }
+
+ free(key);
+
+ c->cur->value->key.scm = new_value;
+ scm_gc_protect_object(c->cur->value->key.scm);
+
+ return SCM_UNSPECIFIED;
+}
+
+SCM_DEFINE (vcomponent_child_count, "%vcomponent-child-count", 1, 0, 0,
+ (SCM component),
+ "Returns number of child components.")
+{
+ scm_assert_foreign_object_type (vcomponent_type, component);
+ vcomponent* c = scm_foreign_object_ref (component, 0);
+ return scm_from_size_t (SIZE(LLIST(vcomponent))(&c->components));
+}
+
+SCM_DEFINE(vcomponent_children, "%vcomponent-children", 1, 0, 0,
+ (SCM component),
+ "")
+{
+ scm_assert_foreign_object_type (vcomponent_type, component);
+ vcomponent* cal = scm_foreign_object_ref (component, 0);
+
+ SCM llist = SCM_EOL;
+ FOR (LLIST, vcomponent, v, &cal->components) {
+ llist = scm_cons(scm_from_vcomponent(v), llist);
+ }
+ return llist;
+}
+
+SCM_DEFINE(vcomponent_filter_children_x, "%vcomponent-filter-children!",
+ 2, 0, 0,
+ (SCM pred, SCM component),
+ "Remove all children from component who DOESN'T satisfy `pred`")
+{
+ scm_assert_foreign_object_type (vcomponent_type, component);
+ vcomponent* cal = scm_foreign_object_ref (component, 0);
+
+ for (LINK(vcomponent)* l = FIRST(&cal->components);
+ l->after != NULL;
+ l = l->after)
+ {
+ if (scm_is_false(scm_call_1 (pred, scm_from_vcomponent(l->value)))) {
+ FFREE(vcomponent, l->value);
+ UNLINK(LINK(vcomponent))(l);
+ }
+ }
+
+ return SCM_UNSPECIFIED;
+}
+
+SCM_DEFINE(vcomponent_push_child_x, "%vcomponent-push-child!", 2, 0, 0,
+ (SCM component, SCM child),
+ "")
+{
+ scm_assert_foreign_object_type (vcomponent_type, component);
+ scm_assert_foreign_object_type (vcomponent_type, child);
+ vcomponent* comp = scm_foreign_object_ref (component, 0);
+ vcomponent* chil = scm_foreign_object_ref (child, 0);
+
+ PUSH(vcomponent)(comp, chil);
+
+ return SCM_UNSPECIFIED;
+}
+
+SCM_DEFINE (vcomponent_parent, "%vcomponent-parent", 1, 0, 0,
+ (SCM component),
+ "")
+{
+ scm_assert_foreign_object_type (vcomponent_type, component);
+ vcomponent* comp = scm_foreign_object_ref (component, 0);
+
+ vcomponent* parent = comp->parent;
+ if (strcmp(parent->type, "ROOT") == 0) {
+ return SCM_BOOL_F;
+ } else {
+ return scm_from_vcomponent(parent);
+ }
+}
+
+SCM_DEFINE(vcomponent_typeof, "%vcomponent-type", 1, 0, 0,
+ (SCM component),
+ "Returns type of vcomponent")
+{
+ scm_assert_foreign_object_type (vcomponent_type, component);
+ vcomponent* comp = scm_foreign_object_ref (component, 0);
+ return scm_from_utf8_symbol(comp->type);
+}
+
+SCM scm_from_vcomponent(vcomponent* v) {
+ if (v->scm == NULL) {
+ v->scm = scm_make_foreign_object_1 (vcomponent_type, v);
+ scm_gc_protect_object(v->scm);
+ }
+ return v->scm;
+}
+
+SCM_DEFINE(vcomponent_attr_list, "%vcomponent-attribute-list", 1, 0, 0,
+ (SCM component),
+ "Returns list of all keys in component.")
+{
+ scm_assert_foreign_object_type (vcomponent_type, component);
+ vcomponent* comp = scm_foreign_object_ref (component, 0);
+ LLIST(strbuf)* keys = KEYS(TRIE(content_line))(&comp->clines);
+
+ SCM llist = SCM_EOL;
+ FOR (LLIST, strbuf, s, keys) {
+ llist = scm_cons(scm_from_strbuf(s), llist);
+ }
+
+ FFREE(LLIST(strbuf), keys);
+
+ return llist;
+}
+
+SCM_DEFINE(vcomponent_shallow_copy, "%vcomponent-shallow-copy", 1, 0, 0,
+ (SCM component),
+ "Creates a shallow copy of the given component.")
+{
+ scm_assert_foreign_object_type (vcomponent_type, component);
+ vcomponent* src = scm_foreign_object_ref (component, 0);
+
+ vcomponent* dest =
+ (vcomponent*) scm_gc_malloc (
+ sizeof(*dest), "vcomponent");
+ INIT(vcomponent, dest, src->type, NULL);
+ vcomponent_copy (dest, src);
+ return scm_from_vcomponent (dest);
+}
+
+void init_lib (void) {
+ init_vcomponent_type();
+
+#ifndef SCM_MAGIC_SNARFER
+#include "guile_interface.x"
+#endif
+}
diff --git a/src/guile_type_helpers.c b/src/guile_type_helpers.c
new file mode 100644
index 00000000..e231f2b1
--- /dev/null
+++ b/src/guile_type_helpers.c
@@ -0,0 +1,13 @@
+#include "guile_type_helpers.h"
+#include "guile_interface.h"
+
+#include "macro.h"
+
+SCM scm_from_strbuf(strbuf* s) {
+ if (s->scm == NULL) {
+ s->scm = scm_from_utf8_stringn (s->mem, s->len);
+ scm_gc_protect_object(s->scm);
+ }
+
+ return s->scm;
+}
diff --git a/src/guile_type_helpers.h b/src/guile_type_helpers.h
new file mode 100644
index 00000000..2ff177e1
--- /dev/null
+++ b/src/guile_type_helpers.h
@@ -0,0 +1,13 @@
+#ifndef GUILE_TYPE_HELPERS_H
+#define GUILE_TYPE_HELPERS_H
+
+#include <libguile.h>
+
+#include "calendar.h"
+#include "strbuf.h"
+
+#define SCM_IS_LIST(x) scm_is_true(scm_list_p(x))
+
+SCM scm_from_strbuf(strbuf* s);
+
+#endif /* GUILE_TYPE_HELPERS_H */
diff --git a/src/linked_list.h b/src/linked_list.h
new file mode 100644
index 00000000..ec1e17e0
--- /dev/null
+++ b/src/linked_list.h
@@ -0,0 +1,92 @@
+#ifndef LINKED_LIST_H
+#define LINKED_LIST_H
+
+#include "macro.h"
+
+#define LLIST(T) TEMPL(llist, T)
+#define LINK(T) TEMPL(llist_link, T)
+
+#define UNLINK(T) TEMPL(unlink, T)
+
+#endif /* LINKED_LIST_H */
+#ifdef TYPE
+
+typedef struct LINK(TYPE) {
+ struct LINK(TYPE)* before;
+ struct LINK(TYPE)* after;
+ TYPE* value;
+} LINK(TYPE);
+
+#define L(link) (link)->value
+
+typedef struct {
+ LINK(TYPE)* head;
+ LINK(TYPE)* tail;
+ LINK(TYPE)* cur;
+ int length;
+} LLIST(TYPE);
+
+#define FIRST(lst) (lst)->head->after
+#define FIRST_V(lst) (lst)->head->after->value
+#define LAST(lst) (lst)->tail->before
+#define LAST_V(lst) (lst)->tail->before->value
+
+INIT_F ( LLIST(TYPE) );
+
+/*
+ * NOTE freeing a linked list alsa FFREE's all its contents.
+ * TODO some form of shared pointer to ensure nothing is free'd twice
+ * would be a good idea.
+ */
+FREE_F ( LLIST(TYPE) );
+
+INIT_F ( LINK(TYPE) );
+INIT_F ( LINK(TYPE), TYPE* val );
+FREE_F ( LINK(TYPE) );
+
+int UNLINK(LINK(TYPE)) ( LINK(TYPE)* );
+
+int PUSH(LLIST(TYPE)) ( LLIST(TYPE)*, TYPE* );
+TYPE* PEEK(LLIST(TYPE)) ( LLIST(TYPE)* );
+TYPE* POP(LLIST(TYPE)) ( LLIST(TYPE)* );
+
+int DEEP_COPY(LLIST(TYPE)) ( LLIST(TYPE)* dest, LLIST(TYPE)* src );
+
+int APPEND(LLIST(TYPE)) ( LLIST(TYPE)* dest, LLIST(TYPE)* new_ );
+
+int SIZE(LLIST(TYPE)) ( LLIST(TYPE)* llist );
+int EMPTY(LLIST(TYPE)) ( LLIST(TYPE)* llist );
+
+/*
+ * Resets a linked list by removing all it's objects.
+ * FREE's all elements stored in the list.
+ */
+int RESET(LLIST(TYPE)) ( LLIST(TYPE)* llist );
+
+/*
+ * Takes to lists, and merges them into a single one. Destroys new_ in
+ * the process.
+ */
+LLIST(TYPE)* RESOLVE(LLIST(TYPE)) (LLIST(TYPE)* dest, LLIST(TYPE)* new_);
+
+FMT_F(LLIST(TYPE));
+
+/* Iterator */
+
+#define __PRE_LLIST(T, l, set) \
+ T* l; LINK(T)* __INTER(l);
+
+#define PRE_FOR_LLIST(T) __PRE_LLIST
+
+// #define __BEG_LLIST(v, set) v = (set)->head
+#define __BEG_LLIST(T, l, set) __INTER(l) = FIRST(set), l = L(__INTER(l))
+#define BEG_LLIST(T) __BEG_LLIST
+
+#define __END_LLIST(T, l, set) __INTER(l) != (set)->tail
+#define END_LLIST(T) __END_LLIST
+
+#define __NXT_LLIST(T, l, set) __INTER(l) = __INTER(l)->after, l = L(__INTER(l))
+// #define __NXT_LLIST(T, l, set) l = L(__INTER(l) = __INTER(l)->after)
+#define NXT_LLIST(T) __NXT_LLIST
+
+#endif /* TYPE */
diff --git a/src/linked_list.inc.h b/src/linked_list.inc.h
new file mode 100644
index 00000000..81974a9c
--- /dev/null
+++ b/src/linked_list.inc.h
@@ -0,0 +1,176 @@
+#ifndef TYPE
+#error "Set TYPE before including self file"
+#else
+
+INIT_F ( LLIST(TYPE) ) {
+ self->length = 0;
+ NEW(LINK(TYPE), head);
+ NEW(LINK(TYPE), tail);
+ self->head = head;
+ self->tail = tail;
+ head->after = tail;
+ tail->before = head;
+ self->cur = head;
+ return 0;
+}
+
+FREE_F (LINK(TYPE)) {
+ UNLINK(LINK(TYPE))(self);
+
+ if (self->value != NULL) FFREE(TYPE, self->value);
+ return 0;
+}
+
+FREE_F( LLIST(TYPE) ) {
+ LINK(TYPE) *n, *next;
+ n = self->head;
+ while ( n != NULL ) {
+ next = n->after;
+ FFREE(LINK(TYPE), n);
+ n = next;
+ }
+
+ self->length = -1;
+
+ return 0;
+}
+
+INIT_F ( LINK(TYPE) ) {
+ self->before = NULL;
+ self->after = NULL;
+ self->value = NULL;
+ return 0;
+}
+
+INIT_F ( LINK(TYPE), TYPE* val ) {
+ self->before = NULL;
+ self->after = NULL;
+ self->value = val;
+ return 0;
+}
+
+int UNLINK(LINK(TYPE)) ( LINK(TYPE)* self ) {
+ if (self->before != NULL) self->before->after = self->after;
+ if (self->after != NULL) self->after->before = self->before;
+ return 0;
+}
+
+
+int PUSH(LLIST(TYPE)) ( LLIST(TYPE)* lst, TYPE* val) {
+ NEW(LINK(TYPE), link, val);
+
+ link->after = FIRST(lst);
+ FIRST(lst) = link;
+
+ link->after->before = link;
+ link->before = lst->head;
+
+ ++lst->length;
+
+ // TODO do I want to change that?
+ lst->cur = link;
+
+ return 0;
+}
+
+TYPE* PEEK(LLIST(TYPE)) ( LLIST(TYPE)* lst ) {
+ if (EMPTY(LLIST(TYPE))(lst)) return NULL;
+
+ return FIRST(lst)->value;
+}
+
+TYPE* POP(LLIST(TYPE)) ( LLIST(TYPE)* lst) {
+ if (EMPTY(LLIST(TYPE))(lst)) return NULL;
+
+ LINK(TYPE)* frst = FIRST(lst);
+ UNLINK(LINK(TYPE))(frst);
+
+ TYPE* retval = frst->value;
+ --lst->length;
+ free (frst);
+ return retval;
+}
+
+int DEEP_COPY(LLIST(TYPE)) ( LLIST(TYPE)* dest, LLIST(TYPE)* src ) {
+ LINK(TYPE)* n = FIRST(src);
+
+ while (n->after != NULL) {
+ NEW(TYPE, cpy);
+ DEEP_COPY(TYPE)(cpy, n->value);
+ PUSH(LLIST(TYPE)) ( dest, cpy );
+ n = n->after;
+ }
+
+ return 0;
+}
+
+/*
+ * Adds two linked lists together.
+ * O(1) time.
+ * destroys new__ in the process, but keeps the elements.
+ * make sure to free(new__) after.
+ */
+int APPEND(LLIST(TYPE)) ( LLIST(TYPE)* dest, LLIST(TYPE)* new__ ) {
+
+ /* Link end of dest onto start of new__. */
+ LAST(dest)->after = FIRST(new__);
+ FIRST(new__)->before = LAST(dest);
+
+ /* Free the two now not needed end links. */
+ free(new__->head);
+ free(dest->tail);
+
+ /* Update dest with new__ tail ptr. */
+ dest->tail = new__->tail;
+
+ dest->length += new__->length;
+
+ return 0;
+}
+
+int SIZE(LLIST(TYPE)) ( LLIST(TYPE)* llist ) {
+ return llist->length;
+}
+
+int EMPTY(LLIST(TYPE)) ( LLIST(TYPE)* llist ) {
+ return FIRST(llist) == llist->tail;
+}
+
+LLIST(TYPE)* RESOLVE(LLIST(TYPE)) (LLIST(TYPE)* dest, LLIST(TYPE)* new__) {
+ if (dest == NULL) return new__;
+ APPEND(LLIST(TYPE))(dest, new__);
+ free(new__);
+ return dest;
+}
+
+int RESET(LLIST(TYPE)) ( LLIST(TYPE)* llist ) {
+
+ LINK(TYPE) *link = FIRST(llist), *next;
+ /*
+ * Manual looping rather than itterators since we destroyed the
+ * loop variable.
+ */
+ while (link != llist->tail) {
+ next = link->after;
+ FFREE(LINK(TYPE), link);
+ link = next;
+ }
+
+ llist->cur = llist->head;
+
+ return 0;
+}
+
+FMT_F(LLIST(TYPE)) {
+ int seek = 0;
+ fmtf("(");
+ FOR(LLIST, TYPE, v, self) {
+ seek += FMT(TYPE)(v, buf + seek);
+ fmtf(" ");
+ }
+ fmtf(")");
+
+ return seek;
+}
+
+#endif /* TYPE */
diff --git a/src/macro.h b/src/macro.h
new file mode 100644
index 00000000..7b620f83
--- /dev/null
+++ b/src/macro.h
@@ -0,0 +1,134 @@
+#ifndef MACRO_H
+#define MACRO_H
+
+/*
+ * Token paste
+ */
+#define TP(a, b) a ## b
+#define TP3(a, b, c) a ## b ## c
+#define TP4(a, b, c, d) a ## b ## c ## d
+#define TP5(a, b, c, d, e) a ## b ## c ## d ## e
+#define TP6(a, b, c, d, e, f) a ## b ## c ## d ## e ## f
+
+/*
+ * Get length of __VA_ARGS__
+ * Borrowed fram:
+ * https://stackoverflow.com/a/35986932
+ */
+#define VA_ARGS_NUM_PRIV(P1, P2, P3, P4, P5, P6, Pn, ...) Pn
+#define VA_ARGS_NUM(...) VA_ARGS_NUM_PRIV(-1, ## __VA_ARGS__, 5, 4, 3, 2, 1, 0)
+
+/*
+ * Templatization macros. Forms symbols on the from name<T>, which
+ * looks really good in debuggers and the like. Unicode characters
+ * written in \U notation since C apparently doesn't support unicode
+ * literals.
+ *
+ * Can be nested (useful for container types).
+ *
+ * Doesn't use ASCII <>, but rather some other ᐸᐳ, meaning that it's
+ * not a reserved character.
+ *
+ * nameᐸTᐳ
+ */
+#define TEMPL(name, T) TP4(name, \U00001438 , T, \U00001433 )
+#define TEMPL2(name, T, V) TP6(name, \U00001438\U00001438 , T , \U00001433_\U00001438 , V, \U00001433\U00001433)
+#define TEMPL_N(name, T, argcount) TP6(name, \U00001438 , T, _, argcount, \U00001433 )
+
+/* Constructor type name */
+#define __INIT_T(T, C) TEMPL_N(init, T, C)
+
+/* Returns full type of constructor */
+#define INIT_F(T, ...) \
+ int __INIT_T(T, VA_ARGS_NUM(__VA_ARGS__)) (T* self, ## __VA_ARGS__)
+
+/*
+ * Call the constructor of an object
+ * `int` part of the macro, to ensure that any attempt to call self
+ * function results in an error.
+ */
+#define INIT(T, N, ...) \
+ __INIT_T(T, VA_ARGS_NUM(__VA_ARGS__)) (N, ## __VA_ARGS__)
+
+/* Allocate a new_ object on the HEAP */
+#define NEW(T, N, ...) \
+ T* N = (T*) malloc(sizeof(*N)); \
+ INIT(T, N, ## __VA_ARGS__);
+
+/*
+ * Reconstructs a object. Use with caution.
+ */
+#define RENEW(T, N, ...) do { \
+ N = (T*) malloc(sizeof(*N)); \
+ INIT(T, N, ## __VA_ARGS__); \
+} while (0)
+
+
+/* Allocate a new_ object on the STACK */
+#define SNEW(T, N, ...) \
+ T N; \
+ INIT(T, & N, ## __VA_ARGS__);
+
+/* Destructor for type */
+#define FREE(T) TEMPL(free, T)
+
+/* Call destructor for type, and free object */
+#define FFREE(T, N) do { FREE(T)(N); free(N); } while (0)
+
+/* Declare destructor */
+#define FREE_F(T) int FREE(T) (T* self)
+
+/* generate reusable internal symbol */
+#define __INTER(s) TP3(__, s, __internal)
+#define __INTER2(s) __INTER(__INTER(s))
+#define __INTER3(s) __INTER(__INTER(__INTER(s)))
+
+/* Iterator macros. */
+#define FOR(CONT_T, T, var, set) \
+ PRE_FOR_ ## CONT_T (T) (T, var, set); \
+ for( BEG_ ## CONT_T (T) (T, var, set); \
+ END_ ## CONT_T (T) (T, var, set); \
+ NXT_ ## CONT_T (T) (T, var, set))
+
+/* Example int implementation
+ * FOR(int, i, 10) { } */
+
+#define PRE_FOR_int(i, set)
+#define BEG_int(i, set) int i = 0
+#define NXT_int(i, set) i++
+#define END_int(i, set) i < set
+
+/*
+ * General functions that different container types may implement.
+ * Actuall implementation and type signature is mostly left to
+ * individual implementations.
+ */
+#define DEEP_COPY(T) TEMPL(copy , T)
+#define RESOLVE(T) TEMPL(resolve , T)
+#define APPEND(T) TEMPL(append , T)
+#define SIZE(T) TEMPL(size , T)
+#define EMPTY(T) TEMPL(empty , T)
+#define PUSH(T) TEMPL(push , T)
+#define PEEK(T) TEMPL(peek , T)
+#define POP(T) TEMPL(pop , T)
+#define GET(T) TEMPL(get , T)
+#define RESET(T) TEMPL(reset , T)
+#define KEYS(T) TEMPL(keys , T)
+
+/*
+ * Formatting macros.
+ * Transform objects into string representation of themselves.
+ * buf should be a suffisiently large memmory location, if it's to
+ * small then bad stuff might happen.
+ *
+ * Should return the number of bytes written (like sprintf).
+ */
+
+#define FMT_T(T) TEMPL(format , T)
+#define FMT_F(T) int FMT_T(T)(T* self, char* buf, ...)
+// TODO change order of buf and item
+#define __FMT_HELP(item, buf, ...) ((item), (buf), VA_ARGS_NUM(__VA_ARGS__), ## __VA_ARGS__)
+#define FMT(T) FMT_T(T) __FMT_HELP
+#define fmtf(...) seek += sprintf(buf + seek, __VA_ARGS__)
+
+#endif /* MACRO_H */
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 00000000..791bc5d3
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,91 @@
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#include "calendar.h"
+#include "macro.h"
+#include "vcal.h"
+#include "graphs.h"
+#include "err.h"
+
+typedef struct {
+ int argc;
+ char** argv;
+} arg;
+
+int arg_shift (arg* a) {
+ if (a->argc == 0) return 0;
+
+ ++a->argv;
+ return --a->argc;
+
+}
+
+int main (int argc, char** argv) {
+ arg args = { .argc = argc, .argv = argv };
+
+ if (arg_shift(&args) == 0) {
+ ERR("Please give vdir or a vcalendar file as first argument");
+ exit (1);
+ }
+
+ char* rootpath = args.argv[0];
+ SNEW(vcomponent, root, "ROOT", rootpath);
+ read_vcalendar(&root, rootpath);
+
+ arg_shift(&args);
+
+ if (args.argc == 0 || strcmp(args.argv[0], "-p") == 0) {
+ INFO_F("Parsed calendar file containing [%u] events",
+ root.components.length);
+
+ puts("CAL : OBJ | Filename | Description");
+ puts("----------+----------+------------");
+
+ /* This loops over all VCALENDAR's in root */
+ FOR (LLIST, vcomponent, cal, &root.components) {
+ assert(strcmp(cal->type, "VCALENDAR") == 0);
+
+ char* filename = vcomponent_get_val(cal, "X-HNH-FILENAME");
+
+ /* This loop over all VEVENT's in the current VCALENDAR */
+ FOR (LLIST, vcomponent, ev, &cal->components) {
+ if (strcmp(ev->type, "VEVENT") != 0) continue;
+
+ printf("%s | %s\n",
+ filename,
+ get_property(ev, "SUMMARY")->cur->value->key.mem);
+ }
+ }
+ } else if (strcmp(args.argv[0], "-g") == 0) {
+ /* TODO self might be broken */
+ if (arg_shift(&args) == 0) {
+ FOR (LLIST, vcomponent, cal, &root.components) {
+ assert(strcmp(cal->type, "VCALENDAR") == 0);
+
+ vcomponent* ev = FCHILD(cal);
+
+ char target[0xFF];
+ target[0] = '\0';
+ strcat(target, "/tmp/dot/");
+ strcat(target, vcomponent_get_val(ev, "X-HNH-FILENAME"));
+ strcat(target, ".dot");
+ // create_graph(ev, target);
+ }
+ } else {
+ // create_graph(FCHILD(FCHILD(&root)), args.argv[0]);
+ INFO("Creating graph for single file");
+ INFO_F("output = %s\n", args.argv[0]);
+ create_graph_vcomponent(&root, args.argv[0]);
+ }
+ }
+
+ /*
+ char buf[0x20000];
+ FMT(vcomponent)(&root, buf);
+ puts(buf);
+ */
+
+ FREE(vcomponent)(&root);
+}
diff --git a/src/pair.h b/src/pair.h
new file mode 100644
index 00000000..e96cf180
--- /dev/null
+++ b/src/pair.h
@@ -0,0 +1,19 @@
+#ifndef PAIR_H
+#define PAIR_H
+
+#define PAIR(T, V) TEMPL2(pair, T, V)
+
+#endif /* PAIR_H */
+#if defined(T) && defined(V)
+
+typedef struct {
+ T key;
+ V val;
+} PAIR(T, V);
+
+INIT_F(PAIR(T, V));
+FREE_F(PAIR(T, V));
+FMT_F(PAIR(T, V));
+int DEEP_COPY(PAIR(T, V)) (PAIR(T, V)* dest, PAIR(T, V)* src);
+
+#endif
diff --git a/src/pair.inc.h b/src/pair.inc.h
new file mode 100644
index 00000000..c42b2dfd
--- /dev/null
+++ b/src/pair.inc.h
@@ -0,0 +1,34 @@
+#if ! (defined(T) && defined(V))
+#error "Both T and V must be defiend here"
+#else
+
+INIT_F(PAIR(T, V)) {
+ INIT(T, &self->key);
+ INIT(V, &self->val);
+
+ return 0;
+}
+
+FREE_F(PAIR(T, V)) {
+ FREE(T)(&self->key);
+ FREE(V)(&self->val);
+
+ return 0;
+}
+
+FMT_F(PAIR(T, V)) {
+ char lbuf[0x100];
+ char rbuf[0x1000];
+ FMT(T)(&self->key, lbuf);
+ FMT(V)(&self->val, rbuf);
+
+ return sprintf(buf, "<%s, %s>", lbuf, rbuf);
+}
+
+int DEEP_COPY(PAIR(T, V)) (PAIR(T, V)* dest, PAIR(T, V)* src) {
+ DEEP_COPY(T)(&dest->key, &src->key);
+ DEEP_COPY(V)(&dest->val, &src->val);
+ return 0;
+}
+
+#endif /* T & V */
diff --git a/src/parse.c b/src/parse.c
new file mode 100644
index 00000000..0e37350d
--- /dev/null
+++ b/src/parse.c
@@ -0,0 +1,351 @@
+#include "parse.h"
+
+#include <errno.h>
+#include <string.h>
+#include <assert.h>
+
+#include "macro.h"
+#include "vcal.h"
+
+#include "err.h"
+
+// #define TYPE vcomponent
+// #include "linked_list.inc.h"
+// #undef TYPE
+
+#define T strbuf
+#define V strbuf
+#include "pair.h"
+#include "pair.inc.h"
+#undef T
+#undef V
+
+/*
+ * name *(";" param) ":" value CRLF
+ */
+int parse_file(char* filename, FILE* f, vcomponent* root) {
+ part_context p_ctx = p_key;
+
+ 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.
+ *
+ * {cline,param}_key is also temporary register used during
+ * parsing.
+ */
+ SNEW(content_line, cline);
+ SNEW(strbuf, cline_key);
+ SNEW(strbuf, param_key);
+
+ char c;
+ while ( (c = fgetc(f)) != EOF) {
+
+ /* We have a linebreak */
+ if (c == '\r' || c == '\n') {
+
+ if (fold(&ctx, c) > 0) {
+ /* Actuall end of line, handle value */
+ TRANSFER(CLINE_CUR_VAL(&cline), &ctx.str);
+ handle_kv(&cline_key, &cline, &ctx);
+ p_ctx = p_key;
+ } /* Else continue on current line */
+
+ /* We have an escaped character */
+ } else if (c == '\\') {
+ handle_escape (&ctx);
+
+ /* Border between param {key, value} */
+ } else if (p_ctx == p_param_name && c == '=') {
+
+ /* Save the current parameter key */
+ TRANSFER (&param_key, &ctx.str);
+ p_ctx = p_param_value;
+
+ /*
+ * One of four cases:
+ * 1) end of key , start of value
+ * 2) ,, key , ,, param
+ * 3) ,, param, ,, param
+ * 4) ,, param, ,, value
+ */
+ } 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) {
+ /* save current parameter value. */
+
+ NEW(strbuf, s);
+ TRANSFER(s, &ctx.str);
+
+ NEW(param_set, ps);
+ PUSH(param_set)(ps, s);
+
+ PUSH(TRIE(param_set))(CLINE_CUR_PARAMS(&cline), param_key.mem, ps);
+ strbuf_soft_reset (&param_key);
+ }
+
+ /*
+ * 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) {
+
+ TRANSFER(&cline_key, &ctx.str);
+
+ NEW(content_set, p);
+ PUSH(LLIST(content_set))(&cline, p);
+ }
+
+ 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);
+
+ ++ctx.column;
+ ++ctx.pcolumn;
+ }
+ }
+
+ if (! feof(f)) {
+ ERR("Error parsing");
+ }
+ /* Check to see if empty line */
+ else if (ctx.str.ptr != 0) {
+ /*
+ * The standard (3.4, l. 2675) says that each icalobject must
+ * end with CRLF. My files however does not, so we also parse
+ * the end here.
+ */
+
+ TRANSFER(CLINE_CUR_VAL(&cline), &ctx.str);
+ handle_kv(&cline_key, &cline, &ctx);
+
+ }
+
+ FREE(content_line)(&cline);
+ FREE(strbuf)(&cline_key);
+ FREE(strbuf)(&param_key);
+
+ assert(POP(LLIST(vcomponent))(&ctx.comp_stack) == root);
+ assert(EMPTY(LLIST(strbuf))(&ctx.key_stack));
+ assert(EMPTY(LLIST(vcomponent))(&ctx.comp_stack));
+
+ FREE(parse_ctx)(&ctx);
+
+ return 0;
+}
+
+/*
+ * We have a complete key value pair.
+ */
+int handle_kv (
+ strbuf* key,
+ content_line* cline,
+ parse_ctx* ctx
+ ) {
+
+ /*
+ * The key being BEGIN means that we decend into a new component.
+ */
+ if (strbuf_c(key, "BEGIN")) {
+ /* 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);
+ 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);
+
+ /*
+ * 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);
+ vcomponent* parent = PEEK(LLIST(vcomponent))(&ctx->comp_stack);
+ 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(key, "END")) {
+ 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",
+ expected_key->mem,
+ CLINE_CUR_VAL(cline)->mem,
+ vcomponent_get_val(
+ PEEK(LLIST(vcomponent))(&ctx->comp_stack),
+ "X-HNH-FILENAME"));
+ PUSH(LLIST(strbuf))(&ctx->key_stack, expected_key);
+
+ return -1;
+
+ } else {
+ 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,
+ key->mem, c);
+
+ RESET(LLIST(content_set))(cline);
+ }
+
+ return 0;
+}
+
+int fold(parse_ctx* ctx, char c) {
+ int retval;
+
+ char buf[2] = {
+ (c == '\n' ? '\n' : (char) fgetc(ctx->f)),
+ (char) fgetc(ctx->f)
+ };
+
+ ctx->pcolumn = 1;
+
+ if (buf[0] != '\n') {
+ ERR_P(ctx, "expected new_line after CR");
+ retval = -1;
+
+ } else if (buf[1] == ' ' || buf[1] == '\t') {
+ retval = 0;
+ ctx->pcolumn++;
+
+ } else if (ungetc(buf[1], ctx->f) != buf[1]) {
+ ERR_P(ctx, "Failed to put character back on FILE");
+ retval = -2;
+
+ } else {
+ retval = 1;
+ ++ctx->line;
+ ctx->column = 0;
+ }
+
+ ++ctx->pline;
+
+ return retval;
+}
+
+
+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;
+
+ self->pline = 1;
+ self->pcolumn = 1;
+
+ INIT(strbuf, &self->str);
+
+ return 0;
+}
+
+FREE_F(parse_ctx) {
+
+ FREE(LLIST(strbuf))(&self->key_stack);
+ FREE(LLIST(vcomponent))(&self->comp_stack);
+ free(self->filename);
+
+ self->line = 0;
+ self->column = 0;
+ FREE(strbuf)(&self->str);
+
+ 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/src/parse.h b/src/parse.h
new file mode 100644
index 00000000..53263b4c
--- /dev/null
+++ b/src/parse.h
@@ -0,0 +1,122 @@
+#ifndef PARSE_H
+#define PARSE_H
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "strbuf.h"
+#include "vcal.h"
+
+// #define TYPE vcomponent
+// #include "linked_list.h"
+// #undef TYPE
+
+/*
+ * The standard says that no line should be longer than 75 octets.
+ * This sets the default amount of memory to allocate for each string,
+ * but strings are reallocated when needed.
+ */
+#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;
+
+/*
+ * Struct holding most state information needed while parsing.
+ * 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
+ * TODO remove this
+ * */
+ int line;
+ int column;
+
+ /* Actuall lines and columns from file */
+ int pline;
+ int pcolumn;
+
+ /*
+ * String which we write everything read into.
+ * Later copied to appropiate places.
+ */
+ strbuf str;
+} parse_ctx;
+
+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(
+ strbuf* key,
+ content_line* cline,
+ parse_ctx* ctx
+ );
+
+/*
+ * Input
+ * f: file to get characters from
+ * ctx: current parse context
+ * c: last read character
+ * output:
+ * 0: line folded
+ * 1: line ended
+ *
+ * A carrige return means that the current line is at an
+ * end. The following character should always be \n.
+ * However, if the first character on the next line is a
+ * whitespace then the two lines should be concatenated.
+ *
+ * NOTE
+ * The above is true according to the standard. But I have
+ * found files with only NL. The code below ends line on the
+ * first of NL or CR, and then ensures that the program thinks
+ * it got the expected CRNL.
+ */
+int fold(parse_ctx* ctx, char c);
+
+int handle_escape (parse_ctx* ctx);
+
+#endif /* PARSE_H */
diff --git a/src/strbuf.c b/src/strbuf.c
new file mode 100644
index 00000000..0e56468b
--- /dev/null
+++ b/src/strbuf.c
@@ -0,0 +1,151 @@
+#include "strbuf.h"
+
+#include <string.h>
+#include <stdio.h>
+
+#include "err.h"
+
+INIT_F(strbuf) {
+ self->alloc = 0x10;
+ self->mem = (char*) calloc(sizeof(*self->mem), self->alloc);
+ self->ptr = 0;
+ self->len = 0;
+ self->scm = NULL;
+ return 0;
+}
+
+int strbuf_realloc(strbuf* str, size_t len) {
+ str->mem = (char*) realloc(str->mem, len);
+ str->alloc = len;
+ return 0;
+}
+
+FREE_F(strbuf) {
+ /* has already been freed */
+ if (self->mem == NULL) return 1;
+
+ free (self->mem);
+ self->mem = NULL;
+ self->alloc = 0;
+ self->len = 0;
+ return 0;
+}
+
+/*
+ * Reallocates memmory for you. Returns 1 if memory was reallocated.
+ */
+int strbuf_append(strbuf* s, char c) {
+ int retval = 0;
+
+ if (s->len + 1 > s->alloc) {
+ s->alloc <<= 1;
+ s->mem = (char*) realloc(s->mem, s->alloc);
+ retval = 1;
+ }
+
+ s->mem[s->len] = c;
+ s->ptr = ++s->len;
+ return retval;
+}
+
+char strbuf_pop(strbuf* s) {
+ char ret = s->mem[--s->len];
+ s->mem[s->len + 1] = '\0';
+ return ret;
+}
+
+int strbuf_cap(strbuf* s) {
+ strbuf_append(s, 0);
+ --s->len;
+ return 0;
+}
+
+int DEEP_COPY(strbuf)(strbuf* dest, strbuf* src) {
+ int retval = 0;
+
+ if (dest->alloc < src->len) {
+ /* +1 in length is to have room for '\0'. */
+ strbuf_realloc(dest, src->len + 1);
+ retval = 1;
+ }
+
+ if (src->scm != NULL) {
+ /* The magic SCM type is copied when reassigned. */
+ dest->scm = src->scm;
+ /* NOTE This is a bit of a leaky abstraction. */
+ scm_gc_protect_object(dest->scm);
+ }
+
+ dest->len = src->len;
+ memcpy(dest->mem, src->mem, src->len);
+ return retval;
+}
+
+int strbuf_cmp(strbuf* a, strbuf* b) {
+ if (a == NULL || a->alloc == 0 ||
+ b == NULL || b->alloc == 0)
+ {
+ ERR("a or b not alloced");
+ // return -1;
+ }
+
+ return strncmp(a->mem, b->mem, a->len);
+}
+
+int strbuf_c(strbuf* a, const char* b) {
+ if (a == NULL || a->alloc == 0) {
+ ERR("a not allocated");
+ return -1;
+ }
+
+ return strcmp(a->mem, b) == 0;
+}
+
+char* charat(strbuf* s, unsigned int idx) {
+ if (idx > s->len) {
+ ERR("Index out of bounds");
+ return (char*) -1;
+ }
+
+ return &s->mem[idx];
+}
+
+char* strbuf_cur(strbuf* s) {
+ return &s->mem[s->ptr];
+}
+
+char* strbuf_end(strbuf* s) {
+ return &s->mem[s->len];
+}
+
+int strbuf_reset(strbuf* s) {
+ s->ptr = 0;
+ return 0;
+}
+
+
+int strbuf_soft_reset(strbuf* s) {
+ s->ptr = s->len = 0;
+ return 0;
+}
+
+strbuf* RESOLVE(strbuf)(strbuf* dest, strbuf* new_) {
+ if (dest == NULL) return new_;
+ else return dest;
+}
+
+FMT_F(strbuf) {
+ return sprintf(buf, "%s", self->mem);
+}
+
+int SIZE(strbuf)(strbuf* self) {
+ return self->len;
+}
+
+int strbuf_load(strbuf* self, const char* str) {
+ for (int i = 0; str[i] != '\0'; i++) {
+ strbuf_append(self, str[i]);
+ }
+ strbuf_cap(self);
+ return 0;
+}
diff --git a/src/strbuf.h b/src/strbuf.h
new file mode 100644
index 00000000..7f936a9e
--- /dev/null
+++ b/src/strbuf.h
@@ -0,0 +1,109 @@
+#ifndef STRBUF_H
+#define STRBUF_H
+
+#include <stdlib.h>
+#include <libguile.h>
+#include "macro.h"
+
+/*
+ * A high level string type which holds it's own length, how much
+ * memmory it has allocated for itself, and a seek pointer into the
+ * string.
+ *
+ * Also comes with a number of functions which allow for safe(er)
+ * access to the memmory.
+ */
+typedef struct {
+ char* mem;
+ SCM scm;
+ /* TODO add support for negative ptr */
+ int ptr;
+ unsigned int alloc;
+ unsigned int len;
+} strbuf;
+
+/*
+ * Init strbuf to size of 10
+ */
+INIT_F(strbuf);
+
+/*
+ * Like realloc, but for strbuf
+ */
+int strbuf_realloc(strbuf* str, size_t len);
+
+/*
+ * Free's contents of str, but keeps str.
+ */
+FREE_F(strbuf);
+
+int strbuf_cmp(strbuf* a, strbuf* b);
+int strbuf_c(strbuf* a, const char* b);
+
+/*
+ * Copy contents from src to dest.
+ * Assumes that dest is already initialized.
+ */
+int DEEP_COPY(strbuf)(strbuf*, strbuf*);
+
+/*
+ * Append char to end of strbuf, determined by s->len.
+ *
+ * TODO rename this PUSH(strbuf)?
+ */
+int strbuf_append(strbuf* s, char c);
+
+char strbuf_pop(strbuf*);
+
+/*
+ * Calls strbuf_append with NULL.
+ */
+int strbuf_cap(strbuf* s);
+
+/*
+ * Returns a pointer to character at index. Allows mutation of the
+ * value pointed to by the return address.
+ */
+char* charat(strbuf* s, unsigned int idx);
+
+/*
+ * Same as `charat`, But returns the current character.
+ */
+char* strbuf_cur(strbuf* s);
+
+/*
+ * Resets the seek for strbuf to 0.
+ */
+int strbuf_reset(strbuf* s);
+
+/*
+ * Sets the length and seek ptr to 0, but doesn't touch the memmory.
+ */
+int strbuf_soft_reset(strbuf* s);
+
+/*
+ * Returns the character after the last, so where null hopefully is.
+ */
+char* strbuf_end(strbuf* s);
+
+/*
+ * Reallocs dest to be the same size as src, and copies the contents
+ * of src into dest.
+ */
+int strbuf_realloc_copy(strbuf* dest, strbuf* src);
+
+/*
+ * Copies contents from src to dest, also allocating dest in the
+ * process. dest should not be initialized before self call.
+ */
+int strbuf_init_copy(strbuf* dest, strbuf* src);
+
+strbuf* RESOLVE(strbuf)(strbuf*, strbuf*);
+
+FMT_F(strbuf);
+
+int SIZE(strbuf)(strbuf*);
+
+int strbuf_load(strbuf* self, const char* str);
+
+#endif /* STRBUF_H */
diff --git a/src/termios.scm.c b/src/termios.scm.c
new file mode 100644
index 00000000..939c3574
--- /dev/null
+++ b/src/termios.scm.c
@@ -0,0 +1,44 @@
+#include <libguile.h>
+#include <unistd.h>
+#include <termios.h>
+#include <stdio.h>
+
+static struct termios *oldt, *newt;
+
+SCM_DEFINE(termios_lflags_and, "c-lflags-disable!", 2, 0, 0,
+ (SCM _fd, SCM _bits),
+ "")
+{
+
+ int fd = scm_to_int (_fd);
+ int bits = scm_to_int (_bits);
+
+ printf("Setting bits [%x]\n", bits);
+
+ tcgetattr(fd, oldt);
+ *newt = *oldt;
+
+ // Make the terminal not echo back,
+ // along with enabling cononical mode
+ newt->c_lflag &= ~ bits;
+ tcsetattr(fd, TCSANOW, newt);
+ return SCM_UNSPECIFIED;
+}
+
+SCM_DEFINE(termios_restore, "c-lflag-restore!", 1, 0, 0,
+ (SCM _fd),
+ "")
+{
+ int fd = scm_to_int (_fd);
+ tcsetattr(fd, TCSANOW, oldt);
+ return SCM_UNSPECIFIED;
+}
+
+void init_termios (void) {
+ oldt = scm_gc_malloc(sizeof(*oldt), "Termios");
+ newt = scm_gc_malloc(sizeof(*newt), "Termios");
+
+#ifndef SCM_MAGIC_SNARFER
+#include "termios.x"
+#endif
+}
diff --git a/src/trie.h b/src/trie.h
new file mode 100644
index 00000000..9de38be3
--- /dev/null
+++ b/src/trie.h
@@ -0,0 +1,54 @@
+#ifndef TRIE_H
+#define TRIE_H
+
+#include <stdio.h>
+
+#include "macro.h"
+
+#define TRIE(T) TEMPL(trie, T)
+#define TRIE_NODE(T) TEMPL(trie_node, T)
+
+#endif /* TRIE_H */
+#ifdef TYPE
+
+#include "linked_list.h"
+#include "strbuf.h"
+
+typedef struct TRIE_NODE(TYPE) {
+ char c;
+ TYPE* value;
+ struct TRIE_NODE(TYPE)* next;
+ struct TRIE_NODE(TYPE)* child;
+} TRIE_NODE(TYPE);
+
+typedef struct {
+ TRIE_NODE(TYPE)* root;
+} TRIE(TYPE);
+
+
+INIT_F ( TRIE(TYPE) );
+
+INIT_F (TRIE_NODE(TYPE), char c);
+
+INIT_F (TRIE_NODE(TYPE),
+ char c, TRIE_NODE(TYPE)* next, TRIE_NODE(TYPE)* child );
+
+int PUSH(TRIE(TYPE)) ( TRIE(TYPE)* trie, char* key, TYPE* val );
+
+TYPE* GET(TRIE(TYPE)) ( TRIE(TYPE)* trie, char* key );
+
+FREE_F(TRIE_NODE(TYPE));
+
+FREE_F(TRIE(TYPE));
+
+int EMPTY(TRIE(TYPE))(TRIE(TYPE)*);
+
+FMT_F(TRIE_NODE(TYPE));
+FMT_F(TRIE(TYPE));
+
+int DEEP_COPY(TRIE_NODE(TYPE)) (TRIE_NODE(TYPE)* dest, TRIE_NODE(TYPE)* src);
+int DEEP_COPY(TRIE(TYPE)) (TRIE(TYPE)* dest, TRIE(TYPE)* src);
+
+LLIST(strbuf)* KEYS(TRIE(TYPE)) (TRIE(TYPE)*);
+
+#endif /* TYPE */
diff --git a/src/trie.inc.h b/src/trie.inc.h
new file mode 100644
index 00000000..ffc8ac8e
--- /dev/null
+++ b/src/trie.inc.h
@@ -0,0 +1,228 @@
+#ifndef TYPE
+#error "Set TYPE before including self file"
+#else
+
+#include <stdarg.h>
+
+#include "err.h"
+#include "macro.h"
+#include "linked_list.inc.h"
+#include "strbuf.h"
+
+INIT_F ( TRIE(TYPE) ) {
+ NEW(TRIE_NODE(TYPE), t, '\0');
+ self->root = t;
+ return 0;
+}
+
+INIT_F (TRIE_NODE(TYPE), char c) {
+ self->c = c;
+ self->value = NULL;
+ self->next = NULL;
+ self->child = NULL;
+ return 0;
+}
+
+INIT_F (TRIE_NODE(TYPE),
+ char c,
+ TRIE_NODE(TYPE)* next,
+ TRIE_NODE(TYPE)* child )
+{
+ self->c = c;
+ self->next = next;
+ self->child = child;
+ return 0;
+}
+
+int PUSH(TRIE(TYPE)) ( TRIE(TYPE)* trie, char* key, TYPE* val ) {
+ TRIE_NODE(TYPE) *cur, *last;
+
+ last = trie->root;
+ cur = last->child;
+
+ char* subkey = key;
+
+ while (1) {
+ if (cur == NULL) {
+ /* Build direct LL for remaining subkey */
+ for (char* c = subkey; c[0] != '\0'; c++) {
+ NEW(TRIE_NODE(TYPE), t, *c);
+ last->child = t;
+ last = t;
+ }
+ last->value = RESOLVE(TYPE)(last->value, val);
+ return 0;
+ } else if (cur->c == subkey[0]) {
+ /* This node belongs to the key,
+ * Decend further */
+ last = cur;
+ cur = cur->child;
+ subkey++;
+ } else if (subkey[0] == '\0') {
+ /* Key finished */
+ last->value = RESOLVE(TYPE)(last->value, val);
+ return 0;
+ } else if (cur->next != NULL) {
+ /* This node was not part of the set, but it's sibling might */
+ cur = cur->next;
+ /* `last` not set since we aren't moving down */
+ } else {
+ /* No node on self level was part of the set, create a new__
+ * sibling and follow down that parse */
+ NEW(TRIE_NODE(TYPE), t, *subkey);
+ cur->next = t;
+ last = cur;
+ cur = t;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * TODO what happens when I give an invalid key?
+ */
+TYPE* GET(TRIE(TYPE)) ( TRIE(TYPE)* trie, char* key ) {
+ TRIE_NODE(TYPE)* n = trie->root->child;
+ char* subkey = key;
+
+ while (n != NULL) {
+ if (subkey[1] == '\0') {
+ /* Wanted node found,
+ * value can however be NULL */
+ return n->value;
+ } else if (subkey[0] == n->c) {
+ n = n->child;
+ subkey++;
+ } else {
+ n = n->next;
+ }
+ }
+
+ /* Position not found */
+ return 0;
+}
+
+FREE_F(TRIE_NODE(TYPE)) {
+ if (self == NULL) return 0;
+ if (self->value != NULL) FFREE(TYPE, self->value);
+ if (self->next != NULL) FREE(TRIE_NODE(TYPE))(self->next);
+ if (self->child != NULL) FREE(TRIE_NODE(TYPE))(self->child);
+ free (self);
+ return 0;
+}
+
+FREE_F(TRIE(TYPE)) {
+ if (self->root->c != '\0') {
+ // ERR("Invalid trie");
+ return 1;
+ }
+ return FREE(TRIE_NODE(TYPE))(self->root);
+}
+
+int EMPTY(TRIE(TYPE))(TRIE(TYPE)* self) {
+ return self->root->child == NULL;
+}
+
+FMT_F(TRIE_NODE(TYPE)) {
+
+ va_list ap;
+ va_start(ap, buf);
+ int argc = va_arg(ap, int);
+ int depth = argc >= 1
+ ? va_arg(ap, int)
+ : 0;
+ va_end(ap);
+
+ int seek = 0;
+
+ TRIE_NODE(TYPE)* n = self;
+
+ if (n == NULL) { fmtf("\n"); }
+ while (n != NULL) {
+ fmtf("|");
+ // FOR(int, i, depth) fmtf(" ");
+ for (int i = 0; i < depth; i++) fmtf(" ");
+ fmtf("%c ", n->c == '\0' ? '0' : n->c);
+ if (n->value != NULL) {
+ seek += FMT(TYPE)(n->value, buf + seek);
+ fmtf("\n");
+ }
+
+ if (n->child != NULL) {
+ fmtf("\n");
+ seek += FMT(TRIE_NODE(TYPE))(n->child, buf + seek, depth + 1);
+ }
+ n = n->next;
+ }
+ return seek;
+
+}
+
+FMT_F(TRIE(TYPE)) {
+ int seek = 0;
+ fmtf("Trie: %p: {", self);
+ if (EMPTY(TRIE(TYPE))(self)) {
+ fmtf(" [EMPTY] ");
+ } else {
+ fmtf("\n");
+ seek += FMT(TRIE_NODE(TYPE))(self->root->child, buf + seek);
+ }
+ fmtf("}");
+ return seek;
+}
+
+int DEEP_COPY(TRIE_NODE(TYPE)) (TRIE_NODE(TYPE)* dest, TRIE_NODE(TYPE)* src) {
+ dest->c = src->c;
+
+ if (src->value != NULL) {
+ RENEW(TYPE, dest->value);
+ DEEP_COPY(TYPE)(dest->value, src->value);
+ }
+
+ if (src->next != NULL) {
+ RENEW(TRIE_NODE(TYPE), dest->next, '\0');
+ DEEP_COPY(TRIE_NODE(TYPE))(dest->next, src->next);
+ }
+
+ if (src->child != NULL) {
+ RENEW(TRIE_NODE(TYPE), dest->child, '\0');
+ DEEP_COPY(TRIE_NODE(TYPE))(dest->child, src->child);
+ }
+
+ return 0;
+}
+
+int DEEP_COPY(TRIE(TYPE)) (TRIE(TYPE)* dest, TRIE(TYPE)* src) {
+ return DEEP_COPY(TRIE_NODE(TYPE))(dest->root, src->root);
+}
+
+void KEYS(TRIE_NODE(TYPE)) (TRIE_NODE(TYPE)* node, LLIST(strbuf)* list, strbuf* path) {
+ if (node == NULL) return;
+
+
+ if (node->value != NULL) {
+ strbuf_append(path, node->c);
+ NEW(strbuf, c);
+ DEEP_COPY(strbuf)(c, path);
+ PUSH(LLIST(strbuf))(list, c);
+ strbuf_pop(path);
+ }
+ if (node->next != NULL) {
+ KEYS(TRIE_NODE(TYPE)) (node->next, list, path);
+ }
+ if (node->child != NULL) {
+ if (node->c != '\0') strbuf_append(path, node->c);
+ KEYS(TRIE_NODE(TYPE)) (node->child, list, path);
+ if (node->c != '\0') strbuf_pop(path);
+ }
+}
+
+LLIST(strbuf)* KEYS(TRIE(TYPE)) (TRIE(TYPE)* trie) {
+ NEW(LLIST(strbuf), retlist);
+ SNEW(strbuf, key);
+ KEYS(TRIE_NODE(TYPE)) (trie->root, retlist, &key);
+ return retlist;
+}
+
+#endif /* TYPE */
diff --git a/src/vcal.c b/src/vcal.c
new file mode 100644
index 00000000..305275e7
--- /dev/null
+++ b/src/vcal.c
@@ -0,0 +1,152 @@
+#include "vcal.h"
+
+#include <string.h>
+
+#define TYPE strbuf
+#include "linked_list.inc.h"
+#undef TYPE
+
+#define TYPE param_set
+#include "trie.inc.h"
+#undef TYPE
+
+#define TYPE content_set
+#include "linked_list.inc.h"
+#undef TYPE
+
+#define T strbuf
+ #define V TRIE(param_set)
+ #include "pair.inc.h"
+ #undef V
+#undef T
+
+#define TYPE content_line
+// #include "hash.inc"
+#include "trie.inc.h"
+#undef TYPE
+
+#define TYPE vcomponent
+// #include "vector.inc.h"
+#include "linked_list.inc.h"
+#undef TYPE
+
+INIT_F(vcomponent) {
+ (void) self;
+ ERR("Do not use");
+ return 0;
+}
+
+INIT_F(vcomponent, const char* type) {
+ return INIT(vcomponent, self, type, NULL);
+}
+
+INIT_F(vcomponent, const char* type, const char* filename) {
+
+ INIT(TRIE(content_line), &self->clines);
+ INIT(LLIST(vcomponent), &self->components);
+
+ if (filename != NULL) {
+ vcomponent_push_val (self, "X-HNH-FILENAME", filename);
+ }
+
+ self->type = (char*) calloc(sizeof(*type), strlen(type) + 1);
+ strcpy(self->type, type);
+
+ self->parent = NULL;
+ self->scm = NULL;
+
+ return 0;
+}
+
+content_line* get_property (vcomponent* ev, const char* key) {
+ size_t len = strlen(key) + 1;
+ char* cpy = (char*) (calloc(sizeof(*cpy), len));
+ strncpy (cpy, key, len);
+
+ content_line* ret = GET(TRIE(content_line))(&ev->clines, cpy);
+
+ free (cpy);
+ return ret;
+}
+
+FREE_F(vcomponent) {
+ free(self->type);
+
+ if (FREE(TRIE(content_line))(&self->clines) != 0) {
+ ERR("Error freeing vcomponent");
+ }
+
+ FREE(LLIST(vcomponent))(&self->components);
+
+ return 0;
+}
+
+int PUSH(vcomponent)(vcomponent* parent, vcomponent* child) {
+ child->parent = parent;
+ return PUSH(LLIST(vcomponent))(&parent->components, child);
+}
+
+int DEEP_COPY(vcomponent)(vcomponent* a, vcomponent* b) {
+ (void) a;
+ (void) b;
+ ERR("Deep copy not implemented for vcomponent");
+ return -1;
+}
+
+int vcomponent_copy(vcomponent* dest, vcomponent* src) {
+
+ DEEP_COPY(TRIE(content_line))(&dest->clines, &src->clines);
+
+ /* Children are the same objects */
+ FOR(LLIST, vcomponent, c, &src->components) {
+ PUSH(LLIST(vcomponent))(&dest->components, c);
+ }
+
+ PUSH(vcomponent)(src->parent, dest);
+
+ return 0;
+}
+
+FMT_F(vcomponent) {
+ int seek = 0;
+
+ for (int i = 0; i < 40; i++) fmtf("_");
+
+ seek += sprintf(buf + seek, _YELLOW);
+ seek += sprintf(buf + seek, "\nVComponet (Type := %s)\n", self->type);
+ seek += sprintf(buf + seek, _RESET);
+ seek += FMT(TRIE(content_line))(&self->clines, buf + seek);
+ seek += sprintf(buf + seek, "\nComponents:\n");
+ FOR(LLIST, vcomponent, comp, &self->components) {
+ seek += FMT(vcomponent)(comp, buf + seek);
+ }
+
+ return seek;
+}
+
+int vcomponent_push_val (vcomponent* comp, const char* key, const char* val) {
+ NEW(content_line, cl);
+ NEW(content_set, cs);
+ strbuf_load(&cs->key, val);
+ PUSH(content_line)(cl, cs);
+
+ char* key_cpy = calloc(sizeof(*key_cpy), strlen(key) + 1);
+ strcpy (key_cpy, key);
+ PUSH(TRIE(content_line))(&comp->clines, key_cpy, cl);
+ free (key_cpy);
+
+ return 0;
+}
+
+char* vcomponent_get_val (vcomponent* comp, const char* key) {
+ char* key_cpy = calloc(sizeof(*key_cpy), strlen(key) + 1);
+ strcpy (key_cpy, key);
+ content_line* cl = GET(TRIE(content_line))(&comp->clines, key_cpy);
+ free (key_cpy);
+
+ if (cl != NULL && cl->cur->value != NULL) {
+ return cl->cur->value->key.mem;
+ }
+
+ return NULL;
+}
diff --git a/src/vcal.h b/src/vcal.h
new file mode 100644
index 00000000..1dfc5b17
--- /dev/null
+++ b/src/vcal.h
@@ -0,0 +1,118 @@
+#ifndef VCAL_H
+#define VCAL_H
+
+#include <stdlib.h>
+
+#include <libguile.h>
+
+#include "strbuf.h"
+
+#define TYPE strbuf
+#include "linked_list.h"
+// #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 param_set LLIST(strbuf)
+
+#define TYPE param_set
+#include "trie.h"
+#undef TYPE
+
+#define T strbuf
+ #define V TRIE(param_set)
+ #include "pair.h"
+ /* left := content | right := params */
+ #define content_set PAIR(strbuf, TRIE(param_set))
+ #undef V
+#undef T
+
+#define TYPE content_set
+#include "linked_list.h"
+#undef TYPE
+
+#define content_line LLIST(content_set)
+
+/*
+ * Helper macros for accessing fields in
+ * content_line, content_set, and param_set
+ */
+
+/* content_set */
+#define CLINE_CUR(c) ((c)->cur->value)
+
+/* strbuf */
+#define CLINE_CUR_VAL(c) (& CLINE_CUR(c)->key)
+
+/* TRIE(param_set) */
+#define CLINE_CUR_PARAMS(c) (& CLINE_CUR(c)->val)
+
+#define TYPE content_line
+#include "trie.h"
+#undef TYPE
+
+typedef struct s_vcomponent vcomponent;
+
+#define TYPE vcomponent
+// #include "vector.h"
+#include "linked_list.h"
+#undef TYPE
+
+struct s_vcomponent {
+ char* type;
+ vcomponent* parent;
+ TRIE(content_line) clines;
+ LLIST(vcomponent) components;
+
+ /*
+ * Holds a Guile representation of this object. Used to always
+ * return the same foreign (for guile) object for the same
+ * vcomponent.
+ */
+ SCM scm;
+};
+
+#define FCHILD(v) FIRST_V(&(v)->components)
+
+INIT_F(vcomponent);
+INIT_F(vcomponent, const char* type);
+INIT_F(vcomponent, const char* type, const char* filename);
+FREE_F(vcomponent);
+
+content_line* get_property (vcomponent* ev, const char* key);
+
+int add_content_line (vcomponent* ev, content_line* c);
+
+int vcomponent_push_val (vcomponent*, const char* key, const char* val);
+char* vcomponent_get_val (vcomponent*, const char* key);
+
+/*
+ * Appends ev to cal. Doesn't copy ev. So make sure that it wont go
+ * out of scope.
+ */
+int PUSH(vcomponent)(vcomponent*, vcomponent*);
+
+/*
+ * Deep copy is currently not implemented for vcomponentes.
+ * The reason for this method being here is since some
+ * generic methods in other places complain otherwise.
+ */
+int DEEP_COPY(vcomponent)(vcomponent*, vcomponent*);
+
+/*
+ * "Shallow" copy of vcomponent.
+ */
+int vcomponent_copy(vcomponent*, vcomponent*);
+
+FMT_F(vcomponent);
+
+#endif /* VCAL_H */