aboutsummaryrefslogtreecommitdiffstats
path: root/libical/src/libical/icalrecur.c
diff options
context:
space:
mode:
Diffstat (limited to 'libical/src/libical/icalrecur.c')
-rw-r--r--libical/src/libical/icalrecur.c910
1 files changed, 600 insertions, 310 deletions
diff --git a/libical/src/libical/icalrecur.c b/libical/src/libical/icalrecur.c
index d5c73e80dd..51fdf63a79 100644
--- a/libical/src/libical/icalrecur.c
+++ b/libical/src/libical/icalrecur.c
@@ -99,26 +99,29 @@
larger time span than the interval. For instance, if
INTERVAL=DAILY, BYMONTH is a contracting rule part.
- Check_contracting_rules() uses check_restriction() to do its
- work. Check_restriction() uses expand_map[] to determine if a rule
+ 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", check_restriction() would check that the time value
+ BYMONTH=6,10", icalrecur_check_rulepart() would check that the time value
given to it has a month of either 6 or 10.
- icalrecurrencetype_test()
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. 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. This signals next_month to
- increment the month.
+ 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.
======================================================================*/
@@ -143,9 +146,31 @@
#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 {
@@ -250,10 +275,10 @@ void icalrecur_add_byrules(struct icalrecur_parser *parser, short *array,
/* Get optional sign. HACK. sign is not allowed for all BYxxx
rule parts */
if( *t == '-'){
- sign = 1;
+ sign = -1;
t++;
} else if (*t == '+'){
- sign = -1;
+ sign = 1;
t++;
}
@@ -279,7 +304,7 @@ void icalrecur_add_bydayrules(struct icalrecur_parser *parser, const char* vals)
char* end;
char* vals_copy;
- vals_copy = strdup(vals);
+ vals_copy = icalmemory_strdup(vals);
end = (char*)vals_copy+strlen(vals_copy);
n = vals_copy;
@@ -343,7 +368,7 @@ struct icalrecurrencetype icalrecurrencetype_from_string(const char* str)
/* Set up the parser struct */
parser.rule = str;
- parser.copy = strdup(parser.rule);
+ parser.copy = icalmemory_strdup(parser.rule);
parser.this_clause = parser.copy;
if(parser.copy == 0){
@@ -366,7 +391,7 @@ struct icalrecurrencetype icalrecurrencetype_from_string(const char* str)
}
if (strcmp(name,"FREQ") == 0){
- parser.rt.freq = icalrecur_string_to_recurrence(value);
+ 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){
@@ -451,7 +476,7 @@ char* icalrecurrencetype_as_string(struct icalrecurrencetype *recur)
icalmemory_append_string(&str,&str_p,&buf_sz,"FREQ=");
icalmemory_append_string(&str,&str_p,&buf_sz,
- icalrecur_recurrence_to_string(recur->freq));
+ icalrecur_freq_to_string(recur->freq));
if(recur->until.year != 0){
@@ -493,7 +518,7 @@ char* icalrecurrencetype_as_string(struct icalrecurrencetype *recur)
pos = icalrecurrencetype_day_position(array[i]);
- if (pos == 1)
+ if (pos == 0)
icalmemory_append_string(&str,&str_p,&buf_sz,daystr);
else {
sprintf(temp,"%d%s",pos,daystr);
@@ -538,20 +563,21 @@ enum byrule {
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 */
+ 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)
@@ -689,20 +715,23 @@ void setup_defaults(struct icalrecur_iterator_impl* impl,
}
-int expand_year_days(struct icalrecur_iterator_impl* impl,short year);
-
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);
@@ -733,6 +762,11 @@ icalrecur_iterator* icalrecur_iterator_new(struct icalrecurrencetype rule,
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]
@@ -762,7 +796,7 @@ icalrecur_iterator* icalrecur_iterator_new(struct icalrecurrencetype rule,
icalrecur_two_byrule(impl,BY_YEAR_DAY,BY_MONTH_DAY) ||
icalrecur_two_byrule(impl,BY_YEAR_DAY,BY_DAY) ){
- icalerror_set_errno(ICAL_USAGE_ERROR);
+ icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
return 0;
}
@@ -770,18 +804,18 @@ icalrecur_iterator* icalrecur_iterator_new(struct icalrecurrencetype rule,
/* BYWEEKNO and BYMONTH rule parts may not both appear.*/
if(icalrecur_two_byrule(impl,BY_WEEK_NO,BY_MONTH)){
- icalerror_set_errno(ICAL_USAGE_ERROR);
+ icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
- icalerror_set_errno(ICAL_USAGE_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_USAGE_ERROR);
+ icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
- icalerror_set_errno(ICAL_USAGE_ERROR);
+ icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
return 0;
}
@@ -790,10 +824,8 @@ icalrecur_iterator* icalrecur_iterator_new(struct icalrecurrencetype rule,
BYWEEKNO may appear. */
if(freq == ICAL_MONTHLY_RECURRENCE &&
- ( icalrecur_one_byrule(impl,BY_WEEK_NO) ||
- icalrecur_one_byrule(impl,BY_YEAR_DAY)) ) {
-
- icalerror_set_errno(ICAL_USAGE_ERROR);
+ icalrecur_one_byrule(impl,BY_WEEK_NO)){
+ icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
return 0;
}
@@ -802,13 +834,17 @@ icalrecur_iterator* icalrecur_iterator_new(struct icalrecurrencetype rule,
BYYEARDAY may appear. */
if(freq == ICAL_WEEKLY_RECURRENCE &&
- ( icalrecur_one_byrule(impl,BY_MONTH_DAY) ||
- icalrecur_one_byrule(impl,BY_YEAR_DAY)) ) {
-
- icalerror_set_errno(ICAL_USAGE_ERROR);
+ 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
@@ -831,6 +867,7 @@ icalrecur_iterator* icalrecur_iterator_new(struct icalrecurrencetype rule,
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){
@@ -841,7 +878,7 @@ icalrecur_iterator* icalrecur_iterator_new(struct icalrecurrencetype rule,
} else {
/* If there is BY_DAY data, then we need to move the initial
- time to the start of the BY_DAY data. That if if the
+ 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
@@ -864,10 +901,11 @@ icalrecur_iterator* icalrecur_iterator_new(struct icalrecurrencetype rule,
}
+ /* For YEARLY rule, begin by setting up the year days array */
if(impl->rule.freq == ICAL_YEARLY_RECURRENCE){
- expand_year_days(impl,impl->dtstart.year);
- }
+ expand_year_days(impl,impl->last.year);
+ }
/* If this is a monthly interval with by day data, then we need to
@@ -882,28 +920,49 @@ icalrecur_iterator* icalrecur_iterator_new(struct icalrecurrencetype rule,
impl->by_ptrs[BY_DAY][impl->by_indices[BY_DAY]]);
short poscount = 0;
- short days_in_month =
- icaltime_days_in_month(impl->last.month, impl->last.year) ;
+ days_in_month =
+ icaltime_days_in_month(impl->last.month, impl->last.year);
- 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){
- break;
- }
- }
- }
+ 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){
- icalerror_set_errno(ICAL_USAGE_ERROR);
+ if(impl->last.day > days_in_month || impl->last.day == 0){
+ icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
return 0;
}
}
+
return impl;
}
@@ -921,34 +980,59 @@ void icalrecur_iterator_free(icalrecur_iterator* i)
}
-
-
void increment_year(struct icalrecur_iterator_impl* impl, int inc)
{
impl->last.year+=inc;
}
-
-
-
-void increment_month(struct icalrecur_iterator_impl* impl, int 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;
- 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);
- }
+ 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)
@@ -964,7 +1048,7 @@ void increment_monthday(struct icalrecur_iterator_impl* impl, int inc)
if (impl->last.day > days_in_month){
impl->last.day = impl->last.day-days_in_month;
- increment_month(impl,1);
+ increment_month(impl);
}
}
}
@@ -1211,46 +1295,6 @@ int next_day(struct icalrecur_iterator_impl* impl)
}
-/* This routine is only called by next_month and next_year, so it does
- not have a clause for this_frequency */
-int next_monthday(struct icalrecur_iterator_impl* impl)
-{
-
- short has_by_data = (impl->by_ptrs[BY_MONTH_DAY][0]!=ICAL_RECURRENCE_ARRAY_MAX);
- short mday;
- short end_of_data = 0;
-
- assert(has_by_data );
-
- if (next_hour(impl) == 0){
- return 0;
- }
-
- impl->by_indices[BY_MONTH_DAY]++;
-
- mday = impl->by_ptrs[BY_MONTH_DAY][impl->by_indices[BY_MONTH_DAY]];
-
- if ( mday ==ICAL_RECURRENCE_ARRAY_MAX){
- impl->by_indices[BY_MONTH_DAY] = 0;
-
- end_of_data = 1;
- }
-
- if (mday > 0){
- impl->last.day = mday;
- } else {
- short days_in_month = icaltime_days_in_month(impl->last.month,
- impl->last.year);
- impl->last.day = days_in_month-mday+1;
- }
-
- if(has_by_data && end_of_data ){
- increment_month(impl,1);
- }
-
- return end_of_data;
-
-}
int next_yearday(struct icalrecur_iterator_impl* impl)
{
@@ -1336,143 +1380,185 @@ int next_weekday_by_week(struct icalrecur_iterator_impl* impl)
}
-int next_weekday_by_month(struct icalrecur_iterator_impl* impl)
-{
+int nth_weekday(short dow, short pos, struct icaltimetype t){
- short end_of_data = 0;
- struct icaltimetype start_of_month; /* Start of month */
- short pos, poscount, dow, days_in_month;
+ short days_in_month = icaltime_days_in_month(t.month,t.year);
+ short end_dow, start_dow;
+ short wd;
- if (next_hour(impl) == 0){
- return 0;
- }
+ 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 */
- assert( impl->by_ptrs[BY_DAY][0]!=ICAL_RECURRENCE_ARRAY_MAX);
+ wd = dow-start_dow+1;
- 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 */
- }
-
- dow = icalrecurrencetype_day_day_of_week(
- impl->by_ptrs[BY_DAY][impl->by_indices[BY_DAY]]);
- pos = icalrecurrencetype_day_position(
- impl->by_ptrs[BY_DAY][impl->by_indices[BY_DAY]]);
+ if (wd <= 0){
+ wd = wd + 7;
+ }
- start_of_month = impl->last;
+ wd = wd + pos * 7;
+
+ } else {
+ t.day = days_in_month;
+ end_dow = icaltime_day_of_week(t);
- /* Find right day in month. HACK. Find an arithmetic way to do
- this */
+ pos++;
- poscount = 0;
- days_in_month =
- icaltime_days_in_month(impl->last.month, impl->last.year) ;
+ /* find month day of last occurrence of dow -- such as the
+ month day of the last monday */
- for(start_of_month.day = 1;
- start_of_month.day <= days_in_month;
- start_of_month.day++){
+ wd = (end_dow - dow);
- if(icaltime_day_of_week(start_of_month) == dow){
- if(++poscount == pos){
- break;
- }
- }
- }
+ if (wd < 0){
+ wd = wd+ 7;
+ }
- if (!end_of_data == 1 &&
- (
- start_of_month.day > days_in_month ||
- icaltime_compare(start_of_month,impl->last) <= 0
- )
- ){
- continue;
- }
+ wd = days_in_month - wd;
- impl->last.day = start_of_month.day;
- impl->last.month = start_of_month.month;
- impl->last.year = start_of_month.year;
-
- return end_of_data;
- }
+ 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;
+ }
+ }
+ }
+ }
- short this_frequency = (impl->rule.freq == ICAL_MONTHLY_RECURRENCE);
-
- short end_of_data = 0;
+ MDEND:
- assert( has_by_data(impl,BY_MONTH) || this_frequency);
+ if ( day > days_in_month){
+ impl->last.day = 1;
+ increment_month(impl);
+ data_valid = 0; /* signal that impl->last is invalid */
+ }
- /* Week day data overrides monthday data */
- if(has_by_data(impl,BY_DAY)){
+
+ } 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. */
- if (next_weekday_by_month(impl) == 0){
- return 0;
- }
- } else {
- if (next_monthday(impl) == 0){
- return 0;
- }
- }
-
- 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;
-
- end_of_data = 1;
+ 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;
+ }
+ }
}
- impl->last.month =
- impl->by_ptrs[BY_MONTH][impl->by_indices[BY_MONTH]];
+ DEND:
- } else if( !has_by_data(impl,BY_MONTH) && this_frequency ){
-
- if(has_by_data(impl,BY_DAY)){
-
- short dayinc = 28;
-
- /* BY_DAY data specified a day of week, but incrementing the
- month changes the day of the week -- Nov 2 is not the
- same DOW as Oct 2. So, we need to fix the day of week by
- incrementing in even weeks into the next month. . */
-
-
- if ( impl->last.day + dayinc
- <= icaltime_days_in_month(impl->last.month, impl->last.year)){
- dayinc += 7;
- }
+ if ( day > days_in_month){
+ impl->last.day = 1;
+ increment_month(impl);
+ data_valid = 0; /* signal that impl->last is invalid */
+ }
- increment_monthday(impl,dayinc);
+ } else if (has_by_data(impl,BY_MONTH_DAY)) {
+ /* Cases like: FREQ=MONTHLY;COUNT=10;BYMONTHDAY=-3 */
+ short day;
- } else {
+ assert( BYMDPTR[0]!=ICAL_RECURRENCE_ARRAY_MAX);
- /* Compute the next value from the last time and the
- frequency interval*/
- increment_month(impl,impl->rule.interval);
+ 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);
}
-
- }
-
-
- if(has_by_data(impl,BY_MONTH) && end_of_data && this_frequency ){
- increment_year(impl,1);
+
+ 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 end_of_data;
+
+ return data_valid; /* Signal that the data is valid */
}
@@ -1484,11 +1570,16 @@ int next_week(struct icalrecur_iterator_impl* impl)
short end_of_data = 0;
+ /* Increment to the next week day */
if (next_weekday_by_week(impl) == 0){
- return 0;
+ return 0; /* Have not reached end of week yet */
}
- if( impl->by_ptrs[BY_WEEK_NO][0]!=ICAL_RECURRENCE_ARRAY_MAX){
+ /* 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;
@@ -1513,11 +1604,11 @@ int next_week(struct icalrecur_iterator_impl* impl)
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);
}
@@ -1527,6 +1618,69 @@ int next_week(struct icalrecur_iterator_impl* impl)
}
+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){
+ /* 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;
+ }
+
+ pvl_push(days_list,(void*)(first+ (pos-1) * 7));
+
+ } else { /* pos < 0 */
+ assert(0);
+ }
+ }
+
+ 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
@@ -1537,15 +1691,31 @@ int expand_year_days(struct icalrecur_iterator_impl* impl,short year)
int j,k;
int days_index=0;
struct icaltimetype t;
+ int flags;
+#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));
- if(has_by_data(impl,BY_MONTH) && !has_by_data(impl,BY_MONTH_DAY)
- && !has_by_data(impl,BY_DAY)){
+ 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;
@@ -1562,10 +1732,81 @@ int expand_year_days(struct icalrecur_iterator_impl* impl,short year)
impl->days[days_index++] = doy;
}
+ break;
+ }
+ case 1<<BY_MONTH_DAY: {
+ /* FREQ=YEARLY; BYMONTHDAY=1,15*/
+ assert(0);
+ 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 */
+
+ break;
+ }
+ case (1<<BY_WEEK_NO) + (1<<BY_MONTH_DAY): {
+ /*FREQ=YEARLY; WEEKNO=20,50; BYMONTH= 6,11 */
+ assert(0);
+ break;
}
- else if ( has_by_data(impl,BY_MONTH) && has_by_data(impl,BY_DAY)){
+
+ 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;
+ }
+
+ break;
+ }
+
+ case (1<<BY_DAY)+(1<<BY_MONTH): {
+ /*FREQ=YEARLY; BYDAY=TH,20MO,-10FR; BYMONTH = 12*/
+
for(j=0;impl->by_ptrs[BY_MONTH][j]!=ICAL_RECURRENCE_ARRAY_MAX;j++){
short month = impl->by_ptrs[BY_MONTH][j];
@@ -1596,58 +1837,90 @@ int expand_year_days(struct icalrecur_iterator_impl* impl,short year)
}
}
}
- } else if (has_by_data(impl,BY_MONTH) && has_by_data(impl,BY_MONTH_DAY)){
+ break;
+ }
- 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;
+ case (1<<BY_DAY) + (1<<BY_MONTH_DAY) : {
+ /*FREQ=YEARLY; BYDAY=TH,20MO,-10FR; BYMONTHDAY=1,15*/
+ assert(0);
+ break;
+ }
- t.day = month_day;
- t.month = month;
- t.year = year;
- t.is_date = 1;
+ 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;
+ }
+ }
+ }
- doy = icaltime_day_of_year(t);
+ }
- impl->days[days_index++] = doy;
+ 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;
+ }
}
+
}
- } else if (has_by_data(impl,BY_WEEK_NO) && !has_by_data(impl,BY_DAY)){
-
- struct icaltimetype t;
- short dow;
-
- t.day = impl->dtstart.day;
- t.month = impl->dtstart.month;
- t.year = year;
- t.is_date = 1;
+ break;
+ }
- dow = icaltime_day_of_week(t);
- /* HACK Not finished */
-
+ case (1<<BY_DAY) + (1<<BY_WEEK_NO) + (1<<BY_MONTH_DAY): {
+ /*FREQ=YEARLY; BYDAY=TH,20MO,-10FR; WEEKNO=20,50; BYMONTHDAY=1,15*/
+ assert(0);
+ break;
+ }
- } else if (has_by_data(impl,BY_WEEK_NO) && has_by_data(impl,BY_DAY)){
- /* HACK Not finished */
- } else if (has_by_data(impl,BY_YEAR_DAY)){
-
+ 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;
- }
-
- } else if (has_by_data(impl,BY_MONTH_DAY) ){
- /* HACK Not finished */
-
- } else if (has_by_data(impl,BY_DAY)){
- /* HACK Not finished */
+ }
+ break;
+ }
- } else {
- assert(0);
- /* HACK Not finished */
+ default: {
+ assert(0);
+ break;
+ }
}
@@ -1658,17 +1931,15 @@ int expand_year_days(struct icalrecur_iterator_impl* impl,short year)
int next_year(struct icalrecur_iterator_impl* impl)
{
struct icaltimetype next;
- short end_of_data=0;
if (next_hour(impl) == 0){
return 0;
}
- impl->days_index++;
-
- if (impl->days[impl->days_index] == ICAL_RECURRENCE_ARRAY_MAX){
+ if (impl->days[++impl->days_index] == ICAL_RECURRENCE_ARRAY_MAX){
impl->days_index = 0;
- end_of_data = 1;
+ increment_year(impl,impl->rule.interval);
+ expand_year_days(impl,impl->last.year);
}
next = icaltime_from_day_of_year(impl->days[impl->days_index],impl->last.year);
@@ -1676,16 +1947,26 @@ int next_year(struct icalrecur_iterator_impl* impl)
impl->last.day = next.day;
impl->last.month = next.month;
-
- if(end_of_data){
- increment_year(impl,impl->rule.interval);
- expand_year_days(impl,impl->last.year);
- }
-
return 1;
}
-int check_restriction(struct icalrecur_iterator_impl* impl,
+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;
@@ -1709,6 +1990,7 @@ int check_restriction(struct icalrecur_iterator_impl* impl,
}
}
+
int check_contracting_rules(struct icalrecur_iterator_impl* impl)
{
@@ -1717,14 +1999,14 @@ int check_contracting_rules(struct icalrecur_iterator_impl* impl)
int year_day=0;
if (
- check_restriction(impl,BY_SECOND,impl->last.second) &&
- check_restriction(impl,BY_MINUTE,impl->last.minute) &&
- check_restriction(impl,BY_HOUR,impl->last.hour) &&
- check_restriction(impl,BY_DAY,day_of_week) &&
- check_restriction(impl,BY_WEEK_NO,week_no) &&
- check_restriction(impl,BY_MONTH_DAY,impl->last.day) &&
- check_restriction(impl,BY_MONTH,impl->last.month) &&
- check_restriction(impl,BY_YEAR_DAY,year_day) )
+ 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;
@@ -1735,6 +2017,7 @@ int check_contracting_rules(struct icalrecur_iterator_impl* impl)
struct icaltimetype icalrecur_iterator_next(icalrecur_iterator *itr)
{
+ int valid = 1;
struct icalrecur_iterator_impl* impl =
(struct icalrecur_iterator_impl*)itr;
@@ -1752,6 +2035,7 @@ struct icaltimetype icalrecur_iterator_next(icalrecur_iterator *itr)
}
do {
+ valid = 1;
switch(impl->rule.freq){
case ICAL_SECONDLY_RECURRENCE: {
@@ -1775,7 +2059,7 @@ struct icaltimetype icalrecur_iterator_next(icalrecur_iterator *itr)
break;
}
case ICAL_MONTHLY_RECURRENCE: {
- next_month(impl);
+ valid = next_month(impl);
break;
}
case ICAL_YEARLY_RECURRENCE:{
@@ -1783,17 +2067,19 @@ struct icaltimetype icalrecur_iterator_next(icalrecur_iterator *itr)
break;
}
default:{
- assert(0); /* HACK, need a better error */
+ icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
+ return icaltime_null_time();
}
}
- if(impl->last.year >= 2038){
+ if(impl->last.year >= 2038 ){
/* HACK */
return icaltime_null_time();
}
} while(!check_contracting_rules(impl)
- || icaltime_compare(impl->last,impl->dtstart) < 0);
+ || icaltime_compare(impl->last,impl->dtstart) < 0
+ || valid == 0);
/* Ignore null times and times that are after the until time */
@@ -1830,6 +2116,9 @@ 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)
@@ -1845,9 +2134,6 @@ short icalrecurrencetype_day_position(short day)
pos = (abs(day)-wd)/8 * ((day<0)?-1:1);
- if(pos == 0){
- pos = 1;
- }
return pos;
}
@@ -1909,7 +2195,7 @@ struct {
{ICAL_NO_RECURRENCE,0}
};
-const char* icalrecur_recurrence_to_string(icalrecurrencetype_frequency kind)
+const char* icalrecur_freq_to_string(icalrecurrencetype_frequency kind)
{
int i;
@@ -1921,7 +2207,7 @@ const char* icalrecur_recurrence_to_string(icalrecurrencetype_frequency kind)
return 0;
}
-icalrecurrencetype_frequency icalrecur_string_to_recurrence(const char* str)
+icalrecurrencetype_frequency icalrecur_string_to_freq(const char* str)
{
int i;
@@ -1933,6 +2219,10 @@ icalrecurrencetype_frequency icalrecur_string_to_recurrence(const char* str)
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)
@@ -1953,7 +2243,7 @@ int icalrecur_expand_recurrence(char* rule, time_t start,
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 ){