summaryrefslogtreecommitdiffabout
path: root/libical/src/libicalss/icalspanlist.c
Unidiff
Diffstat (limited to 'libical/src/libicalss/icalspanlist.c') (more/less context) (show whitespace changes)
-rw-r--r--libical/src/libicalss/icalspanlist.c374
1 files changed, 316 insertions, 58 deletions
diff --git a/libical/src/libicalss/icalspanlist.c b/libical/src/libicalss/icalspanlist.c
index cab6a81..f42ff41 100644
--- a/libical/src/libicalss/icalspanlist.c
+++ b/libical/src/libicalss/icalspanlist.c
@@ -25,20 +25,33 @@
25#ifdef HAVE_CONFIG_H 25#ifdef HAVE_CONFIG_H
26#include "config.h" 26#include "config.h"
27#endif 27#endif
28 28
29#include "ical.h" 29#include "ical.h"
30#include "icalspanlist.h" 30#include "icalspanlist.h"
31#include "pvl.h" 31
32#include <stdlib.h> /* for free and malloc */ 32#include <stdlib.h> /* for free and malloc */
33#include <string.h>
33 34
34struct icalspanlist_impl { 35struct icalspanlist_impl {
35 pvl_list spans; 36 pvl_list spans; /**< list of icaltime_span data **/
37 struct icaltimetype start; /**< start time of span **/
38 struct icaltimetype end;/**< end time of span **/
36}; 39};
37 40
38int compare_span(void* a, void* b) 41/** @brief Internal comparison function for two spans
42 *
43 * @param a a spanlist.
44 * @param b another spanlist.
45 *
46 * @return -1, 0, 1 depending on the comparison of the start times.
47 *
48 * Used to insert spans into the tree in sorted order.
49 */
50
51static int compare_span(void* a, void* b)
39{ 52{
40 struct icaltime_span *span_a = (struct icaltime_span *)a ; 53 struct icaltime_span *span_a = (struct icaltime_span *)a ;
41 struct icaltime_span *span_b = (struct icaltime_span *)b ; 54 struct icaltime_span *span_b = (struct icaltime_span *)b ;
42 55
43 if(span_a->start == span_b->start){ 56 if(span_a->start == span_b->start){
44 return 0; 57 return 0;
@@ -46,61 +59,89 @@ int compare_span(void* a, void* b)
46 return -1; 59 return -1;
47 } else { /*if(span_a->start > span->b.start)*/ 60 } else { /*if(span_a->start > span->b.start)*/
48 return 1; 61 return 1;
49 } 62 }
50} 63}
51 64
52icalcomponent* icalspanlist_get_inner(icalcomponent* comp) 65
66/** @brief callback function for collecting spanlists of a
67 * series of events.
68 *
69 * @param comp A valid icalcomponent.
70 * @param span The span to insert into data.
71 * @param data The actual spanlist to insert into
72 *
73 * This callback is used by icalcomponent_foreach_recurrence()
74 * to build up a spanlist.
75 */
76
77static void icalspanlist_new_callback(icalcomponent *comp,
78 struct icaltime_span *span,
79 void *data)
53{ 80{
54 if (icalcomponent_isa(comp) == ICAL_VCALENDAR_COMPONENT){ 81 icaltime_span *s;
55 return icalcomponent_get_first_real_component(comp); 82 icalspanlist *sl = (icalspanlist*) data;
56 } else { 83
57 return comp; 84 if (span->is_busy == 0)
85 return;
86
87 if ((s=(icaltime_span *) malloc(sizeof(icaltime_span))) == 0) {
88 icalerror_set_errno(ICAL_NEWFAILED_ERROR);
89 return;
58 } 90 }
91
92 /** copy span data into allocated memory.. **/
93 *s = *span;
94 pvl_insert_ordered(sl->spans, compare_span, (void*)s);
59} 95}
60 96
61 97
62void print_span(int c, struct icaltime_span span );
63 98
99/** @brief Make a free list from a set of VEVENT components.
100 *
101 * @param set A valid icalset containing VEVENTS
102 * @param start The free list starts at this date/time
103 * @param end The free list ends at this date/time
104 *
105 * @return A spanlist corresponding to the VEVENTS
106 *
107 * Given a set of components, a start time and an end time
108 * return a spanlist that contains the free/busy times.
109 */
64 110
65/* Make a free list from a set of component */
66icalspanlist* icalspanlist_new(icalset *set, 111icalspanlist* icalspanlist_new(icalset *set,
67 struct icaltimetype start, 112 struct icaltimetype start,
68 struct icaltimetype end) 113 struct icaltimetype end)
69{ 114{
70 struct icaltime_span range; 115 struct icaltime_span range;
71 pvl_elem itr; 116 pvl_elem itr;
72 icalcomponent *c,*inner; 117 icalcomponent *c,*inner;
73 icalcomponent_kind kind, inner_kind; 118 icalcomponent_kind kind, inner_kind;
74 struct icalspanlist_impl *sl; 119 icalspanlist *sl;
75 struct icaltime_span *freetime; 120 struct icaltime_span *freetime;
76 121
77 if ( ( sl = (struct icalspanlist_impl*) 122 if ( ( sl = (struct icalspanlist_impl*)
78 malloc(sizeof(struct icalspanlist_impl))) == 0) { 123 malloc(sizeof(struct icalspanlist_impl))) == 0) {
79 icalerror_set_errno(ICAL_NEWFAILED_ERROR); 124 icalerror_set_errno(ICAL_NEWFAILED_ERROR);
80 return 0; 125 return 0;
81 } 126 }
82 127
83 sl->spans = pvl_newlist(); 128 sl->spans = pvl_newlist();
129 sl->start = start;
130 sl->end = end;
84 131
85 range.start = icaltime_as_timet(start); 132 range.start = icaltime_as_timet(start);
86 range.end = icaltime_as_timet(end); 133 range.end = icaltime_as_timet(end);
87 134
88 printf("Range start: %s",ctime(&range.start));
89 printf("Range end : %s",ctime(&range.end));
90
91
92 /* Get a list of spans of busy time from the events in the set 135 /* Get a list of spans of busy time from the events in the set
93 and order the spans based on the start time */ 136 and order the spans based on the start time */
94 137
95 for(c = icalset_get_first_component(set); 138 for(c = icalset_get_first_component(set);
96 c != 0; 139 c != 0;
97 c = icalset_get_next_component(set)){ 140 c = icalset_get_next_component(set)){
98 141
99 struct icaltime_span span;
100
101 kind = icalcomponent_isa(c); 142 kind = icalcomponent_isa(c);
102 inner = icalcomponent_get_inner(c); 143 inner = icalcomponent_get_inner(c);
103 144
104 if(!inner){ 145 if(!inner){
105 continue; 146 continue;
106 } 147 }
@@ -111,47 +152,28 @@ icalspanlist* icalspanlist_new(icalset *set,
111 inner_kind != ICAL_VEVENT_COMPONENT){ 152 inner_kind != ICAL_VEVENT_COMPONENT){
112 continue; 153 continue;
113 } 154 }
114 155
115 icalerror_clear_errno(); 156 icalerror_clear_errno();
116 157
117 span = icalcomponent_get_span(c); 158 icalcomponent_foreach_recurrence(c, start, end,
118 span.is_busy = 1; 159 icalspanlist_new_callback,
160 (void*)sl);
119 161
120 if(icalerrno != ICAL_NO_ERROR){
121 continue;
122 }
123
124 if ((range.start < span.end && icaltime_is_null_time(end)) ||
125 (range.start < span.end && range.end > span.start )){
126
127 struct icaltime_span *s;
128
129 if ((s=(struct icaltime_span *)
130 malloc(sizeof(struct icaltime_span))) == 0){
131 icalerror_set_errno(ICAL_NEWFAILED_ERROR);
132 return 0;
133 }
134
135 memcpy(s,&span,sizeof(span));
136
137 pvl_insert_ordered(sl->spans,compare_span,(void*)s);
138
139 }
140 } 162 }
141 163
142 /* Now Fill in the free time spans. loop through the spans. if the 164 /* Now Fill in the free time spans. loop through the spans. if the
143 start of the range is not within the span, create a free entry 165 start of the range is not within the span, create a free entry
144 that runs from the start of the range to the start of the 166 that runs from the start of the range to the start of the
145 span. */ 167 span. */
146 168
147 for( itr = pvl_head(sl->spans); 169 for( itr = pvl_head(sl->spans);
148 itr != 0; 170 itr != 0;
149 itr = pvl_next(itr)) 171 itr = pvl_next(itr))
150 { 172 {
151 struct icaltime_span *s = (icalproperty*)pvl_data(itr); 173 struct icaltime_span *s = (struct icaltime_span*)pvl_data(itr);
152 174
153 if ((freetime=(struct icaltime_span *) 175 if ((freetime=(struct icaltime_span *)
154 malloc(sizeof(struct icaltime_span))) == 0){ 176 malloc(sizeof(struct icaltime_span))) == 0){
155 icalerror_set_errno(ICAL_NEWFAILED_ERROR); 177 icalerror_set_errno(ICAL_NEWFAILED_ERROR);
156 return 0; 178 return 0;
157 } 179 }
@@ -175,13 +197,13 @@ icalspanlist* icalspanlist_new(icalset *set,
175 after the last item in the calendar is open and add a span 197 after the last item in the calendar is open and add a span
176 that indicates this */ 198 that indicates this */
177 199
178 if( icaltime_is_null_time(end)){ 200 if( icaltime_is_null_time(end)){
179 struct icaltime_span* last_span; 201 struct icaltime_span* last_span;
180 202
181 last_span = pvl_data(pvl_tail(sl->spans)); 203 last_span = (struct icaltime_span*)pvl_data(pvl_tail(sl->spans));
182 204
183 if (last_span != 0){ 205 if (last_span != 0){
184 206
185 if ((freetime=(struct icaltime_span *) 207 if ((freetime=(struct icaltime_span *)
186 malloc(sizeof(struct icaltime_span))) == 0){ 208 malloc(sizeof(struct icaltime_span))) == 0){
187 icalerror_set_errno(ICAL_NEWFAILED_ERROR); 209 icalerror_set_errno(ICAL_NEWFAILED_ERROR);
@@ -194,96 +216,115 @@ icalspanlist* icalspanlist_new(icalset *set,
194 pvl_insert_ordered(sl->spans,compare_span,(void*)freetime); 216 pvl_insert_ordered(sl->spans,compare_span,(void*)freetime);
195 } 217 }
196 } 218 }
197 219
198 220
199 return sl; 221 return sl;
200
201} 222}
202 223
224/** @brief Destructor.
225 * @param s A valid icalspanlist
226 *
227 * Free memory associated with the spanlist
228 */
229
203void icalspanlist_free(icalspanlist* s) 230void icalspanlist_free(icalspanlist* s)
204{ 231{
205 struct icaltime_span *span; 232 struct icaltime_span *span;
206 struct icalspanlist_impl* impl = (struct icalspanlist_impl*)s;
207 233
208 while( (span=pvl_pop(impl->spans)) != 0){ 234 if (s == NULL)
235 return;
236
237 while( (span=pvl_pop(s->spans)) != 0){
209 free(span); 238 free(span);
210 } 239 }
211 240
212 pvl_free(impl->spans); 241 pvl_free(s->spans);
242
243 s->spans = 0;
213 244
214 impl->spans = 0; 245 free(s);
215} 246}
216 247
217 248
218void icalspanlist_dump(icalspanlist* s){ 249/** @brief (Debug) print out spanlist to stdout.
250 * @param sl A valid icalspanlist.
251 */
219 252
253void icalspanlist_dump(icalspanlist* sl){
220 int i = 0; 254 int i = 0;
221 struct icalspanlist_impl* sl = (struct icalspanlist_impl*)s;
222 pvl_elem itr; 255 pvl_elem itr;
223 256
224 for( itr = pvl_head(sl->spans); 257 for( itr = pvl_head(sl->spans);
225 itr != 0; 258 itr != 0;
226 itr = pvl_next(itr)) 259 itr = pvl_next(itr))
227 { 260 {
228 struct icaltime_span *s = (icalproperty*)pvl_data(itr); 261 struct icaltime_span *s = (struct icaltime_span*)pvl_data(itr);
229 262
230 printf("#%02d %d start: %s",++i,s->is_busy,ctime(&s->start)); 263 printf("#%02d %d start: %s",++i,s->is_busy,ctime(&s->start));
231 printf(" end : %s",ctime(&s->end)); 264 printf(" end : %s",ctime(&s->end));
232 265
233 } 266 }
234} 267}
235 268
236icalcomponent* icalspanlist_make_free_list(icalspanlist* sl); 269icalcomponent* icalspanlist_make_free_list(icalspanlist* sl);
237icalcomponent* icalspanlist_make_busy_list(icalspanlist* sl); 270icalcomponent* icalspanlist_make_busy_list(icalspanlist* sl);
238 271
272
273/** @brief Find next free time span in a spanlist.
274 *
275 * @param sl The spanlist to search.
276 * @param t The time to start looking.
277 *
278 * Given a spanlist and a time, find the next period of time
279 * that is free
280 */
281
239struct icalperiodtype icalspanlist_next_free_time(icalspanlist* sl, 282struct icalperiodtype icalspanlist_next_free_time(icalspanlist* sl,
240 struct icaltimetype t) 283 struct icaltimetype t)
241{ 284{
242 struct icalspanlist_impl* impl = (struct icalspanlist_impl*)sl;
243 pvl_elem itr; 285 pvl_elem itr;
244 struct icalperiodtype period; 286 struct icalperiodtype period;
245 struct icaltime_span *s; 287 struct icaltime_span *s;
246 288
247 time_t rangett= icaltime_as_timet(t); 289 time_t rangett= icaltime_as_timet(t);
248 290
249 period.start = icaltime_null_time(); 291 period.start = icaltime_null_time();
250 period.end = icaltime_null_time(); 292 period.end = icaltime_null_time();
251 293
252 /* Is the reference time before the first span? If so, assume 294 itr = pvl_head(sl->spans);
253 that the reference time is free */ 295 s = (struct icaltime_span *)pvl_data(itr);
254 itr = pvl_head(impl->spans);
255 s = (icalproperty*)pvl_data(itr);
256 296
257 if (s == 0){ 297 if (s == 0){
258 /* No elements in span */ 298 /* No elements in span */
259 return period; 299 return period;
260 } 300 }
261 301
302 /* Is the reference time before the first span? If so, assume
303 that the reference time is free */
262 if(rangett <s->start ){ 304 if(rangett <s->start ){
263 /* End of period is start of first span if span is busy, end 305 /* End of period is start of first span if span is busy, end
264 of the span if it is free */ 306 of the span if it is free */
265 period.start = t; 307 period.start = t;
266 308
267 if (s->is_busy == 0){ 309 if (s->is_busy == 1){
268 period.end = icaltime_from_timet(s->start,0); 310 period.end = icaltime_from_timet(s->start,0);
269 } else { 311 } else {
270 period.end = icaltime_from_timet(s->end,0); 312 period.end = icaltime_from_timet(s->end,0);
271 } 313 }
272 314
273 return period; 315 return period;
274 } 316 }
275 317
276 /* Otherwise, find the first free span that contains the 318 /* Otherwise, find the first free span that contains the
277 reference time. */ 319 reference time. */
278 320 for( itr = pvl_head(sl->spans);
279 for( itr = pvl_head(impl->spans);
280 itr != 0; 321 itr != 0;
281 itr = pvl_next(itr)) 322 itr = pvl_next(itr))
282 { 323 {
283 s = (icalproperty*)pvl_data(itr); 324 s = (struct icaltime_span *)pvl_data(itr);
284 325
285 if(s->is_busy == 0 && s->start >= rangett && 326 if(s->is_busy == 0 && s->start >= rangett &&
286 ( rangett < s->end || s->end == s->start)){ 327 ( rangett < s->end || s->end == s->start)){
287 328
288 if (rangett < s->start){ 329 if (rangett < s->start){
289 period.start = icaltime_from_timet(s->start,0); 330 period.start = icaltime_from_timet(s->start,0);
@@ -304,6 +345,223 @@ struct icalperiodtype icalspanlist_next_free_time(icalspanlist* sl,
304 return period; 345 return period;
305} 346}
306 347
307struct icalperiodtype icalspanlist_next_busy_time(icalspanlist* sl, 348struct icalperiodtype icalspanlist_next_busy_time(icalspanlist* sl,
308 struct icaltimetype t); 349 struct icaltimetype t);
309 350
351
352/** @brief Returns an hour-by-hour array of free/busy times over a
353 * given period.
354 *
355 * @param sl A valid icalspanlist
356 * @param delta_t The time slice to divide by, in seconds. Default 3600.
357 *
358 * @return A pointer to an array of integers containing the number of
359 * busy events in each delta_t time period. The final entry
360 * contains the value -1.
361 *
362 * This calculation is somewhat tricky. This is due to the fact that
363 * the time range contains the start time, but does not contain the
364 * end time. To perform a proper calculation we subtract one second
365 * off the end times to get a true containing time.
366 *
367 * Also note that if you supplying a spanlist that does not start or
368 * end on a time boundary divisible by delta_t you may get results
369 * that are not quite what you expect.
370 */
371
372int* icalspanlist_as_freebusy_matrix(icalspanlist* sl, int delta_t) {
373 pvl_elem itr;
374 int spanduration_secs;
375 int *matrix;
376 int matrix_slots;
377 time_t sl_start, sl_end;
378
379 icalerror_check_arg_rz( (sl!=0), "spanlist");
380
381 if (!delta_t)
382 delta_t = 3600;
383
384 /** calculate the start and end time as time_t **/
385 sl_start = icaltime_as_timet_with_zone(sl->start, icaltimezone_get_utc_timezone());
386 sl_end = icaltime_as_timet_with_zone(sl->end, icaltimezone_get_utc_timezone());
387
388
389 /** insure that the time period falls on a time boundary divisable
390 by delta_t */
391
392 sl_start /= delta_t;
393 sl_start *= delta_t;
394
395 sl_end /= delta_t;
396 sl_end *= delta_t;
397
398
399 /** find the duration of this spanlist **/
400 spanduration_secs = sl_end - sl_start;
401
402
403 /** malloc our matrix, add one extra slot for a final -1 **/
404 matrix_slots = spanduration_secs/delta_t + 1;
405
406 matrix = (int*) malloc(sizeof(int) * matrix_slots);
407 if (matrix == NULL) {
408 icalerror_set_errno(ICAL_NEWFAILED_ERROR);
409 return NULL;
410 }
411 memset(matrix, 0, sizeof(int) * matrix_slots);
412 matrix[matrix_slots-1] = -1;
413
414 /* loop through each span and mark the slots in the array */
415
416 for( itr = pvl_head(sl->spans); itr != 0; itr = pvl_next(itr)) {
417 struct icaltime_span *s = (struct icaltime_span*)pvl_data(itr);
418
419 if (s->is_busy == 1) {
420 int offset_start = s->start/delta_t - sl_start/delta_t;
421 int offset_end = (s->end - 1) /delta_t - sl_start/delta_t + 1;
422 int i;
423
424 if (offset_end >= matrix_slots)
425 offset_end = matrix_slots - 1;
426
427 i = offset_start;
428 for (i=offset_start; i < offset_end; i++) {
429 matrix[i]++;
430 }
431 }
432 }
433 return matrix;
434}
435
436
437/** @brief Return a VFREEBUSY component for the corresponding spanlist
438 *
439 * @param sl A valid icalspanlist, from icalspanlist_new()
440 * @param organizer The organizer specified as MAILTO:user@domain
441 * @param attendee The attendee specified as MAILTO:user@domain
442 *
443 * @return A valid icalcomponent or NULL.
444 *
445 * This function returns a VFREEBUSY component for the given spanlist.
446 * The start time is mapped to DTSTART, the end time to DTEND.
447 * Each busy span is represented as a separate FREEBUSY entry.
448 * An attendee parameter is required, and organizer parameter is
449 * optional.
450 */
451
452icalcomponent *icalspanlist_as_vfreebusy(icalspanlist* sl,
453 const char* organizer,
454 const char* attendee) {
455 icalcomponent *comp;
456 icalproperty *p;
457 struct icaltimetype atime = icaltime_from_timet( time(0),0);
458 pvl_elem itr;
459 icaltimezone *utc_zone;
460 icalparameter *param;
461
462 if (!attendee) {
463 icalerror_set_errno(ICAL_USAGE_ERROR);
464 return 0;
465 }
466
467 utc_zone = icaltimezone_get_utc_timezone ();
468
469 comp = icalcomponent_new_vfreebusy();
470
471 icalcomponent_add_property(comp, icalproperty_new_dtstart(sl->start));
472 icalcomponent_add_property(comp, icalproperty_new_dtend(sl->end));
473 icalcomponent_add_property(comp, icalproperty_new_dtstamp(atime));
474
475 if (organizer) {
476 icalcomponent_add_property(comp, icalproperty_new_organizer(organizer));
477 }
478 icalcomponent_add_property(comp, icalproperty_new_attendee(attendee));
479
480 /* now add the freebusy sections.. */
481
482 for( itr = pvl_head(sl->spans); itr != 0; itr = pvl_next(itr)) {
483 struct icalperiodtype period;
484 struct icaltime_span *s = (struct icaltime_span*)pvl_data(itr);
485
486 if (s->is_busy == 1) {
487
488 period.start = icaltime_from_timet_with_zone (s->start, 0, utc_zone);
489 period.end = icaltime_from_timet_with_zone (s->end, 0, utc_zone);
490 period.duration = icaldurationtype_null_duration();
491
492
493 p = icalproperty_new_freebusy(period);
494 param = icalparameter_new_fbtype(ICAL_FBTYPE_BUSY);
495 icalproperty_add_parameter(p, param);
496
497 icalcomponent_add_property(comp, p);
498 }
499
500 }
501
502 return comp;
503}
504
505
506/** @brief Return a spanlist corresponding to the VFREEBUSY portion of
507 * an icalcomponent.
508 *
509 * @param c A valid icalcomponent.
510 *
511 * @return A valid icalspanlist or NULL if no VFREEBUSY section.
512 *
513 */
514
515
516icalspanlist *icalspanlist_from_vfreebusy(icalcomponent* comp)
517{
518 icalcomponent *inner;
519 icalproperty *prop;
520 icalspanlist *sl;
521
522 icalerror_check_arg_rz((comp != NULL), "comp");
523
524 inner = icalcomponent_get_inner(comp);
525 if (!inner) return NULL;
526
527 if ( ( sl = (icalspanlist*) malloc(sizeof(icalspanlist))) == 0) {
528 icalerror_set_errno(ICAL_NEWFAILED_ERROR);
529 return 0;
530 }
531 sl->spans = pvl_newlist();
532
533 /* cycle through each FREEBUSY property, adding to the spanlist */
534 for (prop = icalcomponent_get_first_property(inner, ICAL_FREEBUSY_PROPERTY);
535 prop != NULL;
536 prop = icalcomponent_get_next_property(inner, ICAL_FREEBUSY_PROPERTY)) {
537 icaltime_span *s = (icaltime_span *) malloc(sizeof(icaltime_span));
538 icalparameter *param;
539 struct icalperiodtype period;
540 icalparameter_fbtype fbtype;
541
542 if (s == 0) {
543 icalerror_set_errno(ICAL_NEWFAILED_ERROR);
544 return 0;
545 }
546
547 param = icalproperty_get_first_parameter(prop, ICAL_FBTYPE_PARAMETER);
548 fbtype = (param) ? icalparameter_get_fbtype(param) : ICAL_FBTYPE_BUSY;
549
550 switch (fbtype) {
551 case ICAL_FBTYPE_FREE:
552 case ICAL_FBTYPE_NONE:
553 case ICAL_FBTYPE_X:
554 s->is_busy = 1;
555 default:
556 s->is_busy = 0;
557 }
558
559 period = icalproperty_get_freebusy(prop);
560 s->start = icaltime_as_timet_with_zone(period.start, icaltimezone_get_utc_timezone());
561 s->end = icaltime_as_timet_with_zone(period.end, icaltimezone_get_utc_timezone());
562;
563 pvl_insert_ordered(sl->spans, compare_span, (void*)s);
564 }
565 /** @todo calculate start/end limits.. fill in holes? **/
566 return sl;
567}