URI: 
       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 }