|
|
ics2tsv.c - ics2txt - convert icalendar .ics file to plain text |
|
|
 |
git clone git://bitreich.org/ics2txt git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/ics2txt (git://bitreich.org) |
|
|
 |
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", "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 } |
|