Coverage Report

Created: 2024-09-30 06:41

/src/libical/src/libical/icalrecur.c
Line
Count
Source (jump to first uncovered line)
1
/*======================================================================
2
 FILE: icalrecur.c
3
 CREATOR: eric 16 May 2000
4
5
 SPDX-FileCopyrightText: 2000, Eric Busboom <eric@civicknowledge.com>
6
7
 SPDX-License-Identifier: LGPL-2.1-only OR MPL-2.0
8
9
========================================================================*/
10
11
/**
12
  icalrecur.c
13
  @brief Implementation of routines for dealing with recurring time
14
15
  How this code works:
16
17
  Processing starts when the caller generates a new recurrence
18
  iterator via icalrecur_iterator_new(). This routine copies the
19
  recurrence rule into the iterator and extracts things like start and
20
  end dates. Then, it checks if the rule is legal, using some logic
21
  from RFC5545 and some logic that probably should be in RFC5545.
22
23
  If compiled with support for Non-Gregorian Recurrence Rules (RFC7529),
24
  icalrecur_iterator_new() verifies that the given RSCALE is supported
25
  and configures ICU4C to convert occurrences to/from non-Gregorian dates.
26
27
  Then, icalrecur_iterator_new() re-writes some of the BY*
28
  arrays. This involves ( via a call to setup_defaults() ) :
29
30
  1) For BY rule parts with no data ( ie BYSECOND was not specified )
31
  copy the corresponding time part from DTSTART into the BY array. (
32
  So impl->by_ptrs[BY_SECOND] will then have one element if is
33
  originally had none ) This only happens if the BY* rule part data
34
  would expand the number of occurrences in the occurrence set. This
35
  lets the code ignore DTSTART later on and still use it to get the
36
  time parts that were not specified in any other way.
37
38
  2) For the by rule part that are not the same interval as the
39
  frequency -- for HOURLY anything but BYHOUR, for instance -- copy the
40
  first data element from the rule part into the first occurrence. For
41
  example, for "INTERVAL=MONTHLY and BYHOUR=10,30", initialize the
42
  first time to be returned to have an hour of 10.
43
44
  Finally, for INTERVAL=YEARLY, the routine expands the rule to get
45
  all of the days specified in the rule. The code will do this for
46
  each new year, and this is the first expansion. This is a special
47
  case for the yearly interval; no other frequency gets expanded this
48
  way. The yearly interval is the most complex, so some special
49
  processing is required.
50
51
  After creating a new iterator, the caller will make successive calls
52
  to icalrecur_iterator_next() to get the next time specified by the
53
  rule. The main part of this routine is a switch on the frequency of
54
  the rule. Each different frequency is handled by a different
55
  routine.
56
57
  For example, next_hour handles the case of INTERVAL=HOURLY, and it
58
  is called by other routines to get the next hour. First, the routine
59
  tries to get the next minute part of a time with a call to
60
  next_minute(). If next_minute() returns 1, it has reached the end of
61
  its data, usually the last element of the BYMINUTE array. Then, if
62
  there is data in the BYHOUR array, the routine changes the hour to
63
  the next one in the array. If INTERVAL=HOURLY, the routine advances
64
  the hour by the interval.
65
66
  If the routine used the last hour in the BYHOUR array, and the
67
  INTERVAL=HOURLY, then the routine calls increment_monthday() to set
68
  the next month day. The increment_* routines may call higher routine
69
  to increment the month or year also.
70
71
  The code for INTERVAL=DAILY is handled by next_day(). First, the
72
  routine tries to get the next hour part of a time with a call to
73
  next_hour. If next_hour() returns 1, it has reached the end of its
74
  data, usually the last element of the BYHOUR array. This means that
75
  next_day() should increment the time to the next day. If FREQUENCY==DAILY,
76
  the routine increments the day by the interval; otherwise, it
77
  increments the day by 1.
78
79
  Next_day() differs from next_hour because it does not use the BYDAY
80
  array to select an appropriate day. Instead, it returns every day (
81
  incrementing by 1 if the frequency is not DAILY with INTERVAL!=1)
82
  Any days that are not specified in a non-empty BYDAY array are
83
  filtered out later.
84
85
  Generally, the flow of these routine is for a next_* call a next_*
86
  routine of a lower interval ( next_day calls next_hour) and then to
87
  possibly call an increment_* routine of an equal or higher
88
  interval. ( next_day calls increment_monthday() )
89
90
  When the call to the original next_* routine returns,
91
  icalrecur_iterator_next() will check the returned data against other
92
  BYrule parts to determine if is should be excluded by calling
93
  check_contracting_rules. Generally, a contracting rule is any with a
94
  larger time span than the interval. For instance, if
95
  INTERVAL=DAILY, BYMONTH is a contracting rule part.
96
97
  Check_contracting_rules() uses icalrecur_check_rulepart() to do its
98
  work. icalrecur_check_rulepart() uses expand_map[] to determine if a rule
99
  is contracting, and if it is, and if the BY rule part has some data,
100
  then the routine checks if the value of a component of the time is
101
  part of the byrule part. For instance, for "INTERVAL=DAILY;
102
  BYMONTH=6,10", icalrecur_check_rulepart() would check that the time value
103
  given to it has a month of either 6 or 10.
104
105
  Finally, icalrecur_iterator_next() does a few other checks on the
106
  time value, and if it passes, it returns the time.
107
108
  A note about the end_of_data flag. The flag indicates that the
109
  routine is at the end of its data -- the last BY rule if the routine
110
  is using by rules, or the last day of the week/month/year/etc if
111
  not.
112
113
  This flag is usually set early in a next_* routine and returned in
114
  the end. The way it is used allows the next_* routine to set the
115
  last time back to the first element in a BYxx rule, and then signal
116
  to the higher level routine to increment the next higher level. For
117
  instance. WITH FREQ=MONTHLY;BYDAY=TU,FR, After next_weekday_by_month
118
  runs though both TU and FR, it sets the week day back to TU and sets
119
  end_of_data to 1x. This signals next_month to increment the month.
120
121
 ======================================================================*/
122
123
#ifdef HAVE_CONFIG_H
124
#include <config.h>
125
#endif
126
127
#include "icalrecur.h"
128
#include "icalerror.h"
129
#include "icalmemory.h"
130
#include "icaltimezone.h"
131
#include "icalvalue.h" /* for print_date[time]_to_string() */
132
133
#include <ctype.h>
134
#include <stddef.h> /* For offsetof() macro */
135
#include <stdlib.h>
136
137
#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD
138
#include <pthread.h>
139
static pthread_mutex_t invalid_rrule_mutex = PTHREAD_MUTEX_INITIALIZER;
140
#endif
141
142
static ICAL_GLOBAL_VAR ical_invalid_rrule_handling invalidRruleHandling = ICAL_RRULE_TREAT_AS_ERROR;
143
144
#if defined(HAVE_LIBICU)
145
#include <unicode/ucal.h>
146
#include <unicode/ustring.h>
147
#if defined(HAVE_STDBOOL_H)
148
#include <stdbool.h>
149
#else
150
#define false 0
151
#define true 1
152
#endif
153
#else
154
155
/* The maximums below are based on Gregorian leap years */
156
#undef ICAL_BY_MONTH_SIZE
157
#undef ICAL_BY_WEEKNO_SIZE
158
#undef ICAL_BY_YEARDAY_SIZE
159
#define ICAL_BY_MONTH_SIZE 13    /* 1 to 12 */
160
454k
#define ICAL_BY_WEEKNO_SIZE 54   /* 1 to 53 */
161
0
#define ICAL_BY_YEARDAY_SIZE 367 /* 1 to 366 */
162
#endif
163
164
#if (SIZEOF_ICALTIME_T > 4)
165
/** Arbitrarily go up to 1000th anniversary of Gregorian calendar, since
166
    64-bit icaltime_t values get us up to the tm_year limit of 2+ billion years. */
167
0
#define MAX_TIME_T_YEAR 2582
168
#else
169
/** This is the last year we will go up to, since 32-bit icaltime_t values
170
   only go up to the start of 2038. */
171
#define MAX_TIME_T_YEAR 2037
172
#endif
173
174
0
#define BYDAYIDX impl->by_indices[BY_DAY]
175
0
#define BYDAYPTR impl->by_ptrs[BY_DAY]
176
177
0
#define BYMONIDX impl->by_indices[BY_MONTH]
178
0
#define BYMONPTR impl->by_ptrs[BY_MONTH]
179
180
#define BYMDIDX impl->by_indices[BY_MONTH_DAY]
181
0
#define BYMDPTR impl->by_ptrs[BY_MONTH_DAY]
182
183
#define BYYDIDX impl->by_indices[BY_YEAR_DAY]
184
0
#define BYYDPTR impl->by_ptrs[BY_YEAR_DAY]
185
186
#define BYWEEKIDX impl->by_indices[BY_WEEK_NO]
187
0
#define BYWEEKPTR impl->by_ptrs[BY_WEEK_NO]
188
189
993
#define LEAP_MONTH 0x1000
190
191
int icalrecurrencetype_rscale_is_supported(void)
192
377
{
193
377
    return 1;
194
377
}
195
196
/****************** Enumeration Routines ******************/
197
198
static const struct freq_map {
199
    icalrecurrencetype_frequency kind;
200
    const char str[9];
201
} freq_map[] = {
202
    {ICAL_SECONDLY_RECURRENCE, "SECONDLY"},
203
    {ICAL_MINUTELY_RECURRENCE, "MINUTELY"},
204
    {ICAL_HOURLY_RECURRENCE, "HOURLY"},
205
    {ICAL_DAILY_RECURRENCE, "DAILY"},
206
    {ICAL_WEEKLY_RECURRENCE, "WEEKLY"},
207
    {ICAL_MONTHLY_RECURRENCE, "MONTHLY"},
208
    {ICAL_YEARLY_RECURRENCE, "YEARLY"},
209
    {ICAL_NO_RECURRENCE, ""}};
210
211
icalrecurrencetype_frequency icalrecur_string_to_freq(const char *str)
212
4.45k
{
213
4.45k
    int i;
214
215
16.2k
    for (i = 0; freq_map[i].kind != ICAL_NO_RECURRENCE; i++) {
216
15.7k
        if (strcasecmp(str, freq_map[i].str) == 0) {
217
3.98k
            return freq_map[i].kind;
218
3.98k
        }
219
15.7k
    }
220
473
    return ICAL_NO_RECURRENCE;
221
4.45k
}
222
223
const char *icalrecur_freq_to_string(icalrecurrencetype_frequency kind)
224
6.22k
{
225
6.22k
    int i;
226
227
12.7k
    for (i = 0; freq_map[i].kind != ICAL_NO_RECURRENCE; i++) {
228
12.7k
        if (freq_map[i].kind == kind) {
229
6.22k
            return freq_map[i].str;
230
6.22k
        }
231
12.7k
    }
232
0
    return 0;
233
6.22k
}
234
235
static const struct skip_map {
236
    icalrecurrencetype_skip kind;
237
    const char str[9];
238
} skip_map[] = {
239
    {ICAL_SKIP_BACKWARD, "BACKWARD"},
240
    {ICAL_SKIP_FORWARD, "FORWARD"},
241
    {ICAL_SKIP_OMIT, "OMIT"},
242
    {ICAL_SKIP_UNDEFINED, ""}};
243
244
icalrecurrencetype_skip icalrecur_string_to_skip(const char *str)
245
1.18k
{
246
1.18k
    int i;
247
248
3.09k
    for (i = 0; skip_map[i].kind != ICAL_SKIP_UNDEFINED; i++) {
249
2.66k
        if (strcasecmp(str, skip_map[i].str) == 0) {
250
750
            return skip_map[i].kind;
251
750
        }
252
2.66k
    }
253
432
    return ICAL_SKIP_UNDEFINED;
254
1.18k
}
255
256
const char *icalrecur_skip_to_string(icalrecurrencetype_skip kind)
257
203
{
258
203
    int i;
259
260
405
    for (i = 0; skip_map[i].kind != ICAL_SKIP_UNDEFINED; i++) {
261
405
        if (skip_map[i].kind == kind) {
262
203
            return skip_map[i].str;
263
203
        }
264
405
    }
265
0
    return 0;
266
203
}
267
268
static const struct wd_map {
269
    icalrecurrencetype_weekday wd;
270
    const char str[3];
271
} wd_map[] = {
272
    {ICAL_SUNDAY_WEEKDAY, "SU"},
273
    {ICAL_MONDAY_WEEKDAY, "MO"},
274
    {ICAL_TUESDAY_WEEKDAY, "TU"},
275
    {ICAL_WEDNESDAY_WEEKDAY, "WE"},
276
    {ICAL_THURSDAY_WEEKDAY, "TH"},
277
    {ICAL_FRIDAY_WEEKDAY, "FR"},
278
    {ICAL_SATURDAY_WEEKDAY, "SA"},
279
    {ICAL_NO_WEEKDAY, ""}};
280
281
const char *icalrecur_weekday_to_string(icalrecurrencetype_weekday kind)
282
1.96k
{
283
1.96k
    int i;
284
285
12.6k
    for (i = 0; wd_map[i].wd != ICAL_NO_WEEKDAY; i++) {
286
12.6k
        if (wd_map[i].wd == kind) {
287
1.96k
            return wd_map[i].str;
288
1.96k
        }
289
12.6k
    }
290
291
0
    return 0;
292
1.96k
}
293
294
icalrecurrencetype_weekday icalrecur_string_to_weekday(const char *str)
295
181k
{
296
181k
    int i;
297
298
1.22M
    for (i = 0; wd_map[i].wd != ICAL_NO_WEEKDAY; i++) {
299
1.22M
        if (strcasecmp(str, wd_map[i].str) == 0) {
300
181k
            return wd_map[i].wd;
301
181k
        }
302
1.22M
    }
303
304
897
    return ICAL_NO_WEEKDAY;
305
181k
}
306
307
/*********************** Rule parsing routines ************************/
308
309
struct icalrecur_parser {
310
    const char *rule;
311
    char *copy;
312
    char *this_clause;
313
    char *next_clause;
314
315
    struct icalrecurrencetype rt;
316
};
317
318
enum byrule
319
{
320
    NO_CONTRACTION = -1,
321
    BY_MONTH = 0,
322
    BY_WEEK_NO = 1,
323
    BY_YEAR_DAY = 2,
324
    BY_MONTH_DAY = 3,
325
    BY_DAY = 4,
326
    BY_HOUR = 5,
327
    BY_MINUTE = 6,
328
    BY_SECOND = 7,
329
    BY_SET_POS = 8
330
};
331
332
238k
#define NUM_BY_PARTS 9
333
334
enum expand_table
335
{
336
    UNKNOWN = 0,
337
    CONTRACT = 1,
338
    EXPAND = 2,
339
    ILLEGAL = 3
340
};
341
342
struct expand_split_map_struct {
343
    icalrecurrencetype_frequency frequency;
344
345
    /* Elements of the 'map' array correspond to the BYxxx rules:
346
       Second,Minute,Hour,Day,Month Day,Year Day,Week No,Month,SetPos */
347
348
    short map[NUM_BY_PARTS];
349
};
350
351
/**
352
 * The split map indicates, for a particular interval, whether a BY_*
353
 * rule part expands the number of instances in the occurrence set or
354
 * contracts it. 1=> contract, 2=>expand, and 3 means the pairing is
355
 * not allowed.
356
 */
357
358
static const struct expand_split_map_struct expand_map[] = {
359
    /*                           M  W  YD MD D  h  m  s  P */
360
    {ICAL_SECONDLY_RECURRENCE, {1, 3, 1, 1, 1, 1, 1, 1, 1}},
361
    {ICAL_MINUTELY_RECURRENCE, {1, 3, 1, 1, 1, 1, 1, 2, 1}},
362
    {ICAL_HOURLY_RECURRENCE, {1, 3, 1, 1, 1, 1, 2, 2, 1}},
363
    {ICAL_DAILY_RECURRENCE, {1, 3, 3, 1, 1, 2, 2, 2, 1}},
364
    {ICAL_WEEKLY_RECURRENCE, {1, 3, 3, 3, 2, 2, 2, 2, 1}},
365
    {ICAL_MONTHLY_RECURRENCE, {1, 3, 3, 2, 2, 2, 2, 2, 1}},
366
    {ICAL_YEARLY_RECURRENCE, {2, 2, 2, 2, 2, 2, 2, 2, 1}},
367
    {ICAL_NO_RECURRENCE, {0, 0, 0, 0, 0, 0, 0, 0, 0}} //krazy:exclude=style
368
};
369
370
static const struct recur_map {
371
    const char *str;
372
    size_t offset;
373
    int size;
374
    int min;
375
} recur_map[] = {
376
    {"BYMONTH", offsetof(struct icalrecurrencetype, by_month),
377
     ICAL_BY_MONTH_SIZE, 1},
378
    {"BYWEEKNO", offsetof(struct icalrecurrencetype, by_week_no),
379
     ICAL_BY_WEEKNO_SIZE, -1},
380
    {"BYYEARDAY", offsetof(struct icalrecurrencetype, by_year_day),
381
     ICAL_BY_YEARDAY_SIZE, -1},
382
    {"BYMONTHDAY", offsetof(struct icalrecurrencetype, by_month_day),
383
     ICAL_BY_MONTHDAY_SIZE, -1},
384
    {"BYDAY", offsetof(struct icalrecurrencetype, by_day),
385
     ICAL_BY_DAY_SIZE, 0},
386
    {"BYHOUR", offsetof(struct icalrecurrencetype, by_hour),
387
     ICAL_BY_HOUR_SIZE, 0},
388
    {"BYMINUTE", offsetof(struct icalrecurrencetype, by_minute),
389
     ICAL_BY_MINUTE_SIZE, 0},
390
    {"BYSECOND", offsetof(struct icalrecurrencetype, by_second),
391
     ICAL_BY_SECOND_SIZE, 0},
392
    {"BYSETPOS", offsetof(struct icalrecurrencetype, by_set_pos),
393
     ICAL_BY_SETPOS_SIZE, -1},
394
};
395
396
static const char *icalrecur_first_clause(struct icalrecur_parser *parser)
397
15.3k
{
398
15.3k
    char *idx;
399
400
15.3k
    parser->this_clause = parser->copy;
401
402
15.3k
    idx = strchr(parser->this_clause, ';');
403
404
15.3k
    if (idx == 0) {
405
8.15k
        parser->next_clause = 0;
406
8.15k
        return 0;
407
8.15k
    }
408
409
7.24k
    *idx = 0;
410
7.24k
    idx++;
411
7.24k
    parser->next_clause = idx;
412
413
7.24k
    return parser->this_clause;
414
15.3k
}
415
416
static const char *icalrecur_next_clause(struct icalrecur_parser *parser)
417
14.8k
{
418
14.8k
    char *idx;
419
420
14.8k
    parser->this_clause = parser->next_clause;
421
422
14.8k
    if (parser->this_clause == 0) {
423
5.63k
        return 0;
424
5.63k
    }
425
426
9.25k
    idx = strchr(parser->this_clause, ';');
427
428
9.25k
    if (idx == 0) {
429
5.59k
        parser->next_clause = 0;
430
5.59k
    } else {
431
3.65k
        *idx = 0;
432
3.65k
        idx++;
433
3.65k
        parser->next_clause = idx;
434
3.65k
    }
435
436
9.25k
    return parser->this_clause;
437
14.8k
}
438
439
static void icalrecur_clause_name_and_value(struct icalrecur_parser *parser,
440
                                            char **name, char **value)
