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