ics2tsv.c - ics2txt - convert icalendar .ics file to plain text | |
git clone git://bitreich.org/ics2txt git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws… | |
Log | |
Files | |
Refs | |
Tags | |
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", "DTE… | |
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)) ==… | |
135 return ical_err(p, strerror(errn… | |
136 } else { | |
137 if (strappend(&block.fields[i], flag_sep… | |
138 strappend(&block.fields[i], value) =… | |
139 return ical_err(p, strerror(errn… | |
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 ti… | |
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.er… | |
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.… | |
219 fclose(fp); | |
220 } | |
221 | |
222 return 0; | |
223 } |