Back to index

lightning-sunbird  0.9+nobinonly
icalrecur.c
Go to the documentation of this file.
00001 /* -*- Mode: C -*-
00002   ======================================================================
00003   FILE: icalrecur.c
00004   CREATOR: eric 16 May 2000
00005   
00006   $Id: icalrecur.c,v 1.63 2005/02/15 17:39:00 acampi Exp $
00007   $Locker:  $
00008     
00009 
00010  (C) COPYRIGHT 2000, Eric Busboom, http://www.softwarestudio.org
00011 
00012  This program is free software; you can redistribute it and/or modify
00013  it under the terms of either: 
00014 
00015     The LGPL as published by the Free Software Foundation, version
00016     2.1, available at: http://www.fsf.org/copyleft/lesser.html
00017 
00018   Or:
00019 
00020     The Mozilla Public License Version 1.0. You may obtain a copy of
00021     the License at http://www.mozilla.org/MPL/
00022 */
00023 
00133 #ifdef HAVE_CONFIG_H
00134 #include "config.h"
00135 #endif
00136 
00137 #ifdef HAVE_STDINT_H
00138 #include <stdint.h>
00139 #endif
00140 
00141 #include <limits.h>
00142 
00143 #ifndef HAVE_INTPTR_T
00144 #if defined (WIN32) || defined (XP_BEOS)
00145 typedef long intptr_t;
00146 #endif
00147 #endif
00148 
00149 #include "icalrecur.h"
00150 
00151 #include "icalerror.h"
00152 #include "icalmemory.h"
00153 
00154 #include <stdlib.h> /* for malloc */
00155 #include <errno.h> /* for errno */
00156 #include <string.h> /* for strdup and strchr*/
00157 #include <assert.h>
00158 #include <stddef.h> /* For offsetof() macro */
00159 
00160 #ifdef WIN32
00161 #define snprintf _snprintf
00162 #endif
00163 
00164 #include "pvl.h"
00165 
00168 #define MAX_TIME_T_YEAR     2037
00169 
00170 #define TEMP_MAX 1024
00171 
00172 
00173 #define BYDAYIDX impl->by_indices[BY_DAY]
00174 #define BYDAYPTR impl->by_ptrs[BY_DAY]
00175 
00176 #define BYMONIDX impl->by_indices[BY_MONTH]
00177 #define BYMONPTR impl->by_ptrs[BY_MONTH]
00178 
00179 #define BYMDIDX impl->by_indices[BY_MONTH_DAY]
00180 #define BYMDPTR impl->by_ptrs[BY_MONTH_DAY]
00181 
00182 #define BYWEEKIDX impl->by_indices[BY_WEEK_NO]
00183 #define BYWEEKPTR impl->by_ptrs[BY_WEEK_NO]
00184 
00185 const char* icalrecur_freq_to_string(icalrecurrencetype_frequency kind);
00186 icalrecurrencetype_frequency icalrecur_string_to_freq(const char* str);
00187 
00188 const char* icalrecur_weekday_to_string(icalrecurrencetype_weekday kind);
00189 icalrecurrencetype_weekday icalrecur_string_to_weekday(const char* str);
00190 
00191 
00192 /*********************** Rule parsing routines ************************/
00193 
00194 struct icalrecur_parser {
00195        const char* rule;
00196         char* copy;
00197        char* this_clause;
00198        char* next_clause;
00199 
00200        struct icalrecurrencetype rt;
00201 };
00202 
00203 const char* icalrecur_first_clause(struct icalrecur_parser *parser)
00204 {
00205     char *idx;
00206     parser->this_clause = parser->copy;
00207     
00208     idx = strchr(parser->this_clause,';');
00209 
00210     if (idx == 0){
00211        parser->next_clause = 0;
00212        return 0;
00213     }
00214 
00215     *idx = 0;
00216     idx++;
00217     parser->next_clause = idx;
00218 
00219     return parser->this_clause;
00220 
00221 }
00222 
00223 const char* icalrecur_next_clause(struct icalrecur_parser *parser)
00224 {
00225     char* idx;
00226 
00227     parser->this_clause = parser->next_clause;
00228 
00229     if(parser->this_clause == 0){
00230        return 0;
00231     }
00232 
00233     idx = strchr(parser->this_clause,';');
00234 
00235     if (idx == 0){
00236        parser->next_clause = 0;
00237     } else {
00238 
00239        *idx = 0;
00240        idx++;
00241        parser->next_clause = idx;
00242     }
00243        
00244     return parser->this_clause;
00245 
00246 }
00247 
00248 void icalrecur_clause_name_and_value(struct icalrecur_parser *parser,
00249                                  char** name, char** value)
00250 {
00251     char *idx;
00252 
00253     *name = parser->this_clause;
00254 
00255     idx = strchr(parser->this_clause,'=');
00256 
00257     if (idx == 0){
00258        *name = 0;
00259        *value = 0;
00260        return;
00261     }
00262     
00263     *idx = 0;
00264     idx++;
00265     *value = idx;
00266 }
00267 
00268 void icalrecur_add_byrules(struct icalrecur_parser *parser, short *array,
00269                         int size, char* vals)
00270 {
00271     char *t, *n;
00272     int i=0;
00273     int sign = 1;
00274     int v;
00275 
00276     n = vals;
00277 
00278     while(n != 0){
00279 
00280        if(i == size){
00281            return;
00282        }
00283        
00284        t = n;
00285 
00286        n = strchr(t,',');
00287 
00288        if(n != 0){
00289            *n = 0;
00290            n++;
00291        }
00292        
00293        /* Get optional sign. HACK. sign is not allowed for all BYxxx
00294            rule parts */
00295        if( *t == '-'){
00296            sign = -1;
00297            t++;
00298        } else if (*t == '+'){
00299            sign = 1;
00300            t++;
00301        } else {
00302            sign = 1;
00303        }
00304 
00305        v = atoi(t) * sign ;
00306 
00307 
00308        array[i++] = (short)v;
00309        array[i] =  ICAL_RECURRENCE_ARRAY_MAX;
00310 
00311     }
00312 
00313 }
00314 
00315 /*
00316  * Days in the BYDAY rule are expected by the code to be sorted, and while
00317  * this may be the common case, the RFC doesn't actually mandate it. This
00318  * function sorts the days taking into account the first day of week.
00319  */
00320 static void
00321 sort_bydayrules(short *array, int week_start)
00322 {
00323        int one, two, i, j;
00324 
00325        for (i=0;
00326                       i<ICAL_BY_DAY_SIZE && array[i] != ICAL_RECURRENCE_ARRAY_MAX;
00327                       i++) {
00328               for (j=0; j<i; j++) {
00329                      one = icalrecurrencetype_day_day_of_week(array[j]) - week_start;
00330                      if (one < 0) one += 7;
00331                      two = icalrecurrencetype_day_day_of_week(array[i]) - week_start;
00332                      if (two < 0) two += 7;
00333 
00334                      if (one > two) {
00335                             short tmp = array[j];
00336                             array[j] = array[i];
00337                             array[i] = tmp;
00338                      }
00339               }
00340        }
00341 }
00342 
00343 void icalrecur_add_bydayrules(struct icalrecur_parser *parser, const char* vals)
00344 {
00345 
00346     char *t, *n;
00347     int i=0;
00348     int sign = 1;
00349     int weekno = 0;
00350     icalrecurrencetype_weekday wd;
00351     short *array = parser->rt.by_day;
00352     char* end;
00353     char* vals_copy;
00354 
00355     vals_copy = icalmemory_strdup(vals);
00356 
00357     end = (char*)vals_copy+strlen(vals_copy);
00358     n = vals_copy;
00359 
00360     while(n != 0){
00361        
00362 
00363        t = n;
00364 
00365        n = strchr(t,',');
00366 
00367        if(n != 0){
00368            *n = 0;
00369            n++;
00370        }
00371        
00372        /* Get optional sign. */
00373        if( *t == '-'){
00374            sign = -1;
00375            t++;
00376        } else if (*t == '+'){
00377            sign = 1;
00378            t++;
00379        } else {
00380            sign = 1;
00381        }
00382 
00383        /* Get Optional weekno */
00384        weekno = strtol(t,&t,10);
00385 
00386        /* Outlook/Exchange generate "BYDAY=MO, FR" and "BYDAY=2 TH".
00387         * Cope with that.
00388         */
00389        if (*t == ' ')
00390            t++;
00391 
00392        wd = icalrecur_string_to_weekday(t);
00393 
00394        array[i++] = (short)(sign* (wd + 8*weekno));
00395        array[i] =  ICAL_RECURRENCE_ARRAY_MAX;
00396 
00397     }
00398 
00399     free(vals_copy);
00400 
00401     sort_bydayrules(parser->rt.by_day,parser->rt.week_start);
00402 }
00403 
00404 
00405 struct icalrecurrencetype icalrecurrencetype_from_string(const char* str)
00406 {
00407     struct icalrecur_parser parser;
00408 
00409     memset(&parser,0,sizeof(parser));
00410     icalrecurrencetype_clear(&parser.rt);
00411 
00412     icalerror_check_arg_re(str!=0,"str",parser.rt);
00413 
00414 
00415     /* Set up the parser struct */
00416     parser.rule = str;
00417     parser.copy = icalmemory_strdup(parser.rule);
00418     parser.this_clause = parser.copy;
00419 
00420     if(parser.copy == 0){
00421        icalerror_set_errno(ICAL_NEWFAILED_ERROR);
00422        return parser.rt;
00423     }
00424 
00425     /* Loop through all of the clauses */
00426     for(icalrecur_first_clause(&parser); 
00427        parser.this_clause != 0;
00428        icalrecur_next_clause(&parser))
00429     {
00430        char *name, *value;
00431        icalrecur_clause_name_and_value(&parser,&name,&value);
00432 
00433        if(name == 0){
00434            icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
00435            icalrecurrencetype_clear(&parser.rt);
00436               free(parser.copy);
00437            return parser.rt;
00438        }
00439 
00440        if (strcmp(name,"FREQ") == 0){
00441            parser.rt.freq = icalrecur_string_to_freq(value);
00442        } else if (strcmp(name,"COUNT") == 0){
00443            int v = atoi(value);
00444            if (v >= 0) {
00445            parser.rt.count = v;
00446            }
00447        } else if (strcmp(name,"UNTIL") == 0){
00448            parser.rt.until = icaltime_from_string(value);
00449        } else if (strcmp(name,"INTERVAL") == 0){
00450            int v = atoi(value);
00451            if (v > 0 && v <= SHRT_MAX) {
00452            parser.rt.interval = (short) v;
00453            }
00454        } else if (strcmp(name,"WKST") == 0){
00455            parser.rt.week_start = icalrecur_string_to_weekday(value);
00456       sort_bydayrules(parser.rt.by_day,parser.rt.week_start);
00457        } else if (strcmp(name,"BYSECOND") == 0){
00458            icalrecur_add_byrules(&parser,parser.rt.by_second,
00459                               ICAL_BY_SECOND_SIZE,value);
00460        } else if (strcmp(name,"BYMINUTE") == 0){
00461            icalrecur_add_byrules(&parser,parser.rt.by_minute,
00462                               ICAL_BY_MINUTE_SIZE,value);
00463        } else if (strcmp(name,"BYHOUR") == 0){
00464            icalrecur_add_byrules(&parser,parser.rt.by_hour,
00465                               ICAL_BY_HOUR_SIZE,value);
00466        } else if (strcmp(name,"BYDAY") == 0){
00467            icalrecur_add_bydayrules(&parser,value);
00468        } else if (strcmp(name,"BYMONTHDAY") == 0){
00469            icalrecur_add_byrules(&parser,parser.rt.by_month_day,
00470                               ICAL_BY_MONTHDAY_SIZE,value);
00471        } else if (strcmp(name,"BYYEARDAY") == 0){
00472            icalrecur_add_byrules(&parser,parser.rt.by_year_day,
00473                               ICAL_BY_YEARDAY_SIZE,value);
00474        } else if (strcmp(name,"BYWEEKNO") == 0){
00475            icalrecur_add_byrules(&parser,parser.rt.by_week_no,
00476                               ICAL_BY_WEEKNO_SIZE,value);
00477        } else if (strcmp(name,"BYMONTH") == 0){
00478            icalrecur_add_byrules(&parser,parser.rt.by_month,
00479                               ICAL_BY_MONTH_SIZE,value);
00480        } else if (strcmp(name,"BYSETPOS") == 0){
00481            icalrecur_add_byrules(&parser,parser.rt.by_set_pos,
00482                               ICAL_BY_SETPOS_SIZE,value);
00483        } else {
00484            icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
00485            icalrecurrencetype_clear(&parser.rt);
00486               free(parser.copy);
00487            return parser.rt;
00488        }
00489        
00490     }
00491 
00492     free(parser.copy);
00493 
00494     return parser.rt;
00495 
00496 }
00497 
00498 static struct { char* str;size_t offset; int limit;  } recurmap[] = 
00499 {
00500     {";BYSECOND=",offsetof(struct icalrecurrencetype,by_second),ICAL_BY_SECOND_SIZE - 1},
00501     {";BYMINUTE=",offsetof(struct icalrecurrencetype,by_minute),ICAL_BY_MINUTE_SIZE - 1},
00502     {";BYHOUR=",offsetof(struct icalrecurrencetype,by_hour),ICAL_BY_HOUR_SIZE - 1},
00503     {";BYDAY=",offsetof(struct icalrecurrencetype,by_day),ICAL_BY_DAY_SIZE - 1},
00504     {";BYMONTHDAY=",offsetof(struct icalrecurrencetype,by_month_day),ICAL_BY_MONTHDAY_SIZE - 1},
00505     {";BYYEARDAY=",offsetof(struct icalrecurrencetype,by_year_day),ICAL_BY_YEARDAY_SIZE - 1},
00506     {";BYWEEKNO=",offsetof(struct icalrecurrencetype,by_week_no),ICAL_BY_WEEKNO_SIZE - 1},
00507     {";BYMONTH=",offsetof(struct icalrecurrencetype,by_month),ICAL_BY_MONTH_SIZE - 1},
00508     {";BYSETPOS=",offsetof(struct icalrecurrencetype,by_set_pos),ICAL_BY_SETPOS_SIZE - 1},
00509     {0,0,0},
00510 };
00511 
00512 /* A private routine in icalvalue.c */
00513 void print_date_to_string(char* str,  struct icaltimetype *data);
00514 void print_datetime_to_string(char* str,  struct icaltimetype *data);
00515 
00516 char* icalrecurrencetype_as_string(struct icalrecurrencetype *recur)
00517 {
00518     char* str;
00519     char *str_p;
00520     size_t buf_sz = 200;
00521     char temp[20];
00522     int i,j;
00523 
00524     if(recur->freq == ICAL_NO_RECURRENCE){
00525        return 0;
00526     }
00527 
00528     str = (char*)icalmemory_tmp_buffer(buf_sz);
00529     str_p = str;
00530 
00531     icalmemory_append_string(&str,&str_p,&buf_sz,"FREQ=");
00532     icalmemory_append_string(&str,&str_p,&buf_sz,
00533                           icalrecur_freq_to_string(recur->freq));
00534 
00535     if(recur->until.year != 0){
00536        
00537        temp[0] = 0;
00538        if (recur->until.is_date)
00539            print_date_to_string(temp,&(recur->until));
00540        else
00541            print_datetime_to_string(temp,&(recur->until));
00542        
00543        icalmemory_append_string(&str,&str_p,&buf_sz,";UNTIL=");
00544        icalmemory_append_string(&str,&str_p,&buf_sz, temp);
00545     }
00546 
00547     if(recur->count != 0){
00548        snprintf(temp,sizeof(temp),"%d",recur->count);
00549        icalmemory_append_string(&str,&str_p,&buf_sz,";COUNT=");
00550        icalmemory_append_string(&str,&str_p,&buf_sz, temp);
00551     }
00552 
00553     if(recur->interval != 0){
00554        snprintf(temp,sizeof(temp),"%d",recur->interval);
00555        icalmemory_append_string(&str,&str_p,&buf_sz,";INTERVAL=");
00556        icalmemory_append_string(&str,&str_p,&buf_sz, temp);
00557     }
00558     
00559     for(j =0; recurmap[j].str != 0; j++){
00560        short* array = (short*)(recurmap[j].offset+ (size_t)recur);
00561        int limit = recurmap[j].limit;
00562 
00563        /* Skip unused arrays */
00564        if( array[0] != ICAL_RECURRENCE_ARRAY_MAX ) {
00565 
00566            icalmemory_append_string(&str,&str_p,&buf_sz,recurmap[j].str);
00567            
00568            for(i=0; 
00569               i< limit  && array[i] != ICAL_RECURRENCE_ARRAY_MAX;
00570               i++){
00571               if (j == 3) { /* BYDAY */
00572                   const char *daystr = icalrecur_weekday_to_string(
00573                      icalrecurrencetype_day_day_of_week(array[i]));
00574                   int pos;
00575 
00576                   pos = icalrecurrencetype_day_position(array[i]);  
00577                   
00578                   if (pos == 0)
00579                      icalmemory_append_string(&str,&str_p,&buf_sz,daystr);
00580                   else {
00581                      snprintf(temp,sizeof(temp),"%d%s",pos,daystr);
00582                      icalmemory_append_string(&str,&str_p,&buf_sz,temp);
00583                   }                  
00584                   
00585               } else {
00586                   snprintf(temp,sizeof(temp),"%d",array[i]);
00587                   icalmemory_append_string(&str,&str_p,&buf_sz, temp);
00588               }
00589               
00590               if( (i+1)<limit &&array[i+1] 
00591                   != ICAL_RECURRENCE_ARRAY_MAX){
00592                   icalmemory_append_char(&str,&str_p,&buf_sz,',');
00593               }
00594            }   
00595        }   
00596     }
00597 
00598     /* If week start is not monday (the default per RFC2445) append WKST */
00599     if (recur->week_start != ICAL_MONDAY_WEEKDAY && recur->week_start != ICAL_NO_WEEKDAY) {
00600        icalmemory_append_string(&str,&str_p,&buf_sz,";WKST=");
00601        icalmemory_append_string(&str,&str_p,&buf_sz,
00602                      icalrecur_weekday_to_string(recur->week_start));
00603     }
00604 
00605     return  str;
00606 }
00607 
00608 
00609 /************************* occurrence iteration routiens ******************/
00610 
00611 enum byrule {
00612     NO_CONTRACTION = -1,
00613     BY_SECOND = 0,
00614     BY_MINUTE = 1,
00615     BY_HOUR = 2,
00616     BY_DAY = 3,
00617     BY_MONTH_DAY = 4,
00618     BY_YEAR_DAY = 5,
00619     BY_WEEK_NO = 6,
00620     BY_MONTH = 7,
00621     BY_SET_POS
00622 };
00623 
00624 
00625 
00626 struct icalrecur_iterator_impl {
00627        
00628     struct icaltimetype dtstart; /* Hack. Make into time_t */
00629     struct icaltimetype last; /* last time return from _iterator_next*/
00630     int occurrence_no; /* number of step made on t iterator */
00631     struct icalrecurrencetype rule;
00632     
00633     short days[366];
00634     short days_index;
00635     
00636     enum byrule byrule;
00637     short by_indices[9];
00638     short orig_data[9]; 
00641     short *by_ptrs[9]; 
00643 };
00644 
00645 static void increment_year(icalrecur_iterator* impl, int inc);
00646 
00647 int icalrecur_iterator_sizeof_byarray(short* byarray)
00648 {
00649     int array_itr;
00650 
00651     for(array_itr = 0; 
00652        byarray[array_itr] != ICAL_RECURRENCE_ARRAY_MAX;
00653        array_itr++){
00654     }
00655 
00656     return array_itr;
00657 }
00658 
00659 enum expand_table {
00660     UNKNOWN  = 0,
00661     CONTRACT = 1,
00662     EXPAND =2,
00663     ILLEGAL=3
00664 };
00665 
00673 struct expand_split_map_struct 
00674 { 
00675        icalrecurrencetype_frequency frequency;
00676 
00677        /* Elements of the 'map' array correspond to the BYxxx rules:
00678            Second,Minute,Hour,Day,Month Day,Year Day,Week No,Month*/
00679 
00680        short map[8];
00681 }; 
00682 
00683 static struct expand_split_map_struct expand_map[] =
00684 {
00685     {ICAL_SECONDLY_RECURRENCE,{1,1,1,1,1,1,1,1}},
00686     {ICAL_MINUTELY_RECURRENCE,{2,1,1,1,1,1,1,1}},
00687     {ICAL_HOURLY_RECURRENCE,  {2,2,1,1,1,1,1,1}},
00688     {ICAL_DAILY_RECURRENCE,   {2,2,2,1,1,1,1,1}},
00689     {ICAL_WEEKLY_RECURRENCE,  {2,2,2,2,3,3,1,1}},
00690     {ICAL_MONTHLY_RECURRENCE, {2,2,2,2,2,3,3,1}},
00691     {ICAL_YEARLY_RECURRENCE,  {2,2,2,2,2,2,2,2}},
00692     {ICAL_NO_RECURRENCE,      {0,0,0,0,0,0,0,0}}
00693 
00694 };
00695 
00696 
00697 
00699 static
00700 int icalrecur_two_byrule(icalrecur_iterator* impl,
00701                       enum byrule one,enum byrule two)
00702 {
00703     short test_array[9];
00704     enum byrule itr;
00705     int passes = 0;
00706 
00707     memset(test_array,0,sizeof(test_array));
00708 
00709     test_array[one] = 1;
00710     test_array[two] = 1;
00711 
00712     for(itr = BY_DAY; itr != BY_SET_POS; itr++){
00713 
00714        if( (test_array[itr] == 0  &&
00715             impl->by_ptrs[itr][0] != ICAL_RECURRENCE_ARRAY_MAX
00716            ) ||
00717            (test_array[itr] == 1  &&
00718             impl->by_ptrs[itr][0] == ICAL_RECURRENCE_ARRAY_MAX
00719               ) 
00720            ) {
00721            /* test failed */
00722            passes = 0;
00723        }
00724     }
00725 
00726     return passes;
00727 
00728 } 
00729 
00731 static int icalrecur_one_byrule(icalrecur_iterator* impl,enum byrule one)
00732 {
00733     int passes = 1;
00734     enum byrule itr;
00735 
00736     for(itr = BY_DAY; itr != BY_SET_POS; itr++){
00737        
00738        if ((itr==one && impl->by_ptrs[itr][0] == ICAL_RECURRENCE_ARRAY_MAX) ||
00739            (itr!=one && impl->by_ptrs[itr][0] != ICAL_RECURRENCE_ARRAY_MAX)) {
00740            passes = 0;
00741        }
00742     }
00743 
00744     return passes;
00745 } 
00746 
00747 static int count_byrules(icalrecur_iterator* impl)
00748 {
00749     int count = 0;
00750     enum byrule itr;
00751 
00752     for(itr = BY_DAY; itr <= BY_SET_POS; itr++){
00753        if(impl->by_ptrs[itr][0] != ICAL_RECURRENCE_ARRAY_MAX){
00754            count++;
00755        }
00756     }
00757 
00758     return count;
00759 }
00760 
00761 
00762 static void setup_defaults(icalrecur_iterator* impl, 
00763                   enum byrule byrule, icalrecurrencetype_frequency req,
00764                   int deftime, int *timepart)
00765 {
00766 
00767     icalrecurrencetype_frequency freq;
00768     freq = impl->rule.freq;
00769 
00770     /* Re-write the BY rule arrays with data from the DTSTART time so
00771        we don't have to explicitly deal with DTSTART */
00772 
00773     if(impl->by_ptrs[byrule][0] == ICAL_RECURRENCE_ARRAY_MAX &&
00774        expand_map[freq].map[byrule] != CONTRACT){
00775        impl->by_ptrs[byrule][0] = (short)deftime;
00776     }
00777 
00778     /* Initialize the first occurrence */
00779     if( freq != req && expand_map[freq].map[byrule] != CONTRACT){
00780        *timepart = impl->by_ptrs[byrule][0];
00781     }
00782 
00783 
00784 }
00785 
00786 static int has_by_data(icalrecur_iterator* impl, enum byrule byrule){
00787 
00788     return (impl->orig_data[byrule] == 1);
00789 }
00790 
00791 
00792 static int expand_year_days(icalrecur_iterator* impl, int year);
00793 
00794 
00795 icalrecur_iterator* icalrecur_iterator_new(struct icalrecurrencetype rule, 
00796                                       struct icaltimetype dtstart)
00797 {
00798     icalrecur_iterator* impl;
00799     icalrecurrencetype_frequency freq;
00800 
00801     if ( ( impl = (icalrecur_iterator*)
00802           malloc(sizeof(icalrecur_iterator))) == 0) {
00803        icalerror_set_errno(ICAL_NEWFAILED_ERROR);
00804        return 0;
00805     }
00806 
00807     memset(impl,0,sizeof(icalrecur_iterator));
00808 
00809     impl->rule = rule;
00810     impl->last = dtstart;
00811     impl->dtstart = dtstart;
00812     impl->days_index =0;
00813     impl->occurrence_no = 0;
00814     freq = impl->rule.freq;
00815 
00816     /* Set up convienience pointers to make the code simpler. Allows
00817        us to iterate through all of the BY* arrays in the rule. */
00818 
00819     impl->by_ptrs[BY_MONTH]=impl->rule.by_month;
00820     impl->by_ptrs[BY_WEEK_NO]=impl->rule.by_week_no;
00821     impl->by_ptrs[BY_YEAR_DAY]=impl->rule.by_year_day;
00822     impl->by_ptrs[BY_MONTH_DAY]=impl->rule.by_month_day;
00823     impl->by_ptrs[BY_DAY]=impl->rule.by_day;
00824     impl->by_ptrs[BY_HOUR]=impl->rule.by_hour;
00825     impl->by_ptrs[BY_MINUTE]=impl->rule.by_minute;
00826     impl->by_ptrs[BY_SECOND]=impl->rule.by_second;
00827     impl->by_ptrs[BY_SET_POS]=impl->rule.by_set_pos;
00828 
00829     memset(impl->orig_data,0,9*sizeof(short));
00830 
00831     /* Note which by rules had data in them when the iterator was
00832        created. We can't use the actuall by_x arrays, because the
00833        empty ones will be given default values later in this
00834        routine. The orig_data array will be used later in has_by_data */
00835 
00836     impl->orig_data[BY_MONTH]
00837        = (short)(impl->rule.by_month[0]!=ICAL_RECURRENCE_ARRAY_MAX);
00838     impl->orig_data[BY_WEEK_NO]
00839       =(short)(impl->rule.by_week_no[0]!=ICAL_RECURRENCE_ARRAY_MAX);
00840     impl->orig_data[BY_YEAR_DAY]
00841     =(short)(impl->rule.by_year_day[0]!=ICAL_RECURRENCE_ARRAY_MAX);
00842     impl->orig_data[BY_MONTH_DAY]
00843     =(short)(impl->rule.by_month_day[0]!=ICAL_RECURRENCE_ARRAY_MAX);
00844     impl->orig_data[BY_DAY]
00845        = (short)(impl->rule.by_day[0]!=ICAL_RECURRENCE_ARRAY_MAX);
00846     impl->orig_data[BY_HOUR]
00847        = (short)(impl->rule.by_hour[0]!=ICAL_RECURRENCE_ARRAY_MAX);
00848     impl->orig_data[BY_MINUTE]
00849      = (short)(impl->rule.by_minute[0]!=ICAL_RECURRENCE_ARRAY_MAX);
00850     impl->orig_data[BY_SECOND]
00851      = (short)(impl->rule.by_second[0]!=ICAL_RECURRENCE_ARRAY_MAX);
00852     impl->orig_data[BY_SET_POS]
00853      = (short)(impl->rule.by_set_pos[0]!=ICAL_RECURRENCE_ARRAY_MAX);
00854 
00855 
00856     /* Check if the recurrence rule is legal */
00857 
00858     /* If the BYYEARDAY appears, no other date rule part may appear.   */
00859 
00860     if(icalrecur_two_byrule(impl,BY_YEAR_DAY,BY_MONTH) ||
00861        icalrecur_two_byrule(impl,BY_YEAR_DAY,BY_WEEK_NO) ||
00862        icalrecur_two_byrule(impl,BY_YEAR_DAY,BY_MONTH_DAY) ||
00863        icalrecur_two_byrule(impl,BY_YEAR_DAY,BY_DAY) ){
00864 
00865        icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
00866 
00867        return 0;
00868     }
00869 
00870     /* BYWEEKNO and BYMONTH rule parts may not both appear.*/
00871 
00872     if(icalrecur_two_byrule(impl,BY_WEEK_NO,BY_MONTH)){
00873        icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
00874 
00875        icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
00876        return 0;
00877     }
00878 
00879     /* BYWEEKNO and BYMONTHDAY rule parts may not both appear.*/
00880 
00881     if(icalrecur_two_byrule(impl,BY_WEEK_NO,BY_MONTH_DAY)){
00882        icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
00883 
00884        icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
00885        return 0;
00886     }
00887 
00888 
00889     /*For MONTHLY recurrences (FREQ=MONTHLY) neither BYYEARDAY nor
00890       BYWEEKNO may appear. */
00891 
00892     if(freq == ICAL_MONTHLY_RECURRENCE && 
00893        icalrecur_one_byrule(impl,BY_WEEK_NO)){
00894        icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
00895        return 0;
00896     }
00897 
00898 
00899     /*For WEEKLY recurrences (FREQ=WEEKLY) neither BYMONTHDAY nor
00900       BYYEARDAY may appear. */
00901 
00902     if(freq == ICAL_WEEKLY_RECURRENCE && 
00903        icalrecur_one_byrule(impl,BY_MONTH_DAY )) {
00904        icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
00905        return 0;
00906     }
00907 
00908     /* BYYEARDAY may only appear in YEARLY rules */
00909     if(freq != ICAL_YEARLY_RECURRENCE && 
00910        icalrecur_one_byrule(impl,BY_YEAR_DAY )) {
00911        icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
00912        return 0;
00913     }
00914 
00915     /* Rewrite some of the rules and set up defaults to make later
00916        processing easier. Primarily, t involves copying an element
00917        from the start time into the corresponding BY_* array when the
00918        BY_* array is empty */
00919 
00920 
00921     setup_defaults(impl,BY_SECOND,ICAL_SECONDLY_RECURRENCE,
00922                  impl->dtstart.second,
00923                  &(impl->last.second));
00924 
00925     setup_defaults(impl,BY_MINUTE,ICAL_MINUTELY_RECURRENCE,
00926                  impl->dtstart.minute,
00927                  &(impl->last.minute));
00928 
00929     setup_defaults(impl,BY_HOUR,ICAL_HOURLY_RECURRENCE,
00930                  impl->dtstart.hour,
00931                  &(impl->last.hour));
00932 
00933     setup_defaults(impl,BY_MONTH_DAY,ICAL_DAILY_RECURRENCE,
00934                  impl->dtstart.day,
00935                  &(impl->last.day));
00936 
00937     setup_defaults(impl,BY_MONTH,ICAL_MONTHLY_RECURRENCE,
00938                  impl->dtstart.month,
00939                  &(impl->last.month));
00940 
00941 
00942     if(impl->rule.freq == ICAL_WEEKLY_RECURRENCE ){
00943 
00944        if(impl->by_ptrs[BY_DAY][0] == ICAL_RECURRENCE_ARRAY_MAX){
00945 
00946           /* Weekly recurrences with no BY_DAY data should occur on the
00947              same day of the week as the start time . */
00948           impl->by_ptrs[BY_DAY][0] = (short)icaltime_day_of_week(impl->dtstart);
00949 
00950        } else {
00951          /* If there is BY_DAY data, then we need to move the initial
00952             time to the start of the BY_DAY data. That is if the
00953             start time is on a Wednesday, and the rule has
00954             BYDAY=MO,WE,FR, move the initial time back to
00955             monday. Otherwise, jumping to the next week ( jumping 7
00956             days ahead ) will skip over some occurrences in the
00957             second week. */
00958 
00959          /* This depends on impl->by_ptrs[BY_DAY] being correctly sorted by
00960           * day. This should probably be abstracted to make such assumption
00961           * more explicit. */
00962          short dow = (short)(impl->by_ptrs[BY_DAY][0]-icaltime_day_of_week(impl->last));
00963          if (dow > impl->rule.week_start-1) dow -= 7;
00964          impl->last.day += dow;
00965          impl->last = icaltime_normalize(impl->last);
00966       }
00967       
00968 
00969     }
00970 
00971     /* For YEARLY rule, begin by setting up the year days array . The
00972        YEARLY rules work by expanding one year at a time. */
00973 
00974     if(impl->rule.freq == ICAL_YEARLY_RECURRENCE){
00975         struct icaltimetype next;
00976 
00977        for (;;) {
00978             expand_year_days(impl, impl->last.year);
00979            if (impl->days[0] != ICAL_RECURRENCE_ARRAY_MAX)
00980                break; /* break when no days are expanded */
00981            increment_year(impl,impl->rule.interval);
00982        }
00983 
00984         /* Copy the first day into last. */
00985        next = icaltime_from_day_of_year(impl->days[0], impl->last.year);
00986     
00987        impl->last.day =  next.day;
00988        impl->last.month =  next.month;
00989     } 
00990 
00991 
00992     /* If this is a monthly interval with by day data, then we need to
00993        set the last value to the appropriate day of the month */
00994 
00995     if(impl->rule.freq == ICAL_MONTHLY_RECURRENCE)
00996        if (has_by_data(impl,BY_DAY)) {
00997 
00998        int dow = icalrecurrencetype_day_day_of_week(
00999            impl->by_ptrs[BY_DAY][impl->by_indices[BY_DAY]]);  
01000        int pos =  icalrecurrencetype_day_position(
01001            impl->by_ptrs[BY_DAY][impl->by_indices[BY_DAY]]);  
01002        
01003        int poscount = 0;
01004        int days_in_month = 
01005             icaltime_days_in_month(impl->last.month, impl->last.year); 
01006        
01007         if(pos >= 0){
01008             /* Count up from the first day pf the month to find the
01009                pos'th weekday of dow ( like the second monday. ) */
01010 
01011             for(impl->last.day = 1;
01012                 impl->last.day <= days_in_month;
01013                 impl->last.day++){
01014                 
01015                 if(icaltime_day_of_week(impl->last) == dow){
01016                     if(++poscount == pos || pos == 0){
01017                         break;
01018                     }
01019                 }
01020             }
01021         } else {
01022             /* Count down from the last day pf the month to find the
01023                pos'th weekday of dow ( like the second to last monday. ) */
01024             pos = -pos;
01025             for(impl->last.day = days_in_month;
01026                 impl->last.day != 0;
01027                 impl->last.day--){
01028                 
01029                 if(icaltime_day_of_week(impl->last) == dow){
01030                     if(++poscount == pos ){
01031                         break;
01032                     }
01033                 }
01034             }
01035         }
01036 
01037 
01038        if(impl->last.day > days_in_month || impl->last.day == 0){
01039            icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
01040            return 0;
01041        }
01042        
01043     } else if (has_by_data(impl,BY_MONTH_DAY)) {
01044        impl->last = icaltime_normalize(impl->last);
01045     }
01046 
01047 
01048 
01049     return impl;
01050 }
01051 
01052 
01053 void icalrecur_iterator_free(icalrecur_iterator* i)
01054 {
01055     icalerror_check_arg_rv((i!=0),"impl");
01056     
01057     free(i);
01058 
01059 }
01060 
01061 static void increment_year(icalrecur_iterator* impl, int inc)
01062 {
01063     impl->last.year+=inc;
01064 }
01065 
01069 static void increment_month(icalrecur_iterator* impl)
01070 {
01071     int years;
01072 
01073      if(has_by_data(impl,BY_MONTH) ){
01074          /* Ignore the frequency and use the byrule data */
01075          
01076          impl->by_indices[BY_MONTH]++;
01077          
01078          if (impl->by_ptrs[BY_MONTH][impl->by_indices[BY_MONTH]]
01079              ==ICAL_RECURRENCE_ARRAY_MAX){
01080              impl->by_indices[BY_MONTH] = 0;
01081 
01082              increment_year(impl,1);
01083              
01084          }
01085          
01086          impl->last.month = 
01087              impl->by_ptrs[BY_MONTH][impl->by_indices[BY_MONTH]];
01088 
01089      } else {
01090          
01091          int inc;
01092 
01093          if(impl->rule.freq == ICAL_MONTHLY_RECURRENCE){
01094             inc =  impl->rule.interval;
01095          } else {
01096              inc = 1;
01097          }
01098 
01099          impl->last.month+=inc;
01100          
01101          /* Months are offset by one */
01102          impl->last.month--;
01103          
01104          years = impl->last.month / 12;
01105          
01106          impl->last.month = impl->last.month % 12;
01107          
01108          impl->last.month++;
01109          
01110          if (years != 0){
01111              increment_year(impl,years);
01112          }
01113      }
01114 }
01115 
01116 static void increment_monthday(icalrecur_iterator* impl, int inc)
01117 {
01118     int i;
01119 
01120     for(i=0; i<inc; i++){
01121        
01122        int days_in_month = 
01123            icaltime_days_in_month(impl->last.month, impl->last.year);
01124 
01125        impl->last.day++;
01126        
01127        if (impl->last.day > days_in_month){
01128            impl->last.day = impl->last.day-days_in_month;
01129            increment_month(impl);
01130        }
01131     }
01132 }
01133 
01134 
01135 static void increment_hour(icalrecur_iterator* impl, int inc)
01136 {
01137     int days;
01138 
01139     impl->last.hour+=inc;
01140 
01141     days = impl->last.hour / 24;
01142     impl->last.hour = impl->last.hour % 24;
01143 
01144     if (impl->days != 0){
01145        increment_monthday(impl,days);
01146     }
01147 }
01148 
01149 static void increment_minute(icalrecur_iterator* impl, int inc)
01150 {
01151     int hours;
01152 
01153     impl->last.minute+=inc;
01154 
01155     hours = impl->last.minute / 60;
01156      impl->last.minute =  impl->last.minute % 60;
01157 
01158      if (hours != 0){
01159        increment_hour(impl,hours);
01160     }
01161 
01162 }
01163 
01164 static void increment_second(icalrecur_iterator* impl, int inc)
01165 {
01166     int minutes;
01167 
01168     impl->last.second+=inc;
01169     
01170     minutes = impl->last.second / 60;
01171     impl->last.second = impl->last.second % 60;
01172     
01173     if (minutes != 0)
01174     {
01175        increment_minute(impl, minutes);
01176     }                 
01177 }
01178 
01179 #if 0
01180 #include "ical.h"
01181 void test_increment()
01182 {
01183     icalrecur_iterator impl;
01184 
01185     impl.last =  icaltime_from_string("20000101T000000Z");
01186 
01187     printf("Orig: %s\n",icaltime_as_ctime(impl.last));
01188     
01189     increment_second(&impl,5);
01190     printf("+ 5 sec    : %s\n",icaltime_as_ctime(impl.last));
01191 
01192     increment_second(&impl,355);
01193     printf("+ 355 sec  : %s\n",icaltime_as_ctime(impl.last));
01194 
01195     increment_minute(&impl,5);
01196     printf("+ 5 min    : %s\n",icaltime_as_ctime(impl.last));
01197 
01198     increment_minute(&impl,360);
01199     printf("+ 360 min  : %s\n",icaltime_as_ctime(impl.last));
01200     increment_hour(&impl,5);
01201     printf("+ 5 hours  : %s\n",icaltime_as_ctime(impl.last));
01202     increment_hour(&impl,43);
01203     printf("+ 43 hours : %s\n",icaltime_as_ctime(impl.last));
01204     increment_monthday(&impl,3);
01205     printf("+ 3 days   : %s\n",icaltime_as_ctime(impl.last));
01206     increment_monthday(&impl,600);
01207     printf("+ 600 days  : %s\n",icaltime_as_ctime(impl.last));
01208        
01209 }
01210 
01211 #endif 
01212 
01213 static int next_second(icalrecur_iterator* impl)
01214 {
01215 
01216   int has_by_second = (impl->by_ptrs[BY_SECOND][0]!=ICAL_RECURRENCE_ARRAY_MAX);
01217   int this_frequency = (impl->rule.freq == ICAL_SECONDLY_RECURRENCE);
01218 
01219   int end_of_data = 0;
01220 
01221   assert(has_by_second || this_frequency);
01222 
01223   if(  has_by_second ){
01224       /* Ignore the frequency and use the byrule data */
01225 
01226       impl->by_indices[BY_SECOND]++;
01227 
01228       if (impl->by_ptrs[BY_SECOND][impl->by_indices[BY_SECOND]]
01229          ==ICAL_RECURRENCE_ARRAY_MAX){
01230          impl->by_indices[BY_SECOND] = 0;
01231 
01232          end_of_data = 1;
01233       }
01234 
01235 
01236       impl->last.second = 
01237          impl->by_ptrs[BY_SECOND][impl->by_indices[BY_SECOND]];
01238       
01239       
01240   } else if( !has_by_second &&  this_frequency ){
01241       /* Compute the next value from the last time and the frequency interval*/
01242       increment_second(impl, impl->rule.interval);
01243 
01244   }
01245 
01246   /* If we have gone through all of the seconds on the BY list, then we
01247      need to move to the next minute */
01248 
01249   if(has_by_second && end_of_data && this_frequency ){
01250       increment_minute(impl,1);
01251   }
01252 
01253   return end_of_data;
01254 
01255 }
01256 
01257 static int next_minute(icalrecur_iterator* impl)
01258 {
01259 
01260   int has_by_minute = (impl->by_ptrs[BY_MINUTE][0]!=ICAL_RECURRENCE_ARRAY_MAX);
01261   int this_frequency = (impl->rule.freq == ICAL_MINUTELY_RECURRENCE);
01262 
01263   int end_of_data = 0;
01264 
01265   assert(has_by_minute || this_frequency);
01266 
01267 
01268   if (next_second(impl) == 0){
01269       return 0;
01270   }
01271 
01272   if(  has_by_minute ){
01273       /* Ignore the frequency and use the byrule data */
01274 
01275       impl->by_indices[BY_MINUTE]++;
01276       
01277       if (impl->by_ptrs[BY_MINUTE][impl->by_indices[BY_MINUTE]]
01278          ==ICAL_RECURRENCE_ARRAY_MAX){
01279 
01280          impl->by_indices[BY_MINUTE] = 0;
01281          
01282          end_of_data = 1;
01283       }
01284 
01285       impl->last.minute = 
01286          impl->by_ptrs[BY_MINUTE][impl->by_indices[BY_MINUTE]];
01287 
01288   } else if( !has_by_minute &&  this_frequency ){
01289       /* Compute the next value from the last time and the frequency interval*/
01290       increment_minute(impl,impl->rule.interval);
01291   } 
01292 
01293 /* If we have gone through all of the minutes on the BY list, then we
01294      need to move to the next hour */
01295 
01296   if(has_by_minute && end_of_data && this_frequency ){
01297       increment_hour(impl,1);
01298   }
01299 
01300   return end_of_data;
01301 }
01302 
01303 static int next_hour(icalrecur_iterator* impl)
01304 {
01305 
01306   int has_by_hour = (impl->by_ptrs[BY_HOUR][0]!=ICAL_RECURRENCE_ARRAY_MAX);
01307   int this_frequency = (impl->rule.freq == ICAL_HOURLY_RECURRENCE);
01308 
01309   int end_of_data = 0;
01310 
01311   assert(has_by_hour || this_frequency);
01312 
01313   if (next_minute(impl) == 0){
01314       return 0;
01315   }
01316 
01317   if(  has_by_hour ){
01318       /* Ignore the frequency and use the byrule data */
01319 
01320       impl->by_indices[BY_HOUR]++;
01321       
01322       if (impl->by_ptrs[BY_HOUR][impl->by_indices[BY_HOUR]]
01323          ==ICAL_RECURRENCE_ARRAY_MAX){
01324          impl->by_indices[BY_HOUR] = 0;
01325          
01326          end_of_data = 1;
01327       }
01328 
01329       impl->last.hour = 
01330          impl->by_ptrs[BY_HOUR][impl->by_indices[BY_HOUR]];
01331 
01332   } else if( !has_by_hour &&  this_frequency ){
01333       /* Compute the next value from the last time and the frequency interval*/
01334       increment_hour(impl,impl->rule.interval);
01335 
01336   }
01337 
01338   /* If we have gone through all of the hours on the BY list, then we
01339      need to move to the next day */
01340 
01341   if(has_by_hour && end_of_data && this_frequency ){
01342       increment_monthday(impl,1);
01343   }
01344 
01345   return end_of_data;
01346 
01347 }
01348 
01349 static int next_day(icalrecur_iterator* impl)
01350 {
01351 
01352   int has_by_day = (impl->by_ptrs[BY_DAY][0]!=ICAL_RECURRENCE_ARRAY_MAX);
01353   int this_frequency = (impl->rule.freq == ICAL_DAILY_RECURRENCE);
01354 
01355   assert(has_by_day || this_frequency);
01356 
01357   if (next_hour(impl) == 0){
01358       return 0;
01359   }
01360 
01361   /* Always increment through the interval, since this routine is not
01362      called by any other next_* routine, and the days that are
01363      excluded will be taken care of by restriction filtering */
01364 
01365   if(this_frequency){
01366       increment_monthday(impl,impl->rule.interval);
01367   } else {
01368       increment_monthday(impl,1);
01369   }
01370 
01371 
01372   return 0;
01373 
01374 }
01375 
01376 
01377 static int next_yearday(icalrecur_iterator* impl)
01378 {
01379 
01380   int has_by_yearday = (impl->by_ptrs[BY_YEAR_DAY][0]!=ICAL_RECURRENCE_ARRAY_MAX);
01381 
01382   int end_of_data = 0;
01383 
01384   assert(has_by_yearday );
01385 
01386   if (next_hour(impl) == 0){
01387       return 0;
01388   }
01389 
01390   impl->by_indices[BY_YEAR_DAY]++;
01391   
01392   if (impl->by_ptrs[BY_YEAR_DAY][impl->by_indices[BY_YEAR_DAY]]
01393       ==ICAL_RECURRENCE_ARRAY_MAX){
01394       impl->by_indices[BY_YEAR_DAY] = 0;
01395       
01396       end_of_data = 1;
01397   }
01398   
01399   impl->last.day = 
01400       impl->by_ptrs[BY_YEAR_DAY][impl->by_indices[BY_YEAR_DAY]];
01401   
01402   if(has_by_yearday && end_of_data){
01403       increment_year(impl,1);
01404   }
01405 
01406   return end_of_data;
01407 
01408 }
01409 
01410 
01411 /* Returns the day of the month for the current month of t that is the
01412    pos'th instance of the day-of-week dow */
01413 
01414 static int nth_weekday(int dow, int pos, struct icaltimetype t){
01415 
01416     int days_in_month = icaltime_days_in_month(t.month, t.year);
01417     int end_dow, start_dow;
01418     int wd;
01419 
01420     if(pos >= 0){
01421         t.day = 1;
01422         start_dow = icaltime_day_of_week(t);        
01423         
01424         if (pos != 0) {
01425             pos--;
01426         }
01427          
01428         /* find month day of first occurrence of dow -- such as the
01429            month day of the first monday */
01430 
01431         wd = dow-start_dow+1;
01432 
01433         if (wd <= 0){
01434             wd = wd + 7;
01435         }
01436 
01437         wd = wd + pos * 7;
01438         
01439     } else {
01440         t.day = days_in_month;
01441         end_dow = icaltime_day_of_week(t);
01442 
01443         pos++;
01444 
01445         /* find month day of last occurrence of dow -- such as the
01446            month day of the last monday */
01447 
01448         wd = (end_dow - dow);
01449 
01450         if (wd < 0){
01451             wd = wd+ 7;
01452         }
01453 
01454         wd = days_in_month - wd;
01455 
01456         wd = wd + pos * 7;
01457     }
01458         
01459     return wd;
01460 }
01461 
01462 static int is_day_in_byday(icalrecur_iterator* impl,struct icaltimetype tt){
01463 
01464     int idx; 
01465 
01466     for(idx = 0; BYDAYPTR[idx] != ICAL_RECURRENCE_ARRAY_MAX; idx++){
01467         int dow = icalrecurrencetype_day_day_of_week(BYDAYPTR[idx]);  
01468         int pos =  icalrecurrencetype_day_position(BYDAYPTR[idx]);  
01469         int this_dow = icaltime_day_of_week(tt);
01470         
01471         if( (pos == 0 &&  dow == this_dow ) || /* Just a dow, like "TU" or "FR" */
01472             (nth_weekday(dow,pos,tt) == tt.day)){ /*pos+wod: "3FR" or -1TU" */
01473             return 1;
01474         }
01475     }
01476 
01477     return 0;
01478 }
01479 
01480 static int next_month(icalrecur_iterator* impl)
01481 {
01482     int data_valid = 1;
01483     
01484     int this_frequency = (impl->rule.freq == ICAL_MONTHLY_RECURRENCE);
01485     
01486     assert( has_by_data(impl,BY_MONTH) || this_frequency);
01487   
01488     /* Iterate through the occurrences within a day. If we don't get to
01489        the end of the intra-day data, don't bother going to the next
01490        month */
01491     
01492     if (next_hour(impl) == 0){
01493         return data_valid; /* Signal that the data is valid */
01494     }
01495     
01496     /* Now iterate through the occurrences within a month -- by days,
01497        weeks or weekdays.  */
01498 
01499    /* 
01500     * Case 1: 
01501     * Rules Like: FREQ=MONTHLY;INTERVAL=1;BYDAY=FR;BYMONTHDAY=13 
01502     */
01503     
01504     if(has_by_data(impl,BY_DAY) && has_by_data(impl,BY_MONTH_DAY)){
01505       int day, idx,j;
01506       int days_in_month = icaltime_days_in_month(impl->last.month,
01507                                                    impl->last.year);
01508       /* Iterate through the remaining days in the month and check if
01509          each day is listed in the BY_DAY array and in the BY_MONTHDAY
01510          array. This seems very inneficient, but I think it is the
01511          simplest way to account for both BYDAY=1FR (First friday in
01512          month) and BYDAY=FR ( every friday in month ) */
01513 
01514       for(day = impl->last.day+1; day <= days_in_month; day++){
01515           for(idx = 0; BYDAYPTR[idx] != ICAL_RECURRENCE_ARRAY_MAX; idx++){
01516               for(j = 0; BYMDPTR[j]!=ICAL_RECURRENCE_ARRAY_MAX; j++){
01517                   int dow = 
01518                       icalrecurrencetype_day_day_of_week(BYDAYPTR[idx]);  
01519                   int pos =  icalrecurrencetype_day_position(BYDAYPTR[idx]);  
01520                   int mday = BYMDPTR[j];
01521                   int this_dow;
01522                   
01523                   impl->last.day = day;
01524                   this_dow = icaltime_day_of_week(impl->last);
01525                   
01526                   if( (pos == 0 &&  dow == this_dow && mday == day) || 
01527                       (nth_weekday(dow,pos,impl->last) == day && mday==day)){
01528                       goto MDEND;
01529                   }
01530               }
01531           }
01532       }
01533 
01534   MDEND:
01535 
01536       if ( day > days_in_month){
01537           impl->last.day = 1;
01538           increment_month(impl);
01539           impl->last.day--; /* Go back one day, so searches next month start at day 1 */
01540           data_valid = 0; /* signal that impl->last is invalid */
01541       }
01542 
01543     
01544    /* 
01545     * Case 2: 
01546     * Rules Like: FREQ=MONTHLY;INTERVAL=1;BYDAY=FR 
01547     */
01548   
01549   }  else if(has_by_data(impl,BY_DAY)){
01550       /* For this case, the weekdays are relative to the
01551          month. BYDAY=FR -> First Friday in month, etc. */
01552 
01553       /* This code iterates through the remaining days in the month
01554          and checks if each day is listed in the BY_DAY array. This
01555          seems very inneficient, but I think it is the simplest way to
01556          account for both BYDAY=1FR (First friday in month) and
01557          BYDAY=FR ( every friday in month ) */
01558 
01559       int day;
01560       int days_in_month = icaltime_days_in_month(impl->last.month,
01561                                                    impl->last.year);
01562       assert( BYDAYPTR[0]!=ICAL_RECURRENCE_ARRAY_MAX);
01563 
01564       for(day = impl->last.day+1; day <= days_in_month; day++){
01565           impl->last.day = day;
01566           if(is_day_in_byday(impl,impl->last)){
01567               data_valid = 1;
01568               break;
01569           }
01570       }
01571 
01572       if ( day > days_in_month){
01573           impl->last.day = 1;
01574           increment_month(impl);
01575 
01576           /* Did moving to the next month put us on a valid date? if
01577              so, note that the new data is valid, if, not, mark it
01578              invalid */
01579 
01580           if(is_day_in_byday(impl,impl->last)){
01581               data_valid = 1;
01582           } else {
01583               data_valid = 0; /* signal that impl->last is invalid */
01584           }
01585       }
01586 
01587      /* 
01588        * Case 3
01589        * Rules Like: FREQ=MONTHLY;COUNT=10;BYMONTHDAY=-3  
01590        */
01591 
01592   } else if (has_by_data(impl,BY_MONTH_DAY)) {
01593       int day, days_in_month;
01594 
01595       assert( BYMDPTR[0]!=ICAL_RECURRENCE_ARRAY_MAX);
01596 
01597       BYMDIDX++;
01598           
01599       /* Are we at the end of the BYDAY array? */
01600       if (BYMDPTR[BYMDIDX] ==ICAL_RECURRENCE_ARRAY_MAX){
01601           
01602           BYMDIDX = 0; /* Reset to 0 */      
01603           increment_month(impl);          
01604       }
01605       
01606       days_in_month = icaltime_days_in_month(impl->last.month,
01607                                                    impl->last.year);
01608 
01609       day = BYMDPTR[BYMDIDX];
01610       
01611       if (day < 0) {
01612           day = icaltime_days_in_month(impl->last.month, impl->last.year) + day + 1;
01613       }
01614 
01615       if ( day > days_in_month){
01616           impl->last.day = 1;
01617 
01618           /* Did moving to the next month put us on a valid date? if
01619              so, note that the new data is valid, if, not, mark it
01620              invalid */
01621 
01622           if(is_day_in_byday(impl,impl->last)){
01623               data_valid = 1;
01624           } else {
01625               data_valid = 0; /* signal that impl->last is invalid */
01626           }
01627       }
01628 
01629       impl->last.day = day;
01630 
01631   } else {
01632       int days_in_month;
01633 
01634       increment_month(impl);
01635 
01636       days_in_month = icaltime_days_in_month(impl->last.month,
01637                                                    impl->last.year);
01638       if (impl->last.day > days_in_month){
01639           data_valid = 0; /* signal that impl->last is invalid */
01640       }
01641   }
01642 
01643   return data_valid;
01644 
01645 }
01646 
01647 static int next_weekday_by_week(icalrecur_iterator* impl)
01648 {
01649 
01650   int end_of_data = 0;
01651   int start_of_week, dow;
01652   struct icaltimetype next;
01653 
01654   if (next_hour(impl) == 0){
01655       return 0;
01656   }
01657 
01658   if(!has_by_data(impl,BY_DAY)){
01659       return 1;
01660   }
01661 
01662        /* this call to 'sort_bydayrules' assures that the occurrences for
01663                weekly recurrences will be generated in a strict linear order. */
01664      sort_bydayrules(BYDAYPTR,impl->rule.week_start);
01665 
01666   /* If we get here, we need to step to tne next day */
01667 
01668   for (;;) {
01669       BYDAYIDX++; /* Look at next elem in BYDAY array */
01670       
01671       /* Are we at the end of the BYDAY array? */
01672       if (BYDAYPTR[BYDAYIDX]==ICAL_RECURRENCE_ARRAY_MAX){
01673          BYDAYIDX = 0; /* Reset to 0 */      
01674          end_of_data = 1; /* Signal that we're at the end */
01675       }
01676       
01677       /* Add the day of week offset to to the start of this week, and use
01678         that to get the next day */
01679       /* ignore position of dow ("4FR"), only use dow ("FR")*/
01680       dow = icalrecurrencetype_day_day_of_week(BYDAYPTR[BYDAYIDX]); 
01681       dow -= impl->rule.week_start; /* Set Sunday to be 0 */
01682       if (dow < 0) dow += 7;
01683 
01684       start_of_week = icaltime_start_doy_week(impl->last, impl->rule.week_start);
01685       
01686       if(dow+start_of_week <1){
01687           /* The selected date is in the previous year. */
01688           if(!end_of_data){    
01689               continue;
01690           }
01691       } 
01692  
01693       next = icaltime_from_day_of_year(start_of_week + dow,impl->last.year);
01694       
01695       impl->last.day =  next.day;
01696       impl->last.month =  next.month;
01697       impl->last.year =  next.year;
01698 
01699       return end_of_data;
01700   }
01701 
01702 }
01703 
01704 static int next_week(icalrecur_iterator* impl)
01705 {
01706   int end_of_data = 0;
01707 
01708   /* Increment to the next week day, if there is data at a level less than a week */
01709   if (next_weekday_by_week(impl) == 0){
01710       return 0; /* Have not reached end of week yet */
01711   }
01712 
01713   /* If we get here, we have incremented through the entire week, and
01714      can increment to the next week */
01715 
01716   if( has_by_data(impl,BY_WEEK_NO)){
01717       /*FREQ=WEEKLY;BYWEEK=20*/
01718     /* Use the Week Number byrule data */
01719       int week_no;
01720       struct icaltimetype t;
01721       
01722       impl->by_indices[BY_WEEK_NO]++;
01723       
01724       if (impl->by_ptrs[BY_WEEK_NO][impl->by_indices[BY_WEEK_NO]]
01725          ==ICAL_RECURRENCE_ARRAY_MAX){
01726          impl->by_indices[BY_WEEK_NO] = 0;
01727          
01728          end_of_data = 1;
01729       }
01730       
01731       t = impl->last;
01732       t.month=1; /* HACK, should be setting to the date of the first week of year*/
01733       t.day=1;
01734       
01735       week_no = impl->by_ptrs[BY_WEEK_NO][impl->by_indices[BY_WEEK_NO]];
01736       
01737       impl->last.day += week_no*7;
01738 
01739       impl->last = icaltime_normalize(impl->last);
01740       
01741   } else {
01742       /* Jump to the next week */
01743       increment_monthday(impl,7*impl->rule.interval);
01744   }
01745 
01746   if( has_by_data(impl,BY_WEEK_NO) && end_of_data){
01747       increment_year(impl,1);
01748   }
01749 
01750   return end_of_data;
01751   
01752 }
01753 
01755 static pvl_list expand_by_day(icalrecur_iterator* impl, int year)
01756 {
01757     /* Try to calculate each of the occurrences. */
01758     int i;
01759     pvl_list days_list = pvl_newlist();
01760 
01761     int start_dow, end_dow, end_year_day;
01762     struct icaltimetype tmp = impl->last;
01763     
01764     tmp.year= year;
01765     tmp.month = 1;
01766     tmp.day = 1;
01767     tmp.is_date = 1;
01768     
01769     /* Find the day that 1st Jan falls on, 1 (Sun) to 7 (Sat). */
01770     start_dow = icaltime_day_of_week(tmp);
01771     
01772     /* Get the last day of the year*/
01773     tmp.year= year;
01774     tmp.month = 12;
01775     tmp.day = 31;
01776     tmp.is_date = 1;
01777     
01778     end_dow =  icaltime_day_of_week(tmp);
01779     end_year_day = icaltime_day_of_year(tmp);
01780     
01781     for(i = 0; BYDAYPTR[i] != ICAL_RECURRENCE_ARRAY_MAX; i++){
01782         /* This is 1 (Sun) to 7 (Sat). */
01783         int dow = 
01784             icalrecurrencetype_day_day_of_week(BYDAYPTR[i]);  
01785         int pos =  icalrecurrencetype_day_position(BYDAYPTR[i]);
01786         
01787         if(pos == 0){
01788             /* The day was specified without a position -- it is just
01789                a bare day of the week ( BYDAY=SU) so add all of the
01790                days of the year with this day-of-week*/
01791             int doy, tmp_start_doy;
01792                 
01793            tmp_start_doy = ((dow + 7 - start_dow) % 7) + 1;
01794 
01795             for (doy = tmp_start_doy; doy <= end_year_day; doy += 7)
01796                     pvl_push(days_list,(void*)(int)doy);
01797             
01798         } else if ( pos > 0) {
01799             int first;
01800             /* First occurrence of dow in year */
01801             if( dow >= start_dow) {
01802                 first = dow - start_dow + 1;
01803             } else {
01804                 first = dow - start_dow + 8;
01805             }
01806 
01807             /* Then just multiple the position times 7 to get the pos'th day in the year */
01808             pvl_push(days_list,(void*)(first+  (pos-1) * 7));
01809             
01810         } else { /* pos < 0 */ 
01811             int last;
01812             pos = -pos;
01813 
01814             /* last occurrence of dow in year */
01815             if( dow <= end_dow) {
01816                 last = end_year_day - end_dow + dow;
01817             } else {
01818                 last = end_year_day - end_dow + dow - 7;
01819             }
01820 
01821             pvl_push(days_list,(void*)(last - (pos-1) * 7));
01822         }
01823     }
01824     return days_list;
01825 }
01826 
01827 
01828 /* For INTERVAL=YEARLY, set up the days[] array in the iterator to
01829    list all of the days of the current year that are specified in this
01830    rule. */
01831 
01832 static int expand_year_days(icalrecur_iterator* impl, int year)
01833 {
01834     int j,k;
01835     int days_index=0;
01836     struct icaltimetype t;
01837     int flags;
01838 
01839     t = icaltime_null_date();
01840 
01841 #define HBD(x) has_by_data(impl,x)
01842 
01843     memset(impl->days,ICAL_RECURRENCE_ARRAY_MAX_BYTE,sizeof(impl->days));
01844     
01845     /* The flags and the following switch statement select which code
01846        to use to expand the yers days, based on which BY-rules are
01847        present. */
01848 
01849     flags = (HBD(BY_DAY) ? 1<<BY_DAY : 0) + 
01850         (HBD(BY_WEEK_NO) ? 1<<BY_WEEK_NO : 0) + 
01851         (HBD(BY_MONTH_DAY) ? 1<<BY_MONTH_DAY : 0) + 
01852         (HBD(BY_MONTH) ? 1<<BY_MONTH : 0) + 
01853         (HBD(BY_YEAR_DAY) ? 1<<BY_YEAR_DAY : 0);
01854 
01855     
01856     switch(flags) {
01857         
01858     case 0: {
01859         /* FREQ=YEARLY; */
01860         t = impl->dtstart;
01861         t.year = impl->last.year;
01862         
01863         impl->days[days_index++] = (short)icaltime_day_of_year(t);
01864   
01865         break;
01866     }
01867     case 1<<BY_MONTH: {
01868         /* FREQ=YEARLY; BYMONTH=3,11*/
01869        
01870         for(j=0;impl->by_ptrs[BY_MONTH][j]!=ICAL_RECURRENCE_ARRAY_MAX;j++){
01871            int month = impl->by_ptrs[BY_MONTH][j];          
01872             int doy;
01873 
01874            t = impl->dtstart;
01875            t.year = year;
01876            t.month = month;
01877            t.is_date = 1;
01878 
01879            doy = icaltime_day_of_year(t);
01880            
01881             impl->days[days_index++] = (short)doy;
01882 
01883         }
01884         break;
01885     }
01886 
01887     case 1<<BY_MONTH_DAY:  {
01888         /* FREQ=YEARLY; BYMONTHDAY=1,15*/
01889         for(k=0;impl->by_ptrs[BY_MONTH_DAY][k]!=ICAL_RECURRENCE_ARRAY_MAX;k++)
01890             {
01891                 int month_day = impl->by_ptrs[BY_MONTH_DAY][k];
01892                 int doy;
01893 
01894                 t = impl->dtstart;
01895               t.day = month_day;
01896               t.year = year;
01897               t.is_date = 1;
01898 
01899               doy = icaltime_day_of_year(t);
01900 
01901               impl->days[days_index++] = (short)doy;
01902 
01903             }
01904         break;
01905     }
01906 
01907     case (1<<BY_MONTH_DAY) + (1<<BY_MONTH): {
01908         /* FREQ=YEARLY; BYMONTHDAY=1,15; BYMONTH=10 */
01909 
01910         for(j=0;impl->by_ptrs[BY_MONTH][j]!=ICAL_RECURRENCE_ARRAY_MAX;j++){
01911             for(k=0;impl->by_ptrs[BY_MONTH_DAY][k]!=ICAL_RECURRENCE_ARRAY_MAX;k++)
01912            {
01913                 int month = impl->by_ptrs[BY_MONTH][j];
01914                 int month_day = impl->by_ptrs[BY_MONTH_DAY][k];
01915                 int doy;
01916 
01917               t.day = month_day;
01918               t.month = month;
01919               t.year = year;
01920               t.is_date = 1;
01921 
01922               doy = icaltime_day_of_year(t);
01923 
01924               impl->days[days_index++] = (short)doy;
01925 
01926             }
01927         }
01928 
01929         break;
01930     }
01931 
01932     case 1<<BY_WEEK_NO: {
01933         /* FREQ=YEARLY; BYWEEKNO=20,50 */
01934 
01935        int dow;
01936 
01937        t.day = impl->dtstart.day;
01938        t.month = impl->dtstart.month;
01939        t.year = year;
01940        t.is_date = 1;
01941 
01942         dow = icaltime_day_of_week(t);
01943        /* HACK Not finished */ 
01944 
01945         icalerror_set_errno(ICAL_UNIMPLEMENTED_ERROR);
01946        
01947         break;
01948     }
01949 
01950     case (1<<BY_WEEK_NO) + (1<<BY_MONTH_DAY): {
01951         /*FREQ=YEARLY; WEEKNO=20,50; BYMONTH= 6,11 */
01952         icalerror_set_errno(ICAL_UNIMPLEMENTED_ERROR);
01953         break;
01954     }
01955 
01956     case 1<<BY_DAY: {
01957         /*FREQ=YEARLY; BYDAY=TH,20MO,-10FR*/
01958         pvl_elem i;
01959         pvl_list days = expand_by_day(impl,year);
01960 
01961 
01962         for(i=pvl_head(days);i!=0;i=pvl_next(i)){
01963             short day = (short)(intptr_t)pvl_data(i);
01964             impl->days[days_index++] = day;
01965         }
01966 
01967         pvl_free(days);
01968 
01969         break;
01970     }
01971 
01972     case (1<<BY_DAY)+(1<<BY_MONTH): {
01973         /*FREQ=YEARLY; BYDAY=TH,20MO,-10FR; BYMONTH = 12*/
01974 
01975 
01976         for(j=0;impl->by_ptrs[BY_MONTH][j]!=ICAL_RECURRENCE_ARRAY_MAX;j++){
01977            int month = impl->by_ptrs[BY_MONTH][j];
01978            int days_in_month = icaltime_days_in_month(month,year);
01979            int first_dow, last_dow, doy_offset;
01980             
01981            t.year = year;
01982            t.month = month;
01983            t.day = 1;
01984            t.is_date = 1;
01985               
01986            first_dow = icaltime_day_of_week(t);
01987 
01988            /* This holds the day offset used to calculate the day of the year
01989               from the month day. Just add the month day to this. */
01990            doy_offset = icaltime_day_of_year(t) - 1;
01991 
01992            t.day = days_in_month;
01993            last_dow = icaltime_day_of_week(t);
01994 
01995            for(k=0;impl->by_ptrs[BY_DAY][k]!=ICAL_RECURRENCE_ARRAY_MAX;k++){
01996                short day_coded = impl->by_ptrs[BY_DAY][k];
01997               enum icalrecurrencetype_weekday dow =
01998                 icalrecurrencetype_day_day_of_week(day_coded);
01999               int pos = icalrecurrencetype_day_position(day_coded);  
02000               int first_matching_day, last_matching_day, day, month_day;
02001 
02002               /* Calculate the first day in the month with the given weekday,
02003                  and the last day. */
02004               first_matching_day = ((dow + 7 - first_dow) % 7) + 1;
02005               last_matching_day = days_in_month - ((last_dow + 7 - dow) % 7);
02006 
02007               if (pos == 0) {
02008                   /* Add all of instances of the weekday within the month. */
02009                 for (day = first_matching_day; day <= days_in_month; day += 7)
02010                     impl->days[days_index++] = (short)(doy_offset + day);
02011 
02012               } else if (pos > 0) {
02013                   /* Add the nth instance of the weekday within the month. */
02014                   month_day = first_matching_day + (pos - 1) * 7;
02015 
02016                   if (month_day <= days_in_month)
02017                       impl->days[days_index++] = (short)(doy_offset + month_day);
02018 
02019               } else {
02020                   /* Add the -nth instance of the weekday within the month.*/
02021                   month_day = last_matching_day + (pos + 1) * 7;
02022 
02023                   if (month_day > 0)
02024                       impl->days[days_index++] = (short)(doy_offset + month_day);
02025               }
02026            }
02027         }
02028         break;
02029     }
02030 
02031     case (1<<BY_DAY) + (1<<BY_MONTH_DAY) : {
02032         /*FREQ=YEARLY; BYDAY=TH,20MO,-10FR; BYMONTHDAY=1,15*/
02033 
02034         pvl_elem itr;
02035         pvl_list days = expand_by_day(impl,year);
02036 
02037         for(itr=pvl_head(days);itr!=0;itr=pvl_next(itr)){
02038             short day = (short)(intptr_t)pvl_data(itr);
02039             struct icaltimetype tt; 
02040             
02041             tt = icaltime_from_day_of_year(day,year);
02042             
02043             for(j = 0; BYMDPTR[j]!=ICAL_RECURRENCE_ARRAY_MAX; j++){
02044                     int mday = BYMDPTR[j];
02045                     
02046                     if(tt.day == mday){
02047                         impl->days[days_index++] = day;
02048                     }
02049             }
02050             
02051         }
02052 
02053         pvl_free(days);
02054        
02055         break;
02056     }
02057 
02058     case (1<<BY_DAY) + (1<<BY_MONTH_DAY) + (1<<BY_MONTH): {
02059         /*FREQ=YEARLY; BYDAY=TH,20MO,-10FR; BYMONTHDAY=10; MYMONTH=6,11*/
02060 
02061         pvl_elem itr;
02062         pvl_list days = expand_by_day(impl,year);
02063 
02064         for(itr=pvl_head(days);itr!=0;itr=pvl_next(itr)){
02065             short day = (short)(intptr_t)pvl_data(itr);
02066             struct icaltimetype tt; 
02067             int i;
02068             
02069             tt = icaltime_from_day_of_year(day,year);
02070             
02071             for(i = 0; BYMONPTR[i] != ICAL_RECURRENCE_ARRAY_MAX; i++){
02072                 for(j = 0; BYMDPTR[j]!=ICAL_RECURRENCE_ARRAY_MAX; j++){
02073                     int mday = BYMDPTR[j];
02074                     int month = BYMONPTR[i];
02075                     
02076                     if(tt.month == month  && tt.day == mday){
02077                         impl->days[days_index++] = day;
02078                     }
02079                 }
02080             }
02081 
02082         }
02083 
02084         pvl_free(days);
02085 
02086        break;
02087 
02088     }
02089 
02090     case (1<<BY_DAY) + (1<<BY_WEEK_NO) : {
02091         /*FREQ=YEARLY; BYDAY=TH,20MO,-10FR;  WEEKNO=20,50*/
02092        
02093         pvl_elem itr;
02094         pvl_list days = expand_by_day(impl,year);
02095 
02096         for(itr=pvl_head(days);itr!=0;itr=pvl_next(itr)){
02097             short day = (short)(intptr_t)pvl_data(itr);
02098             struct icaltimetype tt; 
02099             int i;
02100             
02101             tt = icaltime_from_day_of_year(day,year);
02102             
02103             for(i = 0; BYWEEKPTR[i] != ICAL_RECURRENCE_ARRAY_MAX; i++){
02104                     int weekno = BYWEEKPTR[i];
02105                     int this_weekno = icaltime_week_number(tt);
02106                     if(weekno== this_weekno){
02107                         impl->days[days_index++] = day;
02108                     }
02109             }
02110                     
02111         }
02112 
02113         pvl_free(days);
02114         break;
02115     }
02116 
02117     case (1<<BY_DAY) + (1<<BY_WEEK_NO) + (1<<BY_MONTH_DAY): {
02118         /*FREQ=YEARLY; BYDAY=TH,20MO,-10FR;  WEEKNO=20,50; BYMONTHDAY=1,15*/
02119         icalerror_set_errno(ICAL_UNIMPLEMENTED_ERROR);
02120         break;
02121     }
02122 
02123     case 1<<BY_YEAR_DAY: {
02124        for(j=0;impl->by_ptrs[BY_YEAR_DAY][j]!=ICAL_RECURRENCE_ARRAY_MAX;j++){
02125            impl->days[days_index++] = impl->by_ptrs[BY_YEAR_DAY][j];
02126         }
02127         break;
02128     }
02129 
02130     default: {
02131         icalerror_set_errno(ICAL_UNIMPLEMENTED_ERROR);
02132         break;
02133     }
02134 
02135     }
02136 
02137     return 0;
02138 }                                  
02139 
02140 
02141 static int next_year(icalrecur_iterator* impl)
02142 {
02143     struct icaltimetype next;
02144 
02145     if (next_hour(impl) == 0){
02146        return 0;
02147     }
02148 
02149     if (impl->days[++impl->days_index] == ICAL_RECURRENCE_ARRAY_MAX){
02150        impl->days_index = 0;
02151 
02152        for (;;) {
02153        increment_year(impl,impl->rule.interval);
02154         expand_year_days(impl,impl->last.year);
02155          if (impl->days[0] != ICAL_RECURRENCE_ARRAY_MAX)
02156            break;
02157     }
02158     }
02159 
02160     next = icaltime_from_day_of_year(impl->days[impl->days_index], impl->last.year);
02161     
02162     impl->last.day =  next.day;
02163     impl->last.month =  next.month;
02164   
02165     return 1;
02166 }
02167 
02168 int icalrecur_check_rulepart(icalrecur_iterator* impl,
02169                     int v, enum byrule byrule)
02170 {
02171     int itr;
02172 
02173     if(impl->by_ptrs[byrule][0]!=ICAL_RECURRENCE_ARRAY_MAX){
02174        for(itr=0; impl->by_ptrs[byrule][itr]!=ICAL_RECURRENCE_ARRAY_MAX;itr++){
02175            if(impl->by_ptrs[byrule][itr] == v){
02176               return 1;
02177            }
02178        }
02179     } 
02180 
02181     return 0;
02182 }
02183 
02184 static int check_contract_restriction(icalrecur_iterator* impl,
02185                     enum byrule byrule, int v)
02186 {
02187     int pass = 0;
02188     int itr;
02189     icalrecurrencetype_frequency freq = impl->rule.freq;
02190 
02191     if(impl->by_ptrs[byrule][0]!=ICAL_RECURRENCE_ARRAY_MAX &&
02192        expand_map[freq].map[byrule] == CONTRACT){
02193        for(itr=0; impl->by_ptrs[byrule][itr]!=ICAL_RECURRENCE_ARRAY_MAX;itr++){
02194            if(impl->by_ptrs[byrule][itr] == v){
02195               pass=1;
02196               break;
02197            }
02198        }
02199 
02200        return pass;
02201     } else {
02202        /* This is not a contracting byrule, or it has no data, so the
02203            test passes*/
02204        return 1;
02205     }
02206 }
02207 
02208 
02209 static int check_contracting_rules(icalrecur_iterator* impl)
02210 {
02211 
02212     int day_of_week = icaltime_day_of_week(impl->last);
02213     int week_no = icaltime_week_number(impl->last);
02214     int year_day = icaltime_day_of_year(impl->last);
02215 
02216     if (
02217        check_contract_restriction(impl,BY_SECOND, impl->last.second) &&
02218        check_contract_restriction(impl,BY_MINUTE, impl->last.minute) &&
02219        check_contract_restriction(impl,BY_HOUR, impl->last.hour) &&
02220        check_contract_restriction(impl,BY_DAY, day_of_week) &&
02221        check_contract_restriction(impl,BY_WEEK_NO, week_no) &&
02222        check_contract_restriction(impl,BY_MONTH_DAY, impl->last.day) &&
02223        check_contract_restriction(impl,BY_MONTH, impl->last.month) &&
02224        check_contract_restriction(impl,BY_YEAR_DAY, year_day) )
02225     {
02226 
02227        return 1;
02228     } else {
02229        return 0;
02230     }
02231 }
02232 
02233 struct icaltimetype icalrecur_iterator_next(icalrecur_iterator *impl)
02234 {
02235     int valid = 1;
02236     
02237     if( (impl->rule.count!=0 &&impl->occurrence_no >= impl->rule.count) ||
02238        (!icaltime_is_null_time(impl->rule.until) && 
02239        icaltime_compare(impl->last,impl->rule.until) > 0)) {
02240        return icaltime_null_time();
02241     }
02242 
02243     if(impl->occurrence_no == 0 
02244        &&  icaltime_compare(impl->last,impl->dtstart) >= 0){
02245 
02246        impl->occurrence_no++;
02247        return impl->last;
02248     }
02249 
02250     do {
02251         valid = 1;
02252        switch(impl->rule.freq){
02253            
02254            case ICAL_SECONDLY_RECURRENCE: {
02255               next_second(impl);
02256               break;
02257            }
02258            case ICAL_MINUTELY_RECURRENCE: {
02259               next_minute(impl);
02260               break;
02261            }
02262            case ICAL_HOURLY_RECURRENCE: {
02263               next_hour(impl);
02264               break;
02265            }
02266            case ICAL_DAILY_RECURRENCE: {
02267               next_day(impl);
02268               break;
02269            }
02270            case ICAL_WEEKLY_RECURRENCE: {
02271               next_week(impl);
02272               break;
02273            }
02274            case ICAL_MONTHLY_RECURRENCE: {
02275               valid = next_month(impl);
02276               break;
02277            }
02278            case ICAL_YEARLY_RECURRENCE:{
02279               next_year(impl);
02280               break;
02281            }
02282            default:{
02283               icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
02284                 return icaltime_null_time();
02285            }
02286        }    
02287        
02288     } while(!check_contracting_rules(impl) 
02289            || icaltime_compare(impl->last,impl->dtstart) < 0
02290             || valid == 0);
02291     
02292     
02293 /* Ignore null times and times that are after the until time */
02294     if( !icaltime_is_null_time(impl->rule.until) && 
02295        icaltime_compare(impl->last,impl->rule.until) > 0 ) {
02296        return icaltime_null_time();
02297     }
02298 
02299     impl->occurrence_no++;
02300 
02301     return impl->last;
02302 }
02303 
02304 
02305 /************************** Type Routines **********************/
02306 
02307 
02308 void icalrecurrencetype_clear(struct icalrecurrencetype *recur)
02309 {
02310     memset(recur,ICAL_RECURRENCE_ARRAY_MAX_BYTE,
02311           sizeof(struct icalrecurrencetype));
02312 
02313     recur->week_start = ICAL_MONDAY_WEEKDAY;
02314     recur->freq = ICAL_NO_RECURRENCE;
02315     recur->interval = 1;
02316     memset(&(recur->until),0,sizeof(struct icaltimetype));
02317     recur->count = 0;
02318 }
02319 
02331 enum icalrecurrencetype_weekday icalrecurrencetype_day_day_of_week(short day)
02332 {
02333     return abs(day)%8;
02334 }
02335 
02336 int icalrecurrencetype_day_position(short day)
02337 {
02338     int wd, pos;
02339 
02340     wd = icalrecurrencetype_day_day_of_week(day);
02341 
02342     pos = (abs(day)-wd)/8 * ((day<0)?-1:1);
02343 
02344 
02345     return pos;
02346 }
02347 
02348 
02349 /****************** Enumeration Routines ******************/
02350 
02351 static struct {icalrecurrencetype_weekday wd; const char * str; } 
02352 wd_map[] = {
02353     {ICAL_SUNDAY_WEEKDAY,"SU"},
02354     {ICAL_MONDAY_WEEKDAY,"MO"},
02355     {ICAL_TUESDAY_WEEKDAY,"TU"},
02356     {ICAL_WEDNESDAY_WEEKDAY,"WE"},
02357     {ICAL_THURSDAY_WEEKDAY,"TH"},
02358     {ICAL_FRIDAY_WEEKDAY,"FR"},
02359     {ICAL_SATURDAY_WEEKDAY,"SA"},
02360     {ICAL_NO_WEEKDAY,0}
02361 };
02362 
02363 const char* icalrecur_weekday_to_string(icalrecurrencetype_weekday kind)
02364 {
02365     int i;
02366 
02367     for (i=0; wd_map[i].wd  != ICAL_NO_WEEKDAY; i++) {
02368        if ( wd_map[i].wd ==  kind) {
02369            return wd_map[i].str;
02370        }
02371     }
02372 
02373     return 0;
02374 }
02375 
02376 icalrecurrencetype_weekday icalrecur_string_to_weekday(const char* str)
02377 {
02378     int i;
02379 
02380     for (i=0; wd_map[i].wd  != ICAL_NO_WEEKDAY; i++) {
02381        if ( strcmp(str,wd_map[i].str) == 0){
02382            return wd_map[i].wd;
02383        }
02384     }
02385 
02386     return ICAL_NO_WEEKDAY;
02387 }
02388 
02389 
02390 
02391 static struct {
02392        icalrecurrencetype_frequency kind;
02393        const char* str;
02394 } freq_map[] = {
02395     {ICAL_SECONDLY_RECURRENCE,"SECONDLY"},
02396     {ICAL_MINUTELY_RECURRENCE,"MINUTELY"},
02397     {ICAL_HOURLY_RECURRENCE,"HOURLY"},
02398     {ICAL_DAILY_RECURRENCE,"DAILY"},
02399     {ICAL_WEEKLY_RECURRENCE,"WEEKLY"},
02400     {ICAL_MONTHLY_RECURRENCE,"MONTHLY"},
02401     {ICAL_YEARLY_RECURRENCE,"YEARLY"},
02402     {ICAL_NO_RECURRENCE,0}
02403 };
02404 
02405 const char* icalrecur_freq_to_string(icalrecurrencetype_frequency kind)
02406 {
02407     int i;
02408 
02409     for (i=0; freq_map[i].kind != ICAL_NO_RECURRENCE ; i++) {
02410        if ( freq_map[i].kind == kind ) {
02411            return freq_map[i].str;
02412        }
02413     }
02414     return 0;
02415 }
02416 
02417 icalrecurrencetype_frequency icalrecur_string_to_freq(const char* str)
02418 {
02419     int i;
02420 
02421     for (i=0; freq_map[i].kind != ICAL_NO_RECURRENCE ; i++) {
02422        if ( strcmp(str,freq_map[i].str) == 0){
02423            return freq_map[i].kind;
02424        }
02425     }
02426     return ICAL_NO_RECURRENCE;
02427 }
02428 
02435 int icalrecur_expand_recurrence(char* rule, time_t start,
02436                             int count, time_t* array)
02437 {
02438     struct icalrecurrencetype recur;
02439     icalrecur_iterator* ritr;
02440     time_t tt;
02441     struct icaltimetype icstart, next;
02442     int i = 0;
02443 
02444     memset(array, 0, count*sizeof(time_t));
02445 
02446     icstart = icaltime_from_timet_with_zone(start,0,0);
02447 
02448     recur = icalrecurrencetype_from_string(rule);
02449 
02450     for(ritr = icalrecur_iterator_new(recur,icstart),
02451        next = icalrecur_iterator_next(ritr);
02452        !icaltime_is_null_time(next) && i < count;
02453        next = icalrecur_iterator_next(ritr)){
02454 
02455        tt = icaltime_as_timet(next);
02456        
02457        if (tt >= start ){
02458            array[i++] = tt;
02459        }
02460 
02461     }
02462 
02463     icalrecur_iterator_free(ritr);
02464 
02465     return 1;
02466 }