1 /* -*- Mode: C; tab-width: 4; c-basic-offset: 8; -*-
2 ======================================================================
4 CREATOR: eric 04 August 1999
6 $Id: icalparser.c,v 1.53 2008-01-15 23:17:40 dothebart Exp $
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/
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.
20 This program is free software; you can redistribute it and/or modify
21 it under the terms of either:
23 The LGPL as published by the Free Software Foundation, version
24 2.1, available at: http://www.fsf.org/copyleft/lesser.html
28 The Mozilla Public License Version 1.0. You may obtain a copy of
29 the License at http://www.mozilla.org/MPL/
31 The Initial Developer of the Original Code is Eric Busboom
33 (C) COPYRIGHT 2000, Eric Busboom <eric@softwarestudio.org>
34 http://www.softwarestudio.org
35 ======================================================================*/
43 #include "icalerror.h"
44 #include "icalvalue.h"
45 #include "icalderivedparameter.h"
46 #include "icalparameter.h"
47 #include "icalproperty.h"
48 #include "icalcomponent.h"
50 #include <string.h> /* For strncpy & size_t */
51 #include <stdio.h> /* For FILE and fgets and snprintf */
52 #include <stdlib.h> /* for free */
55 #include "icalmemory.h"
56 #include "icalparser.h"
60 /* Some systems have an imcomplete implementation on wctype (FreeBSD,
61 * Darwin). Cope with that. */
62 # ifndef HAVE_ISWSPACE
63 # define iswspace isspace
66 # if !defined HAVE_ISWSPACE && !defined WIN32
67 # define iswspace isspace
72 #define snprintf _snprintf
73 #define strcasecmp stricmp
76 static char* parser_get_next_char(char c, char *str, int qm);
77 static char* parser_get_next_parameter(char* line,char** end);
78 static char* parser_get_next_value(char* line, char **end, icalvalue_kind kind);
79 static char* parser_get_prop_name(char* line, char** end);
80 static char* parser_get_param_name(char* line, char **end);
82 #define TMP_BUF_SIZE 80
84 struct icalparser_impl
86 int buffer_full; /* flag indicates that temp is smaller that
87 data being read into it*/
88 int continuation_line; /* last line read was a continuation line */
90 char temp[TMP_BUF_SIZE];
91 icalcomponent *root_component;
95 icalparser_state state;
104 * New version of strstrip() that does not move the pointer.
106 void strstriplt(char *buf)
118 while ((buf[0] != 0) && (isspace((unsigned char)buf[len - 1]))) {
125 while ((buf[0]!=0) && (isspace((unsigned char)buf[a]))) {
129 memmove(buf, &buf[a], len - a + 1);
135 icalparser* icalparser_new(void)
137 struct icalparser_impl* impl = 0;
138 if ( ( impl = (struct icalparser_impl*)
139 malloc(sizeof(struct icalparser_impl))) == 0) {
140 icalerror_set_errno(ICAL_NEWFAILED_ERROR);
144 impl->root_component = 0;
145 impl->components = pvl_newlist();
147 impl->state = ICALPARSER_SUCCESS;
148 impl->tmp_buf_size = TMP_BUF_SIZE;
149 impl->buffer_full = 0;
150 impl->continuation_line = 0;
152 impl->continuation_line = 0;
153 memset(impl->temp,0, TMP_BUF_SIZE);
155 return (icalparser*)impl;
159 void icalparser_free(icalparser* parser)
163 if (parser->root_component != 0){
164 icalcomponent_free(parser->root_component);
167 while( (c=pvl_pop(parser->components)) != 0){
168 icalcomponent_free(c);
171 pvl_free(parser->components);
176 void icalparser_set_gen_data(icalparser* parser, void* data)
178 parser->line_gen_data = data;
182 icalvalue* icalvalue_new_From_string_with_error(icalvalue_kind kind,
184 icalproperty **error);
188 char* parser_get_next_char(char c, char *str, int qm)
193 for(p=str; *p!=0; p++){
195 if ( quote_mode == 0 && *p=='"' && p>str && *(p-1) != '\\' ){
200 if ( quote_mode == 1 && *p=='"' && p>str && *(p-1) != '\\' ){
206 if (quote_mode == 0 && *p== c && p>str && *(p-1) != '\\' ){
216 /** make a new tmp buffer out of a substring */
217 static char* make_segment(char* start, char* end)
220 size_t size = (size_t)end - (size_t)start;
222 buf = icalmemory_new_buffer(size+1);
223 strncpy(buf,start,size);
227 while ((tmp >= buf) &&
228 ((*tmp == '\0') || iswspace(*tmp)))
238 char* parser_get_prop_name(char* line, char** end)
244 p = parser_get_next_char(';',line,1);
245 v = parser_get_next_char(':',line,1);
246 if (p== 0 && v == 0) {
250 /* There is no ';' or, it is after the ';' that marks the beginning of
252 if (v!=0 && ( p == 0 || p > v)){
253 str = make_segment(line,v);
256 str = make_segment(line,p);
264 char* parser_get_param_name(char* line, char **end)
269 next = parser_get_next_char('=',line,1);
275 str = make_segment(line,next);
279 next = parser_get_next_char('"',*end,0);
285 *end = make_segment(*end,next);
287 *end = make_segment(*end, *end + strlen(*end));
295 char* parser_get_next_paramvalue(char* line, char **end)
300 next = parser_get_next_char(',',line,1);
303 next = (char*)(size_t)line+(size_t)strlen(line);\
309 str = make_segment(line,next);
316 char* icalparser_get_value(char* line, char **end, icalvalue_kind kind)
319 size_t length = strlen(line);
326 str = make_segment(line, *end);
332 A property may have multiple values, if the values are seperated by
333 commas in the content line. This routine will look for the next
334 comma after line and will set the next place to start searching in
338 char* parser_get_next_value(char* line, char **end, icalvalue_kind kind)
344 size_t length = strlen(line);
347 if( line[0] == '\"' && line[length - 1] == '\"' ) {
348 /* This line is quoted, don't split into multiple values */
355 next = parser_get_next_char(',',p,1);
357 /* Unforunately, RFC2445 says that for the RECUR value, COMMA
358 can both separate digits in a list, and it can separate
359 multiple recurrence specifications. This is not a friendly
360 part of the spec. This weirdness tries to
361 distinguish the two uses. it is probably a HACK*/
363 if( kind == ICAL_RECUR_VALUE ) {
365 (*end+length) > next+5 &&
366 strncmp(next,"FREQ",4) == 0
368 /* The COMMA was followed by 'FREQ', is it a real separator*/
370 } else if (next != 0){
371 /* Not real, get the next COMMA */
377 /* ignore all , for query value. select dtstart, dtend etc ... */
378 else if( kind == ICAL_QUERY_VALUE) {
387 /* If the comma is preceded by a '\', then it is a literal and
388 not a value separator*/
390 if ( (next!=0 && *(next-1) == '\\') ||
391 (next!=0 && *(next-3) == '\\')
393 /*second clause for '/' is on prev line. HACK may be out of bounds */
403 next = (char*)(size_t)line+length;
414 str = make_segment(line,next);
420 char* parser_get_next_parameter(char* line,char** end)
426 v = parser_get_next_char(':',line,1);
427 next = parser_get_next_char(';', line,1);
429 /* There is no ';' or, it is after the ':' that marks the beginning of
432 if (next == 0 || next > v) {
433 next = parser_get_next_char(':', line,1);
437 str = make_segment(line,next);
448 * Get a single property line, from the property name through the
449 * final new line, and include any continuation lines
451 char* icalparser_get_line(icalparser *parser,
452 char* (*line_gen_func)(char *s, size_t size, void *d))
456 size_t buf_size = parser->tmp_buf_size;
458 line_p = line = icalmemory_new_buffer(buf_size);
461 /* Read lines by calling line_gen_func and putting the data into
462 parser->temp. If the line is a continuation line ( begins with a
463 space after a newline ) then append the data onto line and read
464 again. Otherwise, exit the loop. */
468 /* The first part of the loop deals with the temp buffer,
469 which was read on he last pass through the loop. The
470 routine is split like this because it has to read lone line
471 ahead to determine if a line is a continuation line. */
474 /* The tmp buffer is not clear, so transfer the data in it to the
475 output. This may be left over from a previous call */
476 if (parser->temp[0] != '\0' ) {
478 /* If the last position in the temp buffer is occupied,
479 mark the buffer as full. The means we will do another
480 read later, because the line is not finished */
481 if (parser->temp[parser->tmp_buf_size-1] == 0 &&
482 parser->temp[parser->tmp_buf_size-2] != '\n'&&
483 parser->temp[parser->tmp_buf_size-2] != 0 ){
484 parser->buffer_full = 1;
486 parser->buffer_full = 0;
489 /* Copy the temp to the output and clear the temp buffer. */
490 if(parser->continuation_line==1){
491 /* back up the pointer to erase the continuation characters */
492 parser->continuation_line = 0;
495 if ( *(line_p-1) == '\r'){
499 /* copy one space up to eliminate the leading space*/
500 icalmemory_append_string(&line,&line_p,&buf_size,
504 icalmemory_append_string(&line,&line_p,&buf_size,parser->temp);
507 parser->temp[0] = '\0' ;
510 parser->temp[parser->tmp_buf_size-1] = 1; /* Mark end of buffer */
512 /****** Here is where the routine gets string data ******************/
513 if ((*line_gen_func)(parser->temp,parser->tmp_buf_size,parser->line_gen_data)
514 ==0){/* Get more data */
516 /* If the first position is clear, it means we didn't get
517 any more data from the last call to line_ge_func*/
518 if (parser->temp[0] == '\0'){
521 /* There is data in the output, so fall trhough and process it*/
524 /* No data in output; return and signal that there
533 /* If the output line ends in a '\n' and the temp buffer
534 begins with a ' ' or tab, then the buffer holds a continuation
535 line, so keep reading. RFC 2445, section 4.1 */
537 if ( line_p > line+1 && *(line_p-1) == '\n'
538 && (parser->temp[0] == ' ' || parser->temp[0] == '\t') ) {
540 parser->continuation_line = 1;
542 } else if ( parser->buffer_full == 1 ) {
544 /* The buffer was filled on the last read, so read again */
548 /* Looks like the end of this content line, so break */
555 /* Erase the final newline and/or carriage return*/
556 if ( line_p > line+1 && *(line_p-1) == '\n') {
558 if ( *(line_p-2) == '\r'){
566 while ( (*line_p == '\0' || iswspace(*line_p)) && line_p > line )
576 static void insert_error(icalcomponent* comp, const char* text,
577 const char* message, icalparameter_xlicerrortype type)
582 snprintf(temp,1024,"%s:",message);
584 snprintf(temp,1024,"%s: %s",message,text);
587 icalcomponent_add_property
589 icalproperty_vanew_xlicerror(
591 icalparameter_new_xlicerrortype(type),
596 static int line_is_blank(char* line){
599 for(i=0; *(line+i)!=0; i++){
602 if(c != ' ' && c != '\n' && c != '\t'){
610 icalcomponent* icalparser_parse(icalparser *parser,
611 char* (*line_gen_func)(char *s, size_t size,
617 icalcomponent *root=0;
618 icalerrorstate es = icalerror_get_error_state(ICAL_MALFORMEDDATA_ERROR);
621 icalerror_check_arg_rz((parser !=0),"parser");
623 icalerror_set_error_state(ICAL_MALFORMEDDATA_ERROR,ICAL_ERROR_NONFATAL);
626 line = icalparser_get_line(parser, line_gen_func);
628 if ((c = icalparser_add_line(parser,line)) != 0){
630 if(icalcomponent_get_parent(c) !=0){
631 /* This is bad news... assert? */
634 assert(parser->root_component == 0);
635 assert(pvl_count(parser->components) ==0);
638 /* Just one component */
640 } else if(icalcomponent_isa(root) != ICAL_XROOT_COMPONENT) {
641 /*Got a second component, so move the two components under
642 an XROOT container */
643 icalcomponent *tempc = icalcomponent_new(ICAL_XROOT_COMPONENT);
644 icalcomponent_add_component(tempc, root);
645 icalcomponent_add_component(tempc, c);
647 } else if(icalcomponent_isa(root) == ICAL_XROOT_COMPONENT) {
648 /* Already have an XROOT container, so add the component
650 icalcomponent_add_component(root, c);
662 icalmemory_free_buffer(line);
667 icalerror_set_error_state(ICAL_MALFORMEDDATA_ERROR,es);
674 icalcomponent* icalparser_add_line(icalparser* parser,
681 icalproperty_kind prop_kind;
683 icalvalue_kind value_kind = ICAL_NO_VALUE;
686 icalerror_check_arg_rz((parser != 0),"parser");
691 parser->state = ICALPARSER_ERROR;
695 if(line_is_blank(line) == 1){
699 /* Begin by getting the property name at the start of the line. The
700 property name may end up being "BEGIN" or "END" in which case it
701 is not really a property, but the marker for the start or end of
705 str = parser_get_prop_name(line, &end);
707 if (str == 0 || *str == '\0' ){
708 /* Could not get a property name */
709 icalcomponent *tail = pvl_data(pvl_tail(parser->components));
712 insert_error(tail,line,
713 "Got a data line, but could not find a property name or component begin tag",
714 ICAL_XLICERRORTYPE_COMPONENTPARSEERROR);
717 parser->state = ICALPARSER_ERROR;
718 icalmemory_free_buffer(str);
723 /**********************************************************************
724 * Handle begin and end of components
725 **********************************************************************/
726 /* If the property name is BEGIN or END, we are actually
727 starting or ending a new component */
730 if(strcasecmp(str,"BEGIN") == 0){
732 icalcomponent_kind comp_kind;
734 icalmemory_free_buffer(str);
738 str = parser_get_next_value(end,&end, value_kind);
741 comp_kind = icalenum_string_to_component_kind(str);
744 if (comp_kind == ICAL_NO_COMPONENT){
747 c = icalcomponent_new(ICAL_XLICINVALID_COMPONENT);
748 insert_error(c,str,"Parse error in component name",
749 ICAL_XLICERRORTYPE_COMPONENTPARSEERROR);
752 c = icalcomponent_new(comp_kind);
755 c = icalcomponent_new(ICAL_XLICINVALID_COMPONENT);
756 insert_error(c,str,"Parse error in component name",
757 ICAL_XLICERRORTYPE_COMPONENTPARSEERROR);
760 pvl_push(parser->components,c);
762 parser->state = ICALPARSER_BEGIN_COMP;
764 icalmemory_free_buffer(str);
768 } else if (strcasecmp(str,"END") == 0 ) {
771 icalmemory_free_buffer(str);
775 str = parser_get_next_value(end,&end, value_kind);
777 /* Pop last component off of list and add it to the second-to-last*/
778 parser->root_component = pvl_pop(parser->components);
780 tail = pvl_data(pvl_tail(parser->components));
783 icalcomponent_add_component(tail,parser->root_component);
787 icalmemory_free_buffer(str);
790 /* Return the component if we are back to the 0th level */
791 if (parser->level == 0){
794 if(pvl_count(parser->components) != 0){
795 /* There are still components on the stack -- this means
796 that one of them did not have a proper "END" */
797 pvl_push(parser->components,parser->root_component);
798 icalparser_clean(parser); /* may reset parser->root_component*/
801 assert(pvl_count(parser->components) == 0);
803 parser->state = ICALPARSER_SUCCESS;
804 rtrn = parser->root_component;
805 parser->root_component = 0;
809 parser->state = ICALPARSER_END_COMP;
815 /* There is no point in continuing if we have not seen a
818 if(pvl_data(pvl_tail(parser->components)) == 0){
819 parser->state = ICALPARSER_ERROR;
820 icalmemory_free_buffer(str);
826 /**********************************************************************
827 * Handle property names
828 **********************************************************************/
830 /* At this point, the property name really is a property name,
831 (Not a component name) so make a new property and add it to
835 prop_kind = icalproperty_string_to_kind(str);
837 prop = icalproperty_new(prop_kind);
840 icalcomponent *tail = pvl_data(pvl_tail(parser->components));
842 if(prop_kind==ICAL_X_PROPERTY){
843 icalproperty_set_x_name(prop,str);
846 icalcomponent_add_property(tail, prop);
848 /* Set the value kind for the default for this type of
849 property. This may be re-set by a VALUE parameter */
850 value_kind = icalproperty_kind_to_value_kind(icalproperty_isa(prop));
853 icalcomponent* tail = pvl_data(pvl_tail(parser->components));
855 insert_error(tail,str,"Parse error in property name",
856 ICAL_XLICERRORTYPE_PROPERTYPARSEERROR);
859 parser->state = ICALPARSER_ERROR;
860 icalmemory_free_buffer(str);
865 icalmemory_free_buffer(str);
868 /**********************************************************************
869 * Handle parameter values
870 **********************************************************************/
872 /* Now, add any parameters to the last property */
876 if (*(end-1) == ':'){
877 /* if the last separator was a ":" and the value is a
878 URL, icalparser_get_next_parameter will find the
879 ':' in the URL, so better break now. */
883 str = parser_get_next_parameter(end,&end);
889 icalparameter *param = 0;
890 icalparameter_kind kind;
891 icalcomponent *tail = pvl_data(pvl_tail(parser->components));
893 name = parser_get_param_name(str,&pvalue);
896 /* 'tail' defined above */
897 insert_error(tail, str, "Cant parse parameter name",
898 ICAL_XLICERRORTYPE_PARAMETERNAMEPARSEERROR);
903 kind = icalparameter_string_to_kind(name);
905 if(kind == ICAL_X_PARAMETER){
906 param = icalparameter_new(ICAL_X_PARAMETER);
908 icalparameter_set_xname(param,name);
909 icalparameter_set_xvalue(param,pvalue);
911 } else if (kind == ICAL_IANA_PARAMETER){
912 ical_unknown_token_handling tokHandlingSetting =
913 ical_get_unknown_token_handling_setting();
914 if (tokHandlingSetting == ICAL_DISCARD_TOKEN)
916 param = icalparameter_new(ICAL_IANA_PARAMETER);
919 icalparameter_set_xname(param,name);
920 icalparameter_set_xvalue(param,pvalue);
922 } else if (kind == ICAL_TZID_PARAMETER){
924 Special case handling for TZID to workaround invalid incoming data.
925 For example, Google Calendar will send back stuff like this:
926 DTSTART;TZID=GMT+05:30:20120904T020000
928 In this case we read to the last colon rather than the first colon.
929 This way the TZID will become GMT+05:30 rather than trying to parse
930 the date-time as 30:20120904T020000.
933 char *nextColon = end;
935 /* Find the last colon in the line */
937 nextColon = parser_get_next_char(':', nextColon, 1);
940 lastColon = nextColon;
945 Rebuild str so that it includes everything up to the last colon.
946 So given the above example, str will go from
947 "TZID=GMT+05" to "TZID=GMT+05:30"
949 if (lastColon && *(lastColon + 1) != 0) {
950 char *strStart = line + sizeof(str);
954 icalmemory_free_buffer(str);
955 str = make_segment(strStart, end - 1);
958 icalmemory_free_buffer(name);
959 icalmemory_free_buffer(pvalue);
961 /* Reparse the parameter name and value with the new segment */
962 name = parser_get_param_name(str,&pvalue);
963 param = icalparameter_new_from_value_string(kind,pvalue);
964 } else if (kind != ICAL_NO_PARAMETER){
965 param = icalparameter_new_from_value_string(kind,pvalue);
967 /* Error. Failed to parse the parameter*/
968 /* 'tail' defined above */
970 /* Change for mozilla */
971 /* have the option of being flexible towards unsupported parameters */
972 #if ICAL_ERRORS_ARE_FATAL == 1
973 insert_error(tail, str, "Cant parse parameter name",
974 ICAL_XLICERRORTYPE_PARAMETERNAMEPARSEERROR);
976 parser->state = ICALPARSER_ERROR;
985 icalmemory_free_buffer(str);
993 icalmemory_free_buffer(str);
1010 /* 'tail' defined above */
1011 insert_error(tail,str,"Cant parse parameter value",
1012 ICAL_XLICERRORTYPE_PARAMETERVALUEPARSEERROR);
1015 parser->state = ICALPARSER_ERROR;
1017 icalmemory_free_buffer(name);
1019 icalmemory_free_buffer(str);
1025 /* If it is a VALUE parameter, set the kind of value*/
1026 if (icalparameter_isa(param)==ICAL_VALUE_PARAMETER){
1028 value_kind = (icalvalue_kind)
1029 icalparameter_value_to_value_kind(
1030 icalparameter_get_value(param)
1033 if (value_kind == ICAL_NO_VALUE){
1035 /* Ooops, could not parse the value of the
1036 parameter ( it was not one of the defined
1037 values ), so reset the value_kind */
1041 "Got a VALUE parameter with an unknown type",
1042 ICAL_XLICERRORTYPE_PARAMETERVALUEPARSEERROR);
1045 icalproperty_kind_to_value_kind(
1046 icalproperty_isa(prop));
1048 icalparameter_free(param);
1050 parser->state = ICALPARSER_ERROR;
1052 icalmemory_free_buffer(name);
1054 icalmemory_free_buffer(str);
1059 icalmemory_free_buffer(name);
1062 /* Everything is OK, so add the parameter */
1063 icalproperty_add_parameter(prop,param);
1065 icalmemory_free_buffer(str);
1068 } else { /* if ( str != 0) */
1069 /* If we did not get a param string, go on to looking for a value */
1071 icalmemory_free_buffer(str);
1075 } /* if ( str != 0) */
1079 /**********************************************************************
1081 **********************************************************************/
1083 /* Look for values. If there are ',' characters in the values,
1084 then there are multiple values, so clone the current
1085 parameter and add one part of the value to each clone */
1089 /* Only some properies can have multiple values. This list was taken
1090 from rfc2445. Also added the x-properties, because the spec actually
1091 says that commas should be escaped. For x-properties, other apps may
1092 depend on that behaviour
1094 switch (prop_kind) {
1095 case ICAL_X_PROPERTY:
1096 case ICAL_CATEGORIES_PROPERTY:
1097 case ICAL_RESOURCES_PROPERTY:
1098 /* Referring to RFC 2445, section 4.8.5.3 and section 4.8.5.1:
1099 RDATE and EXDATE can specify a list of dates/date-times/periods.
1101 case ICAL_RDATE_PROPERTY:
1102 case ICAL_EXDATE_PROPERTY:
1103 /* Referring to RFC 2445, section 4.8.2.6 Free/Busy Time:
1104 The "FREEBUSY" property can specify more than one value, separated by
1105 the COMMA character (US-ASCII decimal 44).
1107 case ICAL_FREEBUSY_PROPERTY:
1108 str = parser_get_next_value(end,&end, value_kind);
1112 str = icalparser_get_value(end, &end, value_kind);
1120 /* Actually, only clone after the second value */
1121 icalproperty* clone = icalproperty_new_clone(prop);
1122 icalcomponent* tail = pvl_data(pvl_tail(parser->components));
1124 icalcomponent_add_property(tail, clone);
1129 /* If this is a URI value for an ATTACH property, then change
1130 the value to an ATTACH kind as well.
1131 Now we can support ATTACH;TYPE=URI:http://my.fav.url
1133 if(value_kind == ICAL_URI_VALUE && prop_kind == ICAL_ATTACH_PROPERTY){
1134 value_kind = ICAL_ATTACH_VALUE;
1136 value = icalvalue_new_from_string(value_kind, str);
1138 /* Don't add properties without value */
1140 char temp[200]; /* HACK */
1142 icalproperty_kind prop_kind = icalproperty_isa(prop);
1143 icalcomponent* tail = pvl_data(pvl_tail(parser->components));
1145 snprintf(temp,sizeof(temp),"Can't parse as %s value in %s property. Removing entire property",
1146 icalvalue_kind_to_string(value_kind),
1147 icalproperty_kind_to_string(prop_kind));
1149 insert_error(tail, str, temp,
1150 ICAL_XLICERRORTYPE_VALUEPARSEERROR);
1152 /* Remove the troublesome property */
1153 icalcomponent_remove_property(tail,prop);
1154 icalproperty_free(prop);
1157 parser->state = ICALPARSER_ERROR;
1159 icalmemory_free_buffer(str);
1165 icalproperty_set_value(prop, value);
1167 icalmemory_free_buffer(str);
1171 #if ICAL_ALLOW_EMPTY_PROPERTIES
1172 /* Don't replace empty properties with an error.
1173 Set an empty length string (not null) as the value instead */
1175 icalproperty_set_value(prop, icalvalue_new_text(""));
1181 char temp[200]; /* HACK */
1183 icalproperty_kind prop_kind = icalproperty_isa(prop);
1184 icalcomponent *tail = pvl_data(pvl_tail(parser->components));
1186 snprintf(temp,sizeof(temp),"No value for %s property. Removing entire property",
1187 icalproperty_kind_to_string(prop_kind));
1189 insert_error(tail, str, temp,
1190 ICAL_XLICERRORTYPE_VALUEPARSEERROR);
1192 /* Remove the troublesome property */
1193 icalcomponent_remove_property(tail,prop);
1194 icalproperty_free(prop);
1197 parser->state = ICALPARSER_ERROR;
1207 /****************************************************************
1208 * End of component parsing.
1209 *****************************************************************/
1211 if (pvl_data(pvl_tail(parser->components)) == 0 &&
1212 parser->level == 0){
1213 /* HACK. Does this clause ever get executed? */
1214 parser->state = ICALPARSER_SUCCESS;
1216 return parser->root_component;
1218 parser->state = ICALPARSER_IN_PROGRESS;
1224 icalparser_state icalparser_get_state(icalparser* parser)
1226 return parser->state;
1230 icalcomponent* icalparser_clean(icalparser* parser)
1232 icalcomponent *tail;
1234 icalerror_check_arg_rz((parser != 0 ),"parser");
1236 /* We won't get a clean exit if some components did not have an
1237 "END" tag. Clear off any component that may be left in the list */
1239 while((tail=pvl_data(pvl_tail(parser->components))) != 0){
1241 insert_error(tail," ",
1242 "Missing END tag for this component. Closing component at end of input.",
1243 ICAL_XLICERRORTYPE_COMPONENTPARSEERROR);
1246 parser->root_component = pvl_pop(parser->components);
1247 tail=pvl_data(pvl_tail(parser->components));
1249 if(tail != 0 && parser->root_component != NULL){
1250 if(icalcomponent_get_parent(parser->root_component)!=0){
1251 icalerror_warn("icalparser_clean is trying to attach a component for the second time");
1253 icalcomponent_add_component(tail,parser->root_component);
1259 return parser->root_component;
1269 char* icalparser_string_line_generator(char *out, size_t buf_size, void *d)
1273 struct slg_data* data = (struct slg_data*)d;
1276 data->pos=data->str;
1279 /* If the pointer is at the end of the string, we are done */
1280 if (*(data->pos)==0){
1284 n = strchr(data->pos,'\n');
1287 size = strlen(data->pos);
1289 n++; /* include newline in output */
1290 size = (n-data->pos);
1293 if (size > buf_size-1){
1298 strncpy(out,data->pos,size);
1307 icalcomponent* icalparser_parse_string(const char* str)
1313 icalerrorstate es = icalerror_get_error_state(ICAL_MALFORMEDDATA_ERROR);
1318 p = icalparser_new();
1319 icalparser_set_gen_data(p,&d);
1321 icalerror_set_error_state(ICAL_MALFORMEDDATA_ERROR,ICAL_ERROR_NONFATAL);
1323 c = icalparser_parse(p,icalparser_string_line_generator);
1325 icalerror_set_error_state(ICAL_MALFORMEDDATA_ERROR,es);