summaryrefslogtreecommitdiffabout
path: root/libical/src/libical/icalrecur.c
Unidiff
Diffstat (limited to 'libical/src/libical/icalrecur.c') (more/less context) (ignore whitespace changes)
-rw-r--r--libical/src/libical/icalrecur.c2315
1 files changed, 2315 insertions, 0 deletions
diff --git a/libical/src/libical/icalrecur.c b/libical/src/libical/icalrecur.c
new file mode 100644
index 0000000..203ce70
--- a/dev/null
+++ b/libical/src/libical/icalrecur.c
@@ -0,0 +1,2315 @@
1/* -*- Mode: C -*-
2 ======================================================================
3 FILE: icalrecur.c
4 CREATOR: eric 16 May 2000
5
6 $Id$
7 $Locker$
8
9
10 (C) COPYRIGHT 2000, Eric Busboom, http://www.softwarestudio.org
11
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of either:
14
15 The LGPL as published by the Free Software Foundation, version
16 2.1, available at: http://www.fsf.org/copyleft/lesser.html
17
18 Or:
19
20 The Mozilla Public License Version 1.0. You may obtain a copy of
21 the License at http://www.mozilla.org/MPL/
22
23
24 How this code works:
25
26 Processing starts when the caller generates a new recurrence
27 iterator via icalrecur_iterator_new(). This routine copies the
28 recurrence rule into the iterator and extracts things like start and
29 end dates. Then, it checks if the rule is legal, using some logic
30 from RFC2445 and some logic that probably should be in RFC2445.
31
32 Then, icalrecur_iterator_new() re-writes some of the BY*
33 arrays. This involves ( via a call to setup_defaults() ) :
34
35 1) For BY rule parts with no data ( ie BYSECOND was not specified )
36 copy the corresponding time part from DTSTART into the BY array. (
37 So impl->by_ptrs[BY_SECOND] will then have one element if is
38 originally had none ) This only happens if the BY* rule part data
39 would expand the number of occurrences in the occurrence set. This
40 lets the code ignore DTSTART later on and still use it to get the
41 time parts that were not specified in any other way.
42
43 2) For the by rule part that are not the same interval as the
44 frequency -- for HOURLY anything but BYHOUR, for instance -- copy the
45 first data element from the rule part into the first occurrence. For
46 example, for "INTERVAL=MONTHLY and BYHOUR=10,30", initialize the
47 first time to be returned to have an hour of 10.
48
49 Finally, for INTERVAL=YEARLY, the routine expands the rule to get
50 all of the days specified in the rule. The code will do this for
51 each new year, and this is the first expansion. This is a special
52 case for the yearly interval; no other frequency gets expanded this
53 way. The yearly interval is the most complex, so some special
54 processing is required.
55
56 After creating a new iterator, the caller will make successive calls
57 to icalrecur_iterator_next() to get the next time specified by the
58 rule. The main part of this routine is a switch on the frequency of
59 the rule. Each different frequency is handled by a different
60 routine.
61
62 For example, next_hour handles the case of INTERVAL=HOURLY, and it
63 is called by other routines to get the next hour. First, the routine
64 tries to get the next minute part of a time with a call to
65 next_minute(). If next_minute() returns 1, it has reached the end of
66 its data, usually the last element of the BYMINUTE array. Then, if
67 there is data in the BYHOUR array, the routine changes the hour to
68 the next one in the array. If INTERVAL=HOURLY, the routine advances
69 the hour by the interval.
70
71 If the routine used the last hour in the BYHOUR array, and the
72 INTERVAL=HOURLY, then the routine calls increment_monthday() to set
73 the next month day. The increment_* routines may call higher routine
74 to increment the month or year also.
75
76 The code for INTERVAL=DAILY is handled by next_day(). First, the
77 routine tries to get the next hour part of a time with a call to
78 next_hour. If next_hour() returns 1, it has reached the end of its
79 data, usually the last element of the BYHOUR array. This means that
80 next_day() should increment the time to the next day. If FREQUENCY==DAILY,
81 the routine increments the day by the interval; otherwise, it
82 increments the day by 1.
83
84 Next_day() differs from next_hour because it does not use the BYDAY
85 array to select an appropriate day. Instead, it returns every day (
86 incrementing by 1 if the frequency is not DAILY with INTERVAL!=1)
87 Any days that are not specified in an non-empty BYDAY array are
88 filtered out later.
89
90 Generally, the flow of these routine is for a next_* call a next_*
91 routine of a lower interval ( next_day calls next_hour) and then to
92 possibly call an increment_* routine of an equal or higher
93 interval. ( next_day calls increment_monthday() )
94
95 When the call to the original next_* routine returns,
96 icalrecur_iterator_next() will check the returned data against other
97 BYrule parts to determine if is should be excluded by calling
98 check_contracting_rules. Generally, a contracting rule is any with a
99 larger time span than the interval. For instance, if
100 INTERVAL=DAILY, BYMONTH is a contracting rule part.
101
102 Check_contracting_rules() uses icalrecur_check_rulepart() to do its
103 work. icalrecur_check_rulepart() uses expand_map[] to determine if a rule
104 is contracting, and if it is, and if the BY rule part has some data,
105 then the routine checks if the value of a component of the time is
106 part of the byrule part. For instance, for "INTERVAL=DAILY;
107 BYMONTH=6,10", icalrecur_check_rulepart() would check that the time value
108 given to it has a month of either 6 or 10.
109
110 Finally, icalrecur_iterator_next() does a few other checks on the
111 time value, and if it passes, it returns the time.
112
113 A note about the end_of_data flag. The flag indicates that the
114 routine is at the end of its data -- the last BY rule if the routine
115 is using by rules, or the last day of the week/month/year/etc if
116 not.
117
118 This flag is usually set early in a next_* routine and returned in
119 the end. The way it is used allows the next_* routine to set the
120 last time back to the first element in a BYxx rule, and then signal
121 to the higer level routine to increment the next higher level. For
122 instance. WITH FREQ=MONTHLY;BYDAY=TU,FR, After next_weekday_by_month
123 runs though both TU and FR, it sets the week day back to TU and sets
124 end_of_data to 1x. This signals next_month to increment the month.
125
126
127 ======================================================================*/
128
129#ifdef HAVE_CONFIG_H
130#include "config.h"
131#endif
132
133#include "icalrecur.h"
134
135#ifdef ICAL_NO_LIBICAL
136#define icalerror_set_errno(x)
137#define icalerror_check_arg_rv(x,y)
138#else
139#include "icalerror.h"
140#include "icalmemory.h"
141#endif
142
143#include <stdlib.h> /* for malloc */
144#include <errno.h> /* for errno */
145#include <string.h> /* for strdup and strchr*/
146#include <assert.h>
147#include <stddef.h> /* For offsetof() macro */
148
149#include "pvl.h"
150
151#define TEMP_MAX 1024
152
153
154#define BYDAYIDX impl->by_indices[BY_DAY]
155#define BYDAYPTR impl->by_ptrs[BY_DAY]
156
157#define BYMONIDX impl->by_indices[BY_MONTH]
158#define BYMONPTR impl->by_ptrs[BY_MONTH]
159
160#define BYMDIDX impl->by_indices[BY_MONTH_DAY]
161#define BYMDPTR impl->by_ptrs[BY_MONTH_DAY]
162
163#define BYWEEKIDX impl->by_indices[BY_WEEK_NO]
164#define BYWEEKPTR impl->by_ptrs[BY_WEEK_NO]
165
166const char* icalrecur_freq_to_string(icalrecurrencetype_frequency kind);
167icalrecurrencetype_frequency icalrecur_string_to_freq(const char* str);
168
169const char* icalrecur_weekday_to_string(icalrecurrencetype_weekday kind);
170icalrecurrencetype_weekday icalrecur_string_to_weekday(const char* str);
171
172
173
174/*********************** Rule parsing routines ************************/
175
176struct icalrecur_parser {
177 const char* rule;
178 char* copy;
179 char* this_clause;
180 char* next_clause;
181
182 struct icalrecurrencetype rt;
183};
184
185const char* icalrecur_first_clause(struct icalrecur_parser *parser)
186{
187 char *idx;
188 parser->this_clause = parser->copy;
189
190 idx = strchr(parser->this_clause,';');
191
192 if (idx == 0){
193 parser->next_clause = 0;
194 return 0;
195 }
196
197 *idx = 0;
198 idx++;
199 parser->next_clause = idx;
200
201 return parser->this_clause;
202
203}
204
205const char* icalrecur_next_clause(struct icalrecur_parser *parser)
206{
207 char* idx;
208
209 parser->this_clause = parser->next_clause;
210
211 if(parser->this_clause == 0){
212 return 0;
213 }
214
215 idx = strchr(parser->this_clause,';');
216
217 if (idx == 0){
218 parser->next_clause = 0;
219 } else {
220
221 *idx = 0;
222 idx++;
223 parser->next_clause = idx;
224 }
225
226 return parser->this_clause;
227
228}
229
230void icalrecur_clause_name_and_value(struct icalrecur_parser *parser,
231 char** name, char** value)
232{
233 char *idx;
234
235 *name = parser->this_clause;
236
237 idx = strchr(parser->this_clause,'=');
238
239 if (idx == 0){
240 *name = 0;
241 *value = 0;
242 return;
243 }
244
245 *idx = 0;
246 idx++;
247 *value = idx;
248}
249
250void icalrecur_add_byrules(struct icalrecur_parser *parser, short *array,
251 int size, char* vals)
252{
253 char *t, *n;
254 int i=0;
255 int sign = 1;
256 short v;
257
258 n = vals;
259
260 while(n != 0){
261
262 if(i == size){
263 return;
264 }
265
266 t = n;
267
268 n = strchr(t,',');
269
270 if(n != 0){
271 *n = 0;
272 n++;
273 }
274
275 /* Get optional sign. HACK. sign is not allowed for all BYxxx
276 rule parts */
277 if( *t == '-'){
278 sign = -1;
279 t++;
280 } else if (*t == '+'){
281 sign = 1;
282 t++;
283 }
284
285 v = atoi(t) * sign ;
286
287
288 array[i++] = v;
289 array[i] = ICAL_RECURRENCE_ARRAY_MAX;
290
291 }
292
293}
294
295void icalrecur_add_bydayrules(struct icalrecur_parser *parser, const char* vals)
296{
297
298 char *t, *n;
299 int i=0;
300 int sign = 1;
301 int weekno = 0;
302 icalrecurrencetype_weekday wd;
303 short *array = parser->rt.by_day;
304 char* end;
305 char* vals_copy;
306
307 vals_copy = icalmemory_strdup(vals);
308
309 end = (char*)vals_copy+strlen(vals_copy);
310 n = vals_copy;
311
312 while(n != 0){
313
314
315 t = n;
316
317 n = strchr(t,',');
318
319 if(n != 0){
320 *n = 0;
321 n++;
322 }
323
324 /* Get optional sign. */
325 if( *t == '-'){
326 sign = -1;
327 t++;
328 } else if (*t == '+'){
329 sign = 1;
330 t++;
331 } else {
332 sign = 1;
333 }
334
335 weekno = 0;
336 /* Get Optional weekno */
337 if( sscanf(t,"%d",&weekno) != 0){
338 if (n != 0){
339 int weeknolen = (n-t)-3; /* 3 -> one for \0, 2 for day name */
340 /* could use abs(log10(weekno))+1, but that needs libm */
341 t += weeknolen;
342 } else {
343 t = end -2;
344 }
345 }
346
347 wd = icalrecur_string_to_weekday(t);
348
349 array[i++] = sign* ((int)wd + 8*weekno);
350 array[i] = ICAL_RECURRENCE_ARRAY_MAX;
351
352 }
353
354 free(vals_copy);
355
356}
357
358
359struct icalrecurrencetype icalrecurrencetype_from_string(const char* str)
360{
361 struct icalrecur_parser parser;
362
363 memset(&parser,0,sizeof(parser));
364 icalrecurrencetype_clear(&parser.rt);
365
366 icalerror_check_arg_re(str!=0,"str",parser.rt);
367
368
369 /* Set up the parser struct */
370 parser.rule = str;
371 parser.copy = icalmemory_strdup(parser.rule);
372 parser.this_clause = parser.copy;
373
374 if(parser.copy == 0){
375 icalerror_set_errno(ICAL_NEWFAILED_ERROR);
376 return parser.rt;
377 }
378
379 /* Loop through all of the clauses */
380 for(icalrecur_first_clause(&parser);
381 parser.this_clause != 0;
382 icalrecur_next_clause(&parser))
383 {
384 char *name, *value;
385 icalrecur_clause_name_and_value(&parser,&name,&value);
386
387 if(name == 0){
388 icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
389 icalrecurrencetype_clear(&parser.rt);
390 return parser.rt;
391 }
392
393 if (strcmp(name,"FREQ") == 0){
394 parser.rt.freq = icalrecur_string_to_freq(value);
395 } else if (strcmp(name,"COUNT") == 0){
396 parser.rt.count = atoi(value);
397 } else if (strcmp(name,"UNTIL") == 0){
398 parser.rt.until = icaltime_from_string(value);
399 } else if (strcmp(name,"INTERVAL") == 0){
400 parser.rt.interval = atoi(value);
401 } else if (strcmp(name,"WKST") == 0){
402 parser.rt.week_start = icalrecur_string_to_weekday(value);
403 } else if (strcmp(name,"BYSECOND") == 0){
404 icalrecur_add_byrules(&parser,parser.rt.by_second,
405 ICAL_BY_SECOND_SIZE,value);
406 } else if (strcmp(name,"BYMINUTE") == 0){
407 icalrecur_add_byrules(&parser,parser.rt.by_minute,
408 ICAL_BY_MINUTE_SIZE,value);
409 } else if (strcmp(name,"BYHOUR") == 0){
410 icalrecur_add_byrules(&parser,parser.rt.by_hour,
411 ICAL_BY_HOUR_SIZE,value);
412 } else if (strcmp(name,"BYDAY") == 0){
413 icalrecur_add_bydayrules(&parser,value);
414 } else if (strcmp(name,"BYMONTHDAY") == 0){
415 icalrecur_add_byrules(&parser,parser.rt.by_month_day,
416 ICAL_BY_MONTHDAY_SIZE,value);
417 } else if (strcmp(name,"BYYEARDAY") == 0){
418 icalrecur_add_byrules(&parser,parser.rt.by_year_day,
419 ICAL_BY_YEARDAY_SIZE,value);
420 } else if (strcmp(name,"BYWEEKNO") == 0){
421 icalrecur_add_byrules(&parser,parser.rt.by_week_no,
422 ICAL_BY_WEEKNO_SIZE,value);
423 } else if (strcmp(name,"BYMONTH") == 0){
424 icalrecur_add_byrules(&parser,parser.rt.by_month,
425 ICAL_BY_MONTH_SIZE,value);
426 } else if (strcmp(name,"BYSETPOS") == 0){
427 icalrecur_add_byrules(&parser,parser.rt.by_set_pos,
428 ICAL_BY_SETPOS_SIZE,value);
429 } else {
430 icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
431 icalrecurrencetype_clear(&parser.rt);
432 return parser.rt;
433 }
434
435 }
436
437 free(parser.copy);
438
439 return parser.rt;
440
441}
442
443#ifndef ICAL_NO_LIBICAL
444
445struct { char* str;size_t offset; short limit; } recurmap[] =
446{
447 {";BYSECOND=",offsetof(struct icalrecurrencetype,by_second),60},
448 {";BYMINUTE=",offsetof(struct icalrecurrencetype,by_minute),60},
449 {";BYHOUR=",offsetof(struct icalrecurrencetype,by_hour),24},
450 {";BYDAY=",offsetof(struct icalrecurrencetype,by_day),7},
451 {";BYMONTHDAY=",offsetof(struct icalrecurrencetype,by_month_day),31},
452 {";BYYEARDAY=",offsetof(struct icalrecurrencetype,by_year_day),366},
453 {";BYWEEKNO=",offsetof(struct icalrecurrencetype,by_week_no),52},
454 {";BYMONTH=",offsetof(struct icalrecurrencetype,by_month),12},
455 {";BYSETPOS=",offsetof(struct icalrecurrencetype,by_set_pos),366},
456 {0,0,0},
457};
458
459/* A private routine in icalvalue.c */
460void print_datetime_to_string(char* str, struct icaltimetype *data);
461
462char* icalrecurrencetype_as_string(struct icalrecurrencetype *recur)
463{
464 char* str;
465 char *str_p;
466 size_t buf_sz = 200;
467 char temp[20];
468 int i,j;
469
470 if(recur->freq == ICAL_NO_RECURRENCE){
471 return 0;
472 }
473
474 str = (char*)icalmemory_tmp_buffer(buf_sz);
475 str_p = str;
476
477 icalmemory_append_string(&str,&str_p,&buf_sz,"FREQ=");
478 icalmemory_append_string(&str,&str_p,&buf_sz,
479 icalrecur_freq_to_string(recur->freq));
480
481 if(recur->until.year != 0){
482
483 temp[0] = 0;
484 print_datetime_to_string(temp,&(recur->until));
485
486 icalmemory_append_string(&str,&str_p,&buf_sz,";UNTIL=");
487 icalmemory_append_string(&str,&str_p,&buf_sz, temp);
488 }
489
490 if(recur->count != 0){
491 sprintf(temp,"%d",recur->count);
492 icalmemory_append_string(&str,&str_p,&buf_sz,";COUNT=");
493 icalmemory_append_string(&str,&str_p,&buf_sz, temp);
494 }
495
496 if(recur->interval != 0){
497 sprintf(temp,"%d",recur->interval);
498 icalmemory_append_string(&str,&str_p,&buf_sz,";INTERVAL=");
499 icalmemory_append_string(&str,&str_p,&buf_sz, temp);
500 }
501
502 for(j =0; recurmap[j].str != 0; j++){
503 short* array = (short*)(recurmap[j].offset+ (size_t)recur);
504 short limit = recurmap[j].limit;
505
506 /* Skip unused arrays */
507 if( array[0] != ICAL_RECURRENCE_ARRAY_MAX ) {
508
509 icalmemory_append_string(&str,&str_p,&buf_sz,recurmap[j].str);
510
511 for(i=0;
512 i< limit && array[i] != ICAL_RECURRENCE_ARRAY_MAX;
513 i++){
514 if (j == 3) { /* BYDAY */
515 short dow = icalrecurrencetype_day_day_of_week(array[i]);
516 const char *daystr = icalrecur_weekday_to_string(dow);
517 short pos;
518
519 pos = icalrecurrencetype_day_position(array[i]);
520
521 if (pos == 0)
522 icalmemory_append_string(&str,&str_p,&buf_sz,daystr);
523 else {
524 sprintf(temp,"%d%s",pos,daystr);
525 icalmemory_append_string(&str,&str_p,&buf_sz,temp);
526 }
527
528 } else {
529 sprintf(temp,"%d",array[i]);
530 icalmemory_append_string(&str,&str_p,&buf_sz, temp);
531 }
532
533 if( (i+1)<limit &&array[i+1]
534 != ICAL_RECURRENCE_ARRAY_MAX){
535 icalmemory_append_char(&str,&str_p,&buf_sz,',');
536 }
537 }
538 }
539 }
540
541 return str;
542}
543#endif
544
545
546
547/************************* occurrence iteration routiens ******************/
548
549enum byrule {
550 NO_CONTRACTION = -1,
551 BY_SECOND = 0,
552 BY_MINUTE = 1,
553 BY_HOUR = 2,
554 BY_DAY = 3,
555 BY_MONTH_DAY = 4,
556 BY_YEAR_DAY = 5,
557 BY_WEEK_NO = 6,
558 BY_MONTH = 7,
559 BY_SET_POS
560};
561
562
563
564struct icalrecur_iterator_impl {
565
566 struct icaltimetype dtstart; /* Hack. Make into time_t */
567 struct icaltimetype last; /* last time return from _iterator_next*/
568 int occurrence_no; /* number of step made on t iterator */
569 struct icalrecurrencetype rule;
570
571 short days[366];
572 short days_index;
573
574 enum byrule byrule;
575 short by_indices[9];
576 short orig_data[9]; /* 1 if there was data in the byrule */
577
578
579 short *by_ptrs[9]; /* Pointers into the by_* array elements of the rule */
580
581};
582
583int icalrecur_iterator_sizeof_byarray(short* byarray)
584{
585 int array_itr;
586
587 for(array_itr = 0;
588 byarray[array_itr] != ICAL_RECURRENCE_ARRAY_MAX;
589 array_itr++){
590 }
591
592 return array_itr;
593}
594
595enum expand_table {
596 UNKNOWN = 0,
597 CONTRACT = 1,
598 EXPAND =2,
599 ILLEGAL=3
600};
601
602/* The split map indicates, for a particular interval, wether a BY_*
603 rule part expands the number of instances in the occcurrence set or
604 contracts it. 1=> contract, 2=>expand, and 3 means the pairing is
605 not allowed. */
606struct expand_split_map_struct
607{
608 icalrecurrencetype_frequency frequency;
609
610 /* Elements of the 'map' array correspond to the BYxxx rules:
611 Second,Minute,Hour,Day,Month Day,Year Day,Week No,Month*/
612
613 short map[8];
614};
615
616struct expand_split_map_struct expand_map[] =
617{
618 {ICAL_SECONDLY_RECURRENCE,{1,1,1,1,1,1,1,1}},
619 {ICAL_MINUTELY_RECURRENCE,{2,1,1,1,1,1,1,1}},
620 {ICAL_HOURLY_RECURRENCE, {2,2,1,1,1,1,1,1}},
621 {ICAL_DAILY_RECURRENCE, {2,2,2,1,1,1,1,1}},
622 {ICAL_WEEKLY_RECURRENCE, {2,2,2,2,3,3,1,1}},
623 {ICAL_MONTHLY_RECURRENCE, {2,2,2,2,2,3,3,1}},
624 {ICAL_YEARLY_RECURRENCE, {2,2,2,2,2,2,2,2}},
625 {ICAL_NO_RECURRENCE, {0,0,0,0,0,0,0,0}}
626
627};
628
629
630
631/* Check that the rule has only the two given interday byrule parts. */
632int icalrecur_two_byrule(struct icalrecur_iterator_impl* impl,
633 enum byrule one,enum byrule two)
634{
635 short test_array[9];
636 enum byrule itr;
637 int passes = 0;
638
639 memset(test_array,0,9);
640
641 test_array[one] = 1;
642 test_array[two] = 1;
643
644 for(itr = BY_DAY; itr != BY_SET_POS; itr++){
645
646 if( (test_array[itr] == 0 &&
647 impl->by_ptrs[itr][0] != ICAL_RECURRENCE_ARRAY_MAX
648 ) ||
649 (test_array[itr] == 1 &&
650 impl->by_ptrs[itr][0] == ICAL_RECURRENCE_ARRAY_MAX
651 )
652 ) {
653 /* test failed */
654 passes = 0;
655 }
656 }
657
658 return passes;
659
660}
661
662/* Check that the rule has only the one given interdat byrule parts. */
663int icalrecur_one_byrule(struct icalrecur_iterator_impl* impl,enum byrule one)
664{
665 int passes = 1;
666 enum byrule itr;
667
668 for(itr = BY_DAY; itr != BY_SET_POS; itr++){
669
670 if ((itr==one && impl->by_ptrs[itr][0] == ICAL_RECURRENCE_ARRAY_MAX) ||
671 (itr!=one && impl->by_ptrs[itr][0] != ICAL_RECURRENCE_ARRAY_MAX)) {
672 passes = 0;
673 }
674 }
675
676 return passes;
677}
678
679int count_byrules(struct icalrecur_iterator_impl* impl)
680{
681 int count = 0;
682 enum byrule itr;
683
684 for(itr = BY_DAY; itr <= BY_SET_POS; itr++){
685 if(impl->by_ptrs[itr][0] != ICAL_RECURRENCE_ARRAY_MAX){
686 count++;
687 }
688 }
689
690 return count;
691}
692
693
694void setup_defaults(struct icalrecur_iterator_impl* impl,
695 enum byrule byrule, icalrecurrencetype_frequency req,
696 short deftime, int *timepart)
697{
698
699 icalrecurrencetype_frequency freq;
700 freq = impl->rule.freq;
701
702 /* Re-write the BY rule arrays with data from the DTSTART time so
703 we don't have to explicitly deal with DTSTART */
704
705 if(impl->by_ptrs[byrule][0] == ICAL_RECURRENCE_ARRAY_MAX &&
706 expand_map[freq].map[byrule] != CONTRACT){
707 impl->by_ptrs[byrule][0] = deftime;
708 }
709
710 /* Initialize the first occurence */
711 if( freq != req && expand_map[freq].map[byrule] != CONTRACT){
712 *timepart = impl->by_ptrs[byrule][0];
713 }
714
715
716}
717
718int has_by_data(struct icalrecur_iterator_impl* impl, enum byrule byrule){
719
720 return (impl->orig_data[byrule] == 1);
721}
722
723
724int expand_year_days(struct icalrecur_iterator_impl* impl,short year);
725
726
727icalrecur_iterator* icalrecur_iterator_new(struct icalrecurrencetype rule,
728 struct icaltimetype dtstart)
729{
730 struct icalrecur_iterator_impl* impl;
731 icalrecurrencetype_frequency freq;
732
733 short days_in_month;
734
735 if ( ( impl = (struct icalrecur_iterator_impl *)
736 malloc(sizeof(struct icalrecur_iterator_impl))) == 0) {
737 icalerror_set_errno(ICAL_NEWFAILED_ERROR);
738 return 0;
739 }
740
741 memset(impl,0,sizeof(struct icalrecur_iterator_impl));
742
743 impl->rule = rule;
744 impl->last = dtstart;
745 impl->dtstart = dtstart;
746 impl->days_index =0;
747 impl->occurrence_no = 0;
748 freq = impl->rule.freq;
749
750 /* Set up convienience pointers to make the code simpler. Allows
751 us to iterate through all of the BY* arrays in the rule. */
752
753 impl->by_ptrs[BY_MONTH]=impl->rule.by_month;
754 impl->by_ptrs[BY_WEEK_NO]=impl->rule.by_week_no;
755 impl->by_ptrs[BY_YEAR_DAY]=impl->rule.by_year_day;
756 impl->by_ptrs[BY_MONTH_DAY]=impl->rule.by_month_day;
757 impl->by_ptrs[BY_DAY]=impl->rule.by_day;
758 impl->by_ptrs[BY_HOUR]=impl->rule.by_hour;
759 impl->by_ptrs[BY_MINUTE]=impl->rule.by_minute;
760 impl->by_ptrs[BY_SECOND]=impl->rule.by_second;
761 impl->by_ptrs[BY_SET_POS]=impl->rule.by_set_pos;
762
763 memset(impl->orig_data,0,9);
764
765 /* Note which by rules had data in them when the iterator was
766 created. We can't use the actuall by_x arrays, because the
767 empty ones will be given default values later in this
768 routine. The orig_data array will be used later in has_by_data */
769
770 impl->orig_data[BY_MONTH]
771 = (impl->rule.by_month[0]!=ICAL_RECURRENCE_ARRAY_MAX);
772 impl->orig_data[BY_WEEK_NO]
773 =(impl->rule.by_week_no[0]!=ICAL_RECURRENCE_ARRAY_MAX);
774 impl->orig_data[BY_YEAR_DAY]
775 =(impl->rule.by_year_day[0]!=ICAL_RECURRENCE_ARRAY_MAX);
776 impl->orig_data[BY_MONTH_DAY]
777 =(impl->rule.by_month_day[0]!=ICAL_RECURRENCE_ARRAY_MAX);
778 impl->orig_data[BY_DAY]
779 = (impl->rule.by_day[0]!=ICAL_RECURRENCE_ARRAY_MAX);
780 impl->orig_data[BY_HOUR]
781 = (impl->rule.by_hour[0]!=ICAL_RECURRENCE_ARRAY_MAX);
782 impl->orig_data[BY_MINUTE]
783 = (impl->rule.by_minute[0]!=ICAL_RECURRENCE_ARRAY_MAX);
784 impl->orig_data[BY_SECOND]
785 = (impl->rule.by_second[0]!=ICAL_RECURRENCE_ARRAY_MAX);
786 impl->orig_data[BY_SET_POS]
787 = (impl->rule.by_set_pos[0]!=ICAL_RECURRENCE_ARRAY_MAX);
788
789
790 /* Check if the recurrence rule is legal */
791
792 /* If the BYYEARDAY appears, no other date rule part may appear. */
793
794 if(icalrecur_two_byrule(impl,BY_YEAR_DAY,BY_MONTH) ||
795 icalrecur_two_byrule(impl,BY_YEAR_DAY,BY_WEEK_NO) ||
796 icalrecur_two_byrule(impl,BY_YEAR_DAY,BY_MONTH_DAY) ||
797 icalrecur_two_byrule(impl,BY_YEAR_DAY,BY_DAY) ){
798
799 icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
800
801 return 0;
802 }
803
804 /* BYWEEKNO and BYMONTH rule parts may not both appear.*/
805
806 if(icalrecur_two_byrule(impl,BY_WEEK_NO,BY_MONTH)){
807 icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
808
809 icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
810 return 0;
811 }
812
813 /* BYWEEKNO and BYMONTHDAY rule parts may not both appear.*/
814
815 if(icalrecur_two_byrule(impl,BY_WEEK_NO,BY_MONTH_DAY)){
816 icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
817
818 icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
819 return 0;
820 }
821
822
823 /*For MONTHLY recurrences (FREQ=MONTHLY) neither BYYEARDAY nor
824 BYWEEKNO may appear. */
825
826 if(freq == ICAL_MONTHLY_RECURRENCE &&
827 icalrecur_one_byrule(impl,BY_WEEK_NO)){
828 icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
829 return 0;
830 }
831
832
833 /*For WEEKLY recurrences (FREQ=WEEKLY) neither BYMONTHDAY nor
834 BYYEARDAY may appear. */
835
836 if(freq == ICAL_WEEKLY_RECURRENCE &&
837 icalrecur_one_byrule(impl,BY_MONTH_DAY )) {
838 icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
839 return 0;
840 }
841
842 /* BYYEARDAY may only appear in YEARLY rules */
843 if(freq != ICAL_YEARLY_RECURRENCE &&
844 icalrecur_one_byrule(impl,BY_YEAR_DAY )) {
845 icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
846 return 0;
847 }
848
849 /* Rewrite some of the rules and set up defaults to make later
850 processing easier. Primarily, t involves copying an element
851 from the start time into the coresponding BY_* array when the
852 BY_* array is empty */
853
854
855 setup_defaults(impl,BY_SECOND,ICAL_SECONDLY_RECURRENCE,impl->dtstart.second,
856 &(impl->last.second));
857
858 setup_defaults(impl,BY_MINUTE,ICAL_MINUTELY_RECURRENCE,impl->dtstart.minute,
859 &(impl->last.minute));
860
861 setup_defaults(impl,BY_HOUR,ICAL_HOURLY_RECURRENCE,impl->dtstart.hour,
862 &(impl->last.hour));
863
864 setup_defaults(impl,BY_MONTH_DAY,ICAL_DAILY_RECURRENCE,impl->dtstart.day,
865 &(impl->last.day));
866
867 setup_defaults(impl,BY_MONTH,ICAL_MONTHLY_RECURRENCE,impl->dtstart.month,
868 &(impl->last.month));
869
870
871 if(impl->rule.freq == ICAL_WEEKLY_RECURRENCE ){
872
873 if(impl->by_ptrs[BY_DAY][0] == ICAL_RECURRENCE_ARRAY_MAX){
874
875 /* Weekly recurrences with no BY_DAY data should occur on the
876 same day of the week as the start time . */
877 impl->by_ptrs[BY_DAY][0] = icaltime_day_of_week(impl->dtstart);
878
879 } else {
880 /* If there is BY_DAY data, then we need to move the initial
881 time to the start of the BY_DAY data. That is if the
882 start time is on a Wednesday, and the rule has
883 BYDAY=MO,WE,FR, move the initial time back to
884 monday. Otherwise, jumping to the next week ( jumping 7
885 days ahead ) will skip over some occurrences in the
886 second week. */
887
888 /* This is probably a HACK. There should be some more
889 general way to solve this problem */
890
891 short dow = impl->by_ptrs[BY_DAY][0]-icaltime_day_of_week(impl->last);
892
893 if(dow < 0) {
894 /* initial time is after first day of BY_DAY data */
895
896 impl->last.day += dow;
897 impl->last = icaltime_normalize(impl->last);
898 }
899 }
900
901
902 }
903
904 /* For YEARLY rule, begin by setting up the year days array */
905
906 if(impl->rule.freq == ICAL_YEARLY_RECURRENCE){
907 expand_year_days(impl,impl->last.year);
908 }
909
910
911 /* If this is a monthly interval with by day data, then we need to
912 set the last value to the appropriate day of the month */
913
914 if(impl->rule.freq == ICAL_MONTHLY_RECURRENCE &&
915 has_by_data(impl,BY_DAY)) {
916
917 short dow = icalrecurrencetype_day_day_of_week(
918 impl->by_ptrs[BY_DAY][impl->by_indices[BY_DAY]]);
919 short pos = icalrecurrencetype_day_position(
920 impl->by_ptrs[BY_DAY][impl->by_indices[BY_DAY]]);
921
922 short poscount = 0;
923 days_in_month =
924 icaltime_days_in_month(impl->last.month, impl->last.year);
925
926 if(pos >= 0){
927 /* Count up from the first day pf the month to find the
928 pos'th weekday of dow ( like the second monday. ) */
929
930 for(impl->last.day = 1;
931 impl->last.day <= days_in_month;
932 impl->last.day++){
933
934 if(icaltime_day_of_week(impl->last) == dow){
935 if(++poscount == pos || pos == 0){
936 break;
937 }
938 }
939 }
940 } else {
941 /* Count down from the last day pf the month to find the
942 pos'th weekday of dow ( like the second to last monday. ) */
943 pos = -pos;
944 for(impl->last.day = days_in_month;
945 impl->last.day != 0;
946 impl->last.day--){
947
948 if(icaltime_day_of_week(impl->last) == dow){
949 if(++poscount == pos ){
950 break;
951 }
952 }
953 }
954 }
955
956
957 if(impl->last.day > days_in_month || impl->last.day == 0){
958 icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
959 return 0;
960 }
961
962 }
963
964
965
966 return impl;
967}
968
969
970void icalrecur_iterator_free(icalrecur_iterator* i)
971{
972
973 struct icalrecur_iterator_impl* impl =
974 (struct icalrecur_iterator_impl*)i;
975
976 icalerror_check_arg_rv((impl!=0),"impl");
977
978 free(impl);
979
980}
981
982
983void increment_year(struct icalrecur_iterator_impl* impl, int inc)
984{
985 impl->last.year+=inc;
986}
987
988/* Increment month is different that the other incement_* routines --
989 it figures out the interval for itself, and uses BYMONTH data if
990 available. */
991void increment_month(struct icalrecur_iterator_impl* impl)
992{
993 int years;
994
995 if(has_by_data(impl,BY_MONTH) ){
996 /* Ignore the frequency and use the byrule data */
997
998 impl->by_indices[BY_MONTH]++;
999
1000 if (impl->by_ptrs[BY_MONTH][impl->by_indices[BY_MONTH]]
1001 ==ICAL_RECURRENCE_ARRAY_MAX){
1002 impl->by_indices[BY_MONTH] = 0;
1003
1004 increment_year(impl,1);
1005
1006 }
1007
1008 impl->last.month =
1009 impl->by_ptrs[BY_MONTH][impl->by_indices[BY_MONTH]];
1010
1011 } else {
1012
1013 int inc;
1014
1015 if(impl->rule.freq == ICAL_MONTHLY_RECURRENCE){
1016 inc = impl->rule.interval;
1017 } else {
1018 inc = 1;
1019 }
1020
1021 impl->last.month+=inc;
1022
1023 /* Months are offset by one */
1024 impl->last.month--;
1025
1026 years = impl->last.month / 12;
1027
1028 impl->last.month = impl->last.month % 12;
1029
1030 impl->last.month++;
1031
1032 if (years != 0){
1033 increment_year(impl,years);
1034 }
1035 }
1036}
1037
1038void increment_monthday(struct icalrecur_iterator_impl* impl, int inc)
1039{
1040 int i;
1041
1042 for(i=0; i<inc; i++){
1043
1044 short days_in_month =
1045 icaltime_days_in_month(impl->last.month,impl->last.year);
1046
1047 impl->last.day++;
1048
1049 if (impl->last.day > days_in_month){
1050 impl->last.day = impl->last.day-days_in_month;
1051 increment_month(impl);
1052 }
1053 }
1054}
1055
1056
1057void increment_hour(struct icalrecur_iterator_impl* impl, int inc)
1058{
1059 short days;
1060
1061 impl->last.hour+=inc;
1062
1063 days = impl->last.hour / 24;
1064 impl->last.hour = impl->last.hour % 24;
1065
1066 if (impl->days != 0){
1067 increment_monthday(impl,days);
1068 }
1069}
1070
1071void increment_minute(struct icalrecur_iterator_impl* impl, int inc)
1072{
1073 short hours;
1074
1075 impl->last.minute+=inc;
1076
1077 hours = impl->last.minute / 60;
1078 impl->last.minute = impl->last.minute % 60;
1079
1080 if (hours != 0){
1081 increment_hour(impl,hours);
1082 }
1083
1084}
1085
1086void increment_second(struct icalrecur_iterator_impl* impl, int inc)
1087{
1088 short minutes;
1089
1090 impl->last.second+=inc;
1091
1092 minutes = impl->last.second / 60;
1093 impl->last.second = impl->last.second % 60;
1094
1095 if (minutes != 0)
1096 {
1097 increment_minute(impl, minutes);
1098 }
1099}
1100
1101#if 0
1102#include "ical.h"
1103void test_increment()
1104{
1105 struct icalrecur_iterator_impl impl;
1106
1107 impl.last = icaltime_from_string("20000101T000000Z");
1108
1109 printf("Orig: %s\n",icaltime_as_ctime(impl.last));
1110
1111 increment_second(&impl,5);
1112 printf("+ 5 sec : %s\n",icaltime_as_ctime(impl.last));
1113
1114 increment_second(&impl,355);
1115 printf("+ 355 sec : %s\n",icaltime_as_ctime(impl.last));
1116
1117 increment_minute(&impl,5);
1118 printf("+ 5 min : %s\n",icaltime_as_ctime(impl.last));
1119
1120 increment_minute(&impl,360);
1121 printf("+ 360 min : %s\n",icaltime_as_ctime(impl.last));
1122 increment_hour(&impl,5);
1123 printf("+ 5 hours : %s\n",icaltime_as_ctime(impl.last));
1124 increment_hour(&impl,43);
1125 printf("+ 43 hours : %s\n",icaltime_as_ctime(impl.last));
1126 increment_monthday(&impl,3);
1127 printf("+ 3 days : %s\n",icaltime_as_ctime(impl.last));
1128 increment_monthday(&impl,600);
1129 printf("+ 600 days : %s\n",icaltime_as_ctime(impl.last));
1130
1131}
1132
1133#endif
1134
1135short next_second(struct icalrecur_iterator_impl* impl)
1136{
1137
1138 short has_by_data = (impl->by_ptrs[BY_SECOND][0]!=ICAL_RECURRENCE_ARRAY_MAX);
1139 short this_frequency = (impl->rule.freq == ICAL_SECONDLY_RECURRENCE);
1140
1141 short end_of_data = 0;
1142
1143 assert(has_by_data || this_frequency);
1144
1145 if( has_by_data ){
1146 /* Ignore the frequency and use the byrule data */
1147
1148 impl->by_indices[BY_SECOND]++;
1149
1150 if (impl->by_ptrs[BY_SECOND][impl->by_indices[BY_SECOND]]
1151 ==ICAL_RECURRENCE_ARRAY_MAX){
1152 impl->by_indices[BY_SECOND] = 0;
1153
1154 end_of_data = 1;
1155 }
1156
1157
1158 impl->last.second =
1159 impl->by_ptrs[BY_SECOND][impl->by_indices[BY_SECOND]];
1160
1161
1162 } else if( !has_by_data && this_frequency ){
1163 /* Compute the next value from the last time and the frequency interval*/
1164 increment_second(impl, impl->rule.interval);
1165
1166 }
1167
1168 /* If we have gone through all of the seconds on the BY list, then we
1169 need to move to the next minute */
1170
1171 if(has_by_data && end_of_data && this_frequency ){
1172 increment_minute(impl,1);
1173 }
1174
1175 return end_of_data;
1176
1177}
1178
1179int next_minute(struct icalrecur_iterator_impl* impl)
1180{
1181
1182 short has_by_data = (impl->by_ptrs[BY_MINUTE][0]!=ICAL_RECURRENCE_ARRAY_MAX);
1183 short this_frequency = (impl->rule.freq == ICAL_MINUTELY_RECURRENCE);
1184
1185 short end_of_data = 0;
1186
1187 assert(has_by_data || this_frequency);
1188
1189
1190 if (next_second(impl) == 0){
1191 return 0;
1192 }
1193
1194 if( has_by_data ){
1195 /* Ignore the frequency and use the byrule data */
1196
1197 impl->by_indices[BY_MINUTE]++;
1198
1199 if (impl->by_ptrs[BY_MINUTE][impl->by_indices[BY_MINUTE]]
1200 ==ICAL_RECURRENCE_ARRAY_MAX){
1201
1202 impl->by_indices[BY_MINUTE] = 0;
1203
1204 end_of_data = 1;
1205 }
1206
1207 impl->last.minute =
1208 impl->by_ptrs[BY_MINUTE][impl->by_indices[BY_MINUTE]];
1209
1210 } else if( !has_by_data && this_frequency ){
1211 /* Compute the next value from the last time and the frequency interval*/
1212 increment_minute(impl,impl->rule.interval);
1213 }
1214
1215/* If we have gone through all of the minutes on the BY list, then we
1216 need to move to the next hour */
1217
1218 if(has_by_data && end_of_data && this_frequency ){
1219 increment_hour(impl,1);
1220 }
1221
1222 return end_of_data;
1223}
1224
1225int next_hour(struct icalrecur_iterator_impl* impl)
1226{
1227
1228 short has_by_data = (impl->by_ptrs[BY_HOUR][0]!=ICAL_RECURRENCE_ARRAY_MAX);
1229 short this_frequency = (impl->rule.freq == ICAL_HOURLY_RECURRENCE);
1230
1231 short end_of_data = 0;
1232
1233 assert(has_by_data || this_frequency);
1234
1235 if (next_minute(impl) == 0){
1236 return 0;
1237 }
1238
1239 if( has_by_data ){
1240 /* Ignore the frequency and use the byrule data */
1241
1242 impl->by_indices[BY_HOUR]++;
1243
1244 if (impl->by_ptrs[BY_HOUR][impl->by_indices[BY_HOUR]]
1245 ==ICAL_RECURRENCE_ARRAY_MAX){
1246 impl->by_indices[BY_HOUR] = 0;
1247
1248 end_of_data = 1;
1249 }
1250
1251 impl->last.hour =
1252 impl->by_ptrs[BY_HOUR][impl->by_indices[BY_HOUR]];
1253
1254 } else if( !has_by_data && this_frequency ){
1255 /* Compute the next value from the last time and the frequency interval*/
1256 increment_hour(impl,impl->rule.interval);
1257
1258 }
1259
1260 /* If we have gone through all of the hours on the BY list, then we
1261 need to move to the next day */
1262
1263 if(has_by_data && end_of_data && this_frequency ){
1264 increment_monthday(impl,1);
1265 }
1266
1267 return end_of_data;
1268
1269}
1270
1271int next_day(struct icalrecur_iterator_impl* impl)
1272{
1273
1274 short has_by_data = (impl->by_ptrs[BY_DAY][0]!=ICAL_RECURRENCE_ARRAY_MAX);
1275 short this_frequency = (impl->rule.freq == ICAL_DAILY_RECURRENCE);
1276
1277 assert(has_by_data || this_frequency);
1278
1279 if (next_hour(impl) == 0){
1280 return 0;
1281 }
1282
1283 /* Always increment through the interval, since this routine is not
1284 called by any other next_* routine, and the days that are
1285 excluded will be taken care of by restriction filtering */
1286
1287 if(this_frequency){
1288 increment_monthday(impl,impl->rule.interval);
1289 } else {
1290 increment_monthday(impl,1);
1291 }
1292
1293
1294 return 0;
1295
1296}
1297
1298
1299int next_yearday(struct icalrecur_iterator_impl* impl)
1300{
1301
1302 short has_by_data = (impl->by_ptrs[BY_YEAR_DAY][0]!=ICAL_RECURRENCE_ARRAY_MAX);
1303
1304 short end_of_data = 0;
1305
1306 assert(has_by_data );
1307
1308 if (next_hour(impl) == 0){
1309 return 0;
1310 }
1311
1312 impl->by_indices[BY_YEAR_DAY]++;
1313
1314 if (impl->by_ptrs[BY_YEAR_DAY][impl->by_indices[BY_YEAR_DAY]]
1315 ==ICAL_RECURRENCE_ARRAY_MAX){
1316 impl->by_indices[BY_YEAR_DAY] = 0;
1317
1318 end_of_data = 1;
1319 }
1320
1321 impl->last.day =
1322 impl->by_ptrs[BY_YEAR_DAY][impl->by_indices[BY_YEAR_DAY]];
1323
1324 if(has_by_data && end_of_data){
1325 increment_year(impl,1);
1326 }
1327
1328 return end_of_data;
1329
1330}
1331
1332/* This routine is only called by next_week. It is certain that BY_DAY
1333has data */
1334
1335int next_weekday_by_week(struct icalrecur_iterator_impl* impl)
1336{
1337
1338 short end_of_data = 0;
1339 short start_of_week, dow;
1340 struct icaltimetype next;
1341
1342 if (next_hour(impl) == 0){
1343 return 0;
1344 }
1345
1346 assert( impl->by_ptrs[BY_DAY][0]!=ICAL_RECURRENCE_ARRAY_MAX);
1347
1348 while(1) {
1349
1350 impl->by_indices[BY_DAY]++; /* Look at next elem in BYDAY array */
1351
1352 /* Are we at the end of the BYDAY array? */
1353 if (impl->by_ptrs[BY_DAY][impl->by_indices[BY_DAY]]
1354 ==ICAL_RECURRENCE_ARRAY_MAX){
1355
1356 impl->by_indices[BY_DAY] = 0; /* Reset to 0 */
1357 end_of_data = 1; /* Signal that we're at the end */
1358 }
1359
1360 /* Add the day of week offset to to the start of this week, and use
1361 that to get the next day */
1362 dow = impl->by_ptrs[BY_DAY][impl->by_indices[BY_DAY]];
1363 start_of_week = icaltime_start_doy_of_week(impl->last);
1364
1365 dow--; /*Sun is 1, not 0 */
1366
1367 if(dow+start_of_week <1 && !end_of_data){
1368 /* The selected date is in the previous year. */
1369 continue;
1370 }
1371
1372 next = icaltime_from_day_of_year(start_of_week + dow,impl->last.year);
1373
1374 impl->last.day = next.day;
1375 impl->last.month = next.month;
1376 impl->last.year = next.year;
1377
1378 return end_of_data;
1379 }
1380
1381}
1382
1383int nth_weekday(short dow, short pos, struct icaltimetype t){
1384
1385 short days_in_month = icaltime_days_in_month(t.month,t.year);
1386 short end_dow, start_dow;
1387 short wd;
1388
1389 if(pos >= 0){
1390 t.day = 1;
1391 start_dow = icaltime_day_of_week(t);
1392
1393 if (pos != 0) {
1394 pos--;
1395 }
1396
1397 /* find month day of first occurrence of dow -- such as the
1398 month day of the first monday */
1399
1400 wd = dow-start_dow+1;
1401
1402 if (wd <= 0){
1403 wd = wd + 7;
1404 }
1405
1406 wd = wd + pos * 7;
1407
1408 } else {
1409 t.day = days_in_month;
1410 end_dow = icaltime_day_of_week(t);
1411
1412 pos++;
1413
1414 /* find month day of last occurrence of dow -- such as the
1415 month day of the last monday */
1416
1417 wd = (end_dow - dow);
1418
1419 if (wd < 0){
1420 wd = wd+ 7;
1421 }
1422
1423 wd = days_in_month - wd;
1424
1425 wd = wd + pos * 7;
1426 }
1427
1428 return wd;
1429}
1430
1431
1432int next_month(struct icalrecur_iterator_impl* impl)
1433{
1434 int data_valid = 1;
1435
1436 short this_frequency = (impl->rule.freq == ICAL_MONTHLY_RECURRENCE);
1437
1438 assert( has_by_data(impl,BY_MONTH) || this_frequency);
1439
1440 /* Iterate through the occurrences within a day. If we don't get to
1441 the end of the intra-day data, don't bother going to the next
1442 month */
1443
1444 if (next_hour(impl) == 0){
1445 return data_valid; /* Signal that the data is valid */
1446 }
1447
1448
1449 /* Now iterate through the occurrences within a month -- by days,
1450 weeks or weekdays. */
1451
1452 if(has_by_data(impl,BY_DAY) && has_by_data(impl,BY_MONTH_DAY)){
1453 /* Cases like: FREQ=MONTHLY;INTERVAL=1;BYDAY=FR;BYMONTHDAY=13 */
1454 short day, idx,j;
1455 short days_in_month = icaltime_days_in_month(impl->last.month,
1456 impl->last.year);
1457 /* Iterate through the remaining days in the month and check if
1458 each day is listed in the BY_DAY array and in the BY_MONTHDAY
1459 array. This seems very inneficient, but I think it is the
1460 simplest way to account for both BYDAY=1FR (First friday in
1461 month) and BYDAY=FR ( every friday in month ) */
1462
1463 for(day = impl->last.day+1; day <= days_in_month; day++){
1464 for(idx = 0; BYDAYPTR[idx] != ICAL_RECURRENCE_ARRAY_MAX; idx++){
1465 for(j = 0; BYMDPTR[j]!=ICAL_RECURRENCE_ARRAY_MAX; j++){
1466 short dow =
1467 icalrecurrencetype_day_day_of_week(BYDAYPTR[idx]);
1468 short pos = icalrecurrencetype_day_position(BYDAYPTR[idx]);
1469 short mday = BYMDPTR[j];
1470 short this_dow;
1471
1472 impl->last.day = day;
1473 this_dow = icaltime_day_of_week(impl->last);
1474
1475 if( (pos == 0 && dow == this_dow && mday == day) ||
1476 (nth_weekday(dow,pos,impl->last) == day && mday==day)){
1477 goto MDEND;
1478 }
1479 }
1480 }
1481 }
1482
1483 MDEND:
1484
1485 if ( day > days_in_month){
1486 impl->last.day = 1;
1487 increment_month(impl);
1488 data_valid = 0; /* signal that impl->last is invalid */
1489 }
1490
1491
1492 } else if(has_by_data(impl,BY_DAY)){
1493 /* Cases like: FREQ=MONTHLY;INTERVAL=1;BYDAY=FR */
1494 /* For this case, the weekdays are relative to the
1495 month. BYDAY=FR -> First Friday in month, etc. */
1496
1497 short day, idx;
1498 short days_in_month = icaltime_days_in_month(impl->last.month,
1499 impl->last.year);
1500
1501 assert( BYDAYPTR[0]!=ICAL_RECURRENCE_ARRAY_MAX);
1502
1503 /* Iterate through the remaining days in the month and check if
1504 each day is listed in the BY_DAY array. This seems very
1505 inneficient, but I think it is the simplest way to account
1506 for both BYDAY=1FR (First friday in month) and BYDAY=FR (
1507 every friday in month ) */
1508
1509 for(day = impl->last.day+1; day <= days_in_month; day++){
1510 for(idx = 0; BYDAYPTR[idx] != ICAL_RECURRENCE_ARRAY_MAX; idx++){
1511 short dow = icalrecurrencetype_day_day_of_week(BYDAYPTR[idx]);
1512 short pos = icalrecurrencetype_day_position(BYDAYPTR[idx]);
1513 short this_dow;
1514
1515 impl->last.day = day;
1516 this_dow = icaltime_day_of_week(impl->last);
1517
1518 if( (pos == 0 && dow == this_dow ) ||
1519 (nth_weekday(dow,pos,impl->last) == day)){
1520 goto DEND;
1521 }
1522 }
1523 }
1524
1525 DEND:
1526
1527 if ( day > days_in_month){
1528 impl->last.day = 1;
1529 increment_month(impl);
1530 data_valid = 0; /* signal that impl->last is invalid */
1531 }
1532
1533 } else if (has_by_data(impl,BY_MONTH_DAY)) {
1534 /* Cases like: FREQ=MONTHLY;COUNT=10;BYMONTHDAY=-3 */
1535 short day;
1536
1537 assert( BYMDPTR[0]!=ICAL_RECURRENCE_ARRAY_MAX);
1538
1539 BYMDIDX++;
1540
1541 /* Are we at the end of the BYDAY array? */
1542 if (BYMDPTR[BYMDIDX] ==ICAL_RECURRENCE_ARRAY_MAX){
1543
1544 BYMDIDX = 0; /* Reset to 0 */
1545 increment_month(impl);
1546 }
1547
1548 day = BYMDPTR[BYMDIDX];
1549
1550 if (day < 0) {
1551 day = icaltime_days_in_month(impl->last.month,impl->last.year)+
1552 day + 1;
1553 }
1554
1555 impl->last.day = day;
1556
1557 } else {
1558 increment_month(impl);
1559 }
1560
1561 return data_valid; /* Signal that the data is valid */
1562
1563}
1564
1565
1566int next_week(struct icalrecur_iterator_impl* impl)
1567{
1568 short has_by_data = (impl->by_ptrs[BY_WEEK_NO][0]!=ICAL_RECURRENCE_ARRAY_MAX);
1569 short this_frequency = (impl->rule.freq == ICAL_WEEKLY_RECURRENCE);
1570 short end_of_data = 0;
1571
1572 /* Increment to the next week day */
1573 if (next_weekday_by_week(impl) == 0){
1574 return 0; /* Have not reached end of week yet */
1575 }
1576
1577 /* If we get here, we have incremented through the entire week, and
1578 can increment to the next week */
1579
1580
1581 if( has_by_data){
1582 /* Use the Week Number byrule data */
1583 int week_no;
1584 struct icaltimetype t;
1585
1586 impl->by_indices[BY_WEEK_NO]++;
1587
1588 if (impl->by_ptrs[BY_WEEK_NO][impl->by_indices[BY_WEEK_NO]]
1589 ==ICAL_RECURRENCE_ARRAY_MAX){
1590 impl->by_indices[BY_WEEK_NO] = 0;
1591
1592 end_of_data = 1;
1593 }
1594
1595 t = impl->last;
1596 t.month=1; /* HACK, should be setting to the date of the first week of year*/
1597 t.day=1;
1598
1599 week_no = impl->by_ptrs[BY_WEEK_NO][impl->by_indices[BY_WEEK_NO]];
1600
1601 impl->last.day += week_no*7;
1602
1603 impl->last = icaltime_normalize(impl->last);
1604
1605 } else if( !has_by_data && this_frequency ){
1606 /* If there is no BY_WEEK_NO data, just jump forward 7 days. */
1607 increment_monthday(impl,7*impl->rule.interval);
1608 }
1609
1610
1611 if(has_by_data && end_of_data && this_frequency ){
1612 increment_year(impl,1);
1613 }
1614
1615 return end_of_data;
1616
1617}
1618
1619/* Expand the BYDAY rule part and return a pointer to a newly allocated list of days. */
1620pvl_list expand_by_day(struct icalrecur_iterator_impl* impl,short year)
1621{
1622 /* Try to calculate each of the occurrences. */
1623 int i;
1624 pvl_list days_list = pvl_newlist();
1625
1626 short start_dow, end_dow, end_year_day, start_doy;
1627 struct icaltimetype tmp = impl->last;
1628
1629 tmp.year= year;
1630 tmp.month = 1;
1631 tmp.day = 1;
1632 tmp.is_date = 1;
1633
1634 start_dow = icaltime_day_of_week(tmp);
1635 start_doy = icaltime_start_doy_of_week(tmp);
1636
1637 /* Get the last day of the year*/
1638 tmp.year++;
1639 tmp = icaltime_normalize(tmp);
1640 tmp.day--;
1641 tmp = icaltime_normalize(tmp);
1642
1643 end_dow = icaltime_day_of_week(tmp);
1644 end_year_day = icaltime_day_of_year(tmp);
1645
1646 for(i = 0; BYDAYPTR[i] != ICAL_RECURRENCE_ARRAY_MAX; i++){
1647 short dow =
1648 icalrecurrencetype_day_day_of_week(BYDAYPTR[i]);
1649 short pos = icalrecurrencetype_day_position(BYDAYPTR[i]);
1650
1651 if(pos == 0){
1652 /* The day was specified without a position -- it is just
1653 a bare day of the week ( BYDAY=SU) so add all of the
1654 days of the year with this day-of-week*/
1655 int week;
1656 for(week = 0; week < 52 ; week ++){
1657 short doy = start_doy + (week * 7) + dow-1;
1658
1659 if(doy > end_year_day){
1660 break;
1661 } else {
1662 pvl_push(days_list,(void*)(int)doy);
1663 }
1664 }
1665 } else if ( pos > 0) {
1666 int first;
1667 /* First occurrence of dow in year */
1668 if( dow >= start_dow) {
1669 first = dow - start_dow + 1;
1670 } else {
1671 first = dow - start_dow + 8;
1672 }
1673
1674 /* THen just multiple the position times 7 to get the pos'th day in the year */
1675 pvl_push(days_list,(void*)(first+ (pos-1) * 7));
1676
1677 } else { /* pos < 0 */
1678 int last;
1679 pos = -pos;
1680
1681 /* last occurrence of dow in year */
1682 if( dow <= end_dow) {
1683 last = end_year_day - end_dow + dow;
1684 } else {
1685 last = end_year_day - end_dow + dow - 7;
1686 }
1687
1688 pvl_push(days_list,(void*)(last - (pos-1) * 7));
1689 }
1690 }
1691 return days_list;
1692}
1693
1694
1695/* For INTERVAL=YEARLY, set up the days[] array in the iterator to
1696 list all of the days of the current year that are specified in this
1697 rule. */
1698
1699int expand_year_days(struct icalrecur_iterator_impl* impl,short year)
1700{
1701 int j,k;
1702 int days_index=0;
1703 struct icaltimetype t;
1704 int flags;
1705
1706 t = icaltime_null_time();
1707
1708#define HBD(x) has_by_data(impl,x)
1709
1710 t.is_date = 1; /* Needed to make day_of_year routines work property */
1711
1712 memset(&t,0,sizeof(t));
1713 memset(impl->days,ICAL_RECURRENCE_ARRAY_MAX_BYTE,sizeof(impl->days));
1714
1715 flags = (HBD(BY_DAY) ? 1<<BY_DAY : 0) +
1716 (HBD(BY_WEEK_NO) ? 1<<BY_WEEK_NO : 0) +
1717 (HBD(BY_MONTH_DAY) ? 1<<BY_MONTH_DAY : 0) +
1718 (HBD(BY_MONTH) ? 1<<BY_MONTH : 0) +
1719 (HBD(BY_YEAR_DAY) ? 1<<BY_YEAR_DAY : 0);
1720
1721
1722 switch(flags) {
1723
1724 case 0: {
1725 /* FREQ=YEARLY; */
1726
1727 break;
1728 }
1729 case 1<<BY_MONTH: {
1730 /* FREQ=YEARLY; BYMONTH=3,11*/
1731
1732 for(j=0;impl->by_ptrs[BY_MONTH][j]!=ICAL_RECURRENCE_ARRAY_MAX;j++){
1733 struct icaltimetype t;
1734 short month = impl->by_ptrs[BY_MONTH][j];
1735 short doy;
1736
1737 t = impl->dtstart;
1738 t.year = year;
1739 t.month = month;
1740 t.is_date = 1;
1741
1742 doy = icaltime_day_of_year(t);
1743
1744 impl->days[days_index++] = doy;
1745
1746 }
1747 break;
1748 }
1749
1750 case 1<<BY_MONTH_DAY: {
1751 /* FREQ=YEARLY; BYMONTHDAY=1,15*/
1752 for(k=0;impl->by_ptrs[BY_MONTH_DAY][k]!=ICAL_RECURRENCE_ARRAY_MAX;k++)
1753 {
1754 short month_day = impl->by_ptrs[BY_MONTH_DAY][k];
1755 short doy;
1756
1757 t = impl->dtstart;
1758 t.day = month_day;
1759 t.year = year;
1760 t.is_date = 1;
1761
1762 doy = icaltime_day_of_year(t);
1763
1764 impl->days[days_index++] = doy;
1765
1766 }
1767 break;
1768 }
1769
1770 case (1<<BY_MONTH_DAY) + (1<<BY_MONTH): {
1771 /* FREQ=YEARLY; BYMONTHDAY=1,15; BYMONTH=10 */
1772
1773 for(j=0;impl->by_ptrs[BY_MONTH][j]!=ICAL_RECURRENCE_ARRAY_MAX;j++){
1774 for(k=0;impl->by_ptrs[BY_MONTH_DAY][k]!=ICAL_RECURRENCE_ARRAY_MAX;k++)
1775 {
1776 short month = impl->by_ptrs[BY_MONTH][j];
1777 short month_day = impl->by_ptrs[BY_MONTH_DAY][k];
1778 short doy;
1779
1780 t.day = month_day;
1781 t.month = month;
1782 t.year = year;
1783 t.is_date = 1;
1784
1785 doy = icaltime_day_of_year(t);
1786
1787 impl->days[days_index++] = doy;
1788
1789 }
1790 }
1791
1792 break;
1793 }
1794
1795 case 1<<BY_WEEK_NO: {
1796 /* FREQ=YEARLY; BYWEEKNO=20,50 */
1797
1798 struct icaltimetype t;
1799 short dow;
1800
1801 t.day = impl->dtstart.day;
1802 t.month = impl->dtstart.month;
1803 t.year = year;
1804 t.is_date = 1;
1805
1806 dow = icaltime_day_of_week(t);
1807 /* HACK Not finished */
1808
1809 icalerror_set_errno(ICAL_UNIMPLEMENTED_ERROR);
1810
1811 break;
1812 }
1813
1814 case (1<<BY_WEEK_NO) + (1<<BY_MONTH_DAY): {
1815 /*FREQ=YEARLY; WEEKNO=20,50; BYMONTH= 6,11 */
1816 icalerror_set_errno(ICAL_UNIMPLEMENTED_ERROR);
1817 break;
1818 }
1819
1820 case 1<<BY_DAY: {
1821 /*FREQ=YEARLY; BYDAY=TH,20MO,-10FR*/
1822 int days_index = 0;
1823 pvl_elem i;
1824 pvl_list days = expand_by_day(impl,year);
1825
1826
1827 for(i=pvl_head(days);i!=0;i=pvl_next(i)){
1828 short day = (short)(int)pvl_data(i);
1829 impl->days[days_index++] = day;
1830 }
1831
1832 pvl_free(days);
1833
1834 break;
1835 }
1836
1837 case (1<<BY_DAY)+(1<<BY_MONTH): {
1838 /*FREQ=YEARLY; BYDAY=TH,20MO,-10FR; BYMONTH = 12*/
1839
1840 int days_index = 0;
1841 pvl_elem itr;
1842 pvl_list days = expand_by_day(impl,year);
1843
1844 for(itr=pvl_head(days);itr!=0;itr=pvl_next(itr)){
1845 short doy = (short)(int)pvl_data(itr);
1846 struct icaltimetype tt;
1847 short j;
1848
1849 tt = icaltime_from_day_of_year(doy,year);
1850
1851 for(j=0;
1852 impl->by_ptrs[BY_MONTH][j]!=ICAL_RECURRENCE_ARRAY_MAX;
1853 j++){
1854 short month = impl->by_ptrs[BY_MONTH][j];
1855
1856 if(tt.month == month){
1857 impl->days[days_index++] = doy;
1858 }
1859 }
1860
1861 }
1862
1863 pvl_free(days);
1864
1865 break;
1866 }
1867
1868 case (1<<BY_DAY) + (1<<BY_MONTH_DAY) : {
1869 /*FREQ=YEARLY; BYDAY=TH,20MO,-10FR; BYMONTHDAY=1,15*/
1870
1871 int days_index = 0;
1872 pvl_elem itr;
1873 pvl_list days = expand_by_day(impl,year);
1874
1875 for(itr=pvl_head(days);itr!=0;itr=pvl_next(itr)){
1876 short day = (short)(int)pvl_data(itr);
1877 struct icaltimetype tt;
1878 short j;
1879
1880 tt = icaltime_from_day_of_year(day,year);
1881
1882 for(j = 0; BYMDPTR[j]!=ICAL_RECURRENCE_ARRAY_MAX; j++){
1883 short mday = BYMDPTR[j];
1884
1885 if(tt.day == mday){
1886 impl->days[days_index++] = day;
1887 }
1888 }
1889
1890 }
1891
1892 pvl_free(days);
1893
1894 break;
1895 }
1896
1897 case (1<<BY_DAY) + (1<<BY_MONTH_DAY) + (1<<BY_MONTH): {
1898 /*FREQ=YEARLY; BYDAY=TH,20MO,-10FR; BYMONTHDAY=10; MYMONTH=6,11*/
1899
1900 int days_index = 0;
1901 pvl_elem itr;
1902 pvl_list days = expand_by_day(impl,year);
1903
1904 for(itr=pvl_head(days);itr!=0;itr=pvl_next(itr)){
1905 short day = (short)(int)pvl_data(itr);
1906 struct icaltimetype tt;
1907 short i,j;
1908
1909 tt = icaltime_from_day_of_year(day,year);
1910
1911 for(i = 0; BYMONPTR[i] != ICAL_RECURRENCE_ARRAY_MAX; i++){
1912 for(j = 0; BYMDPTR[j]!=ICAL_RECURRENCE_ARRAY_MAX; j++){
1913 short mday = BYMDPTR[j];
1914 short month = BYMONPTR[i];
1915
1916 if(tt.month == month && tt.day == mday){
1917 impl->days[days_index++] = day;
1918 }
1919 }
1920 }
1921
1922 }
1923
1924 pvl_free(days);
1925
1926 break;
1927
1928 }
1929
1930 case (1<<BY_DAY) + (1<<BY_WEEK_NO) : {
1931 /*FREQ=YEARLY; BYDAY=TH,20MO,-10FR; WEEKNO=20,50*/
1932
1933 int days_index = 0;
1934 pvl_elem itr;
1935 pvl_list days = expand_by_day(impl,year);
1936
1937 for(itr=pvl_head(days);itr!=0;itr=pvl_next(itr)){
1938 short day = (short)(int)pvl_data(itr);
1939 struct icaltimetype tt;
1940 short i;
1941
1942 tt = icaltime_from_day_of_year(day,year);
1943
1944 for(i = 0; BYWEEKPTR[i] != ICAL_RECURRENCE_ARRAY_MAX; i++){
1945 short weekno = BYWEEKPTR[i];
1946
1947 if(weekno== icaltime_week_number(tt)){
1948 impl->days[days_index++] = day;
1949 }
1950 }
1951
1952 }
1953
1954 pvl_free(days);
1955 break;
1956 }
1957
1958 case (1<<BY_DAY) + (1<<BY_WEEK_NO) + (1<<BY_MONTH_DAY): {
1959 /*FREQ=YEARLY; BYDAY=TH,20MO,-10FR; WEEKNO=20,50; BYMONTHDAY=1,15*/
1960 icalerror_set_errno(ICAL_UNIMPLEMENTED_ERROR);
1961 break;
1962 }
1963
1964 case 1<<BY_YEAR_DAY: {
1965 for(j=0;impl->by_ptrs[BY_YEAR_DAY][j]!=ICAL_RECURRENCE_ARRAY_MAX;j++){
1966 short doy = impl->by_ptrs[BY_YEAR_DAY][j];
1967 impl->days[days_index++] = doy;
1968 }
1969 break;
1970 }
1971
1972 default: {
1973 icalerror_set_errno(ICAL_UNIMPLEMENTED_ERROR);
1974 break;
1975 }
1976
1977 }
1978
1979 return 0;
1980}
1981
1982
1983int next_year(struct icalrecur_iterator_impl* impl)
1984{
1985 struct icaltimetype next;
1986
1987 /* Next_year does it's own interatio in days, so the next level down is hours */
1988 if (next_hour(impl) == 0){
1989 return 1;
1990 }
1991
1992 if (impl->days[++impl->days_index] == ICAL_RECURRENCE_ARRAY_MAX){
1993 impl->days_index = 0;
1994 increment_year(impl,impl->rule.interval);
1995 expand_year_days(impl,impl->last.year);
1996 }
1997
1998 if(impl->days[0] == ICAL_RECURRENCE_ARRAY_MAX) {
1999 return 0;
2000 }
2001
2002 next = icaltime_from_day_of_year(impl->days[impl->days_index],impl->last.year);
2003
2004 impl->last.day = next.day;
2005 impl->last.month = next.month;
2006
2007 return 1;
2008}
2009
2010int icalrecur_check_rulepart(struct icalrecur_iterator_impl* impl,
2011 short v, enum byrule byrule)
2012{
2013 int itr;
2014
2015 if(impl->by_ptrs[byrule][0]!=ICAL_RECURRENCE_ARRAY_MAX){
2016 for(itr=0; impl->by_ptrs[byrule][itr]!=ICAL_RECURRENCE_ARRAY_MAX;itr++){
2017 if(impl->by_ptrs[byrule][itr] == v){
2018 return 1;
2019 }
2020 }
2021 }
2022
2023 return 0;
2024}
2025
2026int check_contract_restriction(struct icalrecur_iterator_impl* impl,
2027 enum byrule byrule, short v)
2028{
2029 int pass = 0;
2030 int itr;
2031 icalrecurrencetype_frequency freq = impl->rule.freq;
2032
2033 if(impl->by_ptrs[byrule][0]!=ICAL_RECURRENCE_ARRAY_MAX &&
2034 expand_map[freq].map[byrule] == CONTRACT){
2035 for(itr=0; impl->by_ptrs[byrule][itr]!=ICAL_RECURRENCE_ARRAY_MAX;itr++){
2036 if(impl->by_ptrs[byrule][itr] == v){
2037 pass=1;
2038 break;
2039 }
2040 }
2041
2042 return pass;
2043 } else {
2044 /* This is not a contracting byrule, or it has no data, so the
2045 test passes*/
2046 return 1;
2047 }
2048}
2049
2050
2051int check_contracting_rules(struct icalrecur_iterator_impl* impl)
2052{
2053
2054 int day_of_week=0;
2055 int week_no=0;
2056 int year_day=0;
2057
2058 if (
2059 check_contract_restriction(impl,BY_SECOND,impl->last.second) &&
2060 check_contract_restriction(impl,BY_MINUTE,impl->last.minute) &&
2061 check_contract_restriction(impl,BY_HOUR,impl->last.hour) &&
2062 check_contract_restriction(impl,BY_DAY,day_of_week) &&
2063 check_contract_restriction(impl,BY_WEEK_NO,week_no) &&
2064 check_contract_restriction(impl,BY_MONTH_DAY,impl->last.day) &&
2065 check_contract_restriction(impl,BY_MONTH,impl->last.month) &&
2066 check_contract_restriction(impl,BY_YEAR_DAY,year_day) )
2067 {
2068
2069 return 1;
2070 } else {
2071 return 0;
2072 }
2073}
2074
2075struct icaltimetype icalrecur_iterator_next(icalrecur_iterator *itr)
2076{
2077 int valid = 1;
2078 struct icalrecur_iterator_impl* impl =
2079 (struct icalrecur_iterator_impl*)itr;
2080
2081 if( (impl->rule.count!=0 &&impl->occurrence_no >= impl->rule.count) ||
2082 (!icaltime_is_null_time(impl->rule.until) &&
2083 icaltime_compare(impl->last,impl->rule.until) > 0)) {
2084 return icaltime_null_time();
2085 }
2086
2087 if(impl->occurrence_no == 0
2088 && icaltime_compare(impl->last,impl->dtstart) >= 0){
2089
2090 impl->occurrence_no++;
2091 return impl->last;
2092 }
2093
2094 do {
2095 valid = 1;
2096 switch(impl->rule.freq){
2097
2098 case ICAL_SECONDLY_RECURRENCE: {
2099 next_second(impl);
2100 break;
2101 }
2102 case ICAL_MINUTELY_RECURRENCE: {
2103 next_minute(impl);
2104 break;
2105 }
2106 case ICAL_HOURLY_RECURRENCE: {
2107 next_hour(impl);
2108 break;
2109 }
2110 case ICAL_DAILY_RECURRENCE: {
2111 next_day(impl);
2112 break;
2113 }
2114 case ICAL_WEEKLY_RECURRENCE: {
2115 next_week(impl);
2116 break;
2117 }
2118 case ICAL_MONTHLY_RECURRENCE: {
2119 valid = next_month(impl);
2120 break;
2121 }
2122 case ICAL_YEARLY_RECURRENCE:{
2123 valid = next_year(impl);
2124 break;
2125 }
2126 default:{
2127 icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
2128 return icaltime_null_time();
2129 }
2130 }
2131
2132 if(impl->last.year >= 2038 ){
2133 /* HACK */
2134 return icaltime_null_time();
2135 }
2136
2137 } while(!check_contracting_rules(impl)
2138 || icaltime_compare(impl->last,impl->dtstart) <= 0
2139 || valid == 0);
2140
2141
2142/* Ignore null times and times that are after the until time */
2143 if( !icaltime_is_null_time(impl->rule.until) &&
2144 icaltime_compare(impl->last,impl->rule.until) > 0 ) {
2145 return icaltime_null_time();
2146 }
2147
2148 impl->occurrence_no++;
2149
2150 return impl->last;
2151}
2152
2153
2154/************************** Type Routines **********************/
2155
2156
2157void icalrecurrencetype_clear(struct icalrecurrencetype *recur)
2158{
2159 memset(recur,ICAL_RECURRENCE_ARRAY_MAX_BYTE,
2160 sizeof(struct icalrecurrencetype));
2161
2162 recur->week_start = ICAL_MONDAY_WEEKDAY;
2163 recur->freq = ICAL_NO_RECURRENCE;
2164 recur->interval = 1;
2165 memset(&(recur->until),0,sizeof(struct icaltimetype));
2166 recur->count = 0;
2167}
2168
2169/* The 'day' element of icalrecurrencetype_weekday is encoded to allow
2170reporesentation of both the day of the week ( Monday, Tueday), but
2171also the Nth day of the week ( First tuesday of the month, last
2172thursday of the year) These routines decode the day values.
2173
2174The day's position in the period ( Nth-ness) and the numerical value
2175of the day are encoded together as: pos*7 + dow
2176
2177A position of 0 means 'any' or 'every'
2178
2179 */
2180
2181enum icalrecurrencetype_weekday icalrecurrencetype_day_day_of_week(short day)
2182{
2183 return abs(day)%8;
2184}
2185
2186short icalrecurrencetype_day_position(short day)
2187{
2188 short wd, pos;
2189
2190 wd = icalrecurrencetype_day_day_of_week(day);
2191
2192 pos = (abs(day)-wd)/8 * ((day<0)?-1:1);
2193
2194
2195 return pos;
2196}
2197
2198
2199/****************** Enumeration Routines ******************/
2200
2201struct {icalrecurrencetype_weekday wd; const char * str; }
2202wd_map[] = {
2203 {ICAL_SUNDAY_WEEKDAY,"SU"},
2204 {ICAL_MONDAY_WEEKDAY,"MO"},
2205 {ICAL_TUESDAY_WEEKDAY,"TU"},
2206 {ICAL_WEDNESDAY_WEEKDAY,"WE"},
2207 {ICAL_THURSDAY_WEEKDAY,"TH"},
2208 {ICAL_FRIDAY_WEEKDAY,"FR"},
2209 {ICAL_SATURDAY_WEEKDAY,"SA"},
2210 {ICAL_NO_WEEKDAY,0}
2211};
2212
2213const char* icalrecur_weekday_to_string(icalrecurrencetype_weekday kind)
2214{
2215 int i;
2216
2217 for (i=0; wd_map[i].wd != ICAL_NO_WEEKDAY; i++) {
2218 if ( wd_map[i].wd == kind) {
2219 return wd_map[i].str;
2220 }
2221 }
2222
2223 return 0;
2224}
2225
2226icalrecurrencetype_weekday icalrecur_string_to_weekday(const char* str)
2227{
2228 int i;
2229
2230 for (i=0; wd_map[i].wd != ICAL_NO_WEEKDAY; i++) {
2231 if ( strcmp(str,wd_map[i].str) == 0){
2232 return wd_map[i].wd;
2233 }
2234 }
2235
2236 return ICAL_NO_WEEKDAY;
2237}
2238
2239
2240
2241struct {
2242 icalrecurrencetype_frequency kind;
2243 const char* str;
2244} freq_map[] = {
2245 {ICAL_SECONDLY_RECURRENCE,"SECONDLY"},
2246 {ICAL_MINUTELY_RECURRENCE,"MINUTELY"},
2247 {ICAL_HOURLY_RECURRENCE,"HOURLY"},
2248 {ICAL_DAILY_RECURRENCE,"DAILY"},
2249 {ICAL_WEEKLY_RECURRENCE,"WEEKLY"},
2250 {ICAL_MONTHLY_RECURRENCE,"MONTHLY"},
2251 {ICAL_YEARLY_RECURRENCE,"YEARLY"},
2252 {ICAL_NO_RECURRENCE,0}
2253};
2254
2255const char* icalrecur_freq_to_string(icalrecurrencetype_frequency kind)
2256{
2257 int i;
2258
2259 for (i=0; freq_map[i].kind != ICAL_NO_RECURRENCE ; i++) {
2260 if ( freq_map[i].kind == kind ) {
2261 return freq_map[i].str;
2262 }
2263 }
2264 return 0;
2265}
2266
2267icalrecurrencetype_frequency icalrecur_string_to_freq(const char* str)
2268{
2269 int i;
2270
2271 for (i=0; freq_map[i].kind != ICAL_NO_RECURRENCE ; i++) {
2272 if ( strcmp(str,freq_map[i].str) == 0){
2273 return freq_map[i].kind;
2274 }
2275 }
2276 return ICAL_NO_RECURRENCE;
2277}
2278
2279/* Fill an array with the 'count' number of occurrences generated by
2280 the rrule. Note that the times are returned in UTC, but the times
2281 are calculated in local time. YOu will have to convert the results
2282 back into local time before using them. */
2283
2284int icalrecur_expand_recurrence(char* rule, time_t start,
2285 int count, time_t* array)
2286{
2287 struct icalrecurrencetype recur;
2288 icalrecur_iterator* ritr;
2289 time_t tt;
2290 struct icaltimetype icstart, next;
2291 int i = 0;
2292
2293 memset(array, 0, count*sizeof(time_t));
2294
2295 icstart = icaltime_from_timet(start,0);
2296
2297 recur = icalrecurrencetype_from_string(rule);
2298
2299 for(ritr = icalrecur_iterator_new(recur,icstart),
2300 next = icalrecur_iterator_next(ritr);
2301 !icaltime_is_null_time(next) && i < count;
2302 next = icalrecur_iterator_next(ritr)){
2303
2304 tt = icaltime_as_timet(next);
2305
2306 if (tt >= start ){
2307 array[i++] = tt;
2308 }
2309
2310 }
2311
2312 icalrecur_iterator_free(ritr);
2313
2314 return 1;
2315}