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