summaryrefslogtreecommitdiff
path: root/src/libical/icalrecur.c
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2013-05-04 21:39:27 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2013-05-04 21:39:27 +0000
commitfec6336699f34758d3e6cb41b2edf902fedb9035 (patch)
tree8256c1dbf3ca7c9e58a3dbecf07cf826fb2e0ce2 /src/libical/icalrecur.c
parent7dbffd7e2b0067e834801617c5c486e3177f6709 (diff)
downloadlibical-master.tar.gz
Diffstat (limited to 'src/libical/icalrecur.c')
-rw-r--r--src/libical/icalrecur.c214
1 files changed, 168 insertions, 46 deletions
diff --git a/src/libical/icalrecur.c b/src/libical/icalrecur.c
index 850e46d..c0e5eca 100644
--- a/src/libical/icalrecur.c
+++ b/src/libical/icalrecur.c
@@ -139,16 +139,23 @@
#include <stdint.h>
#endif
+#include <stdio.h>
+#include <stdarg.h>
+
+#if defined(_MSC_VER)
+#define snprintf _snprintf
+#endif
+
#include <limits.h>
#ifndef HAVE_INTPTR_T
-#if defined (WIN32) || defined (XP_BEOS)
+#if (defined (WIN32) && !defined (__MINGW32__)) || defined (XP_BEOS)
typedef long intptr_t;
#endif
#endif
-#ifdef WIN32
-#define strcasecmp stricmp
+#ifdef _MSC_VER
+#define strcasecmp stricmp
#endif
#include "icalrecur.h"
@@ -156,7 +163,7 @@ typedef long intptr_t;
#include "icalerror.h"
#include "icalmemory.h"
-#include <stdlib.h> /* for malloc */
+#include <stdlib.h> /* for malloc, strtol */
#include <errno.h> /* for errno */
#include <string.h> /* for strdup and strchr*/
#include <assert.h>
@@ -354,7 +361,7 @@ void icalrecur_add_bydayrules(struct icalrecur_parser *parser, const char* vals)
char *t, *n;
int i=0;
int sign = 1;
- int weekno = 0;
+ char weekno = 0; /* note: Novell/Groupwise sends BYDAY=255SU, so we fit in a signed char to get -1 SU for last sunday. */
icalrecurrencetype_weekday wd;
short *array = parser->rt.by_day;
char* end;
@@ -389,7 +396,7 @@ void icalrecur_add_bydayrules(struct icalrecur_parser *parser, const char* vals)
}
/* Get Optional weekno */
- weekno = strtol(t,&t,10);
+ weekno = (char)strtol(t,&t,10);
/* Outlook/Exchange generate "BYDAY=MO, FR" and "BYDAY=2 TH".
* Cope with that.
@@ -441,7 +448,7 @@ struct icalrecurrencetype icalrecurrencetype_from_string(const char* str)
if(name == 0){
icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
icalrecurrencetype_clear(&parser.rt);
- free(parser.copy);
+ free(parser.copy);
return parser.rt;
}
@@ -453,6 +460,11 @@ struct icalrecurrencetype icalrecurrencetype_from_string(const char* str)
parser.rt.until = icaltime_from_string(value);
} else if (strcasecmp(name,"INTERVAL") == 0){
parser.rt.interval = (short)atoi(value);
+ /* don't allow an interval to be less than 1
+ (RFC specifies an interval must be a positive integer) */
+ if (parser.rt.interval < 1){
+ parser.rt.interval = 1;
+ }
} else if (strcasecmp(name,"WKST") == 0){
parser.rt.week_start = icalrecur_string_to_weekday(value);
sort_bydayrules(&parser);
@@ -485,7 +497,7 @@ struct icalrecurrencetype icalrecurrencetype_from_string(const char* str)
} else {
icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
icalrecurrencetype_clear(&parser.rt);
- free(parser.copy);
+ free(parser.copy);
return parser.rt;
}
@@ -810,6 +822,8 @@ icalrecur_iterator* icalrecur_iterator_new(struct icalrecurrencetype rule,
icalrecur_iterator* impl;
icalrecurrencetype_frequency freq;
+ icalerror_clear_errno();
+
if ( ( impl = (icalrecur_iterator*)
malloc(sizeof(icalrecur_iterator))) == 0) {
icalerror_set_errno(ICAL_NEWFAILED_ERROR);
@@ -875,26 +889,18 @@ icalrecur_iterator* icalrecur_iterator_new(struct icalrecurrencetype rule,
icalrecur_two_byrule(impl,BY_YEAR_DAY,BY_DAY) ){
icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
-
+ free(impl);
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;
+ free(impl);
+ return 0;
}
@@ -904,7 +910,8 @@ icalrecur_iterator* icalrecur_iterator_new(struct icalrecurrencetype rule,
if(freq == ICAL_MONTHLY_RECURRENCE &&
icalrecur_one_byrule(impl,BY_WEEK_NO)){
icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
- return 0;
+ free(impl);
+ return 0;
}
@@ -914,13 +921,15 @@ icalrecur_iterator* icalrecur_iterator_new(struct icalrecurrencetype rule,
if(freq == ICAL_WEEKLY_RECURRENCE &&
icalrecur_one_byrule(impl,BY_MONTH_DAY )) {
icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
- return 0;
+ free(impl);
+ 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);
+ free(impl);
return 0;
}
@@ -990,9 +999,16 @@ icalrecur_iterator* icalrecur_iterator_new(struct icalrecurrencetype rule,
if(impl->rule.freq == ICAL_YEARLY_RECURRENCE){
struct icaltimetype next;
+ icalerror_clear_errno();
- for (;;) {
+ /* Fail after hitting the year 20000 if no expanded days match */
+ while (impl->last.year < 20000) {
expand_year_days(impl, impl->last.year);
+ if( icalerrno != ICAL_NO_ERROR) {
+ icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
+ free(impl);
+ return 0;
+ }
if (impl->days[0] != ICAL_RECURRENCE_ARRAY_MAX)
break; /* break when no days are expanded */
increment_year(impl,impl->rule.interval);
@@ -1054,7 +1070,8 @@ icalrecur_iterator* icalrecur_iterator_new(struct icalrecurrencetype rule,
if(impl->last.day > days_in_month || impl->last.day == 0){
icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
- return 0;
+ free(impl);
+ return 0;
}
} else if (has_by_data(impl,BY_MONTH_DAY)) {
@@ -1158,7 +1175,7 @@ static void increment_hour(icalrecur_iterator* impl, int inc)
days = impl->last.hour / 24;
impl->last.hour = impl->last.hour % 24;
- if (impl->days != 0){
+ if (days != 0){
increment_monthday(impl,days);
}
}
@@ -1494,6 +1511,20 @@ static int is_day_in_byday(icalrecur_iterator* impl,struct icaltimetype tt){
return 0;
}
+int check_set_position(icalrecur_iterator* impl, int set_pos)
+{
+ int i;
+ int found = 0;
+ for (i = 0; impl->rule.by_set_pos[i] != ICAL_RECURRENCE_ARRAY_MAX &&
+ i != ICAL_BY_SETPOS_SIZE; i++){
+ if (impl->rule.by_set_pos[i] == set_pos) {
+ found = 1;
+ break;
+ }
+ }
+ return found;
+}
+
static int next_month(icalrecur_iterator* impl)
{
int data_valid = 1;
@@ -1575,15 +1606,44 @@ static int next_month(icalrecur_iterator* impl)
int day;
int days_in_month = icaltime_days_in_month(impl->last.month,
impl->last.year);
- assert( BYDAYPTR[0]!=ICAL_RECURRENCE_ARRAY_MAX);
+ int set_pos_counter = 0;
+ int set_pos_total = 0;
+ int found = 0;
+
+ assert( BYDAYPTR[0]!=ICAL_RECURRENCE_ARRAY_MAX);
+
+ /* Count the past positions for the BYSETPOS calculation */
+ if(has_by_data(impl,BY_SET_POS)){
+ int last_day = impl->last.day;
+ for(day = 1; day <= days_in_month; day++){
+ impl->last.day = day;
+
+ if(is_day_in_byday(impl,impl->last)){
+ set_pos_total++;
+ if(day <= last_day)
+ set_pos_counter++;
+ }
+ }
+ impl->last.day = last_day;
+ }
+
for(day = impl->last.day+1; day <= days_in_month; day++){
impl->last.day = day;
+
if(is_day_in_byday(impl,impl->last)){
- data_valid = 1;
- break;
- }
+ /* If there is no BYSETPOS rule, calculate only by BYDAY
+ If there is BYSETPOS rule, take into account the occurence
+ matches with BYDAY */
+ if(!has_by_data(impl,BY_SET_POS) || check_set_position(impl, ++set_pos_counter)
+ || check_set_position(impl, set_pos_counter-set_pos_total-1)) {
+ found = 1;
+ break;
+ }
+ }
}
+
+ data_valid = found;
if ( day > days_in_month){
impl->last.day = 1;
@@ -1594,7 +1654,9 @@ static int next_month(icalrecur_iterator* impl)
invalid */
if(is_day_in_byday(impl,impl->last)){
- data_valid = 1;
+ /* If there is no BYSETPOS rule or BYSETPOS=1, new data is valid */
+ if(!has_by_data(impl,BY_SET_POS) || check_set_position(impl,1))
+ data_valid = 1;
} else {
data_valid = 0; /* signal that impl->last is invalid */
}
@@ -1647,12 +1709,15 @@ static int next_month(icalrecur_iterator* impl)
} else {
int days_in_month;
+ assert( BYMDPTR[0]!=ICAL_RECURRENCE_ARRAY_MAX);
+ impl->last.day = BYMDPTR[0];
+
increment_month(impl);
days_in_month = icaltime_days_in_month(impl->last.month,
impl->last.year);
if (impl->last.day > days_in_month){
- data_valid = 0; /* signal that impl->last is invalid */
+ impl->last.day = days_in_month;
}
}
@@ -1850,7 +1915,7 @@ static pvl_list expand_by_day(icalrecur_iterator* impl, int year)
static int expand_year_days(icalrecur_iterator* impl, int year)
{
- int j,k;
+ int i,j,k;
int days_index=0;
struct icaltimetype t;
int flags;
@@ -1872,6 +1937,46 @@ static int expand_year_days(icalrecur_iterator* impl, int year)
(HBD(BY_YEAR_DAY) ? 1<<BY_YEAR_DAY : 0);
+ /* BY_WEEK_NO together with BY_MONTH - may conflict, in this case BY_MONTH wins */
+ if( (flags & 1<<BY_MONTH) && (flags & 1<<BY_WEEK_NO) ){
+ int valid_weeks[ICAL_BY_WEEKNO_SIZE];
+ int valid = 1;
+ memset(valid_weeks, 0, sizeof(valid_weeks));
+ t.year = year;
+ t.is_date = 1;
+
+ /* calculate valid week numbers */
+ for(j=0; impl->by_ptrs[BY_MONTH][j]!=ICAL_RECURRENCE_ARRAY_MAX; j++){
+ int month = impl->by_ptrs[BY_MONTH][j];
+ int first_week, last_week;
+ t.month = month;
+ t.day = 1;
+ first_week = icaltime_week_number(t);
+ t.day = icaltime_days_in_month(month,year);
+ last_week = icaltime_week_number(t);
+ for(j=first_week; j<last_week; j++) {
+ valid_weeks[j] = 1;
+ }
+ }
+
+ /* check valid weeks */
+ for(i = 0; BYWEEKPTR[i] != ICAL_RECURRENCE_ARRAY_MAX && valid; i++){
+ int weekno = BYWEEKPTR[i];
+ if(weekno < ICAL_BY_WEEKNO_SIZE)
+ valid &= valid_weeks[i]; /* check if the week number is valid */
+ else
+ valid = 0; /* invalid week number */
+ }
+
+ /* let us make the decision which rule to keep */
+ if(valid) { /* BYWEEKNO wins */
+ flags -= 1<<BY_MONTH;
+ }
+ else { /* BYMONTH vins */
+ flags -= 1<<BY_WEEK_NO;
+ }
+ }
+
switch(flags) {
case 0: {
@@ -2011,7 +2116,24 @@ static int expand_year_days(icalrecur_iterator* impl, int year)
t.day = days_in_month;
last_dow = icaltime_day_of_week(t);
- for(k=0;impl->by_ptrs[BY_DAY][k]!=ICAL_RECURRENCE_ARRAY_MAX;k++){
+ if(has_by_data(impl,BY_SET_POS)) {
+ /*FREQ=YEARLY; BYDAY=TH,20MO,-10FR; BYMONTH = 12; BYSETPOS=1*/
+ int day;
+ int set_pos_counter = 0;
+ int set_pos_total = 0;
+ int by_month_day[ICAL_BY_MONTHDAY_SIZE];
+ for(day = 1; day <= days_in_month; day++){
+ t.day = day;
+ if(is_day_in_byday(impl,t))
+ by_month_day[set_pos_total++] = day;
+ }
+ for(set_pos_counter = 0; set_pos_counter < set_pos_total; set_pos_counter++){
+ if(check_set_position(impl, set_pos_counter+1) ||
+ check_set_position(impl, set_pos_counter-set_pos_total))
+ impl->days[days_index++] = doy_offset + by_month_day[set_pos_counter];
+ }
+ }
+ else for(k=0;impl->by_ptrs[BY_DAY][k]!=ICAL_RECURRENCE_ARRAY_MAX;k++){
short day_coded = impl->by_ptrs[BY_DAY][k];
enum icalrecurrencetype_weekday dow =
icalrecurrencetype_day_day_of_week(day_coded);
@@ -2252,8 +2374,8 @@ static int check_contracting_rules(icalrecur_iterator* impl)
struct icaltimetype icalrecur_iterator_next(icalrecur_iterator *impl)
{
int valid = 1;
-
- if( (impl->rule.count!=0 &&impl->occurrence_no >= impl->rule.count) ||
+
+ if( !impl || (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();
@@ -2470,21 +2592,21 @@ int icalrecur_expand_recurrence(char* rule, time_t start,
icstart = icaltime_from_timet_with_zone(start,0,0);
recur = icalrecurrencetype_from_string(rule);
+ ritr = icalrecur_iterator_new(recur,icstart);
+ if(ritr) {
+ for(next = icalrecur_iterator_next(ritr);
+ !icaltime_is_null_time(next) && i < count;
+ next = icalrecur_iterator_next(ritr)){
- 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);
+ tt = icaltime_as_timet(next);
- if (tt >= start ){
- array[i++] = tt;
- }
-
+ if (tt >= start ){
+ array[i++] = tt;
+ }
+ }
+ icalrecur_iterator_free(ritr);
}
- icalrecur_iterator_free(ritr);
return 1;
}