add a tool to print the tree of it - ics2txt - convert icalendar .ics file to plain text
(HTM) git clone git://bitreich.org/ics2txt git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/ics2txt
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) Tags
(DIR) README
---
(DIR) commit d4d55c6876bf51dd555a0dbfae0316343d44997e
(DIR) parent 8248ba97aa609be30e0ecf481d93e59a9876afcd
(HTM) Author: Josuah Demangeon <me@josuah.net>
Date: Sun, 28 Jun 2020 18:44:32 +0200
add a tool to print the tree of it
Diffstat:
M .gitignore | 1 +
M Makefile | 2 +-
A ics2tree.c | 100 +++++++++++++++++++++++++++++++
M ics2tsv.c | 25 +++++++++----------------
M src/ical.c | 249 +++++++++++++++++++++++--------
M src/ical.h | 65 ++++++++++++++++++-------------
M src/map.c | 16 +++++++++-------
M src/map.h | 4 ++--
8 files changed, 348 insertions(+), 114 deletions(-)
---
(DIR) diff --git a/.gitignore b/.gitignore
@@ -1,2 +1,3 @@
*.o
ics2tsv
+ics2tree
(DIR) diff --git a/Makefile b/Makefile
@@ -11,7 +11,7 @@ MANPREFIX = ${PREFIX}/man
SRC = src/ical.c src/map.c src/util.c src/log.c
HDR = src/ical.h src/map.h src/util.h src/log.h
OBJ = ${SRC:.c=.o}
-BIN = ics2tsv
+BIN = ics2tsv ics2tree
all: ${BIN}
(DIR) diff --git a/ics2tree.c b/ics2tree.c
@@ -0,0 +1,100 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ical.h"
+#include "log.h"
+#include "util.h"
+
+void
+print_ical_tree_param(struct map_entry *entry, int level)
+{
+ if (entry == NULL)
+ return;
+ for (int i = 0; i < level; i++)
+ printf(": ");
+ fprintf(stdout, "param %s=%s\n", entry->key, (char *)entry->value);
+}
+
+void
+print_ical_tree_value(struct ical_value *value, int level)
+{
+ if (value == NULL)
+ return;
+ for (int i = 0; i < level; i++)
+ printf(": ");
+ fprintf(stdout, "value %s:%s\n", value->name, value->value);
+ for (size_t i = 0; i < value->param.len; i++)
+ print_ical_tree_param(value->param.entry + i, level + 1);
+ print_ical_tree_value(value->next, level);
+}
+
+void
+print_ical_tree_vnode(struct ical_vnode *node, int level)
+{
+ if (node == NULL)
+ return;
+ for (int i = 0; i < level; i++)
+ printf(": ");
+ fprintf(stdout, "node %p %s child=%p next=%p\n", node, node->name, node->child, node->next);
+ for (size_t i = 0; i < node->values.len; i++)
+ print_ical_tree_value(node->values.entry[i].value, level + 1);
+ print_ical_tree_vnode(node->child, level + 1);
+ print_ical_tree_vnode(node->next, level);
+}
+
+int
+print_ical_tree(FILE *fp)
+{
+ struct ical_vcalendar vcal;
+ int e;
+
+ if ((e = ical_read_vcalendar(&vcal, fp)) < 0)
+ die("reading ical file: %s", ical_strerror(e));
+
+ print_ical_tree_vnode(vcal.root, 0);
+ fprintf(stdout, ".\n");
+ fflush(stdout);
+
+ ical_free_vcalendar(&vcal);
+ return 0;
+}
+
+void
+print_header(void)
+{
+ char *fields[] = { "", NULL };
+
+ printf("%s\t%s", "beg", "end");
+
+ for (char **f = fields; *f != NULL; f++) {
+ fprintf(stdout, "\t%s", *f);
+ }
+ fprintf(stdout, "\n");
+}
+
+int
+main(int argc, char **argv)
+{
+ print_header();
+
+ log_arg0 = *argv++;
+
+ if (*argv == NULL) {
+ if (print_ical_tree(stdin) < 0)
+ die("converting stdin");
+ }
+
+ for (; *argv != NULL; argv++, argc--) {
+ FILE *fp;
+
+ info("converting \"%s\"", *argv);
+ if ((fp = fopen(*argv, "r")) == NULL)
+ die("opening %s", *argv);
+ if (print_ical_tree(fp) < 0)
+ die("converting %s", *argv);
+ fclose(fp);
+ }
+
+ return 0;
+}
(DIR) diff --git a/ics2tsv.c b/ics2tsv.c
@@ -7,23 +7,16 @@
#include "util.h"
int
-print_ical_to_tsv(FILE *fp)
+print_ical_tsv(FILE *fp)
{
- struct ical_contentline cl;
- char *line = NULL, *ln = NULL;
- size_t sz = 0;
- ssize_t r;
+ struct ical_vcalendar vcal;
+ int e;
- memset(&cl, 0, sizeof cl);
+ if ((e = ical_read_vcalendar(&vcal, fp)) < 0)
+ die("reading ical file: %s", ical_strerror(e));
- while ((r = ical_read_line(&line, &ln, &sz, fp)) > 0) {
- debug("readling line \"%s\"", line);
- if (ical_parse_contentline(&cl, line) < 0)
- die("parsing line \"%s\"", line);
- }
- free(line);
- free(ln);
- return r;
+ ical_free_vcalendar(&vcal);
+ return 0;
}
void
@@ -47,7 +40,7 @@ main(int argc, char **argv)
log_arg0 = *argv++;
if (*argv == NULL) {
- if (print_ical_to_tsv(stdin) < 0)
+ if (print_ical_tsv(stdin) < 0)
die("converting stdin");
}
@@ -57,7 +50,7 @@ main(int argc, char **argv)
info("converting \"%s\"", *argv);
if ((fp = fopen(*argv, "r")) == NULL)
die("opening %s", *argv);
- if (print_ical_to_tsv(fp) < 0)
+ if (print_ical_tsv(fp) < 0)
die("converting %s", *argv);
fclose(fp);
}
(DIR) diff --git a/src/ical.c b/src/ical.c
@@ -9,8 +9,10 @@
#include "util.h"
+enum ical_err ical_errno;
+
int
-ical_read_line(char **line, char **ln, size_t *sz, FILE *fp)
+ical_getline(char **line, char **ln, size_t *sz, FILE *fp)
{
int c;
void *v;
@@ -35,113 +37,240 @@ ical_read_line(char **line, char **ln, size_t *sz, FILE *fp)
return 1;
}
+char *
+ical_strerror(int i)
+{
+ enum ical_err err = (i > 0) ? i : -i;
+
+ switch (err) {
+ case ICAL_ERR_OK:
+ return "no error";
+ case ICAL_ERR_SYSTEM:
+ return "system error";
+ case ICAL_ERR_END_MISMATCH:
+ return "END: does not match its corresponding BEGIN:";
+ case ICAL_ERR_MISSING_BEGIN:
+ return "unexpected content line before any BEGIN:";
+ case ICAL_ERR_MIN_NESTED:
+ return "too many END: for the number of BEGIN:";
+ case ICAL_ERR_MAX_NESTED:
+ return "maximum nesting level reached";
+ case ICAL_ERR_LENGTH:
+ assert(!"used internally, should not happen");
+ }
+ assert(!"unknown error code");
+ return "not a valid ical error code";
+}
+
+struct ical_value *
+ical_new_value(char const *line)
+{
+ struct ical_value *new;
+ size_t len;
+
+ len = strlen(line);
+ if ((new = calloc(1, sizeof *new + len + 1)) == NULL)
+ return NULL;
+ memcpy(new->buf, line, len + 1);
+ return new;
+}
+
+void
+ical_free_value(struct ical_value *value)
+{
+ debug("free value %p (%s:%s)", value, value->name, value->value);
+ map_free(&value->param, free);
+ free(value);
+}
+
int
-ical_parse_contentline(struct ical_contentline *cl, char *line)
+ical_parse_value(struct ical_value *value)
{
char *column, *equal, *param, *cp;
- size_t sz;
int e = errno;
- if ((column = strchr(line, ':')) == NULL)
+ value->name = value->buf;
+
+ if ((column = strchr(value->buf, ':')) == NULL)
return -1;
*column = '\0';
- if ((cl->value = strdup(column + 1)) == NULL)
- return -1;
+ value->value = column + 1;
- if ((cp = strchr(line, ';')) != NULL)
- cp++;
+ if ((cp = strchr(value->buf, ';')) != NULL)
+ *cp++ = '\0';
while ((param = strsep(&cp, ";")) != NULL) {
if ((equal = strchr(param, '=')) == NULL)
return -1;
*equal = '\0';
- if (map_set(&cl->param, param, equal + 1) < 0)
+ if (map_set(&value->param, param, equal + 1) < 0)
return -1;
}
- sz = sizeof cl->name;
- if (strlcpy(cl->name, line, sz) >= sz)
- return errno=EMSGSIZE, -1;
-
assert(errno == e);
return 0;
}
-int
-ical_parse_tzid(struct ical_value *value, struct ical_contentline *cl)
+struct ical_vnode *
+ical_new_vnode(char const *name)
{
- return 0;
+ struct ical_vnode *new;
+ size_t sz;
+
+ if ((new = calloc(1, sizeof *new)) == NULL)
+ return NULL;
+ sz = sizeof new->name;
+ if (strlcpy(new->name, name, sz) >= sz) {
+ errno = EMSGSIZE;
+ goto err;
+ }
+ return new;
+err:
+ ical_free_vnode(new);
+ return NULL;
}
-int
-ical_parse_date(struct ical_value *value, struct ical_contentline *cl)
+static void
+ical_free_vnode_value(void *v)
{
- return 0;
+ ical_free_value(v);
+}
+
+void
+ical_free_vnode(struct ical_vnode *node)
+{
+ if (node == NULL)
+ return;
+ debug("free vnode %p %s", node, node->name);
+ map_free(&node->values, ical_free_vnode_value);
+ ical_free_vnode(node->child);
+ ical_free_vnode(node->next);
+ free(node);
}
int
-ical_parse_attribute(struct ical_value *value, struct ical_contentline *cl)
+ical_push_nested(struct ical_vcalendar *vcal, struct ical_vnode *new)
{
+ struct ical_vnode **node;
+
+ node = vcal->nested;
+ for (int i = 0; *node != NULL; node++, i++) {
+ if (i >= ICAL_NESTED_MAX)
+ return -ICAL_ERR_MAX_NESTED;
+ }
+ node[0] = new;
+ node[1] = NULL;
return 0;
}
+struct ical_vnode *
+ical_pop_nested(struct ical_vcalendar *vcal)
+{
+ struct ical_vnode **node, **prev = vcal->nested, *old;
+
+ for (prev = node = vcal->nested; *node != NULL; node++) {
+ vcal->current = *prev;
+ prev = node;
+ old = *node;
+ }
+ *prev = NULL;
+ if (vcal->nested[0] == NULL)
+ vcal->current = NULL;
+ return old;
+}
+
int
ical_begin_vnode(struct ical_vcalendar *vcal, char const *name)
{
- if (strcasecmp(name, "VCALENDAR"))
- return 0;
- return -1;
+ struct ical_vnode *new;
+ int e;
+
+ if ((new = ical_new_vnode(name)) == NULL)
+ return -ICAL_ERR_SYSTEM;
+ if ((e = ical_push_nested(vcal, new)) < 0)
+ goto err;
+ if (vcal->root == NULL) {
+ vcal->root = new;
+ vcal->current = new;
+ } else {
+ new->next = vcal->current->child;
+ vcal->current->child = new;
+ vcal->current = new;
+ }
+ return 0;
+err:
+ ical_free_vnode(new);
+ return e;
}
int
ical_end_vnode(struct ical_vcalendar *vcal, char const *name)
{
- if (strcasecmp(name, "VCALENDAR"))
- return 0;
- return -1;
+ struct ical_vnode *old;
+
+ if ((old = ical_pop_nested(vcal)) == NULL)
+ return -ICAL_ERR_MIN_NESTED;
+ if (strcasecmp(name, old->name) != 0)
+ return -ICAL_ERR_END_MISMATCH;
+ return 0;
}
int
-ical_add_contentline(struct ical_vcalendar *vcal, struct ical_contentline *cl)
+ical_push_value(struct ical_vcalendar *vcal, struct ical_value *new)
{
- struct ical_value value_buf, *value = &value_buf;
- int i;
- struct {
- char *name;
- enum ical_value_type type;
- int (*fn)(struct ical_value *, struct ical_contentline *);
- } map[] = {
- { "DTSTART", ICAL_VALUE_TIME, ical_parse_date },
- { "DTEND", ICAL_VALUE_TIME, ical_parse_date },
- { "TZID", ICAL_VALUE_TIME, ical_parse_tzid },
- { NULL, ICAL_VALUE_ATTRIBUTE, ical_parse_attribute },
- };
-
- if (strcasecmp(cl->name, "BEGIN") == 0)
- return ical_begin_vnode(vcal, cl->value);
-
- if (strcasecmp(cl->name, "END") == 0)
- return ical_end_vnode(vcal, cl->value);
-
- memset(value, 0, sizeof *value);
-
- for (i = 0; map[i].name == NULL; i++)
- if (strcasecmp(cl->name, map[i].name) == 0)
- break;
- value->type = map[i].type;
- if (map[i].fn(value, cl) < 0)
- return -1;
+ if (strcasecmp(new->name, "BEGIN") == 0) {
+ int e = ical_begin_vnode(vcal, new->value);
+ ical_free_value(new);
+ return e;
+ }
+ if (strcasecmp(new->name, "END") == 0) {
+ int e = ical_end_vnode(vcal, new->value);
+ ical_free_value(new);
+ return e;
+ }
+
+ if (vcal->current == NULL)
+ return -ICAL_ERR_MISSING_BEGIN;
+
+ debug("new %p %s:%s", new, new->name, new->value);
+ new->next = map_get(&vcal->current->values, new->name);
+ if (map_set(&vcal->current->values, new->name, new) < 0)
+ return -ICAL_ERR_SYSTEM;
+
return 0;
}
-void
-ical_free_value(struct ical_value *value)
+int
+ical_read_vcalendar(struct ical_vcalendar *vcal, FILE *fp)
{
- ;
+ char *line = NULL, *ln = NULL;
+ size_t sz = 0;
+ ssize_t r;
+ int e;
+
+ memset(vcal, 0, sizeof *vcal);
+
+ while ((r = ical_getline(&line, &ln, &sz, fp)) > 0) {
+ struct ical_value *new;
+
+ if ((new = ical_new_value(line)) == NULL) {
+ e = -ICAL_ERR_SYSTEM;
+ goto err;
+ }
+ if ((e = ical_parse_value(new)) < 0)
+ goto err;
+ if ((e = ical_push_value(vcal, new)) < 0)
+ goto err;
+ }
+ e = (r == 0) ? 0 : -ICAL_ERR_SYSTEM;
+err:
+ free(line);
+ free(ln);
+ return e;
}
void
-ical_free_contentline(struct ical_contentline *cl)
+ical_free_vcalendar(struct ical_vcalendar *vcal)
{
- map_free(&cl->param);
- free(cl->value);
+ debug("free vcalendar");
+ ical_free_vnode(vcal->root);
}
(DIR) diff --git a/src/ical.h b/src/ical.h
@@ -6,36 +6,25 @@
#include "map.h"
-#define ICAL_NEST_MAX 4
+#define ICAL_NESTED_MAX 4
-/* */
+enum ical_err {
+ ICAL_ERR_OK,
+ ICAL_ERR_SYSTEM,
+ ICAL_ERR_END_MISMATCH,
+ ICAL_ERR_MISSING_BEGIN,
+ ICAL_ERR_MIN_NESTED,
+ ICAL_ERR_MAX_NESTED,
-struct ical_contentline {
- char name[32], *value;
- struct map param;
-};
-
-/* single value for an iCalendar element attribute */
-
-enum ical_value_type {
- ICAL_VALUE_TIME, ICAL_VALUE_ATTRIBUTE,
-} type;
-
-union ical_value_union {
- time_t *time;
- char *str;
-};
-
-struct ical_value {
- enum ical_value_type type;
- union ical_value_union value;
+ ICAL_ERR_LENGTH,
};
/* global propoerties for an iCalendar document as well as parsing state */
struct ical_vcalendar {
time_t tzid;
- char *stack[ICAL_NEST_MAX + 1];
+ struct ical_vnode *root;
+ struct ical_vnode *nested[ICAL_NESTED_MAX + 1];
struct ical_vnode *current;
};
@@ -44,14 +33,34 @@ struct ical_vcalendar {
struct ical_vnode {
char name[32];
time_t beg, end;
- struct map properties; /* struct ical_value */
- struct ical_vnode *child, *next;
+ struct map values; /*(struct ical_value *)*/
+ struct ical_vnode *child;
+ struct ical_vnode *next;
+};
+
+/* one line whith the whole content unfolded */
+
+struct ical_value {
+ char *name, *value;
+ struct map param;
+ struct ical_value *next;
+ char buf[];
};
/** src/ical.c **/
-int ical_read_line(char **line, char **ln, size_t *sz, FILE *fp);
-int ical_parse_contentline(struct ical_contentline *contentline, char *line);
-void ical_init_contentline(struct ical_contentline *contentline);
-void ical_free_contentline(struct ical_contentline *contentline);
+int ical_getline(char **line, char **ln, size_t *sz, FILE *fp);
+char * ical_strerror(int i);
+struct ical_value * ical_new_value(char const *line);
+void ical_free_value(struct ical_value *value);
+int ical_parse_value(struct ical_value *value);
+struct ical_vnode * ical_new_vnode(char const *name);
+void ical_free_vnode(struct ical_vnode *node);
+int ical_push_nested(struct ical_vcalendar *vcal, struct ical_vnode *new);
+struct ical_vnode * ical_pop_nested(struct ical_vcalendar *vcal);
+int ical_begin_vnode(struct ical_vcalendar *vcal, char const *name);
+int ical_end_vnode(struct ical_vcalendar *vcal, char const *name);
+int ical_push_value(struct ical_vcalendar *vcal, struct ical_value *new);
+void ical_free_vcalendar(struct ical_vcalendar *vcal);
+int ical_read_vcalendar(struct ical_vcalendar *vcal, FILE *fp);
#endif
(DIR) diff --git a/src/map.c b/src/map.c
@@ -54,8 +54,7 @@ map_set(struct map *map, char *key, void *value)
for (; e >= insert; e--)
e[1].key = e[0].key;
- if ((insert->key = strdup(key)) == NULL)
- return -1;
+ insert->key = key;
insert->value = value;
return 0;
@@ -90,16 +89,19 @@ map_init(struct map *map)
}
void
-map_free_values(struct map *map)
+map_free_keys(struct map *map)
{
for (size_t i = 0; i < map->len; i++)
- free(map->entry[map->len - 1].value);
+ free(map->entry[i].key);
}
void
-map_free(struct map *map)
+map_free(struct map *map, void (*fn)(void *))
{
- for (size_t i = 0; i < map->len; i++)
- free(map->entry[map->len - 1].key);
+ if (fn != NULL) {
+ for (size_t i = 0; i < map->len; i++)
+ fn(map->entry[i].value);
+ }
free(map->entry);
+ map->len = 0;
}
(DIR) diff --git a/src/map.h b/src/map.h
@@ -18,7 +18,7 @@ void * map_get(struct map *map, char *key);
int map_set(struct map *map, char *key, void *value);
int map_del(struct map *map, char *key);
void map_init(struct map *map);
-void map_free_values(struct map *map);
-void map_free(struct map *map);
+void map_free_keys(struct map *map);
+void map_free(struct map *map, void (*fn)(void *));
#endif