From d46183860c1f3f10095e95023adcb79b1896ab0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=B6rnquist?= Date: Fri, 22 Mar 2019 20:11:11 +0100 Subject: Move C and Scheme code into subdirs. --- src/calendar.c | 140 ++++++++++++++++++ src/calendar.h | 41 ++++++ src/err.h | 42 ++++++ src/graphs.c | 144 +++++++++++++++++++ src/graphs.h | 15 ++ src/guile_interface.h | 28 ++++ src/guile_interface.scm.c | 220 +++++++++++++++++++++++++++++ src/guile_type_helpers.c | 13 ++ src/guile_type_helpers.h | 13 ++ src/linked_list.h | 92 ++++++++++++ src/linked_list.inc.h | 176 +++++++++++++++++++++++ src/macro.h | 134 ++++++++++++++++++ src/main.c | 91 ++++++++++++ src/pair.h | 19 +++ src/pair.inc.h | 34 +++++ src/parse.c | 351 ++++++++++++++++++++++++++++++++++++++++++++++ src/parse.h | 122 ++++++++++++++++ src/strbuf.c | 151 ++++++++++++++++++++ src/strbuf.h | 109 ++++++++++++++ src/termios.scm.c | 44 ++++++ src/trie.h | 54 +++++++ src/trie.inc.h | 228 ++++++++++++++++++++++++++++++ src/vcal.c | 152 ++++++++++++++++++++ src/vcal.h | 118 ++++++++++++++++ 24 files changed, 2531 insertions(+) create mode 100644 src/calendar.c create mode 100644 src/calendar.h create mode 100644 src/err.h create mode 100644 src/graphs.c create mode 100644 src/graphs.h create mode 100644 src/guile_interface.h create mode 100644 src/guile_interface.scm.c create mode 100644 src/guile_type_helpers.c create mode 100644 src/guile_type_helpers.h create mode 100644 src/linked_list.h create mode 100644 src/linked_list.inc.h create mode 100644 src/macro.h create mode 100644 src/main.c create mode 100644 src/pair.h create mode 100644 src/pair.inc.h create mode 100644 src/parse.c create mode 100644 src/parse.h create mode 100644 src/strbuf.c create mode 100644 src/strbuf.h create mode 100644 src/termios.scm.c create mode 100644 src/trie.h create mode 100644 src/trie.inc.h create mode 100644 src/vcal.c create mode 100644 src/vcal.h (limited to 'src') 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 +#include +#include +#include +#include + +#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 + +#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 +#include +#include +#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 +#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 + +#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, 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 +#include +#include +#include + +#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 +#include +#include + +#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 (¶m_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 (¶m_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)(¶m_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 +#include + +#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 +#include + +#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 +#include +#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 +#include +#include +#include + +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 + +#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 + +#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 + +#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 + +#include + +#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 */ -- cgit v1.2.3