2 ======================================================================
4 CREATOR: ebusboom 23 aug 2000
6 $Id: icalspanlist.c,v 1.15 2008-01-02 20:07:42 dothebart Exp $
9 (C) COPYRIGHT 2000, Eric Busboom, http://www.softwarestudio.org
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of either:
14 The LGPL as published by the Free Software Foundation, version
15 2.1, available at: http://www.fsf.org/copyleft/lesser.html
19 The Mozilla Public License Version 1.0. You may obtain a copy of
20 the License at http://www.mozilla.org/MPL/
23 ======================================================================*/
29 #include <libical/ical.h>
30 #include "icalspanlist.h"
32 #include <stdlib.h> /* for free and malloc */
35 struct icalspanlist_impl {
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 **/
41 /** @brief Internal comparison function for two spans
43 * @param a a spanlist.
44 * @param b another spanlist.
46 * @return -1, 0, 1 depending on the comparison of the start times.
48 * Used to insert spans into the tree in sorted order.
51 static int compare_span(void* a, void* b)
53 struct icaltime_span *span_a = (struct icaltime_span *)a ;
54 struct icaltime_span *span_b = (struct icaltime_span *)b ;
56 if(span_a->start == span_b->start){
58 } else if(span_a->start < span_b->start){
60 } else { /*if(span_a->start > span->b.start)*/
66 /** @brief callback function for collecting spanlists of a
69 * @param comp A valid icalcomponent.
70 * @param span The span to insert into data.
71 * @param data The actual spanlist to insert into
73 * This callback is used by icalcomponent_foreach_recurrence()
74 * to build up a spanlist.
77 static void icalspanlist_new_callback(icalcomponent *comp,
78 struct icaltime_span *span,
82 icalspanlist *sl = (icalspanlist*) data;
84 if (span->is_busy == 0)
87 if ((s=(icaltime_span *) malloc(sizeof(icaltime_span))) == 0) {
88 icalerror_set_errno(ICAL_NEWFAILED_ERROR);
92 /** copy span data into allocated memory.. **/
94 pvl_insert_ordered(sl->spans, compare_span, (void*)s);
99 /** @brief Make a free list from a set of VEVENT components.
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
105 * @return A spanlist corresponding to the VEVENTS
107 * Given a set of components, a start time and an end time
108 * return a spanlist that contains the free/busy times.
111 icalspanlist* icalspanlist_new(icalset *set,
112 struct icaltimetype start,
113 struct icaltimetype end)
115 struct icaltime_span range;
117 icalcomponent *c,*inner;
118 icalcomponent_kind kind, inner_kind;
120 struct icaltime_span *freetime;
122 if ( ( sl = (struct icalspanlist_impl*)
123 malloc(sizeof(struct icalspanlist_impl))) == 0) {
124 icalerror_set_errno(ICAL_NEWFAILED_ERROR);
128 sl->spans = pvl_newlist();
132 range.start = icaltime_as_timet(start);
133 range.end = icaltime_as_timet(end);
135 /* Get a list of spans of busy time from the events in the set
136 and order the spans based on the start time */
138 for(c = icalset_get_first_component(set);
140 c = icalset_get_next_component(set)){
142 kind = icalcomponent_isa(c);
143 inner = icalcomponent_get_inner(c);
149 inner_kind = icalcomponent_isa(inner);
151 if( kind != ICAL_VEVENT_COMPONENT &&
152 inner_kind != ICAL_VEVENT_COMPONENT){
156 icalerror_clear_errno();
158 icalcomponent_foreach_recurrence(c, start, end,
159 icalspanlist_new_callback,
164 /* Now Fill in the free time spans. loop through the spans. if the
165 start of the range is not within the span, create a free entry
166 that runs from the start of the range to the start of the
169 for( itr = pvl_head(sl->spans);
173 struct icaltime_span *s = (struct icaltime_span*)pvl_data(itr);
175 if ((freetime=(struct icaltime_span *)
176 malloc(sizeof(struct icaltime_span))) == 0){
177 icalerror_set_errno(ICAL_NEWFAILED_ERROR);
178 icalspanlist_free(sl);
182 if(range.start < s->start){
183 freetime->start = range.start;
184 freetime->end = s->start;
186 freetime->is_busy = 0;
189 pvl_insert_ordered(sl->spans,compare_span,(void*)freetime);
194 range.start = s->end;
197 /* If the end of the range is null, then assume that everything
198 after the last item in the calendar is open and add a span
199 that indicates this */
201 if( icaltime_is_null_time(end)){
202 struct icaltime_span* last_span;
204 last_span = (struct icaltime_span*)pvl_data(pvl_tail(sl->spans));
208 if ((freetime=(struct icaltime_span *)
209 malloc(sizeof(struct icaltime_span))) == 0){
210 icalerror_set_errno(ICAL_NEWFAILED_ERROR);
211 icalspanlist_free(sl);
215 freetime->is_busy = 0;
216 freetime->start = last_span->end;
217 freetime->end = freetime->start;
218 pvl_insert_ordered(sl->spans,compare_span,(void*)freetime);
226 /** @brief Destructor.
227 * @param s A valid icalspanlist
229 * Free memory associated with the spanlist
232 void icalspanlist_free(icalspanlist* s)
234 struct icaltime_span *span;
239 while( (span=pvl_pop(s->spans)) != 0){
251 /** @brief (Debug) print out spanlist to stdout.
252 * @param sl A valid icalspanlist.
255 void icalspanlist_dump(icalspanlist* sl){
259 for( itr = pvl_head(sl->spans);
263 struct icaltime_span *s = (struct icaltime_span*)pvl_data(itr);
265 printf("#%02d %d start: %s",++i,s->is_busy,ctime(&s->start));
266 printf(" end : %s",ctime(&s->end));
271 icalcomponent* icalspanlist_make_free_list(icalspanlist* sl);
272 icalcomponent* icalspanlist_make_busy_list(icalspanlist* sl);
275 /** @brief Find next free time span in a spanlist.
277 * @param sl The spanlist to search.
278 * @param t The time to start looking.
280 * Given a spanlist and a time, find the next period of time
284 struct icalperiodtype icalspanlist_next_free_time(icalspanlist* sl,
285 struct icaltimetype t)
288 struct icalperiodtype period;
289 struct icaltime_span *s;
291 time_t rangett= icaltime_as_timet(t);
293 period.start = icaltime_null_time();
294 period.end = icaltime_null_time();
296 itr = pvl_head(sl->spans);
297 s = (struct icaltime_span *)pvl_data(itr);
300 /* No elements in span */
304 /* Is the reference time before the first span? If so, assume
305 that the reference time is free */
306 if(rangett <s->start ){
307 /* End of period is start of first span if span is busy, end
308 of the span if it is free */
311 if (s->is_busy == 1){
312 period.end = icaltime_from_timet(s->start,0);
314 period.end = icaltime_from_timet(s->end,0);
320 /* Otherwise, find the first free span that contains the
322 for( itr = pvl_head(sl->spans);
326 s = (struct icaltime_span *)pvl_data(itr);
328 if(s->is_busy == 0 && s->start >= rangett &&
329 ( rangett < s->end || s->end == s->start)){
331 if (rangett < s->start){
332 period.start = icaltime_from_timet(s->start,0);
334 period.start = icaltime_from_timet(rangett,0);
337 period.end = icaltime_from_timet(s->end,0);
344 period.start = icaltime_null_time();
345 period.end = icaltime_null_time();
350 struct icalperiodtype icalspanlist_next_busy_time(icalspanlist* sl,
351 struct icaltimetype t);
354 /** @brief Returns an hour-by-hour array of free/busy times over a
357 * @param sl A valid icalspanlist
358 * @param delta_t The time slice to divide by, in seconds. Default 3600.
360 * @return A pointer to an array of integers containing the number of
361 * busy events in each delta_t time period. The final entry
362 * contains the value -1.
364 * This calculation is somewhat tricky. This is due to the fact that
365 * the time range contains the start time, but does not contain the
366 * end time. To perform a proper calculation we subtract one second
367 * off the end times to get a true containing time.
369 * Also note that if you supplying a spanlist that does not start or
370 * end on a time boundary divisible by delta_t you may get results
371 * that are not quite what you expect.
374 int* icalspanlist_as_freebusy_matrix(icalspanlist* sl, int delta_t) {
376 int spanduration_secs;
379 time_t sl_start, sl_end;
381 icalerror_check_arg_rz( (sl!=0), "spanlist");
386 /** calculate the start and end time as time_t **/
387 sl_start = icaltime_as_timet_with_zone(sl->start, icaltimezone_get_utc_timezone());
388 sl_end = icaltime_as_timet_with_zone(sl->end, icaltimezone_get_utc_timezone());
391 /** insure that the time period falls on a time boundary divisable
401 /** find the duration of this spanlist **/
402 spanduration_secs = sl_end - sl_start;
405 /** malloc our matrix, add one extra slot for a final -1 **/
406 matrix_slots = spanduration_secs/delta_t + 1;
408 matrix = (int*) malloc(sizeof(int) * matrix_slots);
409 if (matrix == NULL) {
410 icalerror_set_errno(ICAL_NEWFAILED_ERROR);
413 memset(matrix, 0, sizeof(int) * matrix_slots);
414 matrix[matrix_slots-1] = -1;
416 /* loop through each span and mark the slots in the array */
418 for( itr = pvl_head(sl->spans); itr != 0; itr = pvl_next(itr)) {
419 struct icaltime_span *s = (struct icaltime_span*)pvl_data(itr);
421 if (s->is_busy == 1) {
422 int offset_start = s->start/delta_t - sl_start/delta_t;
423 int offset_end = (s->end - 1) /delta_t - sl_start/delta_t + 1;
426 if (offset_end >= matrix_slots)
427 offset_end = matrix_slots - 1;
430 for (i=offset_start; i < offset_end; i++) {
439 /** @brief Return a VFREEBUSY component for the corresponding spanlist
441 * @param sl A valid icalspanlist, from icalspanlist_new()
442 * @param organizer The organizer specified as MAILTO:user@domain
443 * @param attendee The attendee specified as MAILTO:user@domain
445 * @return A valid icalcomponent or NULL.
447 * This function returns a VFREEBUSY component for the given spanlist.
448 * The start time is mapped to DTSTART, the end time to DTEND.
449 * Each busy span is represented as a separate FREEBUSY entry.
450 * An attendee parameter is required, and organizer parameter is
454 icalcomponent *icalspanlist_as_vfreebusy(icalspanlist* sl,
455 const char* organizer,
456 const char* attendee) {
459 struct icaltimetype atime = icaltime_from_timet( time(0),0);
461 icaltimezone *utc_zone;
462 icalparameter *param;
465 icalerror_set_errno(ICAL_USAGE_ERROR);
469 utc_zone = icaltimezone_get_utc_timezone ();
471 comp = icalcomponent_new_vfreebusy();
473 icalcomponent_add_property(comp, icalproperty_new_dtstart(sl->start));
474 icalcomponent_add_property(comp, icalproperty_new_dtend(sl->end));
475 icalcomponent_add_property(comp, icalproperty_new_dtstamp(atime));
478 icalcomponent_add_property(comp, icalproperty_new_organizer(organizer));
480 icalcomponent_add_property(comp, icalproperty_new_attendee(attendee));
482 /* now add the freebusy sections.. */
484 for( itr = pvl_head(sl->spans); itr != 0; itr = pvl_next(itr)) {
485 struct icalperiodtype period;
486 struct icaltime_span *s = (struct icaltime_span*)pvl_data(itr);
488 if (s->is_busy == 1) {
490 period.start = icaltime_from_timet_with_zone (s->start, 0, utc_zone);
491 period.end = icaltime_from_timet_with_zone (s->end, 0, utc_zone);
492 period.duration = icaldurationtype_null_duration();
495 p = icalproperty_new_freebusy(period);
496 param = icalparameter_new_fbtype(ICAL_FBTYPE_BUSY);
497 icalproperty_add_parameter(p, param);
499 icalcomponent_add_property(comp, p);
508 /** @brief Return a spanlist corresponding to the VFREEBUSY portion of
511 * @param c A valid icalcomponent.
513 * @return A valid icalspanlist or NULL if no VFREEBUSY section.
518 icalspanlist *icalspanlist_from_vfreebusy(icalcomponent* comp)
520 icalcomponent *inner;
524 icalerror_check_arg_rz((comp != NULL), "comp");
526 inner = icalcomponent_get_inner(comp);
527 if (!inner) return NULL;
529 if ( ( sl = (icalspanlist*) malloc(sizeof(icalspanlist))) == 0) {
530 icalerror_set_errno(ICAL_NEWFAILED_ERROR);
533 sl->spans = pvl_newlist();
535 /* cycle through each FREEBUSY property, adding to the spanlist */
536 for (prop = icalcomponent_get_first_property(inner, ICAL_FREEBUSY_PROPERTY);
538 prop = icalcomponent_get_next_property(inner, ICAL_FREEBUSY_PROPERTY)) {
539 icaltime_span *s = (icaltime_span *) malloc(sizeof(icaltime_span));
540 icalparameter *param;
541 struct icalperiodtype period;
542 icalparameter_fbtype fbtype;
545 icalerror_set_errno(ICAL_NEWFAILED_ERROR);
546 icalspanlist_free(sl);
550 param = icalproperty_get_first_parameter(prop, ICAL_FBTYPE_PARAMETER);
551 fbtype = (param) ? icalparameter_get_fbtype(param) : ICAL_FBTYPE_BUSY;
554 case ICAL_FBTYPE_FREE:
555 case ICAL_FBTYPE_NONE:
563 period = icalproperty_get_freebusy(prop);
564 s->start = icaltime_as_timet_with_zone(period.start, icaltimezone_get_utc_timezone());
565 s->end = icaltime_as_timet_with_zone(period.end, icaltimezone_get_utc_timezone());
567 pvl_insert_ordered(sl->spans, compare_span, (void*)s);
569 /** @todo calculate start/end limits.. fill in holes? **/