441
24.6k
{
442
24.6k
    char *idx;
443
444
24.6k
    *name = parser->this_clause;
445
446
24.6k
    idx = strchr(parser->this_clause, '=');
447
448
24.6k
    if (idx == 0) {
449
1.59k
        *name = 0;
450
1.59k
        *value = 0;
451
1.59k
        return;
452
1.59k
    }
453
454
23.0k
    *idx = 0;
455
23.0k
    idx++;
456
23.0k
    *value = idx;
457
23.0k
}
458
459
/* returns < 0 if a parsing problem:
460
   -2 if an RSCALE rule is encountered yet we don't RSCALE support enabled
461
   -1 for all other parsing problems
462
*/
463
static int icalrecur_add_byrules(struct icalrecur_parser *parser, short *array,
464
                                 int min, int size, char *vals)
465
5.02k
{
466
5.02k
    char *t, *n;
467
5.02k
    int i = 0;
468
5.02k
    int v;
469
5.02k
    int max = size - (min == 0);
470
471
5.02k
    n = vals;
472
473
15.9k
    while (n != 0) {
474
13.0k
        if (i == size) {
475
254
            return -1;
476
254
        }
477
478
12.8k
        t = n;
479
480
12.8k
        n = strchr(t, ',');
481
482
12.8k
        if (n != 0) {
483
8.04k
            *n = 0;
484
8.04k
            n++;
485
8.04k
        }
486
487
12.8k
        v = strtol(t, &t, 10);
488
489
        /* Sanity check value */
490
12.8k
        if (v < 0) {
491
1.04k
            if (min >= 0 || v <= -max) {
492
812
                return -1;
493
812
            }
494
11.7k
        } else if (v > 0) {
495
3.22k
            if (v >= max) {
496
297
                return -1;
497
297
            }
498
8.54k
        } else if (min) {
499
307
            return -1;
500
307
        }
501
502
11.3k
        if (*t) {
503
            /* Check for leap month suffix (RSCALE only) */
504
826
            if (array == parser->rt.by_month && strcmp(t, "L") == 0) {
505
377
                if (icalrecurrencetype_rscale_is_supported()) {
506
                    /* The "L" suffix in a BYMONTH recur-rule-part
507
                       is encoded by setting a high-order bit */
508
377
                    v |= LEAP_MONTH;
509
377
                } else {
510
0
                    return -2;
511
0
                }
512
449
            } else {
513
449
                return -1;
514
449
            }
515
826
        }
516
517
10.9k
        array[i++] = (short)v;
518
10.9k
        array[i] = ICAL_RECURRENCE_ARRAY_MAX;
519
10.9k
    }
520
521
2.90k
    return 0;
522
5.02k
}
523
524
/*
525
 * Days in the BYDAY rule are expected by the code to be sorted, and while
526
 * this may be the common case, the RFC doesn't actually mandate it. This
527
 * function sorts the days taking into account the first day of week.
528
 */
529
static void sort_bydayrules(struct icalrecur_parser *parser)
530
2.39k
{
531
2.39k
    short *array;
532
2.39k
    int week_start, one, two, i, j;
533
534
2.39k
    array = parser->rt.by_day;
535
2.39k
    week_start = parser->rt.week_start;
536
537
2.39k
    for (i = 0;
538
94.2k
         i < ICAL_BY_DAY_SIZE && array[i] != ICAL_RECURRENCE_ARRAY_MAX; i++) {
539
15.9M
        for (j = 0; j < i; j++) {
540
15.8M
            one = icalrecurrencetype_day_day_of_week(array[j]) - week_start;
541
15.8M
            if (one < 0) {
542
524k
                one += 7;
543
524k
            }
544
15.8M
            two = icalrecurrencetype_day_day_of_week(array[i]) - week_start;
545
15.8M
            if (two < 0) {
546
791k
                two += 7;
547
791k
            }
548
549
15.8M
            if (one > two) {
550
26.9k
                short tmp = array[j];
551
552
26.9k
                array[j] = array[i];
553
26.9k
                array[i] = tmp;
554
26.9k
            }
555
15.8M
        }
556
91.8k
    }
557
2.39k
}
558
559
static int icalrecur_add_bydayrules(struct icalrecur_parser *parser,
560
                                    const char *vals)
561
2.25k
{
562
2.25k
    char *t, *n;
563
2.25k
    int i = 0;
564
2.25k
    short *array = parser->rt.by_day;
565
2.25k
    char *vals_copy;
566
567
2.25k
    vals_copy = icalmemory_strdup(vals);
568
2.25k
    n = vals_copy;
569
570
181k
    while (n != 0) {
571
180k
        int sign = 1;
572
180k
        signed char weekno; /* note: Novell/Groupwise sends BYDAY=255SU,
573
                                so we fit in a signed char to get -1 SU for last Sun */
574
180k
        icalrecurrencetype_weekday wd;
575
576
180k
        if (i == ICAL_BY_DAY_SIZE) {
577
201
            icalmemory_free_buffer(vals_copy);
578
201
            return -1;
579
201
        }
580
581
180k
        t = n;
582
583
180k
        n = strchr(t, ',');
584
585
180k
        if (n != 0) {
586
178k
            *n = 0;
587
178k
            n++;
588
178k
        }
589
590
        /* Get Optional weekno */
591
180k
        weekno = (signed char)strtol(t, &t, 10);
592
180k
        if (weekno < 0) {
593
478
            weekno = -weekno;
594
478
            sign = -1;
595
478
        }
596
597
        /* Outlook/Exchange generate "BYDAY=MO, FR" and "BYDAY=2 TH".
598
         * Cope with that.
599
         */
600
180k
        if (*t == ' ') {
601
194
            t++;
602
194
        }
603
604
180k
        wd = icalrecur_string_to_weekday(t);
605
606
        /* Sanity check value */
607
180k
        if (wd == ICAL_NO_WEEKDAY || weekno >= ICAL_BY_WEEKNO_SIZE) {
608
752
            icalmemory_free_buffer(vals_copy);
609
752
            return -1;
610
752
        }
611
612
179k
        array[i++] = icalrecurrencetype_encode_day(wd, sign * weekno);
613
179k
        array[i] = ICAL_RECURRENCE_ARRAY_MAX;
614
179k
    }
615
616
1.30k
    icalmemory_free_buffer(vals_copy);
617
618
1.30k
    sort_bydayrules(parser);
619
620
1.30k
    return 0;
621
2.25k
}
622
623
struct icalrecurrencetype icalrecurrencetype_from_string(const char *str)
624
15.3k
{
625
15.3k
    struct icalrecur_parser parser;
626
15.3k
    enum byrule byrule;
627
628
15.3k
    memset(&parser, 0, sizeof(parser));
629
15.3k
    icalrecurrencetype_clear(&parser.rt);
630
631
15.3k
    icalerror_check_arg_re(str != 0, "str", parser.rt);
632
633
    /* Set up the parser struct */
634
15.3k
    parser.rule = str;
635
15.3k
    parser.copy = icalmemory_strdup(parser.rule);
636
15.3k
    parser.this_clause = parser.copy;
637
638
15.3k
    if (parser.copy == 0) {
639
0
        icalerror_set_errno(ICAL_NEWFAILED_ERROR);
640
0
        return parser.rt;
641
0
    }
642
643
    /* Loop through all of the clauses */
644
15.3k
    for (icalrecur_first_clause(&parser);
645
30.2k
         parser.this_clause != 0; icalrecur_next_clause(&parser)) {
646
24.6k
        char *name, *value;
647
24.6k
        int r = 0;
648
649
24.6k
        icalrecur_clause_name_and_value(&parser, &name, &value);
650
651
24.6k
        if (name == 0) {
652
1.59k
            if (strlen(parser.this_clause) > 0) {
653
1.01k
                r = -1;
654
1.01k
            } else {
655
                /* Hit an empty name/value pair,
656
                   but we're also at the end of the string.
657
                   This was probably a trailing semicolon with no data
658
                   (e.g. "FREQ=WEEKLY;INTERVAL=1;BYDAY=MO;")
659
                */
660
586
                break;
661
586
            }
662
23.0k
        } else if (strcasecmp(name, "FREQ") == 0) {
663
4.65k
            if (parser.rt.freq != ICAL_NO_RECURRENCE) {
664
                /* Don't allow multiple FREQs */
665
199
                r = -1;
666
4.45k
            } else {
667
4.45k
                parser.rt.freq = icalrecur_string_to_freq(value);
668
4.45k
                if (parser.rt.freq == ICAL_NO_RECURRENCE) {
669
473
                    r = -1;
670
473
                }
671
4.45k
            }
672
18.4k
        } else if (strcasecmp(name, "RSCALE") == 0) {
673
2.63k
            if (parser.rt.rscale != NULL) {
674
                /* Don't allow multiple RSCALEs */
675
226
                r = -1;
676
2.40k
            } else {
677
2.40k
                parser.rt.rscale = icalmemory_strdup(value);
678
2.40k
            }
679
15.7k
        } else if (strcasecmp(name, "SKIP") == 0) {
680
1.38k
            if (parser.rt.skip != ICAL_SKIP_OMIT) {
681
                /* Don't allow multiple SKIPs */
682
200
                r = -1;
683
1.18k
            } else {
684
1.18k
                parser.rt.skip = icalrecur_string_to_skip(value);
685
1.18k
                if (parser.rt.skip == ICAL_SKIP_UNDEFINED) {
686
432
                    r = -1;
687
432
                }
688
1.18k
            }
689
14.3k
        } else if (strcasecmp(name, "COUNT") == 0) {
690
1.67k
            if (parser.rt.count > 0 || !icaltime_is_null_time(parser.rt.until)) {
691
                /* Don't allow multiple COUNTs, or both COUNT and UNTIL */
692
427
                r = -1;
693
1.24k
            } else {
694
1.24k
                parser.rt.count = atoi(value);
695
                /* don't allow count to be less than 1 */
696
1.24k
                if (parser.rt.count < 1)
697
244
                    r = -1;
698
1.24k
            }
699
12.7k
        } else if (strcasecmp(name, "UNTIL") == 0) {
700
1.60k
            if (parser.rt.count > 0 || !icaltime_is_null_time(parser.rt.until)) {
701
                /* Don't allow multiple COUNTs, or both COUNT and UNTIL */
702
447
                r = -1;
703
1.16k
            } else {
704
1.16k
                parser.rt.until = icaltime_from_string(value);
705
1.16k
                if (icaltime_is_null_time(parser.rt.until))
706
232
                    r = -1;
707
1.16k
            }
708
11.1k
        } else if (strcasecmp(name, "INTERVAL") == 0) {
709
928
            if (parser.rt.interval > 1) {
710
                /* Don't allow multiple INTERVALs */
711
205
                r = -1;
712
723
            } else {
713
723
                parser.rt.interval = (short)atoi(value);
714
                /* don't allow an interval to be less than 1
715
                   (RFC specifies an interval must be a positive integer) */
716
723
                if (parser.rt.interval < 1)
717
203
                    r = -1;
718
723
            }
719
10.1k
        } else if (strcasecmp(name, "WKST") == 0) {
720
1.65k
            if (parser.rt.week_start != ICAL_MONDAY_WEEKDAY) {
721
                /* Don't allow multiple WKSTs */
722
229
                r = -1;
723
1.42k
            } else {
724
1.42k
                parser.rt.week_start = icalrecur_string_to_weekday(value);
725
1.42k
                if (parser.rt.week_start == ICAL_NO_WEEKDAY) {
726
340
                    r = -1;
727
1.08k
                } else {
728
1.08k
                    sort_bydayrules(&parser);
729
1.08k
                }
730
1.42k
            }
731
8.51k
        } else if (strncasecmp(name, "BY", 2) == 0) {
732
7.67k
            r = -1;
733
734
33.7k
            for (byrule = 0; byrule < NUM_BY_PARTS; byrule++) {
735
33.3k
                if (strcasecmp(name + 2, recur_map[byrule].str + 2) == 0) {
736
7.28k
                    if (byrule == BY_DAY) {
737
2.25k
                        r = icalrecur_add_bydayrules(&parser, value);
738
5.02k
                    } else {
739
5.02k
                        short *array = (short *)(recur_map[byrule].offset + (size_t)&parser.rt);
740
5.02k
                        r = icalrecur_add_byrules(&parser, array,
741
5.02k
                                                  recur_map[byrule].min,
742
5.02k
                                                  recur_map[byrule].size,
743
5.02k
                                                  value);
744
5.02k
                    }
745
7.28k
                    break;
746
7.28k
                }
747
33.3k
            }
748
7.67k
        } else {
749
848
            r = -1;
750
848
        }
751
752
24.0k
        if (r) {
753
            /* Note: silently ignore when we have a leap month, yet don't have RSCALE support.
754
               The magic value "-2" indicates when that happens.
755
            */
756
9.17k
            if (r != -2) {
757
9.17k
                icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
758
9.17k
            }
759
9.17k
            if (parser.rt.rscale) {
760
687
                icalmemory_free_buffer(parser.rt.rscale);
761
687
            }
762
9.17k
            icalrecurrencetype_clear(&parser.rt);
763
9.17k
            break;
764
9.17k
        }
765
24.0k
    }
766
767
142k
    for (byrule = 0; byrule < NUM_BY_PARTS; byrule++) {
768
128k
        short *array = (short *)(recur_map[byrule].offset + (size_t)&parser.rt);
769
770
128k
        if (array[0] != ICAL_RECURRENCE_ARRAY_MAX &&
771
128k
            expand_map[parser.rt.freq].map[byrule] == ILLEGAL) {
772
1.41k
            ical_invalid_rrule_handling rruleHandlingSetting =
773
1.41k
                ical_get_invalid_rrule_handling_setting();
774
775
1.41k
            if (rruleHandlingSetting == ICAL_RRULE_TREAT_AS_ERROR) {
776
1.41k
                icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
777
1.41k
                if (parser.rt.rscale) {
778
1.08k
                    icalmemory_free_buffer(parser.rt.rscale);
779
1.08k
                }
780
1.41k
                icalrecurrencetype_clear(&parser.rt);
781
1.41k
                break;
782
1.41k
            } else {
783
0
                array[0] = ICAL_RECURRENCE_ARRAY_MAX;
784
0
            }
785
1.41k
        }
786
128k
    }
787
788
15.3k
    icalmemory_free_buffer(parser.copy);
789
790
15.3k
    return parser.rt;
791
15.3k
}
792
793
char *icalrecurrencetype_as_string(struct icalrecurrencetype *recur)
794
0
{
795
0
    char *buf;
796
797
0
    buf = icalrecurrencetype_as_string_r(recur);
798
0
    icalmemory_add_tmp_buffer(buf);
799
0
    return buf;
800
0
}
801
802
char *icalrecurrencetype_as_string_r(struct icalrecurrencetype *recur)
803
6.22k
{
804
6.22k
    char *str;
805
6.22k
    char *str_p;
806
6.22k
    size_t buf_sz = 200;
807
6.22k
    char temp[20];
808
6.22k
    int i, j;
809
810
6.22k
    if (recur == 0 || recur->freq == ICAL_NO_RECURRENCE) {
811
0
        return 0;
812
0
    }
813
814
6.22k
    str = (char *)icalmemory_new_buffer(buf_sz);
815
6.22k
    str_p = str;
816
817
6.22k
    if (recur->rscale != 0) {
818
443
        icalmemory_append_string(&str, &str_p, &buf_sz, "RSCALE=");
819
443
        icalmemory_append_string(&str, &str_p, &buf_sz, recur->rscale);
820
821
        /* Omit is the default, so no need to write that out */
822
443
        if (recur->skip != ICAL_SKIP_OMIT) {
823
203
            const char *skipstr = icalrecur_skip_to_string(recur->skip);
824
203
            icalmemory_append_string(&str, &str_p, &buf_sz, ";SKIP=");
825
203
            icalmemory_append_string(&str, &str_p, &buf_sz, skipstr);
826
203
        }
827
443
        icalmemory_append_char(&str, &str_p, &buf_sz, ';');
828
443
    }
829
830
6.22k
    icalmemory_append_string(&str, &str_p, &buf_sz, "FREQ=");
831
6.22k
    icalmemory_append_string(&str, &str_p, &buf_sz,
832
6.22k
                             icalrecur_freq_to_string(recur->freq));
833
834
    /* 1 is the default, so no need to write that out */
835
6.22k
    if (recur->interval != 1) {
836
211
        snprintf(temp, sizeof(temp), "%d", recur->interval);
837
211
        icalmemory_append_string(&str, &str_p, &buf_sz, ";INTERVAL=");
838
211
        icalmemory_append_string(&str, &str_p, &buf_sz, temp);
839
211
    }
840
841
    /* Monday is the default, so no need to write that out */
842
6.22k
    if (recur->week_start != ICAL_MONDAY_WEEKDAY &&
843
6.22k
        recur->week_start != ICAL_NO_WEEKDAY) {
844
430
        int dow = icalrecurrencetype_day_day_of_week(recur->week_start);
845
430
        const char *daystr = icalrecur_weekday_to_string(dow);
846
430
        icalmemory_append_string(&str, &str_p, &buf_sz, ";WKST=");
847
430
        icalmemory_append_string(&str, &str_p, &buf_sz, daystr);
848
430
    }
849
850
62.2k
    for (j = 0; j < NUM_BY_PARTS; j++) {
851
56.0k
        short *array = (short *)(recur_map[j].offset + (size_t)recur);
852
56.0k
        int limit = recur_map[j].size - 1;
853
854
        /* Skip unused arrays */
855
56.0k
        if (array[0] != ICAL_RECURRENCE_ARRAY_MAX) {
856
769
            icalmemory_append_char(&str, &str_p, &buf_sz, ';');
857
769
            icalmemory_append_string(&str, &str_p, &buf_sz, recur_map[j].str);
858
769
            icalmemory_append_char(&str, &str_p, &buf_sz, '=');
859
860
15.2k
            for (i = 0; i < limit && array[i] != ICAL_RECURRENCE_ARRAY_MAX; i++) {
861
14.4k
                if (j == BY_DAY) {
862
1.53k
                    int pos = icalrecurrencetype_day_position(array[i]);
863
1.53k
                    int dow = icalrecurrencetype_day_day_of_week(array[i]);
864
1.53k
                    const char *daystr = icalrecur_weekday_to_string(dow);
865
866
1.53k
                    if (pos == 0) {
867
1.27k
                        icalmemory_append_string(&str, &str_p, &buf_sz, daystr);
868
1.27k
                    } else {
869
254
                        snprintf(temp, sizeof(temp), "%d%s", pos, daystr);
870
254
                        icalmemory_append_string(&str, &str_p, &buf_sz, temp);
871
254
                    }
872
873
12.9k
                } else if (j == BY_MONTH &&
874
12.9k
                           icalrecurrencetype_month_is_leap(array[i])) {
875
198
                    snprintf(temp, sizeof(temp), "%dL",
876
198
                             icalrecurrencetype_month_month(array[i]));
877
198
                    icalmemory_append_string(&str, &str_p, &buf_sz, temp);
878
12.7k
                } else {
879
12.7k
                    snprintf(temp, sizeof(temp), "%d", array[i]);
880
12.7k
                    icalmemory_append_string(&str, &str_p, &buf_sz, temp);
881
12.7k
                }
882
883
14.4k
                if ((i + 1) < limit &&
884
14.4k
                    array[i + 1] != ICAL_RECURRENCE_ARRAY_MAX) {
885
13.7k
                    icalmemory_append_char(&str, &str_p, &buf_sz, ',');
886
13.7k
                }
887
14.4k
            }
888
769
        }
889
56.0k
    }
890
891
6.22k
    if (recur->until.year != 0) {
892
483
        temp[0] = 0;
893
483
        if (recur->until.is_date) {
894
274
            print_date_to_string(temp, &(recur->until));
895
274
        } else {
896
209
            print_datetime_to_string(temp, &(recur->until));
897
209
        }
898
899
483
        icalmemory_append_string(&str, &str_p, &buf_sz, ";UNTIL=");
900
483
        icalmemory_append_string(&str, &str_p, &buf_sz, temp);
901
483
    }
902
903
5.74k
    else if (recur->count != 0) {
904
2.66k
        snprintf(temp, sizeof(temp), "%d", recur->count);
905
2.66k
        icalmemory_append_string(&str, &str_p, &buf_sz, ";COUNT=");
906
2.66k
        icalmemory_append_string(&str, &str_p, &buf_sz, temp);
907
2.66k
    }
908
909
6.22k
    return str;
910
6.22k
}
911
912
/************************* occurrence iteration routines ******************/
913
914
/* Number of bits in an unsigned long */
915
0
#define BITS_PER_LONG (8 * sizeof(unsigned long))
916
917
/* Number of longs in mask of n bits */
918
0
#define LONGS_PER_BITS(n) ((n + BITS_PER_LONG - 1) / BITS_PER_LONG)
919
920
0
#define ICAL_YEARDAYS_MASK_SIZE (ICAL_BY_YEARDAY_SIZE + 7)
921
0
#define ICAL_YEARDAYS_MASK_OFFSET 4
922
923
struct icalrecur_iterator_impl {
924
    struct icaltimetype dtstart;    /* copy of DTSTART: to fill in defaults */
925
    struct icalrecurrencetype rule; /* copy of RRULE */
926
927
    struct icaltimetype rstart; /* DTSTART in RSCALE  */
928
    struct icaltimetype istart; /* Gregorian start time for iterator */
929
    struct icaltimetype iend;   /* Gregorian end time for iterator */
930
    struct icaltimetype last;   /* last time returned from iterator */
931
    int occurrence_no;          /* number of steps made on the iterator */
932
933
#if defined(HAVE_LIBICU)
934
    UCalendar *greg;   /* Gregorian calendar */
935
    UCalendar *rscale; /* RSCALE calendar    */
936
#endif
937
938
    struct icaltimetype period_start; /* Start date of monthly/yearly period */
939
940
    /* days[] is a bitmask of year days.  A bit value of 1 marks an occurrence.
941
       The size of the bitmask is 7 + max days in year to accommodate full first
942
       and last weeks of the year: up to 3 days in previous year and
943
       up to 4 days in following year.  As a result, the days are offset by 4:
944
       bit 0 is day -3 (3rd last day of previous year) and bit 4 is day 1
945
       of the current year.  Days in the following year use higher day numbers,
946
       e.g. day 367 is day 1 or 2 of following year depending on whether the
947
       current year is a leap year.
948
949
       days_index is the day of year of the next occurrence,
950
       with a range of -3 to 4 + days in year.
951
    */
952
    unsigned long days[LONGS_PER_BITS(ICAL_YEARDAYS_MASK_SIZE)];
953
    short days_index;
954
955
    enum byrule byrule;
956
    short by_indices[NUM_BY_PARTS];
957
    short orig_data[NUM_BY_PARTS]; /**< 1 if there was data in the byrule */
958
959
    short *by_ptrs[NUM_BY_PARTS]; /**< Pointers into the by_* array elements of the rule */
960
};
961
962
static void daysmask_clearall(unsigned long mask[])
963
0
{
964
0
    memset(mask, 0,
965
0
           sizeof(unsigned long) * LONGS_PER_BITS(ICAL_YEARDAYS_MASK_SIZE));
966
0
}
967
968
static void daysmask_setbit(unsigned long mask[], short n, int v)
969
0
{
970
0
    n += ICAL_YEARDAYS_MASK_OFFSET;
971
0
    if (v) {
972
0
        mask[n / BITS_PER_LONG] |= (1UL << (n % BITS_PER_LONG));
973
0
    } else {
974
0
        mask[n / BITS_PER_LONG] &= ~(1UL << (n % BITS_PER_LONG));
975
0
    }
976
0
}
977
978
static unsigned long daysmask_getbit(unsigned long mask[], short n)
979
0
{
980
0
    n += ICAL_YEARDAYS_MASK_OFFSET;
981
0
    return (mask[n / BITS_PER_LONG] >> (n % BITS_PER_LONG)) & 1;
982
0
}
983
984
int icalrecur_iterator_sizeof_byarray(short *byarray)
985
0
{
986
0
    int array_itr;
987
988
0
    for (array_itr = 0;
989
0
         byarray[array_itr] != ICAL_RECURRENCE_ARRAY_MAX; array_itr++) {
990
0
    }
991
992
0
    return array_itr;
993
0
}
994
995
static int has_by_data(icalrecur_iterator *impl, enum byrule byrule)
996
0
{
997
0
    return (impl->orig_data[byrule] == 1);
998
0
}
999
1000
static void setup_defaults(icalrecur_iterator *impl,
1001
                           enum byrule byrule, int deftime)
