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