tical.c - ics2txt - convert icalendar .ics file to plain text HTML git clone git://bitreich.org/ics2txt git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/ics2txt DIR Log DIR Files DIR Refs DIR Tags DIR README --- tical.c (5753B) --- 1 #include "ical.h" 2 3 #include <assert.h> 4 #include <errno.h> 5 #include <stdio.h> 6 #include <stdlib.h> 7 #include <string.h> 8 #include <strings.h> /* strcase* */ 9 10 #include "util.h" 11 12 enum ical_err ical_errno; 13 14 int 15 ical_getline(char **line, char **ln, size_t *sz, FILE *fp) 16 { 17 int c; 18 void *v; 19 20 if ((v = realloc(*line, 1)) == NULL) 21 return -ICAL_ERR_SYSTEM; 22 *line = v; 23 (*line)[0] = '\0'; 24 25 do { top: 26 if (getline(ln, sz, fp) <= 0) 27 return ferror(fp) ? -ICAL_ERR_SYSTEM : 0; 28 strchomp(*ln); 29 if (**ln == '\0') 30 goto top; 31 if (strappend(line, *ln) < 0) 32 return -ICAL_ERR_SYSTEM; 33 if ((c = fgetc(fp)) == EOF) 34 return ferror(fp) ? -ICAL_ERR_SYSTEM : 1; 35 } while (c == ' '); 36 37 ungetc(c, fp); 38 assert(!ferror(fp)); 39 return 1; 40 } 41 42 char * 43 ical_strerror(int i) 44 { 45 enum ical_err err = (i > 0) ? i : -i; 46 47 switch (err) { 48 case ICAL_ERR_OK: 49 return "no error"; 50 case ICAL_ERR_SYSTEM: 51 return "system error"; 52 case ICAL_ERR_END_MISMATCH: 53 return "END: does not match its corresponding BEGIN:"; 54 case ICAL_ERR_MISSING_BEGIN: 55 return "unexpected content line before any BEGIN:"; 56 case ICAL_ERR_MISSING_COLUMN: 57 return "missing ':' character from line"; 58 case ICAL_ERR_MISSING_SEMICOLUMN: 59 return "missing ';' character before ':'"; 60 case ICAL_ERR_MISSING_EQUAL: 61 return "missing '=' character in parameter before ':'"; 62 case ICAL_ERR_MIN_NESTED: 63 return "too many END: for the number of BEGIN:"; 64 case ICAL_ERR_MAX_NESTED: 65 return "maximum nesting level reached"; 66 case ICAL_ERR_LENGTH: 67 assert(!"used internally, should not happen"); 68 } 69 assert(!"unknown error code"); 70 return "not a valid ical error code"; 71 } 72 73 struct ical_value * 74 ical_new_value(char const *line) 75 { 76 struct ical_value *new; 77 size_t len; 78 79 len = strlen(line); 80 if ((new = calloc(1, sizeof *new + len + 1)) == NULL) 81 return NULL; 82 memcpy(new->buf, line, len + 1); 83 return new; 84 } 85 86 void 87 ical_free_value(struct ical_value *value) 88 { 89 map_free(&value->param, NULL); 90 free(value); 91 } 92 93 int 94 ical_parse_value(struct ical_value *value) 95 { 96 char *column, *equal, *param, *cp; 97 int e = errno; 98 99 value->name = value->buf; 100 101 if ((column = strchr(value->buf, ':')) == NULL) 102 return -ICAL_ERR_MISSING_COLUMN; 103 *column = '\0'; 104 value->value = column + 1; 105 106 if ((cp = strchr(value->buf, ';')) != NULL) 107 *cp++ = '\0'; 108 while ((param = strsep(&cp, ";")) != NULL) { 109 if ((equal = strchr(param, '=')) == NULL) 110 return -ICAL_ERR_MISSING_EQUAL; 111 *equal = '\0'; 112 if (map_set(&value->param, param, equal + 1) < 0) 113 return -ICAL_ERR_SYSTEM; 114 } 115 116 assert(errno == e); 117 return 0; 118 } 119 120 struct ical_vnode * 121 ical_new_vnode(char const *name) 122 { 123 struct ical_vnode *new; 124 size_t sz; 125 126 if ((new = calloc(1, sizeof *new)) == NULL) 127 return NULL; 128 sz = sizeof new->name; 129 if (strlcpy(new->name, name, sz) >= sz) { 130 errno = EMSGSIZE; 131 goto err; 132 } 133 return new; 134 err: 135 ical_free_vnode(new); 136 return NULL; 137 } 138 139 static void 140 ical_free_value_void(void *v) 141 { 142 ical_free_value(v); 143 } 144 145 static void 146 ical_free_vnode_void(void *v) 147 { 148 ical_free_vnode(v); 149 } 150 151 void 152 ical_free_vnode(struct ical_vnode *node) 153 { 154 if (node == NULL) 155 return; 156 map_free(&node->values, ical_free_value_void); 157 map_free(&node->childs, ical_free_vnode_void); 158 ical_free_vnode(node->next); 159 free(node); 160 } 161 162 int 163 ical_push_nested(struct ical_vcalendar *vcal, struct ical_vnode *new) 164 { 165 struct ical_vnode **node; 166 167 node = vcal->nested; 168 for (int i = 0; *node != NULL; node++, i++) { 169 if (i >= ICAL_NESTED_MAX) 170 return -ICAL_ERR_MAX_NESTED; 171 } 172 node[0] = new; 173 node[1] = NULL; 174 return 0; 175 } 176 177 struct ical_vnode * 178 ical_pop_nested(struct ical_vcalendar *vcal) 179 { 180 struct ical_vnode **node, **prev = vcal->nested, *old; 181 182 for (prev = node = vcal->nested; *node != NULL; node++) { 183 vcal->current = *prev; 184 prev = node; 185 old = *node; 186 } 187 *prev = NULL; 188 if (vcal->nested[0] == NULL) 189 vcal->current = NULL; 190 return old; 191 } 192 193 int 194 ical_begin_vnode(struct ical_vcalendar *vcal, char const *name) 195 { 196 struct ical_vnode *new; 197 int e; 198 199 if ((new = ical_new_vnode(name)) == NULL) 200 return -ICAL_ERR_SYSTEM; 201 if ((e = ical_push_nested(vcal, new)) < 0) 202 goto err; 203 if (vcal->root == NULL) { 204 vcal->root = new; 205 } else { 206 new->next = map_get(&vcal->current->childs, new->name); 207 if (map_set(&vcal->current->childs, new->name, new) < 0) { 208 e = -ICAL_ERR_SYSTEM; 209 goto err; 210 } 211 } 212 vcal->current = new; 213 return 0; 214 err: 215 ical_free_vnode(new); 216 return e; 217 } 218 219 int 220 ical_end_vnode(struct ical_vcalendar *vcal, char const *name) 221 { 222 struct ical_vnode *old; 223 224 if ((old = ical_pop_nested(vcal)) == NULL) 225 return -ICAL_ERR_MIN_NESTED; 226 if (strcasecmp(name, old->name) != 0) 227 return -ICAL_ERR_END_MISMATCH; 228 return 0; 229 } 230 231 int 232 ical_push_value(struct ical_vcalendar *vcal, struct ical_value *new) 233 { 234 if (strcasecmp(new->name, "BEGIN") == 0) { 235 int e = ical_begin_vnode(vcal, new->value); 236 ical_free_value(new); 237 return e; 238 } 239 if (strcasecmp(new->name, "END") == 0) { 240 int e = ical_end_vnode(vcal, new->value); 241 ical_free_value(new); 242 return e; 243 } 244 245 if (vcal->current == NULL) 246 return -ICAL_ERR_MISSING_BEGIN; 247 248 new->next = map_get(&vcal->current->values, new->name); 249 if (map_set(&vcal->current->values, new->name, new) < 0) 250 return -ICAL_ERR_SYSTEM; 251 252 return 0; 253 } 254 255 int 256 ical_read_vcalendar(struct ical_vcalendar *vcal, FILE *fp) 257 { 258 char *line = NULL, *ln = NULL; 259 size_t sz = 0; 260 ssize_t r; 261 int e; 262 263 memset(vcal, 0, sizeof *vcal); 264 265 while ((r = ical_getline(&line, &ln, &sz, fp)) > 0) { 266 struct ical_value *new; 267 268 if ((new = ical_new_value(line)) == NULL) { 269 e = -ICAL_ERR_SYSTEM; 270 goto err; 271 } 272 if ((e = ical_parse_value(new)) < 0) 273 goto err; 274 if ((e = ical_push_value(vcal, new)) < 0) 275 goto err; 276 } 277 e = (r == 0) ? 0 : -ICAL_ERR_SYSTEM; 278 err: 279 free(line); 280 free(ln); 281 return e; 282 } 283 284 void 285 ical_free_vcalendar(struct ical_vcalendar *vcal) 286 { 287 ical_free_vnode(vcal->root); 288 }