refactor the code to avoid copying the whole ical file to memory - ics2txt - co… | |
git clone git://bitreich.org/ics2txt git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws… | |
Log | |
Files | |
Refs | |
Tags | |
README | |
--- | |
commit 917f5b056d0b1241f0816bfd41276a36b5727fb1 | |
parent b81a0df00fa8f12b03fa371762b6bd4ee3db8422 | |
Author: Josuah Demangeon <[email protected]> | |
Date: Sat, 12 Jun 2021 22:50:29 +0200 | |
refactor the code to avoid copying the whole ical file to memory | |
This is how Evil_Bob does it with xml.c, a sane and tiny XML parser. | |
The document structure gets parsed, and a struct parser passed to the | |
parser contains function pointers. As parsed objects get encountered, | |
the matching function pointers gets called with the parsed text as | |
parameter. Then the content can be processed as it is read, instead of | |
parsed first and processed after. | |
Diffstat: | |
M Makefile | 4 ++-- | |
M ical.c | 351 ++++++++++-------------------… | |
M ical.h | 79 ++++++++---------------------… | |
M ics2tree.c | 81 +++++++++++++----------------… | |
D log.c | 89 -----------------------------… | |
D log.h | 15 --------------- | |
D map.c | 106 ------------------------------ | |
D map.h | 24 ------------------------ | |
M util.c | 88 +++++++++++++++++++++++++----… | |
M util.h | 21 +++++++++++++++------ | |
10 files changed, 253 insertions(+), 605 deletions(-) | |
--- | |
diff --git a/Makefile b/Makefile | |
@@ -7,8 +7,8 @@ CFLAGS = $D $W -g | |
PREFIX = /usr/local | |
MANPREFIX = ${PREFIX}/man | |
-SRC = ical.c map.c util.c log.c | |
-HDR = ical.h map.h util.h log.h | |
+SRC = ical.c util.c | |
+HDR = ical.h util.h | |
OBJ = ${SRC:.c=.o} | |
BIN = ics2tree | |
MAN1 = ics2txt.1 | |
diff --git a/ical.c b/ical.c | |
@@ -1,288 +1,157 @@ | |
#include "ical.h" | |
#include <assert.h> | |
+#include <ctype.h> | |
#include <errno.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
-#include <strings.h> /* strcase* */ | |
+#include <strings.h> | |
#include "util.h" | |
-enum ical_err ical_errno; | |
- | |
-int | |
-ical_getline(char **line, char **ln, size_t *sz, FILE *fp) | |
+static int | |
+ical_error(IcalParser *p, char const *msg) | |
{ | |
- int c; | |
- void *v; | |
- | |
- if ((v = realloc(*line, 1)) == NULL) | |
- return -ICAL_ERR_SYSTEM; | |
- *line = v; | |
- (*line)[0] = '\0'; | |
- | |
- do { top: | |
- if (getline(ln, sz, fp) <= 0) | |
- return ferror(fp) ? -ICAL_ERR_SYSTEM : 0; | |
- strchomp(*ln); | |
- if (**ln == '\0') | |
- goto top; | |
- if (strappend(line, *ln) < 0) | |
- return -ICAL_ERR_SYSTEM; | |
- if ((c = fgetc(fp)) == EOF) | |
- return ferror(fp) ? -ICAL_ERR_SYSTEM : 1; | |
- } while (c == ' '); | |
- | |
- ungetc(c, fp); | |
- assert(!ferror(fp)); | |
- return 1; | |
+ p->errmsg = msg; | |
+ return -1; | |
} | |
-char * | |
-ical_strerror(int i) | |
-{ | |
- enum ical_err err = (i > 0) ? i : -i; | |
+#define CALL(p, fn, ...) ((p)->fn ? (p)->fn((p), __VA_ARGS__) : 0) | |
- 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_MISSING_COLUMN: | |
- return "missing ':' character from line"; | |
- case ICAL_ERR_MISSING_SEMICOLUMN: | |
- return "missing ';' character before ':'"; | |
- case ICAL_ERR_MISSING_EQUAL: | |
- return "missing '=' character in parameter before ':'"; | |
- 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) | |
+static int | |
+ical_parse_value(IcalParser *p, char **sp, char *name) | |
{ | |
- struct ical_value *new; | |
- size_t len; | |
+ int err; | |
+ char *s, c, *val; | |
- len = strlen(line); | |
- if ((new = calloc(1, sizeof *new + len + 1)) == NULL) | |
- return NULL; | |
- memcpy(new->buf, line, len + 1); | |
- return new; | |
-} | |
+ s = *sp; | |
-void | |
-ical_free_value(struct ical_value *value) | |
-{ | |
- map_free(&value->param, NULL); | |
- free(value); | |
-} | |
- | |
-int | |
-ical_parse_value(struct ical_value *value) | |
-{ | |
- char *column, *equal, *param, *cp; | |
- int e = errno; | |
- | |
- value->name = value->buf; | |
- | |
- if ((column = strchr(value->buf, ':')) == NULL) | |
- return -ICAL_ERR_MISSING_COLUMN; | |
- *column = '\0'; | |
- value->value = column + 1; | |
- | |
- if ((cp = strchr(value->buf, ';')) != NULL) | |
- *cp++ = '\0'; | |
- while ((param = strsep(&cp, ";")) != NULL) { | |
- if ((equal = strchr(param, '=')) == NULL) | |
- return -ICAL_ERR_MISSING_EQUAL; | |
- *equal = '\0'; | |
- if (map_set(&value->param, param, equal + 1) < 0) | |
- return -ICAL_ERR_SYSTEM; | |
+ if (*s == '"') { | |
+ ++s; | |
+ for (val = s; !iscntrl(*s) && !strchr(",;:\"", *s); s++); | |
+ if (*s != '"') | |
+ return ical_error(p, "missing '\"'"); | |
+ *s++ = '\0'; | |
+ } else { | |
+ for (val = s; !iscntrl(*s) && !strchr(",;:'\"", *s); s++); | |
} | |
- assert(errno == e); | |
- return 0; | |
-} | |
- | |
-struct ical_vnode * | |
-ical_new_vnode(char const *name) | |
-{ | |
- struct ical_vnode *new; | |
- size_t sz; | |
+ c = *s, *s = '\0'; | |
+ if ((err = CALL(p, fn_param_value, name, val)) != 0) | |
+ return err; | |
+ *s = c; | |
- 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; | |
+ *sp = s; | |
+ return 0; | |
} | |
-static void | |
-ical_free_value_void(void *v) | |
+static int | |
+ical_parse_param(IcalParser *p, char **sp) | |
{ | |
- ical_free_value(v); | |
-} | |
+ int err; | |
+ char *s, *name; | |
-static void | |
-ical_free_vnode_void(void *v) | |
-{ | |
- ical_free_vnode(v); | |
-} | |
+ s = *sp; | |
-void | |
-ical_free_vnode(struct ical_vnode *node) | |
-{ | |
- if (node == NULL) | |
- return; | |
- map_free(&node->values, ical_free_value_void); | |
- map_free(&node->childs, ical_free_vnode_void); | |
- ical_free_vnode(node->next); | |
- free(node); | |
-} | |
+ do { | |
+ for (name = s; isalnum(*s) || *s == '-'; s++); | |
+ if (s == name || (*s != '=')) | |
+ return ical_error(p, "invalid parameter name"); | |
+ *s++ = '\0'; | |
+ if ((err = CALL(p, fn_param_name, name)) != 0) | |
+ return err; | |
-int | |
-ical_push_nested(struct ical_vcalendar *vcal, struct ical_vnode *new) | |
-{ | |
- struct ical_vnode **node; | |
+ do { | |
+ if ((err = ical_parse_value(p, &s, name)) != 0) | |
+ return err; | |
+ } while (*s == ',' && s++); | |
+ } while (*s == ';' && s++); | |
- 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; | |
+ *sp = s; | |
return 0; | |
} | |
-struct ical_vnode * | |
-ical_pop_nested(struct ical_vcalendar *vcal) | |
+static int | |
+ical_parse_contentline(IcalParser *p, char *line) | |
{ | |
- struct ical_vnode **node, **prev = vcal->nested, *old; | |
+ int err; | |
+ char *s, c, *name, *end; | |
- 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; | |
-} | |
+ s = line; | |
-int | |
-ical_begin_vnode(struct ical_vcalendar *vcal, char const *name) | |
-{ | |
- struct ical_vnode *new; | |
- int e; | |
+ for (name = s; isalnum(*s) || *s == '-'; s++); | |
+ if (s == name || (*s != ';' && *s != ':')) | |
+ return ical_error(p, "invalid entry name"); | |
+ c = *s, *s = '\0'; | |
+ if (strcasecmp(name, "BEGIN") != 0 && strcasecmp(name, "END") != 0) | |
+ if ((err = CALL(p, fn_entry_name, name)) != 0) | |
+ return err; | |
+ *s = c; | |
+ end = s; | |
- 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; | |
- } else { | |
- new->next = map_get(&vcal->current->childs, new->name); | |
- if (map_set(&vcal->current->childs, new->name, new) < 0) { | |
- e = -ICAL_ERR_SYSTEM; | |
- goto err; | |
- } | |
+ while (*s == ';') { | |
+ s++; | |
+ if ((err = ical_parse_param(p, &s)) != 0) | |
+ return err; | |
} | |
- vcal->current = new; | |
- return 0; | |
-err: | |
- ical_free_vnode(new); | |
- return e; | |
-} | |
-int | |
-ical_end_vnode(struct ical_vcalendar *vcal, char const *name) | |
-{ | |
- 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_push_value(struct ical_vcalendar *vcal, struct ical_value *new) | |
-{ | |
- 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 (*s != ':') | |
+ return ical_error(p, "expected ':' delimiter"); | |
+ s++; | |
+ | |
+ *end = '\0'; | |
+ if (strcasecmp(name, "BEGIN") == 0) { | |
+ if ((err = CALL(p, fn_block_begin, s)) != 0) | |
+ return err; | |
+ p->level++; | |
+ } else if (strcasecmp(name, "END") == 0) { | |
+ if ((err = CALL(p, fn_block_end, s)) != 0) | |
+ return err; | |
+ p->level--; | |
+ } else { | |
+ if ((err = CALL(p, fn_entry_value, name, s)) != 0) | |
+ return err; | |
} | |
- if (vcal->current == NULL) | |
- return -ICAL_ERR_MISSING_BEGIN; | |
- | |
- 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; | |
} | |
int | |
-ical_read_vcalendar(struct ical_vcalendar *vcal, FILE *fp) | |
+ical_parse(IcalParser *p, FILE *fp) | |
{ | |
- char *line = NULL, *ln = NULL; | |
+ char *ln = NULL, *contentline = 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; | |
+ int err, c; | |
+ | |
+ while (!feof(fp)) { | |
+ if ((contentline = realloc(contentline, 1)) == NULL) | |
+ return -1; | |
+ *contentline = '\0'; | |
+ | |
+ do { | |
+ do { | |
+ p->line++; | |
+ if (getline(&ln, &sz, fp) <= 0) | |
+ return -1; | |
+ strchomp(ln); | |
+ } while (*ln == '\0'); | |
+ | |
+ if (strappend(&contentline, ln) < 0) | |
+ return -1; | |
+ if ((c = fgetc(fp)) == EOF) { | |
+ if (ferror(fp)) | |
+ return -1; | |
+ goto done; | |
+ } | |
+ } while (c == ' '); | |
+ ungetc(c, fp); | |
+done: | |
+ assert(!ferror(fp)); | |
+ if ((err = ical_parse_contentline(p, contentline)) != 0) | |
+ break; | |
} | |
- e = (r == 0) ? 0 : -ICAL_ERR_SYSTEM; | |
-err: | |
- free(line); | |
+ free(contentline); | |
free(ln); | |
- return e; | |
-} | |
- | |
-void | |
-ical_free_vcalendar(struct ical_vcalendar *vcal) | |
-{ | |
- ical_free_vnode(vcal->root); | |
+ return err; | |
} | |
diff --git a/ical.h b/ical.h | |
@@ -4,65 +4,28 @@ | |
#include <stdio.h> | |
#include <time.h> | |
-#include "map.h" | |
- | |
-#define ICAL_NESTED_MAX 4 | |
- | |
-enum ical_err { | |
- ICAL_ERR_OK, | |
- ICAL_ERR_SYSTEM, | |
- ICAL_ERR_END_MISMATCH, | |
- ICAL_ERR_MISSING_BEGIN, | |
- ICAL_ERR_MISSING_COLUMN, | |
- ICAL_ERR_MISSING_SEMICOLUMN, | |
- ICAL_ERR_MISSING_EQUAL, | |
- ICAL_ERR_MIN_NESTED, | |
- ICAL_ERR_MAX_NESTED, | |
- | |
- ICAL_ERR_LENGTH, | |
-}; | |
- | |
-/* global propoerties for an iCalendar document as well as parsing state */ | |
- | |
-struct ical_vcalendar { | |
- time_t tzid; | |
- struct ical_vnode *root; | |
- struct ical_vnode *nested[ICAL_NESTED_MAX + 1]; | |
- struct ical_vnode *current; | |
-}; | |
- | |
-/* element part of an iCalendar document with eventual nested childs */ | |
- | |
-struct ical_vnode { | |
- char name[32]; | |
- struct map values; /*(struct ical_value *)*/ | |
- struct map childs; /*(struct ical_vnode *)*/ | |
- 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[]; | |
+typedef struct IcalParser IcalParser; | |
+struct IcalParser { | |
+ /* function called on content */ | |
+ int (*fn_entry_name)(IcalParser *, char *); | |
+ int (*fn_param_name)(IcalParser *, char *); | |
+ int (*fn_param_value)(IcalParser *, char *, char *); | |
+ int (*fn_entry_value)(IcalParser *, char *, char *); | |
+ int (*fn_block_begin)(IcalParser *, char *); | |
+ int (*fn_block_end)(IcalParser *, char *); | |
+ /* if returning non-zero then halt the parser */ | |
+ | |
+ int base64encoded; | |
+ char const *errmsg; | |
+ size_t line; | |
+ | |
+ /* stack of blocks names: "name1\0name2\0...nameN\0\0" */ | |
+ int level; | |
+ char stack[1024]; | |
}; | |
-/** src/ical.c **/ | |
-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); | |
+int ical_parse(IcalParser *, FILE *); | |
+//TODO: char *ical_get_time(char *); | |
+//TODO: char *ical_get_value(IcalCtx *, char *); | |
#endif | |
diff --git a/ics2tree.c b/ics2tree.c | |
@@ -3,76 +3,63 @@ | |
#include <string.h> | |
#include "ical.h" | |
-#include "log.h" | |
#include "util.h" | |
-void | |
+static void | |
print_ruler(int level) | |
{ | |
- for (int i = 0; i < level; i++) | |
+ while (level-- > 0) | |
fprintf(stdout, ": "); | |
} | |
-void | |
-print_ical_tree_param(struct map_entry *entry, int level) | |
+static int | |
+fn_entry_name(IcalParser *p, char *name) | |
{ | |
- if (entry == NULL) | |
- return; | |
- print_ruler(level); | |
- fprintf(stdout, "param %s=%s\n", entry->key, (char *)entry->value); | |
+ print_ruler(p->level); | |
+ printf("name %s\n", name); | |
+ return 0; | |
} | |
-void | |
-print_ical_tree_value(struct ical_value *value, int level) | |
+static int | |
+fn_block_begin(IcalParser *p, char *name) | |
{ | |
- if (value == NULL) | |
- return; | |
- print_ruler(level); | |
- 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); | |
+ print_ruler(p->level); | |
+ printf("begin %s\n", name); | |
+ return 0; | |
} | |
-void | |
-print_ical_tree_vnode(struct ical_vnode *node, int level) | |
+static int | |
+fn_param_value(IcalParser *p, char *name, char *value) | |
{ | |
- if (node == NULL) | |
- return; | |
- print_ruler(level); | |
- fprintf(stdout, "node %s\n", node->name); | |
- for (size_t i = 0; i < node->values.len; i++) | |
- print_ical_tree_value(node->values.entry[i].value, level + 1); | |
- for (size_t i = 0; i < node->childs.len; i++) | |
- print_ical_tree_vnode(node->childs.entry[i].value, level + 1); | |
- print_ical_tree_vnode(node->next, level); | |
+ print_ruler(p->level + 1); | |
+ printf("param %s=%s\n", name, value); | |
+ return 0; | |
} | |
-int | |
-print_ical_tree(FILE *fp) | |
+static int | |
+fn_entry_value(IcalParser *p, char *name, char *value) | |
{ | |
- struct ical_vcalendar vcal; | |
- int e; | |
+ (void)name; | |
- 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, "end\n"); | |
- fflush(stdout); | |
- | |
- ical_free_vcalendar(&vcal); | |
+ print_ruler(p->level + 1); | |
+ printf("value %s\n", value); | |
return 0; | |
} | |
int | |
main(int argc, char **argv) | |
{ | |
- log_arg0 = *argv++; | |
+ IcalParser p = {0}; | |
+ arg0 = *argv++; | |
+ | |
+ p.fn_entry_name = fn_entry_name; | |
+ p.fn_block_begin = fn_block_begin; | |
+ p.fn_param_value = fn_param_value; | |
+ p.fn_entry_value = fn_entry_value; | |
if (*argv == NULL) { | |
- if (print_ical_tree(stdin) < 0) | |
- die("converting stdin"); | |
+ if (ical_parse(&p, stdin) < 0) | |
+ err("parsing stdin:%d %s", p.line, p.errmsg); | |
} | |
for (; *argv != NULL; argv++, argc--) { | |
@@ -80,9 +67,9 @@ main(int argc, char **argv) | |
debug("converting \"%s\"", *argv); | |
if ((fp = fopen(*argv, "r")) == NULL) | |
- die("opening %s", *argv); | |
- if (print_ical_tree(fp) < 0) | |
- die("converting %s", *argv); | |
+ err("opening %s", *argv); | |
+ if (ical_parse(&p, fp) < 0) | |
+ err("parsing %s:%d: %s", *argv, p.line, p.errmsg); | |
fclose(fp); | |
} | |
return 0; | |
diff --git a/log.c b/log.c | |
@@ -1,89 +0,0 @@ | |
-#include "log.h" | |
- | |
-#include <assert.h> | |
-#include <string.h> | |
- | |
-/* | |
- * log.c - log to standard error according to the log level | |
- * | |
- * Instead of logging to syslog, delegate logging to a separate | |
- * tool, such as FreeBSD's daemon(8), POSIX's logger(1). | |
- */ | |
- | |
-#include <errno.h> | |
-#include <stdio.h> | |
-#include <stdlib.h> | |
- | |
-#define LOG_DEFAULT 3 /* info */ | |
- | |
-int log_level = -1; | |
-char *log_arg0 = NULL; | |
- | |
-void | |
-vlogf(int level, char const *flag, char const *fmt, va_list va) | |
-{ | |
- char *env; | |
- int e = errno; | |
- | |
- if (log_level < 0) { | |
- env = getenv("LOG"); | |
- log_level = (env == NULL ? 0 : atoi(env)); | |
- log_level = (log_level > 0 ? log_level : LOG_DEFAULT); | |
- } | |
- | |
- if (log_level < level) | |
- return; | |
- | |
- if (log_arg0 != NULL) | |
- fprintf(stderr, "%s: ", log_arg0); | |
- | |
- fprintf(stderr, "%s: ", flag); | |
- vfprintf(stderr, fmt, va); | |
- | |
- if (e != 0) | |
- fprintf(stderr, ": %s", strerror(e)); | |
- | |
- fprintf(stderr, "\n"); | |
- fflush(stderr); | |
-} | |
- | |
-void | |
-die(char const *fmt, ...) | |
-{ | |
- va_list va; | |
- | |
- va_start(va, fmt); | |
- vlogf(1, "error", fmt, va); | |
- va_end(va); | |
- exit(1); | |
-} | |
- | |
-void | |
-warn(char const *fmt, ...) | |
-{ | |
- va_list va; | |
- | |
- va_start(va, fmt); | |
- vlogf(2, "warn", fmt, va); | |
- va_end(va); | |
-} | |
- | |
-void | |
-info(char const *fmt, ...) | |
-{ | |
- va_list va; | |
- | |
- va_start(va, fmt); | |
- vlogf(3, "info", fmt, va); | |
- va_end(va); | |
-} | |
- | |
-void | |
-debug(char const *fmt, ...) | |
-{ | |
- va_list va; | |
- | |
- va_start(va, fmt); | |
- vlogf(4, "debug", fmt, va); | |
- va_end(va); | |
-} | |
diff --git a/log.h b/log.h | |
@@ -1,15 +0,0 @@ | |
-#ifndef LOG_H | |
-#define LOG_H | |
- | |
-#include <stdarg.h> | |
- | |
-/** src/log.c **/ | |
-extern int log_level; | |
-extern char *log_arg0; | |
-void vlogf(int level, char const *flag, char const *fmt, va_list va); | |
-void die(char const *fmt, ...); | |
-void warn(char const *fmt, ...); | |
-void info(char const *fmt, ...); | |
-void debug(char const *fmt, ...); | |
- | |
-#endif | |
diff --git a/map.c b/map.c | |
@@ -1,106 +0,0 @@ | |
-#include "map.h" | |
- | |
-#include <stdlib.h> | |
-#include <string.h> | |
- | |
-#include "util.h" | |
- | |
-static int | |
-map_cmp(void const *v1, void const *v2) | |
-{ | |
- struct map_entry const *e1 = v1, *e2 = v2; | |
- | |
- return strcmp(e1->key, e2->key); | |
-} | |
- | |
-void * | |
-map_get(struct map *map, char *key) | |
-{ | |
- struct map_entry *entry, k = { .key = key }; | |
- size_t sz; | |
- | |
- sz = sizeof(*map->entry); | |
- if ((entry = bsearch(&k, map->entry, map->len, sz, map_cmp)) == NULL) | |
- return NULL; | |
- return entry->value; | |
-} | |
- | |
-int | |
-map_set(struct map *map, char *key, void *value) | |
-{ | |
- struct map_entry *insert, *e; | |
- size_t i, sz; | |
- void *v; | |
- | |
- for (i = 0; i < map->len; i++) { | |
- int cmp = strcmp(key, map->entry[i].key); | |
- | |
- if (cmp == 0) { | |
- map->entry[i].value = value; | |
- return 0; | |
- } | |
- if (cmp < 0) | |
- break; | |
- } | |
- | |
- sz = sizeof(*map->entry); | |
- if ((v = reallocarray(map->entry, map->len + 1, sz)) == NULL) | |
- return -1; | |
- map->entry = v; | |
- map->len++; | |
- | |
- insert = map->entry + i; | |
- for (e = map->entry + map->len - 2; e >= insert; e--) | |
- e[1] = e[0]; | |
- | |
- insert->key = key; | |
- insert->value = value; | |
- | |
- return 0; | |
-} | |
- | |
-int | |
-map_del(struct map *map, char *key) | |
-{ | |
- size_t i; | |
- | |
- for (i = 0; i < map->len; i++) { | |
- int cmp = strcmp(key, map->entry[i].key); | |
- | |
- if (cmp == 0) | |
- break; | |
- if (cmp < 0) | |
- return -1; | |
- } | |
- if (i == map->len) | |
- return -1; | |
- | |
- map->len--; | |
- for (; i < map->len; i++) | |
- map->entry[i] = map->entry[i + 1]; | |
- return 0; | |
-} | |
- | |
-void | |
-map_init(struct map *map) | |
-{ | |
- memset(map, 0, sizeof(*map)); | |
-} | |
- | |
-void | |
-map_free_keys(struct map *map) | |
-{ | |
- for (size_t i = 0; i < map->len; i++) | |
- free(map->entry[i].key); | |
-} | |
- | |
-void | |
-map_free(struct map *map, void (*fn)(void *)) | |
-{ | |
- if (fn != NULL) { | |
- for (size_t i = 0; i < map->len; i++) | |
- fn(map->entry[i].value); | |
- } | |
- free(map->entry); | |
- map->len = 0; | |
-} | |
diff --git a/map.h b/map.h | |
@@ -1,24 +0,0 @@ | |
-#ifndef MAP_H | |
-#define MAP_H | |
- | |
-#include <stddef.h> | |
- | |
-struct map_entry { | |
- char *key; | |
- void *value; | |
-}; | |
- | |
-struct map { | |
- struct map_entry *entry; | |
- size_t len; | |
-}; | |
- | |
-/** src/map.c **/ | |
-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_keys(struct map *map); | |
-void map_free(struct map *map, void (*fn)(void *)); | |
- | |
-#endif | |
diff --git a/util.c b/util.c | |
@@ -4,6 +4,59 @@ | |
#include <stdint.h> | |
#include <stdlib.h> | |
#include <string.h> | |
+#include <stdio.h> | |
+ | |
+char *arg0; | |
+ | |
+/* logging */ | |
+ | |
+static void | |
+_log(char const *tag, char const *fmt, va_list va) | |
+{ | |
+ if (arg0 != NULL) | |
+ fprintf(stderr, "%s: ", arg0); | |
+ fprintf(stderr, "%s: ", tag); | |
+ vfprintf(stderr, fmt, va); | |
+ if (errno != 0) | |
+ fprintf(stderr, ": %s", strerror(errno)); | |
+ fprintf(stderr, "\n"); | |
+ fflush(stderr); | |
+} | |
+ | |
+void | |
+err(char const *fmt, ...) | |
+{ | |
+ va_list va; | |
+ | |
+ va_start(va, fmt); | |
+ _log("error", fmt, va); | |
+ exit(1); | |
+} | |
+ | |
+void | |
+warn(char const *fmt, ...) | |
+{ | |
+ va_list va; | |
+ | |
+ va_start(va, fmt); | |
+ _log("warning", fmt, va); | |
+} | |
+ | |
+void | |
+debug(char const *fmt, ...) | |
+{ | |
+ static int verbose = -1; | |
+ va_list va; | |
+ | |
+ if (verbose < 0) | |
+ verbose = (getenv("DEBUG") == NULL); | |
+ if (!verbose) | |
+ return; | |
+ va_start(va, fmt); | |
+ _log("debug", fmt, va); | |
+} | |
+ | |
+/* strings */ | |
size_t | |
strlcpy(char *buf, char const *str, size_t sz) | |
@@ -18,21 +71,20 @@ strlcpy(char *buf, char const *str, size_t sz) | |
} | |
char * | |
-strsep(char **str_p, char const *sep) | |
+strsep(char **sp, char const *sep) | |
{ | |
char *s, *prev; | |
- if (*str_p == NULL) | |
+ if (*sp == NULL) | |
return NULL; | |
- | |
- for (s = prev = *str_p; strchr(sep, *s) == NULL; s++) | |
+ prev = *sp; | |
+ for (s = *sp; strchr(sep, *s) == NULL; s++) | |
continue; | |
- | |
if (*s == '\0') { | |
- *str_p = NULL; | |
+ *sp = NULL; | |
} else { | |
+ *sp = s + 1; | |
*s = '\0'; | |
- *str_p = s + 1; | |
} | |
return prev; | |
} | |
@@ -44,28 +96,30 @@ strchomp(char *line) | |
len = strlen(line); | |
if (len > 0 && line[len - 1] == '\n') | |
- line[len-- - 1] = '\0'; | |
+ line[--len] = '\0'; | |
if (len > 0 && line[len - 1] == '\r') | |
- line[len-- - 1] = '\0'; | |
+ line[--len] = '\0'; | |
} | |
int | |
-strappend(char **base_p, char const *s) | |
+strappend(char **dstp, char const *src) | |
{ | |
- size_t base_len, s_len; | |
- void *v; | |
+ size_t dstlen, srclen; | |
+ void *mem; | |
- base_len = (*base_p == NULL) ? (0) : (strlen(*base_p)); | |
- s_len = strlen(s); | |
+ dstlen = (*dstp == NULL) ? 0 : strlen(*dstp); | |
+ srclen = strlen(src); | |
- if ((v = realloc(*base_p, base_len + s_len + 1)) == NULL) | |
+ if ((mem = realloc(*dstp, dstlen + srclen + 1)) == NULL) | |
return -1; | |
+ *dstp = mem; | |
- *base_p = v; | |
- memcpy(*base_p + base_len, s, s_len + 1); | |
+ memcpy(*dstp + dstlen, src, srclen + 1); | |
return 0; | |
} | |
+/* memory */ | |
+ | |
void * | |
reallocarray(void *buf, size_t len, size_t sz) | |
{ | |
diff --git a/util.h b/util.h | |
@@ -2,12 +2,21 @@ | |
#define UTIL_H | |
#include <stddef.h> | |
+#include <stdarg.h> | |
-/** src/util.c **/ | |
-size_t strlcpy(char *buf, char const *str, size_t sz); | |
-char * strsep(char **str_p, char const *sep); | |
-void strchomp(char *line); | |
-int strappend(char **base_p, char const *s); | |
-void * reallocarray(void *buf, size_t len, size_t sz); | |
+/* logging */ | |
+extern char *arg0; | |
+void err(char const *fmt, ...); | |
+void warn(char const *fmt, ...); | |
+void debug(char const *fmt, ...); | |
+ | |
+/* strings */ | |
+size_t strlcpy(char *buf, char const *str, size_t sz); | |
+char *strsep(char **str_p, char const *sep); | |
+void strchomp(char *line); | |
+int strappend(char **base_p, char const *s); | |
+ | |
+/* memory */ | |
+void *reallocarray(void *buf, size_t len, size_t sz); | |
#endif |