summaryrefslogtreecommitdiffabout
path: root/libical/src/libical/icalrecur.c
Side-by-side diff
Diffstat (limited to 'libical/src/libical/icalrecur.c') (more/less context) (ignore whitespace changes)
-rw-r--r--libical/src/libical/icalrecur.c2315
1 files changed, 2315 insertions, 0 deletions
diff --git a/libical/src/libical/icalrecur.c b/libical/src/libical/icalrecur.c
new file mode 100644
index 0000000..203ce70
--- a/dev/null
+++ b/libical/src/libical/icalrecur.c
@@ -0,0 +1,2315 @@
+/* -*- Mode: C -*-
+ ======================================================================
+ FILE: icalrecur.c
+ CREATOR: eric 16 May 2000
+
+ $Id$
+ $Locker$
+
+
+ (C) COPYRIGHT 2000, Eric Busboom, http://www.softwarestudio.org
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of either:
+
+ The LGPL as published by the Free Software Foundation, version
+ 2.1, available at: http://www.fsf.org/copyleft/lesser.html
+
+ Or:
+
+ The Mozilla Public License Version 1.0. You may obtain a copy of
+ the License at http://www.mozilla.org/MPL/
+
+
+ How this code works:
+
+ Processing starts when the caller generates a new recurrence
+ iterator via icalrecur_iterator_new(). This routine copies the
+ recurrence rule into the iterator and extracts things like start and
+ end dates. Then, it checks if the rule is legal, using some logic
+ from RFC2445 and some logic that probably should be in RFC2445.
+
+ Then, icalrecur_iterator_new() re-writes some of the BY*
+ arrays. This involves ( via a call to setup_defaults() ) :
+
+ 1) For BY rule parts with no data ( ie BYSECOND was not specified )
+ copy the corresponding time part from DTSTART into the BY array. (
+ So impl->by_ptrs[BY_SECOND] will then have one element if is
+ originally had none ) This only happens if the BY* rule part data
+ would expand the number of occurrences in the occurrence set. This
+ lets the code ignore DTSTART later on and still use it to get the
+ time parts that were not specified in any other way.
+
+ 2) For the by rule part that are not the same interval as the
+ frequency -- for HOURLY anything but BYHOUR, for instance -- copy the
+ first data element from the rule part into the first occurrence. For
+ example, for "INTERVAL=MONTHLY and BYHOUR=10,30", initialize the
+ first time to be returned to have an hour of 10.
+
+ Finally, for INTERVAL=YEARLY, the routine expands the rule to get
+ all of the days specified in the rule. The code will do this for
+ each new year, and this is the first expansion. This is a special
+ case for the yearly interval; no other frequency gets expanded this
+ way. The yearly interval is the most complex, so some special
+ processing is required.
+
+ After creating a new iterator, the caller will make successive calls
+ to icalrecur_iterator_next() to get the next time specified by the
+ rule. The main part of this routine is a switch on the frequency of
+ the rule. Each different frequency is handled by a different
+ routine.
+
+ For example, next_hour handles the case of INTERVAL=HOURLY, and it
+ is called by other routines to get the next hour. First, the routine
+ tries to get the next minute part of a time with a call to
+ next_minute(). If next_minute() returns 1, it has reached the end of
+ its data, usually the last element of the BYMINUTE array. Then, if
+ there is data in the BYHOUR array, the routine changes the hour to
+ the next one in the array. If INTERVAL=HOURLY, the routine advances
+ the hour by the interval.
+
+ If the routine used the last hour in the BYHOUR array, and the
+ INTERVAL=HOURLY, then the routine calls increment_monthday() to set
+ the next month day. The increment_* routines may call higher routine
+ to increment the month or year also.
+
+ The code for INTERVAL=DAILY is handled by next_day(). First, the
+ routine tries to get the next hour part of a time with a call to
+ next_hour. If next_hour() returns 1, it has reached the end of its
+ data, usually the last element of the BYHOUR array. This means that
+ next_day() should increment the time to the next day. If FREQUENCY==DAILY,
+ the routine increments the day by the interval; otherwise, it
+ increments the day by 1.
+
+ Next_day() differs from next_hour because it does not use the BYDAY
+ array to select an appropriate day. Instead, it returns every day (
+ incrementing by 1 if the frequency is not DAILY with INTERVAL!=1)
+ Any days that are not specified in an non-empty BYDAY array are
+ filtered out later.
+
+ Generally, the flow of these routine is for a next_* call a next_*
+ routine of a lower interval ( next_day calls next_hour) and then to
+ possibly call an increment_* routine of an equal or higher
+ interval. ( next_day calls increment_monthday() )
+
+ When the call to the original next_* routine returns,
+ icalrecur_iterator_next() will check the returned data against other
+ BYrule parts to determine if is should be excluded by calling
+ check_contracting_rules. Generally, a contracting rule is any with a
+ larger time span than the interval. For instance, if
+ INTERVAL=DAILY, BYMONTH is a contracting rule part.
+
+ Check_contracting_rules() uses icalrecur_check_rulepart() to do its
+ work. icalrecur_check_rulepart() uses expand_map[] to determine if a rule
+ is contracting, and if it is, and if the BY rule part has some data,
+ then the routine checks if the value of a component of the time is
+ part of the byrule part. For instance, for "INTERVAL=DAILY;
+ BYMONTH=6,10", icalrecur_check_rulepart() would check that the time value
+ given to it has a month of either 6 or 10.
+
+ Finally, icalrecur_iterator_next() does a few other checks on the
+ time value, and if it passes, it returns the time.
+
+ A note about the end_of_data flag. The flag indicates that the
+ routine is at the end of its data -- the last BY rule if the routine
+ is using by rules, or the last day of the week/month/year/etc if
+ not.
+
+ This flag is usually set early in a next_* routine and returned in
+ the end. The way it is used allows the next_* routine to set the
+ last time back to the first element in a BYxx rule, and then signal
+ to the higer level routine to increment the next higher level. For
+ instance. WITH FREQ=MONTHLY;BYDAY=TU,FR, After next_weekday_by_month
+ runs though both TU and FR, it sets the week day back to TU and sets
+ end_of_data to 1x. This signals next_month to increment the month.
+
+
+ ======================================================================*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "icalrecur.h"
+
+#ifdef ICAL_NO_LIBICAL
+#define icalerror_set_errno(x)
+#define icalerror_check_arg_rv(x,y)
+#else
+#include "icalerror.h"
+#include "icalmemory.h"
+#endif
+
+#include <stdlib.h> /* for malloc */
+#include <errno.h> /* for errno */
+#include <string.h> /* for strdup and strchr*/
+#include <assert.h>
+#include <stddef.h> /* For offsetof() macro */
+
+#include "pvl.h"
+
+#define TEMP_MAX 1024
+
+
+#define BYDAYIDX impl->by_indices[BY_DAY]
+#define BYDAYPTR impl->by_ptrs[BY_DAY]
+
+#define BYMONIDX impl->by_indices[BY_MONTH]
+#define BYMONPTR impl->by_ptrs[BY_MONTH]
+
+#define BYMDIDX impl->by_indices[BY_MONTH_DAY]
+#define BYMDPTR impl->by_ptrs[BY_MONTH_DAY]
+
+#define BYWEEKIDX impl->by_indices[BY_WEEK_NO]
+#define BYWEEKPTR impl->by_ptrs[BY_WEEK_NO]
+
+const char* icalrecur_freq_to_string(icalrecurrencetype_frequency kind);
+icalrecurrencetype_frequency icalrecur_string_to_freq(const char* str);
+
+const char* icalrecur_weekday_to_string(icalrecurrencetype_weekday kind);
+icalrecurrencetype_weekday icalrecur_string_to_weekday(const char* str);
+
+
+
+/*********************** Rule parsing routines ************************/
+
+struct icalrecur_parser {
+ const char* rule;
+ char* copy;
+ char* this_clause;
+ char* next_clause;
+
+ struct icalrecurrencetype rt;
+};
+
+const char* icalrecur_first_clause(struct icalrecur_parser *parser)
+{
+ char *idx;
+ parser->this_clause = parser->copy;
+
+ idx = strchr(parser->this_clause,';');
+
+ if (idx == 0){
+ parser->next_clause = 0;
+ return 0;
+ }
+
+ *idx = 0;
+ idx++;
+ parser->next_clause = idx;
+
+ return parser->this_clause;
+
+}
+
+const char* icalrecur_next_clause(struct icalrecur_parser *parser)
+{
+ char* idx;
+
+ parser->this_clause = parser->next_clause;
+
+ if(parser->this_clause == 0){
+ return 0;
+ }
+
+ idx = strchr(parser->this_clause,';');
+
+ if (idx == 0){
+ parser->next_clause = 0;
+ } else {
+
+ *idx = 0;
+ idx++;
+ parser->next_clause = idx;
+ }
+
+ return parser->this_clause;
+
+}
+
+void icalrecur_clause_name_and_value(struct icalrecur_parser *parser,
+ char** name, char** value)
+{
+ char *idx;
+
+ *name = parser->this_clause;
+
+ idx = strchr(parser->this_clause,'=');
+
+ if (idx == 0){
+ *name = 0;
+ *value = 0;
+ return;
+ }
+
+ *idx = 0;
+ idx++;
+ *value = idx;
+}
+
+void icalrecur_add_byrules(struct icalrecur_parser *parser, short *array,
+ int size, char* vals)
+{
+ char *t, *n;
+ int i=0;
+ int sign = 1;
+ short v;
+
+ n = vals;
+
+ while(n != 0){
+
+ if(i == size){
+ return;
+ }
+
+ t = n;
+
+ n = strchr(t,',');
+
+ if(n != 0){
+ *n = 0;
+ n++;
+ }
+
+ /* Get optional sign. HACK. sign is not allowed for all BYxxx
+ rule parts */
+ if( *t == '-'){
+ sign = -1;
+ t++;
+ } else if (*t == '+'){
+ sign = 1;
+ t++;
+ }
+
+ v = atoi(t) * sign ;
+
+
+ array[i++] = v;
+ array[i] = ICAL_RECURRENCE_ARRAY_MAX;
+
+ }
+
+}
+
+void icalrecur_add_bydayrules(struct icalrecur_parser *parser, const char* vals)
+{
+
+ char *t, *n;
+ int i=0;
+ int sign = 1;
+ int weekno = 0;
+ icalrecurrencetype_weekday wd;
+ short *array = parser->rt.by_day;
+ char* end;
+ char* vals_copy;
+
+ vals_copy = icalmemory_strdup(vals);
+
+ end = (char*)vals_copy+strlen(vals_copy);
+ n = vals_copy;
+
+ while(n != 0){
+
+
+ t = n;
+
+ n = strchr(t,',');
+
+ if(n != 0){
+ *n = 0;
+ n++;
+ }
+
+ /* Get optional sign. */
+ if( *t == '-'){
+ sign = -1;
+ t++;
+ } else if (*t == '+'){
+ sign = 1;
+ t++;
+ } else {
+ sign = 1;
+ }
+
+ weekno = 0;
+ /* Get Optional weekno */
+ if( sscanf(t,"%d",&weekno) != 0){
+ if (n != 0){
+ int weeknolen = (n-t)-3; /* 3 -> one for \0, 2 for day name */
+ /* could use abs(log10(weekno))+1, but that needs libm */
+ t += weeknolen;
+ } else {
+ t = end -2;
+ }
+ }
+
+ wd = icalrecur_string_to_weekday(t);
+
+ array[i++] = sign* ((int)wd + 8*weekno);
+ array[i] = ICAL_RECURRENCE_ARRAY_MAX;
+
+ }
+
+ free(vals_copy);
+
+}
+
+
+struct icalrecurrencetype icalrecurrencetype_from_string(const char* str)
+{
+ struct icalrecur_parser parser;
+
+ memset(&parser,0,sizeof(parser));
+ icalrecurrencetype_clear(&parser.rt);
+
+ icalerror_check_arg_re(str!=0,"str",parser.rt);
+
+
+ /* Set up the parser struct */
+ parser.rule = str;
+ parser.copy = icalmemory_strdup(parser.rule);
+ parser.this_clause = parser.copy;
+
+ if(parser.copy == 0){
+ icalerror_set_errno(ICAL_NEWFAILED_ERROR);
+ return parser.rt;
+ }
+
+ /* Loop through all of the clauses */
+ for(icalrecur_first_clause(&parser);
+ parser.this_clause != 0;
+ icalrecur_next_clause(&parser))
+ {
+ char *name, *value;
+ icalrecur_clause_name_and_value(&parser,&name,&value);
+
+ if(name == 0){
+ icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
+ icalrecurrencetype_clear(&parser.rt);
+ return parser.rt;
+ }
+
+ if (strcmp(name,"FREQ") == 0){
+ parser.rt.freq = icalrecur_string_to_freq(value);
+ } else if (strcmp(name,"COUNT") == 0){
+ parser.rt.count = atoi(value);
+ } else if (strcmp(name,"UNTIL") == 0){
+ parser.rt.until = icaltime_from_string(value);
+ } else if (strcmp(name,"INTERVAL") == 0){
+ parser.rt.interval = atoi(value);
+ } else if (strcmp(name,"WKST") == 0){
+ parser.rt.week_start = icalrecur_string_to_weekday(value);
+ } else if (strcmp(name,"BYSECOND") == 0){
+ icalrecur_add_byrules(&parser,parser.rt.by_second,
+ ICAL_BY_SECOND_SIZE,value);
+ } else if (strcmp(name,"BYMINUTE") == 0){
+ icalrecur_add_byrules(&parser,parser.rt.by_minute,
+ ICAL_BY_MINUTE_SIZE,value);
+ } else if (strcmp(name,"BYHOUR") == 0){
+ icalrecur_add_byrules(&parser,parser.rt.by_hour,
+ ICAL_BY_HOUR_SIZE,value);
+ } else if (strcmp(name,"BYDAY") == 0){
+ icalrecur_add_bydayrules(&parser,value);
+ } else if (strcmp(name,"BYMONTHDAY") == 0){
+ icalrecur_add_byrules(&parser,parser.rt.by_month_day,
+ ICAL_BY_MONTHDAY_SIZE,value);
+ } else if (strcmp(name,"BYYEARDAY") == 0){
+ icalrecur_add_byrules(&parser,parser.rt.by_year_day,
+ ICAL_BY_YEARDAY_SIZE,value);
+ } else if (strcmp(name,"BYWEEKNO") == 0){
+ icalrecur_add_byrules(&parser,parser.rt.by_week_no,
+ ICAL_BY_WEEKNO_SIZE,value);
+ } else if (strcmp(name,"BYMONTH") == 0){
+ icalrecur_add_byrules(&parser,parser.rt.by_month,
+ ICAL_BY_MONTH_SIZE,value);
+ } else if (strcmp(name,"BYSETPOS") == 0){
+ icalrecur_add_byrules(&parser,parser.rt.by_set_pos,
+ ICAL_BY_SETPOS_SIZE,value);
+ } else {
+ icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
+ icalrecurrencetype_clear(&parser.rt);
+ return parser.rt;
+ }
+
+ }
+
+ free(parser.copy);
+
+ return parser.rt;
+
+}
+
+#ifndef ICAL_NO_LIBICAL
+
+struct { char* str;size_t offset; short limit; } recurmap[] =
+{
+ {";BYSECOND=",offsetof(struct icalrecurrencetype,by_second),60},
+ {";BYMINUTE=",offsetof(struct icalrecurrencetype,by_minute),60},
+ {";BYHOUR=",offsetof(struct icalrecurrencetype,by_hour),24},
+ {";BYDAY=",offsetof(struct icalrecurrencetype,by_day),7},
+ {";BYMONTHDAY=",offsetof(struct icalrecurrencetype,by_month_day),31},
+ {";BYYEARDAY=",offsetof(struct icalrecurrencetype,by_year_day),366},
+ {";BYWEEKNO=",offsetof(struct icalrecurrencetype,by_week_no),52},
+ {";BYMONTH=",offsetof(struct icalrecurrencetype,by_month),12},
+ {";BYSETPOS=",offsetof(struct icalrecurrencetype,by_set_pos),366},
+ {0,0,0},
+};
+
+/* A private routine in icalvalue.c */
+void print_datetime_to_string(char* str, struct icaltimetype *data);
+
+char* icalrecurrencetype_as_string(struct icalrecurrencetype *recur)
+{
+ char* str;
+ char *str_p;
+ size_t buf_sz = 200;
+ char temp[20];
+ int i,j;
+
+ if(recur->freq == ICAL_NO_RECURRENCE){
+ return 0;
+ }
+
+ str = (char*)icalmemory_tmp_buffer(buf_sz);
+ str_p = str;
+
+ icalmemory_append_string(&str,&str_p,&buf_sz,"FREQ=");
+ icalmemory_append_string(&str,&str_p,&buf_sz,
+ icalrecur_freq_to_string(recur->freq));
+
+ if(recur->until.year != 0){
+
+ temp[0] = 0;
+ print_datetime_to_string(temp,&(recur->until));
+
+ icalmemory_append_string(&str,&str_p,&buf_sz,";UNTIL=");
+ icalmemory_append_string(&str,&str_p,&buf_sz, temp);
+ }
+
+ if(recur->count != 0){
+ sprintf(temp,"%d",recur->count);
+ icalmemory_append_string(&str,&str_p,&buf_sz,";COUNT=");
+ icalmemory_append_string(&str,&str_p,&buf_sz, temp);
+ }
+
+ if(recur->interval != 0){
+ sprintf(temp,"%d",recur->interval);
+ icalmemory_append_string(&str,&str_p,&buf_sz,";INTERVAL=");
+ icalmemory_append_string(&str,&str_p,&buf_sz, temp);
+ }
+
+ for(j =0; recurmap[j].str != 0; j++){
+ short* array = (short*)(recurmap[j].offset+ (size_t)recur);
+ short limit = recurmap[j].limit;
+
+ /* Skip unused arrays */
+ if( array[0] != ICAL_RECURRENCE_ARRAY_MAX ) {
+
+ icalmemory_append_string(&str,&str_p,&buf_sz,recurmap[j].str);
+
+ for(i=0;
+ i< limit && array[i] != ICAL_RECURRENCE_ARRAY_MAX;
+ i++){
+ if (j == 3) { /* BYDAY */
+ short dow = icalrecurrencetype_day_day_of_week(array[i]);
+ const char *daystr = icalrecur_weekday_to_string(dow);
+ short pos;
+
+ pos = icalrecurrencetype_day_position(array[i]);
+
+ if (pos == 0)
+ icalmemory_append_string(&str,&str_p,&buf_sz,daystr);
+ else {
+ sprintf(temp,"%d%s",pos,daystr);
+ icalmemory_append_string(&str,&str_p,&buf_sz,temp);
+ }
+
+ } else {
+ sprintf(temp,"%d",array[i]);
+ icalmemory_append_string(&str,&str_p,&buf_sz, temp);
+ }
+
+ if( (i+1)<limit &&array[i+1]
+ != ICAL_RECURRENCE_ARRAY_MAX){
+ icalmemory_append_char(&str,&str_p,&buf_sz,',');
+ }
+ }
+ }
+ }
+
+ return str;
+}
+#endif
+
+
+
+/************************* occurrence iteration routiens ******************/
+
+enum byrule {
+ NO_CONTRACTION = -1,
+ BY_SECOND = 0,
+ BY_MINUTE = 1,
+ BY_HOUR = 2,
+ BY_DAY = 3,
+ BY_MONTH_DAY = 4,
+ BY_YEAR_DAY = 5,
+ BY_WEEK_NO = 6,
+ BY_MONTH = 7,
+ BY_SET_POS
+};
+
+
+
+struct icalrecur_iterator_impl {
+
+ struct icaltimetype dtstart; /* Hack. Make into time_t */
+ struct icaltimetype last; /* last time return from _iterator_next*/
+ int occurrence_no; /* number of step made on t iterator */
+ struct icalrecurrencetype rule;
+
+ short days[366];
+ short days_index;
+
+ enum byrule byrule;
+ short by_indices[9];
+ short orig_data[9]; /* 1 if there was data in the byrule */
+
+
+ short *by_ptrs[9]; /* Pointers into the by_* array elements of the rule */
+
+};
+
+int icalrecur_iterator_sizeof_byarray(short* byarray)
+{
+ int array_itr;
+
+ for(array_itr = 0;
+ byarray[array_itr] != ICAL_RECURRENCE_ARRAY_MAX;
+ array_itr++){
+ }
+
+ return array_itr;
+}
+
+enum expand_table {
+ UNKNOWN = 0,
+ CONTRACT = 1,
+ EXPAND =2,
+ ILLEGAL=3
+};
+
+/* The split map indicates, for a particular interval, wether a BY_*
+ rule part expands the number of instances in the occcurrence set or
+ contracts it. 1=> contract, 2=>expand, and 3 means the pairing is
+ not allowed. */
+struct expand_split_map_struct
+{
+ icalrecurrencetype_frequency frequency;
+
+ /* Elements of the 'map' array correspond to the BYxxx rules:
+ Second,Minute,Hour,Day,Month Day,Year Day,Week No,Month*/
+
+ short map[8];
+};
+
+struct expand_split_map_struct expand_map[] =
+{
+ {ICAL_SECONDLY_RECURRENCE,{1,1,1,1,1,1,1,1}},
+ {ICAL_MINUTELY_RECURRENCE,{2,1,1,1,1,1,1,1}},
+ {ICAL_HOURLY_RECURRENCE, {2,2,1,1,1,1,1,1}},
+ {ICAL_DAILY_RECURRENCE, {2,2,2,1,1,1,1,1}},
+ {ICAL_WEEKLY_RECURRENCE, {2,2,2,2,3,3,1,1}},
+ {ICAL_MONTHLY_RECURRENCE, {2,2,2,2,2,3,3,1}},
+ {ICAL_YEARLY_RECURRENCE, {2,2,2,2,2,2,2,2}},
+ {ICAL_NO_RECURRENCE, {0,0,0,0,0,0,0,0}}
+
+};
+
+
+
+/* Check that the rule has only the two given interday byrule parts. */
+int icalrecur_two_byrule(struct icalrecur_iterator_impl* impl,
+ enum byrule one,enum byrule two)
+{
+ short test_array[9];
+ enum byrule itr;
+ int passes = 0;
+
+ memset(test_array,0,9);
+
+ test_array[one] = 1;
+ test_array[two] = 1;
+
+ for(itr = BY_DAY; itr != BY_SET_POS; itr++){
+
+ if( (test_array[itr] == 0 &&
+ impl->by_ptrs[itr][0] != ICAL_RECURRENCE_ARRAY_MAX
+ ) ||
+ (test_array[itr] == 1 &&
+ impl->by_ptrs[itr][0] == ICAL_RECURRENCE_ARRAY_MAX
+ )
+ ) {
+ /* test failed */
+ passes = 0;
+ }
+ }
+
+ return passes;
+
+}
+
+/* Check that the rule has only the one given interdat byrule parts. */
+int icalrecur_one_byrule(struct icalrecur_iterator_impl* impl,enum byrule one)
+{
+ int passes = 1;
+ enum byrule itr;
+
+ for(itr = BY_DAY; itr != BY_SET_POS; itr++){
+
+ if ((itr==one && impl->by_ptrs[itr][0] == ICAL_RECURRENCE_ARRAY_MAX) ||
+ (itr!=one && impl->by_ptrs[itr][0] != ICAL_RECURRENCE_ARRAY_MAX)) {
+ passes = 0;
+ }
+ }
+
+ return passes;
+}
+
+int count_byrules(struct icalrecur_iterator_impl* impl)
+{
+ int count = 0;
+ enum byrule itr;
+
+ for(itr = BY_DAY; itr <= BY_SET_POS; itr++){
+ if(impl->by_ptrs[itr][0] != ICAL_RECURRENCE_ARRAY_MAX){
+ count++;
+ }
+ }
+
+ return count;
+}
+
+
+void setup_defaults(struct icalrecur_iterator_impl* impl,
+ enum byrule byrule, icalrecurrencetype_frequency req,
+ short deftime, int *timepart)
+{
+
+ icalrecurrencetype_frequency freq;
+ freq = impl->rule.freq;
+
+ /* Re-write the BY rule arrays with data from the DTSTART time so
+ we don't have to explicitly deal with DTSTART */
+
+ if(impl->by_ptrs[byrule][0] == ICAL_RECURRENCE_ARRAY_MAX &&
+ expand_map[freq].map[byrule] != CONTRACT){
+ impl->by_ptrs[byrule][0] = deftime;
+ }
+
+ /* Initialize the first occurence */
+ if( freq != req && expand_map[freq].map[byrule] != CONTRACT){
+ *timepart = impl->by_ptrs[byrule][0];
+ }
+
+
+}
+
+int has_by_data(struct icalrecur_iterator_impl* impl, enum byrule byrule){
+
+ return (impl->orig_data[byrule] == 1);
+}
+
+
+int expand_year_days(struct icalrecur_iterator_impl* impl,short year);
+
+
+icalrecur_iterator* icalrecur_iterator_new(struct icalrecurrencetype rule,
+ struct icaltimetype dtstart)
+{
+ struct icalrecur_iterator_impl* impl;
+ icalrecurrencetype_frequency freq;
+
+ short days_in_month;
+
+ if ( ( impl = (struct icalrecur_iterator_impl *)
+ malloc(sizeof(struct icalrecur_iterator_impl))) == 0) {
+ icalerror_set_errno(ICAL_NEWFAILED_ERROR);
+ return 0;
+ }
+
+ memset(impl,0,sizeof(struct icalrecur_iterator_impl));
+
+ impl->rule = rule;
+ impl->last = dtstart;
+ impl->dtstart = dtstart;
+ impl->days_index =0;
+ impl->occurrence_no = 0;
+ freq = impl->rule.freq;
+
+ /* Set up convienience pointers to make the code simpler. Allows
+ us to iterate through all of the BY* arrays in the rule. */
+
+ impl->by_ptrs[BY_MONTH]=impl->rule.by_month;
+ impl->by_ptrs[BY_WEEK_NO]=impl->rule.by_week_no;
+ impl->by_ptrs[BY_YEAR_DAY]=impl->rule.by_year_day;
+ impl->by_ptrs[BY_MONTH_DAY]=impl->rule.by_month_day;
+ impl->by_ptrs[BY_DAY]=impl->rule.by_day;
+ impl->by_ptrs[BY_HOUR]=impl->rule.by_hour;
+ impl->by_ptrs[BY_MINUTE]=impl->rule.by_minute;
+ impl->by_ptrs[BY_SECOND]=impl->rule.by_second;
+ impl->by_ptrs[BY_SET_POS]=impl->rule.by_set_pos;
+
+ memset(impl->orig_data,0,9);
+
+ /* Note which by rules had data in them when the iterator was
+ created. We can't use the actuall by_x arrays, because the
+ empty ones will be given default values later in this
+ routine. The orig_data array will be used later in has_by_data */
+
+ impl->orig_data[BY_MONTH]
+ = (impl->rule.by_month[0]!=ICAL_RECURRENCE_ARRAY_MAX);
+ impl->orig_data[BY_WEEK_NO]
+ =(impl->rule.by_week_no[0]!=ICAL_RECURRENCE_ARRAY_MAX);
+ impl->orig_data[BY_YEAR_DAY]
+ =(impl->rule.by_year_day[0]!=ICAL_RECURRENCE_ARRAY_MAX);
+ impl->orig_data[BY_MONTH_DAY]
+ =(impl->rule.by_month_day[0]!=ICAL_RECURRENCE_ARRAY_MAX);
+ impl->orig_data[BY_DAY]
+ = (impl->rule.by_day[0]!=ICAL_RECURRENCE_ARRAY_MAX);
+ impl->orig_data[BY_HOUR]
+ = (impl->rule.by_hour[0]!=ICAL_RECURRENCE_ARRAY_MAX);
+ impl->orig_data[BY_MINUTE]
+ = (impl->rule.by_minute[0]!=ICAL_RECURRENCE_ARRAY_MAX);
+ impl->orig_data[BY_SECOND]
+ = (impl->rule.by_second[0]!=ICAL_RECURRENCE_ARRAY_MAX);
+ impl->orig_data[BY_SET_POS]
+ = (impl->rule.by_set_pos[0]!=ICAL_RECURRENCE_ARRAY_MAX);
+
+
+ /* Check if the recurrence rule is legal */
+
+ /* If the BYYEARDAY appears, no other date rule part may appear. */
+
+ if(icalrecur_two_byrule(impl,BY_YEAR_DAY,BY_MONTH) ||
+ icalrecur_two_byrule(impl,BY_YEAR_DAY,BY_WEEK_NO) ||
+ icalrecur_two_byrule(impl,BY_YEAR_DAY,BY_MONTH_DAY) ||
+ icalrecur_two_byrule(impl,BY_YEAR_DAY,BY_DAY) ){
+
+ icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
+
+ return 0;
+ }
+
+ /* BYWEEKNO and BYMONTH rule parts may not both appear.*/
+
+ if(icalrecur_two_byrule(impl,BY_WEEK_NO,BY_MONTH)){
+ icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
+
+ icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
+ return 0;
+ }
+
+ /* BYWEEKNO and BYMONTHDAY rule parts may not both appear.*/
+
+ if(icalrecur_two_byrule(impl,BY_WEEK_NO,BY_MONTH_DAY)){
+ icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
+
+ icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
+ return 0;
+ }
+
+
+ /*For MONTHLY recurrences (FREQ=MONTHLY) neither BYYEARDAY nor
+ BYWEEKNO may appear. */
+
+ if(freq == ICAL_MONTHLY_RECURRENCE &&
+ icalrecur_one_byrule(impl,BY_WEEK_NO)){
+ icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
+ return 0;
+ }
+
+
+ /*For WEEKLY recurrences (FREQ=WEEKLY) neither BYMONTHDAY nor
+ BYYEARDAY may appear. */
+
+ if(freq == ICAL_WEEKLY_RECURRENCE &&
+ icalrecur_one_byrule(impl,BY_MONTH_DAY )) {
+ icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
+ return 0;
+ }
+
+ /* BYYEARDAY may only appear in YEARLY rules */
+ if(freq != ICAL_YEARLY_RECURRENCE &&
+ icalrecur_one_byrule(impl,BY_YEAR_DAY )) {
+ icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
+ return 0;
+ }
+
+ /* Rewrite some of the rules and set up defaults to make later
+ processing easier. Primarily, t involves copying an element
+ from the start time into the coresponding BY_* array when the
+ BY_* array is empty */
+
+
+ setup_defaults(impl,BY_SECOND,ICAL_SECONDLY_RECURRENCE,impl->dtstart.second,
+ &(impl->last.second));
+
+ setup_defaults(impl,BY_MINUTE,ICAL_MINUTELY_RECURRENCE,impl->dtstart.minute,
+ &(impl->last.minute));
+
+ setup_defaults(impl,BY_HOUR,ICAL_HOURLY_RECURRENCE,impl->dtstart.hour,
+ &(impl->last.hour));
+
+ setup_defaults(impl,BY_MONTH_DAY,ICAL_DAILY_RECURRENCE,impl->dtstart.day,
+ &(impl->last.day));
+
+ setup_defaults(impl,BY_MONTH,ICAL_MONTHLY_RECURRENCE,impl->dtstart.month,
+ &(impl->last.month));
+
+
+ if(impl->rule.freq == ICAL_WEEKLY_RECURRENCE ){
+
+ if(impl->by_ptrs[BY_DAY][0] == ICAL_RECURRENCE_ARRAY_MAX){
+
+ /* Weekly recurrences with no BY_DAY data should occur on the
+ same day of the week as the start time . */
+ impl->by_ptrs[BY_DAY][0] = icaltime_day_of_week(impl->dtstart);
+
+ } else {
+ /* If there is BY_DAY data, then we need to move the initial
+ time to the start of the BY_DAY data. That is if the
+ start time is on a Wednesday, and the rule has
+ BYDAY=MO,WE,FR, move the initial time back to
+ monday. Otherwise, jumping to the next week ( jumping 7
+ days ahead ) will skip over some occurrences in the
+ second week. */
+
+ /* This is probably a HACK. There should be some more
+ general way to solve this problem */
+
+ short dow = impl->by_ptrs[BY_DAY][0]-icaltime_day_of_week(impl->last);
+
+ if(dow < 0) {
+ /* initial time is after first day of BY_DAY data */
+
+ impl->last.day += dow;
+ impl->last = icaltime_normalize(impl->last);
+ }
+ }
+
+
+ }
+
+ /* For YEARLY rule, begin by setting up the year days array */
+
+ if(impl->rule.freq == ICAL_YEARLY_RECURRENCE){
+ expand_year_days(impl,impl->last.year);
+ }
+
+
+ /* If this is a monthly interval with by day data, then we need to
+ set the last value to the appropriate day of the month */
+
+ if(impl->rule.freq == ICAL_MONTHLY_RECURRENCE &&
+ has_by_data(impl,BY_DAY)) {
+
+ short dow = icalrecurrencetype_day_day_of_week(
+ impl->by_ptrs[BY_DAY][impl->by_indices[BY_DAY]]);
+ short pos = icalrecurrencetype_day_position(
+ impl->by_ptrs[BY_DAY][impl->by_indices[BY_DAY]]);
+
+ short poscount = 0;
+ days_in_month =
+ icaltime_days_in_month(impl->last.month, impl->last.year);
+
+ if(pos >= 0){
+ /* Count up from the first day pf the month to find the
+ pos'th weekday of dow ( like the second monday. ) */
+
+ for(impl->last.day = 1;
+ impl->last.day <= days_in_month;
+ impl->last.day++){
+
+ if(icaltime_day_of_week(impl->last) == dow){
+ if(++poscount == pos || pos == 0){
+ break;
+ }
+ }
+ }
+ } else {
+ /* Count down from the last day pf the month to find the
+ pos'th weekday of dow ( like the second to last monday. ) */
+ pos = -pos;
+ for(impl->last.day = days_in_month;
+ impl->last.day != 0;
+ impl->last.day--){
+
+ if(icaltime_day_of_week(impl->last) == dow){
+ if(++poscount == pos ){
+ break;
+ }
+ }
+ }
+ }
+
+
+ if(impl->last.day > days_in_month || impl->last.day == 0){
+ icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
+ return 0;
+ }
+
+ }
+
+
+
+ return impl;
+}
+
+
+void icalrecur_iterator_free(icalrecur_iterator* i)
+{
+
+ struct icalrecur_iterator_impl* impl =
+ (struct icalrecur_iterator_impl*)i;
+
+ icalerror_check_arg_rv((impl!=0),"impl");
+
+ free(impl);
+
+}
+
+
+void increment_year(struct icalrecur_iterator_impl* impl, int inc)
+{
+ impl->last.year+=inc;
+}
+
+/* Increment month is different that the other incement_* routines --
+ it figures out the interval for itself, and uses BYMONTH data if
+ available. */
+void increment_month(struct icalrecur_iterator_impl* impl)
+{
+ int years;
+
+ if(has_by_data(impl,BY_MONTH) ){
+ /* Ignore the frequency and use the byrule data */
+
+ impl->by_indices[BY_MONTH]++;
+
+ if (impl->by_ptrs[BY_MONTH][impl->by_indices[BY_MONTH]]
+ ==ICAL_RECURRENCE_ARRAY_MAX){
+ impl->by_indices[BY_MONTH] = 0;
+
+ increment_year(impl,1);
+
+ }
+
+ impl->last.month =
+ impl->by_ptrs[BY_MONTH][impl->by_indices[BY_MONTH]];
+
+ } else {
+
+ int inc;
+
+ if(impl->rule.freq == ICAL_MONTHLY_RECURRENCE){
+ inc = impl->rule.interval;
+ } else {
+ inc = 1;
+ }
+
+ impl->last.month+=inc;
+
+ /* Months are offset by one */
+ impl->last.month--;
+
+ years = impl->last.month / 12;
+
+ impl->last.month = impl->last.month % 12;
+
+ impl->last.month++;
+
+ if (years != 0){
+ increment_year(impl,years);
+ }
+ }
+}
+
+void increment_monthday(struct icalrecur_iterator_impl* impl, int inc)
+{
+ int i;
+
+ for(i=0; i<inc; i++){
+
+ short days_in_month =
+ icaltime_days_in_month(impl->last.month,impl->last.year);
+
+ impl->last.day++;
+
+ if (impl->last.day > days_in_month){
+ impl->last.day = impl->last.day-days_in_month;
+ increment_month(impl);
+ }
+ }
+}
+
+
+void increment_hour(struct icalrecur_iterator_impl* impl, int inc)
+{
+ short days;
+
+ impl->last.hour+=inc;
+
+ days = impl->last.hour / 24;
+ impl->last.hour = impl->last.hour % 24;
+
+ if (impl->days != 0){
+ increment_monthday(impl,days);
+ }
+}
+
+void increment_minute(struct icalrecur_iterator_impl* impl, int inc)
+{
+ short hours;
+
+ impl->last.minute+=inc;
+
+ hours = impl->last.minute / 60;
+ impl->last.minute = impl->last.minute % 60;
+
+ if (hours != 0){
+ increment_hour(impl,hours);
+ }
+
+}
+
+void increment_second(struct icalrecur_iterator_impl* impl, int inc)
+{
+ short minutes;
+
+ impl->last.second+=inc;
+
+ minutes = impl->last.second / 60;
+ impl->last.second = impl->last.second % 60;
+
+ if (minutes != 0)
+ {
+ increment_minute(impl, minutes);
+ }
+}
+
+#if 0
+#include "ical.h"
+void test_increment()
+{
+ struct icalrecur_iterator_impl impl;
+
+ impl.last = icaltime_from_string("20000101T000000Z");
+
+ printf("Orig: %s\n",icaltime_as_ctime(impl.last));
+
+ increment_second(&impl,5);
+ printf("+ 5 sec : %s\n",icaltime_as_ctime(impl.last));
+
+ increment_second(&impl,355);
+ printf("+ 355 sec : %s\n",icaltime_as_ctime(impl.last));
+
+ increment_minute(&impl,5);
+ printf("+ 5 min : %s\n",icaltime_as_ctime(impl.last));
+
+ increment_minute(&impl,360);
+ printf("+ 360 min : %s\n",icaltime_as_ctime(impl.last));
+ increment_hour(&impl,5);
+ printf("+ 5 hours : %s\n",icaltime_as_ctime(impl.last));
+ increment_hour(&impl,43);
+ printf("+ 43 hours : %s\n",icaltime_as_ctime(impl.last));
+ increment_monthday(&impl,3);
+ printf("+ 3 days : %s\n",icaltime_as_ctime(impl.last));
+ increment_monthday(&impl,600);
+ printf("+ 600 days : %s\n",icaltime_as_ctime(impl.last));
+
+}
+
+#endif
+
+short next_second(struct icalrecur_iterator_impl* impl)
+{
+
+ short has_by_data = (impl->by_ptrs[BY_SECOND][0]!=ICAL_RECURRENCE_ARRAY_MAX);
+ short this_frequency = (impl->rule.freq == ICAL_SECONDLY_RECURRENCE);
+
+ short end_of_data = 0;
+
+ assert(has_by_data || this_frequency);
+
+ if( has_by_data ){
+ /* Ignore the frequency and use the byrule data */
+
+ impl->by_indices[BY_SECOND]++;
+
+ if (impl->by_ptrs[BY_SECOND][impl->by_indices[BY_SECOND]]
+ ==ICAL_RECURRENCE_ARRAY_MAX){
+ impl->by_indices[BY_SECOND] = 0;
+
+ end_of_data = 1;
+ }
+
+
+ impl->last.second =
+ impl->by_ptrs[BY_SECOND][impl->by_indices[BY_SECOND]];
+
+
+ } else if( !has_by_data && this_frequency ){
+ /* Compute the next value from the last time and the frequency interval*/
+ increment_second(impl, impl->rule.interval);
+
+ }
+
+ /* If we have gone through all of the seconds on the BY list, then we
+ need to move to the next minute */
+
+ if(has_by_data && end_of_data && this_frequency ){
+ increment_minute(impl,1);
+ }
+
+ return end_of_data;
+
+}
+
+int next_minute(struct icalrecur_iterator_impl* impl)
+{
+
+ short has_by_data = (impl->by_ptrs[BY_MINUTE][0]!=ICAL_RECURRENCE_ARRAY_MAX);
+ short this_frequency = (impl->rule.freq == ICAL_MINUTELY_RECURRENCE);
+
+ short end_of_data = 0;
+
+ assert(has_by_data || this_frequency);
+
+
+ if (next_second(impl) == 0){
+ return 0;
+ }
+
+ if( has_by_data ){
+ /* Ignore the frequency and use the byrule data */
+
+ impl->by_indices[BY_MINUTE]++;
+
+ if (impl->by_ptrs[BY_MINUTE][impl->by_indices[BY_MINUTE]]
+ ==ICAL_RECURRENCE_ARRAY_MAX){
+
+ impl->by_indices[BY_MINUTE] = 0;
+
+ end_of_data = 1;
+ }
+
+ impl->last.minute =
+ impl->by_ptrs[BY_MINUTE][impl->by_indices[BY_MINUTE]];
+
+ } else if( !has_by_data && this_frequency ){
+ /* Compute the next value from the last time and the frequency interval*/
+ increment_minute(impl,impl->rule.interval);
+ }
+
+/* If we have gone through all of the minutes on the BY list, then we
+ need to move to the next hour */
+
+ if(has_by_data && end_of_data && this_frequency ){
+ increment_hour(impl,1);
+ }
+
+ return end_of_data;
+}
+
+int next_hour(struct icalrecur_iterator_impl* impl)
+{
+
+ short has_by_data = (impl->by_ptrs[BY_HOUR][0]!=ICAL_RECURRENCE_ARRAY_MAX);
+ short this_frequency = (impl->rule.freq == ICAL_HOURLY_RECURRENCE);
+
+ short end_of_data = 0;
+
+ assert(has_by_data || this_frequency);
+
+ if (next_minute(impl) == 0){
+ return 0;
+ }
+
+ if( has_by_data ){
+ /* Ignore the frequency and use the byrule data */
+
+ impl->by_indices[BY_HOUR]++;
+
+ if (impl->by_ptrs[BY_HOUR][impl->by_indices[BY_HOUR]]
+ ==ICAL_RECURRENCE_ARRAY_MAX){
+ impl->by_indices[BY_HOUR] = 0;
+
+ end_of_data = 1;
+ }
+
+ impl->last.hour =
+ impl->by_ptrs[BY_HOUR][impl->by_indices[BY_HOUR]];
+
+ } else if( !has_by_data && this_frequency ){
+ /* Compute the next value from the last time and the frequency interval*/
+ increment_hour(impl,impl->rule.interval);
+
+ }
+
+ /* If we have gone through all of the hours on the BY list, then we
+ need to move to the next day */
+
+ if(has_by_data && end_of_data && this_frequency ){
+ increment_monthday(impl,1);
+ }
+
+ return end_of_data;
+
+}
+
+int next_day(struct icalrecur_iterator_impl* impl)
+{
+
+ short has_by_data = (impl->by_ptrs[BY_DAY][0]!=ICAL_RECURRENCE_ARRAY_MAX);
+ short this_frequency = (impl->rule.freq == ICAL_DAILY_RECURRENCE);
+
+ assert(has_by_data || this_frequency);
+
+ if (next_hour(impl) == 0){
+ return 0;
+ }
+
+ /* Always increment through the interval, since this routine is not
+ called by any other next_* routine, and the days that are
+ excluded will be taken care of by restriction filtering */
+
+ if(this_frequency){
+ increment_monthday(impl,impl->rule.interval);
+ } else {
+ increment_monthday(impl,1);
+ }
+
+
+ return 0;
+
+}
+
+
+int next_yearday(struct icalrecur_iterator_impl* impl)
+{
+
+ short has_by_data = (impl->by_ptrs[BY_YEAR_DAY][0]!=ICAL_RECURRENCE_ARRAY_MAX);
+
+ short end_of_data = 0;
+
+ assert(has_by_data );
+
+ if (next_hour(impl) == 0){
+ return 0;
+ }
+
+ impl->by_indices[BY_YEAR_DAY]++;
+
+ if (impl->by_ptrs[BY_YEAR_DAY][impl->by_indices[BY_YEAR_DAY]]
+ ==ICAL_RECURRENCE_ARRAY_MAX){
+ impl->by_indices[BY_YEAR_DAY] = 0;
+
+ end_of_data = 1;
+ }
+
+ impl->last.day =
+ impl->by_ptrs[BY_YEAR_DAY][impl->by_indices[BY_YEAR_DAY]];
+
+ if(has_by_data && end_of_data){
+ increment_year(impl,1);
+ }
+
+ return end_of_data;
+
+}
+
+/* This routine is only called by next_week. It is certain that BY_DAY
+has data */
+
+int next_weekday_by_week(struct icalrecur_iterator_impl* impl)
+{
+
+ short end_of_data = 0;
+ short start_of_week, dow;
+ struct icaltimetype next;
+
+ if (next_hour(impl) == 0){
+ return 0;
+ }
+
+ assert( impl->by_ptrs[BY_DAY][0]!=ICAL_RECURRENCE_ARRAY_MAX);
+
+ while(1) {
+
+ impl->by_indices[BY_DAY]++; /* Look at next elem in BYDAY array */
+
+ /* Are we at the end of the BYDAY array? */
+ if (impl->by_ptrs[BY_DAY][impl->by_indices[BY_DAY]]
+ ==ICAL_RECURRENCE_ARRAY_MAX){
+
+ impl->by_indices[BY_DAY] = 0; /* Reset to 0 */
+ end_of_data = 1; /* Signal that we're at the end */
+ }
+
+ /* Add the day of week offset to to the start of this week, and use
+ that to get the next day */
+ dow = impl->by_ptrs[BY_DAY][impl->by_indices[BY_DAY]];
+ start_of_week = icaltime_start_doy_of_week(impl->last);
+
+ dow--; /*Sun is 1, not 0 */
+
+ if(dow+start_of_week <1 && !end_of_data){
+ /* The selected date is in the previous year. */
+ continue;
+ }
+
+ next = icaltime_from_day_of_year(start_of_week + dow,impl->last.year);
+
+ impl->last.day = next.day;
+ impl->last.month = next.month;
+ impl->last.year = next.year;
+
+ return end_of_data;
+ }
+
+}
+
+int nth_weekday(short dow, short pos, struct icaltimetype t){
+
+ short days_in_month = icaltime_days_in_month(t.month,t.year);
+ short end_dow, start_dow;
+ short wd;
+
+ if(pos >= 0){
+ t.day = 1;
+ start_dow = icaltime_day_of_week(t);
+
+ if (pos != 0) {
+ pos--;
+ }
+
+ /* find month day of first occurrence of dow -- such as the
+ month day of the first monday */
+
+ wd = dow-start_dow+1;
+
+ if (wd <= 0){
+ wd = wd + 7;
+ }
+
+ wd = wd + pos * 7;
+
+ } else {
+ t.day = days_in_month;
+ end_dow = icaltime_day_of_week(t);
+
+ pos++;
+
+ /* find month day of last occurrence of dow -- such as the
+ month day of the last monday */
+
+ wd = (end_dow - dow);
+
+ if (wd < 0){
+ wd = wd+ 7;
+ }
+
+ wd = days_in_month - wd;
+
+ wd = wd + pos * 7;
+ }
+
+ return wd;
+}
+
+
+int next_month(struct icalrecur_iterator_impl* impl)
+{
+ int data_valid = 1;
+
+ short this_frequency = (impl->rule.freq == ICAL_MONTHLY_RECURRENCE);
+
+ assert( has_by_data(impl,BY_MONTH) || this_frequency);
+
+ /* Iterate through the occurrences within a day. If we don't get to
+ the end of the intra-day data, don't bother going to the next
+ month */
+
+ if (next_hour(impl) == 0){
+ return data_valid; /* Signal that the data is valid */
+ }
+
+
+ /* Now iterate through the occurrences within a month -- by days,
+ weeks or weekdays. */
+
+ if(has_by_data(impl,BY_DAY) && has_by_data(impl,BY_MONTH_DAY)){
+ /* Cases like: FREQ=MONTHLY;INTERVAL=1;BYDAY=FR;BYMONTHDAY=13 */
+ short day, idx,j;
+ short days_in_month = icaltime_days_in_month(impl->last.month,
+ impl->last.year);
+ /* Iterate through the remaining days in the month and check if
+ each day is listed in the BY_DAY array and in the BY_MONTHDAY
+ array. This seems very inneficient, but I think it is the
+ simplest way to account for both BYDAY=1FR (First friday in
+ month) and BYDAY=FR ( every friday in month ) */
+
+ for(day = impl->last.day+1; day <= days_in_month; day++){
+ for(idx = 0; BYDAYPTR[idx] != ICAL_RECURRENCE_ARRAY_MAX; idx++){
+ for(j = 0; BYMDPTR[j]!=ICAL_RECURRENCE_ARRAY_MAX; j++){
+ short dow =
+ icalrecurrencetype_day_day_of_week(BYDAYPTR[idx]);
+ short pos = icalrecurrencetype_day_position(BYDAYPTR[idx]);
+ short mday = BYMDPTR[j];
+ short this_dow;
+
+ impl->last.day = day;
+ this_dow = icaltime_day_of_week(impl->last);
+
+ if( (pos == 0 && dow == this_dow && mday == day) ||
+ (nth_weekday(dow,pos,impl->last) == day && mday==day)){
+ goto MDEND;
+ }
+ }
+ }
+ }
+
+ MDEND:
+
+ if ( day > days_in_month){
+ impl->last.day = 1;
+ increment_month(impl);
+ data_valid = 0; /* signal that impl->last is invalid */
+ }
+
+
+ } else if(has_by_data(impl,BY_DAY)){
+ /* Cases like: FREQ=MONTHLY;INTERVAL=1;BYDAY=FR */
+ /* For this case, the weekdays are relative to the
+ month. BYDAY=FR -> First Friday in month, etc. */
+
+ short day, idx;
+ short days_in_month = icaltime_days_in_month(impl->last.month,
+ impl->last.year);
+
+ assert( BYDAYPTR[0]!=ICAL_RECURRENCE_ARRAY_MAX);
+
+ /* Iterate through the remaining days in the month and check if
+ each day is listed in the BY_DAY array. This seems very
+ inneficient, but I think it is the simplest way to account
+ for both BYDAY=1FR (First friday in month) and BYDAY=FR (
+ every friday in month ) */
+
+ for(day = impl->last.day+1; day <= days_in_month; day++){
+ for(idx = 0; BYDAYPTR[idx] != ICAL_RECURRENCE_ARRAY_MAX; idx++){
+ short dow = icalrecurrencetype_day_day_of_week(BYDAYPTR[idx]);
+ short pos = icalrecurrencetype_day_position(BYDAYPTR[idx]);
+ short this_dow;
+
+ impl->last.day = day;
+ this_dow = icaltime_day_of_week(impl->last);
+
+ if( (pos == 0 && dow == this_dow ) ||
+ (nth_weekday(dow,pos,impl->last) == day)){
+ goto DEND;
+ }
+ }
+ }
+
+ DEND:
+
+ if ( day > days_in_month){
+ impl->last.day = 1;
+ increment_month(impl);
+ data_valid = 0; /* signal that impl->last is invalid */
+ }
+
+ } else if (has_by_data(impl,BY_MONTH_DAY)) {
+ /* Cases like: FREQ=MONTHLY;COUNT=10;BYMONTHDAY=-3 */
+ short day;
+
+ assert( BYMDPTR[0]!=ICAL_RECURRENCE_ARRAY_MAX);
+
+ BYMDIDX++;
+
+ /* Are we at the end of the BYDAY array? */
+ if (BYMDPTR[BYMDIDX] ==ICAL_RECURRENCE_ARRAY_MAX){
+
+ BYMDIDX = 0; /* Reset to 0 */
+ increment_month(impl);
+ }
+
+ day = BYMDPTR[BYMDIDX];
+
+ if (day < 0) {
+ day = icaltime_days_in_month(impl->last.month,impl->last.year)+
+ day + 1;
+ }
+
+ impl->last.day = day;
+
+ } else {
+ increment_month(impl);
+ }
+
+ return data_valid; /* Signal that the data is valid */
+
+}
+
+
+int next_week(struct icalrecur_iterator_impl* impl)
+{
+ short has_by_data = (impl->by_ptrs[BY_WEEK_NO][0]!=ICAL_RECURRENCE_ARRAY_MAX);
+ short this_frequency = (impl->rule.freq == ICAL_WEEKLY_RECURRENCE);
+ short end_of_data = 0;
+
+ /* Increment to the next week day */
+ if (next_weekday_by_week(impl) == 0){
+ return 0; /* Have not reached end of week yet */
+ }
+
+ /* If we get here, we have incremented through the entire week, and
+ can increment to the next week */
+
+
+ if( has_by_data){
+ /* Use the Week Number byrule data */
+ int week_no;
+ struct icaltimetype t;
+
+ impl->by_indices[BY_WEEK_NO]++;
+
+ if (impl->by_ptrs[BY_WEEK_NO][impl->by_indices[BY_WEEK_NO]]
+ ==ICAL_RECURRENCE_ARRAY_MAX){
+ impl->by_indices[BY_WEEK_NO] = 0;
+
+ end_of_data = 1;
+ }
+
+ t = impl->last;
+ t.month=1; /* HACK, should be setting to the date of the first week of year*/
+ t.day=1;
+
+ week_no = impl->by_ptrs[BY_WEEK_NO][impl->by_indices[BY_WEEK_NO]];
+
+ impl->last.day += week_no*7;
+
+ impl->last = icaltime_normalize(impl->last);
+
+ } else if( !has_by_data && this_frequency ){
+ /* If there is no BY_WEEK_NO data, just jump forward 7 days. */
+ increment_monthday(impl,7*impl->rule.interval);
+ }
+
+
+ if(has_by_data && end_of_data && this_frequency ){
+ increment_year(impl,1);
+ }
+
+ return end_of_data;
+
+}
+
+/* Expand the BYDAY rule part and return a pointer to a newly allocated list of days. */
+pvl_list expand_by_day(struct icalrecur_iterator_impl* impl,short year)
+{
+ /* Try to calculate each of the occurrences. */
+ int i;
+ pvl_list days_list = pvl_newlist();
+
+ short start_dow, end_dow, end_year_day, start_doy;
+ struct icaltimetype tmp = impl->last;
+
+ tmp.year= year;
+ tmp.month = 1;
+ tmp.day = 1;
+ tmp.is_date = 1;
+
+ start_dow = icaltime_day_of_week(tmp);
+ start_doy = icaltime_start_doy_of_week(tmp);
+
+ /* Get the last day of the year*/
+ tmp.year++;
+ tmp = icaltime_normalize(tmp);
+ tmp.day--;
+ tmp = icaltime_normalize(tmp);
+
+ end_dow = icaltime_day_of_week(tmp);
+ end_year_day = icaltime_day_of_year(tmp);
+
+ for(i = 0; BYDAYPTR[i] != ICAL_RECURRENCE_ARRAY_MAX; i++){
+ short dow =
+ icalrecurrencetype_day_day_of_week(BYDAYPTR[i]);
+ short pos = icalrecurrencetype_day_position(BYDAYPTR[i]);
+
+ if(pos == 0){
+ /* The day was specified without a position -- it is just
+ a bare day of the week ( BYDAY=SU) so add all of the
+ days of the year with this day-of-week*/
+ int week;
+ for(week = 0; week < 52 ; week ++){
+ short doy = start_doy + (week * 7) + dow-1;
+
+ if(doy > end_year_day){
+ break;
+ } else {
+ pvl_push(days_list,(void*)(int)doy);
+ }
+ }
+ } else if ( pos > 0) {
+ int first;
+ /* First occurrence of dow in year */
+ if( dow >= start_dow) {
+ first = dow - start_dow + 1;
+ } else {
+ first = dow - start_dow + 8;
+ }
+
+ /* THen just multiple the position times 7 to get the pos'th day in the year */
+ pvl_push(days_list,(void*)(first+ (pos-1) * 7));
+
+ } else { /* pos < 0 */
+ int last;
+ pos = -pos;
+
+ /* last occurrence of dow in year */
+ if( dow <= end_dow) {
+ last = end_year_day - end_dow + dow;
+ } else {
+ last = end_year_day - end_dow + dow - 7;
+ }
+
+ pvl_push(days_list,(void*)(last - (pos-1) * 7));
+ }
+ }
+ return days_list;
+}
+
+
+/* For INTERVAL=YEARLY, set up the days[] array in the iterator to
+ list all of the days of the current year that are specified in this
+ rule. */
+
+int expand_year_days(struct icalrecur_iterator_impl* impl,short year)
+{
+ int j,k;
+ int days_index=0;
+ struct icaltimetype t;
+ int flags;
+
+ t = icaltime_null_time();
+
+#define HBD(x) has_by_data(impl,x)
+
+ t.is_date = 1; /* Needed to make day_of_year routines work property */
+
+ memset(&t,0,sizeof(t));
+ memset(impl->days,ICAL_RECURRENCE_ARRAY_MAX_BYTE,sizeof(impl->days));
+
+ flags = (HBD(BY_DAY) ? 1<<BY_DAY : 0) +
+ (HBD(BY_WEEK_NO) ? 1<<BY_WEEK_NO : 0) +
+ (HBD(BY_MONTH_DAY) ? 1<<BY_MONTH_DAY : 0) +
+ (HBD(BY_MONTH) ? 1<<BY_MONTH : 0) +
+ (HBD(BY_YEAR_DAY) ? 1<<BY_YEAR_DAY : 0);
+
+
+ switch(flags) {
+
+ case 0: {
+ /* FREQ=YEARLY; */
+
+ break;
+ }
+ case 1<<BY_MONTH: {
+ /* FREQ=YEARLY; BYMONTH=3,11*/
+
+ for(j=0;impl->by_ptrs[BY_MONTH][j]!=ICAL_RECURRENCE_ARRAY_MAX;j++){
+ struct icaltimetype t;
+ short month = impl->by_ptrs[BY_MONTH][j];
+ short doy;
+
+ t = impl->dtstart;
+ t.year = year;
+ t.month = month;
+ t.is_date = 1;
+
+ doy = icaltime_day_of_year(t);
+
+ impl->days[days_index++] = doy;
+
+ }
+ break;
+ }
+
+ case 1<<BY_MONTH_DAY: {
+ /* FREQ=YEARLY; BYMONTHDAY=1,15*/
+ for(k=0;impl->by_ptrs[BY_MONTH_DAY][k]!=ICAL_RECURRENCE_ARRAY_MAX;k++)
+ {
+ short month_day = impl->by_ptrs[BY_MONTH_DAY][k];
+ short doy;
+
+ t = impl->dtstart;
+ t.day = month_day;
+ t.year = year;
+ t.is_date = 1;
+
+ doy = icaltime_day_of_year(t);
+
+ impl->days[days_index++] = doy;
+
+ }
+ break;
+ }
+
+ case (1<<BY_MONTH_DAY) + (1<<BY_MONTH): {
+ /* FREQ=YEARLY; BYMONTHDAY=1,15; BYMONTH=10 */
+
+ for(j=0;impl->by_ptrs[BY_MONTH][j]!=ICAL_RECURRENCE_ARRAY_MAX;j++){
+ for(k=0;impl->by_ptrs[BY_MONTH_DAY][k]!=ICAL_RECURRENCE_ARRAY_MAX;k++)
+ {
+ short month = impl->by_ptrs[BY_MONTH][j];
+ short month_day = impl->by_ptrs[BY_MONTH_DAY][k];
+ short doy;
+
+ t.day = month_day;
+ t.month = month;
+ t.year = year;
+ t.is_date = 1;
+
+ doy = icaltime_day_of_year(t);
+
+ impl->days[days_index++] = doy;
+
+ }
+ }
+
+ break;
+ }
+
+ case 1<<BY_WEEK_NO: {
+ /* FREQ=YEARLY; BYWEEKNO=20,50 */
+
+ struct icaltimetype t;
+ short dow;
+
+ t.day = impl->dtstart.day;
+ t.month = impl->dtstart.month;
+ t.year = year;
+ t.is_date = 1;
+
+ dow = icaltime_day_of_week(t);
+ /* HACK Not finished */
+
+ icalerror_set_errno(ICAL_UNIMPLEMENTED_ERROR);
+
+ break;
+ }
+
+ case (1<<BY_WEEK_NO) + (1<<BY_MONTH_DAY): {
+ /*FREQ=YEARLY; WEEKNO=20,50; BYMONTH= 6,11 */
+ icalerror_set_errno(ICAL_UNIMPLEMENTED_ERROR);
+ break;
+ }
+
+ case 1<<BY_DAY: {
+ /*FREQ=YEARLY; BYDAY=TH,20MO,-10FR*/
+ int days_index = 0;
+ pvl_elem i;
+ pvl_list days = expand_by_day(impl,year);
+
+
+ for(i=pvl_head(days);i!=0;i=pvl_next(i)){
+ short day = (short)(int)pvl_data(i);
+ impl->days[days_index++] = day;
+ }
+
+ pvl_free(days);
+
+ break;
+ }
+
+ case (1<<BY_DAY)+(1<<BY_MONTH): {
+ /*FREQ=YEARLY; BYDAY=TH,20MO,-10FR; BYMONTH = 12*/
+
+ int days_index = 0;
+ pvl_elem itr;
+ pvl_list days = expand_by_day(impl,year);
+
+ for(itr=pvl_head(days);itr!=0;itr=pvl_next(itr)){
+ short doy = (short)(int)pvl_data(itr);
+ struct icaltimetype tt;
+ short j;
+
+ tt = icaltime_from_day_of_year(doy,year);
+
+ for(j=0;
+ impl->by_ptrs[BY_MONTH][j]!=ICAL_RECURRENCE_ARRAY_MAX;
+ j++){
+ short month = impl->by_ptrs[BY_MONTH][j];
+
+ if(tt.month == month){
+ impl->days[days_index++] = doy;
+ }
+ }
+
+ }
+
+ pvl_free(days);
+
+ break;
+ }
+
+ case (1<<BY_DAY) + (1<<BY_MONTH_DAY) : {
+ /*FREQ=YEARLY; BYDAY=TH,20MO,-10FR; BYMONTHDAY=1,15*/
+
+ int days_index = 0;
+ pvl_elem itr;
+ pvl_list days = expand_by_day(impl,year);
+
+ for(itr=pvl_head(days);itr!=0;itr=pvl_next(itr)){
+ short day = (short)(int)pvl_data(itr);
+ struct icaltimetype tt;
+ short j;
+
+ tt = icaltime_from_day_of_year(day,year);
+
+ for(j = 0; BYMDPTR[j]!=ICAL_RECURRENCE_ARRAY_MAX; j++){
+ short mday = BYMDPTR[j];
+
+ if(tt.day == mday){
+ impl->days[days_index++] = day;
+ }
+ }
+
+ }
+
+ pvl_free(days);
+
+ break;
+ }
+
+ case (1<<BY_DAY) + (1<<BY_MONTH_DAY) + (1<<BY_MONTH): {
+ /*FREQ=YEARLY; BYDAY=TH,20MO,-10FR; BYMONTHDAY=10; MYMONTH=6,11*/
+
+ int days_index = 0;
+ pvl_elem itr;
+ pvl_list days = expand_by_day(impl,year);
+
+ for(itr=pvl_head(days);itr!=0;itr=pvl_next(itr)){
+ short day = (short)(int)pvl_data(itr);
+ struct icaltimetype tt;
+ short i,j;
+
+ tt = icaltime_from_day_of_year(day,year);
+
+ for(i = 0; BYMONPTR[i] != ICAL_RECURRENCE_ARRAY_MAX; i++){
+ for(j = 0; BYMDPTR[j]!=ICAL_RECURRENCE_ARRAY_MAX; j++){
+ short mday = BYMDPTR[j];
+ short month = BYMONPTR[i];
+
+ if(tt.month == month && tt.day == mday){
+ impl->days[days_index++] = day;
+ }
+ }
+ }
+
+ }
+
+ pvl_free(days);
+
+ break;
+
+ }
+
+ case (1<<BY_DAY) + (1<<BY_WEEK_NO) : {
+ /*FREQ=YEARLY; BYDAY=TH,20MO,-10FR; WEEKNO=20,50*/
+
+ int days_index = 0;
+ pvl_elem itr;
+ pvl_list days = expand_by_day(impl,year);
+
+ for(itr=pvl_head(days);itr!=0;itr=pvl_next(itr)){
+ short day = (short)(int)pvl_data(itr);
+ struct icaltimetype tt;
+ short i;
+
+ tt = icaltime_from_day_of_year(day,year);
+
+ for(i = 0; BYWEEKPTR[i] != ICAL_RECURRENCE_ARRAY_MAX; i++){
+ short weekno = BYWEEKPTR[i];
+
+ if(weekno== icaltime_week_number(tt)){
+ impl->days[days_index++] = day;
+ }
+ }
+
+ }
+
+ pvl_free(days);
+ break;
+ }
+
+ case (1<<BY_DAY) + (1<<BY_WEEK_NO) + (1<<BY_MONTH_DAY): {
+ /*FREQ=YEARLY; BYDAY=TH,20MO,-10FR; WEEKNO=20,50; BYMONTHDAY=1,15*/
+ icalerror_set_errno(ICAL_UNIMPLEMENTED_ERROR);
+ break;
+ }
+
+ case 1<<BY_YEAR_DAY: {
+ for(j=0;impl->by_ptrs[BY_YEAR_DAY][j]!=ICAL_RECURRENCE_ARRAY_MAX;j++){
+ short doy = impl->by_ptrs[BY_YEAR_DAY][j];
+ impl->days[days_index++] = doy;
+ }
+ break;
+ }
+
+ default: {
+ icalerror_set_errno(ICAL_UNIMPLEMENTED_ERROR);
+ break;
+ }
+
+ }
+
+ return 0;
+}
+
+
+int next_year(struct icalrecur_iterator_impl* impl)
+{
+ struct icaltimetype next;
+
+ /* Next_year does it's own interatio in days, so the next level down is hours */
+ if (next_hour(impl) == 0){
+ return 1;
+ }
+
+ if (impl->days[++impl->days_index] == ICAL_RECURRENCE_ARRAY_MAX){
+ impl->days_index = 0;
+ increment_year(impl,impl->rule.interval);
+ expand_year_days(impl,impl->last.year);
+ }
+
+ if(impl->days[0] == ICAL_RECURRENCE_ARRAY_MAX) {
+ return 0;
+ }
+
+ next = icaltime_from_day_of_year(impl->days[impl->days_index],impl->last.year);
+
+ impl->last.day = next.day;
+ impl->last.month = next.month;
+
+ return 1;
+}
+
+int icalrecur_check_rulepart(struct icalrecur_iterator_impl* impl,
+ short v, enum byrule byrule)
+{
+ int itr;
+
+ if(impl->by_ptrs[byrule][0]!=ICAL_RECURRENCE_ARRAY_MAX){
+ for(itr=0; impl->by_ptrs[byrule][itr]!=ICAL_RECURRENCE_ARRAY_MAX;itr++){
+ if(impl->by_ptrs[byrule][itr] == v){
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+int check_contract_restriction(struct icalrecur_iterator_impl* impl,
+ enum byrule byrule, short v)
+{
+ int pass = 0;
+ int itr;
+ icalrecurrencetype_frequency freq = impl->rule.freq;
+
+ if(impl->by_ptrs[byrule][0]!=ICAL_RECURRENCE_ARRAY_MAX &&
+ expand_map[freq].map[byrule] == CONTRACT){
+ for(itr=0; impl->by_ptrs[byrule][itr]!=ICAL_RECURRENCE_ARRAY_MAX;itr++){
+ if(impl->by_ptrs[byrule][itr] == v){
+ pass=1;
+ break;
+ }
+ }
+
+ return pass;
+ } else {
+ /* This is not a contracting byrule, or it has no data, so the
+ test passes*/
+ return 1;
+ }
+}
+
+
+int check_contracting_rules(struct icalrecur_iterator_impl* impl)
+{
+
+ int day_of_week=0;
+ int week_no=0;
+ int year_day=0;
+
+ if (
+ check_contract_restriction(impl,BY_SECOND,impl->last.second) &&
+ check_contract_restriction(impl,BY_MINUTE,impl->last.minute) &&
+ check_contract_restriction(impl,BY_HOUR,impl->last.hour) &&
+ check_contract_restriction(impl,BY_DAY,day_of_week) &&
+ check_contract_restriction(impl,BY_WEEK_NO,week_no) &&
+ check_contract_restriction(impl,BY_MONTH_DAY,impl->last.day) &&
+ check_contract_restriction(impl,BY_MONTH,impl->last.month) &&
+ check_contract_restriction(impl,BY_YEAR_DAY,year_day) )
+ {
+
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+struct icaltimetype icalrecur_iterator_next(icalrecur_iterator *itr)
+{
+ int valid = 1;
+ struct icalrecur_iterator_impl* impl =
+ (struct icalrecur_iterator_impl*)itr;
+
+ if( (impl->rule.count!=0 &&impl->occurrence_no >= impl->rule.count) ||
+ (!icaltime_is_null_time(impl->rule.until) &&
+ icaltime_compare(impl->last,impl->rule.until) > 0)) {
+ return icaltime_null_time();
+ }
+
+ if(impl->occurrence_no == 0
+ && icaltime_compare(impl->last,impl->dtstart) >= 0){
+
+ impl->occurrence_no++;
+ return impl->last;
+ }
+
+ do {
+ valid = 1;
+ switch(impl->rule.freq){
+
+ case ICAL_SECONDLY_RECURRENCE: {
+ next_second(impl);
+ break;
+ }
+ case ICAL_MINUTELY_RECURRENCE: {
+ next_minute(impl);
+ break;
+ }
+ case ICAL_HOURLY_RECURRENCE: {
+ next_hour(impl);
+ break;
+ }
+ case ICAL_DAILY_RECURRENCE: {
+ next_day(impl);
+ break;
+ }
+ case ICAL_WEEKLY_RECURRENCE: {
+ next_week(impl);
+ break;
+ }
+ case ICAL_MONTHLY_RECURRENCE: {
+ valid = next_month(impl);
+ break;
+ }
+ case ICAL_YEARLY_RECURRENCE:{
+ valid = next_year(impl);
+ break;
+ }
+ default:{
+ icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
+ return icaltime_null_time();
+ }
+ }
+
+ if(impl->last.year >= 2038 ){
+ /* HACK */
+ return icaltime_null_time();
+ }
+
+ } while(!check_contracting_rules(impl)
+ || icaltime_compare(impl->last,impl->dtstart) <= 0
+ || valid == 0);
+
+
+/* Ignore null times and times that are after the until time */
+ if( !icaltime_is_null_time(impl->rule.until) &&
+ icaltime_compare(impl->last,impl->rule.until) > 0 ) {
+ return icaltime_null_time();
+ }
+
+ impl->occurrence_no++;
+
+ return impl->last;
+}
+
+
+/************************** Type Routines **********************/
+
+
+void icalrecurrencetype_clear(struct icalrecurrencetype *recur)
+{
+ memset(recur,ICAL_RECURRENCE_ARRAY_MAX_BYTE,
+ sizeof(struct icalrecurrencetype));
+
+ recur->week_start = ICAL_MONDAY_WEEKDAY;
+ recur->freq = ICAL_NO_RECURRENCE;
+ recur->interval = 1;
+ memset(&(recur->until),0,sizeof(struct icaltimetype));
+ recur->count = 0;
+}
+
+/* The 'day' element of icalrecurrencetype_weekday is encoded to allow
+reporesentation of both the day of the week ( Monday, Tueday), but
+also the Nth day of the week ( First tuesday of the month, last
+thursday of the year) These routines decode the day values.
+
+The day's position in the period ( Nth-ness) and the numerical value
+of the day are encoded together as: pos*7 + dow
+
+A position of 0 means 'any' or 'every'
+
+ */
+
+enum icalrecurrencetype_weekday icalrecurrencetype_day_day_of_week(short day)
+{
+ return abs(day)%8;
+}
+
+short icalrecurrencetype_day_position(short day)
+{
+ short wd, pos;
+
+ wd = icalrecurrencetype_day_day_of_week(day);
+
+ pos = (abs(day)-wd)/8 * ((day<0)?-1:1);
+
+
+ return pos;
+}
+
+
+/****************** Enumeration Routines ******************/
+
+struct {icalrecurrencetype_weekday wd; const char * str; }
+wd_map[] = {
+ {ICAL_SUNDAY_WEEKDAY,"SU"},
+ {ICAL_MONDAY_WEEKDAY,"MO"},
+ {ICAL_TUESDAY_WEEKDAY,"TU"},
+ {ICAL_WEDNESDAY_WEEKDAY,"WE"},
+ {ICAL_THURSDAY_WEEKDAY,"TH"},
+ {ICAL_FRIDAY_WEEKDAY,"FR"},
+ {ICAL_SATURDAY_WEEKDAY,"SA"},
+ {ICAL_NO_WEEKDAY,0}
+};
+
+const char* icalrecur_weekday_to_string(icalrecurrencetype_weekday kind)
+{
+ int i;
+
+ for (i=0; wd_map[i].wd != ICAL_NO_WEEKDAY; i++) {
+ if ( wd_map[i].wd == kind) {
+ return wd_map[i].str;
+ }
+ }
+
+ return 0;
+}
+
+icalrecurrencetype_weekday icalrecur_string_to_weekday(const char* str)
+{
+ int i;
+
+ for (i=0; wd_map[i].wd != ICAL_NO_WEEKDAY; i++) {
+ if ( strcmp(str,wd_map[i].str) == 0){
+ return wd_map[i].wd;
+ }
+ }
+
+ return ICAL_NO_WEEKDAY;
+}
+
+
+
+struct {
+ icalrecurrencetype_frequency kind;
+ const char* str;
+} freq_map[] = {
+ {ICAL_SECONDLY_RECURRENCE,"SECONDLY"},
+ {ICAL_MINUTELY_RECURRENCE,"MINUTELY"},
+ {ICAL_HOURLY_RECURRENCE,"HOURLY"},
+ {ICAL_DAILY_RECURRENCE,"DAILY"},
+ {ICAL_WEEKLY_RECURRENCE,"WEEKLY"},
+ {ICAL_MONTHLY_RECURRENCE,"MONTHLY"},
+ {ICAL_YEARLY_RECURRENCE,"YEARLY"},
+ {ICAL_NO_RECURRENCE,0}
+};
+
+const char* icalrecur_freq_to_string(icalrecurrencetype_frequency kind)
+{
+ int i;
+
+ for (i=0; freq_map[i].kind != ICAL_NO_RECURRENCE ; i++) {
+ if ( freq_map[i].kind == kind ) {
+ return freq_map[i].str;
+ }
+ }
+ return 0;
+}
+
+icalrecurrencetype_frequency icalrecur_string_to_freq(const char* str)
+{
+ int i;
+
+ for (i=0; freq_map[i].kind != ICAL_NO_RECURRENCE ; i++) {
+ if ( strcmp(str,freq_map[i].str) == 0){
+ return freq_map[i].kind;
+ }
+ }
+ return ICAL_NO_RECURRENCE;
+}
+
+/* Fill an array with the 'count' number of occurrences generated by
+ the rrule. Note that the times are returned in UTC, but the times
+ are calculated in local time. YOu will have to convert the results
+ back into local time before using them. */
+
+int icalrecur_expand_recurrence(char* rule, time_t start,
+ int count, time_t* array)
+{
+ struct icalrecurrencetype recur;
+ icalrecur_iterator* ritr;
+ time_t tt;
+ struct icaltimetype icstart, next;
+ int i = 0;
+
+ memset(array, 0, count*sizeof(time_t));
+
+ icstart = icaltime_from_timet(start,0);
+
+ recur = icalrecurrencetype_from_string(rule);
+
+ for(ritr = icalrecur_iterator_new(recur,icstart),
+ next = icalrecur_iterator_next(ritr);
+ !icaltime_is_null_time(next) && i < count;
+ next = icalrecur_iterator_next(ritr)){
+
+ tt = icaltime_as_timet(next);
+
+ if (tt >= start ){
+ array[i++] = tt;
+ }
+
+ }
+
+ icalrecur_iterator_free(ritr);
+
+ return 1;
+}