1002
0
{
1003
0
    icalrecurrencetype_frequency freq = impl->rule.freq;
1004
1005
0
    if (expand_map[freq].map[byrule] == EXPAND) {
1006
        /* Re-write the BY rule arrays with data from the DTSTART time so
1007
           we don't have to explicitly deal with DTSTART */
1008
0
        if (impl->by_ptrs[byrule][0] == ICAL_RECURRENCE_ARRAY_MAX) {
1009
0
            impl->by_ptrs[byrule][0] = (short)deftime;
1010
0
        }
1011
0
    }
1012
0
}
1013
1014
/** Calculate ISO weeks per year
1015
   https://en.wikipedia.org/wiki/ISO_week_date#Weeks_per_year */
1016
static int weeks_in_year(int year)
1017
0
{
1018
    /* Long years occur when year starts on Thu or leap year starts on Wed */
1019
0
    int dow = icaltime_day_of_week(icaltime_from_day_of_year(1, year));
1020
0
    int is_long = (dow == 5 || (dow == 4 && icaltime_is_leap_year(year)));
1021
1022
0
    return (52 + is_long);
1023
0
}
1024
1025
/** Calculate the number of Gregorian months between 2 dates */
1026
static int __greg_month_diff(icaltimetype a, icaltimetype b)
1027
0
{
1028
0
    return (12 * (b.year - a.year) + (b.month - a.month));
1029
0
}
1030
1031
static void __get_start_time(icalrecur_iterator *impl, icaltimetype date,
1032
                             int *hour, int *minute, int *second)
1033
0
{
1034
0
    icalrecurrencetype_frequency freq = impl->rule.freq;
1035
1036
0
    if (freq == ICAL_HOURLY_RECURRENCE) {
1037
0
        *hour = date.hour;
1038
0
    } else if (has_by_data(impl, BY_HOUR)) {
1039
0
        *hour = impl->by_ptrs[BY_HOUR][0];
1040
0
    } else {
1041
0
        *hour = impl->rstart.hour;
1042
0
    }
1043
1044
0
    if (freq == ICAL_MINUTELY_RECURRENCE) {
1045
0
        *minute = date.minute;
1046
0
    } else if (has_by_data(impl, BY_MINUTE)) {
1047
0
        *minute = impl->by_ptrs[BY_MINUTE][0];
1048
0
    } else {
1049
0
        *minute = impl->rstart.minute;
1050
0
    }
1051
1052
0
    if (freq == ICAL_SECONDLY_RECURRENCE) {
1053
0
        *second = date.second;
1054
0
    } else if (has_by_data(impl, BY_SECOND)) {
1055
0
        *second = impl->by_ptrs[BY_SECOND][0];
1056
0
    } else {
1057
0
        *second = impl->rstart.second;
1058
0
    }
1059
0
}
1060
1061
static int __day_diff(icalrecur_iterator *impl, icaltimetype a, icaltimetype b);
1062
1063
#if defined(HAVE_LIBICU)
1064
/*
1065
 * Callbacks for recurrence rules with RSCALE support (using ICU)
1066
 *
1067
 * References:
1068
 *   - tools.ietf.org/html/rfc7529
1069
 *   - en.wikipedia.org/wiki/Intercalation_%28timekeeping%29
1070
 *   - icu-project.org/apiref/icu4c/ucal_8h.html
1071
 *   - cldr.unicode.org/development/development-process/design-proposals/chinese-calendar-support
1072
 *   - cldr.unicode.org/development/development-process/design-proposals/islamic-calendar-types
1073
 *
1074
 * ICU Notes:
1075
 *   - Months are 0-based
1076
 *   - Leap months in Chinese and Hebrew calendars are handled differently
1077
 */
1078
1079
icalarray *icalrecurrencetype_rscale_supported_calendars(void)
1080
{
1081
    UErrorCode status = U_ZERO_ERROR;
1082
    UEnumeration *en;
1083
    icalarray *calendars;
1084
    const char *cal;
1085
1086
    calendars = icalarray_new(sizeof(const char **), 20);
1087
1088
    en = ucal_getKeywordValuesForLocale("calendar", "", false, &status);
1089
    while ((cal = uenum_next(en, NULL, &status))) {
1090
        cal = icalmemory_tmp_copy(cal);
1091
        icalarray_append(calendars, &cal);
1092
    }
1093
    uenum_close(en);
1094
1095
    return calendars;
1096
}
1097
1098
static void set_second(icalrecur_iterator *impl, int second)
1099
{
1100
    ucal_set(impl->rscale, UCAL_SECOND, (int32_t)second);
1101
}
1102
1103
static void set_minute(icalrecur_iterator *impl, int minute)
1104
{
1105
    ucal_set(impl->rscale, UCAL_MINUTE, (int32_t)minute);
1106
}
1107
1108
static void set_hour(icalrecur_iterator *impl, int hour)
1109
{
1110
    ucal_set(impl->rscale, UCAL_HOUR_OF_DAY, (int32_t)hour);
1111
}
1112
1113
static void __set_month(icalrecur_iterator *impl, int month)
1114
{
1115
    int is_leap_month = icalrecurrencetype_month_is_leap(month);
1116
1117
    month = icalrecurrencetype_month_month(month) - 1; /* UCal is 0-based */
1118
1119
    ucal_set(impl->rscale, UCAL_MONTH, (int32_t)month);
1120
    if (is_leap_month)
1121
        ucal_set(impl->rscale, UCAL_IS_LEAP_MONTH, 1);
1122
}
1123
1124
static int set_month(icalrecur_iterator *impl, int month)
1125
{
1126
    UErrorCode status = U_ZERO_ERROR;
1127
    int actual_month;
1128
1129
    __set_month(impl, month);
1130
1131
    ucal_set(impl->rscale, UCAL_DAY_OF_MONTH, (int32_t)1);
1132
1133
    actual_month = 1 + /* UCal is 0-based */
1134
                   (int)ucal_get(impl->rscale, UCAL_MONTH, &status);
1135
1136
    if (ucal_get(impl->rscale, UCAL_IS_LEAP_MONTH, &status)) {
1137
        actual_month |= LEAP_MONTH;
1138
    }
1139
1140
    if (actual_month != month) {
1141
        switch (impl->rule.skip) {
1142
        default:
1143
            /* Should never get here! */
1144
1145
        case ICAL_SKIP_OMIT:
1146
            /* Invalid month */
1147
            return 0;
1148
1149
        case ICAL_SKIP_BACKWARD:
1150
            /* Skip back to next valid month */
1151
            ucal_add(impl->rscale, UCAL_MONTH, (int32_t)-1, &status);
1152
            break;
1153
1154
        case ICAL_SKIP_FORWARD:
1155
            /* UCal skips forward to valid month by default */
1156
            break;
1157
        }
1158
    }
1159
1160
    return (1 + /* UCal is 0-based */
1161
            (int)ucal_get(impl->rscale, UCAL_MONTH, &status));
1162
}
1163
1164
static int get_months_in_year(icalrecur_iterator *impl, int year)
1165
{
1166
    UErrorCode status = U_ZERO_ERROR;
1167
1168
    if (year) {
1169
        ucal_set(impl->rscale, UCAL_YEAR, (int32_t)year);
1170
    }
1171
1172
    return (1 + /* UCal is 0-based */
1173
            (int)ucal_getLimit(impl->rscale, UCAL_MONTH,
1174
                               UCAL_ACTUAL_MAXIMUM, &status));
1175
}
1176
1177
static int get_days_in_year(icalrecur_iterator *impl, int year)
1178
{
1179
    UErrorCode status = U_ZERO_ERROR;
1180
1181
    if (year) {
1182
        ucal_set(impl->rscale, UCAL_YEAR, (int32_t)year);
1183
    }
1184
1185
    return (int)ucal_getLimit(impl->rscale, UCAL_DAY_OF_YEAR,
1186
                              UCAL_ACTUAL_MAXIMUM, &status);
1187
}
1188
1189
static void set_day_of_year(icalrecur_iterator *impl, int doy)
1190
{
1191
    if (doy < 1) {
1192
        doy += get_days_in_year(impl, 0);
1193
    }
1194
1195
    ucal_set(impl->rscale, UCAL_DAY_OF_YEAR, (int32_t)doy);
1196
}
1197
1198
static int get_start_of_week(icalrecur_iterator *impl)
1199
{
1200
    UErrorCode status = U_ZERO_ERROR;
1201
    int doy, dow;
1202
1203
    doy = (int)ucal_get(impl->rscale, UCAL_DAY_OF_YEAR, &status);
1204
    dow = (int)ucal_get(impl->rscale, UCAL_DAY_OF_WEEK, &status);
1205
    dow -= impl->rule.week_start;
1206
    if (dow < 0) {
1207
        dow += 7;
1208
    }
1209
1210
    return (doy - dow);
1211
}
1212
1213
static int get_day_of_week(icalrecur_iterator *impl)
1214
{
1215
    UErrorCode status = U_ZERO_ERROR;
1216
1217
    return (int)ucal_get(impl->rscale, UCAL_DAY_OF_WEEK, &status);
1218
}
1219
1220
static int get_week_number(icalrecur_iterator *impl, struct icaltimetype tt)
1221
{
1222
    UErrorCode status = U_ZERO_ERROR;
1223
    UDate last_millis;
1224
    int month, weekno;
1225
1226
    /* Save existing rscale date */
1227
    last_millis = ucal_getMillis(impl->rscale, &status);
1228
1229
    month = icalrecurrencetype_month_month(tt.month) - 1; /* UCal is 0-based */
1230
    ucal_setDate(impl->rscale,
1231
                 (int32_t)tt.year, (int32_t)month, (int32_t)tt.day, &status);
1232
    if (icalrecurrencetype_month_is_leap(tt.month)) {
1233
        ucal_set(impl->rscale, UCAL_IS_LEAP_MONTH, 1);
1234
    }
1235
1236
    weekno = (int)ucal_get(impl->rscale, UCAL_WEEK_OF_YEAR, &status);
1237
1238
    /* Restore saved rscale date */
1239
    ucal_setMillis(impl->rscale, last_millis, &status);
1240
1241
    return weekno;
1242
}
1243
1244
static int get_days_in_month(icalrecur_iterator *impl, int month, int year)
1245
{
1246
    UErrorCode status = U_ZERO_ERROR;
1247
1248
    ucal_set(impl->rscale, UCAL_YEAR, (int32_t)year);
1249
1250
    if (!month) {
1251
        month = impl->rstart.month;
1252
    }
1253
    __set_month(impl, month);
1254
1255
    return (int)ucal_getLimit(impl->rscale,
1256
                              UCAL_DAY_OF_MONTH, UCAL_ACTUAL_MAXIMUM, &status);
1257
}
1258
1259
static int get_day_of_year(icalrecur_iterator *impl,
1260
                           int year, int month, int day, int *dow)
1261
{
1262
    UErrorCode status = U_ZERO_ERROR;
1263
1264
    ucal_set(impl->rscale, UCAL_YEAR, (int32_t)year);
1265
1266
    if (!month) {
1267
        month = impl->rstart.month;
1268
    }
1269
    __set_month(impl, month);
1270
1271
    if (!day) {
1272
        day = impl->rstart.day;
1273
    } else if (day < 0) {
1274
        day += 1 + (int)ucal_getLimit(impl->rscale, UCAL_DAY_OF_MONTH,
1275
                                      UCAL_ACTUAL_MAXIMUM, &status);
1276
    }
1277
    ucal_set(impl->rscale, UCAL_DAY_OF_MONTH, (int32_t)day);
1278
1279
    if (dow) {
1280
        *dow = (int)ucal_get(impl->rscale, UCAL_DAY_OF_WEEK, &status);
1281
    }
1282
1283
    return (int)ucal_get(impl->rscale, UCAL_DAY_OF_YEAR, &status);
1284
}
1285
1286
static struct icaltimetype occurrence_as_icaltime(icalrecur_iterator *impl,
1287
                                                  int normalize)
1288
{
1289
    struct icaltimetype tt = impl->dtstart;
1290
    UErrorCode status = U_ZERO_ERROR;
1291
    UCalendar *cal = impl->rscale;
1292
    int is_leap_month = 0;
1293
1294
    if (normalize && (impl->rscale != impl->greg)) {
1295
        /* Convert to Gregorian date */
1296
        UDate millis = ucal_getMillis(impl->rscale, &status);
1297
1298
        ucal_setMillis(impl->greg, millis, &status);
1299
        cal = impl->greg;
1300
    } else {
1301
        is_leap_month =
1302
            (int)ucal_get(impl->rscale, UCAL_IS_LEAP_MONTH, &status);
1303
    }
1304
1305
    tt.year = (int)ucal_get(cal, UCAL_YEAR, &status);
1306
    tt.day = (int)ucal_get(cal, UCAL_DATE, &status);
1307
    tt.month = 1 + /* UCal is 0-based */
1308
               (int)ucal_get(cal, UCAL_MONTH, &status);
1309
    if (is_leap_month) {
1310
        tt.month |= LEAP_MONTH;
1311
    }
1312
1313
    if (!tt.is_date) {
1314
        tt.hour = (int)ucal_get(cal, UCAL_HOUR_OF_DAY, &status);
1315
        tt.minute = (int)ucal_get(cal, UCAL_MINUTE, &status);
1316
        tt.second = (int)ucal_get(cal, UCAL_SECOND, &status);
1317
    }
1318
1319
    return tt;
1320
}
1321
1322
struct icaltimetype __icaltime_from_day_of_year(icalrecur_iterator *impl,
1323
                                                int day, int year, int *weekno)
