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
@@ -28,14 +28,27 @@
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 ;
@@ -49,20 +62,52 @@ int compare_span(void* a, void* b)
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)
@@ -71,7 +116,7 @@ icalspanlist* icalspanlist_new(icalset *set,
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*)
@@ -81,14 +126,12 @@ icalspanlist* icalspanlist_new(icalset *set,
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
@@ -96,8 +139,6 @@ icalspanlist* icalspanlist_new(icalset *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
@@ -114,29 +155,10 @@ icalspanlist* icalspanlist_new(icalset *set,
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
@@ -148,7 +170,7 @@ icalspanlist* icalspanlist_new(icalset *set,
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){
@@ -178,7 +200,7 @@ icalspanlist* icalspanlist_new(icalset *set,
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
@@ -197,35 +219,46 @@ icalspanlist* icalspanlist_new(icalset *set,
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));
@@ -236,10 +269,19 @@ void icalspanlist_dump(icalspanlist* s){
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;
@@ -249,22 +291,22 @@ struct icalperiodtype icalspanlist_next_free_time(icalspanlist* sl,
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);
@@ -275,12 +317,11 @@ struct icalperiodtype icalspanlist_next_free_time(icalspanlist* sl,
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)){
@@ -307,3 +348,220 @@ struct icalperiodtype icalspanlist_next_free_time(icalspanlist* sl,
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}