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