1324
{
1325
    ucal_set(impl->rscale, UCAL_YEAR, (int32_t)year);
1326
    if (day < 0) {
1327
        day += get_days_in_year(impl, 0) + 1;
1328
    }
1329
1330
    ucal_set(impl->rscale, UCAL_DAY_OF_YEAR, (int32_t)day);
1331
1332
    if (weekno) {
1333
        UErrorCode status = U_ZERO_ERROR;
1334
1335
        *weekno = (int)ucal_get(impl->rscale, UCAL_WEEK_OF_YEAR, &status);
1336
    }
1337
1338
    return occurrence_as_icaltime(impl, 0);
1339
}
1340
1341
static void increment_year(icalrecur_iterator *impl, int inc)
1342
{
1343
    UErrorCode status = U_ZERO_ERROR;
1344
1345
    ucal_add(impl->rscale, UCAL_YEAR, (int32_t)inc, &status);
1346
}
1347
1348
static void __increment_month(icalrecur_iterator *impl, int inc)
1349
{
1350
    UErrorCode status = U_ZERO_ERROR;
1351
1352
    ucal_add(impl->rscale, UCAL_MONTH, (int32_t)inc, &status);
1353
}
1354
1355
static void increment_monthday(icalrecur_iterator *impl, int inc)
1356
{
1357
    UErrorCode status = U_ZERO_ERROR;
1358
1359
    ucal_add(impl->rscale, UCAL_DAY_OF_MONTH, (int32_t)inc, &status);
1360
}
1361
1362
static void increment_hour(icalrecur_iterator *impl, int inc)
1363
{
1364
    UErrorCode status = U_ZERO_ERROR;
1365
1366
    ucal_add(impl->rscale, UCAL_HOUR_OF_DAY, (int32_t)inc, &status);
1367
}
1368
1369
static void increment_minute(icalrecur_iterator *impl, int inc)
1370
{
1371
    UErrorCode status = U_ZERO_ERROR;
1372
1373
    ucal_add(impl->rscale, UCAL_MINUTE, (int32_t)inc, &status);
1374
}
1375
1376
static void increment_second(icalrecur_iterator *impl, int inc)
1377
{
1378
    UErrorCode status = U_ZERO_ERROR;
1379
1380
    ucal_add(impl->rscale, UCAL_SECOND, (int32_t)inc, &status);
1381
}
1382
1383
static int validate_byrule(icalrecur_iterator *impl,
1384
                           enum byrule byrule, UCalendarDateFields field,
1385
                           short (*decode_val)(short *, unsigned),
1386
                           unsigned int decode_flags)
1387
{
1388
    if (has_by_data(impl, byrule)) {
1389
        UErrorCode status = U_ZERO_ERROR;
1390
        short *by_ptr = impl->by_ptrs[byrule];
1391
        short max =
1392
            (short)ucal_getLimit(impl->rscale, field, UCAL_MAXIMUM, &status);
1393
        short idx;
1394
1395
        for (idx = 0; by_ptr[idx] != ICAL_RECURRENCE_ARRAY_MAX; idx++) {
1396
            short val = decode_val ? decode_val(&by_ptr[idx], decode_flags) : by_ptr[idx];
1397
1398
            if (abs(val) > max)
1399
                return 0;
1400
        }
1401
    }
1402
1403
    return 1;
1404
}
1405
1406
static short decode_month(short *month, unsigned int is_hebrew)
1407
{
1408
    if (is_hebrew && *month > 5) { /* 5L == 0x1005 */
1409
        /* Hebrew calendar:
1410
           Translate RSCALE months to ICU (numbered 1-13, where 6 is leap).
1411
           Hence, 5L maps to 6 and 6-12 map to 7-13. */
1412
        *month = icalrecurrencetype_month_month(*month) + 1;
1413
    }
1414
1415
    return icalrecurrencetype_month_month(*month) - 1; /* UCal is 0-based */
1416
}
1417
1418
static short decode_day(short *day, unsigned int flags)
1419
{
1420
    _unused(flags);
1421
1422
    return icalrecurrencetype_day_position(*day);
1423
}
1424
1425
static int initialize_rscale(icalrecur_iterator *impl)
1426
{
1427
    struct icalrecurrencetype rule = impl->rule;
1428
    struct icaltimetype dtstart = impl->dtstart;
1429
    char locale[ULOC_KEYWORD_AND_VALUES_CAPACITY] = "";
1430
    UErrorCode status = U_ZERO_ERROR;
1431
    UChar *tzid = (UChar *)UCAL_UNKNOWN_ZONE_ID;
1432
    short is_hebrew = 0;
1433
1434
    /* Convert the UTF8 timezoneid of dstart to ICU UChar. */
1435
    const char *src = icaltimezone_get_location((icaltimezone *)dtstart.zone);
1436
    if (!src) {
1437
        const char *prefix = icaltimezone_tzid_prefix();
1438
1439
        src = icaltimezone_get_tzid((icaltimezone *)dtstart.zone);
1440
        if (src && !strncmp(src, prefix, strlen(prefix))) {
1441
            /* Skip past our prefix */
1442
            src += strlen(prefix);
1443
        }
1444
    }
1445
    if (src) {
1446
        size_t len = (strlen(src) + 1) * U_SIZEOF_UCHAR;
1447
        tzid = icalmemory_tmp_buffer(len);
1448
        tzid = u_strFromUTF8Lenient(tzid, (int32_t)len, NULL, src, -1, &status);
1449
        if (U_FAILURE(status)) {
1450
            icalerror_set_errno(ICAL_INTERNAL_ERROR);
1451
            return 0;
1452
        }
1453
    }
1454
1455
    /* Create locale for Gregorian calendar */
1456
    (void)uloc_setKeywordValue("calendar", "gregorian",
1457
                               locale, sizeof(locale), &status);
1458
1459
    /* Create Gregorian calendar and set to DTSTART */
1460
    impl->greg = ucal_open(tzid, -1, locale, UCAL_DEFAULT, &status);
1461
    if (impl->greg) {
1462
        ucal_setDateTime(impl->greg,
1463
                         (int32_t)dtstart.year,
1464
                         (int32_t)(dtstart.month - 1), /* UCal is 0-based */
1465
                         (int32_t)dtstart.day,
1466
                         (int32_t)dtstart.hour,
1467
                         (int32_t)dtstart.minute,
1468
                         (int32_t)dtstart.second, &status);
1469
    }
1470
    if (!impl->greg || U_FAILURE(status)) {
1471
        icalerror_set_errno(ICAL_INTERNAL_ERROR);
1472
        return 0;
1473
    }
1474
1475
    if (!rule.rscale) {
1476
        /* Use Gregorian as RSCALE */
1477
        impl->rscale = impl->greg;
1478
    } else {
1479
        UEnumeration *en;
1480
        const char *cal;
1481
        char *r;
1482
1483
        /* Lowercase the specified calendar */
1484
        for (r = rule.rscale; *r; r++) {
1485
            *r = tolower((int)*r);
1486
        }
1487
1488
        /* Check if specified calendar is supported */
1489
        en = ucal_getKeywordValuesForLocale("calendar", "", false, &status);
1490
        while ((cal = uenum_next(en, NULL, &status))) {
1491
            if (!strcmp(cal, rule.rscale)) {
1492
                is_hebrew = !strcmp(rule.rscale, "hebrew");
1493
                break;
1494
            }
1495
        }
1496
        uenum_close(en);
1497
        if (!cal) {
1498
            icalerror_set_errno(ICAL_UNIMPLEMENTED_ERROR);
1499
            return 0;
1500
        }
1501
1502
        /* Create locale for RSCALE calendar */
1503
        (void)uloc_setKeywordValue("calendar", rule.rscale,
1504
                                   locale, sizeof(locale), &status);
1505
1506
        /* Create RSCALE calendar and set to DTSTART */
1507
        impl->rscale = ucal_open(tzid, -1, locale, UCAL_DEFAULT, &status);
1508
        if (impl->rscale) {
1509
            UDate millis = ucal_getMillis(impl->greg, &status);
1510
1511
            ucal_setMillis(impl->rscale, millis, &status);
1512
        }
1513
        if (!impl->rscale || U_FAILURE(status)) {
1514
            icalerror_set_errno(ICAL_INTERNAL_ERROR);
1515
            return 0;
1516
        }
1517
    }
1518
1519
    /* Validate BY_* array values whose legal maximums differ based on RSCALE */
1520
    if (!validate_byrule(impl, BY_MONTH, UCAL_MONTH,
1521
                         &decode_month, (unsigned int)is_hebrew) ||
1522
        !validate_byrule(impl, BY_DAY, UCAL_WEEK_OF_YEAR, &decode_day, 0) ||
1523
        !validate_byrule(impl, BY_MONTH_DAY, UCAL_DAY_OF_MONTH, NULL, 0) ||
1524
        !validate_byrule(impl, BY_YEAR_DAY, UCAL_DAY_OF_YEAR, NULL, 0) ||
1525
        !validate_byrule(impl, BY_WEEK_NO, UCAL_WEEK_OF_YEAR, NULL, 0) ||
1526
        !validate_byrule(impl, BY_SET_POS, UCAL_DAY_OF_YEAR, NULL, 0)) {
1527
        icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
1528
        return 0;
1529
    }
1530
1531
    /* Set iCalendar defaults */
1532
    ucal_setAttribute(impl->rscale, UCAL_MINIMAL_DAYS_IN_FIRST_WEEK, 4);
1533
    ucal_setAttribute(impl->rscale, UCAL_FIRST_DAY_OF_WEEK, rule.week_start);
1534
1535
    /* Get rstart (DTSTART in RSCALE) */
1536
    impl->rstart = occurrence_as_icaltime(impl, 0);
1537
1538
    return 1;
1539
}
1540
1541
/** Sets the Gregorian date and convert to RSCALE */
1542
static void set_datetime(icalrecur_iterator *impl, icaltimetype date)
1543
{
1544
    UErrorCode status = U_ZERO_ERROR;
1545
1546
    impl->last.is_date = impl->rstart.is_date;
1547
    impl->last.zone = impl->rstart.zone;
1548
1549
    if (impl->rstart.is_date) {
1550
        ucal_setDate(impl->greg,
1551
                     (int32_t)date.year,
1552
                     (int32_t)(date.month - 1), /* UCal is 0-based */
1553
                     (int32_t)date.day, &status);
1554
    } else {
1555
        int hour, minute, second;
1556
1557
        __get_start_time(impl, date, &hour, &minute, &second);
1558
1559
        ucal_setDateTime(impl->greg,
1560
                         (int32_t)date.year,
1561
                         (int32_t)(date.month - 1), /* UCal is 0-based */
1562
                         (int32_t)date.day,
1563
                         (int32_t)hour,
1564
                         (int32_t)minute,
1565
                         (int32_t)second,
1566
                         &status);
1567
    }
1568
1569
    if (impl->rscale != impl->greg) {
1570
        UDate millis = ucal_getMillis(impl->greg, &status);
1571
        ucal_setMillis(impl->rscale, millis, &status);
1572
    }
1573
}
1574
1575
/** Calculate the number of RSCALE months between 2 dates */
1576
static int month_diff(icalrecur_iterator *impl, icaltimetype a, icaltimetype b)
1577
{
1578
    int diff;
1579
1580
    if (impl->rscale == impl->greg) {
1581
        /* Use simple Gregorian math */
1582
        diff = __greg_month_diff(a, b);
1583
    } else if (a.year == b.year) {
1584
        diff = b.month - a.month;
1585
    } else {
1586
        /* Count months in each year to account for leap months */
1587
        UErrorCode status = U_ZERO_ERROR;
1588
        UDate millis;
1589
        int year = a.year;
1590
1591
        /* Save current date */
1592
        millis = ucal_getMillis(impl->rscale, &status);
1593
1594
        set_day_of_year(impl, 1);
1595
        diff = get_months_in_year(impl, year) - a.month;
1596
        while (++year < b.year)
1597
            diff += get_months_in_year(impl, year);
1598
        diff += b.month;
1599
1600
        /* Restore date */
1601
        ucal_setMillis(impl->rscale, millis, &status);
1602
    }
1603
1604
    return diff;
1605
}
1606
1607
/** Calculate the number of RSCALE days between 2 dates */
1608
static int day_diff(icalrecur_iterator *impl, icaltimetype a, icaltimetype b)
1609
{
1610
    UErrorCode status = U_ZERO_ERROR;
1611
    UDate millis;
1612
    int diff;
1613
1614
    /* Save current date */
1615
    millis = ucal_getMillis(impl->rscale, &status);
1616
1617
    set_day_of_year(impl, 1);
1618
1619
    diff = __day_diff(impl, a, b);
1620
1621
    /* Restore date */
1622
    ucal_setMillis(impl->rscale, millis, &status);
1623
1624
    return diff;
1625
}
1626
1627
static void reset_period_start(icalrecur_iterator *impl)
1628
{
1629
    struct icaltimetype start = impl->period_start;
1630
1631
    (void)get_day_of_year(impl, start.year, start.month, start.day, NULL);
1632
}
1633
1634
#else /* !HAVE_LIBICU */
1635
1636
/*
1637
 * Callbacks for recurrence rules without RSCALE (Gregorian only)
1638
 */
1639
1640
icalarray *icalrecurrencetype_rscale_supported_calendars(void)
1641
0
{
1642
0
    icalarray *calendars = icalarray_new(sizeof(const char **), 1);
1643
0
    const char *cal = "GREGORIAN";
1644
1645
0
    icalarray_append(calendars, &cal);
1646
1647
0
    return calendars;
1648
0
}
1649
1650
static void set_second(icalrecur_iterator *impl, int second)
1651
0
{
1652
0
    impl->last.second = second;
1653
0
}
1654
1655
static void set_minute(icalrecur_iterator *impl, int minute)
1656
0
{
1657
0
    impl->last.minute = minute;
1658
0
}
1659
1660
static void set_hour(icalrecur_iterator *impl, int hour)
1661
0
{
1662
0
    impl->last.hour = hour;
1663
0
}
1664
1665
static int set_month(icalrecur_iterator *impl, int month)
1666
0
{
1667
0
    return (impl->last.month = month);
1668
0
}
1669
1670
0
#define get_months_in_year(impl, year) (12)
1671
1672
static int get_days_in_year(icalrecur_iterator *impl, int year)
1673
0
{
1674
0
    _unused(impl);
1675
1676
0
    return icaltime_days_in_year(year);
1677
0
}
1678
1679
static void set_day_of_year(icalrecur_iterator *impl, int doy)
1680
0
{
1681
0
    struct icaltimetype next;
1682
1683
0
    if (doy < 1) {
1684
0
        doy += get_days_in_year(impl, impl->last.year);
1685
0
    }
1686
1687
0
    next = icaltime_from_day_of_year(doy, impl->last.year);
1688
1689
0
    impl->last.day = next.day;
1690
0
    impl->last.month = next.month;
1691
0
    impl->last.year = next.year;
1692
0
}
1693
1694
static int get_start_of_week(icalrecur_iterator *impl)
1695
0
{
1696
0
    return icaltime_start_doy_week(impl->last, impl->rule.week_start);
1697
0
}
1698
1699
static int get_day_of_week(icalrecur_iterator *impl)
1700
0
{
1701
0
    return icaltime_day_of_week(impl->last);
1702
0
}
1703
1704
/** Calculate ISO week number
1705
   https://en.wikipedia.org/wiki/ISO_week_date#Calculation */
1706
static int get_week_number(icalrecur_iterator *impl, struct icaltimetype tt)
1707
0
{
1708
0
    int dow, week;
1709
1710
0
    _unused(impl);
1711
1712
    /* Normalize day of week so that week_start day is 1 */
1713
0
    dow = icaltime_day_of_week(tt) - (impl->rule.week_start - 1);
1714
0
    if (dow <= 0)
1715
0
        dow += 7;
1716
1717
0
    week = (icaltime_day_of_year(tt) - dow + 10) / 7;
1718
0
    if (week < 1) {
1719
        /* Last week of preceding year */
1720
0
        week = weeks_in_year(tt.year - 1);
1721
0
    } else if (week > weeks_in_year(tt.year)) {
1722
        /* First week of following year */
1723
0
        week = 1;
1724
0
    }
1725
1726
0
    return week;
1727
0
}
1728
1729
static int get_days_in_month(icalrecur_iterator *impl, int month, int year)
1730
0
{
1731
0
    _unused(impl);
1732
1733
0
    return icaltime_days_in_month(month, year);
1734
0
}
1735
1736
static int get_day_of_year(icalrecur_iterator *impl,
1737
                           int year, int month, int day, int *dow)
1738
0
{
1739
0
    struct icaltimetype t = impl->dtstart;
1740
1741
0
    t.is_date = 1;
1742
0
    t.year = year;
1743
1744
0
    if (!month) {
1745
0
        month = impl->dtstart.month;
1746
0
    }
1747
0
    t.month = month;
1748
1749
0
    if (!day) {
1750
0
        day = impl->dtstart.day;
1751
0
    } else if (day < 0) {
1752
0
        day += icaltime_days_in_month(month, year) + 1;
1753
0
    }
1754
0
    t.day = day;
1755
1756
0
    if (dow) {
1757
0
        *dow = icaltime_day_of_week(t);
1758
0
    }
1759
1760
0
    return icaltime_day_of_year(t);
1761
0
}
1762
1763
static struct icaltimetype occurrence_as_icaltime(icalrecur_iterator *impl,
1764
                                                  int normalize)
1765
0
{
1766
0
    return (normalize ? icaltime_normalize(impl->last) : impl->last);
1767
0
}
1768
1769
struct icaltimetype __icaltime_from_day_of_year(icalrecur_iterator *impl,
1770
                                                int day, int year, int *weekno)
