URI: 
       ics2tsv.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
       ---
       ics2tsv.c (4611B)
       ---
            1 #include <errno.h>
            2 #include <stdio.h>
            3 #include <stdlib.h>
            4 #include <string.h>
            5 #include <strings.h>
            6 #include <time.h>
            7 #include <unistd.h>
            8 #include "ical.h"
            9 #include "util.h"
           10 
           11 #ifndef __OpenBSD__
           12 #define pledge(...) 0
           13 #endif
           14 
           15 #define FIELDS_MAX 128
           16 
           17 typedef struct Field Field;
           18 typedef struct Block Block;
           19 
           20 struct Field {
           21         char *key;
           22         char *value;
           23 };
           24 
           25 struct Block {
           26         time_t beg, end;
           27         char *fields[FIELDS_MAX];
           28 };
           29 
           30 static int flag_header = 1;
           31 static char default_fields[] = "SUMMARY,DESCRIPTION,CATEGORIES,LOCATION";
           32 static char *flag_sep = ",";
           33 static char *flag_timefmt = NULL;
           34 static char *flag_fields = default_fields;
           35 static char *fields[FIELDS_MAX];
           36 static Block block;
           37 
           38 static int
           39 fn_field_name(IcalParser *p, char *name)
           40 {
           41         (void)p;
           42         (void)name;
           43 
           44         return 0;
           45 }
           46 
           47 static int
           48 fn_block_begin(IcalParser *p, char *name)
           49 {
           50         (void)p;
           51         (void)name;
           52 
           53         if (p->blocktype == ICAL_BLOCK_OTHER)
           54                 return 0;
           55 
           56         memset(&block, 0, sizeof block);
           57         return 0;
           58 }
           59 
           60 static int
           61 fn_block_end(IcalParser *p, char *name)
           62 {
           63         (void)name;
           64 
           65         if (p->blocktype == ICAL_BLOCK_OTHER)
           66                 return 0;
           67         fputs(p->current->name, stdout);
           68 
           69         /* printing dates with %s is much much slower than %lld */
           70         if (flag_timefmt == NULL) {
           71                 printf("\t%lld\t%lld", block.beg, block.end);
           72         } else {
           73                 char buf[128];
           74                 struct tm tm = {0};
           75 
           76                 localtime_r(&block.beg, &tm);
           77                 strftime(buf, sizeof buf, flag_timefmt, &tm);
           78                 printf("\t%s", buf);
           79 
           80                 localtime_r(&block.end, &tm);
           81                 strftime(buf, sizeof buf, flag_timefmt, &tm);
           82                 printf("\t%s", buf);
           83         }
           84 
           85         /* reserved for recurring events */
           86         printf("\t%s", "(null)");
           87 
           88         for (int i = 0; fields[i] != NULL; i++) {
           89                 fputc('\t', stdout);
           90                 if (block.fields[i] != NULL)
           91                         fputs(block.fields[i], stdout);
           92         }
           93         printf("\n");
           94         return 0;
           95 }
           96 
           97 static int
           98 fn_param_value(IcalParser *p, char *name, char *value)
           99 {
          100         (void)p;
          101         (void)name;
          102         (void)value;
          103 
          104         return 0;
          105 }
          106 
          107 static int
          108 fn_field_value(IcalParser *p, char *name, char *value)
          109 {
          110         static char *map[][2] = {
          111                 [ICAL_BLOCK_VEVENT]        = { "DTSTART",        "DTEND" },
          112                 [ICAL_BLOCK_VTODO]        = { NULL,        "DUE" },
          113                 [ICAL_BLOCK_VJOURNAL]        = { "DTSTAMP",        NULL },
          114                 [ICAL_BLOCK_VFREEBUSY]        = { "DTSTART",        "DTEND" },
          115                 [ICAL_BLOCK_VALARM]        = { "DTSTART",        NULL },
          116                 [ICAL_BLOCK_OTHER]        = { NULL,        NULL },
          117         };
          118         char *beg, *end;
          119 
          120         /* fill the date fields */
          121         beg = map[p->blocktype][0];
          122         if (beg != NULL && strcasecmp(name, beg) == 0)
          123                 if (ical_get_time(p, value, &block.beg) != 0)
          124                         return -1;
          125         end = map[p->blocktype][1];
          126         if (end != NULL && strcasecmp(name, end) == 0)
          127                 if (ical_get_time(p, value, &block.end) != 0)
          128                         return -1;
          129 
          130         /* fill text fields as requested with -o F1,F2... */
          131         for (int i = 0; fields[i] != NULL; i++) {
          132                 if (strcasecmp(name, fields[i]) == 0) {
          133                         if (block.fields[i] == NULL) {
          134                                 if ((block.fields[i] = strdup(value)) == NULL)
          135                                         return ical_err(p, strerror(errno));
          136                         } else {
          137                                 if (strappend(&block.fields[i], flag_sep) == NULL ||
          138                                     strappend(&block.fields[i], value) == NULL)
          139                                         return ical_err(p, strerror(errno));
          140                         }
          141                 }
          142         }
          143 
          144         return 0;
          145 }
          146 
          147 static void
          148 usage(void)
          149 {
          150         fprintf(stderr,"usage: %s [-1] [-f fields] [-s separator] [-t timefmt]"
          151             " [file...]\n", arg0);
          152         exit(1);
          153 }
          154 
          155 int
          156 main(int argc, char **argv)
          157 {
          158         IcalParser p = {0};
          159         int c;
          160 
          161         arg0 = *argv;
          162 
          163         if (pledge("stdio rpath", "") < 0)
          164                 err(1, "pledge: %s", strerror(errno));
          165 
          166         p.fn_field_name = fn_field_name;
          167         p.fn_block_begin = fn_block_begin;
          168         p.fn_block_end = fn_block_end;
          169         p.fn_param_value = fn_param_value;
          170         p.fn_field_value = fn_field_value;
          171 
          172         while ((c = getopt(argc, argv, "01f:s:t:")) != -1) {
          173                 switch (c) {
          174                 case '0':
          175                         flag_header = 0;
          176                         break;
          177                 case '1':
          178                         flag_header = 1;
          179                         break;
          180                 case 'f':
          181                         flag_fields = optarg;
          182                         break;
          183                 case 's':
          184                         flag_sep = optarg;
          185                         break;
          186                 case 't':
          187                         flag_timefmt = optarg;
          188                         break;
          189                 case '?':
          190                         usage();
          191                         break;
          192                 }
          193         }
          194         argv += optind;
          195         argc -= optind;
          196 
          197         if (strsplit(flag_fields, fields, LEN(fields), ",") < 0)
          198                 err(1, "too many fields specified with -f flag");
          199 
          200         if (flag_header) {
          201                 printf("%s\t%s\t%s\t%s", "TYPE", "START", "END", "RECUR");
          202                 for (size_t i = 0; fields[i] != NULL; i++)
          203                         printf("\t%s", fields[i]);
          204                 fputc('\n', stdout);
          205         }
          206 
          207         if (*argv == NULL || strcmp(*argv, "-") == 0) {
          208                 debug("converting *stdin*");
          209                 if (ical_parse(&p, stdin) < 0)
          210                         err(1, "parsing *stdin*:%d: %s", p.linenum, p.errmsg);
          211         }
          212         for (; *argv != NULL; argv++, argc--) {
          213                 FILE *fp;
          214                 debug("converting \"%s\"", *argv);
          215                 if ((fp = fopen(*argv, "r")) == NULL)
          216                         err(1, "opening %s: %s", *argv, strerror(errno));
          217                 if (ical_parse(&p, fp) < 0)
          218                         err(1, "parsing %s:%d: %s", *argv, p.linenum, p.errmsg);
          219                 fclose(fp);
          220         }
          221 
          222         return 0;
          223 }