Libical API Documentation 4.0 UNRELEASED Go to the stable 3.0 documentation
Loading...
Searching...
No Matches
icalrecur.c
Go to the documentation of this file.
1/*======================================================================
2 FILE: icalrecur.c
3 CREATOR: eric 16 May 2000
4
5 SPDX-FileCopyrightText: 2000, Eric Busboom <eric@civicknowledge.com>
6 SPDX-License-Identifier: LGPL-2.1-only OR MPL-2.0
7========================================================================*/
8
122
123#ifdef HAVE_CONFIG_H
124#include <config.h>
125#endif
126
127#include "icalrecur.h"
128#include "icalerror_p.h"
129#include "icalerror.h"
130#include "icallimits.h"
131#include "icalmemory.h"
132#include "icaltimezone.h"
133#include "icalvalue.h" /* for print_date[time]_to_string() */
134
135#include <ctype.h>
136#include <stddef.h> /* For offsetof() macro */
137#include <stdint.h>
138#include <stdlib.h>
139
140#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD
141#include <pthread.h>
142static pthread_mutex_t invalid_rrule_mutex = PTHREAD_MUTEX_INITIALIZER;
143#endif
144
145static ICAL_GLOBAL_VAR ical_invalid_rrule_handling invalidRruleHandling = ICAL_RRULE_TREAT_AS_ERROR;
146
147#if defined(HAVE_LIBICU)
148#include <unicode/ucal.h>
149#include <unicode/ustring.h>
150#include <stdbool.h>
151#else
152
153/* The maximums below are based on Gregorian leap years */
154#undef ICAL_BY_MONTH_SIZE
155#undef ICAL_BY_WEEKNO_SIZE
156#undef ICAL_BY_YEARDAY_SIZE
158#define ICAL_BY_MONTH_SIZE 13 /* 1 to 12 */
159#define ICAL_BY_WEEKNO_SIZE 54 /* 1 to 53 */
160#define ICAL_BY_YEARDAY_SIZE 367 /* 1 to 366 */
162#endif
163
165#if defined(HAVE_LIBICU)
166#define MAX_TIME_T_YEAR 20000
167#else
168#if (SIZEOF_ICALTIME_T > 4)
171#define MAX_TIME_T_YEAR 2582
172#else
175#define MAX_TIME_T_YEAR 2037
176#endif
177#endif
178
179#define LEAP_MONTH 0x1000
181
182/****************** Forward declarations ******************/
183static void icalrecurrencetype_clear(struct icalrecurrencetype *recur);
184static short daymask_find_next_bit(const unsigned long *days, short start_index);
185
186/****************** Enumeration Routines ******************/
187
188static const struct freq_map {
190 const char str[9];
191} freq_map[] = {
192 {ICAL_SECONDLY_RECURRENCE, "SECONDLY"},
193 {ICAL_MINUTELY_RECURRENCE, "MINUTELY"},
194 {ICAL_HOURLY_RECURRENCE, "HOURLY"},
195 {ICAL_DAILY_RECURRENCE, "DAILY"},
196 {ICAL_WEEKLY_RECURRENCE, "WEEKLY"},
197 {ICAL_MONTHLY_RECURRENCE, "MONTHLY"},
198 {ICAL_YEARLY_RECURRENCE, "YEARLY"},
199 {ICAL_NO_RECURRENCE, ""}};
200
202{
203 int i;
204
205 for (i = 0; freq_map[i].kind != ICAL_NO_RECURRENCE; i++) {
206 if (strcasecmp(str, freq_map[i].str) == 0) {
207 return freq_map[i].kind;
208 }
209 }
210 return ICAL_NO_RECURRENCE;
211}
212
214{
215 int i;
216
217 for (i = 0; freq_map[i].kind != ICAL_NO_RECURRENCE; i++) {
218 if (freq_map[i].kind == kind) {
219 return freq_map[i].str;
220 }
221 }
222 return 0;
223}
224
225static const struct skip_map {
227 const char str[9];
228} skip_map[] = {
229 {ICAL_SKIP_BACKWARD, "BACKWARD"},
230 {ICAL_SKIP_FORWARD, "FORWARD"},
231 {ICAL_SKIP_OMIT, "OMIT"},
232 {ICAL_SKIP_UNDEFINED, ""}};
233
235{
236 int i;
237
238 for (i = 0; skip_map[i].kind != ICAL_SKIP_UNDEFINED; i++) {
239 if (strcasecmp(str, skip_map[i].str) == 0) {
240 return skip_map[i].kind;
241 }
242 }
243 return ICAL_SKIP_UNDEFINED;
244}
245
247{
248 int i;
249
250 for (i = 0; skip_map[i].kind != ICAL_SKIP_UNDEFINED; i++) {
251 if (skip_map[i].kind == kind) {
252 return skip_map[i].str;
253 }
254 }
255 return 0;
256}
257
258static const struct wd_map {
260 const char str[3];
261} wd_map[] = {
262 {ICAL_SUNDAY_WEEKDAY, "SU"},
263 {ICAL_MONDAY_WEEKDAY, "MO"},
264 {ICAL_TUESDAY_WEEKDAY, "TU"},
266 {ICAL_THURSDAY_WEEKDAY, "TH"},
267 {ICAL_FRIDAY_WEEKDAY, "FR"},
268 {ICAL_SATURDAY_WEEKDAY, "SA"},
269 {ICAL_NO_WEEKDAY, ""}};
270
272{
273 int i;
274
275 for (i = 0; wd_map[i].wd != ICAL_NO_WEEKDAY; i++) {
276 if (wd_map[i].wd == kind) {
277 return wd_map[i].str;
278 }
279 }
280
281 return 0;
282}
283
285{
286 int i;
287
288 for (i = 0; wd_map[i].wd != ICAL_NO_WEEKDAY; i++) {
289 if (strcasecmp(str, wd_map[i].str) == 0) {
290 return wd_map[i].wd;
291 }
292 }
293
294 return ICAL_NO_WEEKDAY;
295}
296
297/*********************** Memory management helper routines ************************/
298
299static void icalrecur_free_by(icalrecurrence_by_data *by)
300{
301 icalmemory_free_buffer(by->data);
302 by->data = NULL;
303 by->size = 0;
304}
305
306bool icalrecur_resize_by(icalrecurrence_by_data *by, short size)
307{
308 if (by->size == size) {
309 return true;
310 }
311
312 if (size == 0) {
313 icalrecur_free_by(by);
314 return true;
315 }
316
317 if ((by->data == NULL) || (by->size == 0)) {
318 if ((by->data != NULL) || (by->size != 0)) {
320 return false;
321 }
322
323 by->data = (short *)icalmemory_new_buffer((size_t)size * sizeof(by->data[0]));
324 if (!by->data) {
325 return false;
326 }
327 } else {
328 short *new_data = (short *)icalmemory_resize_buffer(by->data, (size_t)size * sizeof(by->data[0]));
329 if (!new_data) {
330 return false;
331 }
332
333 by->data = new_data;
334 }
335
336 if (size > by->size) {
337 memset(&by->data[by->size], 0, (size_t)(size - by->size) * sizeof(by->data[0]));
338 }
339
340 by->size = size;
341
342 return true;
343}
344
345/*********************** Rule parsing routines ************************/
346
347struct icalrecur_parser {
348 const char *rule;
349 char *copy;
350 char *this_clause;
351 char *next_clause;
352
353 struct icalrecurrencetype *rt;
354};
355
357enum expand_table
358{
359 UNKNOWN = 0,
360 CONTRACT = 1,
361 EXPAND = 2,
362 ILLEGAL = 3
363};
365
366struct expand_split_map_struct {
368
369 /* Elements of the 'map' array correspond to the BYxxx rules:
370 Second,Minute,Hour,Day,Month Day,Year Day,Week No,Month,SetPos */
371
372 short map[ICAL_BY_NUM_PARTS];
373};
374
381
382static const struct expand_split_map_struct expand_map[] = {
383 /* M W YD MD D h m s P */
384 {ICAL_SECONDLY_RECURRENCE, {1, 3, 1, 1, 1, 1, 1, 1, 1}},
385 {ICAL_MINUTELY_RECURRENCE, {1, 3, 1, 1, 1, 1, 1, 2, 1}},
386 {ICAL_HOURLY_RECURRENCE, {1, 3, 1, 1, 1, 1, 2, 2, 1}},
387 {ICAL_DAILY_RECURRENCE, {1, 3, 3, 1, 1, 2, 2, 2, 1}},
388 {ICAL_WEEKLY_RECURRENCE, {1, 3, 3, 3, 2, 2, 2, 2, 1}},
389 {ICAL_MONTHLY_RECURRENCE, {1, 3, 3, 2, 2, 2, 2, 2, 1}},
390 {ICAL_YEARLY_RECURRENCE, {2, 2, 2, 2, 2, 2, 2, 2, 1}},
391 {ICAL_NO_RECURRENCE, {0, 0, 0, 0, 0, 0, 0, 0, 0}}};
392
393static const struct recur_map {
394 const char *str;
395 int size;
396 int min;
397 int isTime;
398} recur_map[] = {
399 {"BYMONTH", ICAL_BY_MONTH_SIZE, 1, 0},
400 {"BYWEEKNO", ICAL_BY_WEEKNO_SIZE, -1, 0},
401 {"BYYEARDAY", ICAL_BY_YEARDAY_SIZE, -1, 0},
402 {"BYMONTHDAY", ICAL_BY_MONTHDAY_SIZE, -1, 0},
403 {"BYDAY", ICAL_BY_DAY_SIZE, 0, 0},
404 {"BYHOUR", ICAL_BY_HOUR_SIZE, 0, 1},
405 {"BYMINUTE", ICAL_BY_MINUTE_SIZE, 0, 1},
406 {"BYSECOND", ICAL_BY_SECOND_SIZE, 0, 1},
407 {"BYSETPOS", ICAL_BY_SETPOS_SIZE, -1, 0},
408};
409
410static const char *icalrecur_first_clause(struct icalrecur_parser *parser)
411{
412 char *idx;
413
414 parser->this_clause = parser->copy;
415
416 idx = strchr(parser->this_clause, ';');
417
418 if (idx == 0) {
419 parser->next_clause = 0;
420 return 0;
421 }
422
423 *idx = 0;
424 idx++;
425 parser->next_clause = idx;
426
427 return parser->this_clause;
428}
429
430static const char *icalrecur_next_clause(struct icalrecur_parser *parser)
431{
432 char *idx;
433
434 parser->this_clause = parser->next_clause;
435
436 if (parser->this_clause == 0) {
437 return 0;
438 }
439
440 idx = strchr(parser->this_clause, ';');
441
442 if (idx == 0) {
443 parser->next_clause = 0;
444 } else {
445 *idx = 0;
446 idx++;
447 parser->next_clause = idx;
448 }
449
450 return parser->this_clause;
451}
452
453static void icalrecur_clause_name_and_value(struct icalrecur_parser *parser,
454 char **name, char **value)
455{
456 char *idx;
457
458 *name = parser->this_clause;
459
460 idx = strchr(parser->this_clause, '=');
461
462 if (idx == 0) {
463 *name = 0;
464 *value = 0;
465 return;
466 }
467
468 *idx = 0;
469 idx++;
470 *value = idx;
471}
472
473/*
474 * We expect BYHOUR, BYMINUTE, and BYSECOND data to be sorted.
475 */
476static void sort_byrules(icalrecurrence_by_data *by)
477{
478 short *array = by->data;
479
480 int i, j;
481
482 for (i = 1; i < by->size; i++) {
483 for (j = i - 1; j >= 0 && array[j] > array[j + 1]; j--) {
484 short tmp = array[j + 1];
485
486 array[j + 1] = array[j];
487 array[j] = tmp;
488 }
489 }
490}
491
492/*
493 * Sort BYSETPOS list in ascending order of magnitude,
494 * with negatives after positives
495 */
496static void sort_bysetpos(icalrecurrence_by_data *by)
497{
499#define SIGN(A) ((A) < 0 ? -1 : 1)
501 short *array = by->data;
502
503 int i, j;
504
505 for (i = 1; i < by->size; i++) {
506 for (j = i - 1;
507 j >= 0 && ((SIGN(array[j]) == SIGN(array[j + 1]) && abs(array[j]) > abs(array[j + 1])) ||
508 (array[j] < 0 && array[j + 1] > 0));
509 j--) {
510 short tmp = array[j + 1];
511
512 array[j + 1] = array[j];
513 array[j] = tmp;
514 }
515 }
516#undef SIGN
517}
518
519/* returns < 0 if a parsing problem:
520 -2 if an RSCALE rule is encountered yet we don't RSCALE support enabled
521 -1 for all other parsing problems
522*/
523static int icalrecur_add_byrules(const struct icalrecur_parser *parser, icalrecurrence_by_data *by,
524 int min, int size, char *vals)
525{
526 char *t, *n;
527 int i = 0;
528 int max = size - (min == 0);
529
530 n = vals;
531
532 if (!icalrecur_resize_by(by, size)) {
533 return -1;
534 }
535
536 while (n != 0) {
537 if (i == size) {
538 return -1;
539 }
540
541 t = n;
542
543 n = strchr(t, ',');
544
545 if (n != 0) {
546 *n = 0;
547 n++;
548 }
549
550 int v = strtol(t, &t, 10);
551
552 /* Sanity check value */
553 if (v < 0) {
554 if (min >= 0 || v <= -max) {
555 return -1;
556 }
557 } else if (v > 0) {
558 if (v >= max) {
559 return -1;
560 }
561 } else if (min) {
562 return -1;
563 }
564
565 if (*t) {
566 /* Check for leap month suffix (RSCALE only) */
567 if (by == &parser->rt->by[ICAL_BY_MONTH] && strcmp(t, "L") == 0) {
568 /* The "L" suffix in a BYMONTH recur-rule-part
569 is encoded by setting a high-order bit */
570 v |= LEAP_MONTH;
571 } else {
572 return -1;
573 }
574 }
575
576 by->data[i++] = (short)v;
577 }
578
579 if (!icalrecur_resize_by(by, i)) {
580 return -1;
581 }
582
583 /* Sort time bylists.
584 * Date bylists do not require sorting because they are implemented
585 * differently (with a bitmask), and are not directly used to find
586 * the next occurrence.
587 */
588 if (by == &parser->rt->by[ICAL_BY_HOUR] ||
589 by == &parser->rt->by[ICAL_BY_MINUTE] ||
590 by == &parser->rt->by[ICAL_BY_SECOND]) {
591 sort_byrules(by);
592 }
593 /* BYSETPOS is sorted specially */
594 else if (by == &parser->rt->by[ICAL_BY_SET_POS]) {
595 sort_bysetpos(by);
596 }
597
598 return 0;
599}
600
601/*
602 * Days in the BYDAY rule are expected by the code to be sorted, and while
603 * this may be the common case, the RFC doesn't actually mandate it. This
604 * function sorts the days taking into account the first day of week.
605 */
606static void sort_bydayrules(struct icalrecur_parser *parser)
607{
608 icalrecurrence_by_data *by = &parser->rt->by[ICAL_BY_DAY];
609 short *array = by->data;
610
611 int week_start, i, j;
612
613 week_start = (int)parser->rt->week_start;
614
615 for (i = 0; i < by->size; i++) {
616 for (j = 0; j < i; j++) {
617 int one = (int)icalrecurrencetype_day_day_of_week(array[j]) - week_start;
618 if (one < 0) {
619 one += 7;
620 }
621 int two = (int)icalrecurrencetype_day_day_of_week(array[i]) - week_start;
622 if (two < 0) {
623 two += 7;
624 }
625
626 if (one > two) {
627 short tmp = array[j];
628
629 array[j] = array[i];
630 array[i] = tmp;
631 }
632 }
633 }
634}
635
636static int icalrecur_add_bydayrules(struct icalrecur_parser *parser,
637 const char *vals)
638{
639 char *t, *n;
640 icalrecurrence_by_data *by = &parser->rt->by[ICAL_BY_DAY];
641
642 char *vals_copy;
643 int idx = 0;
644
645 if (!icalrecur_resize_by(by, ICAL_BY_DAY_SIZE)) {
646 return -1;
647 }
648
649 vals_copy = icalmemory_strdup(vals);
650 n = vals_copy;
651
652 while (n != 0) {
653 int sign = 1;
654 signed char weekno; /* note: Novell/Groupwise sends BYDAY=255SU,
655 so we fit in a signed char to get -1 SU for last Sun */
657
658 if (idx >= by->size) {
659 icalmemory_free_buffer(vals_copy);
660 return -1;
661 }
662
663 t = n;
664
665 n = strchr(t, ',');
666
667 if (n != 0) {
668 *n = 0;
669 n++;
670 }
671
672 /* Get Optional weekno */
673 weekno = (signed char)strtol(t, &t, 10);
674 if (weekno < 0) {
675 weekno = -weekno;
676 sign = -1;
677 }
678
679 /* Outlook/Exchange generate "BYDAY=MO, FR" and "BYDAY=2 TH".
680 * Cope with that.
681 */
682 if (*t == ' ') {
683 t++;
684 }
685
687
688 /* Sanity check value */
689 if (wd == ICAL_NO_WEEKDAY || weekno >= ICAL_BY_WEEKNO_SIZE) {
690 icalmemory_free_buffer(vals_copy);
691 return -1;
692 }
693
694 by->data[idx++] = icalrecurrencetype_encode_day(wd, sign * weekno);
695 }
696
697 icalmemory_free_buffer(vals_copy);
698
699 if (!icalrecur_resize_by(by, idx)) {
700 return -1;
701 }
702
703 sort_bydayrules(parser);
704
705 return 0;
706}
707
709{
710 struct icalrecurrencetype *rule;
711
712 rule = (struct icalrecurrencetype *)icalmemory_new_buffer(sizeof(*rule));
713
714 if (!rule) {
715 return NULL;
716 }
717
718 memset(rule, 0, sizeof(*rule));
719 rule->refcount = 1;
720 icalrecurrencetype_clear(rule);
721
722 return rule;
723}
724
725static void icalrecurrencetype_free(struct icalrecurrencetype *recur, int free_self)
726{
728#define SAFEFREE(p) \
729 if (p) { \
730 icalmemory_free_buffer(p); \
731 (p) = 0; \
732 }
734
735 SAFEFREE(recur->rscale);
736 for (int i = 0; i < ICAL_BY_NUM_PARTS; i++) {
737 SAFEFREE(recur->by[i].data);
738 }
739
740#undef SAFEFREE
741
742 if (free_self) {
744 }
745}
746
748{
749 icalerror_check_arg_rv((recur != NULL), "recur");
750 icalerror_check_arg_rv((recur->refcount > 0), "recur->refcount > 0");
751
752 recur->refcount++;
753}
754
756{
757 icalerror_check_arg_rv((recur != NULL), "recur");
758 icalerror_check_arg_rv((recur->refcount > 0), "recur->refcount > 0");
759
760 recur->refcount--;
761
762 if (recur->refcount != 0) {
763 return;
764 }
765
766 icalrecurrencetype_free(recur, 1);
767}
768
769static void *icalrecur_memdup(void *p, size_t size, int *error)
770{
771 if ((p == NULL) || (size == 0)) {
772 return p;
773 }
774
775 void *newp = icalmemory_new_buffer(size);
776 if (newp) {
777 memcpy(newp, p, size);
778 } else {
779 *error = 1;
780 }
781
782 return newp;
783}
784
785static icalrecurrence_by_data icalrecur_by_dup(icalrecurrence_by_data *by, int *error)
786{
787 icalrecurrence_by_data newby = {0, 0};
788
789 newby.data = icalrecur_memdup(by->data, (size_t)by->size * sizeof(by->data[0]), error);
790 if (newby.data) {
791 newby.size = by->size;
792 }
793
794 return newby;
795}
796
798{
799 struct icalrecurrencetype *res;
800 int error = 0;
801
802 icalerror_check_arg_rz((recur != NULL), "recur");
803
805 if (!res) {
806 return NULL;
807 }
808
809 memcpy(res, recur, sizeof(*res));
810
811 res->refcount = 1;
812
813 if (res->rscale) {
814 res->rscale = icalmemory_strdup(res->rscale);
815 if (!res->rscale) {
816 error = 1;
817 }
818 }
819
820 for (int i = 0; i < ICAL_BY_NUM_PARTS; i++) {
821 icalrecurrence_by_data *src_by = &recur->by[i];
822 icalrecurrence_by_data *dst_by = &res->by[i];
823 *dst_by = icalrecur_by_dup(src_by, &error);
824 }
825
826 if (error) {
827 icalrecurrencetype_free(res, 1);
828 return NULL;
829 }
830
831 return res;
832}
833
835{
836 struct icalrecur_parser parser = {0};
838
839 icalerror_check_arg_re(str != 0, "str", 0);
840
841 parser.rt = icalrecurrencetype_new();
842 if (!parser.rt) {
843 return NULL;
844 }
845
846 /* Set up the parser struct */
847 parser.rule = str;
848 parser.copy = icalmemory_strdup(parser.rule);
849 parser.this_clause = parser.copy;
850
851 if (parser.copy == 0) {
853 icalrecurrencetype_unref(parser.rt);
854 return NULL;
855 }
856
857 /* Loop through all of the clauses */
858 for (icalrecur_first_clause(&parser);
859 parser.this_clause != 0; icalrecur_next_clause(&parser)) {
860 char *name, *value;
861 int r = 0;
862
863 icalrecur_clause_name_and_value(&parser, &name, &value);
864
865 if (name == 0) {
866 if (strlen(parser.this_clause) > 0) {
867 r = -1;
868 } else {
869 /* Hit an empty name/value pair,
870 but we're also at the end of the string.
871 This was probably a trailing semicolon with no data
872 (e.g. "FREQ=WEEKLY;INTERVAL=1;BYDAY=MO;")
873 */
874 break;
875 }
876 } else if (strcasecmp(name, "FREQ") == 0) {
877 if (parser.rt->freq != ICAL_NO_RECURRENCE) {
878 /* Don't allow multiple FREQs */
879 r = -1;
880 } else {
881 parser.rt->freq = icalrecur_string_to_freq(value);
882 if (parser.rt->freq == ICAL_NO_RECURRENCE) {
883 r = -1;
884 }
885 }
886 } else if (strcasecmp(name, "RSCALE") == 0) {
887 if (parser.rt->rscale != NULL) {
888 /* Don't allow multiple RSCALEs */
889 r = -1;
890 } else {
891 parser.rt->rscale = icalmemory_strdup(value);
892 }
893 } else if (strcasecmp(name, "SKIP") == 0) {
894 if (parser.rt->skip != ICAL_SKIP_OMIT) {
895 /* Don't allow multiple SKIPs */
896 r = -1;
897 } else {
898 parser.rt->skip = icalrecur_string_to_skip(value);
899 if (parser.rt->skip == ICAL_SKIP_UNDEFINED) {
900 r = -1;
901 }
902 }
903 } else if (strcasecmp(name, "COUNT") == 0) {
904 if (parser.rt->count > 0 || !icaltime_is_null_time(parser.rt->until)) {
905 /* Don't allow multiple COUNTs, or both COUNT and UNTIL */
906 r = -1;
907 } else {
908 parser.rt->count = atoi(value);
909 /* don't allow count to be less than 1 */
910 if (parser.rt->count < 1) {
911 r = -1;
912 }
913 }
914 } else if (strcasecmp(name, "UNTIL") == 0) {
915 if (parser.rt->count > 0 || !icaltime_is_null_time(parser.rt->until)) {
916 /* Don't allow multiple COUNTs, or both COUNT and UNTIL */
917 r = -1;
918 } else {
919 parser.rt->until = icaltime_from_string(value);
920 if (icaltime_is_null_time(parser.rt->until)) {
921 r = -1;
922 }
923 }
924 } else if (strcasecmp(name, "INTERVAL") == 0) {
925 if (parser.rt->interval > 1) {
926 /* Don't allow multiple INTERVALs */
927 r = -1;
928 } else {
929 parser.rt->interval = (short)atoi(value);
930 /* don't allow an interval to be less than 1
931 (RFC specifies an interval must be a positive integer) */
932 if (parser.rt->interval < 1) {
933 r = -1;
934 }
935 }
936 } else if (strcasecmp(name, "WKST") == 0) {
937 if (parser.rt->week_start != ICAL_MONDAY_WEEKDAY) {
938 /* Don't allow multiple WKSTs */
939 r = -1;
940 } else {
941 parser.rt->week_start = icalrecur_string_to_weekday(value);
942 if (parser.rt->week_start == ICAL_NO_WEEKDAY) {
943 r = -1;
944 } else {
945 sort_bydayrules(&parser);
946 }
947 }
948 } else if (strncasecmp(name, "BY", 2) == 0) {
949 r = -1;
950
951 for (byrule = 0; byrule < ICAL_BY_NUM_PARTS; ++byrule) {
952 if (strcasecmp(name + 2, recur_map[byrule].str + 2) == 0) {
953 if (byrule == ICAL_BY_DAY) {
954 r = icalrecur_add_bydayrules(&parser, value);
955 } else {
956 icalrecurrence_by_data *by = &parser.rt->by[byrule];
957 r = icalrecur_add_byrules(&parser, by,
958 recur_map[byrule].min,
959 recur_map[byrule].size,
960 value);
961 }
962 break;
963 }
964 }
965 } else {
966 r = -1;
967 }
968
969 if (r) {
970 /* Note: silently ignore when we have a leap month, yet don't have RSCALE support.
971 The magic value "-2" indicates when that happens.
972 */
973 if (r != -2) {
975 }
976 icalrecurrencetype_clear(parser.rt);
977 break;
978 }
979 }
980
981 for (byrule = 0; byrule < ICAL_BY_NUM_PARTS; ++byrule) {
982 icalrecurrence_by_data *by = &parser.rt->by[byrule];
983
984 if (by->size > 0 &&
985 expand_map[parser.rt->freq].map[byrule] == ILLEGAL) {
986 ical_invalid_rrule_handling rruleHandlingSetting =
988
989 if (rruleHandlingSetting == ICAL_RRULE_TREAT_AS_ERROR) {
991 icalrecurrencetype_clear(parser.rt);
992 break;
993 } else {
994 icalrecur_free_by(by);
995 }
996 }
997 }
998
999 icalmemory_free_buffer(parser.copy);
1000
1001 if (parser.rt->freq == ICAL_NO_RECURRENCE) {
1002 icalrecurrencetype_unref(parser.rt);
1003 parser.rt = NULL;
1004 }
1005
1006 return parser.rt;
1007}
1008
1010{
1011 char *buf;
1012
1013 buf = icalrecurrencetype_as_string_r(recur);
1015 return buf;
1016}
1017
1019{
1020 char *str;
1021 char *str_p;
1022 size_t buf_sz = 200;
1023 char temp[20] = {0};
1024 int i, j;
1025
1026 if (recur == 0 || recur->freq == ICAL_NO_RECURRENCE) {
1027 return 0;
1028 }
1029
1030 str = (char *)icalmemory_new_buffer(buf_sz);
1031 str_p = str;
1032
1033 if (recur->rscale != 0) {
1034 icalmemory_append_string(&str, &str_p, &buf_sz, "RSCALE=");
1035 icalmemory_append_string(&str, &str_p, &buf_sz, recur->rscale);
1036
1037 /* Omit is the default, so no need to write that out */
1038 if (recur->skip != ICAL_SKIP_OMIT) {
1039 const char *skipstr = icalrecur_skip_to_string(recur->skip);
1040 icalmemory_append_string(&str, &str_p, &buf_sz, ";SKIP=");
1041 icalmemory_append_string(&str, &str_p, &buf_sz, skipstr);
1042 }
1043 icalmemory_append_char(&str, &str_p, &buf_sz, ';');
1044 }
1045
1046 icalmemory_append_string(&str, &str_p, &buf_sz, "FREQ=");
1047 icalmemory_append_string(&str, &str_p, &buf_sz,
1049
1050 /* 1 is the default, so no need to write that out */
1051 if (recur->interval != 1) {
1052 snprintf(temp, sizeof(temp), "%d", recur->interval);
1053 icalmemory_append_string(&str, &str_p, &buf_sz, ";INTERVAL=");
1054 icalmemory_append_string(&str, &str_p, &buf_sz, temp);
1055 }
1056
1057 /* Monday is the default, so no need to write that out */
1058 if (recur->week_start != ICAL_MONDAY_WEEKDAY &&
1059 recur->week_start != ICAL_NO_WEEKDAY) {
1060 int dow = (int)icalrecurrencetype_day_day_of_week(recur->week_start);
1061 const char *daystr = icalrecur_weekday_to_string((enum icalrecurrencetype_weekday)dow);
1062 icalmemory_append_string(&str, &str_p, &buf_sz, ";WKST=");
1063 icalmemory_append_string(&str, &str_p, &buf_sz, daystr);
1064 }
1065
1066 for (j = 0; j < ICAL_BY_NUM_PARTS; j++) {
1067 const icalrecurrence_by_data *by = &recur->by[j];
1068
1069 /* Skip unused arrays */
1070 if (by->size > 0) {
1071 icalmemory_append_char(&str, &str_p, &buf_sz, ';');
1072 icalmemory_append_string(&str, &str_p, &buf_sz, recur_map[j].str);
1073 icalmemory_append_char(&str, &str_p, &buf_sz, '=');
1074
1075 int limit = recur_map[j].size - 1;
1076 for (i = 0; i < limit && i < by->size; i++) {
1077 if (j == ICAL_BY_DAY) {
1078 int pos = icalrecurrencetype_day_position(by->data[i]);
1079 int dow = (int)icalrecurrencetype_day_day_of_week(by->data[i]);
1080 const char *daystr = icalrecur_weekday_to_string((enum icalrecurrencetype_weekday)dow);
1081
1082 if (pos == 0) {
1083 icalmemory_append_string(&str, &str_p, &buf_sz, daystr);
1084 } else {
1085 snprintf(temp, sizeof(temp), "%d%s", pos, daystr);
1086 icalmemory_append_string(&str, &str_p, &buf_sz, temp);
1087 }
1088
1089 } else if (j == ICAL_BY_MONTH &&
1090 icalrecurrencetype_month_is_leap(by->data[i])) {
1091 snprintf(temp, sizeof(temp), "%dL",
1092 icalrecurrencetype_month_month(by->data[i]));
1093 icalmemory_append_string(&str, &str_p, &buf_sz, temp);
1094 } else {
1095 snprintf(temp, sizeof(temp), "%d", by->data[i]);
1096 icalmemory_append_string(&str, &str_p, &buf_sz, temp);
1097 }
1098
1099 if ((i + 1) < limit && by->size > i + 1) {
1100 icalmemory_append_char(&str, &str_p, &buf_sz, ',');
1101 }
1102 }
1103 }
1104 }
1105
1106 if (recur->until.year != 0) {
1107 temp[0] = 0;
1108 if (recur->until.is_date) {
1109 print_date_to_string(temp, &(recur->until));
1110 } else {
1111 print_datetime_to_string(temp, &(recur->until));
1112 }
1113
1114 icalmemory_append_string(&str, &str_p, &buf_sz, ";UNTIL=");
1115 icalmemory_append_string(&str, &str_p, &buf_sz, temp);
1116 }
1117
1118 else if (recur->count != 0) {
1119 snprintf(temp, sizeof(temp), "%d", recur->count);
1120 icalmemory_append_string(&str, &str_p, &buf_sz, ";COUNT=");
1121 icalmemory_append_string(&str, &str_p, &buf_sz, temp);
1122 }
1123
1124 return str;
1125}
1126
1127/************************* occurrence iteration routines ******************/
1128
1130/* Number of bits in an unsigned long */
1131#define BITS_PER_LONG ((unsigned short)(8 * sizeof(unsigned long)))
1132
1133/* Number of longs in mask of n bits */
1134#define LONGS_PER_BITS(n) (((n) + BITS_PER_LONG - 1) / BITS_PER_LONG)
1135
1136#define ICAL_YEARDAYS_MASK_SIZE (ICAL_BY_YEARDAY_SIZE + 7)
1137#define ICAL_YEARDAYS_MASK_OFFSET 4
1139
1140typedef struct icalrecurrence_iterator_by_data {
1141 icalrecurrence_by_data by;
1142 short index;
1143 short orig_data;
1144
1145 // Static buffer for BY values that need to be modified by the iterator, so we don't modify the rule.
1146 // We have one value for each BY rule.
1147 short buffer_value;
1148} icalrecurrence_iterator_by_data;
1149
1150struct icalrecur_iterator_impl {
1151 struct icaltimetype dtstart; /* copy of DTSTART: to fill in defaults */
1152 struct icalrecurrencetype *rule; /* reference to RRULE */
1153
1154 struct icaltimetype rstart; /* DTSTART in RSCALE */
1155 struct icaltimetype istart; /* Gregorian start time for iterator */
1156 struct icaltimetype iend; /* Gregorian end time for iterator */
1157 struct icaltimetype last; /* last time returned from iterator */
1158 int32_t occurrence_no; /* number of steps made on the iterator */
1159
1160 int32_t set_pos; /* our position in the recurrence set */
1161 int32_t recurrence_set_size; /* the size of the recurrence set */
1162 short sp_idxp, sp_idxn; /* positive and negative BYSETPOS indices */
1163 short sp_pmax; /* the last index of the BYSETPOS array with a positive value */
1164
1165#if defined(HAVE_LIBICU)
1166 UCalendar *greg; /* Gregorian calendar */
1167 UCalendar *rscale; /* RSCALE calendar */
1168#endif
1169
1170 struct icaltimetype period_start; /* Start date of monthly/yearly period */
1171
1172 /* days[] is a bitmask of year days. A bit value of 1 marks an occurrence.
1173 The size of the bitmask is 7 + max days in year to accommodate full first
1174 and last weeks of the year: up to 3 days in previous year and
1175 up to 4 days in following year. As a result, the days are offset by 4:
1176 bit 0 is day -3 (3rd last day of previous year) and bit 4 is day 1
1177 of the current year. Days in the following year use higher day numbers,
1178 e.g. day 367 is day 1 or 2 of following year depending on whether the
1179 current year is a leap year.
1180
1181 days_index is the day of year of the next occurrence,
1182 with a range of -3 to 4 + days in year.
1183 */
1184 unsigned long days[LONGS_PER_BITS(ICAL_YEARDAYS_MASK_SIZE)];
1185 short days_index;
1186
1188 icalrecurrence_iterator_by_data bydata[ICAL_BY_NUM_PARTS];
1189};
1190
1191static void daysmask_clearall(unsigned long mask[])
1192{
1193 memset(mask, 0,
1194 sizeof(unsigned long) * LONGS_PER_BITS(ICAL_YEARDAYS_MASK_SIZE));
1195}
1196
1197static void daysmask_set_range(unsigned long days[], int fromDayIncl, int untilDayExcl, int v)
1198{
1199 int fromBitIdx = fromDayIncl + ICAL_YEARDAYS_MASK_OFFSET;
1200 int untilBitIdx = untilDayExcl + ICAL_YEARDAYS_MASK_OFFSET;
1201
1202 for (int word_idx = fromBitIdx / BITS_PER_LONG;
1203 word_idx < (int)((untilBitIdx + BITS_PER_LONG - 1) / BITS_PER_LONG);
1204 word_idx++) {
1205 int lowerBitIdxIncl = (fromBitIdx <= (int)(word_idx * BITS_PER_LONG))
1206 ? 0
1207 : (fromBitIdx - (int)(word_idx * BITS_PER_LONG));
1208 int upperBitIdxExcl = (untilBitIdx >= (int)((word_idx + 1) * BITS_PER_LONG))
1209 ? (int)BITS_PER_LONG
1210 : (int)(untilBitIdx - (int)(word_idx * BITS_PER_LONG));
1211
1212 unsigned long mask = (unsigned long)-1;
1213 if (lowerBitIdxIncl > 0) {
1214 mask &= ((unsigned long)-1) << lowerBitIdxIncl;
1215 }
1216 if (upperBitIdxExcl < (int)BITS_PER_LONG) {
1217 mask &= ((unsigned long)-1) >> (BITS_PER_LONG - upperBitIdxExcl);
1218 }
1219
1220 if (v) {
1221 days[word_idx] |= mask;
1222 } else {
1223 days[word_idx] &= ~mask;
1224 }
1225 }
1226}
1227
1228static int daysmask_setbit(unsigned long mask[], short n, int v)
1229{
1230 int prev;
1231
1232 n += ICAL_YEARDAYS_MASK_OFFSET;
1233
1234 if (n >= 0) {
1235 prev = (mask[n / BITS_PER_LONG] & (1UL << (n % BITS_PER_LONG))) ? 1 : 0;
1236 } else {
1237 prev = (mask[n / BITS_PER_LONG] & (1UL >> (-n % BITS_PER_LONG))) ? 1 : 0;
1238 }
1239 if (v != prev) {
1240 if (v) {
1241 if (n >= 0) {
1242 mask[n / BITS_PER_LONG] |= (1UL << (n % BITS_PER_LONG));
1243 } else {
1244 mask[n / BITS_PER_LONG] |= (1UL >> (-n % BITS_PER_LONG));
1245 }
1246 } else {
1247 if (n >= 0) {
1248 mask[n / BITS_PER_LONG] &= ~(1UL << (n % BITS_PER_LONG));
1249 } else {
1250 mask[n / BITS_PER_LONG] &= ~(1UL >> (-n % BITS_PER_LONG));
1251 }
1252 }
1253 }
1254
1255 return prev;
1256}
1257
1258static unsigned long daysmask_getbit(const unsigned long mask[], short n)
1259{
1260 n += ICAL_YEARDAYS_MASK_OFFSET;
1261 return (mask[n / BITS_PER_LONG] >> (n % BITS_PER_LONG)) & 1;
1262}
1263
1264static bool has_by_data(icalrecur_iterator *impl, icalrecurrencetype_byrule byrule)
1265{
1266 return (impl->bydata[byrule].orig_data == 1);
1267}
1268
1269static void recur_iterator_set_static_single_by_value(icalrecur_iterator *impl,
1270 icalrecurrencetype_byrule byrule, short value)
1271{
1272 icalrecurrence_iterator_by_data *by = &impl->bydata[byrule];
1273 by->by.size = 1;
1274 by->by.data = &by->buffer_value;
1275 by->by.data[0] = value;
1276}
1277
1278static void setup_defaults(icalrecur_iterator *impl,
1279 icalrecurrencetype_byrule byrule, int deftime)
1280{
1281 icalrecurrencetype_frequency freq = impl->rule->freq;
1282
1283 if (impl->dtstart.is_date && recur_map[byrule].isTime) {
1284 // The BYSECOND, BYMINUTE and BYHOUR rule parts MUST NOT be specified
1285 // when the associated "DTSTART" property has a DATE value type.
1286 // These rule parts MUST be ignored in RECUR value that violate the
1287 // above requirement (e.g., generated by applications that pre-date
1288 // this revision of iCalendar).
1289 recur_iterator_set_static_single_by_value(impl, byrule, 0);
1290 } else if (expand_map[freq].map[byrule] == EXPAND) {
1291 /* Re-write the BY rule arrays with data from the DTSTART time so
1292 we don't have to explicitly deal with DTSTART */
1293 if (impl->bydata[byrule].by.size == 0) {
1294 recur_iterator_set_static_single_by_value(impl, byrule, (short)deftime);
1295 }
1296 }
1297}
1298
1301static int weeks_in_year(int year)
1302{
1303 /* Long years occur when year starts on Thu or leap year starts on Wed */
1305 int is_long = (dow == 5 || (dow == 4 && icaltime_is_leap_year(year)));
1306
1307 return (52 + is_long);
1308}
1309
1311static int __greg_month_diff(icaltimetype a, icaltimetype b)
1312{
1313 return (12 * (b.year - a.year) + (b.month - a.month));
1314}
1315
1316static void __get_start_time(icalrecur_iterator *impl, icaltimetype date,
1317 int *hour, int *minute, int *second)
1318{
1319 icalrecurrencetype_frequency freq = impl->rule->freq;
1320
1321 if (freq == ICAL_HOURLY_RECURRENCE) {
1322 *hour = date.hour;
1323 } else if (has_by_data(impl, ICAL_BY_HOUR)) {
1324 *hour = impl->bydata[ICAL_BY_HOUR].by.data[0];
1325 } else {
1326 *hour = impl->rstart.hour;
1327 }
1328
1329 if (freq == ICAL_MINUTELY_RECURRENCE) {
1330 *minute = date.minute;
1331 } else if (has_by_data(impl, ICAL_BY_MINUTE)) {
1332 *minute = impl->bydata[ICAL_BY_MINUTE].by.data[0];
1333 } else {
1334 *minute = impl->rstart.minute;
1335 }
1336
1337 if (freq == ICAL_SECONDLY_RECURRENCE) {
1338 *second = date.second;
1339 } else if (has_by_data(impl, ICAL_BY_SECOND)) {
1340 *second = impl->bydata[ICAL_BY_SECOND].by.data[0];
1341 } else {
1342 *second = impl->rstart.second;
1343 }
1344}
1345
1346static int __day_diff(icalrecur_iterator *impl, icaltimetype a, icaltimetype b);
1347
1348#if defined(HAVE_LIBICU)
1349/*
1350 * Callbacks for recurrence rules with RSCALE support (using ICU)
1351 *
1352 * References:
1353 * - tools.ietf.org/html/rfc7529
1354 * - en.wikipedia.org/wiki/Intercalation_%28timekeeping%29
1355 * - icu-project.org/apiref/icu4c/ucal_8h.html
1356 * - cldr.unicode.org/development/development-process/design-proposals/chinese-calendar-support
1357 * - cldr.unicode.org/development/development-process/design-proposals/islamic-calendar-types
1358 *
1359 * ICU Notes:
1360 * - Months are 0-based
1361 * - Leap months in Chinese and Hebrew calendars are handled differently
1362 */
1363
1365{
1366 UErrorCode status = U_ZERO_ERROR;
1367 UEnumeration *en;
1368 icalarray *calendars;
1369 const char *cal;
1370
1371 calendars = icalarray_new(sizeof(const char **), 20);
1372
1373 en = ucal_getKeywordValuesForLocale("calendar", "", false, &status);
1374 while ((cal = uenum_next(en, NULL, &status))) {
1375 cal = icalmemory_tmp_copy(cal);
1376 icalarray_append(calendars, (const void *)&cal);
1377 }
1378 uenum_close(en);
1379
1380 return calendars;
1381}
1382
1383static void set_second(icalrecur_iterator *impl, int second)
1384{
1385 ucal_set(impl->rscale, UCAL_SECOND, (int32_t)second);
1386}
1387
1388static void set_minute(icalrecur_iterator *impl, int minute)
1389{
1390 ucal_set(impl->rscale, UCAL_MINUTE, (int32_t)minute);
1391}
1392
1393static void set_hour(icalrecur_iterator *impl, int hour)
1394{
1395 ucal_set(impl->rscale, UCAL_HOUR_OF_DAY, (int32_t)hour);
1396}
1397
1398static void __set_month(icalrecur_iterator *impl, int month)
1399{
1400 bool is_leap_month = icalrecurrencetype_month_is_leap(month);
1401
1402 month = icalrecurrencetype_month_month(month) - 1; /* UCal is 0-based */
1403
1404 ucal_set(impl->rscale, UCAL_MONTH, (int32_t)month);
1405 if (is_leap_month) {
1406 ucal_set(impl->rscale, UCAL_IS_LEAP_MONTH, 1);
1407 }
1408}
1409
1410static int set_month(icalrecur_iterator *impl, int month)
1411{
1412 UErrorCode status = U_ZERO_ERROR;
1413 int actual_month;
1414
1415 __set_month(impl, month);
1416
1417 ucal_set(impl->rscale, UCAL_DAY_OF_MONTH, (int32_t)1);
1418
1419 actual_month = 1 + /* UCal is 0-based */
1420 (int)ucal_get(impl->rscale, UCAL_MONTH, &status);
1421
1422 if (ucal_get(impl->rscale, UCAL_IS_LEAP_MONTH, &status)) {
1423 actual_month |= LEAP_MONTH;
1424 }
1425
1426 if (actual_month != month) {
1427 switch (impl->rule->skip) {
1428 default:
1429 /* Should never get here! */
1430
1431 case ICAL_SKIP_OMIT:
1432 /* Invalid month */
1433 return 0;
1434
1435 case ICAL_SKIP_BACKWARD:
1436 /* Skip back to next valid month */
1437 ucal_add(impl->rscale, UCAL_MONTH, (int32_t)-1, &status);
1438 break;
1439
1440 case ICAL_SKIP_FORWARD:
1441 /* UCal skips forward to valid month by default */
1442 break;
1443 }
1444 }
1445
1446 return (1 + /* UCal is 0-based */
1447 (int)ucal_get(impl->rscale, UCAL_MONTH, &status));
1448}
1449
1450static int get_months_in_year(icalrecur_iterator *impl, int year)
1451{
1452 UErrorCode status = U_ZERO_ERROR;
1453
1454 if (year) {
1455 ucal_set(impl->rscale, UCAL_YEAR, (int32_t)year);
1456 }
1457
1458 return (1 + /* UCal is 0-based */
1459 (int)ucal_getLimit(impl->rscale, UCAL_MONTH,
1460 UCAL_ACTUAL_MAXIMUM, &status));
1461}
1462
1463static int get_days_in_year(icalrecur_iterator *impl, int year)
1464{
1465 UErrorCode status = U_ZERO_ERROR;
1466
1467 if (year) {
1468 ucal_set(impl->rscale, UCAL_YEAR, (int32_t)year);
1469 }
1470
1471 return (int)ucal_getLimit(impl->rscale, UCAL_DAY_OF_YEAR,
1472 UCAL_ACTUAL_MAXIMUM, &status);
1473}
1474
1475static void set_day_of_year(icalrecur_iterator *impl, int doy)
1476{
1477 if (doy < 1) {
1478 doy += get_days_in_year(impl, 0);
1479 }
1480
1481 ucal_set(impl->rscale, UCAL_DAY_OF_YEAR, (int32_t)doy);
1482}
1483
1484static int get_start_of_week(icalrecur_iterator *impl)
1485{
1486 UErrorCode status = U_ZERO_ERROR;
1487 int doy, dow;
1488
1489 doy = (int)ucal_get(impl->rscale, UCAL_DAY_OF_YEAR, &status);
1490 dow = (int)ucal_get(impl->rscale, UCAL_DAY_OF_WEEK, &status);
1491 dow -= (int)impl->rule->week_start;
1492 if (dow < 0) {
1493 dow += 7;
1494 }
1495
1496 return (doy - dow);
1497}
1498
1499static int get_day_of_week(icalrecur_iterator *impl)
1500{
1501 UErrorCode status = U_ZERO_ERROR;
1502
1503 return (int)ucal_get(impl->rscale, UCAL_DAY_OF_WEEK, &status);
1504}
1505
1506static int get_week_number(icalrecur_iterator *impl, struct icaltimetype tt)
1507{
1508 UErrorCode status = U_ZERO_ERROR;
1509 UDate last_millis;
1510 int month, weekno;
1511
1512 /* Save existing rscale date */
1513 last_millis = ucal_getMillis(impl->rscale, &status);
1514
1515 month = icalrecurrencetype_month_month(tt.month) - 1; /* UCal is 0-based */
1516 ucal_setDate(impl->rscale,
1517 (int32_t)tt.year, (int32_t)month, (int32_t)tt.day, &status);
1519 ucal_set(impl->rscale, UCAL_IS_LEAP_MONTH, 1);
1520 }
1521
1522 weekno = (int)ucal_get(impl->rscale, UCAL_WEEK_OF_YEAR, &status);
1523
1524 /* Restore saved rscale date */
1525 ucal_setMillis(impl->rscale, last_millis, &status);
1526
1527 return weekno;
1528}
1529
1530static int get_days_in_month(icalrecur_iterator *impl, int month, int year)
1531{
1532 UErrorCode status = U_ZERO_ERROR;
1533
1534 ucal_set(impl->rscale, UCAL_YEAR, (int32_t)year);
1535
1536 if (!month) {
1537 month = impl->rstart.month;
1538 }
1539 __set_month(impl, month);
1540
1541 return (int)ucal_getLimit(impl->rscale,
1542 UCAL_DAY_OF_MONTH, UCAL_ACTUAL_MAXIMUM, &status);
1543}
1544
1545static void prepare_rscale_adjusted(icalrecur_iterator *impl,
1546 int year, int month, int day, UErrorCode *status)
1547{
1548 ucal_set(impl->rscale, UCAL_YEAR, (int32_t)year);
1549
1550 if (!month) {
1551 month = impl->rstart.month;
1552 }
1553 __set_month(impl, month);
1554
1555 if (!day) {
1556 day = impl->rstart.day;
1557 } else if (day < 0) {
1558 day += 1 + (int)ucal_getLimit(impl->rscale, UCAL_DAY_OF_MONTH,
1559 UCAL_ACTUAL_MAXIMUM, status);
1560 }
1561 ucal_set(impl->rscale, UCAL_DAY_OF_MONTH, (int32_t)day);
1562}
1563
1564static int get_day_of_year(icalrecur_iterator *impl,
1565 int year, int month, int day)
1566{
1567 UErrorCode status = U_ZERO_ERROR;
1568 prepare_rscale_adjusted(impl, year, month, day, &status);
1569 return (int)ucal_get(impl->rscale, UCAL_DAY_OF_YEAR, &status);
1570}
1571
1572static int get_day_of_week_adjusted(icalrecur_iterator *impl,
1573 int year, int month, int day)
1574{
1575 UErrorCode status = U_ZERO_ERROR;
1576 prepare_rscale_adjusted(impl, year, month, day, &status);
1577 return (int)ucal_get(impl->rscale, UCAL_DAY_OF_WEEK, &status);
1578}
1579
1580static struct icaltimetype occurrence_as_icaltime(icalrecur_iterator *impl,
1581 int normalize)
1582{
1583 struct icaltimetype tt = impl->dtstart;
1584 UErrorCode status = U_ZERO_ERROR;
1585 UCalendar *cal = impl->rscale;
1586 int is_leap_month = 0;
1587
1588 if (normalize && (impl->rscale != impl->greg)) {
1589 /* Convert to Gregorian date */
1590 UDate millis = ucal_getMillis(impl->rscale, &status);
1591
1592 ucal_setMillis(impl->greg, millis, &status);
1593 cal = impl->greg;
1594 } else {
1595 is_leap_month =
1596 (int)ucal_get(impl->rscale, UCAL_IS_LEAP_MONTH, &status);
1597 }
1598
1599 tt.year = (int)ucal_get(cal, UCAL_YEAR, &status);
1600 tt.day = (int)ucal_get(cal, UCAL_DATE, &status);
1601 tt.month = 1 + /* UCal is 0-based */
1602 (int)ucal_get(cal, UCAL_MONTH, &status);
1603 if (is_leap_month) {
1604 tt.month |= LEAP_MONTH;
1605 }
1606
1607 if (!tt.is_date) {
1608 tt.hour = (int)ucal_get(cal, UCAL_HOUR_OF_DAY, &status);
1609 tt.minute = (int)ucal_get(cal, UCAL_MINUTE, &status);
1610 tt.second = (int)ucal_get(cal, UCAL_SECOND, &status);
1611 }
1612
1613 return tt;
1614}
1615
1616static struct icaltimetype __icaltime_from_day_of_year(icalrecur_iterator *impl,
1617 int day, int year, int *weekno)
1618{
1619 ucal_set(impl->rscale, UCAL_YEAR, (int32_t)year);
1620 if (day < 0) {
1621 day += get_days_in_year(impl, 0) + 1;
1622 }
1623
1624 ucal_set(impl->rscale, UCAL_DAY_OF_YEAR, (int32_t)day);
1625
1626 if (weekno) {
1627 UErrorCode status = U_ZERO_ERROR;
1628
1629 *weekno = (int)ucal_get(impl->rscale, UCAL_WEEK_OF_YEAR, &status);
1630 }
1631
1632 return occurrence_as_icaltime(impl, 0);
1633}
1634
1635static void increment_year(icalrecur_iterator *impl, int inc)
1636{
1637 UErrorCode status = U_ZERO_ERROR;
1638
1639 ucal_add(impl->rscale, UCAL_YEAR, (int32_t)inc, &status);
1640}
1641
1642static void __increment_month(icalrecur_iterator *impl, int inc)
1643{
1644 UErrorCode status = U_ZERO_ERROR;
1645
1646 ucal_add(impl->rscale, UCAL_MONTH, (int32_t)inc, &status);
1647}
1648
1649static void increment_monthday(icalrecur_iterator *impl, int inc)
1650{
1651 UErrorCode status = U_ZERO_ERROR;
1652
1653 ucal_add(impl->rscale, UCAL_DAY_OF_MONTH, (int32_t)inc, &status);
1654}
1655
1656static void increment_hour(icalrecur_iterator *impl, int inc)
1657{
1658 UErrorCode status = U_ZERO_ERROR;
1659
1660 ucal_add(impl->rscale, UCAL_HOUR_OF_DAY, (int32_t)inc, &status);
1661}
1662
1663static void increment_minute(icalrecur_iterator *impl, int inc)
1664{
1665 UErrorCode status = U_ZERO_ERROR;
1666
1667 ucal_add(impl->rscale, UCAL_MINUTE, (int32_t)inc, &status);
1668}
1669
1670static void increment_second(icalrecur_iterator *impl, int inc)
1671{
1672 UErrorCode status = U_ZERO_ERROR;
1673
1674 ucal_add(impl->rscale, UCAL_SECOND, (int32_t)inc, &status);
1675}
1676
1677static bool validate_byrule(icalrecur_iterator *impl,
1678 icalrecurrencetype_byrule byrule, UCalendarDateFields field,
1679 short (*decode_val)(short *, bool),
1680 bool decode_flags)
1681{
1682 if (has_by_data(impl, byrule)) {
1683 UErrorCode status = U_ZERO_ERROR;
1684 const icalrecurrence_by_data *by_ptr = &impl->bydata[byrule].by;
1685 short max =
1686 (short)ucal_getLimit(impl->rscale, field, UCAL_MAXIMUM, &status);
1687 short idx;
1688
1689 for (idx = 0; idx < by_ptr->size; idx++) {
1690 short val = decode_val ? decode_val(&by_ptr->data[idx], decode_flags) : by_ptr->data[idx];
1691
1692 if (abs(val) > max) {
1693 return false;
1694 }
1695 }
1696 }
1697
1698 return true;
1699}
1700
1701static short decode_month(short *month, bool is_hebrew)
1702{
1703 if (is_hebrew && *month > 5) { /* 5L == 0x1005 */
1704 /* Hebrew calendar:
1705 Translate RSCALE months to ICU (numbered 1-13, where 6 is leap).
1706 Hence, 5L maps to 6 and 6-12 map to 7-13. */
1708 }
1709
1710 return icalrecurrencetype_month_month(*month) - 1; /* UCal is 0-based */
1711}
1712
1713/* cppcheck-suppress constParameterCallback */
1714static short decode_day(short *day, bool flags) //NOLINT(readability-non-const-parameter)
1715{
1716 _unused(flags);
1717
1719}
1720
1721static bool initialize_rscale(icalrecur_iterator *impl)
1722{
1723 struct icalrecurrencetype *rule = impl->rule;
1724 struct icaltimetype dtstart = impl->dtstart;
1725 char locale[ULOC_KEYWORD_AND_VALUES_CAPACITY] = {0};
1726 UErrorCode status = U_ZERO_ERROR;
1727 UChar *tzid = (UChar *)UCAL_UNKNOWN_ZONE_ID;
1728 bool is_hebrew = false;
1729
1730 /* Convert the UTF8 timezoneid of dstart to ICU UChar. */
1731 char *src = (char *)icaltimezone_get_location((icaltimezone *)dtstart.zone);
1732 if (!src) {
1733 const char *prefix = icaltimezone_tzid_prefix();
1734 src = (char *)icaltimezone_get_tzid((icaltimezone *)dtstart.zone);
1735 /* coverity[use_after_free] */
1736 if (src && !strncmp(src, prefix, strlen(prefix))) {
1737 /* Skip past our prefix */
1738 src += strlen(prefix);
1739 }
1740 }
1741 if (src) {
1742 size_t len = (strlen(src) + 1) * U_SIZEOF_UCHAR;
1743 tzid = icalmemory_tmp_buffer(len);
1744 tzid = u_strFromUTF8Lenient(tzid, (int32_t)len, NULL, src, -1, &status);
1745 if (U_FAILURE(status)) {
1747 return false;
1748 }
1749 }
1750
1751 /* Create locale for Gregorian calendar */
1752 (void)uloc_setKeywordValue("calendar", "gregorian",
1753 locale, sizeof(locale), &status);
1754
1755 /* Create Gregorian calendar and set to DTSTART */
1756 impl->greg = ucal_open(tzid, -1, locale, UCAL_DEFAULT, &status);
1757 if (impl->greg) {
1758 ucal_setDateTime(impl->greg,
1759 (int32_t)dtstart.year,
1760 (int32_t)(dtstart.month - 1), /* UCal is 0-based */
1761 (int32_t)dtstart.day,
1762 (int32_t)dtstart.hour,
1763 (int32_t)dtstart.minute,
1764 (int32_t)dtstart.second, &status);
1765 }
1766 if (!impl->greg || U_FAILURE(status)) {
1768 return false;
1769 }
1770
1771 if (!rule->rscale) {
1772 /* Use Gregorian as RSCALE */
1773 impl->rscale = impl->greg;
1774 } else {
1775 UEnumeration *en;
1776 const char *cal;
1777 char *r;
1778
1779 /* Lowercase the specified calendar */
1780 for (r = rule->rscale; *r; r++) {
1781 *r = tolower((int)*r);
1782 }
1783
1784 /* Check if specified calendar is supported */
1785 en = ucal_getKeywordValuesForLocale("calendar", "", false, &status);
1786 while ((cal = uenum_next(en, NULL, &status))) {
1787 if (!strcmp(cal, rule->rscale)) {
1788 is_hebrew = !strcmp(rule->rscale, "hebrew");
1789 break;
1790 }
1791 }
1792 uenum_close(en);
1793 if (!cal) {
1795 return false;
1796 }
1797
1798 /* Create locale for RSCALE calendar */
1799 (void)uloc_setKeywordValue("calendar", rule->rscale,
1800 locale, sizeof(locale), &status);
1801
1802 /* Create RSCALE calendar and set to DTSTART */
1803 impl->rscale = ucal_open(tzid, -1, locale, UCAL_DEFAULT, &status);
1804 if (impl->rscale) {
1805 UDate millis = ucal_getMillis(impl->greg, &status);
1806
1807 ucal_setMillis(impl->rscale, millis, &status);
1808 }
1809 if (!impl->rscale || U_FAILURE(status)) {
1811 return false;
1812 }
1813 }
1814
1815 /* Validate BY_* array values whose legal maximums differ based on RSCALE */
1816 if (!validate_byrule(impl, ICAL_BY_MONTH, UCAL_MONTH,
1817 &decode_month, is_hebrew) ||
1818 !validate_byrule(impl, ICAL_BY_DAY, UCAL_WEEK_OF_YEAR, &decode_day, 0) ||
1819 !validate_byrule(impl, ICAL_BY_MONTH_DAY, UCAL_DAY_OF_MONTH, NULL, 0) ||
1820 !validate_byrule(impl, ICAL_BY_YEAR_DAY, UCAL_DAY_OF_YEAR, NULL, 0) ||
1821 !validate_byrule(impl, ICAL_BY_WEEK_NO, UCAL_WEEK_OF_YEAR, NULL, 0) ||
1822 !validate_byrule(impl, ICAL_BY_SET_POS, UCAL_DAY_OF_YEAR, NULL, 0)) {
1824 return false;
1825 }
1826
1827 /* Set iCalendar defaults */
1828 ucal_setAttribute(impl->rscale, UCAL_MINIMAL_DAYS_IN_FIRST_WEEK, 4);
1829 ucal_setAttribute(impl->rscale, UCAL_FIRST_DAY_OF_WEEK, (int32_t)rule->week_start);
1830
1831 /* Get rstart (DTSTART in RSCALE) */
1832 impl->rstart = occurrence_as_icaltime(impl, 0);
1833
1834 return true;
1835}
1836
1838static void set_start(icalrecur_iterator *impl, icaltimetype date)
1839{
1840 UErrorCode status = U_ZERO_ERROR;
1841
1842 impl->last.is_date = impl->rstart.is_date;
1843 impl->last.zone = impl->rstart.zone;
1844
1845 if (impl->rstart.is_date) {
1846 ucal_setDate(impl->greg,
1847 (int32_t)date.year,
1848 (int32_t)(date.month - 1), /* UCal is 0-based */
1849 (int32_t)date.day, &status);
1850 } else {
1851 int hour, minute, second;
1852
1853 __get_start_time(impl, date, &hour, &minute, &second);
1854
1855 ucal_setDateTime(impl->greg,
1856 (int32_t)date.year,
1857 (int32_t)(date.month - 1), /* UCal is 0-based */
1858 (int32_t)date.day,
1859 (int32_t)hour,
1860 (int32_t)minute,
1861 (int32_t)second,
1862 &status);
1863 }
1864
1865 if (impl->rscale != impl->greg) {
1866 UDate millis = ucal_getMillis(impl->greg, &status);
1867 ucal_setMillis(impl->rscale, millis, &status);
1868 }
1869}
1870
1871static void set_datetime(icalrecur_iterator *impl, icaltimetype date)
1872{
1873 UErrorCode status = U_ZERO_ERROR;
1874
1875 impl->last.is_date = impl->rstart.is_date;
1876 impl->last.zone = impl->rstart.zone;
1877
1878 if (impl->rstart.is_date) {
1879 ucal_setDate(impl->greg,
1880 (int32_t)date.year,
1881 (int32_t)(date.month - 1), /* UCal is 0-based */
1882 (int32_t)date.day, &status);
1883 } else {
1884 ucal_setDateTime(impl->greg,
1885 (int32_t)date.year,
1886 (int32_t)(date.month - 1), /* UCal is 0-based */
1887 (int32_t)date.day,
1888 (int32_t)date.hour,
1889 (int32_t)date.minute,
1890 (int32_t)date.second,
1891 &status);
1892 }
1893
1894 if (impl->rscale != impl->greg) {
1895 UDate millis = ucal_getMillis(impl->greg, &status);
1896 ucal_setMillis(impl->rscale, millis, &status);
1897 }
1898}
1899
1901static int month_diff(icalrecur_iterator *impl, icaltimetype a, icaltimetype b)
1902{
1903 int diff;
1904
1905 if (impl->rscale == impl->greg) {
1906 /* Use simple Gregorian math */
1907 diff = __greg_month_diff(a, b);
1908 } else if (a.year == b.year) {
1909 diff = b.month - a.month;
1910 } else {
1911 /* Count months in each year to account for leap months */
1912 UErrorCode status = U_ZERO_ERROR;
1913 UDate millis;
1914 int year = a.year;
1915
1916 /* Save current date */
1917 millis = ucal_getMillis(impl->rscale, &status);
1918
1919 set_day_of_year(impl, 1);
1920 diff = get_months_in_year(impl, year) - a.month;
1921 while (++year < b.year) {
1922 diff += get_months_in_year(impl, year);
1923 }
1924 diff += b.month;
1925
1926 /* Restore date */
1927 ucal_setMillis(impl->rscale, millis, &status);
1928 }
1929
1930 return diff;
1931}
1932
1934static int day_diff(icalrecur_iterator *impl, icaltimetype a, icaltimetype b)
1935{
1936 UErrorCode status = U_ZERO_ERROR;
1937 UDate millis;
1938 int diff;
1939
1940 /* Save current date */
1941 millis = ucal_getMillis(impl->rscale, &status);
1942
1943 set_day_of_year(impl, 1);
1944
1945 diff = __day_diff(impl, a, b);
1946
1947 /* Restore date */
1948 ucal_setMillis(impl->rscale, millis, &status);
1949
1950 return diff;
1951}
1952
1953static void reset_period_start(icalrecur_iterator *impl)
1954{
1955 struct icaltimetype start = impl->period_start;
1956
1957 (void)get_day_of_year(impl, start.year, start.month, start.day);
1958}
1959
1960#else /* !HAVE_LIBICU */
1961
1962/*
1963 * Callbacks for recurrence rules without RSCALE (Gregorian only)
1964 */
1965
1967{
1968 icalarray *calendars = icalarray_new(sizeof(const char **), 1);
1969 const char *cal = "GREGORIAN";
1970
1971 icalarray_append(calendars, &cal);
1972
1973 return calendars;
1974}
1975
1976static void set_second(icalrecur_iterator *impl, int second)
1977{
1978 impl->last.second = second;
1979}
1980
1981static void set_minute(icalrecur_iterator *impl, int minute)
1982{
1983 impl->last.minute = minute;
1984}
1985
1986static void set_hour(icalrecur_iterator *impl, int hour)
1987{
1988 impl->last.hour = hour;
1989}
1990
1991static int set_month(icalrecur_iterator *impl, int month)
1992{
1993 return (impl->last.month = month);
1994}
1995
1997#define get_months_in_year(impl, year) (12)
1999
2000static int get_days_in_year(icalrecur_iterator *impl, int year)
2001{
2002 _unused(impl);
2003
2005}
2006
2007static void set_day_of_year(icalrecur_iterator *impl, int doy)
2008{
2009 struct icaltimetype next;
2010
2011 if (doy < 1) {
2012 doy += get_days_in_year(impl, impl->last.year);
2013 }
2014
2015 next = icaltime_from_day_of_year(doy, impl->last.year);
2016
2017 impl->last.day = next.day;
2018 impl->last.month = next.month;
2019 impl->last.year = next.year;
2020}
2021
2022static int get_start_of_week(const icalrecur_iterator *impl)
2023{
2024 return icaltime_start_doy_week(impl->last, (int)impl->rule->week_start);
2025}
2026
2027static int get_day_of_week(const icalrecur_iterator *impl)
2028{
2029 return icaltime_day_of_week(impl->last);
2030}
2031
2034static int get_week_number(icalrecur_iterator *impl, struct icaltimetype tt)
2035{
2036 int dow, week;
2037
2038 _unused(impl);
2039
2040 /* Normalize day of week so that week_start day is 1 */
2041 dow = icaltime_day_of_week(tt) - (int)(impl->rule->week_start - 1);
2042 if (dow <= 0) {
2043 dow += 7;
2044 }
2045
2046 week = (icaltime_day_of_year(tt) - dow + 10) / 7;
2047 if (week < 1) {
2048 /* Last week of preceding year */
2049 week = weeks_in_year(tt.year - 1);
2050 } else if (week > weeks_in_year(tt.year)) {
2051 /* First week of following year */
2052 week = 1;
2053 }
2054
2055 return week;
2056}
2057
2058static int get_days_in_month(icalrecur_iterator *impl, int month, int year)
2059{
2060 _unused(impl);
2061
2063}
2064
2065static struct icaltimetype get_dtstart_adjusted(icalrecur_iterator *impl,
2066 int year, int month, int day)
2067{
2068 struct icaltimetype t = impl->dtstart;
2069
2070 t.is_date = 1;
2071 t.year = year;
2072
2073 if (!month) {
2074 month = impl->dtstart.month;
2075 }
2076 t.month = month;
2077
2078 if (!day) {
2079 day = impl->dtstart.day;
2080 } else if (day < 0) {
2082 }
2083 t.day = day;
2084
2085 return t;
2086}
2087
2088static int get_day_of_year(icalrecur_iterator *impl,
2089 int year, int month, int day)
2090{
2091 return icaltime_day_of_year(get_dtstart_adjusted(impl, year, month, day));
2092}
2093
2094static int get_day_of_week_adjusted(icalrecur_iterator *impl,
2095 int year, int month, int day)
2096{
2097 return icaltime_day_of_week(get_dtstart_adjusted(impl, year, month, day));
2098}
2099
2100static struct icaltimetype occurrence_as_icaltime(icalrecur_iterator *impl,
2101 int normalize)
2102{
2103 return (normalize ? icaltime_normalize(impl->last) : impl->last);
2104}
2105
2106static struct icaltimetype __icaltime_from_day_of_year(icalrecur_iterator *impl,
2107 int day, int year, int *weekno)
2108{
2109 struct icaltimetype tt;
2110
2111 if (day < 0) {
2112 day += get_days_in_year(impl, year) + 1;
2113 }
2114
2116
2117 if (weekno) {
2118 *weekno = get_week_number(impl, tt);
2119 }
2120 return tt;
2121}
2122
2123static void increment_year(icalrecur_iterator *impl, int inc)
2124{
2125 impl->last.year += inc;
2126}
2127
2128static void __increment_month(icalrecur_iterator *impl, int inc)
2129{
2130 int years;
2131
2132 impl->last.month += inc;
2133
2134 /* Months are offset by one */
2135 impl->last.month--;
2136
2137 years = impl->last.month / 12;
2138
2139 impl->last.month = impl->last.month % 12;
2140
2141 if (impl->last.month < 0) {
2142 impl->last.month = impl->last.month + 12;
2143 years--;
2144 }
2145
2146 impl->last.month++;
2147
2148 if (years != 0) {
2149 increment_year(impl, years);
2150 }
2151}
2152
2153static void increment_monthday(icalrecur_iterator *impl, int inc)
2154{
2155 icaltime_adjust(&impl->last, inc, 0, 0, 0);
2156}
2157
2158static void increment_hour(icalrecur_iterator *impl, int inc)
2159{
2160 icaltime_adjust(&impl->last, 0, inc, 0, 0);
2161}
2162
2163static void increment_minute(icalrecur_iterator *impl, int inc)
2164{
2165 icaltime_adjust(&impl->last, 0, 0, inc, 0);
2166}
2167
2168static void increment_second(icalrecur_iterator *impl, int inc)
2169{
2170 icaltime_adjust(&impl->last, 0, 0, 0, inc);
2171}
2172
2173static bool initialize_rscale(icalrecur_iterator *impl)
2174{
2175 if (impl->rule->rscale && strcasecmp(impl->rule->rscale, "GREGORIAN")) {
2177 return false;
2178 }
2179
2180 impl->rstart = impl->dtstart;
2181
2182 return true;
2183}
2184
2186static void set_start(icalrecur_iterator *impl, icaltimetype date)
2187{
2188 impl->last.year = date.year;
2189 impl->last.month = date.month;
2190 impl->last.day = date.day;
2191 impl->last.is_date = impl->dtstart.is_date;
2192 impl->last.zone = impl->dtstart.zone;
2193
2194 if (!impl->dtstart.is_date) {
2195 __get_start_time(impl, date, &impl->last.hour,
2196 &impl->last.minute, &impl->last.second);
2197 }
2198}
2199
2200static void set_datetime(icalrecur_iterator *impl, icaltimetype date)
2201{
2202 impl->last = date;
2203}
2204
2206static int month_diff(icalrecur_iterator *impl, icaltimetype a, icaltimetype b)
2207{
2208 _unused(impl);
2209
2210 return __greg_month_diff(a, b);
2211}
2212
2214static int day_diff(icalrecur_iterator *impl, icaltimetype a, icaltimetype b)
2215{
2216 return __day_diff(impl, a, b);
2217}
2218
2219static void reset_period_start(icalrecur_iterator *impl)
2220{
2221 /* We only want to set the date, not the time */
2222 impl->last.year = impl->period_start.year;
2223 impl->last.month = impl->period_start.month;
2224 impl->last.day = impl->period_start.day;
2225}
2226
2227#endif /* HAVE_LIBICU */
2228
2229static int get_second(icalrecur_iterator *impl)
2230{
2231 return occurrence_as_icaltime(impl, 1).second;
2232}
2233
2234static int get_minute(icalrecur_iterator *impl)
2235{
2236 return occurrence_as_icaltime(impl, 1).minute;
2237}
2238
2239static int get_hour(icalrecur_iterator *impl)
2240{
2241 return occurrence_as_icaltime(impl, 1).hour;
2242}
2243
2244static bool __iterator_set_start(icalrecur_iterator *impl, icaltimetype start);
2245static void increment_month(icalrecur_iterator *impl, int inc);
2246static void expand_month_days(icalrecur_iterator *impl, int year, int month);
2247static void expand_year_days(icalrecur_iterator *impl, int year);
2248static int next_yearday(icalrecur_iterator *impl,
2249 void (*next_period)(icalrecur_iterator *, int));
2250static int prev_yearday(icalrecur_iterator *impl,
2251 void (*next_period)(icalrecur_iterator *, int));
2252
2253static void adjust_to_byday(icalrecur_iterator *impl)
2254{
2255 /* If there is ICAL_BY_DAY data, then we need to move the initial
2256 time to the start of the ICAL_BY_DAY data. That is if the
2257 start time is on a Wednesday, and the rule has
2258 BYDAY=MO,WE,FR, move the initial time back to
2259 monday. Otherwise, jumping to the next week ( jumping 7
2260 days ahead ) will skip over some occurrences in the
2261 second week. */
2262
2263 /* This depends on impl->bydata[ICAL_BY_DAY].by.data being correctly sorted by
2264 * day. This should probably be abstracted to make such assumption
2265 * more explicit. */
2266 short this_dow = (short)get_day_of_week(impl);
2267 short dow = (short)(impl->bydata[ICAL_BY_DAY].by.data[0] - this_dow);
2268
2269 /* Normalize day of week around week start */
2270 if (dow != 0 && this_dow < (short)impl->rule->week_start) {
2271 dow -= 7;
2272 }
2273
2274 if ((this_dow < impl->bydata[ICAL_BY_DAY].by.data[0] && dow >= 0) || dow < 0) {
2275 /* initial time is after first day of ICAL_BY_DAY data */
2276 increment_monthday(impl, dow);
2277 }
2278}
2279
2280icalrecur_iterator *icalrecur_iterator_new(struct icalrecurrencetype *rule,
2281 struct icaltimetype dtstart)
2282{
2283 if (rule == NULL) {
2285 return 0;
2286 }
2287
2288 icalrecur_iterator *impl;
2291
2293
2294 if (freq == ICAL_NO_RECURRENCE) {
2296 return 0;
2297 }
2298
2300#define IN_RANGE(val, min, max) ((val) >= (min) && (val) <= (max))
2302 /* Make sure that DTSTART is a sane value */
2303 if (!icaltime_is_valid_time(dtstart) ||
2304 !IN_RANGE(dtstart.year, 0, MAX_TIME_T_YEAR) ||
2305 !IN_RANGE(dtstart.month, 1, 12) ||
2306 !IN_RANGE(dtstart.day, 1,
2307 icaltime_days_in_month(dtstart.month, dtstart.year)) ||
2308 (!dtstart.is_date && (!IN_RANGE(dtstart.hour, 0, 23) ||
2309 !IN_RANGE(dtstart.minute, 0, 59) ||
2310 !IN_RANGE(dtstart.second, 0, 59)))) {
2312 return 0;
2313 }
2314
2315 if (!(impl = (icalrecur_iterator *)icalmemory_new_buffer(sizeof(icalrecur_iterator)))) {
2317 return 0;
2318 }
2319
2320 memset(impl, 0, sizeof(icalrecur_iterator));
2321
2322 impl->dtstart = dtstart;
2323
2324#if defined(HAVE_LIBICU)
2325 if (rule->rscale) {
2326 // The referenced rule should be treated as immutable, but in case of rscale we need
2327 // to modify it (change rscale to lower, shift months in decode_month()), so we
2328 // clone the whole rule and leave the original one untouched.
2329 rule = icalrecurrencetype_clone(rule);
2330 if (!rule) {
2333 return 0;
2334 }
2335 } else
2336#endif
2337 {
2338 // Without rscale we don't need to modify the rule state. We need to populate some by
2339 // values if they aren't set, but we have dedicated storage for that within the
2340 // iterator (i.e. icalrecurrence_iterator_by_data.buffer_value). So we simply ref
2341 // the rule but don't clone it.
2343 }
2344
2345 impl->rule = rule;
2346
2347 impl->iend = icaltime_null_time();
2348
2349 for (byrule = 0; byrule < ICAL_BY_NUM_PARTS; ++byrule) {
2350 impl->bydata[byrule].by = impl->rule->by[byrule];
2351
2352 /* Note which by rules had data in them when the iterator was
2353 created. We can't use the actual by_x arrays, because the
2354 empty ones will be given default values later in this
2355 routine. The orig_data array will be used later in has_by_data */
2356
2357 impl->bydata[byrule].orig_data =
2358 (short)(impl->rule->by[byrule].size > 0);
2359
2360 /* Check if the recurrence rule is legal */
2361 if (expand_map[freq].map[byrule] == ILLEGAL &&
2362 has_by_data(impl, byrule)) {
2363 ical_invalid_rrule_handling rruleHandlingSetting =
2365 if (rruleHandlingSetting == ICAL_RRULE_IGNORE_INVALID) {
2366 impl->bydata[byrule].orig_data = 0;
2367 } else {
2370 return 0;
2371 }
2372 }
2373 }
2374
2375 if (initialize_rscale(impl) == 0) {
2377 return 0;
2378 }
2379
2380 /* Set up defaults for BY_* arrays */
2381 setup_defaults(impl, ICAL_BY_SECOND, impl->rstart.second);
2382
2383 setup_defaults(impl, ICAL_BY_MINUTE, impl->rstart.minute);
2384
2385 setup_defaults(impl, ICAL_BY_HOUR, impl->rstart.hour);
2386
2387 setup_defaults(impl, ICAL_BY_MONTH_DAY, impl->rstart.day);
2388
2389 setup_defaults(impl, ICAL_BY_MONTH, impl->rstart.month);
2390
2391 if (has_by_data(impl, ICAL_BY_SET_POS)) {
2392 impl->sp_pmax = 0;
2393 while (impl->sp_pmax < impl->bydata[ICAL_BY_SET_POS].by.size &&
2394 impl->bydata[ICAL_BY_SET_POS].by.data[impl->sp_pmax] > 0) {
2395 impl->sp_pmax++;
2396 }
2397 impl->sp_pmax--;
2398 }
2399
2400 if (!__iterator_set_start(impl, dtstart)) {
2402 return 0;
2403 }
2404
2405 return impl;
2406}
2407
2408void icalrecur_iterator_free(icalrecur_iterator *impl)
2409{
2410 icalerror_check_arg_rv((impl != 0), "impl");
2411
2412#if defined(HAVE_LIBICU)
2413 if (impl->greg) {
2414 if (impl->rscale && (impl->rscale != impl->greg)) {
2415 ucal_close(impl->rscale);
2416 }
2417
2418 ucal_close(impl->greg);
2419 }
2420#endif
2421
2422 icalrecurrencetype_unref(impl->rule);
2424}
2425
2427static int __day_diff(icalrecur_iterator *impl, icaltimetype a, icaltimetype b)
2428{
2429 int diff;
2430
2431 if (a.year == b.year) {
2432 diff = get_day_of_year(impl, b.year, b.month, b.day) -
2433 get_day_of_year(impl, a.year, a.month, a.day);
2434 } else {
2435 /* Swap a and b if a is greater than b */
2436 int flipped = 0;
2437 int year;
2438
2439 if (a.year > b.year) {
2440 icaltimetype temp = a;
2441
2442 a = b;
2443 b = temp;
2444 flipped = 1;
2445 }
2446
2447 /* Count days in each year to account for leap days/months */
2448 year = a.year;
2449
2450 diff = get_days_in_year(impl, year) -
2451 get_day_of_year(impl, a.year, a.month, a.day);
2452 while (++year < b.year) {
2453 diff += get_days_in_year(impl, year);
2454 }
2455 diff += get_day_of_year(impl, b.year, b.month, b.day);
2456
2457 if (flipped) {
2458 /* The difference is negative because a was greater than b */
2459 diff = -diff;
2460 }
2461 }
2462
2463 return diff;
2464}
2465
2469static void increment_month(icalrecur_iterator *impl, int inc)
2470{
2471 __increment_month(impl, inc);
2472
2473 if (has_by_data(impl, ICAL_BY_MONTH)) {
2474 struct icaltimetype this = occurrence_as_icaltime(impl, 0);
2475
2476 while (this.year < MAX_TIME_T_YEAR) {
2477 icalrecurrence_iterator_by_data *bydata = &impl->bydata[ICAL_BY_MONTH];
2478 for (bydata->index = 0;
2479 bydata->index < bydata->by.size; bydata->index++) {
2480 if (this.month == bydata->by.data[bydata->index]) {
2481 return;
2482 }
2483 }
2484
2485 __increment_month(impl, inc);
2486 this = occurrence_as_icaltime(impl, 0);
2487 }
2488 }
2489}
2490
2491static int next_unit(icalrecur_iterator *impl,
2492 int by_unit, icalrecurrencetype_frequency frequency,
2493 int (*next_sub_unit)(icalrecur_iterator *),
2494 void (*set_unit)(icalrecur_iterator *, int),
2495 int (*get_unit)(icalrecur_iterator *),
2496 int period_len,
2497 void (*increment_unit)(icalrecur_iterator *, int))
2498{
2499 int has_by_unit = (by_unit > ICAL_BYRULE_NO_CONTRACTION) &&
2500 (impl->bydata[by_unit].by.size > 0);
2501 int this_frequency = (impl->rule->freq == frequency);
2502
2503 int end_of_data = 0;
2504
2505 icalassert(has_by_unit || this_frequency);
2506
2507 if (next_sub_unit && next_sub_unit(impl) == 0) {
2508 return 0;
2509 }
2510
2511 const size_t max_recurrence_time_count = icallimit_get(ICAL_LIMIT_RECURRENCE_TIME_STANDING_STILL);
2512 if (has_by_unit) {
2513 /* Frequency must be hours, minutes or seconds */
2514 icalrecurrence_iterator_by_data *bydata = &impl->bydata[by_unit];
2515 if (this_frequency) {
2516 bydata->index++;
2517 /* Take the frequency into account and treat the byrule data as limiting */
2518 size_t stalledCnt = 0;
2519 while ((impl->last.year < MAX_TIME_T_YEAR) && (stalledCnt++ < max_recurrence_time_count)) {
2520 int last_unit = get_unit(impl);
2521 /* Find a BY* value that works with the interval length */
2522 while (bydata->index < bydata->by.size) {
2523 int cur_by = bydata->by.data[bydata->index];
2524 if ((cur_by >= last_unit) &&
2525 ((cur_by - last_unit) % impl->rule->interval) == 0) {
2526 set_unit(impl, cur_by);
2527 return 1;
2528 }
2529 bydata->index++;
2530 }
2531 /* If none found, increment to next period (i.e., increment super unit,
2532 * but take into account interval length). */
2533 bydata->index = 0;
2534 int multiplier = 1;
2535 if (last_unit + impl->rule->interval < period_len) {
2536 int diff = period_len - last_unit;
2537 multiplier = (diff / impl->rule->interval + (diff % impl->rule->interval > 0));
2538 }
2539 increment_unit(impl, multiplier * impl->rule->interval);
2540 }
2541 } else {
2542 bydata->index++;
2543
2544 if (bydata->by.size <= bydata->index) {
2545 bydata->index = 0;
2546
2547 end_of_data = 1;
2548 }
2549
2550 if (bydata->index < bydata->by.size) {
2551 set_unit(impl, bydata->by.data[bydata->index]);
2552 } else {
2554 }
2555 }
2556 } else {
2557 /* Compute the next value from the last time and the freq interval */
2558 increment_unit(impl, impl->rule->interval);
2559 end_of_data = 1;
2560 }
2561
2562 return end_of_data;
2563}
2564
2565static int next_second(icalrecur_iterator *impl)
2566{
2567 return next_unit(impl, ICAL_BY_SECOND, ICAL_SECONDLY_RECURRENCE, NULL,
2568 &set_second, &get_second, 60, &increment_second);
2569}
2570
2571static int next_minute(icalrecur_iterator *impl)
2572{
2573 return next_unit(impl, ICAL_BY_MINUTE, ICAL_MINUTELY_RECURRENCE, &next_second,
2574 &set_minute, &get_minute, 60, &increment_minute);
2575}
2576
2577static int next_hour(icalrecur_iterator *impl)
2578{
2579 return next_unit(impl, ICAL_BY_HOUR, ICAL_HOURLY_RECURRENCE, &next_minute,
2580 &set_hour, &get_hour, 24, &increment_hour);
2581}
2582
2583static int next_day(icalrecur_iterator *impl)
2584{
2585 return next_unit(impl, ICAL_BYRULE_NO_CONTRACTION, ICAL_DAILY_RECURRENCE, &next_hour,
2586 NULL, NULL, 0, &increment_monthday);
2587}
2588
2589static int prev_unit(icalrecur_iterator *impl,
2590 int by_unit, icalrecurrencetype_frequency frequency,
2591 int (*prev_sub_unit)(icalrecur_iterator *),
2592 void (*set_unit)(icalrecur_iterator *, int),
2593 int (*get_unit)(icalrecur_iterator *),
2594 void (*increment_unit)(icalrecur_iterator *, int))
2595{
2596 int has_by_unit = (by_unit > ICAL_BYRULE_NO_CONTRACTION) &&
2597 (impl->bydata[by_unit].by.size > 0);
2598 int this_frequency = (impl->rule->freq == frequency);
2599
2600 int end_of_data = 0;
2601
2602 icalassert(has_by_unit || this_frequency);
2603
2604 if (prev_sub_unit && prev_sub_unit(impl) == 0) {
2605 return 0;
2606 }
2607
2608 if (has_by_unit) {
2609 icalrecurrence_iterator_by_data *bydata = &impl->bydata[by_unit];
2610 if (this_frequency) {
2611 bydata->index--;
2612
2613 while (impl->last.year > 0) {
2614 int last_unit = get_unit(impl);
2615 while (bydata->index >= 0) {
2616 int cur_by = bydata->by.data[bydata->index];
2617 if ((cur_by <= last_unit) && (impl->rule->interval > 0) &&
2618 ((last_unit - cur_by) % impl->rule->interval) == 0) {
2619 set_unit(impl, cur_by);
2620 return 1;
2621 }
2622 bydata->index--;
2623 }
2624 bydata->index = bydata->by.size - 1;
2625 int multiplier = 1;
2626 if (last_unit - impl->rule->interval > 0) {
2627 multiplier = (last_unit / impl->rule->interval + (last_unit % impl->rule->interval > 0));
2628 }
2629 increment_unit(impl, -multiplier * impl->rule->interval);
2630 }
2631 } else {
2632 bydata->index--;
2633
2634 if (bydata->index < 0) {
2635 bydata->index =
2636 bydata->by.size - 1;
2637
2638 end_of_data = 1;
2639 }
2640
2641 set_unit(impl, bydata->by.data[bydata->index]);
2642 }
2643
2644 } else {
2645 /* Compute the next value from the last time and the freq interval */
2646 increment_unit(impl, -impl->rule->interval);
2647 end_of_data = 1;
2648 }
2649
2650 return end_of_data;
2651}
2652
2653static int prev_second(icalrecur_iterator *impl)
2654{
2655 return prev_unit(impl, ICAL_BY_SECOND, ICAL_SECONDLY_RECURRENCE, NULL,
2656 &set_second, &get_second, &increment_second);
2657}
2658
2659static int prev_minute(icalrecur_iterator *impl)
2660{
2661 return prev_unit(impl, ICAL_BY_MINUTE, ICAL_MINUTELY_RECURRENCE, &prev_second,
2662 &set_minute, &get_minute, &increment_minute);
2663}
2664
2665static int prev_hour(icalrecur_iterator *impl)
2666{
2667 return prev_unit(impl, ICAL_BY_HOUR, ICAL_HOURLY_RECURRENCE, &prev_minute,
2668 &set_hour, &get_hour, &increment_hour);
2669}
2670
2671static int prev_day(icalrecur_iterator *impl)
2672{
2673 return prev_unit(impl, ICAL_BYRULE_NO_CONTRACTION, ICAL_DAILY_RECURRENCE, &prev_hour,
2674 NULL, NULL, &increment_monthday);
2675}
2676
2678static void expand_bymonth_days(icalrecur_iterator *impl, int year, int month)
2679{
2680 int i;
2681 int days_in_month = get_days_in_month(impl, month, year);
2682
2683 for (i = 0; i < impl->bydata[ICAL_BY_MONTH_DAY].by.size; i++) {
2684 short doy = ICAL_BY_YEARDAY_SIZE, mday = impl->bydata[ICAL_BY_MONTH_DAY].by.data[i];
2685 int this_month = month;
2686
2687 if (abs(mday) > days_in_month) {
2688 int days_in_year = get_days_in_year(impl, year);
2689
2690 switch (impl->rule->skip) {
2691 default:
2692 /* Should never get here! */
2693
2694 case ICAL_SKIP_OMIT:
2695 continue;
2696
2697 case ICAL_SKIP_FORWARD:
2698 if (mday > 0) {
2699 this_month++; /* Next month */
2700 }
2701
2702 if (this_month > get_months_in_year(impl, year)) {
2703 doy = days_in_year + 1; /* First day of next year */
2704 } else {
2705 mday = 1; /* First day of month */
2706 }
2707 break;
2708
2709 case ICAL_SKIP_BACKWARD:
2710 if (mday < 0) {
2711 this_month--; /* Prev month */
2712 }
2713
2714 if (this_month == 0) {
2715 doy = 0; /* Last day of prev year */
2716 } else {
2717 mday = -1; /* Last day of month */
2718 }
2719 break;
2720 }
2721 }
2722
2723 if (doy == ICAL_BY_YEARDAY_SIZE) {
2724 doy = get_day_of_year(impl, year, this_month, mday);
2725 }
2726
2727 daysmask_setbit(impl->days, doy, 1);
2728 if (doy < impl->days_index) {
2729 impl->days_index = doy;
2730 }
2731 }
2732}
2733
2735static void expand_by_day(icalrecur_iterator *impl, int year,
2736 int doy_offset, int last_day,
2737 int first_dow, int last_dow,
2738 int is_limiting)
2739{
2740 /* Try to calculate each of the occurrences. */
2741 unsigned long bydays[LONGS_PER_BITS(ICAL_YEARDAYS_MASK_SIZE)];
2742 int i;
2743
2744 memcpy(bydays, impl->days, sizeof(bydays));
2745
2746 daysmask_set_range(impl->days, doy_offset + 1, doy_offset + last_day + 1, 0);
2747
2748 for (i = 0; i < impl->bydata[ICAL_BY_DAY].by.size; i++) {
2749 /* This is 1 (Sun) to 7 (Sat). */
2750 int dow = (int)icalrecurrencetype_day_day_of_week(impl->bydata[ICAL_BY_DAY].by.data[i]);
2751 int pos = icalrecurrencetype_day_position(impl->bydata[ICAL_BY_DAY].by.data[i]);
2752 int first_matching_day, last_matching_day;
2753 int day, this_weekno;
2754
2755 /* Calculate the first day in the period
2756 with the given weekday, and the last day. */
2757 first_matching_day = ((dow + 7 - first_dow) % 7) + 1;
2758 last_matching_day = last_day - ((last_dow + 7 - dow) % 7);
2759
2760 if (pos == 0) {
2761 /* First instance of the weekday within the period.
2762 (Remaining instances added by loop below. */
2763 day = first_matching_day;
2764
2765 } else if (pos > 0) {
2766 /* nth instance of the weekday within the period. */
2767 day = first_matching_day + (pos - 1) * 7;
2768
2769 if (day > last_matching_day) {
2770 continue;
2771 }
2772
2773 } else { /* pos < 0 */
2774 /* -nth instance of the weekday within the period. */
2775 day = last_matching_day + (pos + 1) * 7;
2776
2777 if (day < first_matching_day) {
2778 continue;
2779 }
2780 }
2781
2782 if (doy_offset < 0) {
2783 this_weekno = 1;
2784 } else {
2785 (void)__icaltime_from_day_of_year(impl, day + doy_offset, year,
2786 &this_weekno);
2787 }
2788
2789 /* Add instance(s) of the weekday within the period */
2790 do {
2791 int valid = 0;
2792
2793 if (has_by_data(impl, ICAL_BY_WEEK_NO)) {
2794 /* Make sure our day falls in one of the BYWEEKNO */
2795 int nweeks = weeks_in_year(year);
2796 int j;
2797
2798 for (j = 0; j < impl->bydata[ICAL_BY_WEEK_NO].by.size; j++) {
2799 int weekno = impl->bydata[ICAL_BY_WEEK_NO].by.data[j];
2800
2801 if (weekno < 0) {
2802 weekno += nweeks + 1;
2803 }
2804
2805 if (weekno == this_weekno) {
2806 valid = 1;
2807 break;
2808 }
2809 }
2810 } else {
2811 valid = 1;
2812 }
2813
2814 if (valid) {
2815 int new_val = is_limiting
2816 /* "Filter" the year days bitmask with the bydays bitmask */
2817 ? (int)daysmask_getbit(bydays, day + doy_offset)
2818 /* Add each BYDAY to the year days bitmask */
2819 : 1;
2820
2821 if (!daysmask_setbit(impl->days, day + doy_offset, new_val) && new_val) {
2822 if (day + doy_offset < impl->days_index) {
2823 impl->days_index = day + doy_offset;
2824 }
2825 }
2826 }
2827
2828 } while (!pos && ((day += 7) <= last_day) && ++this_weekno);
2829 }
2830}
2831
2835static void expand_month_days(icalrecur_iterator *impl, int year, int month)
2836{
2837 int doy_offset, days_in_month, first_dow;
2838
2839 daysmask_clearall(impl->days);
2840
2841 /* We may end up skipping fwd/bwd a month during expansion.
2842 Mark our current start date so next_month() can increment from here */
2843 impl->period_start = occurrence_as_icaltime(impl, 0);
2844
2845 doy_offset = get_day_of_year(impl, year, month, 1) - 1;
2846 first_dow = get_day_of_week_adjusted(impl, year, month, 1);
2847 days_in_month = get_days_in_month(impl, month, year);
2848
2849 /* Add each BYMONTHDAY to the year days bitmask */
2850 expand_bymonth_days(impl, year, month);
2851
2852 if (has_by_data(impl, ICAL_BY_DAY)) {
2853 /* Apply each BYDAY to the year days bitmask */
2854 int last_dow;
2855
2856 impl->days_index = ICAL_YEARDAYS_MASK_SIZE;
2857
2858 last_dow = get_day_of_week_adjusted(impl, year, month, days_in_month);
2859
2860 expand_by_day(impl, year, doy_offset, days_in_month,
2861 first_dow, last_dow,
2862 has_by_data(impl, ICAL_BY_MONTH_DAY));
2863 }
2864}
2865
2866static void __next_month(icalrecur_iterator *impl, int inc)
2867{
2868 struct icaltimetype this;
2869
2870 /* Increment to and expand the next month */
2871 increment_month(impl, inc);
2872 this = occurrence_as_icaltime(impl, 0);
2873 expand_month_days(impl, this.year, this.month);
2874}
2875
2876static int next_month(icalrecur_iterator *impl)
2877{
2878 return next_yearday(impl, &__next_month);
2879}
2880
2881static int prev_month(icalrecur_iterator *impl)
2882{
2883 return prev_yearday(impl, &__next_month);
2884}
2885
2886static int next_weekday_by_week(icalrecur_iterator *impl)
2887{
2888 int end_of_data = 0;
2889
2890 if (next_hour(impl) == 0) {
2891 return 0;
2892 }
2893
2894 if (!has_by_data(impl, ICAL_BY_DAY)) {
2895 return 1;
2896 }
2897
2898 /* If we get here, we need to step to the next day */
2899
2900 for (;;) {
2901 impl->bydata[ICAL_BY_DAY].index++; /* Look at next elem in BYDAY array */
2902
2903 /* Are we at the end of the BYDAY array? */
2904 if (impl->bydata[ICAL_BY_DAY].index >= impl->bydata[ICAL_BY_DAY].by.size) {
2905 impl->bydata[ICAL_BY_DAY].index = 0; /* Reset to 0 */
2906 end_of_data = 1; /* Signal that we're at the end */
2907 }
2908
2909 /* Add the day of week offset to the start of this week, and use
2910 that to get the next day */
2911 /* ignore position of dow ("4FR"), only use dow ("FR") */
2913 impl->bydata[ICAL_BY_DAY].by.data[impl->bydata[ICAL_BY_DAY].index]);
2914 dow -= (int)impl->rule->week_start; /* Set Sunday to be 0 */
2915 if (dow < 0) {
2916 dow += 7;
2917 }
2918
2919 int start_of_week = get_start_of_week(impl);
2920
2921 if (dow + start_of_week < 1) {
2922 /* The selected date is in the previous year. */
2923 if (!end_of_data) {
2924 continue;
2925 }
2926
2927 increment_year(impl, -1);
2928 }
2929
2930 set_day_of_year(impl, start_of_week + dow);
2931
2932 return end_of_data;
2933 }
2934}
2935
2936static bool next_week(icalrecur_iterator *impl)
2937{
2938 /* Increment to the next week day,
2939 if there is data at a level less than a week */
2940 if (next_weekday_by_week(impl) == 0) {
2941 return 0; /* Have not reached end of week yet */
2942 }
2943
2944 /* If we get here, we have incremented through the entire week, and
2945 can increment to the next week */
2946
2947 /* Jump to the next week */
2948 increment_monthday(impl, 7 * impl->rule->interval);
2949
2950 return 1;
2951}
2952
2953static int prev_weekday_by_week(icalrecur_iterator *impl)
2954{
2955 int end_of_data = 0;
2956 int start_of_week, dow;
2957
2958 if (prev_hour(impl) == 0) {
2959 return 0;
2960 }
2961
2962 if (!has_by_data(impl, ICAL_BY_DAY)) {
2963 return 1;
2964 }
2965
2966 /* If we get here, we need to step to the previous day */
2967
2968 impl->bydata[ICAL_BY_DAY].index--; /* Look at previous elem in BYDAY array */
2969
2970 /* Are we at the end of the BYDAY array? */
2971 if (impl->bydata[ICAL_BY_DAY].index < 0) {
2972 impl->bydata[ICAL_BY_DAY].index = impl->bydata[ICAL_BY_DAY].by.size - 1;
2973 end_of_data = 1; /* Signal that we're at the end */
2974 }
2975
2976 /* Add the day of week offset to the start of this week, and use
2977 that to get the next day */
2978 /* ignore position of dow ("4FR"), only use dow ("FR") */
2979 dow = (int)icalrecurrencetype_day_day_of_week(impl->bydata[ICAL_BY_DAY].by.data[impl->bydata[ICAL_BY_DAY].index]);
2980 dow -= (int)impl->rule->week_start; /* Set Sunday to be 0 */
2981 if (dow < 0) {
2982 dow += 7;
2983 }
2984
2985 start_of_week = get_start_of_week(impl);
2986
2987 if (dow + start_of_week < 1) {
2988 /* The selected date is in the previous year. */
2989 increment_year(impl, -1);
2990 }
2991
2992 set_day_of_year(impl, start_of_week + dow);
2993
2994 return end_of_data;
2995}
2996
2997static int prev_week(icalrecur_iterator *impl)
2998{
2999 /* Decrement to the previous week day,
3000 if there is data at a level less than a week */
3001 if (prev_weekday_by_week(impl) == 0) {
3002 return 0; /* Have not reached start of week yet */
3003 }
3004
3005 /* If we get here, we have decremented through the entire week, and
3006 can decrement to the previous week */
3007
3008 /* Jump to the previous week */
3009 increment_monthday(impl, 7 * -impl->rule->interval);
3010
3011 return 1;
3012}
3013
3014/* For INTERVAL=YEARLY, set up the year days bitmask in the iterator to
3015 list all of the days of the current year that are specified in this
3016 rule. */
3017static void expand_year_days(icalrecur_iterator *impl, int year)
3018{
3019 int i;
3020 short days_in_year = (short)get_days_in_year(impl, year);
3021 short doy;
3022
3023 daysmask_clearall(impl->days);
3024
3025 /* We may end up skipping fwd/bwd a year during expansion.
3026 Mark our current start date so next_year() can increment from here */
3027 impl->period_start = occurrence_as_icaltime(impl, 0);
3028
3029 if (has_by_data(impl, ICAL_BY_YEAR_DAY)) {
3030 /* We only support BYYEARDAY + BYDAY */
3031 if (has_by_data(impl, ICAL_BY_WEEK_NO) ||
3032 has_by_data(impl, ICAL_BY_MONTH) || has_by_data(impl, ICAL_BY_MONTH_DAY)) {
3034 return;
3035 }
3036
3037 /* Add each BYYEARDAY to the year days bitmask */
3038 for (i = 0; i < impl->bydata[ICAL_BY_YEAR_DAY].by.size; i++) {
3039 doy = impl->bydata[ICAL_BY_YEAR_DAY].by.data[i];
3040
3041 if (abs(doy) > days_in_year) {
3042 switch (impl->rule->skip) {
3043 default:
3044 /* Should never get here! */
3045
3046 case ICAL_SKIP_OMIT:
3047 /* Invalid day */
3048 continue;
3049
3050 case ICAL_SKIP_FORWARD:
3051 if (doy < 0) {
3052 doy = 1; /* First day of this year */
3053 } else {
3054 doy = days_in_year + 1; /* First day of next year */
3055 }
3056 break;
3057
3058 case ICAL_SKIP_BACKWARD:
3059 if (doy < 0) {
3060 doy = 0; /* Last day of prev year */
3061 } else {
3062 doy = days_in_year; /* Last day of this year */
3063 }
3064 break;
3065 }
3066 } else if (doy < 0) {
3067 doy += days_in_year + 1;
3068 }
3069
3070 daysmask_setbit(impl->days, doy, 1);
3071 if (doy < impl->days_index) {
3072 impl->days_index = doy;
3073 }
3074 }
3075 } else if (has_by_data(impl, ICAL_BY_WEEK_NO)) {
3076 int weekno;
3077
3078 /* We only support BYWEEKNO + BYDAY */
3079 if (has_by_data(impl, ICAL_BY_YEAR_DAY) ||
3080 has_by_data(impl, ICAL_BY_MONTH_DAY) ||
3081 (has_by_data(impl, ICAL_BY_MONTH) && !has_by_data(impl, ICAL_BY_DAY))) {
3083 return;
3084 }
3085
3086 /* BYWEEKNO + BYDAY handled below */
3087 if (!has_by_data(impl, ICAL_BY_DAY)) {
3088 int nweeks = weeks_in_year(year);
3089
3090 int start_doy = 1;
3091 /* See which week contains Jan 1 */
3092 (void)__icaltime_from_day_of_year(impl, 1, year, &weekno);
3093 if (weekno > 1) {
3094 /* Jan 1 is in last week of previous year - jump ahead */
3095 start_doy += 7;
3096 }
3097 /* Get the first day of the first week,
3098 * accounting for the week start */
3099 set_day_of_year(impl, 1);
3100 start_doy += get_start_of_week(impl) - 1;
3101 /* Adjust to the next instance of DTSTART's week day */
3102 start_doy += (get_day_of_week_adjusted(impl, impl->dtstart.year,
3103 impl->dtstart.month, impl->dtstart.day) -
3104 (int)impl->rule->week_start + 7) %
3105 7;
3106 /* Reset impl to this year */
3107 (void)get_days_in_year(impl, year);
3108
3109 /* Add day of week in each BYWEEKNO to the year days bitmask */
3110 for (i = 0; i < impl->bydata[ICAL_BY_WEEK_NO].by.size; i++) {
3111 weekno = impl->bydata[ICAL_BY_WEEK_NO].by.data[i];
3112
3113 if (weekno < 0) {
3114 weekno += nweeks + 1;
3115 } else if (weekno > nweeks) {
3116 continue;
3117 }
3118
3119 doy = start_doy + 7 * (weekno - 1);
3120
3121 daysmask_setbit(impl->days, doy, 1);
3122 if (doy < impl->days_index) {
3123 impl->days_index = doy;
3124 }
3125 }
3126 }
3127 } else {
3128 /* Add each BYMONTHDAY in each BYMONTH to the year days bitmask */
3129 for (i = 0; i < impl->bydata[ICAL_BY_MONTH].by.size; i++) {
3130 int month = set_month(impl, impl->bydata[ICAL_BY_MONTH].by.data[i]);
3131
3132 if (month > 0 && month < ICAL_BY_MONTH_SIZE) {
3133 expand_bymonth_days(impl, year, month);
3134 }
3135 }
3136 }
3137
3138 if (has_by_data(impl, ICAL_BY_DAY)) {
3139 /* Apply each BYDAY to the year days bitmask */
3140 int limiting =
3141 has_by_data(impl, ICAL_BY_YEAR_DAY) || has_by_data(impl, ICAL_BY_MONTH_DAY);
3142 int first_dow, last_dow;
3143
3144 impl->days_index = ICAL_YEARDAYS_MASK_SIZE;
3145
3146 if (has_by_data(impl, ICAL_BY_MONTH)) {
3147 /* Numeric BYDAY are within each month */
3148
3149 for (i = 0; i < impl->bydata[ICAL_BY_MONTH].by.size; i++) {
3150 short month = impl->bydata[ICAL_BY_MONTH].by.data[i];
3151 if (month > 0 && month < ICAL_BY_MONTH_SIZE) {
3152 int doy_offset, days_in_month;
3153
3154 /* Get offset within year & day of week of first day of month */
3155 doy_offset =
3156 get_day_of_year(impl, year, month, 1) - 1;
3157 first_dow = get_day_of_week_adjusted(impl, year, month, 1);
3158
3159 /* Get day of week of last day of month */
3160 days_in_month = get_days_in_month(impl, month, year);
3161 last_dow = get_day_of_week_adjusted(impl, year,
3162 month, days_in_month);
3163
3164 expand_by_day(impl, year, doy_offset, days_in_month,
3165 first_dow, last_dow, limiting);
3166 }
3167 }
3168 } else {
3169 /* Numeric BYDAY are within the year */
3170 short doy_offset = 0, last_day;
3171
3172 if (has_by_data(impl, ICAL_BY_WEEK_NO)) {
3173 int weekno;
3174
3175 /* See which week contains Jan 1 */
3176 (void)__icaltime_from_day_of_year(impl, 1, year, &weekno);
3177 if (weekno > 1) {
3178 /* Jan 1 is in last week of previous year - jump ahead */
3179 doy_offset += 7;
3180 }
3181
3182 /* Set start and end of ISO week-numbering year */
3183 set_day_of_year(impl, 1);
3184 doy_offset += get_start_of_week(impl) - 1;
3185 last_day = (7 * weeks_in_year(year)) - doy_offset - 1;
3186
3187 first_dow = (int)impl->rule->week_start;
3188 last_dow = (first_dow + 6) % 7;
3189 } else {
3190 /* Get day of week of first day of year */
3191 first_dow = get_day_of_week_adjusted(impl, year, 1, 1);
3192
3193 /* Get day of week of last day of year */
3194 set_day_of_year(impl, days_in_year);
3195 last_dow = get_day_of_week(impl);
3196
3197 last_day = days_in_year;
3198 }
3199
3200 expand_by_day(impl, year, doy_offset, last_day, first_dow, last_dow, limiting);
3201 }
3202 }
3203}
3204
3205static void __next_year(icalrecur_iterator *impl, int inc)
3206{
3207 struct icaltimetype this;
3208
3209 /* Increment to and expand the next year */
3210 increment_year(impl, inc);
3211 this = occurrence_as_icaltime(impl, 0);
3212 expand_year_days(impl, this.year);
3213}
3214
3215static int next_year(icalrecur_iterator *impl)
3216{
3217 return next_yearday(impl, &__next_year);
3218}
3219
3220static int prev_year(icalrecur_iterator *impl)
3221{
3222 return prev_yearday(impl, &__next_year);
3223}
3224
3225static short daymask_find_next_bit(const unsigned long *days, short start_index)
3226{
3227 short days_index = start_index;
3228 unsigned long v;
3229 short startBitIndex;
3230 unsigned short wordIdx;
3231
3232 if (days_index >= ICAL_YEARDAYS_MASK_SIZE) {
3233 return ICAL_YEARDAYS_MASK_SIZE;
3234 }
3235
3236 // Prepare the first word, where searching might not start at the beginning
3237 startBitIndex = days_index + ICAL_YEARDAYS_MASK_OFFSET;
3238 wordIdx = (unsigned short)(startBitIndex / BITS_PER_LONG);
3239 v = days[wordIdx];
3240 if (startBitIndex >= 0) {
3241 v >>= startBitIndex % BITS_PER_LONG;
3242 } else {
3243 v <<= -startBitIndex % BITS_PER_LONG;
3244 }
3245
3246 if (!v) {
3247 // so the first word didn't contain any bits of interest.
3248 days_index += BITS_PER_LONG - startBitIndex % BITS_PER_LONG;
3249
3250 // Are there more empty words following? Skip them.
3251 unsigned short maxWordIdx = (unsigned short)(LONGS_PER_BITS(ICAL_YEARDAYS_MASK_SIZE)) - 1;
3252 while (days_index < ICAL_YEARDAYS_MASK_SIZE && wordIdx < maxWordIdx) {
3253 wordIdx++;
3254 v = days[wordIdx];
3255
3256 if (v) {
3257 break;
3258 }
3259
3260 days_index += BITS_PER_LONG;
3261 }
3262 }
3263
3264 if (v) {
3265 // We found a word containing the next bit but don't know the exact
3266 // position yet. Do a b-search to find it.
3267
3268 unsigned long mask;
3269 int maskSize = (int)(BITS_PER_LONG / 2);
3270 mask = (((unsigned long)1) << maskSize) - 1;
3271
3272 while (maskSize) {
3273 if ((v & mask) == 0) {
3274 v >>= maskSize;
3275 days_index += maskSize;
3276 }
3277 maskSize /= 2;
3278 mask >>= maskSize;
3279 }
3280 }
3281
3282 return days_index;
3283}
3284
3285static short daymask_find_prev_bit(const unsigned long *days, short start_index)
3286{
3287 short days_index = start_index;
3288 unsigned long v;
3289 short startBitIndex;
3290 int wordIdx;
3291
3292 if (days_index <= -ICAL_YEARDAYS_MASK_OFFSET) {
3293 return -ICAL_YEARDAYS_MASK_OFFSET;
3294 }
3295
3296 // Prepare the first word, where searching might not start at the beginning
3297 startBitIndex = days_index + ICAL_YEARDAYS_MASK_OFFSET;
3298 wordIdx = (int)(startBitIndex / BITS_PER_LONG);
3299 v = days[wordIdx];
3300 v <<= BITS_PER_LONG - (startBitIndex % BITS_PER_LONG) - 1;
3301
3302 if (!v) {
3303 // so the first word didn't contain any bits of interest.
3304 days_index -= (startBitIndex % BITS_PER_LONG) + 1;
3305
3306 // Are there more empty words leading? Skip them.
3307 while (days_index > -ICAL_YEARDAYS_MASK_OFFSET) {
3308 wordIdx--;
3309 v = days[wordIdx];
3310
3311 if (v) {
3312 break;
3313 }
3314
3315 days_index -= BITS_PER_LONG;
3316 }
3317 }
3318
3319 if (v) {
3320 // We found a word containing the next bit but don't know the exact
3321 // position yet. Do a b-search to find it.
3322
3323 unsigned long mask;
3324 int maskSize = (int)(BITS_PER_LONG / 2);
3325 mask = ((((unsigned long)1) << maskSize) - 1) << maskSize;
3326
3327 while (maskSize) {
3328 if ((v & mask) == 0) {
3329 v <<= maskSize;
3330 days_index -= maskSize;
3331 }
3332 maskSize /= 2;
3333 /* coverity[integer_overflow] */
3334 mask <<= maskSize;
3335 }
3336 }
3337
3338 return days_index;
3339}
3340
3341static int next_yearday(icalrecur_iterator *impl,
3342 void (*next_period)(icalrecur_iterator *, int))
3343{
3344 if (next_hour(impl) == 0) {
3345 return 0;
3346 }
3347
3348 /* We may have skipped fwd/bwd a month/year with previous occurrence.
3349 Reset the period start date so we can increment properly */
3350 reset_period_start(impl);
3351
3352 /* Find next year day that is set */
3353 impl->days_index = daymask_find_next_bit(impl->days, impl->days_index + 1);
3354
3355 int ret = 0;
3356
3357 if (impl->days_index >= ICAL_YEARDAYS_MASK_SIZE) {
3358 ret = 1;
3359 if (next_period) {
3360 for (;;) {
3361 /* Increment to and expand the next period */
3362 next_period(impl, impl->rule->interval);
3363
3364 if (impl->days_index < ICAL_YEARDAYS_MASK_SIZE) {
3365 break; /* break when a matching day is found */
3366 }
3367 }
3368 } else {
3369 /* When next_period is NULL,
3370 we only indicate that we have
3371 reached the end of the period */
3372 return 1;
3373 }
3374 }
3375
3376 if (impl->days_index < 1) {
3377 /* Day is in previous year */
3378 increment_year(impl, -1);
3379 }
3380
3381 set_day_of_year(impl, impl->days_index);
3382
3383 return ret;
3384}
3385
3386static int prev_yearday(icalrecur_iterator *impl,
3387 void (*next_period)(icalrecur_iterator *, int))
3388{
3389 if (prev_hour(impl) == 0) {
3390 return 0;
3391 }
3392
3393 /* We may have skipped fwd/bwd a month/year with previous occurrence.
3394 Reset the period start date so we can decrement properly */
3395 reset_period_start(impl);
3396
3397 /* Find previous year day that is set */
3398 impl->days_index = daymask_find_prev_bit(impl->days, impl->days_index - 1);
3399
3400 int ret = 0;
3401
3402 while (impl->days_index <= -ICAL_YEARDAYS_MASK_OFFSET) {
3403 if (next_period) {
3404 ret = 1;
3405 /* Decrement to and expand the previous period */
3406 next_period(impl, -impl->rule->interval);
3407
3408 impl->days_index = ICAL_YEARDAYS_MASK_SIZE;
3409 impl->days_index = daymask_find_prev_bit(impl->days, impl->days_index - 1);
3410 } else {
3411 /* When next_period is NULL,
3412 we only indicate that we have
3413 reached the end of the period */
3414 return 1;
3415 }
3416 }
3417
3418 if (impl->days_index < 1) {
3419 /* Day is in previous year */
3420 increment_year(impl, -1);
3421 }
3422
3423 set_day_of_year(impl, impl->days_index);
3424
3425 return ret;
3426}
3427
3428static int days_in_current_month(icalrecur_iterator *impl)
3429{
3430 return get_days_in_month(impl, impl->last.month, impl->last.year);
3431}
3432
3433static int days_in_current_year(icalrecur_iterator *impl)
3434{
3435 return get_days_in_year(impl, impl->last.year);
3436}
3437
3438static inline int has_contract_restriction(icalrecur_iterator *impl,
3440{
3441 return impl->bydata[byrule].by.size > 0 &&
3442 expand_map[impl->rule->freq].map[byrule] == CONTRACT;
3443}
3444
3445static bool check_contract_restriction(icalrecur_iterator *impl,
3446 icalrecurrencetype_byrule byrule, int v,
3447 int (*get_total)(icalrecur_iterator *))
3448{
3449 if (has_contract_restriction(impl, byrule)) {
3450 int total = 0;
3451 bool pass = false;
3452 for (int itr = 0; itr < impl->bydata[byrule].by.size; itr++) {
3453 short byval = impl->bydata[byrule].by.data[itr];
3454 if ((byval < 0) && (total == 0)) {
3455 if (get_total) {
3456 // load total value lazily only when needed
3457 total = get_total(impl);
3458 } else {
3459 // limiting by negative values is only allowed for
3460 // BYMONTHDAY, BYYEARDAY (BYDAY is handled separately)
3462 continue;
3463 }
3464 }
3465
3466 if (v == ((byval >= 0) ? byval : (total + 1 + byval))) {
3467 pass = true;
3468 break;
3469 }
3470 }
3471
3472 return pass;
3473 }
3474
3475 /* This is not a contracting byrule, or it has no data, so the test passes */
3476 return true;
3477}
3478
3479static bool check_contracting_rules(icalrecur_iterator *impl)
3480{
3481 struct icaltimetype last = occurrence_as_icaltime(impl, 0);
3482
3484// Check `has_contract_restriction` before calling `check_contract_restriction` to avoid
3485// evaluating potentially expensive `v` if not needed.
3486#define CHECK_CONTRACT_RESTRICTION(by, v, get_total) \
3487 (!has_contract_restriction(impl, (by)) || check_contract_restriction(impl, (by), (v), (get_total)))
3488
3489 if (
3490 CHECK_CONTRACT_RESTRICTION(ICAL_BY_SECOND, last.second, NULL) &&
3491 CHECK_CONTRACT_RESTRICTION(ICAL_BY_MINUTE, last.minute, NULL) &&
3492 CHECK_CONTRACT_RESTRICTION(ICAL_BY_HOUR, last.hour, NULL) &&
3493 CHECK_CONTRACT_RESTRICTION(ICAL_BY_MONTH_DAY, last.day, days_in_current_month) &&
3494 CHECK_CONTRACT_RESTRICTION(ICAL_BY_MONTH, last.month, NULL) &&
3495 CHECK_CONTRACT_RESTRICTION(ICAL_BY_WEEK_NO, get_week_number(impl, last), NULL) &&
3496 CHECK_CONTRACT_RESTRICTION(
3497 ICAL_BY_DAY, get_day_of_week_adjusted(impl, last.year, last.month, last.day), NULL) &&
3498 CHECK_CONTRACT_RESTRICTION(
3499 ICAL_BY_YEAR_DAY, get_day_of_year(impl, last.year, last.month, last.day), days_in_current_year)) {
3500 return true;
3501 }
3502
3503#undef CHECK_CONTRACT_RESTRICTION
3505
3506 return false;
3507}
3508
3509/* Initialize data relating to BYSETPOS, in particular:
3510 * set_pos, sp_idxp, sp_idxn, and recurrence_set_size.
3511 * This must be called at the start of each new period
3512 *
3513 * next == 1 indicates we are advancing the iterator,
3514 * and so are at the start of a new period, while
3515 * next == 0 indicates we are at the end of one
3516 */
3517static void setup_setpos(icalrecur_iterator *impl, int next)
3518{
3519 /* Save data that may be modified */
3520 int days_index = impl->days_index;
3521 int bydata_indices[ICAL_BY_NUM_PARTS];
3522 for (int byrule = 0; byrule < ICAL_BY_NUM_PARTS; byrule++) {
3523 bydata_indices[byrule] = impl->bydata[byrule].index;
3524 }
3525 struct icaltimetype last = impl->last;
3526
3527 impl->recurrence_set_size = 1;
3528 int period_change = 1;
3529 do {
3530 switch (impl->rule->freq) {
3532 break;
3534 /* call next_second instead of next_minute
3535 * to avoid going to the next minute */
3536 period_change = (next ? next_second : prev_second)(impl);
3537 break;
3539 period_change = (next ? next_minute : prev_minute)(impl);
3540 break;
3542 period_change = (next ? next_hour : prev_hour)(impl);
3543 break;
3545 period_change = (next ? next_weekday_by_week : prev_weekday_by_week)(impl);
3546 break;
3548 /* call next_yearday instead of next_month
3549 * to avoid expanding month days */
3550 period_change = (next ? next_yearday : prev_yearday)(impl, NULL);
3551 break;
3553 period_change = (next ? next_yearday : prev_yearday)(impl, NULL);
3554 break;
3555 default:
3557 return;
3558 }
3559 if (period_change == 0 && check_contracting_rules(impl)) {
3560 impl->recurrence_set_size++;
3561 }
3562 } while (period_change == 0);
3563
3564 if (next) {
3565 impl->set_pos = 1;
3566 impl->sp_idxp = 0;
3567 impl->sp_idxn = impl->bydata[ICAL_BY_SET_POS].by.size - 1;
3568 } else {
3569 impl->set_pos = impl->recurrence_set_size;
3570 impl->sp_idxp = impl->sp_pmax;
3571 impl->sp_idxn = impl->sp_pmax + 1;
3572 }
3573
3574 /* Restore what was modified
3575 * Because we do not expand month/year days,
3576 * the days bitfield is not modified */
3577 set_datetime(impl, last);
3578 impl->last = last;
3579 impl->days_index = days_index;
3580 for (int byrule = 0; byrule < ICAL_BY_NUM_PARTS; byrule++) {
3581 impl->bydata[byrule].index = bydata_indices[byrule];
3582 }
3583}
3584
3585/* If s1 occurs before s2 in the recurrence set, return -1
3586 * If s1 occurs after, return 1
3587 * If they are equal, return 0
3588 */
3589static inline int setpos_cmp(int s1, int s2, int next)
3590{
3591 if (s1 < s2) {
3592 return (next ? -1 : 1);
3593 } else if (s2 < s1) {
3594 return (next ? 1 : -1);
3595 }
3596 return 0;
3597}
3598
3599/* Check whether impl->set_pos is a valid recurrence set position
3600 *
3601 * next == 1 indicates that we should increase and decrease
3602 * sp_idxp and sp_idxn, respectively, while
3603 * next == 0 indicates that we should decrease and increase them
3604 */
3605static bool check_setpos(icalrecur_iterator *impl, int next)
3606{
3607 if (!has_by_data(impl, ICAL_BY_SET_POS)) {
3608 return true;
3609 }
3610 icalrecurrence_by_data *by = &(impl->bydata[ICAL_BY_SET_POS].by);
3611 int32_t set_pos;
3612
3613 /* If we have positive BYSETPOS data */
3614 if (impl->sp_pmax >= 0) {
3615 set_pos = by->data[impl->sp_idxp];
3616 /* Increment positive index while set_pos is before impl->set_pos */
3617 while (setpos_cmp(set_pos, impl->set_pos, next) < 0) {
3618 if (next && impl->sp_idxp < impl->sp_pmax) {
3619 impl->sp_idxp++;
3620 } else if (!next && impl->sp_idxp > 0) {
3621 impl->sp_idxp--;
3622 } else {
3623 break;
3624 }
3625 set_pos = by->data[impl->sp_idxp];
3626 }
3627 if (impl->set_pos == set_pos) {
3628 return true;
3629 }
3630 }
3631
3632 if (impl->sp_pmax < by->size - 1) {
3633 set_pos = by->data[impl->sp_idxn] + impl->recurrence_set_size + 1;
3634 while (setpos_cmp(set_pos, impl->set_pos, next) < 0) {
3635 if (next && impl->sp_idxn > impl->sp_pmax + 1) {
3636 impl->sp_idxn--;
3637 } else if (!next && impl->sp_idxn < by->size - 1) {
3638 impl->sp_idxn++;
3639 } else {
3640 break;
3641 }
3642 set_pos = by->data[impl->sp_idxn] + impl->recurrence_set_size + 1;
3643 }
3644 if (impl->set_pos == set_pos) {
3645 return true;
3646 }
3647 }
3648 return false;
3649}
3650
3651struct icaltimetype icalrecur_iterator_next(icalrecur_iterator *impl)
3652{
3653 /* Quit if we reached COUNT or if last time is after the UNTIL time */
3654 if (!impl ||
3655 (impl->rule->count != 0 && impl->occurrence_no >= impl->rule->count) ||
3656 (!icaltime_is_null_time(impl->rule->until) &&
3657 icaltime_compare(impl->last, impl->rule->until) > 0)) {
3658 return icaltime_null_time();
3659 }
3660
3661 /* If initial time is valid, return it */
3662 if ((impl->occurrence_no == 0) &&
3663 (icaltime_compare(impl->last, impl->istart) >= 0) &&
3664 check_setpos(impl, 1) &&
3665 check_contracting_rules(impl)) {
3666 impl->occurrence_no++;
3667 return impl->last;
3668 }
3669
3670 int period_change = 1;
3671 /* store previous instance, including iterator structures
3672 * (e.g., bydata) */
3673 icalrecur_iterator impl_last = *impl;
3674
3675 /* Iterate until we get the next valid time */
3676 size_t stalledCnt = 0;
3677 const size_t max_recurrence_time_count = icallimit_get(ICAL_LIMIT_RECURRENCE_TIME_STANDING_STILL);
3678 int lastTimeCompare = 0;
3679 bool hasByData = false;
3680 int checkContractingRules = 0;
3681 size_t cntRecurrences = 0;
3682 const size_t max_recurrences = icallimit_get(ICAL_LIMIT_RECURRENCE_SEARCH);
3683 do {
3684 switch (impl->rule->freq) {
3686 /* period_change is always true for secondly recurrence */
3687 next_second(impl);
3688 break;
3689
3691 period_change = next_minute(impl);
3692 break;
3693
3695 period_change = next_hour(impl);
3696 break;
3697
3699 period_change = next_day(impl);
3700 break;
3701
3703 period_change = next_week(impl);
3704 break;
3705
3707 period_change = next_month(impl);
3708 break;
3709
3711 period_change = next_year(impl);
3712 break;
3713
3714 default:
3716 return icaltime_null_time();
3717 }
3718
3719 impl->last = occurrence_as_icaltime(impl, 1);
3720
3721 /* Ignore times that are after the MAX year,
3722 or the UNTIL time, or the end time */
3723 if (impl->last.year > MAX_TIME_T_YEAR ||
3724 (!icaltime_is_null_time(impl->rule->until) &&
3725 icaltime_compare(impl->last, impl->rule->until) > 0) ||
3726 (!icaltime_is_null_time(impl->iend) &&
3727 icaltime_compare(impl->last, impl->iend) >= 0)) {
3728 /* reset to valid instance */
3729 *impl = impl_last;
3730 set_datetime(impl, impl_last.last);
3731 return icaltime_null_time();
3732 }
3733
3734 hasByData = has_by_data(impl, ICAL_BY_SET_POS);
3735 checkContractingRules = -1;
3736 if (hasByData) {
3737 checkContractingRules = check_contracting_rules(impl) ? 1 : 0;
3738 if (checkContractingRules == 1) {
3739 if (period_change) {
3740 setup_setpos(impl, 1);
3741 } else {
3742 impl->set_pos++;
3743 }
3744 }
3745 }
3746
3747 // is time standing still? if so, break out of here
3748 lastTimeCompare = icaltime_compare(impl->last, impl_last.last);
3749 if (lastTimeCompare == 0) {
3750 if (stalledCnt++ == max_recurrence_time_count) {
3751 break;
3752 }
3753 } else {
3754 stalledCnt = 0;
3755 }
3756 } while ((cntRecurrences++ < max_recurrences) &&
3757 ((lastTimeCompare == 0) ||
3758 (hasByData && !check_setpos(impl, 1)) ||
3759 icaltime_compare(impl->last, impl->istart) < 0 ||
3760 (checkContractingRules == 0) ||
3761 (checkContractingRules == -1 && !check_contracting_rules(impl))));
3762
3763 impl->occurrence_no++;
3764
3765 return impl->last;
3766}
3767
3768struct icaltimetype icalrecur_iterator_prev(icalrecur_iterator *impl)
3769{
3770 /* Quit if last time is before the DTSTART time */
3771 if (!impl || icaltime_compare(impl->last, impl->dtstart) < 0) {
3772 return icaltime_null_time();
3773 }
3774
3775 int period_change = 1;
3776 icalrecur_iterator impl_last = *impl;
3777
3778 /* Iterate until we get the next valid time */
3779 do {
3780 switch (impl->rule->freq) {
3782 prev_second(impl);
3783 break;
3784
3786 period_change = prev_minute(impl);
3787 break;
3788
3790 period_change = prev_hour(impl);
3791 break;
3792
3794 period_change = prev_day(impl);
3795 break;
3796
3798 period_change = prev_week(impl);
3799 break;
3800
3802 period_change = prev_month(impl);
3803 break;
3804
3806 period_change = prev_year(impl);
3807 break;
3808
3809 default:
3811 return icaltime_null_time();
3812 }
3813
3814 impl->last = occurrence_as_icaltime(impl, 1);
3815
3816 /* Ignore times that are before the DTSTART time */
3817 if (icaltime_compare(impl->last, impl->dtstart) < 0 ||
3818 (!icaltime_is_null_time(impl->istart) &&
3819 icaltime_compare(impl->last, impl->istart) < 0)) {
3820 *impl = impl_last;
3821 set_datetime(impl, impl_last.last);
3822 return icaltime_null_time();
3823 }
3824
3825 if (has_by_data(impl, ICAL_BY_SET_POS) && check_contracting_rules(impl)) {
3826 if (period_change) {
3827 setup_setpos(impl, 0);
3828 } else {
3829 impl->set_pos--;
3830 }
3831 }
3832
3833 } while (impl->last.year > MAX_TIME_T_YEAR ||
3834 (!icaltime_is_null_time(impl->rule->until) &&
3835 icaltime_compare(impl->last, impl->rule->until) > 0) ||
3836 (!icaltime_is_null_time(impl->iend) &&
3837 icaltime_compare(impl->last, impl->iend) > 0) ||
3838 icaltime_compare(impl->last, impl_last.last) == 0 ||
3839 (has_by_data(impl, ICAL_BY_SET_POS) && !check_setpos(impl, 0)) ||
3840 !check_contracting_rules(impl));
3841
3842 impl->occurrence_no--;
3843
3844 return impl->last;
3845}
3846
3849static void set_bydata_start(icalrecurrence_iterator_by_data *bydata, int tfield)
3850{
3851 int bdi;
3852 for (bdi = 0;
3853 bdi < bydata->by.size; bdi++) {
3854 if (bydata->by.data[bdi] == tfield) {
3855 bydata->index = bdi;
3856 return;
3857 }
3858 }
3859}
3860
3861static bool __iterator_set_start(icalrecur_iterator *impl, icaltimetype start)
3862{
3863 icalrecurrencetype_frequency freq = impl->rule->freq;
3864 short interval = impl->rule->interval;
3865 int diff;
3866
3867 impl->istart = start;
3868 impl->occurrence_no = 0;
3869 impl->days_index = ICAL_YEARDAYS_MASK_SIZE;
3870
3871 /* Set Gregorian start date */
3872 set_start(impl, start);
3873
3874 switch (freq) {
3876 /* For YEARLY rule, begin by setting up the year days array.
3877 The YEARLY rules work by expanding one year at a time. */
3878
3879 if ((interval > 1) &&
3880 (diff = (impl->istart.year - impl->rstart.year) % interval)) {
3881 /* Specified start year doesn't match interval -
3882 bump start to first day of next year that matches interval */
3883 set_day_of_year(impl, 1);
3884 increment_year(impl, interval - diff);
3885 }
3886
3887 /* Get (adjusted) start date as RSCALE date */
3888 start = occurrence_as_icaltime(impl, 0);
3889
3890 if (has_by_data(impl, ICAL_BY_WEEK_NO)) {
3891 int start_weekno = get_week_number(impl, start);
3892 if (start_weekno > 5 &&
3893 start.month == 1) {
3894 /* if we are in the last week of the previous year,
3895 * expand year days for the previous year
3896 */
3897 increment_year(impl, -1);
3898 expand_year_days(impl, start.year - 1);
3899 int days_in_year = get_days_in_year(impl, start.year - 1);
3900 impl->days_index = daymask_find_next_bit(impl->days, days_in_year + 1);
3901 if (impl->days_index >= ICAL_YEARDAYS_MASK_SIZE) {
3902 increment_year(impl, 1);
3903 }
3904 } else if (start_weekno < 45 &&
3905 start.month == 12) {
3906 /* if we are in the first week of the next year,
3907 * expand year days for the next year
3908 */
3909 increment_year(impl, 1);
3910 expand_year_days(impl, start.year + 1);
3911 impl->days_index = daymask_find_next_bit(impl->days, -ICAL_YEARDAYS_MASK_OFFSET);
3912 if (impl->days_index >= ICAL_YEARDAYS_MASK_SIZE) {
3913 increment_year(impl, -1);
3914 }
3915 }
3916 }
3917
3918 /* Expand days array for (adjusted) start year -
3919 fail after hitting the year MAX_TIME_T_YEAR if no expanded days match */
3920 while (start.year < MAX_TIME_T_YEAR && impl->days_index >= ICAL_YEARDAYS_MASK_SIZE) {
3921 expand_year_days(impl, start.year);
3922
3924 switch (err) {
3925 case ICAL_NO_ERROR:
3926 break;
3928 return false;
3929 default:
3931 return false;
3932 }
3933
3934 if (impl->days_index >= ICAL_YEARDAYS_MASK_SIZE) {
3935 increment_year(impl, interval);
3936 start = occurrence_as_icaltime(impl, 0);
3937 }
3938 }
3939
3940 /* Copy the first day into last */
3941 set_day_of_year(impl, impl->days_index);
3942 if (impl->days_index < 1) {
3943 increment_year(impl, -1);
3944 }
3945
3946 break;
3947
3949 /* For MONTHLY rule, begin by setting up the year days array.
3950 The MONTHLY rules work by expanding one month at a time. */
3951
3952 if ((interval > 1) &&
3953 (diff = month_diff(impl, impl->rstart, impl->istart) % interval)) {
3954 /* Specified month doesn't match interval -
3955 bump start to first day of next month that matches interval */
3956 increment_monthday(impl, -impl->istart.day + 1);
3957 __increment_month(impl, interval - diff);
3958 }
3959
3960 /* Get (adjusted) start date as RSCALE date */
3961 start = occurrence_as_icaltime(impl, 0);
3962
3963 /* Expand days array for (adjusted) start month -
3964 fail after hitting the year 20000 if no expanded days match */
3965 while (start.year < 20000) {
3966 expand_month_days(impl, start.year, start.month);
3967 if (impl->days_index < ICAL_YEARDAYS_MASK_SIZE) {
3968 break; /* break when a matching day is found */
3969 }
3970 increment_month(impl, impl->rule->interval);
3971 start = occurrence_as_icaltime(impl, 0);
3972 }
3973
3974 /* Copy the first day into last */
3975 set_day_of_year(impl, impl->days_index);
3976
3977 break;
3978
3980 if (impl->bydata[ICAL_BY_DAY].by.size <= 0) {
3981 /* Weekly recurrences with no ICAL_BY_DAY data should occur on the
3982 same day of the week as the start time . */
3983 recur_iterator_set_static_single_by_value(impl, ICAL_BY_DAY, (short)get_day_of_week(impl));
3984 } else {
3985 adjust_to_byday(impl);
3986
3987 /* If start == DTSTART, adjust rstart */
3988 if (icaltime_compare(start, impl->dtstart) == 0) {
3989 impl->rstart = occurrence_as_icaltime(impl, 0);
3990 }
3991
3992 /* Get (adjusted) start date as RSCALE date */
3993 start = occurrence_as_icaltime(impl, 0);
3994
3995 if ((interval > 1) &&
3996 (diff = (day_diff(impl, impl->rstart, start) + 6) / 7) % interval) {
3997 /* Specified week doesn't match interval -
3998 bump start to next week that matches interval */
3999 increment_monthday(impl, 7 * (interval - diff));
4000 }
4001 }
4002 break;
4003
4005 if ((interval > 1) &&
4006 (diff = day_diff(impl, impl->rstart, impl->istart) % interval)) {
4007 /* Specified day doesn't match interval -
4008 bump start to next day that matches interval */
4009 increment_monthday(impl, interval - diff);
4010 }
4011 break;
4012
4014 if ((interval > 1) &&
4015 (diff = abs(impl->istart.hour - impl->rstart.hour) % interval)) {
4016 /* Specified hour doesn't match interval -
4017 bump start to next hour that matches interval */
4018 increment_hour(impl, interval - diff);
4019 }
4020 set_bydata_start(&impl->bydata[ICAL_BY_HOUR], impl->istart.hour);
4021 break;
4022
4024 if ((interval > 1) &&
4025 (diff = abs(impl->istart.minute - impl->rstart.minute) % interval)) {
4026 /* Specified minute doesn't match interval -
4027 bump start to next minute that matches interval */
4028 increment_minute(impl, interval - diff);
4029 }
4030 set_bydata_start(&impl->bydata[ICAL_BY_MINUTE], impl->istart.minute);
4031 break;
4032
4034 if ((interval > 1) &&
4035 (diff = abs(impl->istart.second - impl->rstart.second) % interval)) {
4036 /* Specified second doesn't match interval -
4037 bump start to next second that matches interval */
4038 increment_second(impl, interval - diff);
4039 }
4040 set_bydata_start(&impl->bydata[ICAL_BY_SECOND], impl->istart.second);
4041 break;
4042
4043 default:
4044 break;
4045 }
4046
4047 /* Get start date as Gregorian date */
4048 impl->last = occurrence_as_icaltime(impl, 1);
4049 if (has_by_data(impl, ICAL_BY_SET_POS)) {
4050 setup_setpos(impl, 1);
4051 }
4052
4053 /* Fail if first instance exceeds MAX_TIME_T_YEAR */
4054 if (impl->last.year > MAX_TIME_T_YEAR) {
4056 return false;
4057 }
4058
4059 return true;
4060}
4061
4062bool icalrecur_iterator_set_start(icalrecur_iterator *impl,
4063 struct icaltimetype start)
4064{
4065 /* We can't adjust start date if we need to count occurrences */
4066 if (impl->rule->count > 0) {
4068 return false;
4069 }
4070
4071 /* Convert start to same time zone as DTSTART */
4072 start = icaltime_convert_to_zone(start, (icaltimezone *)impl->dtstart.zone);
4073
4074 if (icaltime_compare(start, impl->dtstart) < 0) {
4075 /* If start is before DTSTART, use DTSTART */
4076 start = impl->dtstart;
4077 } else if (!icaltime_is_null_time(impl->rule->until) &&
4078 icaltime_compare(start, impl->rule->until) > 0) {
4079 /* If start is after UNTIL, we're done */
4080 impl->last = start;
4081 return true;
4082 }
4083
4084 return __iterator_set_start(impl, start);
4085}
4086
4087bool icalrecur_iterator_set_end(icalrecur_iterator *impl,
4088 struct icaltimetype end)
4089{
4090 /* Convert end to same time zone as DTSTART */
4091 end = icaltime_convert_to_zone(end, (icaltimezone *)impl->dtstart.zone);
4092
4093 impl->iend = end;
4094
4095 return true;
4096}
4097
4098bool icalrecur_iterator_set_range(icalrecur_iterator *impl,
4099 struct icaltimetype from,
4100 struct icaltimetype to)
4101{
4102 if (impl->rule->count > 0 || icaltime_is_null_time(from)) {
4103 /* Can't set a range without 'from' or if we need to count occurrences */
4105 return false;
4106 }
4107
4108 if (!icaltime_is_null_time(to) && icaltime_compare(to, from) < 0) {
4109 /* Setting up for the reverse iterator */
4110 const icaltimezone *zone = impl->dtstart.zone;
4111
4112 /* Convert 'from' to same time zone as DTSTART */
4114
4115 if (icaltime_compare(from, impl->rule->until) > 0) {
4116 /* If 'from' is after UNTIL, use UNTIL */
4117 from = impl->rule->until;
4118 } else if (icaltime_compare(from, impl->dtstart) < 0) {
4119 /* If 'from' is before START, we're done */
4120 impl->last = from;
4121 return true;
4122 }
4123
4124 if (!__iterator_set_start(impl, from)) {
4125 return false;
4126 }
4127
4128 /* __iterator_set_start() may back us up earlier than 'from'
4129 Iterate forward until we are later than 'from'.
4130 */
4131 while (icaltime_compare(impl->last, from) < 0) {
4132 (void)icalrecur_iterator_next(impl);
4133 }
4134
4135 /* Convert 'to' to same time zone as DTSTART */
4137
4138 if (icaltime_compare(to, impl->dtstart) < 0) {
4139 /* If 'to' is before DTSTART, use DTSTART */
4140 to = impl->dtstart;
4141 }
4142
4143 impl->istart = to;
4144 impl->iend = from;
4145 impl->days_index = 0;
4146 } else {
4147 if (!icalrecur_iterator_set_start(impl, from)) {
4148 return false;
4149 }
4150
4152 }
4153
4154 return true;
4155}
4156
4157/************************** Type Routines **********************/
4158
4159static void icalrecurrencetype_clear(struct icalrecurrencetype *recur)
4160{
4161 int refcount = recur->refcount;
4162
4163 icalrecurrencetype_free(recur, 0);
4164
4165 memset(recur, 0, sizeof(*recur));
4166
4167 recur->refcount = refcount;
4168
4170 recur->freq = ICAL_NO_RECURRENCE;
4171 recur->interval = 1;
4172 recur->until = icaltime_null_time();
4173 recur->count = 0;
4174 recur->rscale = NULL;
4175 recur->skip = ICAL_SKIP_OMIT;
4176}
4177
4182
4184{
4185 int wd, pos;
4186
4188
4189 pos = (abs(day) - wd) / 8 * ((day < 0) ? -1 : 1);
4190
4191 return pos;
4192}
4193
4195{
4196 short s_weekday = (short)weekday;
4197 short a_position = (short)(8 * abs(position));
4198 return (s_weekday + a_position) * ((position < 0) ? -1 : 1);
4199}
4200
4202{
4203 return (month & LEAP_MONTH);
4204}
4205
4207{
4208 return (month & ~LEAP_MONTH);
4209}
4210
4212{
4213 return (short)month | (is_leap ? LEAP_MONTH : 0);
4214}
4215
4216bool icalrecur_expand_recurrence(const char *rule,
4217 icaltime_t start, int count, icaltime_t *array)
4218{
4219 struct icalrecurrencetype *recur;
4220 icalrecur_iterator *ritr;
4221 struct icaltimetype icstart;
4222
4223 memset(array, 0, (size_t)count * sizeof(icaltime_t));
4224
4225 icstart = icaltime_from_timet_with_zone(start, 0, 0);
4226
4228 if (!recur) {
4229 return false;
4230 }
4231
4232 ritr = icalrecur_iterator_new(recur, icstart);
4233 if (ritr) {
4234 int i = 0;
4235 for (struct icaltimetype next = icalrecur_iterator_next(ritr);
4236 !icaltime_is_null_time(next) && i < count;
4237 next = icalrecur_iterator_next(ritr)) {
4238 icaltime_t tt = icaltime_as_timet(next);
4239
4240 if (tt >= start) {
4241 array[i++] = tt;
4242 }
4243 }
4245 }
4246
4248
4249 return true;
4250}
4251
4253{
4254 ical_invalid_rrule_handling myHandling;
4255
4256#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD
4257 pthread_mutex_lock(&invalid_rrule_mutex);
4258#endif
4259
4260 myHandling = invalidRruleHandling;
4261
4262#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD
4263 pthread_mutex_unlock(&invalid_rrule_mutex);
4264#endif
4265
4266 return myHandling;
4267}
4268
4270{
4271#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD
4272 pthread_mutex_lock(&invalid_rrule_mutex);
4273#endif
4274
4275 invalidRruleHandling = newSetting;
4276
4277#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD
4278 pthread_mutex_unlock(&invalid_rrule_mutex);
4279#endif
4280}
void icalarray_append(icalarray *array, const void *element)
Appends an element to an array.
Definition icalarray.c:119
icalarray * icalarray_new(size_t element_size, size_t increment_size)
Definition icalarray.c:36
void icalerror_set_errno(icalerrorenum x)
Sets the icalerrno to a given error.
Definition icalerror.c:90
void icalerror_clear_errno(void)
Resets icalerrno to ICAL_NO_ERROR.
Definition icalerror.c:85
Error handling for libical.
icalerrorenum
Represents the different types of errors that can be triggered in libical.
Definition icalerror.h:42
@ ICAL_NEWFAILED_ERROR
Definition icalerror.h:50
@ ICAL_INTERNAL_ERROR
Definition icalerror.h:62
@ ICAL_NO_ERROR
Definition icalerror.h:44
@ ICAL_MALFORMEDDATA_ERROR
Definition icalerror.h:56
@ ICAL_UNIMPLEMENTED_ERROR
Definition icalerror.h:71
@ ICAL_USAGE_ERROR
Definition icalerror.h:68
#define icalerrno
Access the current icalerrno value.
Definition icalerror.h:130
size_t icallimit_get(icallimits_kind kind)
Definition icallimits.c:29
Defines the interface for getting/setting internal library limits.
@ ICAL_LIMIT_RECURRENCE_SEARCH
Definition icallimits.h:38
@ ICAL_LIMIT_RECURRENCE_TIME_STANDING_STILL
Definition icallimits.h:40
void icalmemory_free_buffer(void *buf)
Releases a buffer.
Definition icalmemory.c:353
char * icalmemory_strdup(const char *s)
Creates a duplicate of a string.
Definition icalmemory.c:240
void icalmemory_append_string(char **buf, char **pos, size_t *buf_size, const char *string)
Appends a string to a buffer.
Definition icalmemory.c:363
void * icalmemory_resize_buffer(void *buf, size_t size)
Resizes a buffer created with icalmemory_new_buffer().
Definition icalmemory.c:334
void * icalmemory_new_buffer(size_t size)
Creates new buffer with the specified size.
Definition icalmemory.c:313
char * icalmemory_tmp_copy(const char *str)
Creates a copy of the given string, stored on the ring buffer, and returns it.
Definition icalmemory.c:220
void icalmemory_append_char(char **buf, char **pos, size_t *buf_size, char ch)
Appends a character to a buffer.
Definition icalmemory.c:404
void icalmemory_add_tmp_buffer(void *buf)
Adds an externally allocated buffer to the ring.
Definition icalmemory.c:156
void * icalmemory_tmp_buffer(size_t size)
Creates a new temporary buffer on the ring and returns it.
Definition icalmemory.c:180
Common memory management routines.
bool icalrecur_iterator_set_start(icalrecur_iterator *impl, struct icaltimetype start)
Definition icalrecur.c:4062
struct icalrecurrencetype * icalrecurrencetype_new_from_string(const char *str)
Definition icalrecur.c:834
struct icaltimetype icalrecur_iterator_prev(icalrecur_iterator *impl)
Definition icalrecur.c:3768
void icalrecurrencetype_ref(struct icalrecurrencetype *recur)
Definition icalrecur.c:747
icalrecurrencetype_skip icalrecur_string_to_skip(const char *str)
Definition icalrecur.c:234
bool icalrecur_iterator_set_end(icalrecur_iterator *impl, struct icaltimetype end)
Definition icalrecur.c:4087
short icalrecurrencetype_encode_month(int month, bool is_leap)
Definition icalrecur.c:4211
short icalrecurrencetype_encode_day(enum icalrecurrencetype_weekday weekday, int position)
Definition icalrecur.c:4194
bool icalrecurrencetype_month_is_leap(short month)
Definition icalrecur.c:4201
const char * icalrecur_weekday_to_string(icalrecurrencetype_weekday kind)
Definition icalrecur.c:271
const char * icalrecur_skip_to_string(icalrecurrencetype_skip kind)
Definition icalrecur.c:246
bool icalrecur_resize_by(icalrecurrence_by_data *by, short size)
Definition icalrecur.c:306
struct icalrecurrencetype * icalrecurrencetype_clone(struct icalrecurrencetype *recur)
Definition icalrecur.c:797
const char * icalrecur_freq_to_string(icalrecurrencetype_frequency kind)
Definition icalrecur.c:213
void ical_set_invalid_rrule_handling_setting(ical_invalid_rrule_handling newSetting)
Definition icalrecur.c:4269
void icalrecur_iterator_free(icalrecur_iterator *impl)
Definition icalrecur.c:2408
icalrecurrencetype_frequency icalrecur_string_to_freq(const char *str)
Definition icalrecur.c:201
struct icalrecurrencetype * icalrecurrencetype_new(void)
Definition icalrecur.c:708
bool icalrecur_iterator_set_range(icalrecur_iterator *impl, struct icaltimetype from, struct icaltimetype to)
Definition icalrecur.c:4098
icalarray * icalrecurrencetype_rscale_supported_calendars(void)
Definition icalrecur.c:1966
enum icalrecurrencetype_weekday icalrecurrencetype_day_day_of_week(short day)
Definition icalrecur.c:4178
ical_invalid_rrule_handling ical_get_invalid_rrule_handling_setting(void)
Definition icalrecur.c:4252
int icalrecurrencetype_day_position(short day)
Definition icalrecur.c:4183
bool icalrecur_expand_recurrence(const char *rule, icaltime_t start, int count, icaltime_t *array)
Definition icalrecur.c:4216
char * icalrecurrencetype_as_string(struct icalrecurrencetype *recur)
Definition icalrecur.c:1009
void icalrecurrencetype_unref(struct icalrecurrencetype *recur)
Definition icalrecur.c:755
icalrecur_iterator * icalrecur_iterator_new(struct icalrecurrencetype *rule, struct icaltimetype dtstart)
Definition icalrecur.c:2280
struct icaltimetype icalrecur_iterator_next(icalrecur_iterator *impl)
Definition icalrecur.c:3651
icalrecurrencetype_weekday icalrecur_string_to_weekday(const char *str)
Definition icalrecur.c:284
int icalrecurrencetype_month_month(short month)
Definition icalrecur.c:4206
char * icalrecurrencetype_as_string_r(struct icalrecurrencetype *recur)
Definition icalrecur.c:1018
Routines for dealing with recurring time.
icalrecurrencetype_weekday
Definition icalrecur.h:100
@ ICAL_TUESDAY_WEEKDAY
Definition icalrecur.h:104
@ ICAL_NO_WEEKDAY
Definition icalrecur.h:101
@ ICAL_WEDNESDAY_WEEKDAY
Definition icalrecur.h:105
@ ICAL_FRIDAY_WEEKDAY
Definition icalrecur.h:107
@ ICAL_THURSDAY_WEEKDAY
Definition icalrecur.h:106
@ ICAL_MONDAY_WEEKDAY
Definition icalrecur.h:103
@ ICAL_SATURDAY_WEEKDAY
Definition icalrecur.h:108
@ ICAL_SUNDAY_WEEKDAY
Definition icalrecur.h:102
icalrecurrencetype_frequency
Definition icalrecur.h:85
@ ICAL_NO_RECURRENCE
Definition icalrecur.h:93
@ ICAL_HOURLY_RECURRENCE
Definition icalrecur.h:88
@ ICAL_SECONDLY_RECURRENCE
Definition icalrecur.h:86
@ ICAL_YEARLY_RECURRENCE
Definition icalrecur.h:92
@ ICAL_DAILY_RECURRENCE
Definition icalrecur.h:89
@ ICAL_MINUTELY_RECURRENCE
Definition icalrecur.h:87
@ ICAL_WEEKLY_RECURRENCE
Definition icalrecur.h:90
@ ICAL_MONTHLY_RECURRENCE
Definition icalrecur.h:91
icalrecurrencetype_skip
Definition icalrecur.h:115
@ ICAL_SKIP_OMIT
Definition icalrecur.h:118
@ ICAL_SKIP_UNDEFINED
Definition icalrecur.h:119
@ ICAL_SKIP_FORWARD
Definition icalrecur.h:117
@ ICAL_SKIP_BACKWARD
Definition icalrecur.h:116
icalrecurrencetype_byrule
Definition icalrecur.h:126
@ ICAL_BY_SET_POS
Definition icalrecur.h:136
@ ICAL_BY_HOUR
Definition icalrecur.h:133
@ ICAL_BY_MINUTE
Definition icalrecur.h:134
@ ICAL_BY_MONTH
Definition icalrecur.h:128
@ ICAL_BY_SECOND
Definition icalrecur.h:135
@ ICAL_BY_YEAR_DAY
Definition icalrecur.h:130
@ ICAL_BY_MONTH_DAY
Definition icalrecur.h:131
@ ICAL_BY_DAY
Definition icalrecur.h:132
@ ICAL_BY_WEEK_NO
Definition icalrecur.h:129
@ ICAL_BYRULE_NO_CONTRACTION
Definition icalrecur.h:127
@ ICAL_BY_NUM_PARTS
Definition icalrecur.h:138
ical_invalid_rrule_handling
Definition icalrecur.h:617
@ ICAL_RRULE_TREAT_AS_ERROR
Definition icalrecur.h:619
@ ICAL_RRULE_IGNORE_INVALID
Definition icalrecur.h:621
struct icaltimetype icaltime_from_timet_with_zone(const icaltime_t tm, const bool is_date, const icaltimezone *zone)
Constructor.
Definition icaltime.c:209
struct icaltimetype icaltime_from_string(const char *str)
Definition icaltime.c:374
int icaltime_day_of_year(const struct icaltimetype t)
Definition icaltime.c:533
int icaltime_start_doy_week(const struct icaltimetype t, int fdow)
Definition icaltime.c:512
bool icaltime_is_leap_year(const int year)
Definition icaltime.c:451
int icaltime_day_of_week(const struct icaltimetype t)
Definition icaltime.c:497
struct icaltimetype icaltime_from_day_of_year(const int _doy, const int _year)
Definition icaltime.c:540
bool icaltime_is_valid_time(const struct icaltimetype t)
Definition icaltime.c:603
int icaltime_days_in_month(const int month, const int year)
Definition icaltime.c:471
struct icaltimetype icaltime_convert_to_zone(const struct icaltimetype tt, icaltimezone *zone)
Definition icaltime.c:855
int icaltime_days_in_year(const int year)
Definition icaltime.c:460
bool icaltime_is_null_time(const struct icaltimetype t)
Definition icaltime.c:626
struct icaltimetype icaltime_normalize(const struct icaltimetype tt)
Definition icaltime.c:366
icaltime_t icaltime_as_timet(const struct icaltimetype tt)
Definition icaltime.c:259
int icaltime_compare(const struct icaltimetype a_in, const struct icaltimetype b_in)
Definition icaltime.c:635
void icaltime_adjust(struct icaltimetype *tt, const int days, const int hours, const int minutes, const int seconds)
Definition icaltime.c:764
struct icaltimetype icaltime_null_time(void)
Definition icaltime.c:575
const char * icaltimezone_get_location(const icaltimezone *zone)
const char * icaltimezone_get_tzid(icaltimezone *zone)
const char * icaltimezone_tzid_prefix(void)
Timezone handling routines.
struct _icaltimezone icaltimezone
Defines the data structure representing iCalendar parameter values.
icalrecurrencetype_frequency freq
Definition icalrecur.h:255
icalrecurrencetype_skip skip
Definition icalrecur.h:292
icalrecurrencetype_weekday week_start
Definition icalrecur.h:267
struct icaltimetype until
Definition icalrecur.h:258
icalrecurrence_by_data by[ICAL_BY_NUM_PARTS]
Definition icalrecur.h:286
const icaltimezone * zone
Definition icaltime.h:102