1771
0
{
1772
0
    struct icaltimetype tt;
1773
1774
0
    if (day < 0) {
1775
0
        day += get_days_in_year(impl, year) + 1;
1776
0
    }
1777
1778
0
    tt = icaltime_from_day_of_year(day, year);
1779
1780
0
    if (weekno) {
1781
0
        *weekno = get_week_number(impl, tt);
1782
0
    }
1783
0
    return tt;
1784
0
}
1785
1786
static void increment_year(icalrecur_iterator *impl, int inc)
1787
0
{
1788
0
    impl->last.year += inc;
1789
0
}
1790
1791
static void __increment_month(icalrecur_iterator *impl, int inc)
1792
0
{
1793
0
    int years;
1794
1795
0
    impl->last.month += inc;
1796
1797
    /* Months are offset by one */
1798
0
    impl->last.month--;
1799
1800
0
    years = impl->last.month / 12;
1801
1802
0
    impl->last.month = impl->last.month % 12;
1803
1804
0
    if (impl->last.month < 0) {
1805
0
        impl->last.month = impl->last.month + 12;
1806
0
        years--;
1807
0
    }
1808
1809
0
    impl->last.month++;
1810
1811
0
    if (years != 0) {
1812
0
        increment_year(impl, years);
1813
0
    }
1814
0
}
1815
1816
static void increment_monthday(icalrecur_iterator *impl, int inc)
1817
0
{
1818
0
    icaltime_adjust(&impl->last, inc, 0, 0, 0);
1819
0
}
1820
1821
static void increment_hour(icalrecur_iterator *impl, int inc)
1822
0
{
1823
0
    icaltime_adjust(&impl->last, 0, inc, 0, 0);
1824
0
}
1825
1826
static void increment_minute(icalrecur_iterator *impl, int inc)
1827
0
{
1828
0
    icaltime_adjust(&impl->last, 0, 0, inc, 0);
1829
0
}
1830
1831
static void increment_second(icalrecur_iterator *impl, int inc)
1832
0
{
1833
0
    icaltime_adjust(&impl->last, 0, 0, 0, inc);
1834
0
}
1835
1836
static int initialize_rscale(icalrecur_iterator *impl)
1837
0
{
1838
0
    if (impl->rule.rscale && strcasecmp(impl->rule.rscale, "GREGORIAN")) {
1839
0
        icalerror_set_errno(ICAL_UNIMPLEMENTED_ERROR);
1840
0
        return 0;
1841
0
    }
1842
1843
0
    impl->rstart = impl->dtstart;
1844
1845
0
    return 1;
1846
0
}
1847
1848
/** Sets the Gregorian date */
1849
static void set_datetime(icalrecur_iterator *impl, icaltimetype date)
1850
0
{
1851
0
    impl->last.year = date.year;
1852
0
    impl->last.month = date.month;
1853
0
    impl->last.day = date.day;
1854
0
    impl->last.is_date = impl->dtstart.is_date;
1855
0
    impl->last.zone = impl->dtstart.zone;
1856
1857
0
    if (!impl->dtstart.is_date) {
1858
0
        __get_start_time(impl, date, &impl->last.hour,
1859
0
                         &impl->last.minute, &impl->last.second);
1860
0
    }
1861
0
}
1862
1863
/** Calculate the number of Gregorian months between 2 dates */
1864
static int month_diff(icalrecur_iterator *impl, icaltimetype a, icaltimetype b)
1865
0
{
1866
0
    _unused(impl);
1867
1868
0
    return __greg_month_diff(a, b);
1869
0
}
1870
1871
/** Calculate the number of Gregorian days between 2 dates */
1872
static int day_diff(icalrecur_iterator *impl, icaltimetype a, icaltimetype b)
1873
0
{
1874
0
    return __day_diff(impl, a, b);
1875
0
}
1876
1877
static void reset_period_start(icalrecur_iterator *impl)
1878
0
{
1879
0
    set_datetime(impl, impl->period_start);
1880
0
}
1881
1882
#endif /* HAVE_LIBICU */
1883
1884
static int __iterator_set_start(icalrecur_iterator *impl, icaltimetype start);
1885
static void increment_month(icalrecur_iterator *impl, int inc);
1886
static int expand_month_days(icalrecur_iterator *impl, int year, int month);
1887
static int expand_year_days(icalrecur_iterator *impl, int year);
1888
static int next_yearday(icalrecur_iterator *impl,
1889
                        void (*next_period)(icalrecur_iterator *, int));
1890
static int prev_yearday(icalrecur_iterator *impl,
1891
                        void (*next_period)(icalrecur_iterator *, int));
1892
1893
static void adjust_to_byday(icalrecur_iterator *impl)
1894
0
{
1895
    /* If there is BY_DAY data, then we need to move the initial
1896
       time to the start of the BY_DAY data. That is if the
1897
       start time is on a Wednesday, and the rule has
1898
       BYDAY=MO,WE,FR, move the initial time back to
1899
       monday. Otherwise, jumping to the next week ( jumping 7
1900
       days ahead ) will skip over some occurrences in the
1901
       second week. */
1902
1903
    /* This depends on impl->by_ptrs[BY_DAY] being correctly sorted by
1904
     * day. This should probably be abstracted to make such assumption
1905
     * more explicit. */
1906
0
    short this_dow = (short)get_day_of_week(impl);
1907
0
    short dow = (short)(impl->by_ptrs[BY_DAY][0] - this_dow);
1908
1909
    /* Normalize day of week around week start */
1910
0
    if (dow != 0 && this_dow < (short)impl->rule.week_start)
1911
0
        dow -= 7;
1912
1913
0
    if ((this_dow < impl->by_ptrs[BY_DAY][0] && dow >= 0) || dow < 0) {
1914
        /* initial time is after first day of BY_DAY data */
1915
0
        increment_monthday(impl, dow);
1916
0
    }
1917
0
}
1918
1919
icalrecur_iterator *icalrecur_iterator_new(struct icalrecurrencetype rule,
1920
                                           struct icaltimetype dtstart)
1921
0
{
1922
0
    icalrecur_iterator *impl;
1923
0
    icalrecurrencetype_frequency freq = rule.freq;
1924
0
    enum byrule byrule;
1925
1926
0
    icalerror_clear_errno();
1927
1928
0
    if (freq == ICAL_NO_RECURRENCE) {
1929
0
        icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
1930
0
        return 0;
1931
0
    }
1932
1933
0
#define IN_RANGE(val, min, max) (val >= min && val <= max)
1934
1935
    /* Make sure that DTSTART is a sane value */
1936
0
    if (!icaltime_is_valid_time(dtstart) ||
1937
0
        !IN_RANGE(dtstart.year, 0, MAX_TIME_T_YEAR) ||
1938
0
        !IN_RANGE(dtstart.month, 1, 12) ||
1939
0
        !IN_RANGE(dtstart.day, 1,
1940
0
                  icaltime_days_in_month(dtstart.month, dtstart.year)) ||
1941
0
        (!dtstart.is_date && (!IN_RANGE(dtstart.hour, 0, 23) ||
1942
0
                              !IN_RANGE(dtstart.minute, 0, 59) ||
1943
0
                              !IN_RANGE(dtstart.second, 0, 59)))) {
1944
0
        icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
1945
0
        return 0;
1946
0
    }
1947
1948
0
    if (!(impl = (icalrecur_iterator *)icalmemory_new_buffer(sizeof(icalrecur_iterator)))) {
1949
0
        icalerror_set_errno(ICAL_NEWFAILED_ERROR);
1950
0
        return 0;
1951
0
    }
1952
1953
0
    memset(impl, 0, sizeof(icalrecur_iterator));
1954
1955
0
    impl->dtstart = dtstart;
1956
0
    impl->rule = rule;
1957
0
    impl->iend = icaltime_null_time();
1958
1959
    /* Set up convenience pointers to make the code simpler. Allows
1960
       us to iterate through all of the BY* arrays in the rule. */
1961
1962
0
    impl->by_ptrs[BY_MONTH] = impl->rule.by_month;
1963
0
    impl->by_ptrs[BY_WEEK_NO] = impl->rule.by_week_no;
1964
0
    impl->by_ptrs[BY_YEAR_DAY] = impl->rule.by_year_day;
1965
0
    impl->by_ptrs[BY_MONTH_DAY] = impl->rule.by_month_day;
1966
0
    impl->by_ptrs[BY_DAY] = impl->rule.by_day;
1967
0
    impl->by_ptrs[BY_HOUR] = impl->rule.by_hour;
1968
0
    impl->by_ptrs[BY_MINUTE] = impl->rule.by_minute;
1969
0
    impl->by_ptrs[BY_SECOND] = impl->rule.by_second;
1970
0
    impl->by_ptrs[BY_SET_POS] = impl->rule.by_set_pos;
1971
1972
0
    memset(impl->orig_data, 0, NUM_BY_PARTS * sizeof(short));
1973
1974
    /* Note which by rules had data in them when the iterator was
1975
       created. We can't use the actual by_x arrays, because the
1976
       empty ones will be given default values later in this
1977
       routine. The orig_data array will be used later in has_by_data */
1978
1979
0
    impl->orig_data[BY_MONTH] =
1980
0
        (short)(impl->rule.by_month[0] != ICAL_RECURRENCE_ARRAY_MAX);
1981
0
    impl->orig_data[BY_WEEK_NO] =
1982
0
        (short)(impl->rule.by_week_no[0] != ICAL_RECURRENCE_ARRAY_MAX);
1983
0
    impl->orig_data[BY_YEAR_DAY] =
1984
0
        (short)(impl->rule.by_year_day[0] != ICAL_RECURRENCE_ARRAY_MAX);
1985
0
    impl->orig_data[BY_MONTH_DAY] =
1986
0
        (short)(impl->rule.by_month_day[0] != ICAL_RECURRENCE_ARRAY_MAX);
1987
0
    impl->orig_data[BY_DAY] =
1988
0
        (short)(impl->rule.by_day[0] != ICAL_RECURRENCE_ARRAY_MAX);
1989
0
    impl->orig_data[BY_HOUR] =
1990
0
        (short)(impl->rule.by_hour[0] != ICAL_RECURRENCE_ARRAY_MAX);
1991
0
    impl->orig_data[BY_MINUTE] =
1992
0
        (short)(impl->rule.by_minute[0] != ICAL_RECURRENCE_ARRAY_MAX);
1993
0
    impl->orig_data[BY_SECOND] =
1994
0
        (short)(impl->rule.by_second[0] != ICAL_RECURRENCE_ARRAY_MAX);
1995
0
    impl->orig_data[BY_SET_POS] =
1996
0
        (short)(impl->rule.by_set_pos[0] != ICAL_RECURRENCE_ARRAY_MAX);
1997
1998
    /* Check if the recurrence rule is legal */
1999
2000
0
    for (byrule = 0; byrule < NUM_BY_PARTS; byrule++) {
2001
0
        if (expand_map[freq].map[byrule] == ILLEGAL &&
2002
0
            has_by_data(impl, byrule)) {
2003
0
            ical_invalid_rrule_handling rruleHandlingSetting =
2004
0
                ical_get_invalid_rrule_handling_setting();
2005
0
            if (rruleHandlingSetting == ICAL_RRULE_IGNORE_INVALID) {
2006
0
                impl->orig_data[byrule] = 0;
2007
0
            } else {
2008
0
                icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
2009
0
                icalmemory_free_buffer(impl);
2010
0
                return 0;
2011
0
            }
2012
0
        }
2013
0
    }
2014
2015
0
    if (initialize_rscale(impl) == 0) {
2016
0
        icalrecur_iterator_free(impl);
2017
0
        return 0;
2018
0
    }
2019
2020
    /* Set up defaults for BY_* arrays */
2021
0
    setup_defaults(impl, BY_SECOND, impl->rstart.second);
2022
2023
0
    setup_defaults(impl, BY_MINUTE, impl->rstart.minute);
2024
2025
0
    setup_defaults(impl, BY_HOUR, impl->rstart.hour);
2026
2027
0
    setup_defaults(impl, BY_MONTH_DAY, impl->rstart.day);
2028
2029
0
    setup_defaults(impl, BY_MONTH, impl->rstart.month);
2030
2031
0
    if (!__iterator_set_start(impl, dtstart)) {
2032
0
        icalrecur_iterator_free(impl);
2033
0
        return 0;
2034
0
    }
2035
2036
0
    return impl;
2037
0
}
2038
2039
void icalrecur_iterator_free(icalrecur_iterator *i)
2040
0
{
2041
0
    icalerror_check_arg_rv((i != 0), "impl");
2042
2043
#if defined(HAVE_LIBICU)
2044
    if (i->greg) {
2045
        if (i->rscale && (i->rscale != i->greg)) {
2046
            ucal_close(i->rscale);
2047
        }
2048
2049
        ucal_close(i->greg);
2050
    }
2051
#endif
2052
2053
0
    icalmemory_free_buffer(i);
2054
0
}
2055
2056
/** Calculate the number of days between 2 dates */
2057
static int __day_diff(icalrecur_iterator *impl, icaltimetype a, icaltimetype b)
2058
0
{
2059
0
    int diff;
2060
2061
0
    if (a.year == b.year) {
2062
0
        diff = get_day_of_year(impl, b.year, b.month, b.day, NULL) -
2063
0
               get_day_of_year(impl, a.year, a.month, a.day, NULL);
2064
0
    } else {
2065
        /* Swap a and b if a is greater than b */
2066
0
        int flipped = 0;
2067
0
        int year;
2068
2069
0
        if (a.year > b.year) {
2070
0
            icaltimetype temp = a;
2071
2072
0
            a = b;
2073
0
            b = temp;
2074
0
            flipped = 1;
2075
0
        }
2076
2077
        /* Count days in each year to account for leap days/months */
2078
0
        year = a.year;
2079
2080
0
        diff = get_days_in_year(impl, year) -
2081
0
               get_day_of_year(impl, a.year, a.month, a.day, NULL);
2082
0
        while (++year < b.year)
2083
0
            diff += get_days_in_year(impl, year);
2084
0
        diff += get_day_of_year(impl, b.year, b.month, b.day, NULL);
2085
2086
0
        if (flipped) {
2087
            /* The difference is negative because a was greater than b */
2088
0
            diff = -diff;
2089
0
        }
2090
0
    }
2091
2092
0
    return diff;
2093
0
}
2094
2095
/** increment_month is different than the other increment_* routines --
2096
   it figures out the interval for itself, and uses BYMONTH data if
2097
   available. */
2098
static void increment_month(icalrecur_iterator *impl, int inc)
2099
0
{
2100
0
    __increment_month(impl, inc);
2101
2102
0
    if (has_by_data(impl, BY_MONTH)) {
2103
0
        struct icaltimetype this = occurrence_as_icaltime(impl, 0);
2104
2105
0
        while (this.year < 20000) {
2106
0
            for (BYMONIDX = 0;
2107
0
                 BYMONPTR[BYMONIDX] != ICAL_RECURRENCE_ARRAY_MAX; BYMONIDX++) {
2108
0
                if (this.month == BYMONPTR[BYMONIDX])
2109
0
                    return;
2110
0
            }
2111
2112
0
            __increment_month(impl, inc);
2113
0
            this = occurrence_as_icaltime(impl, 0);
2114
0
        }
2115
0
    }
2116
0
}
2117
2118
#if 0
2119
#include "ical.h"
2120
void test_increment()
2121
{
2122
    icalrecur_iterator impl;
2123
2124
    impl.last = icaltime_from_string("20000101T000000Z");
2125
2126
    printf("Orig: %s\n", icaltime_as_ctime(impl.last));
2127
2128
    increment_second(&impl, 5);
2129
    printf("+ 5 sec    : %s\n", icaltime_as_ctime(impl.last));
2130
2131
    increment_second(&impl, 355);
2132
    printf("+ 355 sec  : %s\n", icaltime_as_ctime(impl.last));
2133
2134
    increment_minute(&impl, 5);
2135
    printf("+ 5 min    : %s\n", icaltime_as_ctime(impl.last));
2136
2137
    increment_minute(&impl, 360);
2138
    printf("+ 360 min  : %s\n", icaltime_as_ctime(impl.last));
2139
    increment_hour(&impl, 5);
2140
    printf("+ 5 hours  : %s\n", icaltime_as_ctime(impl.last));
2141
    increment_hour(&impl, 43);
2142
    printf("+ 43 hours : %s\n", icaltime_as_ctime(impl.last));
2143
    increment_monthday(&impl, 3);
2144
    printf("+ 3 days   : %s\n", icaltime_as_ctime(impl.last));
2145
    increment_monthday(&impl, 600);
2146
    printf("+ 600 days  : %s\n", icaltime_as_ctime(impl.last));
2147
}
2148
2149
#endif
2150
2151
static int next_unit(icalrecur_iterator *impl,
2152
                     int by_unit, icalrecurrencetype_frequency frequency,
2153
                     int (*next_sub_unit)(icalrecur_iterator *),
2154
                     void (*set_unit)(icalrecur_iterator *, int),
2155
                     void (*increment_unit)(icalrecur_iterator *, int),
2156
                     void (*increment_super_unit)(icalrecur_iterator *, int))
2157
0
{
2158
0
    int has_by_unit = (by_unit > NO_CONTRACTION) &&
2159
0
                      (impl->by_ptrs[by_unit][0] != ICAL_RECURRENCE_ARRAY_MAX);
2160
0
    int this_frequency = (impl->rule.freq == frequency);
2161
2162
0
    int end_of_data = 0;
2163
2164
0
    icalassert(has_by_unit || this_frequency);
2165
2166
0
    if (next_sub_unit && next_sub_unit(impl) == 0) {
2167
0
        return 0;
2168
0
    }
2169
2170
0
    if (has_by_unit) {
2171
        /* Ignore the frequency and use the byrule data */
2172
2173
0
        impl->by_indices[by_unit]++;
2174
2175
0
        if (impl->by_ptrs[by_unit][impl->by_indices[by_unit]] ==
2176
0
            ICAL_RECURRENCE_ARRAY_MAX) {
2177
0
            impl->by_indices[by_unit] = 0;
2178
2179
0
            end_of_data = 1;
2180
0
        }
2181
2182
0
        set_unit(impl, impl->by_ptrs[by_unit][impl->by_indices[by_unit]]);
2183
2184
0
    } else if (!has_by_unit && this_frequency) {
2185
        /* Compute the next value from the last time and the freq interval */
2186
0
        increment_unit(impl, impl->rule.interval);
2187
0
    }
2188
2189
    /* If we have gone through all of the units on the BY list, then we
2190
       need to move to the next larger unit */
2191
2192
0
    if (has_by_unit && end_of_data && this_frequency) {
2193
0
        increment_super_unit(impl, 1);
2194
0
    }
2195
2196
0
    return end_of_data;
2197
0
}
2198
2199
static int next_second(icalrecur_iterator *impl)
2200
0
{
2201
0
    return next_unit(impl, BY_SECOND, ICAL_SECONDLY_RECURRENCE, NULL,
2202
0
                     &set_second, &increment_second, &increment_minute);
2203
0
}
2204
2205
static int next_minute(icalrecur_iterator *impl)
2206
0
{
2207
0
    return next_unit(impl, BY_MINUTE, ICAL_MINUTELY_RECURRENCE, &next_second,
2208
0
                     &set_minute, &increment_minute, &increment_hour);
2209
0
}
2210
2211
static int next_hour(icalrecur_iterator *impl)
2212
0
{
2213
0
    return next_unit(impl, BY_HOUR, ICAL_HOURLY_RECURRENCE, &next_minute,
2214
0
                     &set_hour, &increment_hour, &increment_monthday);
2215
0
}
2216
2217
static int next_day(icalrecur_iterator *impl)
2218
0
{
2219
0
    return next_unit(impl, NO_CONTRACTION, ICAL_DAILY_RECURRENCE, &next_hour,
2220
0
                     NULL, &increment_monthday, NULL);
2221
0
}
2222
2223
static int prev_unit(icalrecur_iterator *impl,
2224
                     int by_unit, icalrecurrencetype_frequency frequency,
2225
                     int (*prev_sub_unit)(icalrecur_iterator *),
2226
                     void (*set_unit)(icalrecur_iterator *, int),
2227
                     void (*increment_unit)(icalrecur_iterator *, int),
2228
                     void (*increment_super_unit)(icalrecur_iterator *, int))
