b4783343a64232ded573ff0466a15cc28f7b80ea
[platform/upstream/libical.git] / src / libical / icalvalue.c
1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vi:set ts=4 sts=4 sw=4 expandtab : */
3 /*======================================================================
4   FILE: icalvalue.c
5   CREATOR: eric 02 May 1999
6   
7   $Id: icalvalue.c,v 1.44 2008-01-15 23:17:43 dothebart Exp $
8
9
10  (C) COPYRIGHT 2000, Eric Busboom <eric@softwarestudio.org>
11      http://www.softwarestudio.org
12
13  This program is free software; you can redistribute it and/or modify
14  it under the terms of either: 
15
16     The LGPL as published by the Free Software Foundation, version
17     2.1, available at: http://www.fsf.org/copyleft/lesser.html
18
19   Or:
20
21     The Mozilla Public License Version 1.0. You may obtain a copy of
22     the License at http://www.mozilla.org/MPL/
23
24   The original code is icalvalue.c
25
26   Contributions from:
27      Graham Davison <g.m.davison@computer.org>
28
29
30 ======================================================================*/
31
32 #ifdef HAVE_CONFIG_H
33 #include <config.h>
34 #endif
35
36 #include "icalerror.h"
37 #include "icalmemory.h"
38 #include "icalparser.h"
39 #include "icalenums.h"
40 #include "icalvalueimpl.h"
41
42 #include <stdlib.h> /* for malloc */
43 #include <stdio.h> /* for snprintf */
44 #include <string.h> /* For memset, others */
45 #include <stddef.h> /* For offsetof() macro */
46 #include <errno.h>
47 #include <time.h> /* for mktime */
48 #include <stdlib.h> /* for atoi and atof */
49 #include <limits.h> /* for SHRT_MAX */         
50 #include <locale.h>
51 #include <ctype.h> /* for isspace and isdigit */
52
53 #if defined(_MSC_VER)
54 #define snprintf _snprintf
55 #define strcasecmp stricmp
56 #endif
57
58 #if _MAC_OS_
59 #include "icalmemory_strdup.h"
60 #endif
61
62 #define TMP_BUF_SIZE 1024
63
64 void print_datetime_to_string(char* str,  const struct icaltimetype *data);
65 void print_date_to_string(char* str,  const struct icaltimetype *data);
66 void print_time_to_string(char* str,  const struct icaltimetype *data);
67
68
69 struct icalvalue_impl*  icalvalue_new_impl(icalvalue_kind kind){
70
71     struct icalvalue_impl* v;
72
73     if (!icalvalue_kind_is_valid(kind))
74       return NULL;
75
76     if ( ( v = (struct icalvalue_impl*)
77            malloc(sizeof(struct icalvalue_impl))) == 0) {
78         icalerror_set_errno(ICAL_NEWFAILED_ERROR);
79         return 0;
80     }
81     
82     strcpy(v->id,"val");
83     
84     v->kind = kind;
85     v->size = 0;
86     v->parent = 0;
87     v->x_value = 0;
88     memset(&(v->data),0,sizeof(v->data));
89     
90     return v;
91
92 }
93
94
95
96 icalvalue*
97 icalvalue_new (icalvalue_kind kind)
98 {
99     return (icalvalue*)icalvalue_new_impl(kind);
100 }
101
102 icalvalue* icalvalue_new_clone(const icalvalue* old) {
103     struct icalvalue_impl* new;
104
105     new = icalvalue_new_impl(old->kind);
106
107     if (new == 0){
108         return 0;
109     }
110
111     strcpy(new->id, old->id);
112     new->kind = old->kind;
113     new->size = old->size;
114
115     switch (new->kind){
116         case ICAL_ATTACH_VALUE: 
117         case ICAL_BINARY_VALUE: 
118         {
119             /* Hmm.  We just ref the attach value, which may not be the right
120              * thing to do.  We cannot quite copy the data, anyways, since we
121              * don't know how long it is.
122              */
123             new->data.v_attach = old->data.v_attach;
124             if (new->data.v_attach)
125                 icalattach_ref (new->data.v_attach);
126
127             break;
128         }
129         case ICAL_QUERY_VALUE:
130         case ICAL_STRING_VALUE:
131         case ICAL_TEXT_VALUE:
132         case ICAL_CALADDRESS_VALUE:
133         case ICAL_URI_VALUE:
134         {
135             if (old->data.v_string != 0) { 
136                 new->data.v_string=icalmemory_strdup(old->data.v_string);
137
138                 if ( new->data.v_string == 0 ) {
139                     icalvalue_free(new);
140                     return 0;
141                 }                   
142
143             }
144             break;
145         }
146         case ICAL_RECUR_VALUE:
147         {
148             if(old->data.v_recur != 0){
149                 new->data.v_recur = malloc(sizeof(struct icalrecurrencetype));
150
151                 if(new->data.v_recur == 0){
152                     icalvalue_free(new);
153                     return 0;
154                 }
155
156                 memcpy( new->data.v_recur, old->data.v_recur,
157                         sizeof(struct icalrecurrencetype));     
158             }
159             break;
160         }
161
162         case ICAL_X_VALUE: 
163         {
164             if (old->x_value != 0) {
165                 new->x_value=icalmemory_strdup(old->x_value);
166
167                 if (new->x_value == 0) {
168                     icalvalue_free(new);
169                     return 0;
170                 }
171             }
172
173             break;
174         }
175
176         default:
177         {
178             /* all of the other types are stored as values, not
179                pointers, so we can just copy the whole structure. */
180
181             new->data = old->data;
182         }
183     }
184
185     return new;
186 }
187
188 static char* icalmemory_strdup_and_dequote(const char* str)
189 {
190     const char* p;
191     char* out = (char*)malloc(sizeof(char) * strlen(str) +1);
192     char* pout;
193
194     if (out == 0){
195         return 0;
196     }
197
198     pout = out;
199
200     for (p = str; *p!=0; p++){
201         
202         if( *p == '\\')
203         {
204             p++;
205             switch(*p){
206                 case 0:
207                 {
208                     *pout = '\0';
209                     break;
210
211                 }
212                 case 'n':
213                 case 'N':
214                 {
215                     *pout = '\n';
216                     break;
217                 }
218                 case 't':
219                 case 'T':
220                 {
221                     *pout = '\t';
222                     break;
223                 }
224                 case 'r':
225                 case 'R':
226                 {
227                     *pout = '\r';
228                     break;
229                 }
230                 case 'b':
231                 case 'B':
232                 {
233                     *pout = '\b';
234                     break;
235                 }
236                 case 'f':
237                 case 'F':
238                 {
239                     *pout = '\f';
240                     break;
241                 }
242                 case ';':
243                 case ',':
244                 case '"':
245                 case '\\':
246                 {
247                     *pout = *p;
248                     break;
249                 }
250                 default:
251                 {
252                     *pout = ' ';
253                 }               
254             }
255         } else {
256             *pout = *p;
257         }
258
259         pout++;
260         
261     }
262
263     *pout = '\0';
264
265     return out;
266 }
267
268 /*
269  * Returns a quoted copy of a string
270  * @todo This is not RFC2445 compliant.
271  * The RFC only allows:
272  * TSAFE-CHAR = %x20-21 / %x23-2B / %x2D-39 / %x3C-5B / %x5D-7E / NON-US-ASCII
273  * As such, \t\r\b\f are not allowed, not even escaped
274  */
275 static char* icalmemory_strdup_and_quote(const icalvalue* value, 
276                                          const char* unquoted_str)
277 {
278     char *str;
279     char *str_p;
280     const char *p;
281     size_t buf_sz;
282
283     buf_sz = strlen(unquoted_str)+1;
284
285     str_p = str = (char*)icalmemory_new_buffer(buf_sz);
286
287     if (str_p == 0){
288       return 0;
289     }
290
291     for(p=unquoted_str; *p!=0; p++){
292
293         switch(*p){
294             case '\n': {
295             icalmemory_append_string(&str,&str_p,&buf_sz,"\\n");
296             break;
297             }
298
299             case '\t': {
300             icalmemory_append_string(&str,&str_p,&buf_sz,"\\t");
301             break;
302             }
303             case '\r': {
304             icalmemory_append_string(&str,&str_p,&buf_sz,"\\r");
305             break;
306             }
307             case '\b': {
308             icalmemory_append_string(&str,&str_p,&buf_sz,"\\b");
309             break;
310             }
311             case '\f': {
312             icalmemory_append_string(&str,&str_p,&buf_sz,"\\f");
313             break;
314             }
315
316             case ';':
317         case ',':
318             /* unescaped COMMA is allowed in CATEGORIES property as its
319                considered a list delimiter here, see:
320                http://tools.ietf.org/html/rfc2445#section-4.3.11 */
321             if (icalproperty_isa(value->parent) == ICAL_CATEGORIES_PROPERTY) {
322                 icalmemory_append_char(&str,&str_p,&buf_sz,*p);
323                 break;
324             }
325             case '"':
326             case '\\': {
327             icalmemory_append_char(&str,&str_p,&buf_sz,'\\');
328             icalmemory_append_char(&str,&str_p,&buf_sz,*p);
329             break;
330             }
331
332             default: {
333             icalmemory_append_char(&str,&str_p,&buf_sz,*p);
334             }
335         }
336     }
337
338     /* Assume the last character is not a '\0' and add one. We could
339        check *str_p != 0, but that would be an uninitialized memory
340        read. */
341     
342     icalmemory_append_char(&str,&str_p,&buf_sz,'\0');
343     return str;
344 }
345
346 /*
347  * FIXME
348  *
349  * This is a bad API, as it forces callers to specify their own X type.
350  * This function should take care of this by itself.
351  */
352 static
353 icalvalue* icalvalue_new_enum(icalvalue_kind kind, int x_type, const char* str)
354 {
355     int e = icalproperty_kind_and_string_to_enum(kind, str);
356     struct icalvalue_impl *value; 
357
358     if(e != 0 && icalproperty_enum_belongs_to_property(
359                    icalproperty_value_kind_to_kind(kind),e)) {
360         
361         value = icalvalue_new_impl(kind);
362         value->data.v_enum = e;
363     } else {
364         /* Make it an X value */
365         value = icalvalue_new_impl(kind);
366         value->data.v_enum = x_type;
367         icalvalue_set_x(value,str);
368     }
369
370     return value;
371 }
372
373 /**
374  * Transforms a simple float number string into a double.
375  * The decimal separator (if any) of the double has to be '.'
376  * The code is locale *independant* and does *not* change the locale.
377  * It should be thread safe.
378  * If you want a code that that does the same job with a decimal separator
379  * dependant on the current locale, then use strtof() from libc.
380  */
381 int simple_str_to_double(const char* from,
382                          double *result,
383                          char** to)
384 {
385 #define TMP_NUM_SIZE 100
386     char *start=NULL, *end=NULL, *cur=(char*)from ;
387     char tmp_buf[TMP_NUM_SIZE+1] ; /*hack*/
388 #ifndef _WIN32_WCE
389     struct lconv *loc_data = localeconv () ;
390 #endif
391     int i=0 ;
392
393     /*sanity checks*/
394     if (!from || !result) {
395         return 1 ;
396     }
397
398     /*skip the white spaces at the beginning*/
399     while (cur && isspace (*cur))
400         cur++ ;
401
402     start = cur ;
403     /*
404      * copy the part that looks like a double into tmp_buf
405      * so that we can call strtof() on it.
406      * during the copy, we give ourselves a chance to convert the '.'
407      * into the decimal separator of the current locale.
408      */
409     while (cur && (isdigit (*cur) ||
410                    *cur == '.'    ||
411                    *cur == '+'    ||
412                    *cur == '-')){
413         ++cur ;
414     }
415     end = cur ;
416     if (end - start + 1> 100) {
417         /*huh hoh, number is too big. getting out*/
418         return 1 ;
419     }
420     memset(tmp_buf, 0, TMP_NUM_SIZE+1) ;
421     i=0 ;
422     /*
423      * copy the float number string into tmp_buf, and take
424      * care to have the (optional) decimal separator be the one
425      * of the current locale.
426      */
427 #ifndef _WIN32_WCE
428     for (i=0 ; i < end - from ;++i) {
429         if (start[i] == '.'
430             && loc_data
431             && loc_data->decimal_point
432             && loc_data->decimal_point[0]
433             && loc_data->decimal_point[0] != '.') {
434             /*replace '.' by the digit separator of the current locale*/
435             tmp_buf[i] = loc_data->decimal_point[0] ;
436         } else {
437             tmp_buf[i] = start[i] ;
438         }
439     }
440 #else
441     GetNumberFormat(LOCALE_SYSTEM_DEFAULT,0,start, NULL, tmp_buf,TMP_NUM_SIZE);
442 #endif
443     if (to)
444         *to = end ;
445     *result = atof(tmp_buf) ;
446     return 0 ;
447 }
448
449 icalvalue* icalvalue_new_from_string_with_error(icalvalue_kind kind,const char* str,icalproperty** error)
450 {
451
452     struct icalvalue_impl *value = 0;
453
454     icalerror_check_arg_rz(str!=0,"str");
455
456     if (error != 0){
457         *error = 0;
458     }
459
460     switch (kind){
461         
462     case ICAL_ATTACH_VALUE:
463         {
464             icalattach *attach;
465
466             attach = icalattach_new_from_url (str);
467             if (!attach)
468                 break;
469
470             value = icalvalue_new_attach (attach);
471             icalattach_unref (attach);
472             break;
473         }
474
475     case ICAL_BINARY_VALUE:
476     {
477         icalattach *attach;
478         attach = icalattach_new_from_data (str, 0, 0);
479         if ( !attach )
480           break;
481         value = icalvalue_new_attach (attach);
482         icalattach_unref (attach);
483         break;
484     }
485     case ICAL_BOOLEAN_VALUE:
486         {
487             /* HACK */
488             value = 0;
489             
490             if (error != 0){
491                 char temp[TMP_BUF_SIZE];
492                 snprintf(temp,sizeof(temp),"%s Values are not implemented",
493                         icalvalue_kind_to_string(kind)); 
494                 *error = icalproperty_vanew_xlicerror( 
495                                    temp, 
496                                    icalparameter_new_xlicerrortype( 
497                                         ICAL_XLICERRORTYPE_VALUEPARSEERROR), 
498                                    0); 
499             }
500             break;
501         }
502         
503
504     case ICAL_TRANSP_VALUE:
505         value = icalvalue_new_enum(kind, (int)ICAL_TRANSP_X,str);
506         break;
507     case ICAL_METHOD_VALUE:
508         value = icalvalue_new_enum(kind, (int)ICAL_METHOD_X,str);
509         break;
510     case ICAL_STATUS_VALUE:
511         value = icalvalue_new_enum(kind, (int)ICAL_STATUS_X,str);
512         break;
513     case ICAL_ACTION_VALUE:
514         value = icalvalue_new_enum(kind, (int)ICAL_ACTION_X,str);
515         break;
516
517     case ICAL_QUERY_VALUE:
518             value = icalvalue_new_query(str);
519             break;
520
521     case ICAL_CLASS_VALUE:
522         value = icalvalue_new_enum(kind, (int)ICAL_CLASS_X,str);
523         break;
524     case ICAL_CMD_VALUE:
525         value = icalvalue_new_enum(kind, ICAL_CMD_X,str);
526         break;
527     case ICAL_QUERYLEVEL_VALUE:
528         value = icalvalue_new_enum(kind, ICAL_QUERYLEVEL_X,str);
529         break;
530     case ICAL_CARLEVEL_VALUE:
531         value = icalvalue_new_enum(kind, ICAL_CARLEVEL_X,str);
532         break;
533
534     case ICAL_INTEGER_VALUE:
535             value = icalvalue_new_integer(atoi(str));
536             break;
537
538     case ICAL_FLOAT_VALUE:
539             value = icalvalue_new_float((float)atof(str));
540             break;
541
542     case ICAL_UTCOFFSET_VALUE:
543         {
544             int t,utcoffset, hours, minutes, seconds;
545             /* treat the UTCOFSET string a a decimal number, disassemble its digits
546                and reconstruct it as sections */
547             t = strtol(str,0,10);
548             /* add phantom seconds field */
549             if(abs(t)<9999){t *= 100; }
550             hours = (t/10000);
551             minutes = (t-hours*10000)/100;
552             seconds = (t-hours*10000-minutes*100);
553             utcoffset = hours*3600+minutes*60+seconds;
554
555             value = icalvalue_new_utcoffset(utcoffset);
556
557             break;
558         }
559         
560     case ICAL_TEXT_VALUE:
561         {
562             char* dequoted_str = icalmemory_strdup_and_dequote(str);
563             value = icalvalue_new_text(dequoted_str);
564             free(dequoted_str);
565             break;
566         }
567         
568     case ICAL_STRING_VALUE:
569             value = icalvalue_new_string(str);
570             break;
571         
572     case ICAL_CALADDRESS_VALUE:
573             value = icalvalue_new_caladdress(str);
574             break;
575         
576     case ICAL_URI_VALUE:
577             value = icalvalue_new_uri(str);
578             break;
579         
580     case ICAL_GEO_VALUE:
581     {
582         char *cur=NULL ;
583         struct icalgeotype geo = {0.0, 0.0};
584   
585         if (simple_str_to_double (str, &geo.lat, &cur)) {
586             goto geo_parsing_error ;
587         }
588   
589         /*skip white spaces*/
590         while (cur && isspace (*cur)) {
591             ++cur ;
592         }
593
594         /*there is a ';' between the latitude and longitude parts*/
595         if (!cur || *cur != ';') {
596             goto geo_parsing_error ;
597         }
598
599         ++cur ;
600         if (!cur)
601             goto geo_parsing_error ;
602
603         /*skip white spaces*/
604         while (cur && isspace (*cur)) {
605             ++cur ;
606         }
607
608         if (simple_str_to_double (cur, &geo.lon, &cur)) {
609             goto geo_parsing_error ;
610         }
611         value = icalvalue_new_geo (geo) ;
612         break ;
613
614 geo_parsing_error:
615         if (error != 0){
616             char temp[TMP_BUF_SIZE];
617             sprintf(temp, "Could not parse %s as a %s property",
618                     str, icalvalue_kind_to_string(kind));
619             *error = icalproperty_vanew_xlicerror(
620                                    temp,
621                                    icalparameter_new_xlicerrortype(
622                                         ICAL_XLICERRORTYPE_VALUEPARSEERROR),
623                                    0);
624         }
625     }
626             break;
627         
628     case ICAL_RECUR_VALUE:
629         {
630             struct icalrecurrencetype rt;
631             rt = icalrecurrencetype_from_string(str);
632             if(rt.freq != ICAL_NO_RECURRENCE){
633                 value = icalvalue_new_recur(rt);
634             }
635             break;
636         }
637         
638     case ICAL_DATE_VALUE:
639     case ICAL_DATETIME_VALUE:
640         {
641             struct icaltimetype tt;
642       
643             tt = icaltime_from_string(str);
644             if(!icaltime_is_null_time(tt)){
645                 value = icalvalue_new_impl(kind);
646                 value->data.v_time = tt;
647
648                 icalvalue_reset_kind(value);
649             }
650             break;
651         }
652         
653     case ICAL_DATETIMEPERIOD_VALUE:
654         {
655             struct icaltimetype tt;
656             struct icalperiodtype p;
657             tt = icaltime_from_string(str);
658
659             if(!icaltime_is_null_time(tt)){
660                 value = icalvalue_new_datetime(tt);             
661                 break;
662             }  
663             
664             p = icalperiodtype_from_string(str);            
665             if (!icalperiodtype_is_null_period(p)){
666                 value = icalvalue_new_period(p);
667             }            
668
669             break;
670         }
671         
672     case ICAL_DURATION_VALUE:
673         {
674             struct icaldurationtype dur = icaldurationtype_from_string(str);
675             
676             if (!icaldurationtype_is_bad_duration(dur)) {    /* failed to parse */
677                 value = icalvalue_new_duration(dur);
678             }
679             
680             break;
681         }
682         
683     case ICAL_PERIOD_VALUE:
684         {
685             struct icalperiodtype p;
686             p = icalperiodtype_from_string(str);  
687             
688             if(!icalperiodtype_is_null_period(p)){
689                 value = icalvalue_new_period(p);
690             }
691             break; 
692         }
693         
694     case ICAL_TRIGGER_VALUE:
695         {
696             struct icaltriggertype tr = icaltriggertype_from_string(str);
697             if (!icaltriggertype_is_bad_trigger(tr)) {
698                 value = icalvalue_new_trigger(tr);
699             }
700             break;
701         }
702         
703     case ICAL_REQUESTSTATUS_VALUE:
704         {
705             struct icalreqstattype rst = icalreqstattype_from_string(str);
706             if(rst.code != ICAL_UNKNOWN_STATUS){
707                 value = icalvalue_new_requeststatus(rst);
708             }
709             break;
710
711         }
712
713     case ICAL_X_VALUE:
714         {
715             char* dequoted_str = icalmemory_strdup_and_dequote(str);
716             value = icalvalue_new_x(dequoted_str);
717             free(dequoted_str);
718         }
719         break;
720
721     default:
722         {
723             if (error != 0 ){
724                 char temp[TMP_BUF_SIZE];
725                 
726                 snprintf(temp,TMP_BUF_SIZE,"Unknown type for \'%s\'",str);
727                             
728                 *error = icalproperty_vanew_xlicerror( 
729                     temp, 
730                     icalparameter_new_xlicerrortype( 
731                         ICAL_XLICERRORTYPE_VALUEPARSEERROR), 
732                     0); 
733             }
734
735             icalerror_warn("icalvalue_new_from_string got an unknown value type");
736             value=0;
737         }
738     }
739
740
741     if (error != 0 && *error == 0 && value == 0){
742         char temp[TMP_BUF_SIZE];
743         
744         snprintf(temp,TMP_BUF_SIZE,"Failed to parse value: \'%s\'",str);
745         
746         *error = icalproperty_vanew_xlicerror( 
747             temp, 
748             icalparameter_new_xlicerrortype( 
749                 ICAL_XLICERRORTYPE_VALUEPARSEERROR), 
750             0); 
751     }
752
753
754     return value;
755
756 }
757
758 icalvalue* icalvalue_new_from_string(icalvalue_kind kind,const char* str)
759 {
760     return icalvalue_new_from_string_with_error(kind,str,(icalproperty**)0);
761 }
762
763
764
765 void
766 icalvalue_free (icalvalue* v)
767 {
768     icalerror_check_arg_rv((v != 0),"value");
769
770 #ifdef ICAL_FREE_ON_LIST_IS_ERROR
771     icalerror_assert( (v->parent ==0),"This value is still attached to a property");
772     
773 #else
774     if(v->parent !=0){
775         return;
776     }
777 #endif
778
779     if(v->x_value != 0){
780         free(v->x_value);
781     }
782
783     switch (v->kind){
784         case ICAL_BINARY_VALUE: 
785         case ICAL_ATTACH_VALUE: {
786             if (v->data.v_attach) {
787                 icalattach_unref (v->data.v_attach);
788                 v->data.v_attach = NULL;
789             }
790
791             break;
792         }
793         case ICAL_TEXT_VALUE:
794         case ICAL_CALADDRESS_VALUE:
795         case ICAL_URI_VALUE:
796         case ICAL_STRING_VALUE:
797         case ICAL_QUERY_VALUE:
798         {
799             if (v->data.v_string != 0) { 
800                 free((void*)v->data.v_string);
801                 v->data.v_string = 0;
802             }
803             break;
804         }
805         case ICAL_RECUR_VALUE:
806         {
807             if(v->data.v_recur != 0){
808                 free((void*)v->data.v_recur);
809                 v->data.v_recur = 0;
810             }
811             break;
812         }
813
814         default:
815         {
816             /* Nothing to do */
817         }
818     }
819
820     v->kind = ICAL_NO_VALUE;
821     v->size = 0;
822     v->parent = 0;
823     memset(&(v->data),0,sizeof(v->data));
824     v->id[0] = 'X';
825     free(v);
826 }
827
828 int
829 icalvalue_is_valid (const icalvalue* value)
830 {
831     if(value == 0){
832         return 0;
833     }
834     
835     return 1;
836 }
837
838 static char* icalvalue_binary_as_ical_string_r(const icalvalue* value) {
839
840     const char* data;
841     char* str;
842     icalerror_check_arg_rz( (value!=0),"value");
843
844     data = icalvalue_get_binary(value);
845
846     str = (char*)icalmemory_new_buffer(60);
847     snprintf(str, 60,"icalvalue_binary_as_ical_string is not implemented yet");
848
849     return str;
850 }
851
852
853 #define MAX_INT_DIGITS 12 /* Enough for 2^32 + sign*/ 
854     
855 static char* icalvalue_int_as_ical_string_r(const icalvalue* value) {
856     int data;
857     char* str;
858
859     icalerror_check_arg_rz( (value!=0),"value");
860     str = (char*)icalmemory_new_buffer(MAX_INT_DIGITS); 
861
862     data = icalvalue_get_integer(value);
863         
864     snprintf(str,MAX_INT_DIGITS,"%d",data);
865
866     return str;
867 }
868
869
870 static char* icalvalue_utcoffset_as_ical_string_r(const icalvalue* value)
871 {    
872     int data,h,m,s;
873     char sign;
874     char* str;
875
876     icalerror_check_arg_rz( (value!=0),"value");
877
878     str = (char*)icalmemory_new_buffer(9);
879     data = icalvalue_get_utcoffset(value);
880
881     if (abs(data) == data){
882         sign = '+';
883     } else {
884         sign = '-';
885     }
886
887     h = data/3600;
888     m = (data - (h*3600))/ 60;
889     s = (data - (h*3600) - (m*60));
890
891     if (s > 0)
892     snprintf(str,9,"%c%02d%02d%02d",sign,abs(h),abs(m),abs(s));
893     else
894     snprintf(str,9,"%c%02d%02d",sign,abs(h),abs(m));
895
896     return str;
897 }
898
899 static char* icalvalue_string_as_ical_string_r(const icalvalue* value) {
900
901     const char* data;
902     char* str = 0;
903     icalerror_check_arg_rz( (value!=0),"value");
904     data = value->data.v_string;
905
906     str = (char*)icalmemory_new_buffer(strlen(data)+1);   
907
908     strcpy(str,data);
909
910     return str;
911 }
912
913
914 static char* icalvalue_recur_as_ical_string_r(const icalvalue* value) 
915 {
916     struct icalrecurrencetype *recur = value->data.v_recur;
917     return icalrecurrencetype_as_string_r(recur);
918 }
919
920 static char* icalvalue_text_as_ical_string_r(const icalvalue* value) {
921     return icalmemory_strdup_and_quote(value, value->data.v_string);
922 }
923
924
925
926 static char* 
927 icalvalue_attach_as_ical_string_r(const icalvalue* value) 
928 {
929     icalattach *a;
930     char * str;
931
932     icalerror_check_arg_rz( (value!=0),"value");
933
934     a = icalvalue_get_attach(value);
935
936     if (icalattach_get_is_url (a)) {
937         const char *url;
938
939         url = icalattach_get_url (a);
940         str = icalmemory_new_buffer (strlen (url) + 1);
941         strcpy (str, url);
942         return str;
943     } else {
944       const char *data = 0;
945       data = (const char*)icalattach_get_data(a);
946       str = icalmemory_new_buffer (strlen (data) + 1);
947       strcpy (str, data);
948       return str;
949 }
950 }
951
952
953 static char* icalvalue_duration_as_ical_string_r(const icalvalue* value) {
954
955     struct icaldurationtype data;
956
957     icalerror_check_arg_rz( (value!=0),"value");
958     data = icalvalue_get_duration(value);
959
960     return icaldurationtype_as_ical_string_r(data);
961 }
962
963
964
965 void print_time_to_string(char* str, const struct icaltimetype *data)
966 {
967     char temp[20];
968     str[0] = '\0';
969
970     if (data != 0) {
971         if (icaltime_is_utc(*data)){
972             snprintf(temp,sizeof(temp),"%02d%02d%02dZ",data->hour,data->minute,data->second);
973             strncat(str,temp,7);
974         } else {
975             snprintf(temp,sizeof(temp),"%02d%02d%02d",data->hour,data->minute,data->second);
976             strncat(str,temp,6);
977         }   
978     }
979 }
980
981  
982 void print_date_to_string(char* str,  const struct icaltimetype *data)
983 {
984     char temp[20];
985     str[0] = '\0';
986
987     if (data != 0) {
988         snprintf(temp,sizeof(temp),"%04d%02d%02d",data->year,data->month,data->day);
989         strncat(str,temp,8);
990     }
991 }
992
993 static char* icalvalue_date_as_ical_string_r(const icalvalue* value) {
994
995     struct icaltimetype data;
996     char* str;
997     icalerror_check_arg_rz( (value!=0),"value");
998     data = icalvalue_get_date(value);
999
1000     str = (char*)icalmemory_new_buffer(9);
1001  
1002     str[0] = '\0';
1003     print_date_to_string(str,&data);
1004    
1005     return str;
1006 }
1007
1008 void print_datetime_to_string(char* str,  const struct icaltimetype *data)
1009 {
1010     char temp[20];
1011     str[0] = '\0';
1012
1013     if (data != 0) {
1014         print_date_to_string(str,data);
1015         if ( !data->is_date ) {
1016             strncat(str,"T",19);
1017             temp[0] = '\0';
1018             print_time_to_string(temp,data);
1019             strncat(str,temp,19);
1020         }
1021     }
1022 }
1023
1024
1025 static char* icalvalue_datetime_as_ical_string_r(const icalvalue* value) {
1026     
1027     struct icaltimetype data;
1028     char* str;
1029     icalvalue_kind kind = icalvalue_isa(value);    
1030
1031     icalerror_check_arg_rz( (value!=0),"value");
1032
1033
1034     if( !(kind == ICAL_DATE_VALUE || kind == ICAL_DATETIME_VALUE ))
1035         {
1036             icalerror_set_errno(ICAL_BADARG_ERROR);
1037             return 0;
1038         }
1039
1040     data = icalvalue_get_datetime(value);
1041
1042     str = (char*)icalmemory_new_buffer(20);
1043  
1044     str[0] = 0;
1045     print_datetime_to_string(str,&data);
1046    
1047     return str;
1048
1049 }
1050
1051 static char* icalvalue_float_as_ical_string_r(const icalvalue* value) {
1052
1053     float data;
1054     char* str;
1055     icalerror_check_arg_rz( (value!=0),"value");
1056     data = icalvalue_get_float(value);
1057
1058     str = (char*)icalmemory_new_buffer(40);
1059
1060     snprintf(str,40,"%f",data);
1061
1062     return str;
1063 }
1064
1065
1066 static char* icalvalue_geo_as_ical_string_r(const icalvalue* value) {
1067
1068     struct icalgeotype data;
1069     char* str;
1070     icalerror_check_arg_rz( (value!=0),"value");
1071
1072     data = icalvalue_get_geo(value);
1073
1074     str = (char*)icalmemory_new_buffer(80);
1075
1076     snprintf(str,80,"%f;%f",data.lat,data.lon);
1077
1078     return str;
1079 }
1080
1081
1082 static char* icalvalue_datetimeperiod_as_ical_string_r(const icalvalue* value) {
1083     struct icaldatetimeperiodtype dtp = icalvalue_get_datetimeperiod(value);
1084
1085     icalerror_check_arg_rz( (value!=0),"value");
1086
1087     if(!icaltime_is_null_time(dtp.time)){
1088         return icaltime_as_ical_string_r(dtp.time);
1089     } else {
1090         return icalperiodtype_as_ical_string_r(dtp.period);
1091     }
1092 }
1093
1094
1095 static char* icalvalue_period_as_ical_string_r(const icalvalue* value) {
1096     struct icalperiodtype data;
1097     icalerror_check_arg_rz( (value!=0),"value");
1098     data = icalvalue_get_period(value);
1099
1100     return icalperiodtype_as_ical_string_r(data);
1101
1102 }
1103
1104
1105 static char* icalvalue_trigger_as_ical_string_r(const icalvalue* value) {
1106
1107     struct icaltriggertype data;
1108
1109     icalerror_check_arg_rz( (value!=0),"value");
1110     data = icalvalue_get_trigger(value);
1111
1112     if(!icaltime_is_null_time(data.time)){
1113         return icaltime_as_ical_string_r(data.time);
1114     } else {
1115         return icaldurationtype_as_ical_string_r(data.duration);
1116     }   
1117
1118 }
1119
1120 const char*
1121 icalvalue_as_ical_string(const icalvalue* value)
1122 {
1123         char *buf;
1124         buf = icalvalue_as_ical_string_r(value);
1125         icalmemory_add_tmp_buffer(buf);
1126         return buf;
1127 }
1128
1129
1130 char*
1131 icalvalue_as_ical_string_r(const icalvalue* value)
1132 {
1133     if(value == 0){
1134         return 0;
1135     }
1136
1137     switch (value->kind){
1138
1139     case ICAL_ATTACH_VALUE:
1140         return icalvalue_attach_as_ical_string_r(value);
1141         
1142     case ICAL_BINARY_VALUE:
1143         return icalvalue_binary_as_ical_string_r(value);
1144         
1145     case ICAL_BOOLEAN_VALUE:
1146     case ICAL_INTEGER_VALUE:
1147         return icalvalue_int_as_ical_string_r(value);                  
1148         
1149     case ICAL_UTCOFFSET_VALUE:
1150         return icalvalue_utcoffset_as_ical_string_r(value);                  
1151         
1152     case ICAL_TEXT_VALUE:
1153         return icalvalue_text_as_ical_string_r(value);
1154         
1155     case ICAL_QUERY_VALUE:
1156         return icalvalue_string_as_ical_string_r(value);
1157         
1158     case ICAL_STRING_VALUE:
1159     case ICAL_URI_VALUE:
1160     case ICAL_CALADDRESS_VALUE:
1161         return icalvalue_string_as_ical_string_r(value);
1162         
1163     case ICAL_DATE_VALUE:
1164         return icalvalue_date_as_ical_string_r(value);
1165     case ICAL_DATETIME_VALUE:
1166         return icalvalue_datetime_as_ical_string_r(value);
1167     case ICAL_DURATION_VALUE:
1168         return icalvalue_duration_as_ical_string_r(value);
1169         
1170     case ICAL_PERIOD_VALUE:
1171         return icalvalue_period_as_ical_string_r(value);
1172     case ICAL_DATETIMEPERIOD_VALUE:
1173         return icalvalue_datetimeperiod_as_ical_string_r(value);
1174         
1175     case ICAL_FLOAT_VALUE:
1176         return icalvalue_float_as_ical_string_r(value);
1177         
1178     case ICAL_GEO_VALUE:
1179         return icalvalue_geo_as_ical_string_r(value);
1180         
1181     case ICAL_RECUR_VALUE:
1182         return icalvalue_recur_as_ical_string_r(value);
1183         
1184     case ICAL_TRIGGER_VALUE:
1185         return icalvalue_trigger_as_ical_string_r(value);
1186
1187     case ICAL_REQUESTSTATUS_VALUE:
1188         return icalreqstattype_as_string_r(value->data.v_requeststatus);
1189         
1190     case ICAL_ACTION_VALUE:
1191     case ICAL_CMD_VALUE:
1192     case ICAL_QUERYLEVEL_VALUE:
1193     case ICAL_CARLEVEL_VALUE:
1194     case ICAL_METHOD_VALUE:
1195     case ICAL_STATUS_VALUE:
1196     case ICAL_TRANSP_VALUE:
1197     case ICAL_CLASS_VALUE:
1198         if(value->x_value !=0){
1199             return icalmemory_strdup(value->x_value);
1200         }
1201
1202         return icalproperty_enum_to_string_r(value->data.v_enum);
1203         
1204     case ICAL_X_VALUE: 
1205         if (value->x_value != 0)
1206         return icalmemory_strdup_and_quote(value,value->x_value);
1207
1208     /* FALLTHRU */
1209
1210     case ICAL_NO_VALUE:
1211     default:
1212         {
1213             return 0;
1214         }
1215     }
1216 }
1217
1218
1219 icalvalue_kind
1220 icalvalue_isa (const icalvalue* value)
1221 {
1222     if(value == 0){
1223         return ICAL_NO_VALUE;
1224     }
1225
1226     return value->kind;
1227 }
1228
1229
1230 int
1231 icalvalue_isa_value (void* value)
1232 {
1233     struct icalvalue_impl *impl = (struct icalvalue_impl *)value;
1234
1235     icalerror_check_arg_rz( (value!=0), "value");
1236
1237     if (strcmp(impl->id,"val") == 0) {
1238         return 1;
1239     } else {
1240         return 0;
1241     }
1242 }
1243
1244
1245 static int icalvalue_is_time(const icalvalue* a) {
1246     icalvalue_kind kind = icalvalue_isa(a);
1247
1248     if(kind == ICAL_DATETIME_VALUE ||
1249        kind == ICAL_DATE_VALUE ){
1250         return 1;
1251     }
1252
1253     return 0;
1254
1255 }
1256
1257 /*
1258  * In case of error, this function returns 0. This is partly bogus, as 0 is
1259  * not part of the returned enum.
1260  * FIXME We should probably add an error value to the enum.
1261  */
1262 icalparameter_xliccomparetype
1263 icalvalue_compare(const icalvalue* a, const icalvalue *b)
1264 {
1265
1266     icalerror_check_arg_rz( (a!=0), "a");
1267     icalerror_check_arg_rz( (b!=0), "b");
1268
1269     /* Not the same type; they can only be unequal */
1270     if( ! (icalvalue_is_time(a) && icalvalue_is_time(b)) &&
1271         icalvalue_isa(a) != icalvalue_isa(b)){
1272         return ICAL_XLICCOMPARETYPE_NOTEQUAL;
1273     }
1274
1275     switch (icalvalue_isa(a)){
1276
1277         case ICAL_ATTACH_VALUE:
1278         {
1279             if (icalattach_get_is_url(a->data.v_attach) &&
1280                 icalattach_get_is_url(b->data.v_attach)) {
1281                 if (strcasecmp(icalattach_get_url(a->data.v_attach),
1282                                icalattach_get_url(b->data.v_attach)) == 0)
1283                     return ICAL_XLICCOMPARETYPE_EQUAL;
1284                 else
1285                     return ICAL_XLICCOMPARETYPE_NOTEQUAL;
1286             }
1287             else {
1288                 if (a->data.v_attach == b->data.v_attach)
1289                     return ICAL_XLICCOMPARETYPE_EQUAL;
1290                 else
1291                     return ICAL_XLICCOMPARETYPE_NOTEQUAL;
1292             }
1293         }
1294         case ICAL_BINARY_VALUE:
1295         {
1296             if (a->data.v_attach == b->data.v_attach)
1297                 return ICAL_XLICCOMPARETYPE_EQUAL;
1298             else
1299                 return ICAL_XLICCOMPARETYPE_NOTEQUAL;
1300         }
1301
1302         case ICAL_BOOLEAN_VALUE:
1303         {
1304             if (icalvalue_get_boolean(a) == icalvalue_get_boolean(b)){
1305                 return ICAL_XLICCOMPARETYPE_EQUAL;
1306             } else {
1307                 return ICAL_XLICCOMPARETYPE_NOTEQUAL;
1308             }
1309         }
1310
1311         case ICAL_FLOAT_VALUE:
1312         {
1313             if (a->data.v_float > b->data.v_float){
1314                 return ICAL_XLICCOMPARETYPE_GREATER;
1315             } else if (a->data.v_float < b->data.v_float){
1316                 return ICAL_XLICCOMPARETYPE_LESS;
1317             } else {
1318                 return ICAL_XLICCOMPARETYPE_EQUAL;
1319             }
1320         }
1321
1322         case ICAL_INTEGER_VALUE:
1323         case ICAL_UTCOFFSET_VALUE:
1324         {
1325             if (a->data.v_int > b->data.v_int){
1326                 return ICAL_XLICCOMPARETYPE_GREATER;
1327             } else if (a->data.v_int < b->data.v_int){
1328                 return ICAL_XLICCOMPARETYPE_LESS;
1329             } else {
1330                 return ICAL_XLICCOMPARETYPE_EQUAL;
1331             }
1332         }
1333
1334         case ICAL_DURATION_VALUE: 
1335         {
1336             int dur_a = icaldurationtype_as_int(a->data.v_duration);
1337             int dur_b = icaldurationtype_as_int(b->data.v_duration);
1338
1339             if (dur_a > dur_b){
1340                 return ICAL_XLICCOMPARETYPE_GREATER;
1341             } else if (dur_a < dur_b){
1342                 return ICAL_XLICCOMPARETYPE_LESS;
1343             } else {
1344                 return ICAL_XLICCOMPARETYPE_EQUAL;
1345             }
1346         }           
1347
1348
1349         case ICAL_TEXT_VALUE:
1350         case ICAL_URI_VALUE:
1351         case ICAL_CALADDRESS_VALUE:
1352         case ICAL_TRIGGER_VALUE:
1353         case ICAL_DATE_VALUE:
1354         case ICAL_DATETIME_VALUE:
1355         case ICAL_DATETIMEPERIOD_VALUE:
1356         case ICAL_QUERY_VALUE:
1357         case ICAL_RECUR_VALUE:
1358         {
1359             int r;
1360             char *temp1, *temp2;
1361             temp1 = icalvalue_as_ical_string_r(a);
1362             temp2 = icalvalue_as_ical_string_r(b);
1363             r =  strcmp(temp1, temp2);
1364             free(temp1);
1365             free(temp2);
1366
1367             if (r > 0) {        
1368                 return ICAL_XLICCOMPARETYPE_GREATER;
1369             } else if (r < 0){
1370                 return ICAL_XLICCOMPARETYPE_LESS;
1371             } else {
1372                 return ICAL_XLICCOMPARETYPE_EQUAL;
1373             }
1374
1375                 
1376         }
1377
1378         case ICAL_METHOD_VALUE:
1379         {
1380             if (icalvalue_get_method(a) == icalvalue_get_method(b)){
1381                 return ICAL_XLICCOMPARETYPE_EQUAL;
1382             } else {
1383                 return ICAL_XLICCOMPARETYPE_NOTEQUAL;
1384             }
1385
1386         }
1387
1388         case ICAL_STATUS_VALUE:
1389         {
1390             if (icalvalue_get_status(a) == icalvalue_get_status(b)){
1391                 return ICAL_XLICCOMPARETYPE_EQUAL;
1392             } else {
1393                 return ICAL_XLICCOMPARETYPE_NOTEQUAL;
1394             }
1395
1396         }
1397
1398         case ICAL_TRANSP_VALUE:
1399         {
1400             if (icalvalue_get_transp(a) == icalvalue_get_transp(b)){
1401                 return ICAL_XLICCOMPARETYPE_EQUAL;
1402             } else {
1403                 return ICAL_XLICCOMPARETYPE_NOTEQUAL;
1404             }
1405         }
1406
1407         case ICAL_ACTION_VALUE:
1408         {
1409             if (icalvalue_get_action(a) == icalvalue_get_action(b)){
1410                 return ICAL_XLICCOMPARETYPE_EQUAL;
1411             } else {
1412                 return ICAL_XLICCOMPARETYPE_NOTEQUAL;
1413             }
1414         }
1415
1416         case ICAL_PERIOD_VALUE:
1417         case ICAL_GEO_VALUE:
1418         case ICAL_NO_VALUE:
1419         default:
1420         {
1421             icalerror_warn("Comparison not implemented for value type");
1422             return 0;
1423         }
1424     }   
1425
1426 }
1427
1428 /** Examine the value and possibly change the kind to agree with the
1429  *  value 
1430  */
1431
1432 void icalvalue_reset_kind(icalvalue* value)
1433 {
1434     if( (value->kind==ICAL_DATETIME_VALUE || value->kind==ICAL_DATE_VALUE )&&
1435         !icaltime_is_null_time(value->data.v_time) ) {
1436         
1437         if(icaltime_is_date(value->data.v_time)){
1438             value->kind = ICAL_DATE_VALUE;
1439         } else {
1440             value->kind = ICAL_DATETIME_VALUE;
1441         }
1442     }
1443        
1444 }
1445
1446 void icalvalue_set_parent(icalvalue* value,
1447                              icalproperty* property)
1448 {
1449     value->parent = property;
1450 }
1451
1452 icalproperty* icalvalue_get_parent(icalvalue* value)
1453 {
1454     return value->parent;
1455 }
1456
1457
1458 int icalvalue_encode_ical_string(const char *szText, char *szEncText, int nMaxBufferLen)
1459 {
1460     char   *ptr;
1461     icalvalue *value = 0;
1462
1463     if ((szText == 0) || (szEncText == 0))
1464         return 0;
1465    
1466     value = icalvalue_new_from_string(ICAL_STRING_VALUE, szText);
1467     
1468     if (value == 0)
1469         return 0;
1470     
1471     ptr = icalvalue_text_as_ical_string_r(value);
1472     if (ptr == 0)
1473         return 0;
1474     
1475     if ((int)strlen(ptr) >= nMaxBufferLen)
1476         {
1477             icalvalue_free (value);
1478             free(ptr);
1479             return 0;
1480         }
1481
1482     strcpy(szEncText, ptr);
1483     free(ptr);
1484
1485     icalvalue_free ((icalvalue*)value);
1486
1487     return 1;
1488 }
1489
1490 int icalvalue_decode_ical_string(const char *szText, char *szDecText, int nMaxBufferLen)
1491 {
1492     char *str, *str_p;
1493     const char *p;
1494     size_t buf_sz;
1495
1496     if ((szText == 0) || (szDecText == 0))
1497         return 0;
1498         
1499     buf_sz = strlen(szText);
1500     str_p = str = (char*)icalmemory_new_buffer(buf_sz + 1);
1501
1502     if (str_p == 0){
1503         return 0;
1504     }
1505
1506     for (p=szText; *p!=0; p++) {
1507         if (*p == '\\') {
1508             icalmemory_append_char (&str,&str_p,&buf_sz,*(p+1));
1509             p++;
1510         }           
1511         else
1512             icalmemory_append_char (&str,&str_p,&buf_sz,*p);
1513     }       
1514     
1515     icalmemory_append_char(&str,&str_p,&buf_sz,'\0');
1516
1517     if ((int)strlen(str) > nMaxBufferLen) {
1518         icalmemory_free_buffer(str);    
1519         return 0;
1520     }
1521
1522     strcpy(szDecText, str);
1523
1524     icalmemory_free_buffer(str);        
1525     return 1;
1526 }
1527
1528
1529 /* The remaining interfaces are 'new', 'set' and 'get' for each of the value
1530    types */