1 /* -*- Mode: C -*-
2   ======================================================================
3   FILE: icaltime.c
4   CREATOR: eric 02 June 2000
5 
6   $Id: icalduration.c,v 1.21 2008-01-15 23:17:40 dothebart Exp $
7   $Locker:  $
8 
9  (C) COPYRIGHT 2000, Eric Busboom, http://www.softwarestudio.org
10 
11  This program is free software; you can redistribute it and/or modify
12  it under the terms of either:
13 
14     The LGPL as published by the Free Software Foundation, version
15     2.1, available at: http://www.fsf.org/copyleft/lesser.html
16 
17   Or:
18 
19     The Mozilla Public License Version 1.0. You may obtain a copy of
20     the License at http://www.mozilla.org/MPL/
21 
22  The Original Code is eric. The Initial Developer of the Original
23  Code is Eric Busboom
24 
25 
26  ======================================================================*/
27 
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31 
32 #include "icalduration.h"
33 
34 #include <assert.h>
35 #include <string.h>
36 #include <stdlib.h>
37 #include <stdio.h>
38 
39 #include "icalerror.h"
40 #include "icalmemory.h"
41 #include "icalvalue.h"
42 
43 #ifdef WIN32
44 #if defined(_MSC_VER) && (_MSC_VER < 1900)
45 #define snprintf _snprintf
46 #endif
47 #endif
48 
49 
50 
51 /* From Seth Alves,  <alves@hungry.com>   */
icaldurationtype_from_int(int t)52 struct icaldurationtype icaldurationtype_from_int(int t)
53 {
54     struct icaldurationtype dur;
55     int used = 0;
56 
57     dur = icaldurationtype_null_duration();
58 
59     if(t < 0){
60 	dur.is_neg = 1;
61 	t = -t;
62     }
63 
64     if (t % (60 * 60 * 24 * 7) == 0) {
65 	dur.weeks = t / (60 * 60 * 24 * 7);
66     } else {
67 	used += dur.weeks * (60 * 60 * 24 * 7);
68 	dur.days = (t - used) / (60 * 60 * 24);
69 	used += dur.days * (60 * 60 * 24);
70 	dur.hours = (t - used) / (60 * 60);
71 	used += dur.hours * (60 * 60);
72 	dur.minutes = (t - used) / (60);
73 	used += dur.minutes * (60);
74 	dur.seconds = (t - used);
75     }
76 
77     return dur;
78 }
79 
icaldurationtype_from_string(const char * str)80 struct icaldurationtype icaldurationtype_from_string(const char* str)
81 {
82 
83     int i;
84     int begin_flag = 0;
85     int time_flag = 0;
86     int date_flag = 0;
87     int week_flag = 0;
88     int digits=-1;
89     int scan_size = -1;
90     int size = strlen(str);
91     char p;
92     struct icaldurationtype d;
93 
94     memset(&d, 0, sizeof(struct icaldurationtype));
95 
96     for(i=0;i != size;i++){
97 	p = str[i];
98 
99 	switch(p)
100 	    {
101 	    case '-': {
102 		if(i != 0 || begin_flag == 1) goto error;
103 
104 		d.is_neg = 1;
105 		break;
106 	    }
107 
108 	    case 'P': {
109 		if (i != 0 && i !=1 ) goto error;
110 		begin_flag = 1;
111 		break;
112 	    }
113 
114 	    case 'T': {
115 		time_flag = 1;
116 		break;
117 	    }
118 
119 	    case '0':
120 	    case '1':
121 	    case '2':
122 	    case '3':
123 	    case '4':
124 	    case '5':
125 	    case '6':
126 	    case '7':
127 	    case '8':
128 	    case '9':
129 		{
130 
131 		    /* HACK. Skip any more digits if the l;ast one
132                        read has not been assigned */
133 		    if(digits != -1){
134 			break;
135 		    }
136 
137 		    if (begin_flag == 0) goto error;
138 		    /* Get all of the digits, not one at a time */
139 		    scan_size = sscanf(&str[i],"%d",&digits);
140 		    if(scan_size == 0) goto error;
141 		    break;
142 		}
143 
144 	    case 'H': {
145 		if (time_flag == 0||week_flag == 1||d.hours !=0||digits ==-1)
146 		    goto error;
147 		d.hours = digits; digits = -1;
148 		break;
149 	    }
150 	    case 'M': {
151 		if (time_flag == 0||week_flag==1||d.minutes != 0||digits ==-1)
152 		    goto error;
153 		d.minutes = digits; digits = -1;
154 		break;
155 	    }
156 	    case 'S': {
157 		if (time_flag == 0||week_flag==1||d.seconds!=0||digits ==-1)
158 		    goto error;
159 		d.seconds = digits; digits = -1;
160 		break;
161 	    }
162 	    case 'W': {
163 		if (time_flag==1||date_flag==1||d.weeks!=0||digits ==-1)
164 		    goto error;
165 		week_flag = 1;
166 		d.weeks = digits; digits = -1;
167 		break;
168 	    }
169 	    case 'D': {
170 		if (time_flag==1||week_flag==1||d.days!=0||digits ==-1)
171 		    goto error;
172 		date_flag = 1;
173 		d.days = digits; digits = -1;
174 		break;
175 	    }
176 	    default: {
177 		goto error;
178 	    }
179 
180 	    }
181     }
182 
183     return d;
184 
185 
186  error:
187     icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
188     return icaldurationtype_bad_duration();
189 }
190 
191 #define TMP_BUF_SIZE 1024
192 static
append_duration_segment(char ** buf,char ** buf_ptr,size_t * buf_size,const char * sep,unsigned int value)193 void append_duration_segment(char** buf, char** buf_ptr, size_t* buf_size,
194 			     const char* sep, unsigned int value) {
195 
196     char temp[TMP_BUF_SIZE];
197 
198     snprintf(temp,sizeof(temp),"%d",value);
199 
200     icalmemory_append_string(buf, buf_ptr, buf_size, temp);
201     icalmemory_append_string(buf, buf_ptr, buf_size, sep);
202 
203 }
204 
icaldurationtype_as_ical_string(struct icaldurationtype d)205 char* icaldurationtype_as_ical_string(struct icaldurationtype d)
206 {
207 	char *buf;
208 	buf = icaldurationtype_as_ical_string_r(d);
209 	icalmemory_add_tmp_buffer(buf);
210 	return buf;
211 }
212 
213 
icaldurationtype_as_ical_string_r(struct icaldurationtype d)214 char* icaldurationtype_as_ical_string_r(struct icaldurationtype d)
215 {
216 
217     char *buf;
218     size_t buf_size = 256;
219     char* buf_ptr = 0;
220     int seconds;
221 
222     buf = (char*)icalmemory_new_buffer(buf_size);
223     buf_ptr = buf;
224 
225 
226     seconds = icaldurationtype_as_int(d);
227 
228     if(seconds !=0){
229 
230 	if(d.is_neg == 1){
231 	    icalmemory_append_char(&buf, &buf_ptr, &buf_size, '-');
232 	}
233 
234 	icalmemory_append_char(&buf, &buf_ptr, &buf_size, 'P');
235 
236 	if (d.weeks != 0 ) {
237 	    append_duration_segment(&buf, &buf_ptr, &buf_size, "W", d.weeks);
238 	}
239 
240 	if (d.days != 0 ) {
241 	    append_duration_segment(&buf, &buf_ptr, &buf_size, "D", d.days);
242 	}
243 
244 	if (d.hours != 0 || d.minutes != 0 || d.seconds != 0) {
245 
246 	    icalmemory_append_string(&buf, &buf_ptr, &buf_size, "T");
247 
248 	    if (d.hours != 0 ) {
249 		append_duration_segment(&buf, &buf_ptr, &buf_size, "H", d.hours);
250 	    }
251 	    if (d.minutes != 0 ) {
252 		append_duration_segment(&buf, &buf_ptr, &buf_size, "M",
253 					d.minutes);
254 	    }
255 	    if (d.seconds != 0 ) {
256 		append_duration_segment(&buf, &buf_ptr, &buf_size, "S",
257 					d.seconds);
258 	    }
259 
260 	}
261     } else {
262 	icalmemory_append_string(&buf, &buf_ptr, &buf_size, "PT0S");
263     }
264 
265     return buf;
266 }
267 
268 
269 /* From Russel Steinthal */
icaldurationtype_as_int(struct icaldurationtype dur)270 int icaldurationtype_as_int(struct icaldurationtype dur)
271 {
272     return (int)( (dur.seconds +
273 		   (60 * dur.minutes) +
274 		   (60 * 60 * dur.hours) +
275 		   (60 * 60 * 24 * dur.days) +
276 		   (60 * 60 * 24 * 7 * dur.weeks))
277 		  * (dur.is_neg==1? -1 : 1) ) ;
278 }
279 
icaldurationtype_null_duration(void)280 struct icaldurationtype icaldurationtype_null_duration(void)
281 {
282     struct icaldurationtype d;
283 
284     memset(&d,0,sizeof(struct icaldurationtype));
285 
286     return d;
287 }
288 
icaldurationtype_is_null_duration(struct icaldurationtype d)289 int icaldurationtype_is_null_duration(struct icaldurationtype d)
290 {
291     if(icaldurationtype_as_int(d) == 0){
292 	return 1;
293     } else {
294 	return 0;
295     }
296 }
297 
298 /* in icalvalue_new_from_string_with_error, we should not call
299    icaldurationtype_is_null_duration() to see if there is an error
300    condition. Null duration is perfectly valid for an alarm.
301    We cannot depend on the caller to check icalerrno either,
302    following the philosophy of unix errno. we set the is_neg
303    to -1 to indicate that this is a bad duration.
304 */
icaldurationtype_bad_duration()305 struct icaldurationtype icaldurationtype_bad_duration()
306 {
307     struct icaldurationtype d;
308     memset(&d,0,sizeof(struct icaldurationtype));
309     d.is_neg = -1;
310     return d;
311 }
312 
icaldurationtype_is_bad_duration(struct icaldurationtype d)313 int icaldurationtype_is_bad_duration(struct icaldurationtype d)
314 {
315     return (d.is_neg == -1);
316 }
317 
318 
icaltime_add(struct icaltimetype t,struct icaldurationtype d)319 struct icaltimetype  icaltime_add(struct icaltimetype t,
320 				  struct icaldurationtype  d)
321 {
322     if (!d.is_neg) {
323         t.second += d.seconds;
324         t.minute += d.minutes;
325         t.hour += d.hours;
326         t.day += d.days;
327         t.day += d.weeks * 7;
328     } else {
329         t.second -= d.seconds;
330         t.minute -= d.minutes;
331         t.hour -= d.hours;
332         t.day -= d.days;
333         t.day -= d.weeks * 7;
334     }
335 
336     t = icaltime_normalize(t);
337 
338     return t;
339 }
340 
icaltime_subtract(struct icaltimetype t1,struct icaltimetype t2)341 struct icaldurationtype  icaltime_subtract(struct icaltimetype t1,
342 					   struct icaltimetype t2)
343 {
344 
345     time_t t1t = icaltime_as_timet(t1);
346     time_t t2t = icaltime_as_timet(t2);
347 
348     return icaldurationtype_from_int((int)(t1t-t2t));
349 
350 
351 }
352 
353