2229
0
{
2230
0
    int has_by_unit = (by_unit > NO_CONTRACTION) &&
2231
0
                      (impl->by_ptrs[by_unit][0] != ICAL_RECURRENCE_ARRAY_MAX);
2232
0
    int this_frequency = (impl->rule.freq == frequency);
2233
2234
0
    int end_of_data = 0;
2235
2236
0
    icalassert(has_by_unit || this_frequency);
2237
2238
0
    if (prev_sub_unit && prev_sub_unit(impl) == 0) {
2239
0
        return 0;
2240
0
    }
2241
2242
0
    if (has_by_unit) {
2243
        /* Ignore the frequency and use the byrule data */
2244
2245
0
        impl->by_indices[by_unit]--;
2246
2247
0
        if (impl->by_indices[by_unit] < 0) {
2248
0
            impl->by_indices[by_unit] =
2249
0
                icalrecur_iterator_sizeof_byarray(impl->by_ptrs[by_unit]) - 1;
2250
2251
0
            end_of_data = 1;
2252
0
        }
2253
2254
0
        set_unit(impl, impl->by_ptrs[by_unit][impl->by_indices[by_unit]]);
2255
2256
0
    } else if (!has_by_unit && this_frequency) {
2257
        /* Compute the next value from the last time and the freq interval */
2258
0
        increment_unit(impl, -impl->rule.interval);
2259
0
    }
2260
2261
    /* If we have gone through all of the units on the BY list, then we
2262
       need to move to the next larger unit */
2263
2264
0
    if (has_by_unit && end_of_data && this_frequency) {
2265
0
        increment_super_unit(impl, -1);
2266
0
    }
2267
2268
0
    return end_of_data;
2269
0
}
2270
2271
static int prev_second(icalrecur_iterator *impl)
2272
0
{
2273
0
    return prev_unit(impl, BY_SECOND, ICAL_SECONDLY_RECURRENCE, NULL,
2274
0
                     &set_second, &increment_second, &increment_minute);
2275
0
}
2276
2277
static int prev_minute(icalrecur_iterator *impl)
2278
0
{
2279
0
    return prev_unit(impl, BY_MINUTE, ICAL_MINUTELY_RECURRENCE, &prev_second,
2280
0
                     &set_minute, &increment_minute, &increment_hour);
2281
0
}
2282
2283
static int prev_hour(icalrecur_iterator *impl)
2284
0
{
2285
0
    return prev_unit(impl, BY_HOUR, ICAL_HOURLY_RECURRENCE, &prev_minute,
2286
0
                     &set_hour, &increment_hour, &increment_monthday);
2287
0
}
2288
2289
static int prev_day(icalrecur_iterator *impl)
2290
0
{
2291
0
    return prev_unit(impl, NO_CONTRACTION, ICAL_DAILY_RECURRENCE, &prev_hour,
2292
0
                     NULL, &increment_monthday, NULL);
2293
0
}
2294
2295
static int check_set_position(icalrecur_iterator *impl, int set_pos)
2296
0
{
2297
0
    int i;
2298
0
    int found = 0;
2299
2300
0
    for (i = 0;
2301
0
         i < ICAL_BY_SETPOS_SIZE &&
2302
0
         impl->rule.by_set_pos[i] != ICAL_RECURRENCE_ARRAY_MAX;
2303
0
         i++) {
2304
0
        if (impl->rule.by_set_pos[i] == set_pos) {
2305
0
            found = 1;
2306
0
            break;
2307
0
        }
2308
0
    }
2309
0
    return found;
2310
0
}
2311
2312
/** Add each BYMONTHDAY to the year days bitmask */
2313
static int expand_bymonth_days(icalrecur_iterator *impl, int year, int month)
2314
0
{
2315
0
    int i, set_pos_total = 0;
2316
0
    int days_in_month = get_days_in_month(impl, month, year);
2317
2318
0
    for (i = 0; BYMDPTR[i] != ICAL_RECURRENCE_ARRAY_MAX; i++) {
2319
0
        short doy = ICAL_BY_YEARDAY_SIZE, mday = BYMDPTR[i];
2320
0
        int this_month = month;
2321
2322
0
        if (abs(mday) > days_in_month) {
2323
0
            int days_in_year = get_days_in_year(impl, year);
2324
2325
0
            switch (impl->rule.skip) {
2326
0
            default:
2327
                /* Should never get here! */
2328
2329
0
            case ICAL_SKIP_OMIT:
2330
0
                continue;
2331
2332
0
            case ICAL_SKIP_FORWARD:
2333
0
                if (mday > 0)
2334
0
                    this_month++; /* Next month */
2335
2336
0
                if (this_month > get_months_in_year(impl, year)) {
2337
0
                    doy = days_in_year + 1; /* First day of next year */
2338
0
                } else {
2339
0
                    mday = 1; /* First day of month */
2340
0
                }
2341
0
                break;
2342
2343
0
            case ICAL_SKIP_BACKWARD:
2344
0
                if (mday < 0) {
2345
0
                    this_month--; /* Prev month */
2346
0
                }
2347
2348
0
                if (this_month == 0) {
2349
0
                    doy = 0; /* Last day of prev year */
2350
0
                } else {
2351
0
                    mday = -1; /* Last day of month */
2352
0
                }
2353
0
                break;
2354
0
            }
2355
0
        }
2356
2357
0
        if (doy == ICAL_BY_YEARDAY_SIZE) {
2358
0
            doy = get_day_of_year(impl, year, this_month, mday, NULL);
2359
0
        }
2360
2361
0
        daysmask_setbit(impl->days, doy, 1);
2362
0
        set_pos_total++;
2363
0
        if (doy < impl->days_index)
2364
0
            impl->days_index = doy;
2365
0
    }
2366
2367
0
    return set_pos_total;
2368
0
}
2369
2370
/** Expand the BYDAY rule part and apply it to the year days map. */
2371
static int expand_by_day(icalrecur_iterator *impl, int year,
2372
                         int doy_offset, int last_day,
2373
                         int first_dow, int last_dow,
2374
                         int is_limiting)
2375
0
{
2376
    /* Try to calculate each of the occurrences. */
2377
0
    unsigned long bydays[LONGS_PER_BITS(ICAL_YEARDAYS_MASK_SIZE)];
2378
0
    int i, set_pos_total = 0;
2379
0
    short doy;
2380
2381
0
    daysmask_clearall(bydays);
2382
2383
0
    for (i = 0; BYDAYPTR[i] != ICAL_RECURRENCE_ARRAY_MAX; i++) {
2384
        /* This is 1 (Sun) to 7 (Sat). */
2385
0
        int dow = icalrecurrencetype_day_day_of_week(BYDAYPTR[i]);
2386
0
        int pos = icalrecurrencetype_day_position(BYDAYPTR[i]);
2387
0
        int first_matching_day, last_matching_day;
2388
0
        int day, this_weekno;
2389
2390
        /* Calculate the first day in the period
2391
           with the given weekday, and the last day. */
2392
0
        first_matching_day = ((dow + 7 - first_dow) % 7) + 1;
2393
0
        last_matching_day = last_day - ((last_dow + 7 - dow) % 7);
2394
2395
0
        if (pos == 0) {
2396
            /* First instance of the weekday within the period.
2397
               (Remaining instances added by loop below. */
2398
0
            day = first_matching_day;
2399
2400
0
        } else if (pos > 0) {
2401
            /* nth instance of the weekday within the period. */
2402
0
            day = first_matching_day + (pos - 1) * 7;
2403
2404
0
            if (day > last_matching_day) {
2405
0
                continue;
2406
0
            }
2407
2408
0
        } else { /* pos < 0 */
2409
            /* -nth instance of the weekday within the period. */
2410
0
            day = last_matching_day + (pos + 1) * 7;
2411
2412
0
            if (day < first_matching_day) {
2413
0
                continue;
2414
0
            }
2415
0
        }
2416
2417
0
        if (doy_offset < 0) {
2418
0
            this_weekno = 1;
2419
0
        } else {
2420
0
            (void)__icaltime_from_day_of_year(impl, day + doy_offset, year,
2421
0
                                              &this_weekno);
2422
0
        }
2423
2424
        /* Add instance(s) of the weekday within the period */
2425
0
        do {
2426
0
            int valid = 0;
2427
2428
0
            if (has_by_data(impl, BY_WEEK_NO)) {
2429
                /* Make sure our day falls in one of the BYWEEKNO */
2430
0
                int nweeks = weeks_in_year(year);
2431
0
                int j;
2432
2433
0
                for (j = 0; BYWEEKPTR[j] != ICAL_RECURRENCE_ARRAY_MAX; j++) {
2434
0
                    int weekno = BYWEEKPTR[j];
2435
2436
0
                    if (weekno < 0)
2437
0
                        weekno += nweeks + 1;
2438
2439
0
                    if (weekno == this_weekno) {
2440
0
                        valid = 1;
2441
0
                        break;
2442
0
                    }
2443
0
                }
2444
0
            } else {
2445
0
                valid = 1;
2446
0
            }
2447
2448
0
            if (valid) {
2449
0
                daysmask_setbit(bydays, day + doy_offset, 1);
2450
0
            }
2451
2452
0
        } while (!pos && ((day += 7) <= last_day) && ++this_weekno);
2453
0
    }
2454
2455
    /* Apply bydays map to the year days bitmask */
2456
0
    for (doy = doy_offset + 1; doy <= doy_offset + last_day; doy++) {
2457
0
        int valid;
2458
2459
0
        if (is_limiting) {
2460
            /* "Filter" the year days bitmask with the bydays bitmask */
2461
0
            valid = (int)(daysmask_getbit(impl->days, doy) &
2462
0
                          daysmask_getbit(bydays, doy));
2463
0
        } else {
2464
            /* Add each BYDAY to the year days bitmask */
2465
0
            valid = (int)daysmask_getbit(bydays, doy);
2466
0
        }
2467
2468
0
        daysmask_setbit(impl->days, doy, valid);
2469
2470
0
        if (valid) {
2471
0
            set_pos_total++;
2472
0
            if (doy < impl->days_index)
2473
0
                impl->days_index = doy;
2474
0
        }
2475
0
    }
2476
2477
0
    return set_pos_total;
2478
0
}
2479
2480
/** "Filter" the year days bitmask with each BYSETPOS */
2481
static void filter_bysetpos(icalrecur_iterator *impl, int pos_total,
2482
                            int start_doy, int end_doy)
2483
0
{
2484
0
    int pos_count = 0;
2485
0
    short doy;
2486
2487
0
    impl->days_index = ICAL_YEARDAYS_MASK_SIZE;
2488
2489
0
    for (doy = start_doy; doy <= end_doy; doy++) {
2490
0
        if (daysmask_getbit(impl->days, doy)) {
2491
0
            daysmask_setbit(impl->days, doy,
2492
0
                            (check_set_position(impl, pos_count + 1) ||
2493
0
                             check_set_position(impl, pos_count - pos_total)));
2494
2495
0
            if (daysmask_getbit(impl->days, doy) && doy < impl->days_index) {
2496
0
                impl->days_index = doy;
2497
0
            }
2498
0
            pos_count++;
2499
0
        }
2500
0
    }
2501
0
}
2502
2503
/** For INTERVAL=MONTHLY, set up the year days bitmask in the iterator to
2504
   list all of the days of the current month that are specified in this
2505
   rule. */
2506
static int expand_month_days(icalrecur_iterator *impl, int year, int month)
2507
0
{
2508
0
    int doy_offset, days_in_month, first_dow, set_pos_total;
2509
2510
0
    daysmask_clearall(impl->days);
2511
2512
    /* We may end up skipping fwd/bwd a month during expansion.
2513
       Mark our current start date so next_month() can increment from here */
2514
0
    impl->period_start = occurrence_as_icaltime(impl, 0);
2515
2516
0
    doy_offset = get_day_of_year(impl, year, month, 1, &first_dow) - 1;
2517
0
    days_in_month = get_days_in_month(impl, month, year);
2518
2519
    /* Add each BYMONTHDAY to the year days bitmask */
2520
0
    set_pos_total = expand_bymonth_days(impl, year, month);
2521
2522
0
    if (has_by_data(impl, BY_DAY)) {
2523
        /* Apply each BYDAY to the year days bitmask */
2524
0
        int last_dow;
2525
2526
0
        impl->days_index = ICAL_YEARDAYS_MASK_SIZE;
2527
2528
0
        (void)get_day_of_year(impl, year, month, days_in_month, &last_dow);
2529
2530
0
        set_pos_total = expand_by_day(impl, year, doy_offset, days_in_month,
2531
0
                                      first_dow, last_dow,
2532
0
                                      has_by_data(impl, BY_MONTH_DAY));
2533
0
    }
2534
2535
0
    if (has_by_data(impl, BY_SET_POS)) {
2536
        /* "Filter" the year days bitmask with each BYSETPOS */
2537
0
        filter_bysetpos(impl, set_pos_total,
2538
0
                        doy_offset + 1, doy_offset + days_in_month);
2539
0
    }
2540
2541
0
    return 0;
2542
0
}
2543
2544
static void __next_month(icalrecur_iterator *impl, int inc)
2545
0
{
2546
0
    struct icaltimetype this;
2547
2548
    /* Increment to and expand the next month */
2549
0
    increment_month(impl, inc);
2550
0
    this = occurrence_as_icaltime(impl, 0);
2551
0
    expand_month_days(impl, this.year, this.month);
2552
0
}
2553
2554
static int next_month(icalrecur_iterator *impl)
2555
0
{
2556
0
    return next_yearday(impl, &__next_month);
2557
0
}
2558
2559
static int prev_month(icalrecur_iterator *impl)
2560
0
{
2561
0
    return prev_yearday(impl, &__next_month);
2562
0
}
2563
2564
static int next_weekday_by_week(icalrecur_iterator *impl)
2565
0
{
2566
0
    int end_of_data = 0;
2567
0
    int start_of_week, dow;
2568
2569
0
    if (next_hour(impl) == 0) {
2570
0
        return 0;
2571
0
    }
2572
2573
0
    if (!has_by_data(impl, BY_DAY)) {
2574
0
        return 1;
2575
0
    }
2576
2577
    /* If we get here, we need to step to the next day */
2578
2579
0
    for (;;) {
2580
0
        BYDAYIDX++; /* Look at next elem in BYDAY array */
2581
2582
        /* Are we at the end of the BYDAY array? */
2583
0
        if (BYDAYPTR[BYDAYIDX] == ICAL_RECURRENCE_ARRAY_MAX) {
2584
0
            BYDAYIDX = 0;    /* Reset to 0 */
2585
0
            end_of_data = 1; /* Signal that we're at the end */
2586
0
        }
2587
2588
        /* Add the day of week offset to the start of this week, and use
2589
           that to get the next day */
2590
        /* ignore position of dow ("4FR"), only use dow ("FR") */
2591
0
        dow = icalrecurrencetype_day_day_of_week(BYDAYPTR[BYDAYIDX]);
2592
0
        dow -= impl->rule.week_start; /* Set Sunday to be 0 */
2593
0
        if (dow < 0) {
2594
0
            dow += 7;
2595
0
        }
2596
2597
0
        start_of_week = get_start_of_week(impl);
2598
2599
0
        if (dow + start_of_week < 1) {
2600
            /* The selected date is in the previous year. */
2601
0
            if (!end_of_data) {
2602
0
                continue;
2603
0
            }
2604
2605
0
            increment_year(impl, -1);
2606
0
        }
2607
2608
0
        set_day_of_year(impl, start_of_week + dow);
2609
2610
0
        return end_of_data;
2611
0
    }
2612
0
}
2613
2614
static int next_week(icalrecur_iterator *impl)
2615
0
{
2616
0
    int end_of_data = 0;
2617
2618
    /* Increment to the next week day,
2619
       if there is data at a level less than a week */
2620
0
    if (next_weekday_by_week(impl) == 0) {
2621
0
        return 0; /* Have not reached end of week yet */
2622
0
    }
2623
2624
    /* If we get here, we have incremented through the entire week, and
2625
       can increment to the next week */
2626
2627
    /* Jump to the next week */
2628
0
    increment_monthday(impl, 7 * impl->rule.interval);
2629
2630
0
    return end_of_data;
2631
0
}
2632
2633
static int prev_weekday_by_week(icalrecur_iterator *impl)
2634
0
{
2635
0
    int end_of_data = 0;
2636
0
    int start_of_week, dow;
2637
2638
0
    if (prev_hour(impl) == 0) {
2639
0
        return 0;
2640
0
    }
2641
2642
0
    if (!has_by_data(impl, BY_DAY)) {
2643
0
        return 1;
2644
0
    }
2645
2646
    /* If we get here, we need to step to the previous day */
2647
2648
0
    BYDAYIDX--; /* Look at previous elem in BYDAY array */
2649
2650
    /* Are we at the end of the BYDAY array? */
2651
0
    if (BYDAYIDX < 0) {
2652
0
        BYDAYIDX = icalrecur_iterator_sizeof_byarray(impl->by_ptrs[BY_DAY]) - 1;
2653
0
        end_of_data = 1; /* Signal that we're at the end */
2654
0
    }
2655
2656
    /* Add the day of week offset to the start of this week, and use
2657
       that to get the next day */
2658
    /* ignore position of dow ("4FR"), only use dow ("FR") */
2659
0
    dow = icalrecurrencetype_day_day_of_week(BYDAYPTR[BYDAYIDX]);
2660
0
    dow -= impl->rule.week_start; /* Set Sunday to be 0 */
2661
0
    if (dow < 0) {
2662
0
        dow += 7;
2663
0
    }
2664
2665
0
    start_of_week = get_start_of_week(impl);
2666
2667
0
    if (dow + start_of_week < 1) {
2668
        /* The selected date is in the previous year. */
2669
0
        increment_year(impl, -1);
2670
0
    }
2671
2672
0
    set_day_of_year(impl, start_of_week + dow);
2673
2674
0
    return end_of_data;
2675
0
}
2676
2677
static int prev_week(icalrecur_iterator *impl)
2678
0
{
2679
0
    int end_of_data = 0;
2680
2681
    /* Decrement to the previous week day,
2682
       if there is data at a level less than a week */
2683
0
    if (prev_weekday_by_week(impl) == 0) {
2684
0
        return 0; /* Have not reached start of week yet */
2685
0
    }
2686
2687
    /* If we get here, we have decremented through the entire week, and
2688
       can decrement to the previous week */
2689
2690
    /* Jump to the previous week */
2691
0
    increment_monthday(impl, 7 * -impl->rule.interval);
2692
2693
0
    return end_of_data;
2694
0
}
2695
2696
/* For INTERVAL=YEARLY, set up the year days bitmask in the iterator to
2697
   list all of the days of the current year that are specified in this
2698
   rule. */
