iical.c - ics2txt - convert icalendar .ics file to plain text Err bitreich.org 70 hgit clone git://bitreich.org/ics2txt git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/ics2txt URL:git://bitreich.org/ics2txt git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/ics2txt bitreich.org 70 1Log /scm/ics2txt/log.gph bitreich.org 70 1Files /scm/ics2txt/files.gph bitreich.org 70 1Refs /scm/ics2txt/refs.gph bitreich.org 70 1Tags /scm/ics2txt/tag bitreich.org 70 1README /scm/ics2txt/file/README.md.gph bitreich.org 70 i--- Err bitreich.org 70 iical.c (7209B) Err bitreich.org 70 i--- Err bitreich.org 70 i 1 #include "ical.h" Err bitreich.org 70 i 2 #include Err bitreich.org 70 i 3 #include Err bitreich.org 70 i 4 #include Err bitreich.org 70 i 5 #include Err bitreich.org 70 i 6 #include Err bitreich.org 70 i 7 #include Err bitreich.org 70 i 8 #include Err bitreich.org 70 i 9 #include "util.h" Err bitreich.org 70 i 10 #include "base64.h" Err bitreich.org 70 i 11 Err bitreich.org 70 i 12 char *ical_block_name[ICAL_BLOCK_OTHER + 1] = { Err bitreich.org 70 i 13 [ICAL_BLOCK_VEVENT] = "VEVENT", Err bitreich.org 70 i 14 [ICAL_BLOCK_VTODO] = "VTODO", Err bitreich.org 70 i 15 [ICAL_BLOCK_VJOURNAL] = "VJOURNAL", Err bitreich.org 70 i 16 [ICAL_BLOCK_VFREEBUSY] = "VFREEBUSY", Err bitreich.org 70 i 17 [ICAL_BLOCK_VALARM] = "VALARM", Err bitreich.org 70 i 18 [ICAL_BLOCK_OTHER] = NULL, Err bitreich.org 70 i 19 }; Err bitreich.org 70 i 20 Err bitreich.org 70 i 21 /* valuel helpers: common utilities to call within the p->fn() Err bitreich.org 70 i 22 * callbacks as well as in the code below */ Err bitreich.org 70 i 23 Err bitreich.org 70 i 24 int Err bitreich.org 70 i 25 ical_err(IcalParser *p, char *msg) Err bitreich.org 70 i 26 { Err bitreich.org 70 i 27 p->errmsg = msg; Err bitreich.org 70 i 28 return -1; Err bitreich.org 70 i 29 } Err bitreich.org 70 i 30 Err bitreich.org 70 i 31 int Err bitreich.org 70 i 32 ical_get_level(IcalParser *p) Err bitreich.org 70 i 33 { Err bitreich.org 70 i 34 return p->current - p->stack; Err bitreich.org 70 i 35 } Err bitreich.org 70 i 36 Err bitreich.org 70 i 37 int Err bitreich.org 70 i 38 ical_get_value(IcalParser *p, char *s, size_t *len) Err bitreich.org 70 i 39 { Err bitreich.org 70 i 40 *len = strlen(s); Err bitreich.org 70 i 41 if (p->base64) Err bitreich.org 70 i 42 if (base64_decode(s, len, s, len) < 0) Err bitreich.org 70 i 43 return ical_err(p, "invalid base64 data"); Err bitreich.org 70 i 44 return 0; Err bitreich.org 70 i 45 } Err bitreich.org 70 i 46 Err bitreich.org 70 i 47 int Err bitreich.org 70 i 48 ical_get_time(IcalParser *p, char *s, time_t *t) Err bitreich.org 70 i 49 { Err bitreich.org 70 i 50 struct tm tm = {0}; Err bitreich.org 70 i 51 char const *tzid; Err bitreich.org 70 i 52 Err bitreich.org 70 i 53 tzid = (p->tzid) ? p->tzid : Err bitreich.org 70 i 54 (p->current && p->current->tzid[0] != '\0') ? p->current->tzid : Err bitreich.org 70 i 55 ""; Err bitreich.org 70 i 56 Err bitreich.org 70 i 57 #define N(i, x) ((s[i] - '0') * x) Err bitreich.org 70 i 58 Err bitreich.org 70 i 59 /* date */ Err bitreich.org 70 i 60 for (int i = 0; i < 8; i++) Err bitreich.org 70 i 61 if (!isdigit(s[i])) Err bitreich.org 70 i 62 return ical_err(p, "invalid date format"); Err bitreich.org 70 i 63 tm.tm_year = N(0,1000) + N(1,100) + N(2,10) + N(3,1) - 1900; Err bitreich.org 70 i 64 tm.tm_mon = N(4,10) + N(5,1) - 1; Err bitreich.org 70 i 65 tm.tm_mday = N(6,10) + N(7,1); Err bitreich.org 70 i 66 s += 8; Err bitreich.org 70 i 67 Err bitreich.org 70 i 68 if (*s == 'T') { Err bitreich.org 70 i 69 /* time */ Err bitreich.org 70 i 70 s++; Err bitreich.org 70 i 71 for (int i = 0; i < 6; i++) Err bitreich.org 70 i 72 if (!isdigit(s[i])) Err bitreich.org 70 i 73 return ical_err(p, "invalid time format"); Err bitreich.org 70 i 74 tm.tm_hour = N(0,10) + N(1,1); Err bitreich.org 70 i 75 tm.tm_min = N(2,10) + N(3,1); Err bitreich.org 70 i 76 tm.tm_sec = N(4,10) + N(5,1); Err bitreich.org 70 i 77 if (s[6] == 'Z') Err bitreich.org 70 i 78 tzid = "UTC"; Err bitreich.org 70 i 79 } Err bitreich.org 70 i 80 Err bitreich.org 70 i 81 #undef N Err bitreich.org 70 i 82 Err bitreich.org 70 i 83 if ((*t = tztime(&tm, tzid)) == (time_t)-1) Err bitreich.org 70 i 84 return ical_err(p, "could not convert time"); Err bitreich.org 70 i 85 Err bitreich.org 70 i 86 return 0; Err bitreich.org 70 i 87 } Err bitreich.org 70 i 88 Err bitreich.org 70 i 89 /* hooks: called just before user functions to do extra work such as Err bitreich.org 70 i 90 * processing time zones definition or prepare base64 decoding, and Err bitreich.org 70 i 91 * permit to only have parsing code left to parsing functions */ Err bitreich.org 70 i 92 Err bitreich.org 70 i 93 static int Err bitreich.org 70 i 94 hook_field_name(IcalParser *p, char *name) Err bitreich.org 70 i 95 { Err bitreich.org 70 i 96 (void)p; (void)name; Err bitreich.org 70 i 97 return 0; Err bitreich.org 70 i 98 } Err bitreich.org 70 i 99 Err bitreich.org 70 i 100 static int Err bitreich.org 70 i 101 hook_param_name(IcalParser *p, char *name) Err bitreich.org 70 i 102 { Err bitreich.org 70 i 103 (void)p; (void)name; Err bitreich.org 70 i 104 return 0; Err bitreich.org 70 i 105 } Err bitreich.org 70 i 106 Err bitreich.org 70 i 107 static int Err bitreich.org 70 i 108 hook_param_value(IcalParser *p, char *name, char *value) Err bitreich.org 70 i 109 { Err bitreich.org 70 i 110 if (strcasecmp(name, "ENCODING") == 0) Err bitreich.org 70 i 111 p->base64 = (strcasecmp(value, "BASE64") == 0); Err bitreich.org 70 i 112 Err bitreich.org 70 i 113 if (strcasecmp(name, "TZID") == 0) Err bitreich.org 70 i 114 p->tzid = value; Err bitreich.org 70 i 115 Err bitreich.org 70 i 116 return 0; Err bitreich.org 70 i 117 } Err bitreich.org 70 i 118 Err bitreich.org 70 i 119 static int Err bitreich.org 70 i 120 hook_field_value(IcalParser *p, char *name, char *value) Err bitreich.org 70 i 121 { Err bitreich.org 70 i 122 if (strcasecmp(name, "TZID") == 0) Err bitreich.org 70 i 123 if (strlcpy(p->current->tzid, value, sizeof p->current->tzid) >= Err bitreich.org 70 i 124 sizeof p->current->tzid) Err bitreich.org 70 i 125 return ical_err(p, "TZID: name too large"); Err bitreich.org 70 i 126 Err bitreich.org 70 i 127 p->tzid = NULL; Err bitreich.org 70 i 128 Err bitreich.org 70 i 129 return 0; Err bitreich.org 70 i 130 } Err bitreich.org 70 i 131 Err bitreich.org 70 i 132 static int Err bitreich.org 70 i 133 hook_block_begin(IcalParser *p, char *name) Err bitreich.org 70 i 134 { Err bitreich.org 70 i 135 p->current++; Err bitreich.org 70 i 136 memset(p->current, 0, sizeof(*p->current)); Err bitreich.org 70 i 137 if (ical_get_level(p) >= ICAL_STACK_SIZE) Err bitreich.org 70 i 138 return ical_err(p, "max recurion reached"); Err bitreich.org 70 i 139 if (strlcpy(p->current->name, name, sizeof p->current->name) >= Err bitreich.org 70 i 140 sizeof p->current->name) Err bitreich.org 70 i 141 return ical_err(p, "value too large"); Err bitreich.org 70 i 142 Err bitreich.org 70 i 143 for (int i = 0; ical_block_name[i] != NULL; i++) { Err bitreich.org 70 i 144 if (strcasecmp(ical_block_name[i], name) == 0) { Err bitreich.org 70 i 145 if (p->blocktype != ICAL_BLOCK_OTHER) Err bitreich.org 70 i 146 return ical_err(p, "BEGIN:V* in BEGIN:V*"); Err bitreich.org 70 i 147 p->blocktype = i; Err bitreich.org 70 i 148 } Err bitreich.org 70 i 149 } Err bitreich.org 70 i 150 Err bitreich.org 70 i 151 return 0; Err bitreich.org 70 i 152 } Err bitreich.org 70 i 153 Err bitreich.org 70 i 154 static int Err bitreich.org 70 i 155 hook_block_end_before(IcalParser *p, char *name) Err bitreich.org 70 i 156 { Err bitreich.org 70 i 157 if (p->current == p->stack) Err bitreich.org 70 i 158 return ical_err(p, "more END: than BEGIN:"); Err bitreich.org 70 i 159 if (strcasecmp(p->current->name, name) != 0) Err bitreich.org 70 i 160 return ical_err(p, "mismatching BEGIN: and END:"); Err bitreich.org 70 i 161 if (p->current <= p->stack) Err bitreich.org 70 i 162 return ical_err(p, "more END: than BEGIN:"); Err bitreich.org 70 i 163 return 0; Err bitreich.org 70 i 164 } Err bitreich.org 70 i 165 Err bitreich.org 70 i 166 static int Err bitreich.org 70 i 167 hook_block_end_after(IcalParser *p, char *name) Err bitreich.org 70 i 168 { Err bitreich.org 70 i 169 p->current--; Err bitreich.org 70 i 170 if (ical_block_name[p->blocktype] != NULL && Err bitreich.org 70 i 171 strcasecmp(ical_block_name[p->blocktype], name) == 0) Err bitreich.org 70 i 172 p->blocktype = ICAL_BLOCK_OTHER; Err bitreich.org 70 i 173 return 0; Err bitreich.org 70 i 174 } Err bitreich.org 70 i 175 Err bitreich.org 70 i 176 /* parsers: in charge of reading from `fp`, splitting text into Err bitreich.org 70 i 177 * fields, and call hooks and user functions. */ Err bitreich.org 70 i 178 Err bitreich.org 70 i 179 #define CALL(p, fn, ...) ((p)->fn ? (p)->fn((p), __VA_ARGS__) : 0) Err bitreich.org 70 i 180 Err bitreich.org 70 i 181 static int Err bitreich.org 70 i 182 ical_parse_value(IcalParser *p, char **sp, char *name) Err bitreich.org 70 i 183 { Err bitreich.org 70 i 184 int err; Err bitreich.org 70 i 185 char *s, c, *val; Err bitreich.org 70 i 186 Err bitreich.org 70 i 187 s = *sp; Err bitreich.org 70 i 188 if (*s == '"') { Err bitreich.org 70 i 189 val = ++s; Err bitreich.org 70 i 190 while (!iscntrl(*s) && *s != '"') Err bitreich.org 70 i 191 s++; Err bitreich.org 70 i 192 if (*s != '"') Err bitreich.org 70 i 193 return ical_err(p, "missing '\"'"); Err bitreich.org 70 i 194 *s++ = '\0'; Err bitreich.org 70 i 195 } else { Err bitreich.org 70 i 196 val = s; Err bitreich.org 70 i 197 while (!iscntrl(*s) && !strchr(",;:'\"", *s)) Err bitreich.org 70 i 198 s++; Err bitreich.org 70 i 199 } Err bitreich.org 70 i 200 c = *s, *s = '\0'; Err bitreich.org 70 i 201 if ((err = hook_param_value(p, name, val)) != 0 || Err bitreich.org 70 i 202 (err = CALL(p, fn_param_value, name, val)) != 0) Err bitreich.org 70 i 203 return err; Err bitreich.org 70 i 204 *s = c; Err bitreich.org 70 i 205 *sp = s; Err bitreich.org 70 i 206 return 0; Err bitreich.org 70 i 207 } Err bitreich.org 70 i 208 Err bitreich.org 70 i 209 static int Err bitreich.org 70 i 210 ical_parse_param(IcalParser *p, char **sp) Err bitreich.org 70 i 211 { Err bitreich.org 70 i 212 int err; Err bitreich.org 70 i 213 char *s, *name; Err bitreich.org 70 i 214 Err bitreich.org 70 i 215 s = *sp; Err bitreich.org 70 i 216 do { Err bitreich.org 70 i 217 for (name = s; isalnum(*s) || *s == '-'; s++); Err bitreich.org 70 i 218 if (s == name || (*s != '=')) Err bitreich.org 70 i 219 return ical_err(p, "invalid parameter name"); Err bitreich.org 70 i 220 *s++ = '\0'; Err bitreich.org 70 i 221 if ((err = hook_param_name(p, name)) != 0 || Err bitreich.org 70 i 222 (err = CALL(p, fn_param_name, name)) != 0) Err bitreich.org 70 i 223 return err; Err bitreich.org 70 i 224 do { Err bitreich.org 70 i 225 if ((err = ical_parse_value(p, &s, name)) != 0) Err bitreich.org 70 i 226 return err; Err bitreich.org 70 i 227 } while (*s == ',' && s++); Err bitreich.org 70 i 228 } while (*s == ';' && s++); Err bitreich.org 70 i 229 *sp = s; Err bitreich.org 70 i 230 return 0; Err bitreich.org 70 i 231 } Err bitreich.org 70 i 232 Err bitreich.org 70 i 233 static int Err bitreich.org 70 i 234 ical_parse_contentline(IcalParser *p, char *s) Err bitreich.org 70 i 235 { Err bitreich.org 70 i 236 int err; Err bitreich.org 70 i 237 char c, *name, *sep; Err bitreich.org 70 i 238 Err bitreich.org 70 i 239 if (*s == '\0') Err bitreich.org 70 i 240 return 0; Err bitreich.org 70 i 241 Err bitreich.org 70 i 242 for (name = s; isalnum(*s) || *s == '-'; s++); Err bitreich.org 70 i 243 if (s == name || (*s != ';' && *s != ':')) Err bitreich.org 70 i 244 return ical_err(p, "invalid property name"); Err bitreich.org 70 i 245 c = *s, *s = '\0'; Err bitreich.org 70 i 246 if (strcasecmp(name, "BEGIN") != 0 && strcasecmp(name, "END") != 0) Err bitreich.org 70 i 247 if ((err = hook_field_name(p, name)) != 0 || Err bitreich.org 70 i 248 (err = CALL(p, fn_field_name, name)) != 0) Err bitreich.org 70 i 249 return err; Err bitreich.org 70 i 250 *s = c; Err bitreich.org 70 i 251 sep = s; Err bitreich.org 70 i 252 Err bitreich.org 70 i 253 p->base64 = 0; Err bitreich.org 70 i 254 while (*s == ';') { Err bitreich.org 70 i 255 s++; Err bitreich.org 70 i 256 if ((err = ical_parse_param(p, &s)) != 0) Err bitreich.org 70 i 257 return err; Err bitreich.org 70 i 258 } Err bitreich.org 70 i 259 Err bitreich.org 70 i 260 if (*s != ':') Err bitreich.org 70 i 261 return ical_err(p, "expected ':' delimiter"); Err bitreich.org 70 i 262 s++; Err bitreich.org 70 i 263 Err bitreich.org 70 i 264 *sep = '\0'; Err bitreich.org 70 i 265 if (strcasecmp(name, "BEGIN") == 0) { Err bitreich.org 70 i 266 if ((err = hook_block_begin(p, s)) != 0 || Err bitreich.org 70 i 267 (err = CALL(p, fn_block_begin, s)) != 0) Err bitreich.org 70 i 268 return err; Err bitreich.org 70 i 269 } else if (strcasecmp(name, "END") == 0) { Err bitreich.org 70 i 270 if ((err = hook_block_end_before(p, s)) != 0 || Err bitreich.org 70 i 271 (err = CALL(p, fn_block_end, s)) != 0 || Err bitreich.org 70 i 272 (err = hook_block_end_after(p, s)) != 0) Err bitreich.org 70 i 273 return err; Err bitreich.org 70 i 274 } else { Err bitreich.org 70 i 275 if ((err = hook_field_value(p, name, s)) != 0 || Err bitreich.org 70 i 276 (err = CALL(p, fn_field_value, name, s)) != 0) Err bitreich.org 70 i 277 return err; Err bitreich.org 70 i 278 } Err bitreich.org 70 i 279 return 0; Err bitreich.org 70 i 280 } Err bitreich.org 70 i 281 Err bitreich.org 70 i 282 static ssize_t Err bitreich.org 70 i 283 ical_getline(char **contentline, char **line, size_t *sz, FILE *fp) Err bitreich.org 70 i 284 { Err bitreich.org 70 i 285 size_t num = 0; Err bitreich.org 70 i 286 int c; Err bitreich.org 70 i 287 Err bitreich.org 70 i 288 if ((*contentline = realloc(*contentline, 1)) == NULL) Err bitreich.org 70 i 289 return -1; Err bitreich.org 70 i 290 **contentline = '\0'; Err bitreich.org 70 i 291 Err bitreich.org 70 i 292 do { Err bitreich.org 70 i 293 if (getline(line, sz, fp) <= 0) Err bitreich.org 70 i 294 goto end; Err bitreich.org 70 i 295 num++; Err bitreich.org 70 i 296 strchomp(*line); Err bitreich.org 70 i 297 Err bitreich.org 70 i 298 if (strappend(contentline, *line) == NULL) Err bitreich.org 70 i 299 return -1; Err bitreich.org 70 i 300 if ((c = fgetc(fp)) == EOF) Err bitreich.org 70 i 301 goto end; Err bitreich.org 70 i 302 } while (c == ' '); Err bitreich.org 70 i 303 ungetc(c, fp); Err bitreich.org 70 i 304 assert(!ferror(fp)); Err bitreich.org 70 i 305 end: Err bitreich.org 70 i 306 return ferror(fp) ? -1 : num; Err bitreich.org 70 i 307 } Err bitreich.org 70 i 308 Err bitreich.org 70 i 309 int Err bitreich.org 70 i 310 ical_parse(IcalParser *p, FILE *fp) Err bitreich.org 70 i 311 { Err bitreich.org 70 i 312 char *line = NULL, *contentline = NULL; Err bitreich.org 70 i 313 size_t sz = 0; Err bitreich.org 70 i 314 ssize_t l; Err bitreich.org 70 i 315 int err; Err bitreich.org 70 i 316 Err bitreich.org 70 i 317 p->current = p->stack; Err bitreich.org 70 i 318 p->linenum = 0; Err bitreich.org 70 i 319 p->blocktype = ICAL_BLOCK_OTHER; Err bitreich.org 70 i 320 Err bitreich.org 70 i 321 do { Err bitreich.org 70 i 322 if ((l = ical_getline(&contentline, &line, &sz, fp)) < 0) { Err bitreich.org 70 i 323 err = ical_err(p, "readling line"); Err bitreich.org 70 i 324 break; Err bitreich.org 70 i 325 } Err bitreich.org 70 i 326 p->linenum += l; Err bitreich.org 70 i 327 } while (l > 0 && (err = ical_parse_contentline(p, contentline)) == 0); Err bitreich.org 70 i 328 Err bitreich.org 70 i 329 free(contentline); Err bitreich.org 70 i 330 Err bitreich.org 70 i 331 if (err == 0 && p->current != p->stack) Err bitreich.org 70 i 332 return ical_err(p, "more BEGIN: than END:"); Err bitreich.org 70 i 333 Err bitreich.org 70 i 334 return err; Err bitreich.org 70 i 335 } Err bitreich.org 70 .