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, char **buf_value);
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, char **buf)
269 next = parser_get_next_char('=',line,1);
276 str = make_segment(line,next);
280 next = parser_get_next_char('"',*end,0);
286 *buf = *end = make_segment(*end,next);
294 char* parser_get_next_paramvalue(char* line, char **end)
299 next = parser_get_next_char(',',line,1);
302 next = (char*)(size_t)line+(size_t)strlen(line);\
308 str = make_segment(line,next);
315 char* icalparser_get_value(char* line, char **end, icalvalue_kind kind)
318 size_t length = strlen(line);
325 str = make_segment(line, *end);
331 A property may have multiple values, if the values are seperated by
332 commas in the content line. This routine will look for the next
333 comma after line and will set the next place to start searching in
337 char* parser_get_next_value(char* line, char **end, icalvalue_kind kind)
343 size_t length = strlen(line);
348 next = parser_get_next_char(',',p,1);
350 /* Unforunately, RFC2445 says that for the RECUR value, COMMA
351 can both separate digits in a list, and it can separate
352 multiple recurrence specifications. This is not a friendly
353 part of the spec. This weirdness tries to
354 distinguish the two uses. it is probably a HACK*/
356 if( kind == ICAL_RECUR_VALUE ) {
358 (*end+length) > next+5 &&
359 strncmp(next,"FREQ",4) == 0
361 /* The COMMA was followed by 'FREQ', is it a real separator*/
363 } else if (next != 0){
364 /* Not real, get the next COMMA */
370 /* ignore all , for query value. select dtstart, dtend etc ... */
371 else if( kind == ICAL_QUERY_VALUE) {
380 /* If the comma is preceded by a '\', then it is a literal and
381 not a value separator*/
383 if ( (next!=0 && *(next-1) == '\\') ||
384 (next!=0 && *(next-3) == '\\')
386 /*second clause for '/' is on prev line. HACK may be out of bounds */
396 next = (char*)(size_t)line+length;
407 str = make_segment(line,next);
413 char* parser_get_next_parameter(char* line,char** end)
419 v = parser_get_next_char(':',line,1);
420 next = parser_get_next_char(';', line,1);
422 /* There is no ';' or, it is after the ':' that marks the beginning of
425 if (next == 0 || next > v) {
426 next = parser_get_next_char(':', line,1);
430 str = make_segment(line,next);
441 * Get a single property line, from the property name through the
442 * final new line, and include any continuation lines
444 char* icalparser_get_line(icalparser *parser,
445 char* (*line_gen_func)(char *s, size_t size, void *d))
449 size_t buf_size = parser->tmp_buf_size;
451 line_p = line = icalmemory_new_buffer(buf_size);
454 /* Read lines by calling line_gen_func and putting the data into
455 parser->temp. If the line is a continuation line ( begins with a
456 space after a newline ) then append the data onto line and read
457 again. Otherwise, exit the loop. */
461 /* The first part of the loop deals with the temp buffer,
462 which was read on he last pass through the loop. The
463 routine is split like this because it has to read lone line
464 ahead to determine if a line is a continuation line. */
467 /* The tmp buffer is not clear, so transfer the data in it to the
468 output. This may be left over from a previous call */
469 if (parser->temp[0] != '\0' ) {
471 /* If the last position in the temp buffer is occupied,
472 mark the buffer as full. The means we will do another
473 read later, because the line is not finished */
474 if (parser->temp[parser->tmp_buf_size-1] == 0 &&
475 parser->temp[parser->tmp_buf_size-2] != '\n'&&
476 parser->temp[parser->tmp_buf_size-2] != 0 ){
477 parser->buffer_full = 1;
479 parser->buffer_full = 0;
482 /* Copy the temp to the output and clear the temp buffer. */
483 if(parser->continuation_line==1){
484 /* back up the pointer to erase the continuation characters */
485 parser->continuation_line = 0;
488 if ( *(line_p-1) == '\r'){
492 /* copy one space up to eliminate the leading space*/
493 icalmemory_append_string(&line,&line_p,&buf_size,
497 icalmemory_append_string(&line,&line_p,&buf_size,parser->temp);
500 parser->temp[0] = '\0' ;
503 parser->temp[parser->tmp_buf_size-1] = 1; /* Mark end of buffer */
505 /****** Here is where the routine gets string data ******************/
506 if ((*line_gen_func)(parser->temp,parser->tmp_buf_size,parser->line_gen_data)
507 ==0){/* Get more data */
509 /* If the first position is clear, it means we didn't get
510 any more data from the last call to line_ge_func*/
511 if (parser->temp[0] == '\0'){
514 /* There is data in the output, so fall trhough and process it*/
517 /* No data in output; return and signal that there
526 /* If the output line ends in a '\n' and the temp buffer
527 begins with a ' ' or tab, then the buffer holds a continuation
528 line, so keep reading. RFC 2445, section 4.1 */
530 if ( line_p > line+1 && *(line_p-1) == '\n'
531 && (parser->temp[0] == ' ' || parser->temp[0] == '\t') ) {
533 parser->continuation_line = 1;
535 } else if ( parser->buffer_full == 1 ) {
537 /* The buffer was filled on the last read, so read again */
541 /* Looks like the end of this content line, so break */
548 /* Erase the final newline and/or carriage return*/
549 if ( line_p > line+1 && *(line_p-1) == '\n') {
551 if ( *(line_p-2) == '\r'){
559 while ( (*line_p == '\0' || iswspace(*line_p)) && line_p > line )
569 static void insert_error(icalcomponent* comp, const char* text,
570 const char* message, icalparameter_xlicerrortype type)
575 snprintf(temp,1024,"%s:",message);
577 snprintf(temp,1024,"%s: %s",message,text);
580 icalcomponent_add_property
582 icalproperty_vanew_xlicerror(
584 icalparameter_new_xlicerrortype(type),
589 static int line_is_blank(char* line){
592 for(i=0; *(line+i)!=0; i++){
595 if(c != ' ' && c != '\n' && c != '\t'){
603 icalcomponent* icalparser_parse(icalparser *parser,
604 char* (*line_gen_func)(char *s, size_t size,
610 icalcomponent *root=0;
611 icalerrorstate es = icalerror_get_error_state(ICAL_MALFORMEDDATA_ERROR);
614 icalerror_check_arg_rz((parser !=0),"parser");
616 icalerror_set_error_state(ICAL_MALFORMEDDATA_ERROR,ICAL_ERROR_NONFATAL);
619 line = icalparser_get_line(parser, line_gen_func);
621 if ((c = icalparser_add_line(parser,line)) != 0){
623 if(icalcomponent_get_parent(c) !=0){
624 /* This is bad news... assert? */
627 assert(parser->root_component == 0);
628 assert(pvl_count(parser->components) ==0);
631 /* Just one component */
633 } else if(icalcomponent_isa(root) != ICAL_XROOT_COMPONENT) {
634 /*Got a second component, so move the two components under
635 an XROOT container */
636 icalcomponent *tempc = icalcomponent_new(ICAL_XROOT_COMPONENT);
637 icalcomponent_add_component(tempc, root);
638 icalcomponent_add_component(tempc, c);
640 } else if(icalcomponent_isa(root) == ICAL_XROOT_COMPONENT) {
641 /* Already have an XROOT container, so add the component
643 icalcomponent_add_component(root, c);
655 icalmemory_free_buffer(line);
660 icalerror_set_error_state(ICAL_MALFORMEDDATA_ERROR,es);
667 icalcomponent* icalparser_add_line(icalparser* parser,
674 icalproperty_kind prop_kind;
676 icalvalue_kind value_kind = ICAL_NO_VALUE;
679 icalerror_check_arg_rz((parser != 0),"parser");
684 parser->state = ICALPARSER_ERROR;
688 if(line_is_blank(line) == 1){
692 /* Begin by getting the property name at the start of the line. The
693 property name may end up being "BEGIN" or "END" in which case it
694 is not really a property, but the marker for the start or end of
698 str = parser_get_prop_name(line, &end);
700 if (str == 0 || *str == '\0' ){
701 /* Could not get a property name */
702 icalcomponent *tail = pvl_data(pvl_tail(parser->components));
705 insert_error(tail,line,
706 "Got a data line, but could not find a property name or component begin tag",
707 ICAL_XLICERRORTYPE_COMPONENTPARSEERROR);
710 parser->state = ICALPARSER_ERROR;
711 icalmemory_free_buffer(str);
716 /**********************************************************************
717 * Handle begin and end of components
718 **********************************************************************/
719 /* If the property name is BEGIN or END, we are actually
720 starting or ending a new component */
723 if(strcasecmp(str,"BEGIN") == 0){
725 icalcomponent_kind comp_kind;
727 icalmemory_free_buffer(str);
731 str = parser_get_next_value(end,&end, value_kind);
734 comp_kind = icalenum_string_to_component_kind(str);
737 if (comp_kind == ICAL_NO_COMPONENT){
740 c = icalcomponent_new(ICAL_XLICINVALID_COMPONENT);
741 insert_error(c,str,"Parse error in component name",
742 ICAL_XLICERRORTYPE_COMPONENTPARSEERROR);
745 c = icalcomponent_new(comp_kind);
748 c = icalcomponent_new(ICAL_XLICINVALID_COMPONENT);
749 insert_error(c,str,"Parse error in component name",
750 ICAL_XLICERRORTYPE_COMPONENTPARSEERROR);
753 pvl_push(parser->components,c);
755 parser->state = ICALPARSER_BEGIN_COMP;
757 icalmemory_free_buffer(str);
761 } else if (strcasecmp(str,"END") == 0 ) {
764 icalmemory_free_buffer(str);
768 str = parser_get_next_value(end,&end, value_kind);
770 /* Pop last component off of list and add it to the second-to-last*/
771 parser->root_component = pvl_pop(parser->components);
773 tail = pvl_data(pvl_tail(parser->components));
776 icalcomponent_add_component(tail,parser->root_component);
780 icalmemory_free_buffer(str);
783 /* Return the component if we are back to the 0th level */
784 if (parser->level == 0){
787 if(pvl_count(parser->components) != 0){
788 /* There are still components on the stack -- this means
789 that one of them did not have a proper "END" */
790 pvl_push(parser->components,parser->root_component);
791 icalparser_clean(parser); /* may reset parser->root_component*/
794 assert(pvl_count(parser->components) == 0);
796 parser->state = ICALPARSER_SUCCESS;
797 rtrn = parser->root_component;
798 parser->root_component = 0;
802 parser->state = ICALPARSER_END_COMP;
808 /* There is no point in continuing if we have not seen a
811 if(pvl_data(pvl_tail(parser->components)) == 0){
812 parser->state = ICALPARSER_ERROR;
813 icalmemory_free_buffer(str);
819 /**********************************************************************
820 * Handle property names
821 **********************************************************************/
823 /* At this point, the property name really is a property name,
824 (Not a component name) so make a new property and add it to
828 prop_kind = icalproperty_string_to_kind(str);
830 prop = icalproperty_new(prop_kind);
833 icalcomponent *tail = pvl_data(pvl_tail(parser->components));
835 if(prop_kind==ICAL_X_PROPERTY){
836 icalproperty_set_x_name(prop,str);
839 icalcomponent_add_property(tail, prop);
841 /* Set the value kind for the default for this type of
842 property. This may be re-set by a VALUE parameter */
843 value_kind = icalproperty_kind_to_value_kind(icalproperty_isa(prop));
846 icalcomponent* tail = pvl_data(pvl_tail(parser->components));
848 insert_error(tail,str,"Parse error in property name",
849 ICAL_XLICERRORTYPE_PROPERTYPARSEERROR);
852 parser->state = ICALPARSER_ERROR;
853 icalmemory_free_buffer(str);
858 icalmemory_free_buffer(str);
861 /**********************************************************************
862 * Handle parameter values
863 **********************************************************************/
865 /* Now, add any parameters to the last property */
869 if (*(end-1) == ':'){
870 /* if the last separator was a ":" and the value is a
871 URL, icalparser_get_next_parameter will find the
872 ':' in the URL, so better break now. */
876 str = parser_get_next_parameter(end,&end);
881 char *buf_value = NULL;
883 icalparameter *param = 0;
884 icalparameter_kind kind;
885 icalcomponent *tail = pvl_data(pvl_tail(parser->components));
887 name = parser_get_param_name(str,&pvalue,&buf_value);
890 /* 'tail' defined above */
891 insert_error(tail, str, "Cant parse parameter name",
892 ICAL_XLICERRORTYPE_PARAMETERNAMEPARSEERROR);
897 kind = icalparameter_string_to_kind(name);
899 if(kind == ICAL_X_PARAMETER){
900 param = icalparameter_new(ICAL_X_PARAMETER);
902 icalparameter_set_xname(param,name);
903 icalparameter_set_xvalue(param,pvalue);
905 icalmemory_free_buffer(buf_value);
907 } else if (kind == ICAL_IANA_PARAMETER){
908 ical_unknown_token_handling tokHandlingSetting =
909 ical_get_unknown_token_handling_setting();
910 if (tokHandlingSetting == ICAL_DISCARD_TOKEN)
912 param = icalparameter_new(ICAL_IANA_PARAMETER);
915 icalparameter_set_xname(param,name);
916 icalparameter_set_xvalue(param,pvalue);
918 icalmemory_free_buffer(buf_value);
921 } else if (kind != ICAL_NO_PARAMETER){
922 param = icalparameter_new_from_value_string(kind,pvalue);
924 icalmemory_free_buffer(buf_value);
928 /* Error. Failed to parse the parameter*/
929 /* 'tail' defined above */
931 icalmemory_free_buffer(buf_value);
934 /* Change for mozilla */
935 /* have the option of being flexible towards unsupported parameters */
936 #if ICAL_ERRORS_ARE_FATAL == 1
937 insert_error(tail, str, "Cant parse parameter name",
938 ICAL_XLICERRORTYPE_PARAMETERNAMEPARSEERROR);
940 parser->state = ICALPARSER_ERROR;
950 icalmemory_free_buffer(str);
958 icalmemory_free_buffer(str);
975 /* 'tail' defined above */
976 insert_error(tail,str,"Cant parse parameter value",
977 ICAL_XLICERRORTYPE_PARAMETERVALUEPARSEERROR);
980 parser->state = ICALPARSER_ERROR;
982 icalmemory_free_buffer(buf_value);
984 icalmemory_free_buffer(name);
986 icalmemory_free_buffer(str);
992 /* If it is a VALUE parameter, set the kind of value*/
993 if (icalparameter_isa(param)==ICAL_VALUE_PARAMETER){
995 value_kind = (icalvalue_kind)
996 icalparameter_value_to_value_kind(
997 icalparameter_get_value(param)
1000 if (value_kind == ICAL_NO_VALUE){
1002 /* Ooops, could not parse the value of the
1003 parameter ( it was not one of the defined
1004 values ), so reset the value_kind */
1008 "Got a VALUE parameter with an unknown type",
1009 ICAL_XLICERRORTYPE_PARAMETERVALUEPARSEERROR);
1012 icalproperty_kind_to_value_kind(
1013 icalproperty_isa(prop));
1015 icalparameter_free(param);
1017 parser->state = ICALPARSER_ERROR;
1019 icalmemory_free_buffer(name);
1021 icalmemory_free_buffer(str);
1026 icalmemory_free_buffer(name);
1029 /* Everything is OK, so add the parameter */
1030 icalproperty_add_parameter(prop,param);
1032 icalmemory_free_buffer(str);
1035 } else { /* if ( str != 0) */
1036 /* If we did not get a param string, go on to looking for a value */
1038 icalmemory_free_buffer(str);
1042 } /* if ( str != 0) */
1046 /**********************************************************************
1048 **********************************************************************/
1050 /* Look for values. If there are ',' characters in the values,
1051 then there are multiple values, so clone the current
1052 parameter and add one part of the value to each clone */
1056 /* Only some properies can have multiple values. This list was taken
1057 from rfc2445. Also added the x-properties, because the spec actually
1058 says that commas should be escaped. For x-properties, other apps may
1059 depend on that behaviour
1061 switch (prop_kind) {
1062 case ICAL_X_PROPERTY:
1063 case ICAL_CATEGORIES_PROPERTY:
1064 case ICAL_RESOURCES_PROPERTY:
1065 /* Referring to RFC 2445, section 4.8.5.3 and section 4.8.5.1:
1066 RDATE and EXDATE can specify a list of dates/date-times/periods.
1068 case ICAL_RDATE_PROPERTY:
1069 case ICAL_EXDATE_PROPERTY:
1070 /* Referring to RFC 2445, section 4.8.2.6 Free/Busy Time:
1071 The "FREEBUSY" property can specify more than one value, separated by
1072 the COMMA character (US-ASCII decimal 44).
1074 case ICAL_FREEBUSY_PROPERTY:
1075 str = parser_get_next_value(end,&end, value_kind);
1079 str = icalparser_get_value(end, &end, value_kind);
1087 /* Actually, only clone after the second value */
1088 icalproperty* clone = icalproperty_new_clone(prop);
1089 icalcomponent* tail = pvl_data(pvl_tail(parser->components));
1091 icalcomponent_add_property(tail, clone);
1096 /* If this is a URI value for an ATTACH property, then change
1097 the value to an ATTACH kind as well.
1098 Now we can support ATTACH;TYPE=URI:http://my.fav.url
1100 if(value_kind == ICAL_URI_VALUE && prop_kind == ICAL_ATTACH_PROPERTY){
1101 value_kind = ICAL_ATTACH_VALUE;
1103 value = icalvalue_new_from_string(value_kind, str);
1105 /* Don't add properties without value */
1107 char temp[200]; /* HACK */
1109 icalproperty_kind prop_kind = icalproperty_isa(prop);
1110 icalcomponent* tail = pvl_data(pvl_tail(parser->components));
1112 snprintf(temp,sizeof(temp),"Can't parse as %s value in %s property. Removing entire property",
1113 icalvalue_kind_to_string(value_kind),
1114 icalproperty_kind_to_string(prop_kind));
1116 insert_error(tail, str, temp,
1117 ICAL_XLICERRORTYPE_VALUEPARSEERROR);
1119 /* Remove the troublesome property */
1120 icalcomponent_remove_property(tail,prop);
1121 icalproperty_free(prop);
1124 parser->state = ICALPARSER_ERROR;
1126 icalmemory_free_buffer(str);
1132 icalproperty_set_value(prop, value);
1134 icalmemory_free_buffer(str);
1138 #if ICAL_ALLOW_EMPTY_PROPERTIES
1139 /* Don't replace empty properties with an error */
1143 char temp[200]; /* HACK */
1145 icalproperty_kind prop_kind = icalproperty_isa(prop);
1146 icalcomponent *tail = pvl_data(pvl_tail(parser->components));
1148 snprintf(temp,sizeof(temp),"No value for %s property. Removing entire property",
1149 icalproperty_kind_to_string(prop_kind));
1151 insert_error(tail, str, temp,
1152 ICAL_XLICERRORTYPE_VALUEPARSEERROR);
1154 /* Remove the troublesome property */
1155 icalcomponent_remove_property(tail,prop);
1156 icalproperty_free(prop);
1159 parser->state = ICALPARSER_ERROR;
1169 /****************************************************************
1170 * End of component parsing.
1171 *****************************************************************/
1173 if (pvl_data(pvl_tail(parser->components)) == 0 &&
1174 parser->level == 0){
1175 /* HACK. Does this clause ever get executed? */
1176 parser->state = ICALPARSER_SUCCESS;
1178 return parser->root_component;
1180 parser->state = ICALPARSER_IN_PROGRESS;
1186 icalparser_state icalparser_get_state(icalparser* parser)
1188 return parser->state;
1192 icalcomponent* icalparser_clean(icalparser* parser)
1194 icalcomponent *tail;
1196 icalerror_check_arg_rz((parser != 0 ),"parser");
1198 /* We won't get a clean exit if some components did not have an
1199 "END" tag. Clear off any component that may be left in the list */
1201 while((tail=pvl_data(pvl_tail(parser->components))) != 0){
1203 insert_error(tail," ",
1204 "Missing END tag for this component. Closing component at end of input.",
1205 ICAL_XLICERRORTYPE_COMPONENTPARSEERROR);
1208 parser->root_component = pvl_pop(parser->components);
1209 tail=pvl_data(pvl_tail(parser->components));
1211 if(tail != 0 && parser->root_component != NULL){
1212 if(icalcomponent_get_parent(parser->root_component)!=0){
1213 icalerror_warn("icalparser_clean is trying to attach a component for the second time");
1215 icalcomponent_add_component(tail,parser->root_component);
1221 return parser->root_component;
1231 char* icalparser_string_line_generator(char *out, size_t buf_size, void *d)
1235 struct slg_data* data = (struct slg_data*)d;
1238 data->pos=data->str;
1241 /* If the pointer is at the end of the string, we are done */
1242 if (*(data->pos)==0){
1246 n = strchr(data->pos,'\n');
1249 size = strlen(data->pos);
1251 n++; /* include newline in output */
1252 size = (n-data->pos);
1255 if (size > buf_size-1){
1260 strncpy(out,data->pos,size);
1269 icalcomponent* icalparser_parse_string(const char* str)
1275 icalerrorstate es = icalerror_get_error_state(ICAL_MALFORMEDDATA_ERROR);
1280 p = icalparser_new();
1281 icalparser_set_gen_data(p,&d);
1283 icalerror_set_error_state(ICAL_MALFORMEDDATA_ERROR,ICAL_ERROR_NONFATAL);
1285 c = icalparser_parse(p,icalparser_string_line_generator);
1287 icalerror_set_error_state(ICAL_MALFORMEDDATA_ERROR,es);