2699
static int expand_year_days(icalrecur_iterator *impl, int year)
2700
0
{
2701
0
    int i;
2702
0
    int set_pos_total = 0;
2703
0
    short days_in_year = (short)get_days_in_year(impl, year);
2704
0
    short doy;
2705
2706
0
    daysmask_clearall(impl->days);
2707
2708
    /* We may end up skipping fwd/bwd a year during expansion.
2709
       Mark our current start date so next_year() can increment from here */
2710
0
    impl->period_start = occurrence_as_icaltime(impl, 0);
2711
2712
0
    if (has_by_data(impl, BY_YEAR_DAY)) {
2713
        /* We only support BYYEARDAY + BYDAY */
2714
0
        if (has_by_data(impl, BY_WEEK_NO) ||
2715
0
            has_by_data(impl, BY_MONTH) || has_by_data(impl, BY_MONTH_DAY)) {
2716
0
            icalerror_set_errno(ICAL_UNIMPLEMENTED_ERROR);
2717
0
            return 0;
2718
0
        }
2719
2720
        /* Add each BYYEARDAY to the year days bitmask */
2721
0
        for (i = 0; BYYDPTR[i] != ICAL_RECURRENCE_ARRAY_MAX; i++) {
2722
0
            doy = BYYDPTR[i];
2723
2724
0
            if (abs(doy) > days_in_year) {
2725
0
                switch (impl->rule.skip) {
2726
0
                default:
2727
                    /* Should never get here! */
2728
2729
0
                case ICAL_SKIP_OMIT:
2730
                    /* Invalid day */
2731
0
                    continue;
2732
2733
0
                case ICAL_SKIP_FORWARD:
2734
0
                    if (doy < 0) {
2735
0
                        doy = 1; /* First day of this year */
2736
0
                    } else {
2737
0
                        doy = days_in_year + 1; /* First day of next year */
2738
0
                    }
2739
0
                    break;
2740
2741
0
                case ICAL_SKIP_BACKWARD:
2742
0
                    if (doy < 0) {
2743
0
                        doy = 0; /* Last day of prev year */
2744
0
                    } else {
2745
0
                        doy = days_in_year; /* Last day of this year */
2746
0
                    }
2747
0
                    break;
2748
0
                }
2749
0
            } else if (doy < 0) {
2750
0
                doy += days_in_year + 1;
2751
0
            }
2752
2753
0
            daysmask_setbit(impl->days, doy, 1);
2754
0
            set_pos_total++;
2755
0
            if (doy < impl->days_index)
2756
0
                impl->days_index = doy;
2757
0
        }
2758
0
    } else if (has_by_data(impl, BY_WEEK_NO)) {
2759
0
        int weekno, start_doy;
2760
2761
        /* We only support BYWEEKNO + BYDAY */
2762
0
        if (has_by_data(impl, BY_YEAR_DAY) ||
2763
0
            has_by_data(impl, BY_MONTH_DAY) ||
2764
0
            (has_by_data(impl, BY_MONTH) && !has_by_data(impl, BY_DAY))) {
2765
0
            icalerror_set_errno(ICAL_UNIMPLEMENTED_ERROR);
2766
0
            return 0;
2767
0
        }
2768
2769
        /* BYWEEKNO + BYDAY handled below */
2770
0
        if (!has_by_data(impl, BY_DAY)) {
2771
0
            int nweeks = weeks_in_year(year);
2772
2773
            /* Calculate location of DTSTART day in weekno 1 */
2774
0
            doy = get_day_of_year(impl, year,
2775
0
                                  impl->dtstart.month, impl->dtstart.day, NULL);
2776
0
            (void)__icaltime_from_day_of_year(impl, doy, year, &weekno);
2777
0
            if (weekno > doy)
2778
0
                doy += 7;
2779
0
            start_doy = doy + get_start_of_week(impl);
2780
2781
            /* Add day of week in each BYWEEKNO to the year days bitmask */
2782
0
            for (i = 0; BYWEEKPTR[i] != ICAL_RECURRENCE_ARRAY_MAX; i++) {
2783
0
                weekno = BYWEEKPTR[i];
2784
2785
0
                if (weekno < 0) {
2786
0
                    weekno += nweeks + 1;
2787
0
                }
2788
2789
0
                doy = start_doy + 7 * (weekno - 1);
2790
2791
0
                daysmask_setbit(impl->days, doy, 1);
2792
0
                set_pos_total++;
2793
0
                if (doy < impl->days_index)
2794
0
                    impl->days_index = doy;
2795
0
            }
2796
0
        }
2797
0
    } else {
2798
        /* Add each BYMONTHDAY in each BYMONTH to the year days bitmask */
2799
0
        for (i = 0; BYMONPTR[i] != ICAL_RECURRENCE_ARRAY_MAX; i++) {
2800
0
            int month = set_month(impl, BYMONPTR[i]);
2801
2802
0
            if (month)
2803
0
                set_pos_total += expand_bymonth_days(impl, year, month);
2804
0
        }
2805
0
    }
2806
2807
0
    if (has_by_data(impl, BY_DAY)) {
2808
        /* Apply each BYDAY to the year days bitmask */
2809
0
        int limiting =
2810
0
            has_by_data(impl, BY_YEAR_DAY) || has_by_data(impl, BY_MONTH_DAY);
2811
0
        int first_dow, last_dow;
2812
2813
0
        impl->days_index = ICAL_YEARDAYS_MASK_SIZE;
2814
0
        set_pos_total = 0;
2815
2816
0
        if (has_by_data(impl, BY_MONTH)) {
2817
            /* Numeric BYDAY are within each month */
2818
2819
0
            for (i = 0; BYMONPTR[i] != ICAL_RECURRENCE_ARRAY_MAX; i++) {
2820
0
                short month = BYMONPTR[i];
2821
0
                int doy_offset, days_in_month;
2822
2823
                /* Get offset within year & day of week of first day of month */
2824
0
                doy_offset =
2825
0
                    get_day_of_year(impl, year, month, 1, &first_dow) - 1;
2826
2827
                /* Get day of week of last day of month */
2828
0
                days_in_month = get_days_in_month(impl, month, year);
2829
0
                (void)get_day_of_year(impl, year,
2830
0
                                      month, days_in_month, &last_dow);
2831
2832
0
                set_pos_total += expand_by_day(impl, year,
2833
0
                                               doy_offset, days_in_month,
2834
0
                                               first_dow, last_dow, limiting);
2835
0
            }
2836
0
        } else {
2837
            /* Numeric BYDAY are within the year */
2838
0
            short doy_offset = 0, last_day;
2839
2840
0
            if (has_by_data(impl, BY_WEEK_NO)) {
2841
0
                int weekno;
2842
2843
                /* See which week contains Jan 1 */
2844
0
                (void)__icaltime_from_day_of_year(impl, 1, year, &weekno);
2845
0
                if (weekno > 1) {
2846
                    /* Jan 1 is in last week of previous year - jump ahead */
2847
0
                    doy_offset += 7;
2848
0
                }
2849
2850
                /* Set start and end of ISO week-numbering year */
2851
0
                doy_offset += get_start_of_week(impl) - 1;
2852
0
                last_day = (7 * weeks_in_year(year)) - doy_offset - 1;
2853
2854
0
                first_dow = impl->rule.week_start;
2855
0
                last_dow = (first_dow + 6) % 7;
2856
0
            } else {
2857
                /* Get day of week of first day of year */
2858
0
                (void)get_day_of_year(impl, year, 1, 1, &first_dow);
2859
2860
                /* Get day of week of last day of year */
2861
0
                set_day_of_year(impl, days_in_year);
2862
0
                last_dow = get_day_of_week(impl);
2863
2864
0
                last_day = days_in_year;
2865
0
            }
2866
2867
0
            set_pos_total += expand_by_day(impl, year, doy_offset, last_day,
2868
0
                                           first_dow, last_dow, limiting);
2869
0
        }
2870
0
    }
2871
2872
0
    if (has_by_data(impl, BY_SET_POS)) {
2873
        /* "Filter" the year days bitmask with each BYSETPOS */
2874
0
        filter_bysetpos(impl, set_pos_total, 1, days_in_year);
2875
0
    }
2876
2877
0
    return 0;
2878
0
}
2879
2880
static void __next_year(icalrecur_iterator *impl, int inc)
2881
0
{
2882
0
    struct icaltimetype this;
2883
2884
    /* Increment to and expand the next year */
2885
0
    increment_year(impl, inc);
2886
0
    this = occurrence_as_icaltime(impl, 0);
2887
0
    expand_year_days(impl, this.year);
2888
0
}
2889
2890
static int next_year(icalrecur_iterator *impl)
2891
0
{
2892
0
    return next_yearday(impl, &__next_year);
2893
0
}
2894
2895
static int prev_year(icalrecur_iterator *impl)
2896
0
{
2897
0
    return prev_yearday(impl, &__next_year);
2898
0
}
2899
2900
static short daymask_find_next_bit(icalrecur_iterator *impl)
2901
0
{
2902
0
    unsigned long *days = impl->days;
2903
0
    short days_index = impl->days_index + 1;
2904
0
    unsigned long v;
2905
0
    short startBitIndex;
2906
0
    unsigned short wordIdx, maxWordIdx;
2907
2908
0
    if (days_index >= ICAL_YEARDAYS_MASK_SIZE)
2909
0
        return ICAL_YEARDAYS_MASK_SIZE;
2910
2911
    // Prepare the first word, where searching might not start at the beginning
2912
0
    startBitIndex = days_index + ICAL_YEARDAYS_MASK_OFFSET;
2913
0
    wordIdx = (unsigned short)(startBitIndex / BITS_PER_LONG);
2914
0
    v = days[wordIdx];
2915
0
    v >>= startBitIndex % BITS_PER_LONG;
2916
2917
0
    if (!v) {
2918
        // so the first word didn't contain any bits of interest.
2919
0
        days_index += BITS_PER_LONG - startBitIndex % BITS_PER_LONG;
2920
2921
        // Are there more empty words following? Skip them.
2922
0
        maxWordIdx = (unsigned short)(LONGS_PER_BITS(ICAL_YEARDAYS_MASK_SIZE))-1;
2923
0
        while (days_index < ICAL_YEARDAYS_MASK_SIZE && wordIdx < maxWordIdx) {
2924
0
            wordIdx++;
2925
0
            v = days[wordIdx];
2926
2927
0
            if (v)
2928
0
                break;
2929
2930
0
            days_index += BITS_PER_LONG;
2931
0
        }
2932
0
    }
2933
2934
0
    if (v) {
2935
        // We found a word containing the next bit but don't know the exact
2936
        // position yet. Do a b-search to find it.
2937
2938
0
        unsigned long mask;
2939
0
        int maskSize = (int)(BITS_PER_LONG / 2);
2940
0
        mask = (((unsigned long)1) << maskSize) - 1;
2941
2942
0
        while (maskSize) {
2943
0
            if ((v & mask) == 0) {
2944
0
                v >>= maskSize;
2945
0
                days_index += maskSize;
2946
0
            }
2947
0
            maskSize /= 2;
2948
0
            mask >>= maskSize;
2949
0
        }
2950
0
    }
2951
2952
0
    return days_index;
2953
0
}
2954
2955
static short daymask_find_prev_bit(icalrecur_iterator *impl)
2956
0
{
2957
0
    unsigned long *days = impl->days;
2958
0
    short days_index = impl->days_index - 1;
2959
0
    unsigned long v;
2960
0
    short startBitIndex;
2961
0
    int wordIdx;
2962
2963
0
    if (days_index <= -ICAL_YEARDAYS_MASK_OFFSET)
2964
0
        return -ICAL_YEARDAYS_MASK_OFFSET;
2965
2966
    // Prepare the first word, where searching might not start at the beginning
2967
0
    startBitIndex = days_index + ICAL_YEARDAYS_MASK_OFFSET;
2968
0
    wordIdx = (int)(startBitIndex / BITS_PER_LONG);
2969
0
    v = days[wordIdx];
2970
0
    v <<= BITS_PER_LONG - (startBitIndex % BITS_PER_LONG) - 1;
2971
2972
0
    if (!v) {
2973
        // so the first word didn't contain any bits of interest.
2974
0
        days_index -= (startBitIndex % BITS_PER_LONG) + 1;
2975
2976
        // Are there more empty words leading? Skip them.
2977
0
        while (days_index > -ICAL_YEARDAYS_MASK_OFFSET) {
2978
0
            wordIdx--;
2979
0
            v = days[wordIdx];
2980
2981
0
            if (v)
2982
0
                break;
2983
2984
0
            days_index -= BITS_PER_LONG;
2985
0
        }
2986
0
    }
2987
2988
0
    if (v) {
2989
        // We found a word containing the next bit but don't know the exact
2990
        // position yet. Do a b-search to find it.
2991
2992
0
        unsigned long mask;
2993
0
        int maskSize = (int)(BITS_PER_LONG / 2);
2994
0
        mask = ((((unsigned long)1) << maskSize) - 1) << maskSize;
2995
2996
0
        while (maskSize) {
2997
0
            if ((v & mask) == 0) {
2998
0
                v <<= maskSize;
2999
0
                days_index -= maskSize;
3000
0
            }
3001
0
            maskSize /= 2;
3002
0
            mask <<= maskSize;
3003
0
        }
3004
0
    }
3005
3006
0
    return days_index;
3007
0
}
3008
3009
static int next_yearday(icalrecur_iterator *impl,
3010
                        void (*next_period)(icalrecur_iterator *, int))
3011
0
{
3012
0
    if (next_hour(impl) == 0) {
3013
0
        return 0;
3014
0
    }
3015
3016
    /* We may have skipped fwd/bwd a month/year with previous occurrence.
3017
       Reset the period start date so we can increment properly */
3018
0
    reset_period_start(impl);
3019
3020
    /* Find next year day that is set */
3021
0
    impl->days_index = daymask_find_next_bit(impl);
3022
3023
0
    if (impl->days_index >= ICAL_YEARDAYS_MASK_SIZE) {
3024
0
        for (;;) {
3025
            /* Increment to and expand the next period */
3026
0
            next_period(impl, impl->rule.interval);
3027
3028
0
            if (impl->days_index < ICAL_YEARDAYS_MASK_SIZE) {
3029
0
                break; /* break when a matching day is found */
3030
0
            }
3031
0
        }
3032
0
    }
3033
3034
0
    if (impl->days_index < 1) {
3035
        /* Day is in previous year */
3036
0
        increment_year(impl, -1);
3037
0
    }
3038
3039
0
    set_day_of_year(impl, impl->days_index);
3040
3041
0
    return 1;
3042
0
}
3043
3044
static int prev_yearday(icalrecur_iterator *impl,
3045
                        void (*next_period)(icalrecur_iterator *, int))
3046
0
{
3047
0
    if (prev_hour(impl) == 0) {
3048
0
        return 0;
3049
0
    }
3050
3051
    /* We may have skipped fwd/bwd a month/year with previous occurrence.
3052
       Reset the period start date so we can decrement properly */
3053
0
    reset_period_start(impl);
3054
3055
    /* Find previous year day that is set */
3056
0
    impl->days_index = daymask_find_prev_bit(impl);
3057
3058
0
    while (impl->days_index <= -ICAL_YEARDAYS_MASK_OFFSET) {
3059
        /* Decrement to and expand the previous period */
3060
0
        next_period(impl, -impl->rule.interval);
3061
3062
0
        impl->days_index = ICAL_YEARDAYS_MASK_SIZE;
3063
0
        impl->days_index = daymask_find_prev_bit(impl);
3064
0
    }
3065
3066
0
    if (impl->days_index < 1) {
3067
        /* Day is in previous year */
3068
0
        increment_year(impl, -1);
3069
0
    }
3070
3071
0
    set_day_of_year(impl, impl->days_index);
3072
3073
0
    return 1;
3074
0
}
3075
3076
int icalrecur_check_rulepart(icalrecur_iterator *impl,
3077
                             int v, enum byrule byrule)
3078
0
{
3079
0
    int itr;
3080
3081
0
    if (impl->by_ptrs[byrule][0] != ICAL_RECURRENCE_ARRAY_MAX) {
3082
0
        for (itr = 0;
3083
0
             impl->by_ptrs[byrule][itr] != ICAL_RECURRENCE_ARRAY_MAX; itr++) {
3084
0
            if (impl->by_ptrs[byrule][itr] == v) {
3085
0
                return 1;
3086
0
            }
3087
0
        }
3088
0
    }
3089
3090
0
    return 0;
3091
0
}
3092
3093
static int check_contract_restriction(icalrecur_iterator *impl,
3094
                                      enum byrule byrule, int v)
