diff options
Diffstat (limited to 'libical/src/libical/icalparser.c')
-rw-r--r-- | libical/src/libical/icalparser.c | 901 |
1 files changed, 901 insertions, 0 deletions
diff --git a/libical/src/libical/icalparser.c b/libical/src/libical/icalparser.c new file mode 100644 index 0000000000..286c2a8c02 --- /dev/null +++ b/libical/src/libical/icalparser.c @@ -0,0 +1,901 @@ +/* -*- Mode: C -*- + ====================================================================== + FILE: icalparser.c + CREATOR: eric 04 August 1999 + + $Id$ + $Locker$ + + The contents of this file are subject to the Mozilla Public License + Version 1.0 (the "License"); you may not use this file except in + compliance with the License. You may obtain a copy of the License at + http://www.mozilla.org/MPL/ + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and + limitations under the License. + + The Original Code is eric. The Initial Developer of the Original + Code is + + (C) COPYRIGHT 1999 The Software Studio. + http://www.softwarestudio.org + + ======================================================================*/ +#include "ical.h" +#include "pvl.h" +#include "icalparser.h" +#include "icalmemory.h" +#include <string.h> /* For strncpy */ +#include <stdio.h> /* For FILE and fgets and sprintf */ +#include <stdlib.h> /* for free */ + + +extern icalvalue* icalparser_yy_value; +void set_parser_value_state(icalvalue_kind kind); +int yyparse(void); + +char* icalparser_get_next_char(char c, char *str); +char* icalparser_get_next_parameter(char* line,char** end); +char* icalparser_get_next_value(char* line, char **end, icalvalue_kind kind); +char* icalparser_get_prop_name(char* line, char** end); +char* icalparser_get_param_name(char* line, char **end); + +icalvalue* icalvalue_new_from_string_with_error(icalvalue_kind kind, char* str, icalproperty **error); + + + +char* icalparser_get_next_char(char c, char *str) +{ + int quote_mode = 0; + char* p; + + + for(p=str; *p!=0; p++){ + + if ( quote_mode == 0 && *p=='"' && *(p-1) != '\\' ){ + quote_mode =1; + continue; + } + + if ( quote_mode == 1 && *p=='"' && *(p-1) != '\\' ){ + quote_mode =0; + continue; + } + + if (quote_mode == 0 && *p== c && *(p-1) != '\\' ){ + return p; + } + + } + + return 0; +} + +/* make a new tmp buffer out of a substring */ +char* make_segment(char* start, char* end) +{ + char *buf; + size_t size = (size_t)end - (size_t)start; + + buf = icalmemory_tmp_buffer(size+1); + + + strncpy(buf,start,size); + *(buf+size) = 0; + + return buf; + +} + +char* input_buffer; +char* input_buffer_p; +#define min(a,b) ((a) < (b) ? (a) : (b)) + +int icalparser_flex_input(char* buf, int max_size) +{ + int n = min(max_size,strlen(input_buffer_p)); + + if (n > 0){ + memcpy(buf, input_buffer_p, n); + input_buffer_p += n; + return n; + } else { + return 0; + } +} + +void icalparser_clear_flex_input() +{ + input_buffer_p = input_buffer+strlen(input_buffer); +} + +/* Cal the flex parser to parse a complex value */ + +icalvalue* icalparser_parse_value(icalvalue_kind kind, + char* str, icalproperty** error) +{ + int r; + input_buffer_p = input_buffer = str; + + set_parser_value_state(kind); + icalparser_yy_value = 0; + + r = yyparse(); + + /* Error. Parse failed */ + if( icalparser_yy_value == 0 || r != 0){ + + if(icalparser_yy_value !=0){ + icalvalue_free(icalparser_yy_value); + icalparser_yy_value = 0; + } + + return 0; + } + + if (error != 0){ + *error = 0; + } + + return icalparser_yy_value; +} + +char* icalparser_get_prop_name(char* line, char** end) +{ + char* p; + char* v; + char *str; + + p = icalparser_get_next_char(';',line); + v = icalparser_get_next_char(':',line); + if (p== 0 && v == 0) { + return 0; + } + + /* There is no ';' or, it is after the ';' that marks the beginning of + the value */ + if (v!=0 && ( p == 0 || p > v)){ + str = make_segment(line,v); + *end = v+1; + } else { + str = make_segment(line,p); + *end = p+1; + } + + return str; +} + +char* icalparser_get_param_name(char* line, char **end) +{ + + char* next; + char *str; + + next = icalparser_get_next_char('=',line); + + if (next == 0) { + return 0; + } + + str = make_segment(line,next); + *end = next+1; + return str; + +} + +char* icalparser_get_next_paramvalue(char* line, char **end) +{ + + char* next; + char *str; + + next = icalparser_get_next_char(',',line); + + if (next == 0){ + next = (char*)(size_t)line+(size_t)strlen(line);\ + } + + if (next == line){ + return 0; + } else { + str = make_segment(line,next); + *end = next+1; + return str; + } + +} + +/* A property may have multiple values, if the values are seperated by + commas in the content line. This routine will look for the next + comma after line and will set the next place to start searching in + end. */ + +char* icalparser_get_next_value(char* line, char **end, icalvalue_kind kind) +{ + + char* next; + char *p; + char *str; + size_t length = strlen(line); + + p = line; + while(1){ + + next = icalparser_get_next_char(',',p); + + /* Unforunately, RFC2445 says that for the RECUR value, COMMA + can both seperate digits in a list, and it can seperate + multiple recurrence specifications. This is not a friendly + part of the spec. This weirdness tries to + distinguish the two uses. it is probably a HACK*/ + + if( kind == ICAL_RECUR_VALUE ) { + if ( next != 0 && + (*end+length) > next+5 && + strncmp(next,"FREQ",4) == 0 + ) { + /* The COMMA was followed by 'FREQ', is it a real seperator*/ + /* Fall through */ + printf("%s\n",next); + } else if (next != 0){ + /* Not real, get the next COMMA */ + p = next+1; + next = 0; + continue; + } + } + + /* If the comma is preceeded by a '\', then it is a literal and + not a value seperator*/ + + if ( (next!=0 && *(next-1) == '\\') || + (next!=0 && *(next-3) == '\\') + ) + /*second clause for '/' is on prev line. HACK may be out of bounds */ + { + p = next+1; + } else { + break; + } + + } + + if (next == 0){ + next = (char*)(size_t)line+length; + *end = next; + } else { + *end = next+1; + } + + if (next == line){ + return 0; + } + + + str = make_segment(line,next); + return str; + +} + +char* icalparser_get_next_parameter(char* line,char** end) +{ + char *next; + char *v; + char *str; + + v = icalparser_get_next_char(':',line); + next = icalparser_get_next_char(';', line); + + /* There is no ';' or, it is after the ':' that marks the beginning of + the value */ + + if (next == 0 || next > v) { + next = icalparser_get_next_char(':', line); + } + + if (next != 0) { + str = make_segment(line,next); + *end = next+1; + return str; + } else { + *end = line; + return 0; + } +} + +/* HACK. This is not threadsafe */ +int buffer_full=0; +size_t tmp_buf_size = 80; +char temp[80]; + + +/* Get a single property line, from the property name through the + final new line, and include any continuation lines */ + +char* icalparser_get_line(char* (*line_gen_func)(char *s, size_t size, void *d), int *lineno) +{ + char *line; + char *line_p; + size_t buf_size = tmp_buf_size; + + + line_p = line = icalmemory_new_buffer(buf_size); + line[0] = '\0'; + + while(1) { + + /* The buffer is not clear, so transfer the data in it to the + output. This may be left over from a previous call */ + if (temp[0] != '\0' ) { + if (temp[tmp_buf_size-1] == 0){ + buffer_full = 1; + } else { + buffer_full = 0; + } + icalmemory_append_string(&line,&line_p,&buf_size,temp); + temp[0] = '\0' ; + } + + temp[tmp_buf_size-1] = 1; /* Mark end of buffer */ + + if ((*line_gen_func)(temp,tmp_buf_size,0)==0){/* Get more data */ + if (temp[0] == '\0'){ + if(line[0] != '\0'){ + break; + } else { + free(line); + return 0; + } + } + } + + if ( line_p > line+1 && *(line_p-1) == '\n' && temp[0] == ' ') { + + /* If the output line ends in a '\n' and the temp buffer + begins with a ' ', then the buffer holds a continuation + line, so keep reading. */ + + /* back up the pointer to erase the continuation characters */ + line_p--; + + /* shift the temp buffer down to eliminate the leading space*/ + memmove(&(temp[0]),&(temp[1]),tmp_buf_size); + temp[tmp_buf_size-1] = temp[tmp_buf_size-2]; + + } else if ( buffer_full == 1 ) { + + /* The buffer was filled on the last read, so read again */ + + } else { + + /* Looks like the end of this content line, so break */ + break; + } + + + } + + /* Erase the final newline */ + if ( line_p > line+1 && *(line_p-1) == '\n') { + + *(line_p-1) = '\0'; + } else { + *(line_p) = '\0'; + } + + return line; + +} + +#if 0 +char* icalparser_old_get_line(char* (*line_gen_func)(char *s, size_t size, void *d), int *lineno) +{ + char *line, *output_line; + char *line_p; + char last_line = 0; + size_t buf_size = tmp_buf_size; + int break_flag = 0; + + line_p = line = icalmemory_new_buffer(buf_size); + + /* If the hold buffer is empty, read a line. Otherwise, use the data that + is still in 'temp'*/ + if (hold == 1){ + /* Do nothing */ + } else { + temp[tmp_buf_size-1] = 1; /* Mark end of buffer */ + (*line_gen_func)(temp,tmp_buf_size,0); + } + + /* Append the hold buffer or new line into the output */ + icalmemory_append_string(&line,&line_p,&buf_size,temp); + + if ( temp[tmp_buf_size-1] == 0 ) { /* Check if mark was overwritten */ + buffer_full = 1; + } else { + buffer_full = 0; + } + + /* Try to suck up any continuation lines */ + while(last_line == 0){ + + temp[tmp_buf_size-1] = 1; /* Mark end of buffer */ + if ((*line_gen_func)(temp,tmp_buf_size,0) == 0){ + /* No more lines -- we are finished */ + if (hold == 1) { + hold = 0; + break_flag = 1; + } else { + icalmemory_free_buffer(line); + return 0; + } + } else { + if ( temp[tmp_buf_size-1] == 0 ) { + buffer_full = 1; + } else { + buffer_full = 0; + } + } + + /* keep track of line numbers */ + if (lineno != 0){ + (*lineno)++; + } + + + /* Determine wether to copy the line to the output, or + save it in the hold buffer */ + + if ( line_p > line+1 && *(line_p-1) == '\n' && temp[0] == ' ') { + + /* If the last line ( in the 'line' string ) ends in a '\n' + and the current line begins with a ' ', then the current line + is a continuation line, so append it. */ + + /* back up the pointer to erase the continuation characters */ + line_p--; + icalmemory_append_string(&line,&line_p,&buf_size,&(temp[1])); + + hold = 0; + buffer_full= 0; + + } else if (buffer_full == 1) { + + /* The last line that was read filled up the read + buffer. Append it and read again */ + + icalmemory_append_string(&line,&line_p,&buf_size,temp); + + hold = 0; + + } else if (break_flag != 1 ){ + /* Nope -- the line was not a continuation line. + Save the line for the next call */ + + hold =1; + break; + } else { + break; + } + } + + /* Erase the final newline */ + if ( line_p > line+1 && *(line_p-1) == '\n') { + + *(line_p-1) = '\0'; + } + + output_line = icalmemory_tmp_copy(line); + icalmemory_free_buffer(line); + + return output_line; +} +#endif + +void insert_error(icalcomponent* comp, char* text, + char* message, icalparameter_xlicerrortype type) +{ + char temp[1024]; + + if (strlen(text) > 256) { + sprintf(temp,"%s: \'%256s...\'",message,text); + } else { + sprintf(temp,"%s: \'%s\'",message,text); + } + + icalcomponent_add_property + (comp, + icalproperty_vanew_xlicerror( + temp, + icalparameter_new_xlicerrortype(type), + 0)); +} + +icalcomponent* icalparser_parse(char* (*line_gen_func)(char *s, size_t size, void* d)) +{ + + char *line = 0; + char *p; + char *str; + char *end; + int lineno = 0; + + int vcount = 0; + + icalcomponent *root_component = 0; + icalcomponent *tail = 0; + icalproperty *prop; + icalvalue *value; + + icalvalue_kind value_kind; + + pvl_list components = pvl_newlist(); + + do { + + value_kind = ICAL_NO_VALUE; + + /* Get a single property line, from a property name through a + newline */ + if (line!=0){ + free(line); + } + + line = icalparser_get_line(line_gen_func,&lineno); + + if (line == 0){ + continue; + } + + end = 0; + + str = icalparser_get_prop_name(line, &end); + + if (str == 0){ + tail = pvl_data(pvl_tail(components)); + + if (tail){ + insert_error(tail,line, + "Got a data line, but could not find a property name or component begin tag", + ICAL_XLICERRORTYPE_COMPONENTPARSEERROR); + } + tail = 0; + continue; + } + +/********************************************************************** + * Handle begin and end of components + **********************************************************************/ + + + /* If the property name is BEGIN or END, we are actually + starting or ending a new component */ + + if(strcmp(str,"BEGIN") == 0){ + icalcomponent *c; ; + + str = icalparser_get_next_value(end,&end, value_kind); + + c = icalcomponent_new_from_string(str); + + if (c == 0){ + c = icalcomponent_new(ICAL_XLICINVALID_COMPONENT); + insert_error(c,str,"Parse error in component name", + ICAL_XLICERRORTYPE_COMPONENTPARSEERROR); + } + + pvl_push(components,c); + + continue; + } else if (strcmp(str,"END") == 0 ) { + + str = icalparser_get_next_value(end,&end, value_kind); + + root_component = pvl_pop(components); + + tail = pvl_data(pvl_tail(components)); + + if(tail != 0){ + icalcomponent_add_component(tail,root_component); + } + + tail = 0; + continue; + } + + + /* There is no point in continuing if we have not seen a + component yet */ + + if(pvl_data(pvl_tail(components)) == 0){ + continue; + } + + +/********************************************************************** + * Handle property names + **********************************************************************/ + /* At this point, the property name really is a property name, + (Not a component name) so make a new property and add it to + the component */ + + prop = icalproperty_new_from_string(str); + + if (prop != 0){ + tail = pvl_data(pvl_tail(components)); + + icalcomponent_add_property(tail, prop); + + /* Set the value kind for the default for this type of + property. This may be re-set by a VALUE parameter */ + value_kind = + icalenum_property_kind_to_value_kind( + icalproperty_isa(prop)); + } else { + icalcomponent* tail = pvl_data(pvl_tail(components)); + + insert_error(tail,str,"Parse error in property name", + ICAL_XLICERRORTYPE_PROPERTYPARSEERROR); + + tail = 0; + continue; + } + +/********************************************************************** + * Handle parameter values + **********************************************************************/ + + /* Now, add any parameters to the last property */ + + p = 0; + while(1) { + + if (*(end-1) == ':'){ + /* if the last seperator was a ":" and the value is a + URL, icalparser_get_next_parameter will find the + ':' in the URL, so better break now. */ + break; + } + + str = icalparser_get_next_parameter(end,&end); + + if (str != 0){ + char* name; + char* pvalue; + icalparameter *param = 0; + icalparameter_kind kind; + + tail = pvl_data(pvl_tail(components)); + + name = icalparser_get_param_name(str,&pvalue); + + if (name == 0){ + insert_error(tail, str, "Can't parse parameter name", + ICAL_XLICERRORTYPE_PARAMETERPARSEERROR); + tail = 0; + break; + } + + kind = icalenum_string_to_parameter_kind(name); + if (kind != ICAL_NO_PARAMETER){ + param = icalparameter_new_from_string(kind,pvalue); + } else { + + /* Error. Failed to parse the parameter*/ + insert_error(tail, str, "Can't parse parameter name", + ICAL_XLICERRORTYPE_PARAMETERPARSEERROR); + tail = 0; + continue; + } + + if (param == 0){ + insert_error(tail,str,"Can't parse parameter value", + ICAL_XLICERRORTYPE_PARAMETERPARSEERROR); + + tail = 0; + continue; + } + + /* If it is a VALUE parameter, set the kind of value*/ + if (icalparameter_isa(param)==ICAL_VALUE_PARAMETER){ + + value_kind = (icalvalue_kind) + icalparameter_get_value(param); + + if (value_kind == ICAL_NO_VALUE){ + + /* Ooops, could not parse the value of the + parameter ( it was not one of the defined + values ), so reset the value_kind */ + + icalcomponent* tail + = pvl_data(pvl_tail(components)); + + insert_error( + tail, str, + "Got a VALUE parameter with an unknown type", + ICAL_XLICERRORTYPE_PARAMETERPARSEERROR); + icalparameter_free(param); + + value_kind = + icalenum_property_kind_to_value_kind( + icalproperty_isa(prop)); + + icalparameter_free(param); + tail = 0; + continue; + } + } + + /* Everything is OK, so add the parameter */ + icalproperty_add_parameter(prop,param); + tail = 0; + + } else { + /* If we did not get a param string, go on to looking + for a value */ + break; + } + } + +/********************************************************************** + * Handle values + **********************************************************************/ + + /* Look for values. If there are ',' characters in the values, + then there are multiple values, so clone the current + parameter and add one part of the value to each clone */ + + vcount=0; + while(1) { + str = icalparser_get_next_value(end,&end, value_kind); + + if (str != 0){ + + if (vcount > 0){ + /* Actually, only clone after the second value */ + icalproperty* clone = icalproperty_new_clone(prop); + tail = pvl_data(pvl_tail(components)); + + icalcomponent_add_property(tail, clone); + prop = clone; + tail = 0; + } + + value = icalvalue_new_from_string(value_kind, str); + + /* Don't add properties without value */ + if (value == 0){ + char temp[1024]; + + icalproperty_kind prop_kind = icalproperty_isa(prop); + tail = pvl_data(pvl_tail(components)); + + sprintf(temp,"Can't parse as %s value in %s property. Removing entire property", + icalenum_value_kind_to_string(value_kind), + icalenum_property_kind_to_string(prop_kind)); + + insert_error(tail, str, temp, + ICAL_XLICERRORTYPE_VALUEPARSEERROR); + + /* Remove the troublesome property */ + icalcomponent_remove_property(tail,prop); + icalproperty_free(prop); + prop = 0; + tail = 0; + break; + + } else { + vcount++; + icalproperty_set_value(prop, value); + } + + + } else { + + break; + } + } + + } while( !feof(stdin) && line !=0 ); + + + if (pvl_data(pvl_tail(components)) == 0){ + /* A nice, clean exit */ + pvl_free(components); + free(line); + return root_component; + } + + /* Clear off any component that may be left in the list */ + /* This will happen if some components did not have an "END" tag*/ + + while((tail=pvl_data(pvl_tail(components))) != 0){ + + insert_error(tail," ", + "Missing END tag for this component. Closing component at end of input.", + ICAL_XLICERRORTYPE_COMPONENTPARSEERROR); + + + root_component = pvl_pop(components); + tail=pvl_data(pvl_tail(components)); + + if(tail != 0){ + icalcomponent_add_component(tail,root_component); + } + } + + free(line); + return root_component; +} + +char* string_line_generator_pos=0; +char* string_line_generator_str; +char* string_line_generator(char *out, size_t buf_size, void *d) +{ + char *n; + + if(string_line_generator_pos==0){ + string_line_generator_pos=string_line_generator_str; + } + + /* If the pointer is at the end of the string, we are done */ + if (*string_line_generator_pos ==0){ + return 0; + } + + n = strchr(string_line_generator_pos,'\n'); + + /* If no newline, take the rest of the string, and leave the + pointer at the \0 */ + + if (n == 0) { + n = string_line_generator_pos + strlen(string_line_generator_pos); + } else { + n++; + } + + strncpy(out,string_line_generator_pos,(n-string_line_generator_pos)); + + *(out+(n-string_line_generator_pos)) = '\0'; + + string_line_generator_pos = n; + + return out; + +} + +void _test_string_line_generator(char* str) +{ + char *line; + int lineno=0; + string_line_generator_str = str; + string_line_generator_pos = 0; + + while((line = icalparser_get_line(string_line_generator,&lineno))){ + printf("#%d: %s\n",lineno,line); + } + + + string_line_generator_pos = 0; + string_line_generator_str = 0; +} + + + +icalcomponent* icalparser_parse_string(char* str) +{ + + icalcomponent *c; + + string_line_generator_str = str; + string_line_generator_pos = 0; + c = icalparser_parse(string_line_generator); + string_line_generator_pos = 0; + string_line_generator_str = 0; + + return c; + +} |