1 /*
2 * icalfilter -- filter an iCalendar file
3 *
4 * Author: Bert Bos <bert@w3.org>
5 * Created: 30 Sep 2002
6 */
7
8 #include "config.h"
9 #include <unistd.h>
10 #include <stdio.h>
11 #include <errno.h>
12 #include <stdio.h>
13 #include <string.h>
14 #include <stdlib.h>
15 #include <stdarg.h>
16 #include <getopt.h>
17 #include <ctype.h>
18 /*
19 #include <icaltime.h>
20 #include <icalcomponent.h>
21 #include <icalerror.h>
22 #include <icaltimezone.h>
23 */
24 #include <libical/ical.h>
25 #include <libical/icalss.h>
26 /*
27 #include <icalset.h>
28 #include <icalfileset.h>
29 */
30
31 #define PRODID "-//W3C//NONSGML icalfilter 0.1//EN"
32
33 #define ERR_OUT_OF_MEM 1 /* Program exit codes */
34 #define ERR_USAGE 2
35 #define ERR_DATE 3
36 #define ERR_PARSE 4
37 #define ERR_FILEIO 5
38 #define ERR_ICAL_ERR 6 /* Other error */
39
40 #define USAGE "Usage: ical2html [options] input output\n\
41 -p, --class=CLASS only (PUBLIC, CONFIDENTIAL, PRIVATE, NONE)\n\
42 -P, --not-class=CLASS exclude (PUBLIC, CONFIDENTIAL, PRIVATE, NONE)\n\
43 -c, --category=CATEGORY only events of this category\n\
44 -C, --not-category=CATEGORY exclude events of this category\n\
45 input and output are iCalendar files\n"
46
47 /* Long command line options */
48 static struct option options[] = {
49 {"class", 1, 0, 'p'},
50 {"not-class", 1, 0, 'P'},
51 {"category", 1, 0, 'c'},
52 {"not-category", 1, 0, 'C'},
53 {0, 0, 0, 0}
54 };
55
56 #define OPTIONS "p:P:c:C:"
57
58 /* Structure for storing applicable events */
59 typedef struct _event_item {
60 struct icaltimetype start;
61 struct icaltimetype end;
62 icalcomponent *event;
63 } event_item;
64
65
66
67 /* fatal -- print error message and exit with errcode */
fatal(int errcode,const char * message,...)68 static void fatal(int errcode, const char *message,...)
69 {
70 va_list args;
71 va_start(args, message);
72 vfprintf(stderr, message, args);
73 va_end(args);
74 exit(errcode);
75 }
76
77
78 /* read_stream -- read size bytes into s from stream d */
read_stream(char * s,size_t size,void * d)79 static char* read_stream(char *s, size_t size, void *d)
80 {
81 return fgets(s, size, (FILE*)d);
82 }
83
84
85 /* main */
main(int argc,char * argv[])86 int main(int argc, char *argv[])
87 {
88 FILE* stream;
89 icalcomponent *comp;
90 icalparser *parser;
91 char *classmask = NULL, *categorymask = NULL;
92 char *notclassmask = NULL, *notcategorymask = NULL;
93 char c;
94 const char *class;
95 icalset *out;
96 icalcomponent *h, *next, *newset;
97 icalproperty *p;
98
99 /* We handle errors ourselves */
100 icalerrno = 0;
101
102 /* Read commandline */
103 while ((c = getopt_long(argc, argv, OPTIONS, options, NULL)) != -1) {
104 switch (c) {
105 case 'p': classmask = strdup(optarg); break;
106 case 'P': notclassmask = strdup(optarg); break;
107 case 'c': categorymask = strdup(optarg); break;
108 case 'C': notcategorymask = strdup(optarg); break;
109 default: fatal(ERR_USAGE, USAGE);
110 }
111 }
112 /* Get input file name */
113 if (optind == argc) fatal(ERR_USAGE, USAGE);
114 stream = optind == argc ? stdin : fopen(argv[optind], "r");
115 if (!stream) fatal(ERR_FILEIO, "%s: %s\n", argv[optind], strerror(errno));
116 optind++;
117
118 /* Get output file name */
119 if (optind == argc) fatal(ERR_USAGE, USAGE);
120 (void) unlink(argv[optind]); /* Remove output file if it already exists */
121 out = icalfileset_new(argv[optind]);
122 if (!out) fatal(ERR_FILEIO, "%s: %s\n", argv[optind], strerror(errno));
123
124 /* Should have no more arguments */
125 if (optind + 1 != argc) fatal(ERR_USAGE, USAGE);
126
127 /* Create a new parser object */
128 parser = icalparser_new();
129
130 /* Tell the parser what input stream it should use */
131 icalparser_set_gen_data(parser, stream);
132
133 /* Let the parser read the file and return all components */
134 if (! (comp = icalparser_parse(parser, read_stream)))
135 fatal(ERR_PARSE, "Parse error: %s\n", icalerror_strerror(icalerrno));
136 icalparser_free(parser);
137
138 /* Initialize a new VCALENDAR */
139 newset = icalcomponent_vanew(ICAL_VCALENDAR_COMPONENT,
140 icalproperty_new_version("2.0"),
141 icalproperty_new_prodid(PRODID),
142 0);
143
144 /* Iterate over VEVENTs */
145 for (h = icalcomponent_get_first_component(comp, ICAL_VEVENT_COMPONENT);
146 h; h = next) {
147
148 next = icalcomponent_get_next_component(comp, ICAL_VEVENT_COMPONENT);
149
150 /* Check if the event is of the right class (unless we accept all) */
151 if (classmask || notclassmask) {
152 p = icalcomponent_get_first_property(h, ICAL_CLASS_PROPERTY);
153 class = p ? icalvalue_as_ical_string(icalproperty_get_value(p))
154 : "NONE";
155 if (classmask && strcasecmp(classmask, class) != 0) continue;
156 if (notclassmask && strcasecmp(notclassmask, class) == 0) continue;
157 }
158
159 /* Check if the event is of the right category (unless we accept all) */
160 if (categorymask) {
161 p = icalcomponent_get_first_property(h, ICAL_CATEGORIES_PROPERTY);
162 while (p && strcasecmp(categorymask, icalproperty_get_categories(p)))
163 p = icalcomponent_get_next_property(h, ICAL_CATEGORIES_PROPERTY);
164 if (!p) continue; /* No category was equal to categorymask */
165 }
166 if (notcategorymask) {
167 p = icalcomponent_get_first_property(h, ICAL_CATEGORIES_PROPERTY);
168 while (p && strcasecmp(notcategorymask, icalproperty_get_categories(p)))
169 p = icalcomponent_get_next_property(h, ICAL_CATEGORIES_PROPERTY);
170 if (p) continue; /* Some category equal to notcategorymask */
171 }
172
173 /* The event passed our filters, so add it to the output set */
174 icalcomponent_remove_component(comp, h); /* Move from comp... */
175 icalcomponent_add_component(newset, h); /* ... to out */
176 }
177
178 /* Put new vcalendar in output file, flush to disk and close file */
179 icalfileset_add_component(out, newset);
180 icalfileset_free(out);
181
182 /* Clean up */
183 icalcomponent_free(comp);
184
185 return 0;
186 }
187