128#include "icalerror_p.h"
140#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD
142static pthread_mutex_t invalid_rrule_mutex = PTHREAD_MUTEX_INITIALIZER;
147#if defined(HAVE_LIBICU)
148#include <unicode/ucal.h>
149#include <unicode/ustring.h>
154#undef ICAL_BY_MONTH_SIZE
155#undef ICAL_BY_WEEKNO_SIZE
156#undef ICAL_BY_YEARDAY_SIZE
158#define ICAL_BY_MONTH_SIZE 13
159#define ICAL_BY_WEEKNO_SIZE 54
160#define ICAL_BY_YEARDAY_SIZE 367
165#if defined(HAVE_LIBICU)
166#define MAX_TIME_T_YEAR 20000
168#if (SIZEOF_ICALTIME_T > 4)
171#define MAX_TIME_T_YEAR 2582
175#define MAX_TIME_T_YEAR 2037
179#define LEAP_MONTH 0x1000
184static short daymask_find_next_bit(
const unsigned long *days,
short start_index);
188static const struct freq_map {
206 if (strcasecmp(str, freq_map[i].str) == 0) {
207 return freq_map[i].kind;
218 if (freq_map[i].kind == kind) {
219 return freq_map[i].str;
225static const struct skip_map {
239 if (strcasecmp(str, skip_map[i].str) == 0) {
240 return skip_map[i].kind;
251 if (skip_map[i].kind == kind) {
252 return skip_map[i].str;
258static const struct wd_map {
276 if (wd_map[i].wd == kind) {
277 return wd_map[i].str;
289 if (strcasecmp(str, wd_map[i].str) == 0) {
299static void icalrecur_free_by(icalrecurrence_by_data *by)
308 if (by->size == size) {
313 icalrecur_free_by(by);
317 if ((by->data == NULL) || (by->size == 0)) {
318 if ((by->data != NULL) || (by->size != 0)) {
336 if (size > by->size) {
337 memset(&by->data[by->size], 0, (
size_t)(size - by->size) *
sizeof(by->data[0]));
347struct icalrecur_parser {
353 struct icalrecurrencetype *rt;
366struct expand_split_map_struct {
382static const struct expand_split_map_struct expand_map[] = {
384 {
ICAL_SECONDLY_RECURRENCE, {1, 3, 1, 1, 1, 1, 1, 1, 1}},
385 {
ICAL_MINUTELY_RECURRENCE, {1, 3, 1, 1, 1, 1, 1, 2, 1}},
386 {
ICAL_HOURLY_RECURRENCE, {1, 3, 1, 1, 1, 1, 2, 2, 1}},
387 {
ICAL_DAILY_RECURRENCE, {1, 3, 3, 1, 1, 2, 2, 2, 1}},
388 {
ICAL_WEEKLY_RECURRENCE, {1, 3, 3, 3, 2, 2, 2, 2, 1}},
389 {
ICAL_MONTHLY_RECURRENCE, {1, 3, 3, 2, 2, 2, 2, 2, 1}},
390 {
ICAL_YEARLY_RECURRENCE, {2, 2, 2, 2, 2, 2, 2, 2, 1}},
393static const struct recur_map {
399 {
"BYMONTH", ICAL_BY_MONTH_SIZE, 1, 0},
400 {
"BYWEEKNO", ICAL_BY_WEEKNO_SIZE, -1, 0},
401 {
"BYYEARDAY", ICAL_BY_YEARDAY_SIZE, -1, 0},
402 {
"BYMONTHDAY", ICAL_BY_MONTHDAY_SIZE, -1, 0},
403 {
"BYDAY", ICAL_BY_DAY_SIZE, 0, 0},
404 {
"BYHOUR", ICAL_BY_HOUR_SIZE, 0, 1},
405 {
"BYMINUTE", ICAL_BY_MINUTE_SIZE, 0, 1},
406 {
"BYSECOND", ICAL_BY_SECOND_SIZE, 0, 1},
407 {
"BYSETPOS", ICAL_BY_SETPOS_SIZE, -1, 0},
410static const char *icalrecur_first_clause(
struct icalrecur_parser *parser)
414 parser->this_clause = parser->copy;
416 idx = strchr(parser->this_clause,
';');
419 parser->next_clause = 0;
425 parser->next_clause = idx;
427 return parser->this_clause;
430static const char *icalrecur_next_clause(
struct icalrecur_parser *parser)
434 parser->this_clause = parser->next_clause;
436 if (parser->this_clause == 0) {
440 idx = strchr(parser->this_clause,
';');
443 parser->next_clause = 0;
447 parser->next_clause = idx;
450 return parser->this_clause;
453static void icalrecur_clause_name_and_value(
struct icalrecur_parser *parser,
454 char **name,
char **value)
458 *name = parser->this_clause;
460 idx = strchr(parser->this_clause,
'=');
476static void sort_byrules(icalrecurrence_by_data *by)
478 short *array = by->data;
482 for (i = 1; i < by->size; i++) {
483 for (j = i - 1; j >= 0 && array[j] > array[j + 1]; j--) {
484 short tmp = array[j + 1];
486 array[j + 1] = array[j];
496static void sort_bysetpos(icalrecurrence_by_data *by)
499#define SIGN(A) ((A) < 0 ? -1 : 1)
501 short *array = by->data;
505 for (i = 1; i < by->size; i++) {
507 j >= 0 && ((SIGN(array[j]) == SIGN(array[j + 1]) && abs(array[j]) > abs(array[j + 1])) ||
508 (array[j] < 0 && array[j + 1] > 0));
510 short tmp = array[j + 1];
512 array[j + 1] = array[j];
523static int icalrecur_add_byrules(
const struct icalrecur_parser *parser, icalrecurrence_by_data *by,
524 int min,
int size,
char *vals)
528 int max = size - (min == 0);
556 int v = strtol(t, &t_end, 10);
567 if (min >= 0 || v <= -max) {
589 by->data[i++] = (short)v;
619static void sort_bydayrules(
struct icalrecur_parser *parser)
622 short *array = by->data;
624 int week_start, i, j;
628 for (i = 0; i < by->size; i++) {
629 for (j = 0; j < i; j++) {
640 short tmp = array[j];
649static int icalrecur_add_bydayrules(
struct icalrecur_parser *parser,
670 if (idx >= by->size) {
692 long tmpl = strtol(t, &t_end, 10);
693 weekno = (
signed char)tmpl;
697 if (weekno != tmpl) {
703 if ((weekno == 0) && (t != t_end)) {
738 sort_bydayrules(parser);
753 memset(rule, 0,
sizeof(*rule));
755 icalrecurrencetype_clear(rule);
765 icalmemory_free_buffer(p); \
772 SAFEFREE(recur->
by[i].data);
784 icalerror_check_arg_rv((recur != NULL),
"recur");
785 icalerror_check_arg_rv((recur->
refcount > 0),
"recur->refcount > 0");
792 icalerror_check_arg_rv((recur != NULL),
"recur");
793 icalerror_check_arg_rv((recur->
refcount > 0),
"recur->refcount > 0");
801 icalrecurrencetype_free(recur, 1);
804static void *icalrecur_memdup(
void *p,
size_t size,
int *error)
806 if ((p == NULL) || (size == 0)) {
812 memcpy(newp, p, size);
820static icalrecurrence_by_data icalrecur_by_dup(icalrecurrence_by_data *
by,
int *error)
822 icalrecurrence_by_data newby = {0, 0};
824 newby.data = icalrecur_memdup(
by->data, (
size_t)
by->size *
sizeof(
by->data[0]), error);
826 newby.size =
by->size;
837 icalerror_check_arg_rz((recur != NULL),
"recur");
844 memcpy(res, recur,
sizeof(*res));
856 icalrecurrence_by_data *src_by = &recur->
by[i];
857 icalrecurrence_by_data *dst_by = &res->
by[i];
858 *dst_by = icalrecur_by_dup(src_by, &error);
862 icalrecurrencetype_free(res, 1);
871 struct icalrecur_parser parser = {0};
874 icalerror_check_arg_re(str != 0,
"str", 0);
884 parser.this_clause = parser.copy;
886 if (parser.copy == 0) {
893 for (icalrecur_first_clause(&parser);
894 parser.this_clause != 0; icalrecur_next_clause(&parser)) {
898 icalrecur_clause_name_and_value(&parser, &name, &value);
901 if (strlen(parser.this_clause) > 0) {
911 }
else if (strcasecmp(name,
"FREQ") == 0) {
921 }
else if (strcasecmp(name,
"RSCALE") == 0) {
922 if (parser.rt->
rscale != NULL) {
928 }
else if (strcasecmp(name,
"SKIP") == 0) {
938 }
else if (strcasecmp(name,
"COUNT") == 0) {
943 parser.rt->
count = atoi(value);
945 if (parser.rt->
count < 1) {
949 }
else if (strcasecmp(name,
"UNTIL") == 0) {
959 }
else if (strcasecmp(name,
"INTERVAL") == 0) {
964 int tmp = atoi(value);
979 }
else if (strcasecmp(name,
"WKST") == 0) {
988 sort_bydayrules(&parser);
991 }
else if (strncasecmp(name,
"BY", 2) == 0) {
995 if (strcasecmp(name + 2, recur_map[byrule].str + 2) == 0) {
997 r = icalrecur_add_bydayrules(&parser, value);
999 icalrecurrence_by_data *by = &parser.rt->
by[byrule];
1000 r = icalrecur_add_byrules(&parser, by,
1001 recur_map[byrule].min,
1002 recur_map[byrule].size,
1019 icalrecurrencetype_clear(parser.rt);
1025 icalrecurrence_by_data *by = &parser.rt->
by[byrule];
1028 expand_map[parser.rt->
freq].map[byrule] == ILLEGAL) {
1034 icalrecurrencetype_clear(parser.rt);
1037 icalrecur_free_by(by);
1065 size_t buf_sz = 200;
1066 char temp[20] = {0};
1076 if (recur->
rscale != 0) {
1095 snprintf(temp,
sizeof(temp),
"%d", recur->
interval);
1110 const icalrecurrence_by_data *by = &recur->
by[j];
1118 int limit = recur_map[j].size - 1;
1119 for (i = 0; i < limit && i < by->size; i++) {
1128 snprintf(temp,
sizeof(temp),
"%d%s", pos, daystr);
1134 snprintf(temp,
sizeof(temp),
"%dL",
1138 snprintf(temp,
sizeof(temp),
"%d", by->data[i]);
1142 if ((i + 1) < limit && by->size > i + 1) {
1152 print_date_to_string(temp, &(recur->
until));
1154 print_datetime_to_string(temp, &(recur->
until));
1161 else if (recur->
count != 0) {
1162 snprintf(temp,
sizeof(temp),
"%d", recur->
count);
1174#define BITS_PER_LONG ((unsigned short)(8 * sizeof(unsigned long)))
1177#define LONGS_PER_BITS(n) (((n) + BITS_PER_LONG - 1) / BITS_PER_LONG)
1179#define ICAL_YEARDAYS_MASK_SIZE (ICAL_BY_YEARDAY_SIZE + 7)
1180#define ICAL_YEARDAYS_MASK_OFFSET 4
1183typedef struct icalrecurrence_iterator_by_data {
1184 icalrecurrence_by_data by;
1191} icalrecurrence_iterator_by_data;
1193struct icalrecur_iterator_impl {
1194 struct icaltimetype dtstart;
1195 struct icalrecurrencetype *rule;
1197 struct icaltimetype rstart;
1198 struct icaltimetype istart;
1199 struct icaltimetype iend;
1200 struct icaltimetype last;
1201 int32_t occurrence_no;
1204 int32_t recurrence_set_size;
1205 short sp_idxp, sp_idxn;
1208#if defined(HAVE_LIBICU)
1213 struct icaltimetype period_start;
1227 unsigned long days[LONGS_PER_BITS(ICAL_YEARDAYS_MASK_SIZE)];
1234static void daysmask_clearall(
unsigned long mask[])
1237 sizeof(
unsigned long) * LONGS_PER_BITS(ICAL_YEARDAYS_MASK_SIZE));
1240static void daysmask_set_range(
unsigned long days[],
int fromDayIncl,
int untilDayExcl,
int v)
1242 int fromBitIdx = fromDayIncl + ICAL_YEARDAYS_MASK_OFFSET;
1243 int untilBitIdx = untilDayExcl + ICAL_YEARDAYS_MASK_OFFSET;
1245 for (
int word_idx = fromBitIdx / BITS_PER_LONG;
1246 word_idx < (int)((untilBitIdx + BITS_PER_LONG - 1) / BITS_PER_LONG);
1248 int lowerBitIdxIncl = (fromBitIdx <= (int)(word_idx * BITS_PER_LONG))
1250 : (fromBitIdx - (int)(word_idx * BITS_PER_LONG));
1251 int upperBitIdxExcl = (untilBitIdx >= (int)((word_idx + 1) * BITS_PER_LONG))
1252 ? (int)BITS_PER_LONG
1253 : (int)(untilBitIdx - (int)(word_idx * BITS_PER_LONG));
1255 unsigned long mask = (
unsigned long)-1;
1256 if (lowerBitIdxIncl > 0) {
1257 mask &= ((
unsigned long)-1) << lowerBitIdxIncl;
1259 if (upperBitIdxExcl < (
int)BITS_PER_LONG) {
1260 mask &= ((
unsigned long)-1) >> (BITS_PER_LONG - upperBitIdxExcl);
1264 days[word_idx] |= mask;
1266 days[word_idx] &= ~mask;
1271static int daysmask_setbit(
unsigned long mask[],
short n,
int v)
1275 n += ICAL_YEARDAYS_MASK_OFFSET;
1278 prev = (mask[n / BITS_PER_LONG] & (1UL << (n % BITS_PER_LONG))) ? 1 : 0;
1280 prev = (mask[n / BITS_PER_LONG] & (1UL >> (-n % BITS_PER_LONG))) ? 1 : 0;
1285 mask[n / BITS_PER_LONG] |= (1UL << (n % BITS_PER_LONG));
1287 mask[n / BITS_PER_LONG] |= (1UL >> (-n % BITS_PER_LONG));
1291 mask[n / BITS_PER_LONG] &= ~(1UL << (n % BITS_PER_LONG));
1293 mask[n / BITS_PER_LONG] &= ~(1UL >> (-n % BITS_PER_LONG));
1301static unsigned long daysmask_getbit(
const unsigned long mask[],
short n)
1303 n += ICAL_YEARDAYS_MASK_OFFSET;
1304 return (mask[n / BITS_PER_LONG] >> (n % BITS_PER_LONG)) & 1;
1309 return (impl->bydata[byrule].orig_data == 1);
1312static void recur_iterator_set_static_single_by_value(icalrecur_iterator *impl,
1315 icalrecurrence_iterator_by_data *by = &impl->bydata[byrule];
1317 by->by.data = &by->buffer_value;
1318 by->by.data[0] = value;
1321static void setup_defaults(icalrecur_iterator *impl,
1326 if (impl->dtstart.is_date && recur_map[byrule].isTime) {
1332 recur_iterator_set_static_single_by_value(impl, byrule, 0);
1333 }
else if (expand_map[freq].map[byrule] == EXPAND) {
1336 if (impl->bydata[byrule].by.size == 0) {
1337 recur_iterator_set_static_single_by_value(impl, byrule, (
short)deftime);
1344static int weeks_in_year(
int year)
1350 return (52 + is_long);
1359static void __get_start_time(icalrecur_iterator *impl,
icaltimetype date,
1369 *
hour = impl->rstart.hour;
1377 *
minute = impl->rstart.minute;
1385 *
second = impl->rstart.second;
1391#if defined(HAVE_LIBICU)
1409 UErrorCode status = U_ZERO_ERROR;
1411 icalarray *calendars;
1416 en = ucal_getKeywordValuesForLocale(
"calendar",
"",
false, &status);
1417 while ((cal = uenum_next(en, NULL, &status))) {
1426static void set_second(icalrecur_iterator *impl,
int second)
1428 ucal_set(impl->rscale, UCAL_SECOND, (int32_t)
second);
1431static void set_minute(icalrecur_iterator *impl,
int minute)
1433 ucal_set(impl->rscale, UCAL_MINUTE, (int32_t)
minute);
1436static void set_hour(icalrecur_iterator *impl,
int hour)
1438 ucal_set(impl->rscale, UCAL_HOUR_OF_DAY, (int32_t)
hour);
1441static void __set_month(icalrecur_iterator *impl,
int month)
1447 ucal_set(impl->rscale, UCAL_MONTH, (int32_t)
month);
1448 if (is_leap_month) {
1449 ucal_set(impl->rscale, UCAL_IS_LEAP_MONTH, 1);
1453static int set_month(icalrecur_iterator *impl,
int month)
1455 UErrorCode status = U_ZERO_ERROR;
1458 __set_month(impl,
month);
1460 ucal_set(impl->rscale, UCAL_DAY_OF_MONTH, (int32_t)1);
1463 (int)ucal_get(impl->rscale, UCAL_MONTH, &status);
1465 if (ucal_get(impl->rscale, UCAL_IS_LEAP_MONTH, &status)) {
1466 actual_month |= LEAP_MONTH;
1469 if (actual_month !=
month) {
1470 switch (impl->rule->skip) {
1480 ucal_add(impl->rscale, UCAL_MONTH, (int32_t)-1, &status);
1490 (
int)ucal_get(impl->rscale, UCAL_MONTH, &status));
1493static int get_months_in_year(icalrecur_iterator *impl,
int year)
1495 UErrorCode status = U_ZERO_ERROR;
1498 ucal_set(impl->rscale, UCAL_YEAR, (int32_t)
year);
1502 (
int)ucal_getLimit(impl->rscale, UCAL_MONTH,
1503 UCAL_ACTUAL_MAXIMUM, &status));
1506static int get_days_in_year(icalrecur_iterator *impl,
int year)
1508 UErrorCode status = U_ZERO_ERROR;
1511 ucal_set(impl->rscale, UCAL_YEAR, (int32_t)
year);
1514 return (
int)ucal_getLimit(impl->rscale, UCAL_DAY_OF_YEAR,
1515 UCAL_ACTUAL_MAXIMUM, &status);
1518static void set_day_of_year(icalrecur_iterator *impl,
int doy)
1521 doy += get_days_in_year(impl, 0);
1524 ucal_set(impl->rscale, UCAL_DAY_OF_YEAR, (int32_t)doy);
1527static int get_start_of_week(icalrecur_iterator *impl)
1529 UErrorCode status = U_ZERO_ERROR;
1532 doy = (int)ucal_get(impl->rscale, UCAL_DAY_OF_YEAR, &status);
1533 dow = (int)ucal_get(impl->rscale, UCAL_DAY_OF_WEEK, &status);
1534 dow -= (int)impl->rule->week_start;
1542static int get_day_of_week(icalrecur_iterator *impl)
1544 UErrorCode status = U_ZERO_ERROR;
1546 return (
int)ucal_get(impl->rscale, UCAL_DAY_OF_WEEK, &status);
1549static int get_week_number(icalrecur_iterator *impl,
struct icaltimetype tt)
1551 UErrorCode status = U_ZERO_ERROR;
1556 last_millis = ucal_getMillis(impl->rscale, &status);
1559 ucal_setDate(impl->rscale,
1560 (int32_t)tt.
year, (int32_t)
month, (int32_t)tt.
day, &status);
1562 ucal_set(impl->rscale, UCAL_IS_LEAP_MONTH, 1);
1565 weekno = (int)ucal_get(impl->rscale, UCAL_WEEK_OF_YEAR, &status);
1568 ucal_setMillis(impl->rscale, last_millis, &status);
1573static int get_days_in_month(icalrecur_iterator *impl,
int month,
int year)
1575 UErrorCode status = U_ZERO_ERROR;
1577 ucal_set(impl->rscale, UCAL_YEAR, (int32_t)
year);
1580 month = impl->rstart.month;
1582 __set_month(impl,
month);
1584 return (
int)ucal_getLimit(impl->rscale,
1585 UCAL_DAY_OF_MONTH, UCAL_ACTUAL_MAXIMUM, &status);
1588static void prepare_rscale_adjusted(icalrecur_iterator *impl,
1591 ucal_set(impl->rscale, UCAL_YEAR, (int32_t)
year);
1594 month = impl->rstart.month;
1596 __set_month(impl,
month);
1599 day = impl->rstart.day;
1600 }
else if (
day < 0) {
1601 day += 1 + (int)ucal_getLimit(impl->rscale, UCAL_DAY_OF_MONTH,
1602 UCAL_ACTUAL_MAXIMUM, status);
1604 ucal_set(impl->rscale, UCAL_DAY_OF_MONTH, (int32_t)
day);
1607static int get_day_of_year(icalrecur_iterator *impl,
1610 UErrorCode status = U_ZERO_ERROR;
1611 prepare_rscale_adjusted(impl,
year,
month,
day, &status);
1612 return (
int)ucal_get(impl->rscale, UCAL_DAY_OF_YEAR, &status);
1615static int get_day_of_week_adjusted(icalrecur_iterator *impl,
1618 UErrorCode status = U_ZERO_ERROR;
1619 prepare_rscale_adjusted(impl,
year,
month,
day, &status);
1620 return (
int)ucal_get(impl->rscale, UCAL_DAY_OF_WEEK, &status);
1623static struct icaltimetype occurrence_as_icaltime(icalrecur_iterator *impl,
1627 UErrorCode status = U_ZERO_ERROR;
1628 UCalendar *cal = impl->rscale;
1629 int is_leap_month = 0;
1631 if (normalize && (impl->rscale != impl->greg)) {
1633 UDate millis = ucal_getMillis(impl->rscale, &status);
1635 ucal_setMillis(impl->greg, millis, &status);
1639 (int)ucal_get(impl->rscale, UCAL_IS_LEAP_MONTH, &status);
1642 tt.
year = (int)ucal_get(cal, UCAL_YEAR, &status);
1643 tt.
day = (int)ucal_get(cal, UCAL_DATE, &status);
1645 (int)ucal_get(cal, UCAL_MONTH, &status);
1646 if (is_leap_month) {
1647 tt.
month |= LEAP_MONTH;
1651 tt.
hour = (int)ucal_get(cal, UCAL_HOUR_OF_DAY, &status);
1652 tt.
minute = (int)ucal_get(cal, UCAL_MINUTE, &status);
1653 tt.
second = (int)ucal_get(cal, UCAL_SECOND, &status);
1659static struct icaltimetype __icaltime_from_day_of_year(icalrecur_iterator *impl,
1660 int day,
int year,
int *weekno)
1662 ucal_set(impl->rscale, UCAL_YEAR, (int32_t)
year);
1664 day += get_days_in_year(impl, 0) + 1;
1667 ucal_set(impl->rscale, UCAL_DAY_OF_YEAR, (int32_t)
day);
1670 UErrorCode status = U_ZERO_ERROR;
1672 *weekno = (int)ucal_get(impl->rscale, UCAL_WEEK_OF_YEAR, &status);
1675 return occurrence_as_icaltime(impl, 0);
1678static void increment_year(icalrecur_iterator *impl,
int inc)
1680 UErrorCode status = U_ZERO_ERROR;
1682 ucal_add(impl->rscale, UCAL_YEAR, (int32_t)inc, &status);
1685static void __increment_month(icalrecur_iterator *impl,
int inc)
1687 UErrorCode status = U_ZERO_ERROR;
1689 ucal_add(impl->rscale, UCAL_MONTH, (int32_t)inc, &status);
1692static void increment_monthday(icalrecur_iterator *impl,
int inc)
1694 UErrorCode status = U_ZERO_ERROR;
1696 ucal_add(impl->rscale, UCAL_DAY_OF_MONTH, (int32_t)inc, &status);
1699static void increment_hour(icalrecur_iterator *impl,
int inc)
1701 UErrorCode status = U_ZERO_ERROR;
1703 ucal_add(impl->rscale, UCAL_HOUR_OF_DAY, (int32_t)inc, &status);
1706static void increment_minute(icalrecur_iterator *impl,
int inc)
1708 UErrorCode status = U_ZERO_ERROR;
1710 ucal_add(impl->rscale, UCAL_MINUTE, (int32_t)inc, &status);
1713static void increment_second(icalrecur_iterator *impl,
int inc)
1715 UErrorCode status = U_ZERO_ERROR;
1717 ucal_add(impl->rscale, UCAL_SECOND, (int32_t)inc, &status);
1720static bool validate_byrule(icalrecur_iterator *impl,
1722 short (*decode_val)(
short *,
bool),
1725 if (has_by_data(impl, byrule)) {
1726 UErrorCode status = U_ZERO_ERROR;
1727 const icalrecurrence_by_data *by_ptr = &impl->bydata[byrule].by;
1729 (short)ucal_getLimit(impl->rscale, field, UCAL_MAXIMUM, &status);
1732 for (idx = 0; idx < by_ptr->size; idx++) {
1733 short val = decode_val ? decode_val(&by_ptr->data[idx], decode_flags) : by_ptr->data[idx];
1735 if (abs(val) > max) {
1744static short decode_month(
short *
month,
bool is_hebrew)
1746 if (is_hebrew && *
month > 5) {
1757static short decode_day(
short *
day,
bool flags)
1764static bool initialize_rscale(icalrecur_iterator *impl)
1768 char locale[ULOC_KEYWORD_AND_VALUES_CAPACITY] = {0};
1769 UErrorCode status = U_ZERO_ERROR;
1770 UChar *tzid = (UChar *)UCAL_UNKNOWN_ZONE_ID;
1771 bool is_hebrew =
false;
1779 if (src && !strncmp(src, prefix, strlen(prefix))) {
1781 src += strlen(prefix);
1785 size_t len = (strlen(src) + 1) * U_SIZEOF_UCHAR;
1787 tzid = u_strFromUTF8Lenient(tzid, (int32_t)len, NULL, src, -1, &status);
1788 if (U_FAILURE(status)) {
1795 (void)uloc_setKeywordValue(
"calendar",
"gregorian",
1796 locale,
sizeof(locale), &status);
1799 impl->greg = ucal_open(tzid, -1, locale, UCAL_DEFAULT, &status);
1801 ucal_setDateTime(impl->greg,
1802 (int32_t)dtstart.
year,
1803 (int32_t)(dtstart.
month - 1),
1804 (int32_t)dtstart.
day,
1805 (int32_t)dtstart.
hour,
1807 (int32_t)dtstart.
second, &status);
1809 if (!impl->greg || U_FAILURE(status)) {
1816 impl->rscale = impl->greg;
1823 for (r = rule->
rscale; *r; r++) {
1824 *r = tolower((
int)*r);
1828 en = ucal_getKeywordValuesForLocale(
"calendar",
"",
false, &status);
1829 while ((cal = uenum_next(en, NULL, &status))) {
1830 if (!strcmp(cal, rule->
rscale)) {
1831 is_hebrew = !strcmp(rule->
rscale,
"hebrew");
1842 (void)uloc_setKeywordValue(
"calendar", rule->
rscale,
1843 locale,
sizeof(locale), &status);
1846 impl->rscale = ucal_open(tzid, -1, locale, UCAL_DEFAULT, &status);
1848 UDate millis = ucal_getMillis(impl->greg, &status);
1850 ucal_setMillis(impl->rscale, millis, &status);
1852 if (!impl->rscale || U_FAILURE(status)) {
1860 &decode_month, is_hebrew) ||
1861 !validate_byrule(impl,
ICAL_BY_DAY, UCAL_WEEK_OF_YEAR, &decode_day, 0) ||
1864 !validate_byrule(impl,
ICAL_BY_WEEK_NO, UCAL_WEEK_OF_YEAR, NULL, 0) ||
1871 ucal_setAttribute(impl->rscale, UCAL_MINIMAL_DAYS_IN_FIRST_WEEK, 4);
1872 ucal_setAttribute(impl->rscale, UCAL_FIRST_DAY_OF_WEEK, (int32_t)rule->
week_start);
1875 impl->rstart = occurrence_as_icaltime(impl, 0);
1881static void set_start(icalrecur_iterator *impl,
icaltimetype date)
1883 UErrorCode status = U_ZERO_ERROR;
1885 impl->last.is_date = impl->rstart.is_date;
1886 impl->last.zone = impl->rstart.zone;
1888 if (impl->rstart.is_date) {
1889 ucal_setDate(impl->greg,
1891 (int32_t)(date.
month - 1),
1892 (int32_t)date.
day, &status);
1898 ucal_setDateTime(impl->greg,
1900 (int32_t)(date.
month - 1),
1908 if (impl->rscale != impl->greg) {
1909 UDate millis = ucal_getMillis(impl->greg, &status);
1910 ucal_setMillis(impl->rscale, millis, &status);
1914static void set_datetime(icalrecur_iterator *impl,
icaltimetype date)
1916 UErrorCode status = U_ZERO_ERROR;
1918 impl->last.is_date = impl->rstart.is_date;
1919 impl->last.zone = impl->rstart.zone;
1921 if (impl->rstart.is_date) {
1922 ucal_setDate(impl->greg,
1924 (int32_t)(date.
month - 1),
1925 (int32_t)date.
day, &status);
1927 ucal_setDateTime(impl->greg,
1929 (int32_t)(date.
month - 1),
1937 if (impl->rscale != impl->greg) {
1938 UDate millis = ucal_getMillis(impl->greg, &status);
1939 ucal_setMillis(impl->rscale, millis, &status);
1948 if (impl->rscale == impl->greg) {
1950 diff = __greg_month_diff(a, b);
1955 UErrorCode status = U_ZERO_ERROR;
1960 millis = ucal_getMillis(impl->rscale, &status);
1962 set_day_of_year(impl, 1);
1963 diff = get_months_in_year(impl,
year) - a.
month;
1965 diff += get_months_in_year(impl,
year);
1970 ucal_setMillis(impl->rscale, millis, &status);
1979 UErrorCode status = U_ZERO_ERROR;
1984 millis = ucal_getMillis(impl->rscale, &status);
1986 set_day_of_year(impl, 1);
1988 diff = __day_diff(impl, a, b);
1991 ucal_setMillis(impl->rscale, millis, &status);
1996static void reset_period_start(icalrecur_iterator *impl)
2000 (void)get_day_of_year(impl, start.
year, start.
month, start.
day);
2011 icalarray *calendars =
icalarray_new(
sizeof(
const char **), 1);
2012 const char *cal =
"GREGORIAN";
2019static void set_second(icalrecur_iterator *impl,
int second)
2021 impl->last.second =
second;
2024static void set_minute(icalrecur_iterator *impl,
int minute)
2026 impl->last.minute =
minute;
2029static void set_hour(icalrecur_iterator *impl,
int hour)
2031 impl->last.hour =
hour;
2034static int set_month(icalrecur_iterator *impl,
int month)
2036 return (impl->last.month =
month);
2040#define get_months_in_year(impl, year) (12)
2043static int get_days_in_year(icalrecur_iterator *impl,
int year)
2050static void set_day_of_year(icalrecur_iterator *impl,
int doy)
2055 doy += get_days_in_year(impl, impl->last.year);
2060 impl->last.day = next.day;
2061 impl->last.month = next.month;
2062 impl->last.year = next.year;
2065static int get_start_of_week(
const icalrecur_iterator *impl)
2070static int get_day_of_week(
const icalrecur_iterator *impl)
2077static int get_week_number(icalrecur_iterator *impl,
struct icaltimetype tt)
2092 week = weeks_in_year(tt.
year - 1);
2093 }
else if (week > weeks_in_year(tt.
year)) {
2101static int get_days_in_month(icalrecur_iterator *impl,
int month,
int year)
2108static struct icaltimetype get_dtstart_adjusted(icalrecur_iterator *impl,
2123 }
else if (
day < 0) {
2131static int get_day_of_year(icalrecur_iterator *impl,
2137static int get_day_of_week_adjusted(icalrecur_iterator *impl,
2143static struct icaltimetype occurrence_as_icaltime(icalrecur_iterator *impl,
2149static struct icaltimetype __icaltime_from_day_of_year(icalrecur_iterator *impl,
2150 int day,
int year,
int *weekno)
2155 day += get_days_in_year(impl,
year) + 1;
2161 *weekno = get_week_number(impl, tt);
2166static void increment_year(icalrecur_iterator *impl,
int inc)
2168 impl->last.
year += inc;
2171static void __increment_month(icalrecur_iterator *impl,
int inc)
2175 impl->last.month += inc;
2180 years = impl->last.month / 12;
2182 impl->last.month = impl->last.month % 12;
2184 if (impl->last.month < 0) {
2185 impl->last.month = impl->last.month + 12;
2192 increment_year(impl, years);
2196static void increment_monthday(icalrecur_iterator *impl,
int inc)
2201static void increment_hour(icalrecur_iterator *impl,
int inc)
2206static void increment_minute(icalrecur_iterator *impl,
int inc)
2211static void increment_second(icalrecur_iterator *impl,
int inc)
2216static bool initialize_rscale(icalrecur_iterator *impl)
2218 if (impl->rule->rscale && strcasecmp(impl->rule->rscale,
"GREGORIAN")) {
2223 impl->rstart = impl->dtstart;
2229static void set_start(icalrecur_iterator *impl,
icaltimetype date)
2231 impl->last.year = date.
year;
2232 impl->last.month = date.
month;
2233 impl->last.day = date.
day;
2234 impl->last.is_date = impl->dtstart.is_date;
2235 impl->last.zone = impl->dtstart.zone;
2237 if (!impl->dtstart.is_date) {
2238 __get_start_time(impl, date, &impl->last.hour,
2239 &impl->last.minute, &impl->last.second);
2243static void set_datetime(icalrecur_iterator *impl,
icaltimetype date)
2253 return __greg_month_diff(a, b);
2259 return __day_diff(impl, a, b);
2262static void reset_period_start(icalrecur_iterator *impl)
2265 impl->last.year = impl->period_start.year;
2266 impl->last.month = impl->period_start.month;
2267 impl->last.day = impl->period_start.day;
2272static int get_second(icalrecur_iterator *impl)
2274 return occurrence_as_icaltime(impl, 1).second;
2277static int get_minute(icalrecur_iterator *impl)
2279 return occurrence_as_icaltime(impl, 1).minute;
2282static int get_hour(icalrecur_iterator *impl)
2284 return occurrence_as_icaltime(impl, 1).hour;
2287static bool __iterator_set_start(icalrecur_iterator *impl,
icaltimetype start);
2288static void increment_month(icalrecur_iterator *impl,
int inc);
2289static void expand_month_days(icalrecur_iterator *impl,
int year,
int month);
2290static void expand_year_days(icalrecur_iterator *impl,
int year);
2291static int next_yearday(icalrecur_iterator *impl,
2292 void (*next_period)(icalrecur_iterator *,
int));
2293static int prev_yearday(icalrecur_iterator *impl,
2294 void (*next_period)(icalrecur_iterator *,
int));
2296static void adjust_to_byday(icalrecur_iterator *impl)
2309 short this_dow = (short)get_day_of_week(impl);
2310 short dow = (short)(impl->bydata[
ICAL_BY_DAY].by.data[0] - this_dow);
2313 if (dow != 0 && this_dow < (
short)impl->rule->week_start) {
2317 if ((this_dow < impl->bydata[
ICAL_BY_DAY].by.data[0] && dow >= 0) || dow < 0) {
2319 increment_monthday(impl, dow);
2331 icalrecur_iterator *impl;
2343#define IN_RANGE(val, min, max) ((val) >= (min) && (val) <= (max))
2347 !IN_RANGE(dtstart.
year, 0, MAX_TIME_T_YEAR) ||
2348 !IN_RANGE(dtstart.
month, 1, 12) ||
2349 !IN_RANGE(dtstart.
day, 1,
2351 (!dtstart.
is_date && (!IN_RANGE(dtstart.
hour, 0, 23) ||
2352 !IN_RANGE(dtstart.
minute, 0, 59) ||
2353 !IN_RANGE(dtstart.
second, 0, 59)))) {
2363 memset(impl, 0,
sizeof(icalrecur_iterator));
2365 impl->dtstart = dtstart;
2367#if defined(HAVE_LIBICU)
2393 impl->bydata[byrule].by = impl->rule->by[byrule];
2400 impl->bydata[byrule].orig_data =
2401 (short)(impl->rule->by[byrule].size > 0);
2404 if (expand_map[freq].map[byrule] == ILLEGAL &&
2405 has_by_data(impl, byrule)) {
2409 impl->bydata[byrule].orig_data = 0;
2418 if (initialize_rscale(impl) == 0) {
2443 if (!__iterator_set_start(impl, dtstart)) {
2453 icalerror_check_arg_rv((impl != 0),
"impl");
2455#if defined(HAVE_LIBICU)
2457 if (impl->rscale && (impl->rscale != impl->greg)) {
2458 ucal_close(impl->rscale);
2461 ucal_close(impl->greg);
2493 diff = get_days_in_year(impl,
year) -
2496 diff += get_days_in_year(impl,
year);
2512static void increment_month(icalrecur_iterator *impl,
int inc)
2514 __increment_month(impl, inc);
2517 struct icaltimetype this = occurrence_as_icaltime(impl, 0);
2519 while (this.year < MAX_TIME_T_YEAR) {
2520 icalrecurrence_iterator_by_data *bydata = &impl->bydata[
ICAL_BY_MONTH];
2521 for (bydata->index = 0;
2522 bydata->index < bydata->by.size; bydata->index++) {
2523 if (this.month == bydata->by.data[bydata->index]) {
2528 __increment_month(impl, inc);
2529 this = occurrence_as_icaltime(impl, 0);
2534static int next_unit(icalrecur_iterator *impl,
2536 int (*next_sub_unit)(icalrecur_iterator *),
2537 void (*set_unit)(icalrecur_iterator *,
int),
2538 int (*get_unit)(icalrecur_iterator *),
2540 void (*increment_unit)(icalrecur_iterator *,
int))
2543 (impl->bydata[by_unit].by.size > 0);
2544 int this_frequency = (impl->rule->freq == frequency);
2546 int end_of_data = 0;
2548 icalassert(has_by_unit || this_frequency);
2550 if (next_sub_unit && next_sub_unit(impl) == 0) {
2557 icalrecurrence_iterator_by_data *bydata = &impl->bydata[by_unit];
2558 if (this_frequency) {
2561 size_t stalledCnt = 0;
2562 while ((impl->last.year < MAX_TIME_T_YEAR) && (stalledCnt++ < max_recurrence_time_count)) {
2563 int last_unit = get_unit(impl);
2565 while (bydata->index < bydata->by.size) {
2566 int cur_by = bydata->by.data[bydata->index];
2567 if ((cur_by >= last_unit) &&
2568 ((cur_by - last_unit) % impl->rule->interval) == 0) {
2569 set_unit(impl, cur_by);
2578 if (last_unit + impl->rule->interval < period_len) {
2579 int diff = period_len - last_unit;
2580 multiplier = (diff / impl->rule->interval + (diff % impl->rule->interval > 0));
2582 increment_unit(impl, multiplier * impl->rule->interval);
2587 if (bydata->by.size <= bydata->index) {
2593 if (bydata->index < bydata->by.size) {
2594 set_unit(impl, bydata->by.data[bydata->index]);
2601 increment_unit(impl, impl->rule->interval);
2608static int next_second(icalrecur_iterator *impl)
2611 &set_second, &get_second, 60, &increment_second);
2614static int next_minute(icalrecur_iterator *impl)
2617 &set_minute, &get_minute, 60, &increment_minute);
2620static int next_hour(icalrecur_iterator *impl)
2623 &set_hour, &get_hour, 24, &increment_hour);
2626static int next_day(icalrecur_iterator *impl)
2629 NULL, NULL, 0, &increment_monthday);
2632static int prev_unit(icalrecur_iterator *impl,
2634 int (*prev_sub_unit)(icalrecur_iterator *),
2635 void (*set_unit)(icalrecur_iterator *,
int),
2636 int (*get_unit)(icalrecur_iterator *),
2637 void (*increment_unit)(icalrecur_iterator *,
int))
2640 (impl->bydata[by_unit].by.size > 0);
2641 int this_frequency = (impl->rule->freq == frequency);
2643 int end_of_data = 0;
2645 icalassert(has_by_unit || this_frequency);
2647 if (prev_sub_unit && prev_sub_unit(impl) == 0) {
2652 icalrecurrence_iterator_by_data *bydata = &impl->bydata[by_unit];
2653 if (this_frequency) {
2656 while (impl->last.year > 0) {
2657 int last_unit = get_unit(impl);
2658 while (bydata->index >= 0) {
2659 int cur_by = bydata->by.data[bydata->index];
2660 if ((cur_by <= last_unit) && (impl->rule->interval > 0) &&
2661 ((last_unit - cur_by) % impl->rule->interval) == 0) {
2662 set_unit(impl, cur_by);
2667 bydata->index = bydata->by.size - 1;
2669 if (last_unit - impl->rule->interval > 0) {
2670 multiplier = (last_unit / impl->rule->interval + (last_unit % impl->rule->interval > 0));
2672 increment_unit(impl, -multiplier * impl->rule->interval);
2677 if (bydata->index < 0) {
2679 bydata->by.size - 1;
2684 set_unit(impl, bydata->by.data[bydata->index]);
2689 increment_unit(impl, -impl->rule->interval);
2696static int prev_second(icalrecur_iterator *impl)
2699 &set_second, &get_second, &increment_second);
2702static int prev_minute(icalrecur_iterator *impl)
2705 &set_minute, &get_minute, &increment_minute);
2708static int prev_hour(icalrecur_iterator *impl)
2711 &set_hour, &get_hour, &increment_hour);
2714static int prev_day(icalrecur_iterator *impl)
2717 NULL, NULL, &increment_monthday);
2721static void expand_bymonth_days(icalrecur_iterator *impl,
int year,
int month)
2724 int days_in_month = get_days_in_month(impl,
month,
year);
2727 short doy = ICAL_BY_YEARDAY_SIZE, mday = impl->bydata[
ICAL_BY_MONTH_DAY].by.data[i];
2728 int this_month =
month;
2730 if (abs(mday) > days_in_month) {
2731 int days_in_year = get_days_in_year(impl,
year);
2733 switch (impl->rule->skip) {
2745 if (this_month > get_months_in_year(impl,
year)) {
2746 doy = days_in_year + 1;
2757 if (this_month == 0) {
2766 if (doy == ICAL_BY_YEARDAY_SIZE) {
2767 doy = get_day_of_year(impl,
year, this_month, mday);
2770 daysmask_setbit(impl->days, doy, 1);
2771 if (doy < impl->days_index) {
2772 impl->days_index = doy;
2778static void expand_by_day(icalrecur_iterator *impl,
int year,
2779 int doy_offset,
int last_day,
2780 int first_dow,
int last_dow,
2784 unsigned long bydays[LONGS_PER_BITS(ICAL_YEARDAYS_MASK_SIZE)];
2787 memcpy(bydays, impl->days,
sizeof(bydays));
2789 daysmask_set_range(impl->days, doy_offset + 1, doy_offset + last_day + 1, 0);
2791 for (i = 0; i < impl->bydata[
ICAL_BY_DAY].by.size; i++) {
2795 int first_matching_day, last_matching_day;
2796 int day, this_weekno;
2800 first_matching_day = ((dow + 7 - first_dow) % 7) + 1;
2801 last_matching_day = last_day - ((last_dow + 7 - dow) % 7);
2806 day = first_matching_day;
2808 }
else if (pos > 0) {
2810 day = first_matching_day + (pos - 1) * 7;
2812 if (
day > last_matching_day) {
2818 day = last_matching_day + (pos + 1) * 7;
2820 if (
day < first_matching_day) {
2825 if (doy_offset < 0) {
2828 (void)__icaltime_from_day_of_year(impl,
day + doy_offset,
year,
2838 int nweeks = weeks_in_year(
year);
2845 weekno += nweeks + 1;
2848 if (weekno == this_weekno) {
2858 int new_val = is_limiting
2860 ? (int)daysmask_getbit(bydays,
day + doy_offset)
2864 if (!daysmask_setbit(impl->days,
day + doy_offset, new_val) && new_val) {
2865 if (
day + doy_offset < impl->days_index) {
2866 impl->days_index =
day + doy_offset;
2871 }
while (!pos && ((
day += 7) <= last_day) && ++this_weekno);
2878static void expand_month_days(icalrecur_iterator *impl,
int year,
int month)
2880 int doy_offset, days_in_month, first_dow;
2882 daysmask_clearall(impl->days);
2886 impl->period_start = occurrence_as_icaltime(impl, 0);
2888 doy_offset = get_day_of_year(impl,
year,
month, 1) - 1;
2889 first_dow = get_day_of_week_adjusted(impl,
year,
month, 1);
2890 days_in_month = get_days_in_month(impl,
month,
year);
2899 impl->days_index = ICAL_YEARDAYS_MASK_SIZE;
2901 last_dow = get_day_of_week_adjusted(impl,
year,
month, days_in_month);
2903 expand_by_day(impl,
year, doy_offset, days_in_month,
2904 first_dow, last_dow,
2909static void __next_month(icalrecur_iterator *impl,
int inc)
2914 increment_month(impl, inc);
2915 this = occurrence_as_icaltime(impl, 0);
2916 expand_month_days(impl, this.year, this.month);
2919static int next_month(icalrecur_iterator *impl)
2921 return next_yearday(impl, &__next_month);
2924static int prev_month(icalrecur_iterator *impl)
2926 return prev_yearday(impl, &__next_month);
2929static int next_weekday_by_week(icalrecur_iterator *impl)
2931 int end_of_data = 0;
2933 if (next_hour(impl) == 0) {
2957 dow -= (int)impl->rule->week_start;
2962 int start_of_week = get_start_of_week(impl);
2964 if (dow + start_of_week < 1) {
2970 increment_year(impl, -1);
2973 set_day_of_year(impl, start_of_week + dow);
2979static bool next_week(icalrecur_iterator *impl)
2983 if (next_weekday_by_week(impl) == 0) {
2991 increment_monthday(impl, 7 * impl->rule->interval);
2996static int prev_weekday_by_week(icalrecur_iterator *impl)
2998 int end_of_data = 0;
2999 int start_of_week, dow;
3001 if (prev_hour(impl) == 0) {
3023 dow -= (int)impl->rule->week_start;
3028 start_of_week = get_start_of_week(impl);
3030 if (dow + start_of_week < 1) {
3032 increment_year(impl, -1);
3035 set_day_of_year(impl, start_of_week + dow);
3040static int prev_week(icalrecur_iterator *impl)
3044 if (prev_weekday_by_week(impl) == 0) {
3052 increment_monthday(impl, 7 * -impl->rule->interval);
3060static void expand_year_days(icalrecur_iterator *impl,
int year)
3063 short days_in_year = (short)get_days_in_year(impl,
year);
3066 daysmask_clearall(impl->days);
3070 impl->period_start = occurrence_as_icaltime(impl, 0);
3084 if (abs(doy) > days_in_year) {
3085 switch (impl->rule->skip) {
3097 doy = days_in_year + 1;
3109 }
else if (doy < 0) {
3110 doy += days_in_year + 1;
3113 daysmask_setbit(impl->days, doy, 1);
3114 if (doy < impl->days_index) {
3115 impl->days_index = doy;
3131 int nweeks = weeks_in_year(
year);
3135 (void)__icaltime_from_day_of_year(impl, 1,
year, &weekno);
3142 set_day_of_year(impl, 1);
3143 start_doy += get_start_of_week(impl) - 1;
3145 start_doy += (get_day_of_week_adjusted(impl, impl->dtstart.year,
3146 impl->dtstart.month, impl->dtstart.day) -
3147 (int)impl->rule->week_start + 7) %
3150 (void)get_days_in_year(impl,
year);
3157 weekno += nweeks + 1;
3158 }
else if (weekno > nweeks) {
3162 doy = start_doy + 7 * (weekno - 1);
3164 daysmask_setbit(impl->days, doy, 1);
3165 if (doy < impl->days_index) {
3166 impl->days_index = doy;
3175 if (
month > 0 &&
month < ICAL_BY_MONTH_SIZE) {
3185 int first_dow, last_dow;
3187 impl->days_index = ICAL_YEARDAYS_MASK_SIZE;
3194 if (
month > 0 &&
month < ICAL_BY_MONTH_SIZE) {
3195 int doy_offset, days_in_month;
3199 get_day_of_year(impl,
year,
month, 1) - 1;
3200 first_dow = get_day_of_week_adjusted(impl,
year,
month, 1);
3203 days_in_month = get_days_in_month(impl,
month,
year);
3204 last_dow = get_day_of_week_adjusted(impl,
year,
3205 month, days_in_month);
3207 expand_by_day(impl,
year, doy_offset, days_in_month,
3208 first_dow, last_dow, limiting);
3213 short doy_offset = 0, last_day;
3219 (void)__icaltime_from_day_of_year(impl, 1,
year, &weekno);
3226 set_day_of_year(impl, 1);
3227 doy_offset += get_start_of_week(impl) - 1;
3228 last_day = (7 * weeks_in_year(
year)) - doy_offset - 1;
3230 first_dow = (int)impl->rule->week_start;
3231 last_dow = (first_dow + 6) % 7;
3234 first_dow = get_day_of_week_adjusted(impl,
year, 1, 1);
3237 set_day_of_year(impl, days_in_year);
3238 last_dow = get_day_of_week(impl);
3240 last_day = days_in_year;
3243 expand_by_day(impl,
year, doy_offset, last_day, first_dow, last_dow, limiting);
3248static void __next_year(icalrecur_iterator *impl,
int inc)
3253 increment_year(impl, inc);
3254 this = occurrence_as_icaltime(impl, 0);
3255 expand_year_days(impl, this.year);
3258static int next_year(icalrecur_iterator *impl)
3260 return next_yearday(impl, &__next_year);
3263static int prev_year(icalrecur_iterator *impl)
3265 return prev_yearday(impl, &__next_year);
3268static short daymask_find_next_bit(
const unsigned long *days,
short start_index)
3270 short days_index = start_index;
3272 short startBitIndex;
3273 unsigned short wordIdx;
3275 if (days_index >= ICAL_YEARDAYS_MASK_SIZE) {
3276 return ICAL_YEARDAYS_MASK_SIZE;
3280 startBitIndex = days_index + ICAL_YEARDAYS_MASK_OFFSET;
3281 wordIdx = (
unsigned short)(startBitIndex / BITS_PER_LONG);
3283 if (startBitIndex >= 0) {
3284 v >>= startBitIndex % BITS_PER_LONG;
3286 v <<= -startBitIndex % BITS_PER_LONG;
3291 days_index += BITS_PER_LONG - startBitIndex % BITS_PER_LONG;
3294 unsigned short maxWordIdx = (
unsigned short)(LONGS_PER_BITS(ICAL_YEARDAYS_MASK_SIZE)) - 1;
3295 while (days_index < ICAL_YEARDAYS_MASK_SIZE && wordIdx < maxWordIdx) {
3303 days_index += BITS_PER_LONG;
3312 int maskSize = (int)(BITS_PER_LONG / 2);
3313 mask = (((
unsigned long)1) << maskSize) - 1;
3316 if ((v & mask) == 0) {
3318 days_index += maskSize;
3328static short daymask_find_prev_bit(
const unsigned long *days,
short start_index)
3330 short days_index = start_index;
3332 short startBitIndex;
3335 if (days_index <= -ICAL_YEARDAYS_MASK_OFFSET) {
3336 return -ICAL_YEARDAYS_MASK_OFFSET;
3340 startBitIndex = days_index + ICAL_YEARDAYS_MASK_OFFSET;
3341 wordIdx = (int)(startBitIndex / BITS_PER_LONG);
3343 v <<= BITS_PER_LONG - (startBitIndex % BITS_PER_LONG) - 1;
3347 days_index -= (startBitIndex % BITS_PER_LONG) + 1;
3350 while (days_index > -ICAL_YEARDAYS_MASK_OFFSET) {
3358 days_index -= BITS_PER_LONG;
3367 int maskSize = (int)(BITS_PER_LONG / 2);
3368 mask = ((((
unsigned long)1) << maskSize) - 1) << maskSize;
3371 if ((v & mask) == 0) {
3373 days_index -= maskSize;
3384static int next_yearday(icalrecur_iterator *impl,
3385 void (*next_period)(icalrecur_iterator *,
int))
3387 if (next_hour(impl) == 0) {
3393 reset_period_start(impl);
3396 impl->days_index = daymask_find_next_bit(impl->days, impl->days_index + 1);
3400 if (impl->days_index >= ICAL_YEARDAYS_MASK_SIZE) {
3405 next_period(impl, impl->rule->interval);
3407 if (impl->days_index < ICAL_YEARDAYS_MASK_SIZE) {
3419 if (impl->days_index < 1) {
3421 increment_year(impl, -1);
3424 set_day_of_year(impl, impl->days_index);
3429static int prev_yearday(icalrecur_iterator *impl,
3430 void (*next_period)(icalrecur_iterator *,
int))
3432 if (prev_hour(impl) == 0) {
3438 reset_period_start(impl);
3441 impl->days_index = daymask_find_prev_bit(impl->days, impl->days_index - 1);
3445 while (impl->days_index <= -ICAL_YEARDAYS_MASK_OFFSET) {
3449 next_period(impl, -impl->rule->interval);
3451 impl->days_index = ICAL_YEARDAYS_MASK_SIZE;
3452 impl->days_index = daymask_find_prev_bit(impl->days, impl->days_index - 1);
3461 if (impl->days_index < 1) {
3463 increment_year(impl, -1);
3466 set_day_of_year(impl, impl->days_index);
3471static int days_in_current_month(icalrecur_iterator *impl)
3473 return get_days_in_month(impl, impl->last.month, impl->last.year);
3476static int days_in_current_year(icalrecur_iterator *impl)
3478 return get_days_in_year(impl, impl->last.year);
3481static inline int has_contract_restriction(icalrecur_iterator *impl,
3484 return impl->bydata[byrule].by.size > 0 &&
3485 expand_map[impl->rule->freq].map[byrule] == CONTRACT;
3488static bool check_contract_restriction(icalrecur_iterator *impl,
3490 int (*get_total)(icalrecur_iterator *))
3492 if (has_contract_restriction(impl, byrule)) {
3495 for (
int itr = 0; itr < impl->bydata[byrule].by.size; itr++) {
3496 short byval = impl->bydata[byrule].by.data[itr];
3497 if ((byval < 0) && (total == 0)) {
3500 total = get_total(impl);
3509 if (v == ((byval >= 0) ? byval : (total + 1 + byval))) {
3522static bool check_contracting_rules(icalrecur_iterator *impl)
3524 struct icaltimetype last = occurrence_as_icaltime(impl, 0);
3529#define CHECK_CONTRACT_RESTRICTION(by, v, get_total) \
3530 (!has_contract_restriction(impl, (by)) || check_contract_restriction(impl, (by), (v), (get_total)))
3538 CHECK_CONTRACT_RESTRICTION(
ICAL_BY_WEEK_NO, get_week_number(impl, last), NULL) &&
3539 CHECK_CONTRACT_RESTRICTION(
3541 CHECK_CONTRACT_RESTRICTION(
3546#undef CHECK_CONTRACT_RESTRICTION
3560static void setup_setpos(icalrecur_iterator *impl,
int next)
3563 int days_index = impl->days_index;
3566 bydata_indices[byrule] = impl->bydata[byrule].index;
3570 impl->recurrence_set_size = 1;
3571 int period_change = 1;
3573 switch (impl->rule->freq) {
3579 period_change = (next ? next_second : prev_second)(impl);
3582 period_change = (next ? next_minute : prev_minute)(impl);
3585 period_change = (next ? next_hour : prev_hour)(impl);
3588 period_change = (next ? next_weekday_by_week : prev_weekday_by_week)(impl);
3593 period_change = (next ? next_yearday : prev_yearday)(impl, NULL);
3596 period_change = (next ? next_yearday : prev_yearday)(impl, NULL);
3602 if (period_change == 0 && check_contracting_rules(impl)) {
3603 impl->recurrence_set_size++;
3605 }
while (period_change == 0);
3612 impl->set_pos = impl->recurrence_set_size;
3613 impl->sp_idxp = impl->sp_pmax;
3614 impl->sp_idxn = impl->sp_pmax + 1;
3620 set_datetime(impl, last);
3622 impl->days_index = days_index;
3624 impl->bydata[byrule].index = bydata_indices[byrule];
3632static inline int setpos_cmp(
int s1,
int s2,
int next)
3635 return (next ? -1 : 1);
3636 }
else if (s2 < s1) {
3637 return (next ? 1 : -1);
3648static bool check_setpos(icalrecur_iterator *impl,
int next)
3657 if (impl->sp_pmax >= 0) {
3658 set_pos = by->data[impl->sp_idxp];
3660 while (setpos_cmp(set_pos, impl->set_pos, next) < 0) {
3661 if (next && impl->sp_idxp < impl->sp_pmax) {
3663 }
else if (!next && impl->sp_idxp > 0) {
3668 set_pos = by->data[impl->sp_idxp];
3670 if (impl->set_pos == set_pos) {
3675 if (impl->sp_pmax < by->size - 1) {
3676 set_pos = by->data[impl->sp_idxn] + impl->recurrence_set_size + 1;
3677 while (setpos_cmp(set_pos, impl->set_pos, next) < 0) {
3678 if (next && impl->sp_idxn > impl->sp_pmax + 1) {
3680 }
else if (!next && impl->sp_idxn < by->size - 1) {
3685 set_pos = by->data[impl->sp_idxn] + impl->recurrence_set_size + 1;
3687 if (impl->set_pos == set_pos) {
3698 (impl->rule->count != 0 && impl->occurrence_no >= impl->rule->count) ||
3705 if ((impl->occurrence_no == 0) &&
3707 check_setpos(impl, 1) &&
3708 check_contracting_rules(impl)) {
3709 impl->occurrence_no++;
3713 int period_change = 1;
3716 icalrecur_iterator impl_last = *impl;
3719 size_t stalledCnt = 0;
3721 int lastTimeCompare = 0;
3722 bool hasByData =
false;
3723 int checkContractingRules = 0;
3724 size_t cntRecurrences = 0;
3727 switch (impl->rule->freq) {
3734 period_change = next_minute(impl);
3738 period_change = next_hour(impl);
3742 period_change = next_day(impl);
3746 period_change = next_week(impl);
3750 period_change = next_month(impl);
3754 period_change = next_year(impl);
3762 impl->last = occurrence_as_icaltime(impl, 1);
3766 if (impl->last.year > MAX_TIME_T_YEAR ||
3773 set_datetime(impl, impl_last.last);
3778 checkContractingRules = -1;
3780 checkContractingRules = check_contracting_rules(impl) ? 1 : 0;
3781 if (checkContractingRules == 1) {
3782 if (period_change) {
3783 setup_setpos(impl, 1);
3792 if (lastTimeCompare == 0) {
3793 if (stalledCnt++ == max_recurrence_time_count) {
3799 }
while ((cntRecurrences++ < max_recurrences) &&
3800 ((lastTimeCompare == 0) ||
3801 (hasByData && !check_setpos(impl, 1)) ||
3803 (checkContractingRules == 0) ||
3804 (checkContractingRules == -1 && !check_contracting_rules(impl))));
3806 impl->occurrence_no++;
3818 int period_change = 1;
3819 icalrecur_iterator impl_last = *impl;
3823 switch (impl->rule->freq) {
3829 period_change = prev_minute(impl);
3833 period_change = prev_hour(impl);
3837 period_change = prev_day(impl);
3841 period_change = prev_week(impl);
3845 period_change = prev_month(impl);
3849 period_change = prev_year(impl);
3857 impl->last = occurrence_as_icaltime(impl, 1);
3864 set_datetime(impl, impl_last.last);
3868 if (has_by_data(impl,
ICAL_BY_SET_POS) && check_contracting_rules(impl)) {
3869 if (period_change) {
3870 setup_setpos(impl, 0);
3876 }
while (impl->last.year > MAX_TIME_T_YEAR ||
3883 !check_contracting_rules(impl));
3885 impl->occurrence_no--;
3892static void set_bydata_start(icalrecurrence_iterator_by_data *bydata,
int tfield)
3896 bdi < bydata->by.size; bdi++) {
3897 if (bydata->by.data[bdi] == tfield) {
3898 bydata->index = bdi;
3904static bool __iterator_set_start(icalrecur_iterator *impl,
icaltimetype start)
3907 short interval = impl->rule->interval;
3910 impl->istart = start;
3911 impl->occurrence_no = 0;
3912 impl->days_index = ICAL_YEARDAYS_MASK_SIZE;
3915 set_start(impl, start);
3922 if ((interval > 1) &&
3923 (diff = (impl->istart.year - impl->rstart.year) % interval)) {
3926 set_day_of_year(impl, 1);
3927 increment_year(impl, interval - diff);
3931 start = occurrence_as_icaltime(impl, 0);
3934 int start_weekno = get_week_number(impl, start);
3935 if (start_weekno > 5 &&
3940 increment_year(impl, -1);
3941 expand_year_days(impl, start.
year - 1);
3942 int days_in_year = get_days_in_year(impl, start.
year - 1);
3943 impl->days_index = daymask_find_next_bit(impl->days, days_in_year + 1);
3944 if (impl->days_index >= ICAL_YEARDAYS_MASK_SIZE) {
3945 increment_year(impl, 1);
3947 }
else if (start_weekno < 45 &&
3948 start.
month == 12) {
3952 increment_year(impl, 1);
3953 expand_year_days(impl, start.
year + 1);
3954 impl->days_index = daymask_find_next_bit(impl->days, -ICAL_YEARDAYS_MASK_OFFSET);
3955 if (impl->days_index >= ICAL_YEARDAYS_MASK_SIZE) {
3956 increment_year(impl, -1);
3963 while (start.
year < MAX_TIME_T_YEAR && impl->days_index >= ICAL_YEARDAYS_MASK_SIZE) {
3964 expand_year_days(impl, start.
year);
3977 if (impl->days_index >= ICAL_YEARDAYS_MASK_SIZE) {
3978 increment_year(impl, interval);
3979 start = occurrence_as_icaltime(impl, 0);
3984 set_day_of_year(impl, impl->days_index);
3985 if (impl->days_index < 1) {
3986 increment_year(impl, -1);
3995 if ((interval > 1) &&
3996 (diff = month_diff(impl, impl->rstart, impl->istart) % interval)) {
3999 increment_monthday(impl, -impl->istart.day + 1);
4000 __increment_month(impl, interval - diff);
4004 start = occurrence_as_icaltime(impl, 0);
4008 while (start.
year < 20000) {
4009 expand_month_days(impl, start.
year, start.
month);
4010 if (impl->days_index < ICAL_YEARDAYS_MASK_SIZE) {
4013 increment_month(impl, impl->rule->interval);
4014 start = occurrence_as_icaltime(impl, 0);
4018 set_day_of_year(impl, impl->days_index);
4026 recur_iterator_set_static_single_by_value(impl,
ICAL_BY_DAY, (
short)get_day_of_week(impl));
4028 adjust_to_byday(impl);
4032 impl->rstart = occurrence_as_icaltime(impl, 0);
4036 start = occurrence_as_icaltime(impl, 0);
4038 if ((interval > 1) &&
4039 (diff = (day_diff(impl, impl->rstart, start) + 6) / 7) % interval) {
4042 increment_monthday(impl, 7 * (interval - diff));
4048 if ((interval > 1) &&
4049 (diff = day_diff(impl, impl->rstart, impl->istart) % interval)) {
4052 increment_monthday(impl, interval - diff);
4057 if ((interval > 1) &&
4058 (diff = abs(impl->istart.hour - impl->rstart.hour) % interval)) {
4061 increment_hour(impl, interval - diff);
4063 set_bydata_start(&impl->bydata[
ICAL_BY_HOUR], impl->istart.hour);
4067 if ((interval > 1) &&
4068 (diff = abs(impl->istart.minute - impl->rstart.minute) % interval)) {
4071 increment_minute(impl, interval - diff);
4073 set_bydata_start(&impl->bydata[
ICAL_BY_MINUTE], impl->istart.minute);
4077 if ((interval > 1) &&
4078 (diff = abs(impl->istart.second - impl->rstart.second) % interval)) {
4081 increment_second(impl, interval - diff);
4083 set_bydata_start(&impl->bydata[
ICAL_BY_SECOND], impl->istart.second);
4091 impl->last = occurrence_as_icaltime(impl, 1);
4093 setup_setpos(impl, 1);
4097 if (impl->last.year > MAX_TIME_T_YEAR) {
4109 if (impl->rule->count > 0) {
4119 start = impl->dtstart;
4127 return __iterator_set_start(impl, start);
4160 from = impl->rule->until;
4167 if (!__iterator_set_start(impl, from)) {
4188 impl->days_index = 0;
4206 icalrecurrencetype_free(recur, 0);
4208 memset(recur, 0,
sizeof(*recur));
4232 pos = (abs(
day) - wd) / 8 * ((
day < 0) ? -1 : 1);
4239 short s_weekday = (short)weekday;
4240 short a_position = (short)(8 * abs(position));
4241 return (s_weekday + a_position) * ((position < 0) ? -1 : 1);
4246 return (
month & LEAP_MONTH);
4251 return (
month & ~LEAP_MONTH);
4256 return (
short)
month | (is_leap ? LEAP_MONTH : 0);
4260 icaltime_t start,
int count, icaltime_t *array)
4263 icalrecur_iterator *ritr;
4266 memset(array, 0, (
size_t)count *
sizeof(icaltime_t));
4299#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD
4300 if (pthread_mutex_lock(&invalid_rrule_mutex) != 0) {
4305 myHandling = invalidRruleHandling;
4307#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD
4308 if (pthread_mutex_unlock(&invalid_rrule_mutex) != 0) {
4318#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD
4319 if (pthread_mutex_lock(&invalid_rrule_mutex) != 0) {
4324 invalidRruleHandling = newSetting;
4326#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD
4327 if (pthread_mutex_unlock(&invalid_rrule_mutex) != 0) {
void icalarray_append(icalarray *array, const void *element)
Appends an element to an array.
icalarray * icalarray_new(size_t element_size, size_t increment_size)
void icalerror_set_errno(icalerrorenum x)
Sets the icalerrno to a given error.
void icalerror_clear_errno(void)
Resets icalerrno to ICAL_NO_ERROR.
Error handling for libical.
icalerrorenum
Represents the different types of errors that can be triggered in libical.
@ ICAL_MALFORMEDDATA_ERROR
@ ICAL_UNIMPLEMENTED_ERROR
#define icalerrno
Access the current icalerrno value.
size_t icallimit_get(icallimits_kind kind)
Defines the interface for getting/setting internal library limits.
@ ICAL_LIMIT_RECURRENCE_SEARCH
@ ICAL_LIMIT_RECURRENCE_TIME_STANDING_STILL
void icalmemory_free_buffer(void *buf)
Releases a buffer.
char * icalmemory_strdup(const char *s)
Creates a duplicate of a string.
void icalmemory_append_string(char **buf, char **pos, size_t *buf_size, const char *string)
Appends a string to a buffer.
void * icalmemory_resize_buffer(void *buf, size_t size)
Resizes a buffer created with icalmemory_new_buffer().
void * icalmemory_new_buffer(size_t size)
Creates new buffer with the specified size.
char * icalmemory_tmp_copy(const char *str)
Creates a copy of the given string, stored on the ring buffer, and returns it.
void icalmemory_append_char(char **buf, char **pos, size_t *buf_size, char ch)
Appends a character to a buffer.
void icalmemory_add_tmp_buffer(void *buf)
Adds an externally allocated buffer to the ring.
void * icalmemory_tmp_buffer(size_t size)
Creates a new temporary buffer on the ring and returns it.
Common memory management routines.
bool icalrecur_iterator_set_start(icalrecur_iterator *impl, struct icaltimetype start)
struct icalrecurrencetype * icalrecurrencetype_new_from_string(const char *str)
struct icaltimetype icalrecur_iterator_prev(icalrecur_iterator *impl)
void icalrecurrencetype_ref(struct icalrecurrencetype *recur)
icalrecurrencetype_skip icalrecur_string_to_skip(const char *str)
bool icalrecur_iterator_set_end(icalrecur_iterator *impl, struct icaltimetype end)
short icalrecurrencetype_encode_month(int month, bool is_leap)
short icalrecurrencetype_encode_day(enum icalrecurrencetype_weekday weekday, int position)
bool icalrecurrencetype_month_is_leap(short month)
const char * icalrecur_weekday_to_string(icalrecurrencetype_weekday kind)
const char * icalrecur_skip_to_string(icalrecurrencetype_skip kind)
bool icalrecur_resize_by(icalrecurrence_by_data *by, short size)
struct icalrecurrencetype * icalrecurrencetype_clone(struct icalrecurrencetype *recur)
const char * icalrecur_freq_to_string(icalrecurrencetype_frequency kind)
void ical_set_invalid_rrule_handling_setting(ical_invalid_rrule_handling newSetting)
void icalrecur_iterator_free(icalrecur_iterator *impl)
icalrecurrencetype_frequency icalrecur_string_to_freq(const char *str)
struct icalrecurrencetype * icalrecurrencetype_new(void)
bool icalrecur_iterator_set_range(icalrecur_iterator *impl, struct icaltimetype from, struct icaltimetype to)
icalarray * icalrecurrencetype_rscale_supported_calendars(void)
enum icalrecurrencetype_weekday icalrecurrencetype_day_day_of_week(short day)
ical_invalid_rrule_handling ical_get_invalid_rrule_handling_setting(void)
int icalrecurrencetype_day_position(short day)
bool icalrecur_expand_recurrence(const char *rule, icaltime_t start, int count, icaltime_t *array)
char * icalrecurrencetype_as_string(struct icalrecurrencetype *recur)
void icalrecurrencetype_unref(struct icalrecurrencetype *recur)
icalrecur_iterator * icalrecur_iterator_new(struct icalrecurrencetype *rule, struct icaltimetype dtstart)
struct icaltimetype icalrecur_iterator_next(icalrecur_iterator *impl)
icalrecurrencetype_weekday icalrecur_string_to_weekday(const char *str)
int icalrecurrencetype_month_month(short month)
char * icalrecurrencetype_as_string_r(struct icalrecurrencetype *recur)
Routines for dealing with recurring time.
icalrecurrencetype_weekday
icalrecurrencetype_frequency
@ ICAL_SECONDLY_RECURRENCE
@ ICAL_MINUTELY_RECURRENCE
@ ICAL_MONTHLY_RECURRENCE
icalrecurrencetype_byrule
@ ICAL_BYRULE_NO_CONTRACTION
ical_invalid_rrule_handling
@ ICAL_RRULE_TREAT_AS_ERROR
@ ICAL_RRULE_IGNORE_INVALID
struct icaltimetype icaltime_from_timet_with_zone(const icaltime_t tm, const bool is_date, const icaltimezone *zone)
Constructor.
struct icaltimetype icaltime_from_string(const char *str)
int icaltime_day_of_year(const struct icaltimetype t)
int icaltime_start_doy_week(const struct icaltimetype t, int fdow)
bool icaltime_is_leap_year(const int year)
int icaltime_day_of_week(const struct icaltimetype t)
struct icaltimetype icaltime_from_day_of_year(const int _doy, const int _year)
bool icaltime_is_valid_time(const struct icaltimetype t)
int icaltime_days_in_month(const int month, const int year)
struct icaltimetype icaltime_convert_to_zone(const struct icaltimetype tt, icaltimezone *zone)
int icaltime_days_in_year(const int year)
bool icaltime_is_null_time(const struct icaltimetype t)
struct icaltimetype icaltime_normalize(const struct icaltimetype tt)
icaltime_t icaltime_as_timet(const struct icaltimetype tt)
int icaltime_compare(const struct icaltimetype a_in, const struct icaltimetype b_in)
void icaltime_adjust(struct icaltimetype *tt, const int days, const int hours, const int minutes, const int seconds)
struct icaltimetype icaltime_null_time(void)
const char * icaltimezone_get_location(const icaltimezone *zone)
const char * icaltimezone_get_tzid(icaltimezone *zone)
const char * icaltimezone_tzid_prefix(void)
Timezone handling routines.
struct _icaltimezone icaltimezone
Defines the data structure representing iCalendar parameter values.
icalrecurrencetype_frequency freq
icalrecurrencetype_skip skip
icalrecurrencetype_weekday week_start
struct icaltimetype until
icalrecurrence_by_data by[ICAL_BY_NUM_PARTS]
const icaltimezone * zone