aboutsummaryrefslogtreecommitdiffstats
path: root/libical/src/libical/icalparser.c
diff options
context:
space:
mode:
Diffstat (limited to 'libical/src/libical/icalparser.c')
-rw-r--r--libical/src/libical/icalparser.c901
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;
+
+}