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 }