3095
0
{
3096
0
    int pass = 0;
3097
0
    int itr;
3098
0
    icalrecurrencetype_frequency freq = impl->rule.freq;
3099
3100
0
    if (impl->by_ptrs[byrule][0] != ICAL_RECURRENCE_ARRAY_MAX &&
3101
0
        expand_map[freq].map[byrule] == CONTRACT) {
3102
0
        for (itr = 0;
3103
0
             impl->by_ptrs[byrule][itr] != ICAL_RECURRENCE_ARRAY_MAX; itr++) {
3104
0
            if (impl->by_ptrs[byrule][itr] == v) {
3105
0
                pass = 1;
3106
0
                break;
3107
0
            }
3108
0
        }
3109
3110
0
        return pass;
3111
0
    } else {
3112
        /* This is not a contracting byrule, or it has no data, so the
3113
           test passes */
3114
0
        return 1;
3115
0
    }
3116
0
}
3117
3118
static int check_contracting_rules(icalrecur_iterator *impl)
3119
0
{
3120
0
    struct icaltimetype last = occurrence_as_icaltime(impl, 0);
3121
0
    int day_of_week;
3122
0
    int week_no = get_week_number(impl, last);
3123
0
    int year_day =
3124
0
        get_day_of_year(impl, last.year, last.month, last.day, &day_of_week);
3125
3126
0
    if (check_contract_restriction(impl, BY_SECOND, last.second) &&
3127
0
        check_contract_restriction(impl, BY_MINUTE, last.minute) &&
3128
0
        check_contract_restriction(impl, BY_HOUR, last.hour) &&
3129
0
        check_contract_restriction(impl, BY_DAY, day_of_week) &&
3130
0
        check_contract_restriction(impl, BY_WEEK_NO, week_no) &&
3131
0
        check_contract_restriction(impl, BY_MONTH_DAY, last.day) &&
3132
0
        check_contract_restriction(impl, BY_MONTH, last.month) &&
3133
0
        check_contract_restriction(impl, BY_YEAR_DAY, year_day)) {
3134
0
        return 1;
3135
0
    } else {
3136
0
        return 0;
3137
0
    }
3138
0
}
3139
3140
struct icaltimetype icalrecur_iterator_next(icalrecur_iterator *impl)
3141
0
{
3142
    /* Quit if we reached COUNT or if last time is after the UNTIL time */
3143
0
    if (!impl ||
3144
0
        (impl->rule.count != 0 && impl->occurrence_no >= impl->rule.count) ||
3145
0
        (!icaltime_is_null_time(impl->rule.until) &&
3146
0
         icaltime_compare(impl->last, impl->rule.until) > 0)) {
3147
0
        return icaltime_null_time();
3148
0
    }
3149
3150
    /* If initial time is valid, return it */
3151
0
    if ((impl->occurrence_no == 0) &&
3152
0
        (icaltime_compare(impl->last, impl->istart) >= 0) &&
3153
0
        check_contracting_rules(impl)) {
3154
0
        impl->occurrence_no++;
3155
0
        return impl->last;
3156
0
    }
3157
3158
    /* Iterate until we get the next valid time */
3159
0
    do {
3160
0
        switch (impl->rule.freq) {
3161
0
        case ICAL_SECONDLY_RECURRENCE:
3162
0
            next_second(impl);
3163
0
            break;
3164
3165
0
        case ICAL_MINUTELY_RECURRENCE:
3166
0
            next_minute(impl);
3167
0
            break;
3168
3169
0
        case ICAL_HOURLY_RECURRENCE:
3170
0
            next_hour(impl);
3171
0
            break;
3172
3173
0
        case ICAL_DAILY_RECURRENCE:
3174
0
            next_day(impl);
3175
0
            break;
3176
3177
0
        case ICAL_WEEKLY_RECURRENCE:
3178
0
            next_week(impl);
3179
0
            break;
3180
3181
0
        case ICAL_MONTHLY_RECURRENCE:
3182
0
            next_month(impl);
3183
0
            break;
3184
3185
0
        case ICAL_YEARLY_RECURRENCE:
3186
0
            next_year(impl);
3187
0
            break;
3188
3189
0
        default:
3190
0
            icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
3191
0
            return icaltime_null_time();
3192
0
        }
3193
3194
0
        impl->last = occurrence_as_icaltime(impl, 1);
3195
3196
        /* Ignore times that are after the MAX year,
3197
           or the UNTIL time, or the end time */
3198
0
        if (impl->last.year > MAX_TIME_T_YEAR ||
3199
0
            (!icaltime_is_null_time(impl->rule.until) &&
3200
0
             icaltime_compare(impl->last, impl->rule.until) > 0) ||
3201
0
            (!icaltime_is_null_time(impl->iend) &&
3202
0
             icaltime_compare(impl->last, impl->iend) >= 0)) {
3203
0
            return icaltime_null_time();
3204
0
        }
3205
3206
0
    } while (icaltime_compare(impl->last, impl->istart) < 0 ||
3207
0
             !check_contracting_rules(impl));
3208
3209
0
    impl->occurrence_no++;
3210
3211
0
    return impl->last;
3212
0
}
3213
3214
struct icaltimetype icalrecur_iterator_prev(icalrecur_iterator *impl)
3215
0
{
3216
    /* Quit if last time is before the DTSTART time */
3217
0
    if (!impl || icaltime_compare(impl->last, impl->dtstart) < 0) {
3218
0
        return icaltime_null_time();
3219
0
    }
3220
3221
#if 0 //  Mostly for testing -- users probably don't want/expect this
3222
    /* If last time is valid, return it */
3223
    if (impl->rule.count != 0 && impl->occurrence_no == impl->rule.count &&
3224
        (icaltime_is_null_time(impl->iend) ||
3225
         icaltime_compare(impl->last, impl->iend) <= 0) &&
3226
        check_contracting_rules(impl)) {
3227
3228
        impl->occurrence_no--;
3229
        return impl->last;
3230
    }
3231
#endif
3232
3233
    /* Iterate until we get the next valid time */
3234
0
    do {
3235
0
        switch (impl->rule.freq) {
3236
0
        case ICAL_SECONDLY_RECURRENCE:
3237
0
            prev_second(impl);
3238
0
            break;
3239
3240
0
        case ICAL_MINUTELY_RECURRENCE:
3241
0
            prev_minute(impl);
3242
0
            break;
3243
3244
0
        case ICAL_HOURLY_RECURRENCE:
3245
0
            prev_hour(impl);
3246
0
            break;
3247
3248
0
        case ICAL_DAILY_RECURRENCE:
3249
0
            prev_day(impl);
3250
0
            break;
3251
3252
0
        case ICAL_WEEKLY_RECURRENCE:
3253
0
            prev_week(impl);
3254
0
            break;
3255
3256
0
        case ICAL_MONTHLY_RECURRENCE:
3257
0
            prev_month(impl);
3258
0
            break;
3259
3260
0
        case ICAL_YEARLY_RECURRENCE:
3261
0
            prev_year(impl);
3262
0
            break;
3263
3264
0
        default:
3265
0
            icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
3266
0
            return icaltime_null_time();
3267
0
        }
3268
3269
0
        impl->last = occurrence_as_icaltime(impl, 1);
3270
3271
        /* Ignore times that are before the DTSTART time */
3272
0
        if (icaltime_compare(impl->last, impl->dtstart) < 0 ||
3273
0
            (!icaltime_is_null_time(impl->istart) &&
3274
0
             icaltime_compare(impl->last, impl->istart) < 0)) {
3275
0
            return icaltime_null_time();
3276
0
        }
3277
3278
0
    } while (impl->last.year > MAX_TIME_T_YEAR ||
3279
0
             (!icaltime_is_null_time(impl->rule.until) &&
3280
0
              icaltime_compare(impl->last, impl->rule.until) > 0) ||
3281
0
             (!icaltime_is_null_time(impl->iend) &&
3282
0
              icaltime_compare(impl->last, impl->iend) > 0) ||
3283
0
             !check_contracting_rules(impl));
3284
3285
0
    impl->occurrence_no--;
3286
3287
0
    return impl->last;
3288
0
}
3289
3290
static int __iterator_set_start(icalrecur_iterator *impl, icaltimetype start)
3291
0
{
3292
0
    icalrecurrencetype_frequency freq = impl->rule.freq;
3293
0
    short interval = impl->rule.interval;
3294
0
    int diff;
3295
3296
0
    impl->istart = start;
3297
0
    impl->occurrence_no = 0;
3298
0
    impl->days_index = ICAL_YEARDAYS_MASK_SIZE;
3299
3300
    /* Set Gregorian start date */
3301
0
    set_datetime(impl, start);
3302
3303
0
    switch (freq) {
3304
0
    case ICAL_YEARLY_RECURRENCE:
3305
        /* For YEARLY rule, begin by setting up the year days array.
3306
           The YEARLY rules work by expanding one year at a time. */
3307
3308
0
        if ((interval > 1) &&
3309
0
            (diff = (impl->istart.year - impl->rstart.year) % interval)) {
3310
            /* Specified start year doesn't match interval -
3311
               bump start to first day of next year that matches interval */
3312
0
            set_day_of_year(impl, 1);
3313
0
            increment_year(impl, interval - diff);
3314
0
        }
3315
3316
        /* Get (adjusted) start date as RSCALE date */
3317
0
        start = occurrence_as_icaltime(impl, 0);
3318
3319
        /* Expand days array for (adjusted) start year -
3320
           fail after hitting the year 20000 if no expanded days match */
3321
0
        while (start.year < 20000) {
3322
0
            expand_year_days(impl, start.year);
3323
0
            if (icalerrno != ICAL_NO_ERROR) {
3324
0
                icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
3325
0
                return 0;
3326
0
            }
3327
0
            if (impl->days_index < ICAL_YEARDAYS_MASK_SIZE) {
3328
0
                break; /* break when a matching day is found */
3329
0
            }
3330
0
            increment_year(impl, interval);
3331
0
            start = occurrence_as_icaltime(impl, 0);
3332
0
        }
3333
3334
        /* Copy the first day into last */
3335
0
        set_day_of_year(impl, impl->days_index);
3336
3337
0
        break;
3338
3339
0
    case ICAL_MONTHLY_RECURRENCE:
3340
        /* For MONTHLY rule, begin by setting up the year days array.
3341
           The MONTHLY rules work by expanding one month at a time. */
3342
3343
0
        if ((interval > 1) &&
3344
0
            (diff = month_diff(impl, impl->rstart, impl->istart) % interval)) {
3345
            /* Specified month doesn't match interval -
3346
               bump start to first day of next month that matches interval */
3347
0
            increment_monthday(impl, -impl->istart.day + 1);
3348
0
            __increment_month(impl, interval - diff);
3349
0
        }
3350
3351
        /* Get (adjusted) start date as RSCALE date */
3352
0
        start = occurrence_as_icaltime(impl, 0);
3353
3354
        /* Expand days array for (adjusted) start month -
3355
           fail after hitting the year 20000 if no expanded days match */
3356
0
        while (start.year < 20000) {
3357
0
            expand_month_days(impl, start.year, start.month);
3358
0
            if (impl->days_index < ICAL_YEARDAYS_MASK_SIZE) {
3359
0
                break; /* break when a matching day is found */
3360
0
            }
3361
0
            increment_month(impl, impl->rule.interval);
3362
0
            start = occurrence_as_icaltime(impl, 0);
3363
0
        }
3364
3365
        /* Copy the first day into last */
3366
0
        set_day_of_year(impl, impl->days_index);
3367
3368
0
        break;
3369
3370
0
    case ICAL_WEEKLY_RECURRENCE:
3371
0
        if (impl->by_ptrs[BY_DAY][0] == ICAL_RECURRENCE_ARRAY_MAX) {
3372
            /* Weekly recurrences with no BY_DAY data should occur on the
3373
               same day of the week as the start time . */
3374
0
            impl->by_ptrs[BY_DAY][0] = (short)get_day_of_week(impl);
3375
3376
0
        } else {
3377
0
            adjust_to_byday(impl);
3378
3379
            /* If start == DTSTART, adjust rstart */
3380
0
            if (icaltime_compare(start, impl->dtstart) == 0) {
3381
0
                impl->rstart = occurrence_as_icaltime(impl, 0);
3382
0
            }
3383
3384
            /* Get (adjusted) start date as RSCALE date */
3385
0
            start = occurrence_as_icaltime(impl, 0);
3386
3387
0
            if ((interval > 1) &&
3388
0
                (diff = (day_diff(impl, impl->rstart, start) + 6) / 7) % interval) {
3389
                /* Specified week doesn't match interval -
3390
                   bump start to next week that matches interval */
3391
0
                increment_monthday(impl, 7 * (interval - diff));
3392
0
            }
3393
0
        }
3394
0
        break;
3395
3396
0
    case ICAL_DAILY_RECURRENCE:
3397
0
        if ((interval > 1) &&
3398
0
            (diff = day_diff(impl, impl->rstart, impl->istart) % interval)) {
3399
            /* Specified day doesn't match interval -
3400
               bump start to next day that matches interval */
3401
0
            increment_monthday(impl, interval - diff);
3402
0
        }
3403
0
        break;
3404
3405
0
    case ICAL_HOURLY_RECURRENCE:
3406
0
        if ((interval > 1) &&
3407
0
            (diff = abs(impl->istart.hour - impl->rstart.hour) % interval)) {
3408
            /* Specified hour doesn't match interval -
3409
               bump start to next hour that matches interval */
3410
0
            increment_hour(impl, interval - diff);
3411
0
        }
3412
0
        break;
3413
3414
0
    case ICAL_MINUTELY_RECURRENCE:
3415
0
        if ((interval > 1) &&
3416
0
            (diff = abs(impl->istart.minute - impl->rstart.minute) % interval)) {
3417
            /* Specified minute doesn't match interval -
3418
               bump start to next minute that matches interval */
3419
0
            increment_minute(impl, interval - diff);
3420
0
        }
3421
0
        break;
3422
3423
0
    case ICAL_SECONDLY_RECURRENCE:
3424
0
        if ((interval > 1) &&
3425
0
            (diff = abs(impl->istart.second - impl->rstart.second) % interval)) {
3426
            /* Specified second doesn't match interval -
3427
               bump start to next second that matches interval */
3428
0
            increment_second(impl, interval - diff);
3429
0
        }
3430
0
        break;
3431
3432
0
    default:
3433
0
        break;
3434
0
    }
3435
3436
    /* Get start date as Gregorian date */
3437
0
    impl->last = occurrence_as_icaltime(impl, 1);
3438
3439
    /* Fail if first instance exceeds MAX_TIME_T_YEAR */
3440
0
    if (impl->last.year > MAX_TIME_T_YEAR) {
3441
0
        icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
3442
0
        return 0;
3443
0
    }
3444
3445
0
    return 1;
3446
0
}
3447
3448
int icalrecur_iterator_set_start(icalrecur_iterator *impl,
3449
                                 struct icaltimetype start)
3450
0
{
3451
    /* We can't adjust start date if we need to count occurrences */
3452
0
    if (impl->rule.count > 0) {
3453
0
        icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
3454
0
        return 0;
3455
0
    }
3456
3457
    /* Convert start to same time zone as DTSTART */
3458
0
    start = icaltime_convert_to_zone(start, (icaltimezone *)impl->dtstart.zone);
3459
3460
0
    if (icaltime_compare(start, impl->dtstart) < 0) {
3461
        /* If start is before DTSTART, use DTSTART */
3462
0
        start = impl->dtstart;
3463
0
    } else if (!icaltime_is_null_time(impl->rule.until) &&
3464
0
               icaltime_compare(start, impl->rule.until) > 0) {
3465
        /* If start is after UNTIL, we're done */
3466
0
        impl->last = start;
3467
0
        return 1;
3468
0
    }
3469
3470
0
    return __iterator_set_start(impl, start);
3471
0
}
3472
3473
int icalrecur_iterator_set_end(icalrecur_iterator *impl,
3474
                               struct icaltimetype end)
3475
0
{
3476
    /* Convert end to same time zone as DTSTART */
3477
0
    end = icaltime_convert_to_zone(end, (icaltimezone *)impl->dtstart.zone);
3478
3479
0
    impl->iend = end;
3480
3481
0
    return 1;
3482
0
}
3483
3484
int icalrecur_iterator_set_range(icalrecur_iterator *impl,
3485
                                 struct icaltimetype from,
3486
                                 struct icaltimetype to)
3487
0
{
3488
0
    if (impl->rule.count > 0 || icaltime_is_null_time(from)) {
3489
        /* Can't set a range without 'from' or if we need to count occurrences */
3490
0
        icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
3491
0
        return 0;
3492
0
    }
3493
3494
0
    if (!icaltime_is_null_time(to) && icaltime_compare(to, from) < 0) {
3495
        /* Setting up for the reverse iterator */
3496
0
        const icaltimezone *zone = impl->dtstart.zone;
3497
3498
        /* Convert 'from' to same time zone as DTSTART */
3499
0
        from = icaltime_convert_to_zone(from, (icaltimezone *)zone);
3500
3501
0
        if (icaltime_compare(from, impl->rule.until) > 0) {
3502
            /* If 'from' is after UNTIL, use UNTIL */
3503
0
            from = impl->rule.until;
3504
0
        } else if (icaltime_compare(from, impl->dtstart) < 0) {
3505
            /* If 'from' is before START, we're done */
3506
0
            impl->last = from;
3507
0
            return 1;
3508
0
        }
3509
3510
0
        if (!__iterator_set_start(impl, from))
3511
0
            return 0;
3512
3513
        /* __iterator_set_start() may back us up earlier than 'from'
3514
           Iterate forward until we are later than 'from'.
3515
        */
3516
0
        while (icaltime_compare(impl->last, from) < 0) {
3517
0
            (void)icalrecur_iterator_next(impl);
3518
0
        }
3519
3520
        /* Convert 'to' to same time zone as DTSTART */
3521
0
        to = icaltime_convert_to_zone(to, (icaltimezone *)zone);
3522
3523
0
        if (icaltime_compare(to, impl->dtstart) < 0) {
3524
            /* If 'to' is before DTSTART, use DTSTART */
3525
0
            to = impl->dtstart;
3526
0
        }
3527
3528
0
        impl->istart = to;
3529
0
        impl->iend = from;
3530
0
        impl->days_index = 0;
3531
0
    } else {
3532
0
        if (!icalrecur_iterator_set_start(impl, from))
3533
0
            return 0;
3534
3535
0
        icalrecur_iterator_set_end(impl, to);
3536
0
    }
3537
3538
0
    return 1;
3539
0
}
3540
3541
/************************** Type Routines **********************/
3542
3543
void icalrecurrencetype_clear(struct icalrecurrencetype *recur)
3544
25.9k
{
3545
25.9k
    memset(recur,
3546
25.9k
           ICAL_RECURRENCE_ARRAY_MAX_BYTE, sizeof(struct icalrecurrencetype));
3547
3548
25.9k
    recur->week_start = ICAL_MONDAY_WEEKDAY;
3549
25.9k
    recur->freq = ICAL_NO_RECURRENCE;
3550
25.9k
    recur->interval = 1;
3551
25.9k
    recur->until = icaltime_null_time();
3552
25.9k
    recur->count = 0;
3553
25.9k
    recur->rscale = NULL;
3554
25.9k
    recur->skip = ICAL_SKIP_OMIT;
3555
25.9k
}
3556
3557
enum icalrecurrencetype_weekday icalrecurrencetype_day_day_of_week(short day)
3558
31.7M
{
3559
31.7M
    return abs(day) % 8;
3560
31.7M
}
3561
3562
int icalrecurrencetype_day_position(short day)
3563
1.53k
{
3564
1.53k
    int wd, pos;
3565
3566
1.53k
    wd = icalrecurrencetype_day_day_of_week(day);
3567
3568
1.53k
    pos = (abs(day) - wd) / 8 * ((day < 0) ? -1 : 1);
3569
3570
1.53k
    return pos;
3571
1.53k
}
3572
3573
short icalrecurrencetype_encode_day(enum icalrecurrencetype_weekday weekday, int position)
3574
179k
{
3575
179k
    return (weekday + (8 * abs(position))) * ((position < 0) ? -1 : 1);
3576
179k
}
3577
3578
int icalrecurrencetype_month_is_leap(short month)
3579
418
{
3580
418
    return (month & LEAP_MONTH);
3581
418
}
3582
3583
int icalrecurrencetype_month_month(short month)
3584
198
{
3585
198
    return (month & ~LEAP_MONTH);
3586
198
}
3587
3588
short icalrecurrencetype_encode_month(int month, int is_leap)
3589
0
{
3590
0
    return month | (is_leap ? LEAP_MONTH : 0);
3591
0
}
3592
3593
int icalrecur_expand_recurrence(const char *rule,
3594
                                icaltime_t start, int count, icaltime_t *array)
3595
0
{
3596
0
    struct icalrecurrencetype recur;
3597
0
    icalrecur_iterator *ritr;
3598
0
    icaltime_t tt;
3599
0
    struct icaltimetype icstart, next;
3600
0
    int i = 0;
3601
3602
0
    memset(array, 0, count * sizeof(icaltime_t));
3603
3604
0
    icstart = icaltime_from_timet_with_zone(start, 0, 0);
3605
3606
0
    recur = icalrecurrencetype_from_string(rule);
3607
0
    ritr = icalrecur_iterator_new(recur, icstart);
3608
0
    if (ritr) {
3609
0
        for (next = icalrecur_iterator_next(ritr);
3610
0
             !icaltime_is_null_time(next) && i < count;
3611
0
             next = icalrecur_iterator_next(ritr)) {
3612
0
            tt = icaltime_as_timet(next);
3613
3614
0
            if (tt >= start) {
3615
0
                array[i++] = tt;
3616
0
            }
3617
0
        }
3618
0
        icalrecur_iterator_free(ritr);
3619
0
    }
3620
0
    if (recur.rscale)
3621
0
        icalmemory_free_buffer(recur.rscale);
3622
3623
0
    return 1;
3624
0
}
3625
3626
ical_invalid_rrule_handling ical_get_invalid_rrule_handling_setting(void)
3627
1.41k
{
3628
1.41k
    ical_invalid_rrule_handling myHandling;
3629
3630
1.41k
#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD
3631
1.41k
    pthread_mutex_lock(&invalid_rrule_mutex);
3632
1.41k
#endif
3633
3634
1.41k
    myHandling = invalidRruleHandling;
3635
3636
1.41k
#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD
3637
1.41k
    pthread_mutex_unlock(&invalid_rrule_mutex);
3638
1.41k
#endif
3639
3640
1.41k
    return myHandling;
3641
1.41k
}
3642
3643
void ical_set_invalid_rrule_handling_setting(ical_invalid_rrule_handling newSetting)
3644
0
{
3645
0
#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD
3646
0
    pthread_mutex_lock(&invalid_rrule_mutex);
3647
0
#endif
3648
3649
0
    invalidRruleHandling = newSetting;
3650
3651
0
#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD
3652
0
    pthread_mutex_unlock(&invalid_rrule_mutex);
3653
0
#endif
3654
0
}