Diffstat (limited to 'libical/src/libical/icalparser.c') (more/less context) (ignore whitespace changes)
-rw-r--r-- | libical/src/libical/icalparser.c | 1111 |
1 files changed, 1111 insertions, 0 deletions
diff --git a/libical/src/libical/icalparser.c b/libical/src/libical/icalparser.c new file mode 100644 index 0000000..b5824d5 --- a/dev/null +++ b/libical/src/libical/icalparser.c | |||
@@ -0,0 +1,1111 @@ | |||
1 | /* -*- Mode: C -*- | ||
2 | ====================================================================== | ||
3 | FILE: icalparser.c | ||
4 | CREATOR: eric 04 August 1999 | ||
5 | |||
6 | $Id$ | ||
7 | $Locker$ | ||
8 | |||
9 | The contents of this file are subject to the Mozilla Public License | ||
10 | Version 1.0 (the "License"); you may not use this file except in | ||
11 | compliance with the License. You may obtain a copy of the License at | ||
12 | http://www.mozilla.org/MPL/ | ||
13 | |||
14 | Software distributed under the License is distributed on an "AS IS" | ||
15 | basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See | ||
16 | the License for the specific language governing rights and | ||
17 | limitations under the License. | ||
18 | |||
19 | |||
20 | This program is free software; you can redistribute it and/or modify | ||
21 | it under the terms of either: | ||
22 | |||
23 | The LGPL as published by the Free Software Foundation, version | ||
24 | 2.1, available at: http://www.fsf.org/copyleft/lesser.html | ||
25 | |||
26 | Or: | ||
27 | |||
28 | The Mozilla Public License Version 1.0. You may obtain a copy of | ||
29 | the License at http://www.mozilla.org/MPL/ | ||
30 | |||
31 | The Initial Developer of the Original Code is Eric Busboom | ||
32 | |||
33 | (C) COPYRIGHT 2000, Eric Busboom, http://www.softwarestudio.org | ||
34 | ======================================================================*/ | ||
35 | |||
36 | #ifdef HAVE_CONFIG_H | ||
37 | #include "config.h" | ||
38 | #endif | ||
39 | |||
40 | |||
41 | #include "icalparser.h" | ||
42 | #include "pvl.h" | ||
43 | #include "icalmemory.h" | ||
44 | #include "icalerror.h" | ||
45 | #include "icalvalue.h" | ||
46 | #include "icalderivedparameter.h" | ||
47 | #include "icalparameter.h" | ||
48 | #include "icalproperty.h" | ||
49 | #include "icalcomponent.h" | ||
50 | |||
51 | #include <string.h> /* For strncpy & size_t */ | ||
52 | #include <stdio.h> /* For FILE and fgets and sprintf */ | ||
53 | #include <stdlib.h> /* for free */ | ||
54 | |||
55 | int snprintf(char *str, size_t n, char const *fmt, ...); | ||
56 | |||
57 | extern icalvalue* icalparser_yy_value; | ||
58 | void set_parser_value_state(icalvalue_kind kind); | ||
59 | int ical_yyparse(void); | ||
60 | |||
61 | char* icalparser_get_next_char(char c, char *str); | ||
62 | char* icalparser_get_next_parameter(char* line,char** end); | ||
63 | char* icalparser_get_next_value(char* line, char **end, icalvalue_kind kind); | ||
64 | char* icalparser_get_prop_name(char* line, char** end); | ||
65 | char* icalparser_get_param_name(char* line, char **end); | ||
66 | |||
67 | #define TMP_BUF_SIZE 80 | ||
68 | |||
69 | struct icalparser_impl | ||
70 | { | ||
71 | int buffer_full; /* flag indicates that temp is smaller that | ||
72 | data being read into it*/ | ||
73 | int continuation_line; /* last line read was a continuation line */ | ||
74 | size_t tmp_buf_size; | ||
75 | char temp[TMP_BUF_SIZE]; | ||
76 | icalcomponent *root_component; | ||
77 | int version; | ||
78 | int level; | ||
79 | int lineno; | ||
80 | icalparser_state state; | ||
81 | pvl_list components; | ||
82 | |||
83 | void *line_gen_data; | ||
84 | |||
85 | }; | ||
86 | |||
87 | |||
88 | icalparser* icalparser_new(void) | ||
89 | { | ||
90 | struct icalparser_impl* impl = 0; | ||
91 | if ( ( impl = (struct icalparser_impl*) | ||
92 | malloc(sizeof(struct icalparser_impl))) == 0) { | ||
93 | icalerror_set_errno(ICAL_NEWFAILED_ERROR); | ||
94 | return 0; | ||
95 | } | ||
96 | |||
97 | impl->root_component = 0; | ||
98 | impl->components = pvl_newlist(); | ||
99 | impl->level = 0; | ||
100 | impl->state = ICALPARSER_SUCCESS; | ||
101 | impl->tmp_buf_size = TMP_BUF_SIZE; | ||
102 | impl->buffer_full = 0; | ||
103 | impl->lineno = 0; | ||
104 | impl->continuation_line = 0; | ||
105 | memset(impl->temp,0, TMP_BUF_SIZE); | ||
106 | |||
107 | return (icalparser*)impl; | ||
108 | } | ||
109 | |||
110 | |||
111 | void icalparser_free(icalparser* parser) | ||
112 | { | ||
113 | struct icalparser_impl* impl = (struct icalparser_impl*)parser; | ||
114 | icalcomponent *c; | ||
115 | |||
116 | if (impl->root_component != 0){ | ||
117 | icalcomponent_free(impl->root_component); | ||
118 | } | ||
119 | |||
120 | while( (c=pvl_pop(impl->components)) != 0){ | ||
121 | icalcomponent_free(c); | ||
122 | } | ||
123 | |||
124 | pvl_free(impl->components); | ||
125 | |||
126 | free(impl); | ||
127 | } | ||
128 | |||
129 | void icalparser_set_gen_data(icalparser* parser, void* data) | ||
130 | { | ||
131 | struct icalparser_impl* impl = (struct icalparser_impl*)parser; | ||
132 | |||
133 | impl->line_gen_data = data; | ||
134 | } | ||
135 | |||
136 | |||
137 | icalvalue* icalvalue_new_From_string_with_error(icalvalue_kind kind, | ||
138 | char* str, | ||
139 | icalproperty **error); | ||
140 | |||
141 | |||
142 | |||
143 | char* icalparser_get_next_char(char c, char *str) | ||
144 | { | ||
145 | int quote_mode = 0; | ||
146 | char* p; | ||
147 | |||
148 | |||
149 | for(p=str; *p!=0; p++){ | ||
150 | |||
151 | if ( quote_mode == 0 && *p=='"' && *(p-1) != '\\' ){ | ||
152 | quote_mode =1; | ||
153 | continue; | ||
154 | } | ||
155 | |||
156 | if ( quote_mode == 1 && *p=='"' && *(p-1) != '\\' ){ | ||
157 | quote_mode =0; | ||
158 | continue; | ||
159 | } | ||
160 | |||
161 | if (quote_mode == 0 && *p== c && *(p-1) != '\\' ){ | ||
162 | return p; | ||
163 | } | ||
164 | |||
165 | } | ||
166 | |||
167 | return 0; | ||
168 | } | ||
169 | |||
170 | /* make a new tmp buffer out of a substring */ | ||
171 | char* make_segment(char* start, char* end) | ||
172 | { | ||
173 | char *buf; | ||
174 | size_t size = (size_t)end - (size_t)start; | ||
175 | |||
176 | buf = icalmemory_tmp_buffer(size+1); | ||
177 | |||
178 | |||
179 | strncpy(buf,start,size); | ||
180 | *(buf+size) = 0; | ||
181 | |||
182 | return buf; | ||
183 | |||
184 | } | ||
185 | |||
186 | const char* input_buffer; | ||
187 | const char* input_buffer_p; | ||
188 | //#define min(a,b) ((a) < (b) ? (a) : (b)) | ||
189 | |||
190 | int icalparser_flex_input(char* buf, int max_size) | ||
191 | { | ||
192 | int n = max_size; // = min(max_size,strlen(input_buffer_p)); | ||
193 | if ( n < ((int )strlen(input_buffer_p)) ) | ||
194 | n = strlen(input_buffer_p); | ||
195 | if (n > 0){ | ||
196 | memcpy(buf, input_buffer_p, n); | ||
197 | input_buffer_p += n; | ||
198 | return n; | ||
199 | } else { | ||
200 | return 0; | ||
201 | } | ||
202 | } | ||
203 | |||
204 | void icalparser_clear_flex_input(void) | ||
205 | { | ||
206 | input_buffer_p = input_buffer+strlen(input_buffer); | ||
207 | } | ||
208 | |||
209 | /* Call the flex/bison parser to parse a complex value */ | ||
210 | |||
211 | icalvalue* icalparser_parse_value(icalvalue_kind kind, | ||
212 | const char* str, icalproperty** error) | ||
213 | { | ||
214 | int r; | ||
215 | input_buffer_p = input_buffer = str; | ||
216 | |||
217 | set_parser_value_state(kind); | ||
218 | icalparser_yy_value = 0; | ||
219 | |||
220 | r = ical_yyparse(); | ||
221 | |||
222 | /* Error. Parse failed */ | ||
223 | if( icalparser_yy_value == 0 || r != 0){ | ||
224 | |||
225 | if(icalparser_yy_value !=0){ | ||
226 | icalvalue_free(icalparser_yy_value); | ||
227 | icalparser_yy_value = 0; | ||
228 | } | ||
229 | |||
230 | return 0; | ||
231 | } | ||
232 | |||
233 | if (error != 0){ | ||
234 | *error = 0; | ||
235 | } | ||
236 | |||
237 | return icalparser_yy_value; | ||
238 | } | ||
239 | |||
240 | char* icalparser_get_prop_name(char* line, char** end) | ||
241 | { | ||
242 | char* p; | ||
243 | char* v; | ||
244 | char *str; | ||
245 | |||
246 | p = icalparser_get_next_char(';',line); | ||
247 | v = icalparser_get_next_char(':',line); | ||
248 | if (p== 0 && v == 0) { | ||
249 | return 0; | ||
250 | } | ||
251 | |||
252 | /* There is no ';' or, it is after the ';' that marks the beginning of | ||
253 | the value */ | ||
254 | if (v!=0 && ( p == 0 || p > v)){ | ||
255 | str = make_segment(line,v); | ||
256 | *end = v+1; | ||
257 | } else { | ||
258 | str = make_segment(line,p); | ||
259 | *end = p+1; | ||
260 | } | ||
261 | |||
262 | return str; | ||
263 | } | ||
264 | |||
265 | char* icalparser_get_param_name(char* line, char **end) | ||
266 | { | ||
267 | |||
268 | char* next; | ||
269 | char *str; | ||
270 | |||
271 | next = icalparser_get_next_char('=',line); | ||
272 | |||
273 | if (next == 0) { | ||
274 | return 0; | ||
275 | } | ||
276 | |||
277 | str = make_segment(line,next); | ||
278 | *end = next+1; | ||
279 | return str; | ||
280 | |||
281 | } | ||
282 | |||
283 | char* icalparser_get_next_paramvalue(char* line, char **end) | ||
284 | { | ||
285 | |||
286 | char* next; | ||
287 | char *str; | ||
288 | |||
289 | next = icalparser_get_next_char(',',line); | ||
290 | |||
291 | if (next == 0){ | ||
292 | next = (char*)(size_t)line+(size_t)strlen(line);\ | ||
293 | } | ||
294 | |||
295 | if (next == line){ | ||
296 | return 0; | ||
297 | } else { | ||
298 | str = make_segment(line,next); | ||
299 | *end = next+1; | ||
300 | return str; | ||
301 | } | ||
302 | |||
303 | } | ||
304 | |||
305 | /* A property may have multiple values, if the values are seperated by | ||
306 | commas in the content line. This routine will look for the next | ||
307 | comma after line and will set the next place to start searching in | ||
308 | end. */ | ||
309 | |||
310 | char* icalparser_get_next_value(char* line, char **end, icalvalue_kind kind) | ||
311 | { | ||
312 | |||
313 | char* next; | ||
314 | char *p; | ||
315 | char *str; | ||
316 | size_t length = strlen(line); | ||
317 | |||
318 | p = line; | ||
319 | while(1){ | ||
320 | |||
321 | next = icalparser_get_next_char(',',p); | ||
322 | |||
323 | /* Unforunately, RFC2445 says that for the RECUR value, COMMA | ||
324 | can both seperate digits in a list, and it can seperate | ||
325 | multiple recurrence specifications. This is not a friendly | ||
326 | part of the spec. This weirdness tries to | ||
327 | distinguish the two uses. it is probably a HACK*/ | ||
328 | |||
329 | if( kind == ICAL_RECUR_VALUE ) { | ||
330 | if ( next != 0 && | ||
331 | (*end+length) > next+5 && | ||
332 | strncmp(next,"FREQ",4) == 0 | ||
333 | ) { | ||
334 | /* The COMMA was followed by 'FREQ', is it a real seperator*/ | ||
335 | /* Fall through */ | ||
336 | } else if (next != 0){ | ||
337 | /* Not real, get the next COMMA */ | ||
338 | p = next+1; | ||
339 | next = 0; | ||
340 | continue; | ||
341 | } | ||
342 | } | ||
343 | |||
344 | /* If the comma is preceeded by a '\', then it is a literal and | ||
345 | not a value seperator*/ | ||
346 | |||
347 | if ( (next!=0 && *(next-1) == '\\') || | ||
348 | (next!=0 && *(next-3) == '\\') | ||
349 | ) | ||
350 | /*second clause for '/' is on prev line. HACK may be out of bounds */ | ||
351 | { | ||
352 | p = next+1; | ||
353 | } else { | ||
354 | break; | ||
355 | } | ||
356 | |||
357 | } | ||
358 | |||
359 | if (next == 0){ | ||
360 | next = (char*)(size_t)line+length; | ||
361 | *end = next; | ||
362 | } else { | ||
363 | *end = next+1; | ||
364 | } | ||
365 | |||
366 | if (next == line){ | ||
367 | return 0; | ||
368 | } | ||
369 | |||
370 | |||
371 | str = make_segment(line,next); | ||
372 | return str; | ||
373 | |||
374 | } | ||
375 | |||
376 | char* icalparser_get_next_parameter(char* line,char** end) | ||
377 | { | ||
378 | char *next; | ||
379 | char *v; | ||
380 | char *str; | ||
381 | |||
382 | v = icalparser_get_next_char(':',line); | ||
383 | next = icalparser_get_next_char(';', line); | ||
384 | |||
385 | /* There is no ';' or, it is after the ':' that marks the beginning of | ||
386 | the value */ | ||
387 | |||
388 | if (next == 0 || next > v) { | ||
389 | next = icalparser_get_next_char(':', line); | ||
390 | } | ||
391 | |||
392 | if (next != 0) { | ||
393 | str = make_segment(line,next); | ||
394 | *end = next+1; | ||
395 | return str; | ||
396 | } else { | ||
397 | *end = line; | ||
398 | return 0; | ||
399 | } | ||
400 | } | ||
401 | |||
402 | /* Get a single property line, from the property name through the | ||
403 | final new line, and include any continuation lines */ | ||
404 | |||
405 | char* icalparser_get_line(icalparser *parser, | ||
406 | char* (*line_gen_func)(char *s, size_t size, void *d)) | ||
407 | { | ||
408 | char *line; | ||
409 | char *line_p; | ||
410 | struct icalparser_impl* impl = (struct icalparser_impl*)parser; | ||
411 | size_t buf_size = impl->tmp_buf_size; | ||
412 | |||
413 | |||
414 | line_p = line = icalmemory_new_buffer(buf_size); | ||
415 | line[0] = '\0'; | ||
416 | |||
417 | /* Read lines by calling line_gen_func and putting the data into | ||
418 | impl->temp. If the line is a continuation line ( begins with a | ||
419 | space after a newline ) then append the data onto line and read | ||
420 | again. Otherwise, exit the loop. */ | ||
421 | |||
422 | while(1) { | ||
423 | |||
424 | /* The first part of the loop deals with the temp buffer, | ||
425 | which was read on he last pass through the loop. The | ||
426 | routine is split like this because it has to read lone line | ||
427 | ahead to determine if a line is a continuation line. */ | ||
428 | |||
429 | |||
430 | /* The tmp buffer is not clear, so transfer the data in it to the | ||
431 | output. This may be left over from a previous call */ | ||
432 | if (impl->temp[0] != '\0' ) { | ||
433 | |||
434 | /* If the last position in the temp buffer is occupied, | ||
435 | mark the buffer as full. The means we will do another | ||
436 | read later, because the line is not finished */ | ||
437 | if (impl->temp[impl->tmp_buf_size-1] == 0 && | ||
438 | impl->temp[impl->tmp_buf_size-2] != '\n'&& | ||
439 | impl->temp[impl->tmp_buf_size-2] != 0 ){ | ||
440 | impl->buffer_full = 1; | ||
441 | } else { | ||
442 | impl->buffer_full = 0; | ||
443 | } | ||
444 | |||
445 | /* Copy the temp to the output and clear the temp buffer. */ | ||
446 | if(impl->continuation_line==1){ | ||
447 | /* back up the pointer to erase the continuation characters */ | ||
448 | impl->continuation_line = 0; | ||
449 | line_p--; | ||
450 | |||
451 | if ( *(line_p-1) == '\r'){ | ||
452 | line_p--; | ||
453 | } | ||
454 | |||
455 | /* copy one space up to eliminate the leading space*/ | ||
456 | icalmemory_append_string(&line,&line_p,&buf_size, | ||
457 | impl->temp+1); | ||
458 | |||
459 | } else { | ||
460 | icalmemory_append_string(&line,&line_p,&buf_size,impl->temp); | ||
461 | } | ||
462 | |||
463 | impl->temp[0] = '\0' ; | ||
464 | } | ||
465 | |||
466 | impl->temp[impl->tmp_buf_size-1] = 1; /* Mark end of buffer */ | ||
467 | |||
468 | /****** Here is where the routine gets string data ******************/ | ||
469 | if ((*line_gen_func)(impl->temp,impl->tmp_buf_size,impl->line_gen_data) | ||
470 | ==0){/* Get more data */ | ||
471 | |||
472 | /* If the first position is clear, it means we didn't get | ||
473 | any more data from the last call to line_ge_func*/ | ||
474 | if (impl->temp[0] == '\0'){ | ||
475 | |||
476 | if(line[0] != '\0'){ | ||
477 | /* There is data in the output, so fall trhough and process it*/ | ||
478 | break; | ||
479 | } else { | ||
480 | /* No data in output; return and signal that there | ||
481 | is no more input*/ | ||
482 | free(line); | ||
483 | return 0; | ||
484 | } | ||
485 | } | ||
486 | } | ||
487 | |||
488 | |||
489 | /* If the output line ends in a '\n' and the temp buffer | ||
490 | begins with a ' ', then the buffer holds a continuation | ||
491 | line, so keep reading. */ | ||
492 | |||
493 | if ( line_p > line+1 && *(line_p-1) == '\n' | ||
494 | && (impl->temp[0] == ' ' || impl->temp[0] == '\t') ) { | ||
495 | |||
496 | impl->continuation_line = 1; | ||
497 | |||
498 | } else if ( impl->buffer_full == 1 ) { | ||
499 | |||
500 | /* The buffer was filled on the last read, so read again */ | ||
501 | |||
502 | } else { | ||
503 | |||
504 | /* Looks like the end of this content line, so break */ | ||
505 | break; | ||
506 | } | ||
507 | |||
508 | |||
509 | } | ||
510 | |||
511 | /* Erase the final newline and/or carriage return*/ | ||
512 | if ( line_p > line+1 && *(line_p-1) == '\n') { | ||
513 | *(line_p-1) = '\0'; | ||
514 | if ( *(line_p-2) == '\r'){ | ||
515 | *(line_p-2) = '\0'; | ||
516 | } | ||
517 | |||
518 | } else { | ||
519 | *(line_p) = '\0'; | ||
520 | } | ||
521 | |||
522 | return line; | ||
523 | |||
524 | } | ||
525 | |||
526 | void insert_error(icalcomponent* comp, char* text, | ||
527 | char* message, icalparameter_xlicerrortype type) | ||
528 | { | ||
529 | char temp[1024]; | ||
530 | |||
531 | if (text == 0){ | ||
532 | snprintf(temp,1024,"%s:",message); | ||
533 | } else { | ||
534 | snprintf(temp,1024,"%s: %s",message,text); | ||
535 | } | ||
536 | |||
537 | icalcomponent_add_property | ||
538 | (comp, | ||
539 | icalproperty_vanew_xlicerror( | ||
540 | temp, | ||
541 | icalparameter_new_xlicerrortype(type), | ||
542 | 0)); | ||
543 | } | ||
544 | |||
545 | int line_is_blank(char* line){ | ||
546 | int i=0; | ||
547 | |||
548 | for(i=0; *(line+i)!=0; i++){ | ||
549 | char c = *(line+i); | ||
550 | |||
551 | if(c != ' ' && c != '\n' && c != '\t'){ | ||
552 | return 0; | ||
553 | } | ||
554 | } | ||
555 | |||
556 | return 1; | ||
557 | } | ||
558 | |||
559 | icalcomponent* icalparser_parse(icalparser *parser, | ||
560 | char* (*line_gen_func)(char *s, size_t size, | ||
561 | void* d)) | ||
562 | { | ||
563 | |||
564 | char* line; | ||
565 | icalcomponent *c=0; | ||
566 | icalcomponent *root=0; | ||
567 | struct icalparser_impl *impl = (struct icalparser_impl*)parser; | ||
568 | icalerrorstate es = icalerror_get_error_state(ICAL_MALFORMEDDATA_ERROR); | ||
569 | |||
570 | icalerror_check_arg_rz((parser !=0),"parser"); | ||
571 | |||
572 | icalerror_set_error_state(ICAL_MALFORMEDDATA_ERROR,ICAL_ERROR_NONFATAL); | ||
573 | |||
574 | do{ | ||
575 | line = icalparser_get_line(parser, line_gen_func); | ||
576 | |||
577 | if ((c = icalparser_add_line(parser,line)) != 0){ | ||
578 | |||
579 | if(icalcomponent_get_parent(c) !=0){ | ||
580 | /* This is bad news... assert? */ | ||
581 | } | ||
582 | |||
583 | assert(impl->root_component == 0); | ||
584 | assert(pvl_count(impl->components) ==0); | ||
585 | |||
586 | if (root == 0){ | ||
587 | /* Just one component */ | ||
588 | root = c; | ||
589 | } else if(icalcomponent_isa(root) != ICAL_XROOT_COMPONENT) { | ||
590 | /*Got a second component, so move the two components under | ||
591 | an XROOT container */ | ||
592 | icalcomponent *tempc = icalcomponent_new(ICAL_XROOT_COMPONENT); | ||
593 | icalcomponent_add_component(tempc, root); | ||
594 | icalcomponent_add_component(tempc, c); | ||
595 | root = tempc; | ||
596 | } else if(icalcomponent_isa(root) == ICAL_XROOT_COMPONENT) { | ||
597 | /* Already have an XROOT container, so add the component | ||
598 | to it*/ | ||
599 | icalcomponent_add_component(root, c); | ||
600 | |||
601 | } else { | ||
602 | /* Badness */ | ||
603 | assert(0); | ||
604 | } | ||
605 | |||
606 | c = 0; | ||
607 | |||
608 | } | ||
609 | if(line != 0){ | ||
610 | free(line); | ||
611 | } | ||
612 | } while ( line != 0); | ||
613 | |||
614 | icalerror_set_error_state(ICAL_MALFORMEDDATA_ERROR,es); | ||
615 | |||
616 | return root; | ||
617 | |||
618 | } | ||
619 | |||
620 | |||
621 | icalcomponent* icalparser_add_line(icalparser* parser, | ||
622 | char* line) | ||
623 | { | ||
624 | char *p; | ||
625 | char *str; | ||
626 | char *end; | ||
627 | int vcount = 0; | ||
628 | icalproperty *prop; | ||
629 | icalproperty_kind prop_kind; | ||
630 | icalvalue *value; | ||
631 | icalvalue_kind value_kind = ICAL_NO_VALUE; | ||
632 | |||
633 | |||
634 | struct icalparser_impl *impl = (struct icalparser_impl*)parser; | ||
635 | icalerror_check_arg_rz((parser != 0),"parser"); | ||
636 | |||
637 | |||
638 | if (line == 0) | ||
639 | { | ||
640 | impl->state = ICALPARSER_ERROR; | ||
641 | return 0; | ||
642 | } | ||
643 | |||
644 | if(line_is_blank(line) == 1){ | ||
645 | return 0; | ||
646 | } | ||
647 | |||
648 | /* Begin by getting the property name at the start of the line. The | ||
649 | property name may end up being "BEGIN" or "END" in which case it | ||
650 | is not really a property, but the marker for the start or end of | ||
651 | a component */ | ||
652 | |||
653 | end = 0; | ||
654 | str = icalparser_get_prop_name(line, &end); | ||
655 | |||
656 | if (str == 0 || strlen(str) == 0 ){ | ||
657 | /* Could not get a property name */ | ||
658 | icalcomponent *tail = pvl_data(pvl_tail(impl->components)); | ||
659 | |||
660 | if (tail){ | ||
661 | insert_error(tail,line, | ||
662 | "Got a data line, but could not find a property name or component begin tag", | ||
663 | ICAL_XLICERRORTYPE_COMPONENTPARSEERROR); | ||
664 | } | ||
665 | tail = 0; | ||
666 | impl->state = ICALPARSER_ERROR; | ||
667 | return 0; | ||
668 | } | ||
669 | |||
670 | /********************************************************************** | ||
671 | * Handle begin and end of components | ||
672 | **********************************************************************/ | ||
673 | /* If the property name is BEGIN or END, we are actually | ||
674 | starting or ending a new component */ | ||
675 | |||
676 | if(strcmp(str,"BEGIN") == 0){ | ||
677 | icalcomponent *c; | ||
678 | icalcomponent_kind comp_kind; | ||
679 | |||
680 | impl->level++; | ||
681 | str = icalparser_get_next_value(end,&end, value_kind); | ||
682 | |||
683 | |||
684 | comp_kind = icalenum_string_to_component_kind(str); | ||
685 | |||
686 | if (comp_kind == ICAL_NO_COMPONENT){ | ||
687 | c = icalcomponent_new(ICAL_XLICINVALID_COMPONENT); | ||
688 | insert_error(c,str,"Parse error in component name", | ||
689 | ICAL_XLICERRORTYPE_COMPONENTPARSEERROR); | ||
690 | } | ||
691 | |||
692 | c = icalcomponent_new(comp_kind); | ||
693 | |||
694 | if (c == 0){ | ||
695 | c = icalcomponent_new(ICAL_XLICINVALID_COMPONENT); | ||
696 | insert_error(c,str,"Parse error in component name", | ||
697 | ICAL_XLICERRORTYPE_COMPONENTPARSEERROR); | ||
698 | } | ||
699 | |||
700 | pvl_push(impl->components,c); | ||
701 | |||
702 | impl->state = ICALPARSER_BEGIN_COMP; | ||
703 | return 0; | ||
704 | |||
705 | } else if (strcmp(str,"END") == 0 ) { | ||
706 | icalcomponent* tail; | ||
707 | |||
708 | impl->level--; | ||
709 | str = icalparser_get_next_value(end,&end, value_kind); | ||
710 | |||
711 | /* Pop last component off of list and add it to the second-to-last*/ | ||
712 | impl->root_component = pvl_pop(impl->components); | ||
713 | |||
714 | tail = pvl_data(pvl_tail(impl->components)); | ||
715 | |||
716 | if(tail != 0){ | ||
717 | icalcomponent_add_component(tail,impl->root_component); | ||
718 | } | ||
719 | |||
720 | tail = 0; | ||
721 | |||
722 | /* Return the component if we are back to the 0th level */ | ||
723 | if (impl->level == 0){ | ||
724 | icalcomponent *rtrn; | ||
725 | |||
726 | if(pvl_count(impl->components) != 0){ | ||
727 | /* There are still components on the stack -- this means | ||
728 | that one of them did not have a proper "END" */ | ||
729 | pvl_push(impl->components,impl->root_component); | ||
730 | icalparser_clean(parser); /* may reset impl->root_component*/ | ||
731 | } | ||
732 | |||
733 | assert(pvl_count(impl->components) == 0); | ||
734 | |||
735 | impl->state = ICALPARSER_SUCCESS; | ||
736 | rtrn = impl->root_component; | ||
737 | impl->root_component = 0; | ||
738 | return rtrn; | ||
739 | |||
740 | } else { | ||
741 | impl->state = ICALPARSER_END_COMP; | ||
742 | return 0; | ||
743 | } | ||
744 | } | ||
745 | |||
746 | |||
747 | /* There is no point in continuing if we have not seen a | ||
748 | component yet */ | ||
749 | |||
750 | if(pvl_data(pvl_tail(impl->components)) == 0){ | ||
751 | impl->state = ICALPARSER_ERROR; | ||
752 | return 0; | ||
753 | } | ||
754 | |||
755 | |||
756 | /********************************************************************** | ||
757 | * Handle property names | ||
758 | **********************************************************************/ | ||
759 | |||
760 | /* At this point, the property name really is a property name, | ||
761 | (Not a component name) so make a new property and add it to | ||
762 | the component */ | ||
763 | |||
764 | |||
765 | prop_kind = icalproperty_string_to_kind(str); | ||
766 | |||
767 | prop = icalproperty_new(prop_kind); | ||
768 | |||
769 | if (prop != 0){ | ||
770 | icalcomponent *tail = pvl_data(pvl_tail(impl->components)); | ||
771 | |||
772 | if(prop_kind==ICAL_X_PROPERTY){ | ||
773 | icalproperty_set_x_name(prop,str); | ||
774 | } | ||
775 | |||
776 | icalcomponent_add_property(tail, prop); | ||
777 | |||
778 | /* Set the value kind for the default for this type of | ||
779 | property. This may be re-set by a VALUE parameter */ | ||
780 | value_kind = icalproperty_kind_to_value_kind(icalproperty_isa(prop)); | ||
781 | |||
782 | } else { | ||
783 | icalcomponent* tail = pvl_data(pvl_tail(impl->components)); | ||
784 | |||
785 | insert_error(tail,str,"Parse error in property name", | ||
786 | ICAL_XLICERRORTYPE_PROPERTYPARSEERROR); | ||
787 | |||
788 | tail = 0; | ||
789 | impl->state = ICALPARSER_ERROR; | ||
790 | return 0; | ||
791 | } | ||
792 | |||
793 | /********************************************************************** | ||
794 | * Handle parameter values | ||
795 | **********************************************************************/ | ||
796 | |||
797 | /* Now, add any parameters to the last property */ | ||
798 | |||
799 | p = 0; | ||
800 | while(1) { | ||
801 | |||
802 | if (*(end-1) == ':'){ | ||
803 | /* if the last seperator was a ":" and the value is a | ||
804 | URL, icalparser_get_next_parameter will find the | ||
805 | ':' in the URL, so better break now. */ | ||
806 | break; | ||
807 | } | ||
808 | |||
809 | str = icalparser_get_next_parameter(end,&end); | ||
810 | |||
811 | if (str != 0){ | ||
812 | char* name; | ||
813 | char* pvalue; | ||
814 | |||
815 | icalparameter *param = 0; | ||
816 | icalparameter_kind kind; | ||
817 | icalcomponent *tail = pvl_data(pvl_tail(impl->components)); | ||
818 | |||
819 | name = icalparser_get_param_name(str,&pvalue); | ||
820 | |||
821 | if (name == 0){ | ||
822 | /* 'tail' defined above */ | ||
823 | insert_error(tail, str, "Cant parse parameter name", | ||
824 | ICAL_XLICERRORTYPE_PARAMETERNAMEPARSEERROR); | ||
825 | tail = 0; | ||
826 | break; | ||
827 | } | ||
828 | |||
829 | kind = icalparameter_string_to_kind(name); | ||
830 | |||
831 | if(kind == ICAL_X_PARAMETER){ | ||
832 | param = icalparameter_new(ICAL_X_PARAMETER); | ||
833 | |||
834 | if(param != 0){ | ||
835 | icalparameter_set_xname(param,name); | ||
836 | icalparameter_set_xvalue(param,pvalue); | ||
837 | } | ||
838 | |||
839 | |||
840 | } else if (kind != ICAL_NO_PARAMETER){ | ||
841 | param = icalparameter_new_from_value_string(kind,pvalue); | ||
842 | } else { | ||
843 | /* Error. Failed to parse the parameter*/ | ||
844 | /* 'tail' defined above */ | ||
845 | insert_error(tail, str, "Cant parse parameter name", | ||
846 | ICAL_XLICERRORTYPE_PARAMETERNAMEPARSEERROR); | ||
847 | tail = 0; | ||
848 | impl->state = ICALPARSER_ERROR; | ||
849 | return 0; | ||
850 | } | ||
851 | |||
852 | if (param == 0){ | ||
853 | /* 'tail' defined above */ | ||
854 | insert_error(tail,str,"Cant parse parameter value", | ||
855 | ICAL_XLICERRORTYPE_PARAMETERVALUEPARSEERROR); | ||
856 | |||
857 | tail = 0; | ||
858 | impl->state = ICALPARSER_ERROR; | ||
859 | continue; | ||
860 | } | ||
861 | |||
862 | /* If it is a VALUE parameter, set the kind of value*/ | ||
863 | if (icalparameter_isa(param)==ICAL_VALUE_PARAMETER){ | ||
864 | |||
865 | value_kind = (icalvalue_kind) | ||
866 | icalparameter_value_to_value_kind( | ||
867 | icalparameter_get_value(param) | ||
868 | ); | ||
869 | |||
870 | if (value_kind == ICAL_NO_VALUE){ | ||
871 | |||
872 | /* Ooops, could not parse the value of the | ||
873 | parameter ( it was not one of the defined | ||
874 | values ), so reset the value_kind */ | ||
875 | |||
876 | insert_error( | ||
877 | tail, str, | ||
878 | "Got a VALUE parameter with an unknown type", | ||
879 | ICAL_XLICERRORTYPE_PARAMETERVALUEPARSEERROR); | ||
880 | icalparameter_free(param); | ||
881 | |||
882 | value_kind = | ||
883 | icalproperty_kind_to_value_kind( | ||
884 | icalproperty_isa(prop)); | ||
885 | |||
886 | icalparameter_free(param); | ||
887 | tail = 0; | ||
888 | impl->state = ICALPARSER_ERROR; | ||
889 | return 0; | ||
890 | } | ||
891 | } | ||
892 | |||
893 | /* Everything is OK, so add the parameter */ | ||
894 | icalproperty_add_parameter(prop,param); | ||
895 | tail = 0; | ||
896 | |||
897 | } else { /* if ( str != 0) */ | ||
898 | /* If we did not get a param string, go on to looking | ||
899 | for a value */ | ||
900 | break; | ||
901 | } /* if ( str != 0) */ | ||
902 | |||
903 | } /* while(1) */ | ||
904 | |||
905 | /********************************************************************** | ||
906 | * Handle values | ||
907 | **********************************************************************/ | ||
908 | |||
909 | /* Look for values. If there are ',' characters in the values, | ||
910 | then there are multiple values, so clone the current | ||
911 | parameter and add one part of the value to each clone */ | ||
912 | |||
913 | vcount=0; | ||
914 | while(1) { | ||
915 | str = icalparser_get_next_value(end,&end, value_kind); | ||
916 | |||
917 | if (str != 0){ | ||
918 | |||
919 | if (vcount > 0){ | ||
920 | /* Actually, only clone after the second value */ | ||
921 | icalproperty* clone = icalproperty_new_clone(prop); | ||
922 | icalcomponent* tail = pvl_data(pvl_tail(impl->components)); | ||
923 | |||
924 | icalcomponent_add_property(tail, clone); | ||
925 | prop = clone; | ||
926 | tail = 0; | ||
927 | } | ||
928 | |||
929 | value = icalvalue_new_from_string(value_kind, str); | ||
930 | |||
931 | /* Don't add properties without value */ | ||
932 | if (value == 0){ | ||
933 | char temp[200]; /* HACK */ | ||
934 | |||
935 | icalproperty_kind prop_kind = icalproperty_isa(prop); | ||
936 | icalcomponent* tail = pvl_data(pvl_tail(impl->components)); | ||
937 | |||
938 | sprintf(temp,"Cant parse as %s value in %s property. Removing entire property", | ||
939 | icalvalue_kind_to_string(value_kind), | ||
940 | icalproperty_kind_to_string(prop_kind)); | ||
941 | |||
942 | insert_error(tail, str, temp, | ||
943 | ICAL_XLICERRORTYPE_VALUEPARSEERROR); | ||
944 | |||
945 | /* Remove the troublesome property */ | ||
946 | icalcomponent_remove_property(tail,prop); | ||
947 | icalproperty_free(prop); | ||
948 | prop = 0; | ||
949 | tail = 0; | ||
950 | impl->state = ICALPARSER_ERROR; | ||
951 | return 0; | ||
952 | |||
953 | } else { | ||
954 | vcount++; | ||
955 | icalproperty_set_value(prop, value); | ||
956 | } | ||
957 | |||
958 | |||
959 | } else { | ||
960 | if (vcount == 0){ | ||
961 | char temp[200]; /* HACK */ | ||
962 | |||
963 | icalproperty_kind prop_kind = icalproperty_isa(prop); | ||
964 | icalcomponent *tail = pvl_data(pvl_tail(impl->components)); | ||
965 | |||
966 | sprintf(temp,"No value for %s property. Removing entire property", | ||
967 | icalproperty_kind_to_string(prop_kind)); | ||
968 | |||
969 | insert_error(tail, str, temp, | ||
970 | ICAL_XLICERRORTYPE_VALUEPARSEERROR); | ||
971 | |||
972 | /* Remove the troublesome property */ | ||
973 | icalcomponent_remove_property(tail,prop); | ||
974 | icalproperty_free(prop); | ||
975 | prop = 0; | ||
976 | tail = 0; | ||
977 | impl->state = ICALPARSER_ERROR; | ||
978 | return 0; | ||
979 | } else { | ||
980 | |||
981 | break; | ||
982 | } | ||
983 | } | ||
984 | } | ||
985 | |||
986 | /**************************************************************** | ||
987 | * End of component parsing. | ||
988 | *****************************************************************/ | ||
989 | |||
990 | if (pvl_data(pvl_tail(impl->components)) == 0 && | ||
991 | impl->level == 0){ | ||
992 | /* HACK. Does this clause ever get executed? */ | ||
993 | impl->state = ICALPARSER_SUCCESS; | ||
994 | assert(0); | ||
995 | return impl->root_component; | ||
996 | } else { | ||
997 | impl->state = ICALPARSER_IN_PROGRESS; | ||
998 | return 0; | ||
999 | } | ||
1000 | |||
1001 | } | ||
1002 | |||
1003 | icalparser_state icalparser_get_state(icalparser* parser) | ||
1004 | { | ||
1005 | struct icalparser_impl* impl = (struct icalparser_impl*) parser; | ||
1006 | return impl->state; | ||
1007 | |||
1008 | } | ||
1009 | |||
1010 | icalcomponent* icalparser_clean(icalparser* parser) | ||
1011 | { | ||
1012 | struct icalparser_impl* impl = (struct icalparser_impl*) parser; | ||
1013 | icalcomponent *tail; | ||
1014 | |||
1015 | icalerror_check_arg_rz((parser != 0 ),"parser"); | ||
1016 | |||
1017 | /* We won't get a clean exit if some components did not have an | ||
1018 | "END" tag. Clear off any component that may be left in the list */ | ||
1019 | |||
1020 | while((tail=pvl_data(pvl_tail(impl->components))) != 0){ | ||
1021 | |||
1022 | insert_error(tail," ", | ||
1023 | "Missing END tag for this component. Closing component at end of input.", | ||
1024 | ICAL_XLICERRORTYPE_COMPONENTPARSEERROR); | ||
1025 | |||
1026 | |||
1027 | impl->root_component = pvl_pop(impl->components); | ||
1028 | tail=pvl_data(pvl_tail(impl->components)); | ||
1029 | |||
1030 | if(tail != 0){ | ||
1031 | if(icalcomponent_get_parent(impl->root_component)!=0){ | ||
1032 | icalerror_warn("icalparser_clean is trying to attach a component for the second time"); | ||
1033 | } else { | ||
1034 | icalcomponent_add_component(tail,impl->root_component); | ||
1035 | } | ||
1036 | } | ||
1037 | |||
1038 | } | ||
1039 | |||
1040 | return impl->root_component; | ||
1041 | |||
1042 | } | ||
1043 | |||
1044 | struct slg_data { | ||
1045 | const char* pos; | ||
1046 | const char* str; | ||
1047 | }; | ||
1048 | |||
1049 | char* string_line_generator(char *out, size_t buf_size, void *d) | ||
1050 | { | ||
1051 | char *n; | ||
1052 | size_t size; | ||
1053 | struct slg_data* data = (struct slg_data*)d; | ||
1054 | |||
1055 | if(data->pos==0){ | ||
1056 | data->pos=data->str; | ||
1057 | } | ||
1058 | |||
1059 | /* If the pointer is at the end of the string, we are done */ | ||
1060 | if (*(data->pos)==0){ | ||
1061 | return 0; | ||
1062 | } | ||
1063 | |||
1064 | n = strchr(data->pos,'\n'); | ||
1065 | |||
1066 | if (n == 0){ | ||
1067 | size = strlen(data->pos); | ||
1068 | } else { | ||
1069 | n++; /* include newline in output */ | ||
1070 | size = (n-data->pos); | ||
1071 | } | ||
1072 | |||
1073 | if (size > buf_size-1){ | ||
1074 | size = buf_size-1; | ||
1075 | } | ||
1076 | |||
1077 | |||
1078 | strncpy(out,data->pos,size); | ||
1079 | |||
1080 | *(out+size) = '\0'; | ||
1081 | |||
1082 | data->pos += size; | ||
1083 | |||
1084 | return out; | ||
1085 | } | ||
1086 | |||
1087 | icalcomponent* icalparser_parse_string(const char* str) | ||
1088 | { | ||
1089 | icalcomponent *c; | ||
1090 | struct slg_data d; | ||
1091 | icalparser *p; | ||
1092 | |||
1093 | icalerrorstate es = icalerror_get_error_state(ICAL_MALFORMEDDATA_ERROR); | ||
1094 | |||
1095 | d.pos = 0; | ||
1096 | d.str = str; | ||
1097 | |||
1098 | p = icalparser_new(); | ||
1099 | icalparser_set_gen_data(p,&d); | ||
1100 | |||
1101 | icalerror_set_error_state(ICAL_MALFORMEDDATA_ERROR,ICAL_ERROR_NONFATAL); | ||
1102 | |||
1103 | c = icalparser_parse(p,string_line_generator); | ||
1104 | |||
1105 | icalerror_set_error_state(ICAL_MALFORMEDDATA_ERROR,es); | ||
1106 | |||
1107 | icalparser_free(p); | ||
1108 | |||
1109 | return c; | ||
1110 | |||
1111 | } | ||