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);
550 int v = strtol(t, &t, 10);
554 if (min >= 0 || v <= -max) {
576 by->data[i++] = (short)v;
606static void sort_bydayrules(
struct icalrecur_parser *parser)
609 short *array = by->data;
611 int week_start, i, j;
615 for (i = 0; i < by->size; i++) {
616 for (j = 0; j < i; j++) {
627 short tmp = array[j];
636static int icalrecur_add_bydayrules(
struct icalrecur_parser *parser,
658 if (idx >= by->size) {
673 weekno = (
signed char)strtol(t, &t, 10);
703 sort_bydayrules(parser);
718 memset(rule, 0,
sizeof(*rule));
720 icalrecurrencetype_clear(rule);
730 icalmemory_free_buffer(p); \
737 SAFEFREE(recur->
by[i].data);
749 icalerror_check_arg_rv((recur != NULL),
"recur");
750 icalerror_check_arg_rv((recur->
refcount > 0),
"recur->refcount > 0");
757 icalerror_check_arg_rv((recur != NULL),
"recur");
758 icalerror_check_arg_rv((recur->
refcount > 0),
"recur->refcount > 0");
766 icalrecurrencetype_free(recur, 1);
769static void *icalrecur_memdup(
void *p,
size_t size,
int *error)
771 if ((p == NULL) || (size == 0)) {
777 memcpy(newp, p, size);
785static icalrecurrence_by_data icalrecur_by_dup(icalrecurrence_by_data *
by,
int *error)
787 icalrecurrence_by_data newby = {0, 0};
789 newby.data = icalrecur_memdup(
by->data, (
size_t)
by->size *
sizeof(
by->data[0]), error);
791 newby.size =
by->size;
802 icalerror_check_arg_rz((recur != NULL),
"recur");
809 memcpy(res, recur,
sizeof(*res));
821 icalrecurrence_by_data *src_by = &recur->
by[i];
822 icalrecurrence_by_data *dst_by = &res->
by[i];
823 *dst_by = icalrecur_by_dup(src_by, &error);
827 icalrecurrencetype_free(res, 1);
836 struct icalrecur_parser parser = {0};
839 icalerror_check_arg_re(str != 0,
"str", 0);
849 parser.this_clause = parser.copy;
851 if (parser.copy == 0) {
858 for (icalrecur_first_clause(&parser);
859 parser.this_clause != 0; icalrecur_next_clause(&parser)) {
863 icalrecur_clause_name_and_value(&parser, &name, &value);
866 if (strlen(parser.this_clause) > 0) {
876 }
else if (strcasecmp(name,
"FREQ") == 0) {
886 }
else if (strcasecmp(name,
"RSCALE") == 0) {
887 if (parser.rt->
rscale != NULL) {
893 }
else if (strcasecmp(name,
"SKIP") == 0) {
903 }
else if (strcasecmp(name,
"COUNT") == 0) {
908 parser.rt->
count = atoi(value);
910 if (parser.rt->
count < 1) {
914 }
else if (strcasecmp(name,
"UNTIL") == 0) {
924 }
else if (strcasecmp(name,
"INTERVAL") == 0) {
929 parser.rt->
interval = (short)atoi(value);
936 }
else if (strcasecmp(name,
"WKST") == 0) {
945 sort_bydayrules(&parser);
948 }
else if (strncasecmp(name,
"BY", 2) == 0) {
952 if (strcasecmp(name + 2, recur_map[byrule].str + 2) == 0) {
954 r = icalrecur_add_bydayrules(&parser, value);
956 icalrecurrence_by_data *by = &parser.rt->
by[byrule];
957 r = icalrecur_add_byrules(&parser, by,
958 recur_map[byrule].min,
959 recur_map[byrule].size,
976 icalrecurrencetype_clear(parser.rt);
982 icalrecurrence_by_data *by = &parser.rt->
by[byrule];
985 expand_map[parser.rt->
freq].map[byrule] == ILLEGAL) {
991 icalrecurrencetype_clear(parser.rt);
994 icalrecur_free_by(by);
1022 size_t buf_sz = 200;
1023 char temp[20] = {0};
1033 if (recur->
rscale != 0) {
1052 snprintf(temp,
sizeof(temp),
"%d", recur->
interval);
1067 const icalrecurrence_by_data *by = &recur->
by[j];
1075 int limit = recur_map[j].size - 1;
1076 for (i = 0; i < limit && i < by->size; i++) {
1085 snprintf(temp,
sizeof(temp),
"%d%s", pos, daystr);
1091 snprintf(temp,
sizeof(temp),
"%dL",
1095 snprintf(temp,
sizeof(temp),
"%d", by->data[i]);
1099 if ((i + 1) < limit && by->size > i + 1) {
1109 print_date_to_string(temp, &(recur->
until));
1111 print_datetime_to_string(temp, &(recur->
until));
1118 else if (recur->
count != 0) {
1119 snprintf(temp,
sizeof(temp),
"%d", recur->
count);
1131#define BITS_PER_LONG ((unsigned short)(8 * sizeof(unsigned long)))
1134#define LONGS_PER_BITS(n) (((n) + BITS_PER_LONG - 1) / BITS_PER_LONG)
1136#define ICAL_YEARDAYS_MASK_SIZE (ICAL_BY_YEARDAY_SIZE + 7)
1137#define ICAL_YEARDAYS_MASK_OFFSET 4
1140typedef struct icalrecurrence_iterator_by_data {
1141 icalrecurrence_by_data by;
1148} icalrecurrence_iterator_by_data;
1150struct icalrecur_iterator_impl {
1151 struct icaltimetype dtstart;
1152 struct icalrecurrencetype *rule;
1154 struct icaltimetype rstart;
1155 struct icaltimetype istart;
1156 struct icaltimetype iend;
1157 struct icaltimetype last;
1158 int32_t occurrence_no;
1161 int32_t recurrence_set_size;
1162 short sp_idxp, sp_idxn;
1165#if defined(HAVE_LIBICU)
1170 struct icaltimetype period_start;
1184 unsigned long days[LONGS_PER_BITS(ICAL_YEARDAYS_MASK_SIZE)];
1191static void daysmask_clearall(
unsigned long mask[])
1194 sizeof(
unsigned long) * LONGS_PER_BITS(ICAL_YEARDAYS_MASK_SIZE));
1197static void daysmask_set_range(
unsigned long days[],
int fromDayIncl,
int untilDayExcl,
int v)
1199 int fromBitIdx = fromDayIncl + ICAL_YEARDAYS_MASK_OFFSET;
1200 int untilBitIdx = untilDayExcl + ICAL_YEARDAYS_MASK_OFFSET;
1202 for (
int word_idx = fromBitIdx / BITS_PER_LONG;
1203 word_idx < (int)((untilBitIdx + BITS_PER_LONG - 1) / BITS_PER_LONG);
1205 int lowerBitIdxIncl = (fromBitIdx <= (int)(word_idx * BITS_PER_LONG))
1207 : (fromBitIdx - (int)(word_idx * BITS_PER_LONG));
1208 int upperBitIdxExcl = (untilBitIdx >= (int)((word_idx + 1) * BITS_PER_LONG))
1209 ? (int)BITS_PER_LONG
1210 : (int)(untilBitIdx - (int)(word_idx * BITS_PER_LONG));
1212 unsigned long mask = (
unsigned long)-1;
1213 if (lowerBitIdxIncl > 0) {
1214 mask &= ((
unsigned long)-1) << lowerBitIdxIncl;
1216 if (upperBitIdxExcl < (
int)BITS_PER_LONG) {
1217 mask &= ((
unsigned long)-1) >> (BITS_PER_LONG - upperBitIdxExcl);
1221 days[word_idx] |= mask;
1223 days[word_idx] &= ~mask;
1228static int daysmask_setbit(
unsigned long mask[],
short n,
int v)
1232 n += ICAL_YEARDAYS_MASK_OFFSET;
1235 prev = (mask[n / BITS_PER_LONG] & (1UL << (n % BITS_PER_LONG))) ? 1 : 0;
1237 prev = (mask[n / BITS_PER_LONG] & (1UL >> (-n % BITS_PER_LONG))) ? 1 : 0;
1242 mask[n / BITS_PER_LONG] |= (1UL << (n % BITS_PER_LONG));
1244 mask[n / BITS_PER_LONG] |= (1UL >> (-n % BITS_PER_LONG));
1248 mask[n / BITS_PER_LONG] &= ~(1UL << (n % BITS_PER_LONG));
1250 mask[n / BITS_PER_LONG] &= ~(1UL >> (-n % BITS_PER_LONG));
1258static unsigned long daysmask_getbit(
const unsigned long mask[],
short n)
1260 n += ICAL_YEARDAYS_MASK_OFFSET;
1261 return (mask[n / BITS_PER_LONG] >> (n % BITS_PER_LONG)) & 1;
1266 return (impl->bydata[byrule].orig_data == 1);
1269static void recur_iterator_set_static_single_by_value(icalrecur_iterator *impl,
1272 icalrecurrence_iterator_by_data *by = &impl->bydata[byrule];
1274 by->by.data = &by->buffer_value;
1275 by->by.data[0] = value;
1278static void setup_defaults(icalrecur_iterator *impl,
1283 if (impl->dtstart.is_date && recur_map[byrule].isTime) {
1289 recur_iterator_set_static_single_by_value(impl, byrule, 0);
1290 }
else if (expand_map[freq].map[byrule] == EXPAND) {
1293 if (impl->bydata[byrule].by.size == 0) {
1294 recur_iterator_set_static_single_by_value(impl, byrule, (
short)deftime);
1301static int weeks_in_year(
int year)
1307 return (52 + is_long);
1316static void __get_start_time(icalrecur_iterator *impl,
icaltimetype date,
1326 *
hour = impl->rstart.hour;
1334 *
minute = impl->rstart.minute;
1342 *
second = impl->rstart.second;
1348#if defined(HAVE_LIBICU)
1366 UErrorCode status = U_ZERO_ERROR;
1368 icalarray *calendars;
1373 en = ucal_getKeywordValuesForLocale(
"calendar",
"",
false, &status);
1374 while ((cal = uenum_next(en, NULL, &status))) {
1383static void set_second(icalrecur_iterator *impl,
int second)
1385 ucal_set(impl->rscale, UCAL_SECOND, (int32_t)
second);
1388static void set_minute(icalrecur_iterator *impl,
int minute)
1390 ucal_set(impl->rscale, UCAL_MINUTE, (int32_t)
minute);
1393static void set_hour(icalrecur_iterator *impl,
int hour)
1395 ucal_set(impl->rscale, UCAL_HOUR_OF_DAY, (int32_t)
hour);
1398static void __set_month(icalrecur_iterator *impl,
int month)
1404 ucal_set(impl->rscale, UCAL_MONTH, (int32_t)
month);
1405 if (is_leap_month) {
1406 ucal_set(impl->rscale, UCAL_IS_LEAP_MONTH, 1);
1410static int set_month(icalrecur_iterator *impl,
int month)
1412 UErrorCode status = U_ZERO_ERROR;
1415 __set_month(impl,
month);
1417 ucal_set(impl->rscale, UCAL_DAY_OF_MONTH, (int32_t)1);
1420 (int)ucal_get(impl->rscale, UCAL_MONTH, &status);
1422 if (ucal_get(impl->rscale, UCAL_IS_LEAP_MONTH, &status)) {
1423 actual_month |= LEAP_MONTH;
1426 if (actual_month !=
month) {
1427 switch (impl->rule->skip) {
1437 ucal_add(impl->rscale, UCAL_MONTH, (int32_t)-1, &status);
1447 (
int)ucal_get(impl->rscale, UCAL_MONTH, &status));
1450static int get_months_in_year(icalrecur_iterator *impl,
int year)
1452 UErrorCode status = U_ZERO_ERROR;
1455 ucal_set(impl->rscale, UCAL_YEAR, (int32_t)
year);
1459 (
int)ucal_getLimit(impl->rscale, UCAL_MONTH,
1460 UCAL_ACTUAL_MAXIMUM, &status));
1463static int get_days_in_year(icalrecur_iterator *impl,
int year)
1465 UErrorCode status = U_ZERO_ERROR;
1468 ucal_set(impl->rscale, UCAL_YEAR, (int32_t)
year);
1471 return (
int)ucal_getLimit(impl->rscale, UCAL_DAY_OF_YEAR,
1472 UCAL_ACTUAL_MAXIMUM, &status);
1475static void set_day_of_year(icalrecur_iterator *impl,
int doy)
1478 doy += get_days_in_year(impl, 0);
1481 ucal_set(impl->rscale, UCAL_DAY_OF_YEAR, (int32_t)doy);
1484static int get_start_of_week(icalrecur_iterator *impl)
1486 UErrorCode status = U_ZERO_ERROR;
1489 doy = (int)ucal_get(impl->rscale, UCAL_DAY_OF_YEAR, &status);
1490 dow = (int)ucal_get(impl->rscale, UCAL_DAY_OF_WEEK, &status);
1491 dow -= (int)impl->rule->week_start;
1499static int get_day_of_week(icalrecur_iterator *impl)
1501 UErrorCode status = U_ZERO_ERROR;
1503 return (
int)ucal_get(impl->rscale, UCAL_DAY_OF_WEEK, &status);
1506static int get_week_number(icalrecur_iterator *impl,
struct icaltimetype tt)
1508 UErrorCode status = U_ZERO_ERROR;
1513 last_millis = ucal_getMillis(impl->rscale, &status);
1516 ucal_setDate(impl->rscale,
1517 (int32_t)tt.
year, (int32_t)
month, (int32_t)tt.
day, &status);
1519 ucal_set(impl->rscale, UCAL_IS_LEAP_MONTH, 1);
1522 weekno = (int)ucal_get(impl->rscale, UCAL_WEEK_OF_YEAR, &status);
1525 ucal_setMillis(impl->rscale, last_millis, &status);
1530static int get_days_in_month(icalrecur_iterator *impl,
int month,
int year)
1532 UErrorCode status = U_ZERO_ERROR;
1534 ucal_set(impl->rscale, UCAL_YEAR, (int32_t)
year);
1537 month = impl->rstart.month;
1539 __set_month(impl,
month);
1541 return (
int)ucal_getLimit(impl->rscale,
1542 UCAL_DAY_OF_MONTH, UCAL_ACTUAL_MAXIMUM, &status);
1545static void prepare_rscale_adjusted(icalrecur_iterator *impl,
1548 ucal_set(impl->rscale, UCAL_YEAR, (int32_t)
year);
1551 month = impl->rstart.month;
1553 __set_month(impl,
month);
1556 day = impl->rstart.day;
1557 }
else if (
day < 0) {
1558 day += 1 + (int)ucal_getLimit(impl->rscale, UCAL_DAY_OF_MONTH,
1559 UCAL_ACTUAL_MAXIMUM, status);
1561 ucal_set(impl->rscale, UCAL_DAY_OF_MONTH, (int32_t)
day);
1564static int get_day_of_year(icalrecur_iterator *impl,
1567 UErrorCode status = U_ZERO_ERROR;
1568 prepare_rscale_adjusted(impl,
year,
month,
day, &status);
1569 return (
int)ucal_get(impl->rscale, UCAL_DAY_OF_YEAR, &status);
1572static int get_day_of_week_adjusted(icalrecur_iterator *impl,
1575 UErrorCode status = U_ZERO_ERROR;
1576 prepare_rscale_adjusted(impl,
year,
month,
day, &status);
1577 return (
int)ucal_get(impl->rscale, UCAL_DAY_OF_WEEK, &status);
1580static struct icaltimetype occurrence_as_icaltime(icalrecur_iterator *impl,
1584 UErrorCode status = U_ZERO_ERROR;
1585 UCalendar *cal = impl->rscale;
1586 int is_leap_month = 0;
1588 if (normalize && (impl->rscale != impl->greg)) {
1590 UDate millis = ucal_getMillis(impl->rscale, &status);
1592 ucal_setMillis(impl->greg, millis, &status);
1596 (int)ucal_get(impl->rscale, UCAL_IS_LEAP_MONTH, &status);
1599 tt.
year = (int)ucal_get(cal, UCAL_YEAR, &status);
1600 tt.
day = (int)ucal_get(cal, UCAL_DATE, &status);
1602 (int)ucal_get(cal, UCAL_MONTH, &status);
1603 if (is_leap_month) {
1604 tt.
month |= LEAP_MONTH;
1608 tt.
hour = (int)ucal_get(cal, UCAL_HOUR_OF_DAY, &status);
1609 tt.
minute = (int)ucal_get(cal, UCAL_MINUTE, &status);
1610 tt.
second = (int)ucal_get(cal, UCAL_SECOND, &status);
1616static struct icaltimetype __icaltime_from_day_of_year(icalrecur_iterator *impl,
1617 int day,
int year,
int *weekno)
1619 ucal_set(impl->rscale, UCAL_YEAR, (int32_t)
year);
1621 day += get_days_in_year(impl, 0) + 1;
1624 ucal_set(impl->rscale, UCAL_DAY_OF_YEAR, (int32_t)
day);
1627 UErrorCode status = U_ZERO_ERROR;
1629 *weekno = (int)ucal_get(impl->rscale, UCAL_WEEK_OF_YEAR, &status);
1632 return occurrence_as_icaltime(impl, 0);
1635static void increment_year(icalrecur_iterator *impl,
int inc)
1637 UErrorCode status = U_ZERO_ERROR;
1639 ucal_add(impl->rscale, UCAL_YEAR, (int32_t)inc, &status);
1642static void __increment_month(icalrecur_iterator *impl,
int inc)
1644 UErrorCode status = U_ZERO_ERROR;
1646 ucal_add(impl->rscale, UCAL_MONTH, (int32_t)inc, &status);
1649static void increment_monthday(icalrecur_iterator *impl,
int inc)
1651 UErrorCode status = U_ZERO_ERROR;
1653 ucal_add(impl->rscale, UCAL_DAY_OF_MONTH, (int32_t)inc, &status);
1656static void increment_hour(icalrecur_iterator *impl,
int inc)
1658 UErrorCode status = U_ZERO_ERROR;
1660 ucal_add(impl->rscale, UCAL_HOUR_OF_DAY, (int32_t)inc, &status);
1663static void increment_minute(icalrecur_iterator *impl,
int inc)
1665 UErrorCode status = U_ZERO_ERROR;
1667 ucal_add(impl->rscale, UCAL_MINUTE, (int32_t)inc, &status);
1670static void increment_second(icalrecur_iterator *impl,
int inc)
1672 UErrorCode status = U_ZERO_ERROR;
1674 ucal_add(impl->rscale, UCAL_SECOND, (int32_t)inc, &status);
1677static bool validate_byrule(icalrecur_iterator *impl,
1679 short (*decode_val)(
short *,
bool),
1682 if (has_by_data(impl, byrule)) {
1683 UErrorCode status = U_ZERO_ERROR;
1684 const icalrecurrence_by_data *by_ptr = &impl->bydata[byrule].by;
1686 (short)ucal_getLimit(impl->rscale, field, UCAL_MAXIMUM, &status);
1689 for (idx = 0; idx < by_ptr->size; idx++) {
1690 short val = decode_val ? decode_val(&by_ptr->data[idx], decode_flags) : by_ptr->data[idx];
1692 if (abs(val) > max) {
1701static short decode_month(
short *
month,
bool is_hebrew)
1703 if (is_hebrew && *
month > 5) {
1714static short decode_day(
short *
day,
bool flags)
1721static bool initialize_rscale(icalrecur_iterator *impl)
1725 char locale[ULOC_KEYWORD_AND_VALUES_CAPACITY] = {0};
1726 UErrorCode status = U_ZERO_ERROR;
1727 UChar *tzid = (UChar *)UCAL_UNKNOWN_ZONE_ID;
1728 bool is_hebrew =
false;
1736 if (src && !strncmp(src, prefix, strlen(prefix))) {
1738 src += strlen(prefix);
1742 size_t len = (strlen(src) + 1) * U_SIZEOF_UCHAR;
1744 tzid = u_strFromUTF8Lenient(tzid, (int32_t)len, NULL, src, -1, &status);
1745 if (U_FAILURE(status)) {
1752 (void)uloc_setKeywordValue(
"calendar",
"gregorian",
1753 locale,
sizeof(locale), &status);
1756 impl->greg = ucal_open(tzid, -1, locale, UCAL_DEFAULT, &status);
1758 ucal_setDateTime(impl->greg,
1759 (int32_t)dtstart.
year,
1760 (int32_t)(dtstart.
month - 1),
1761 (int32_t)dtstart.
day,
1762 (int32_t)dtstart.
hour,
1764 (int32_t)dtstart.
second, &status);
1766 if (!impl->greg || U_FAILURE(status)) {
1773 impl->rscale = impl->greg;
1780 for (r = rule->
rscale; *r; r++) {
1781 *r = tolower((
int)*r);
1785 en = ucal_getKeywordValuesForLocale(
"calendar",
"",
false, &status);
1786 while ((cal = uenum_next(en, NULL, &status))) {
1787 if (!strcmp(cal, rule->
rscale)) {
1788 is_hebrew = !strcmp(rule->
rscale,
"hebrew");
1799 (void)uloc_setKeywordValue(
"calendar", rule->
rscale,
1800 locale,
sizeof(locale), &status);
1803 impl->rscale = ucal_open(tzid, -1, locale, UCAL_DEFAULT, &status);
1805 UDate millis = ucal_getMillis(impl->greg, &status);
1807 ucal_setMillis(impl->rscale, millis, &status);
1809 if (!impl->rscale || U_FAILURE(status)) {
1817 &decode_month, is_hebrew) ||
1818 !validate_byrule(impl,
ICAL_BY_DAY, UCAL_WEEK_OF_YEAR, &decode_day, 0) ||
1821 !validate_byrule(impl,
ICAL_BY_WEEK_NO, UCAL_WEEK_OF_YEAR, NULL, 0) ||
1828 ucal_setAttribute(impl->rscale, UCAL_MINIMAL_DAYS_IN_FIRST_WEEK, 4);
1829 ucal_setAttribute(impl->rscale, UCAL_FIRST_DAY_OF_WEEK, (int32_t)rule->
week_start);
1832 impl->rstart = occurrence_as_icaltime(impl, 0);
1838static void set_start(icalrecur_iterator *impl,
icaltimetype date)
1840 UErrorCode status = U_ZERO_ERROR;
1842 impl->last.is_date = impl->rstart.is_date;
1843 impl->last.zone = impl->rstart.zone;
1845 if (impl->rstart.is_date) {
1846 ucal_setDate(impl->greg,
1848 (int32_t)(date.
month - 1),
1849 (int32_t)date.
day, &status);
1855 ucal_setDateTime(impl->greg,
1857 (int32_t)(date.
month - 1),
1865 if (impl->rscale != impl->greg) {
1866 UDate millis = ucal_getMillis(impl->greg, &status);
1867 ucal_setMillis(impl->rscale, millis, &status);
1871static void set_datetime(icalrecur_iterator *impl,
icaltimetype date)
1873 UErrorCode status = U_ZERO_ERROR;
1875 impl->last.is_date = impl->rstart.is_date;
1876 impl->last.zone = impl->rstart.zone;
1878 if (impl->rstart.is_date) {
1879 ucal_setDate(impl->greg,
1881 (int32_t)(date.
month - 1),
1882 (int32_t)date.
day, &status);
1884 ucal_setDateTime(impl->greg,
1886 (int32_t)(date.
month - 1),
1894 if (impl->rscale != impl->greg) {
1895 UDate millis = ucal_getMillis(impl->greg, &status);
1896 ucal_setMillis(impl->rscale, millis, &status);
1905 if (impl->rscale == impl->greg) {
1907 diff = __greg_month_diff(a, b);
1912 UErrorCode status = U_ZERO_ERROR;
1917 millis = ucal_getMillis(impl->rscale, &status);
1919 set_day_of_year(impl, 1);
1920 diff = get_months_in_year(impl,
year) - a.
month;
1922 diff += get_months_in_year(impl,
year);
1927 ucal_setMillis(impl->rscale, millis, &status);
1936 UErrorCode status = U_ZERO_ERROR;
1941 millis = ucal_getMillis(impl->rscale, &status);
1943 set_day_of_year(impl, 1);
1945 diff = __day_diff(impl, a, b);
1948 ucal_setMillis(impl->rscale, millis, &status);
1953static void reset_period_start(icalrecur_iterator *impl)
1957 (void)get_day_of_year(impl, start.
year, start.
month, start.
day);
1968 icalarray *calendars =
icalarray_new(
sizeof(
const char **), 1);
1969 const char *cal =
"GREGORIAN";
1976static void set_second(icalrecur_iterator *impl,
int second)
1978 impl->last.second =
second;
1981static void set_minute(icalrecur_iterator *impl,
int minute)
1983 impl->last.minute =
minute;
1986static void set_hour(icalrecur_iterator *impl,
int hour)
1988 impl->last.hour =
hour;
1991static int set_month(icalrecur_iterator *impl,
int month)
1993 return (impl->last.month =
month);
1997#define get_months_in_year(impl, year) (12)
2000static int get_days_in_year(icalrecur_iterator *impl,
int year)
2007static void set_day_of_year(icalrecur_iterator *impl,
int doy)
2012 doy += get_days_in_year(impl, impl->last.year);
2017 impl->last.day = next.day;
2018 impl->last.month = next.month;
2019 impl->last.year = next.year;
2022static int get_start_of_week(
const icalrecur_iterator *impl)
2027static int get_day_of_week(
const icalrecur_iterator *impl)
2034static int get_week_number(icalrecur_iterator *impl,
struct icaltimetype tt)
2049 week = weeks_in_year(tt.
year - 1);
2050 }
else if (week > weeks_in_year(tt.
year)) {
2058static int get_days_in_month(icalrecur_iterator *impl,
int month,
int year)
2065static struct icaltimetype get_dtstart_adjusted(icalrecur_iterator *impl,
2080 }
else if (
day < 0) {
2088static int get_day_of_year(icalrecur_iterator *impl,
2094static int get_day_of_week_adjusted(icalrecur_iterator *impl,
2100static struct icaltimetype occurrence_as_icaltime(icalrecur_iterator *impl,
2106static struct icaltimetype __icaltime_from_day_of_year(icalrecur_iterator *impl,
2107 int day,
int year,
int *weekno)
2112 day += get_days_in_year(impl,
year) + 1;
2118 *weekno = get_week_number(impl, tt);
2123static void increment_year(icalrecur_iterator *impl,
int inc)
2125 impl->last.
year += inc;
2128static void __increment_month(icalrecur_iterator *impl,
int inc)
2132 impl->last.month += inc;
2137 years = impl->last.month / 12;
2139 impl->last.month = impl->last.month % 12;
2141 if (impl->last.month < 0) {
2142 impl->last.month = impl->last.month + 12;
2149 increment_year(impl, years);
2153static void increment_monthday(icalrecur_iterator *impl,
int inc)
2158static void increment_hour(icalrecur_iterator *impl,
int inc)
2163static void increment_minute(icalrecur_iterator *impl,
int inc)
2168static void increment_second(icalrecur_iterator *impl,
int inc)
2173static bool initialize_rscale(icalrecur_iterator *impl)
2175 if (impl->rule->rscale && strcasecmp(impl->rule->rscale,
"GREGORIAN")) {
2180 impl->rstart = impl->dtstart;
2186static void set_start(icalrecur_iterator *impl,
icaltimetype date)
2188 impl->last.year = date.
year;
2189 impl->last.month = date.
month;
2190 impl->last.day = date.
day;
2191 impl->last.is_date = impl->dtstart.is_date;
2192 impl->last.zone = impl->dtstart.zone;
2194 if (!impl->dtstart.is_date) {
2195 __get_start_time(impl, date, &impl->last.hour,
2196 &impl->last.minute, &impl->last.second);
2200static void set_datetime(icalrecur_iterator *impl,
icaltimetype date)
2210 return __greg_month_diff(a, b);
2216 return __day_diff(impl, a, b);
2219static void reset_period_start(icalrecur_iterator *impl)
2222 impl->last.year = impl->period_start.year;
2223 impl->last.month = impl->period_start.month;
2224 impl->last.day = impl->period_start.day;
2229static int get_second(icalrecur_iterator *impl)
2231 return occurrence_as_icaltime(impl, 1).second;
2234static int get_minute(icalrecur_iterator *impl)
2236 return occurrence_as_icaltime(impl, 1).minute;
2239static int get_hour(icalrecur_iterator *impl)
2241 return occurrence_as_icaltime(impl, 1).hour;
2244static bool __iterator_set_start(icalrecur_iterator *impl,
icaltimetype start);
2245static void increment_month(icalrecur_iterator *impl,
int inc);
2246static void expand_month_days(icalrecur_iterator *impl,
int year,
int month);
2247static void expand_year_days(icalrecur_iterator *impl,
int year);
2248static int next_yearday(icalrecur_iterator *impl,
2249 void (*next_period)(icalrecur_iterator *,
int));
2250static int prev_yearday(icalrecur_iterator *impl,
2251 void (*next_period)(icalrecur_iterator *,
int));
2253static void adjust_to_byday(icalrecur_iterator *impl)
2266 short this_dow = (short)get_day_of_week(impl);
2267 short dow = (short)(impl->bydata[
ICAL_BY_DAY].by.data[0] - this_dow);
2270 if (dow != 0 && this_dow < (
short)impl->rule->week_start) {
2274 if ((this_dow < impl->bydata[
ICAL_BY_DAY].by.data[0] && dow >= 0) || dow < 0) {
2276 increment_monthday(impl, dow);
2288 icalrecur_iterator *impl;
2300#define IN_RANGE(val, min, max) ((val) >= (min) && (val) <= (max))
2304 !IN_RANGE(dtstart.
year, 0, MAX_TIME_T_YEAR) ||
2305 !IN_RANGE(dtstart.
month, 1, 12) ||
2306 !IN_RANGE(dtstart.
day, 1,
2308 (!dtstart.
is_date && (!IN_RANGE(dtstart.
hour, 0, 23) ||
2309 !IN_RANGE(dtstart.
minute, 0, 59) ||
2310 !IN_RANGE(dtstart.
second, 0, 59)))) {
2320 memset(impl, 0,
sizeof(icalrecur_iterator));
2322 impl->dtstart = dtstart;
2324#if defined(HAVE_LIBICU)
2350 impl->bydata[byrule].by = impl->rule->by[byrule];
2357 impl->bydata[byrule].orig_data =
2358 (short)(impl->rule->by[byrule].size > 0);
2361 if (expand_map[freq].map[byrule] == ILLEGAL &&
2362 has_by_data(impl, byrule)) {
2366 impl->bydata[byrule].orig_data = 0;
2375 if (initialize_rscale(impl) == 0) {
2400 if (!__iterator_set_start(impl, dtstart)) {
2410 icalerror_check_arg_rv((impl != 0),
"impl");
2412#if defined(HAVE_LIBICU)
2414 if (impl->rscale && (impl->rscale != impl->greg)) {
2415 ucal_close(impl->rscale);
2418 ucal_close(impl->greg);
2450 diff = get_days_in_year(impl,
year) -
2453 diff += get_days_in_year(impl,
year);
2469static void increment_month(icalrecur_iterator *impl,
int inc)
2471 __increment_month(impl, inc);
2474 struct icaltimetype this = occurrence_as_icaltime(impl, 0);
2476 while (this.year < MAX_TIME_T_YEAR) {
2477 icalrecurrence_iterator_by_data *bydata = &impl->bydata[
ICAL_BY_MONTH];
2478 for (bydata->index = 0;
2479 bydata->index < bydata->by.size; bydata->index++) {
2480 if (this.month == bydata->by.data[bydata->index]) {
2485 __increment_month(impl, inc);
2486 this = occurrence_as_icaltime(impl, 0);
2491static int next_unit(icalrecur_iterator *impl,
2493 int (*next_sub_unit)(icalrecur_iterator *),
2494 void (*set_unit)(icalrecur_iterator *,
int),
2495 int (*get_unit)(icalrecur_iterator *),
2497 void (*increment_unit)(icalrecur_iterator *,
int))
2500 (impl->bydata[by_unit].by.size > 0);
2501 int this_frequency = (impl->rule->freq == frequency);
2503 int end_of_data = 0;
2505 icalassert(has_by_unit || this_frequency);
2507 if (next_sub_unit && next_sub_unit(impl) == 0) {
2514 icalrecurrence_iterator_by_data *bydata = &impl->bydata[by_unit];
2515 if (this_frequency) {
2518 size_t stalledCnt = 0;
2519 while ((impl->last.year < MAX_TIME_T_YEAR) && (stalledCnt++ < max_recurrence_time_count)) {
2520 int last_unit = get_unit(impl);
2522 while (bydata->index < bydata->by.size) {
2523 int cur_by = bydata->by.data[bydata->index];
2524 if ((cur_by >= last_unit) &&
2525 ((cur_by - last_unit) % impl->rule->interval) == 0) {
2526 set_unit(impl, cur_by);
2535 if (last_unit + impl->rule->interval < period_len) {
2536 int diff = period_len - last_unit;
2537 multiplier = (diff / impl->rule->interval + (diff % impl->rule->interval > 0));
2539 increment_unit(impl, multiplier * impl->rule->interval);
2544 if (bydata->by.size <= bydata->index) {
2550 if (bydata->index < bydata->by.size) {
2551 set_unit(impl, bydata->by.data[bydata->index]);
2558 increment_unit(impl, impl->rule->interval);
2565static int next_second(icalrecur_iterator *impl)
2568 &set_second, &get_second, 60, &increment_second);
2571static int next_minute(icalrecur_iterator *impl)
2574 &set_minute, &get_minute, 60, &increment_minute);
2577static int next_hour(icalrecur_iterator *impl)
2580 &set_hour, &get_hour, 24, &increment_hour);
2583static int next_day(icalrecur_iterator *impl)
2586 NULL, NULL, 0, &increment_monthday);
2589static int prev_unit(icalrecur_iterator *impl,
2591 int (*prev_sub_unit)(icalrecur_iterator *),
2592 void (*set_unit)(icalrecur_iterator *,
int),
2593 int (*get_unit)(icalrecur_iterator *),
2594 void (*increment_unit)(icalrecur_iterator *,
int))
2597 (impl->bydata[by_unit].by.size > 0);
2598 int this_frequency = (impl->rule->freq == frequency);
2600 int end_of_data = 0;
2602 icalassert(has_by_unit || this_frequency);
2604 if (prev_sub_unit && prev_sub_unit(impl) == 0) {
2609 icalrecurrence_iterator_by_data *bydata = &impl->bydata[by_unit];
2610 if (this_frequency) {
2613 while (impl->last.year > 0) {
2614 int last_unit = get_unit(impl);
2615 while (bydata->index >= 0) {
2616 int cur_by = bydata->by.data[bydata->index];
2617 if ((cur_by <= last_unit) && (impl->rule->interval > 0) &&
2618 ((last_unit - cur_by) % impl->rule->interval) == 0) {
2619 set_unit(impl, cur_by);
2624 bydata->index = bydata->by.size - 1;
2626 if (last_unit - impl->rule->interval > 0) {
2627 multiplier = (last_unit / impl->rule->interval + (last_unit % impl->rule->interval > 0));
2629 increment_unit(impl, -multiplier * impl->rule->interval);
2634 if (bydata->index < 0) {
2636 bydata->by.size - 1;
2641 set_unit(impl, bydata->by.data[bydata->index]);
2646 increment_unit(impl, -impl->rule->interval);
2653static int prev_second(icalrecur_iterator *impl)
2656 &set_second, &get_second, &increment_second);
2659static int prev_minute(icalrecur_iterator *impl)
2662 &set_minute, &get_minute, &increment_minute);
2665static int prev_hour(icalrecur_iterator *impl)
2668 &set_hour, &get_hour, &increment_hour);
2671static int prev_day(icalrecur_iterator *impl)
2674 NULL, NULL, &increment_monthday);
2678static void expand_bymonth_days(icalrecur_iterator *impl,
int year,
int month)
2681 int days_in_month = get_days_in_month(impl,
month,
year);
2684 short doy = ICAL_BY_YEARDAY_SIZE, mday = impl->bydata[
ICAL_BY_MONTH_DAY].by.data[i];
2685 int this_month =
month;
2687 if (abs(mday) > days_in_month) {
2688 int days_in_year = get_days_in_year(impl,
year);
2690 switch (impl->rule->skip) {
2702 if (this_month > get_months_in_year(impl,
year)) {
2703 doy = days_in_year + 1;
2714 if (this_month == 0) {
2723 if (doy == ICAL_BY_YEARDAY_SIZE) {
2724 doy = get_day_of_year(impl,
year, this_month, mday);
2727 daysmask_setbit(impl->days, doy, 1);
2728 if (doy < impl->days_index) {
2729 impl->days_index = doy;
2735static void expand_by_day(icalrecur_iterator *impl,
int year,
2736 int doy_offset,
int last_day,
2737 int first_dow,
int last_dow,
2741 unsigned long bydays[LONGS_PER_BITS(ICAL_YEARDAYS_MASK_SIZE)];
2744 memcpy(bydays, impl->days,
sizeof(bydays));
2746 daysmask_set_range(impl->days, doy_offset + 1, doy_offset + last_day + 1, 0);
2748 for (i = 0; i < impl->bydata[
ICAL_BY_DAY].by.size; i++) {
2752 int first_matching_day, last_matching_day;
2753 int day, this_weekno;
2757 first_matching_day = ((dow + 7 - first_dow) % 7) + 1;
2758 last_matching_day = last_day - ((last_dow + 7 - dow) % 7);
2763 day = first_matching_day;
2765 }
else if (pos > 0) {
2767 day = first_matching_day + (pos - 1) * 7;
2769 if (
day > last_matching_day) {
2775 day = last_matching_day + (pos + 1) * 7;
2777 if (
day < first_matching_day) {
2782 if (doy_offset < 0) {
2785 (void)__icaltime_from_day_of_year(impl,
day + doy_offset,
year,
2795 int nweeks = weeks_in_year(
year);
2802 weekno += nweeks + 1;
2805 if (weekno == this_weekno) {
2815 int new_val = is_limiting
2817 ? (int)daysmask_getbit(bydays,
day + doy_offset)
2821 if (!daysmask_setbit(impl->days,
day + doy_offset, new_val) && new_val) {
2822 if (
day + doy_offset < impl->days_index) {
2823 impl->days_index =
day + doy_offset;
2828 }
while (!pos && ((
day += 7) <= last_day) && ++this_weekno);
2835static void expand_month_days(icalrecur_iterator *impl,
int year,
int month)
2837 int doy_offset, days_in_month, first_dow;
2839 daysmask_clearall(impl->days);
2843 impl->period_start = occurrence_as_icaltime(impl, 0);
2845 doy_offset = get_day_of_year(impl,
year,
month, 1) - 1;
2846 first_dow = get_day_of_week_adjusted(impl,
year,
month, 1);
2847 days_in_month = get_days_in_month(impl,
month,
year);
2856 impl->days_index = ICAL_YEARDAYS_MASK_SIZE;
2858 last_dow = get_day_of_week_adjusted(impl,
year,
month, days_in_month);
2860 expand_by_day(impl,
year, doy_offset, days_in_month,
2861 first_dow, last_dow,
2866static void __next_month(icalrecur_iterator *impl,
int inc)
2871 increment_month(impl, inc);
2872 this = occurrence_as_icaltime(impl, 0);
2873 expand_month_days(impl, this.year, this.month);
2876static int next_month(icalrecur_iterator *impl)
2878 return next_yearday(impl, &__next_month);
2881static int prev_month(icalrecur_iterator *impl)
2883 return prev_yearday(impl, &__next_month);
2886static int next_weekday_by_week(icalrecur_iterator *impl)
2888 int end_of_data = 0;
2890 if (next_hour(impl) == 0) {
2914 dow -= (int)impl->rule->week_start;
2919 int start_of_week = get_start_of_week(impl);
2921 if (dow + start_of_week < 1) {
2927 increment_year(impl, -1);
2930 set_day_of_year(impl, start_of_week + dow);
2936static bool next_week(icalrecur_iterator *impl)
2940 if (next_weekday_by_week(impl) == 0) {
2948 increment_monthday(impl, 7 * impl->rule->interval);
2953static int prev_weekday_by_week(icalrecur_iterator *impl)
2955 int end_of_data = 0;
2956 int start_of_week, dow;
2958 if (prev_hour(impl) == 0) {
2980 dow -= (int)impl->rule->week_start;
2985 start_of_week = get_start_of_week(impl);
2987 if (dow + start_of_week < 1) {
2989 increment_year(impl, -1);
2992 set_day_of_year(impl, start_of_week + dow);
2997static int prev_week(icalrecur_iterator *impl)
3001 if (prev_weekday_by_week(impl) == 0) {
3009 increment_monthday(impl, 7 * -impl->rule->interval);
3017static void expand_year_days(icalrecur_iterator *impl,
int year)
3020 short days_in_year = (short)get_days_in_year(impl,
year);
3023 daysmask_clearall(impl->days);
3027 impl->period_start = occurrence_as_icaltime(impl, 0);
3041 if (abs(doy) > days_in_year) {
3042 switch (impl->rule->skip) {
3054 doy = days_in_year + 1;
3066 }
else if (doy < 0) {
3067 doy += days_in_year + 1;
3070 daysmask_setbit(impl->days, doy, 1);
3071 if (doy < impl->days_index) {
3072 impl->days_index = doy;
3088 int nweeks = weeks_in_year(
year);
3092 (void)__icaltime_from_day_of_year(impl, 1,
year, &weekno);
3099 set_day_of_year(impl, 1);
3100 start_doy += get_start_of_week(impl) - 1;
3102 start_doy += (get_day_of_week_adjusted(impl, impl->dtstart.year,
3103 impl->dtstart.month, impl->dtstart.day) -
3104 (int)impl->rule->week_start + 7) %
3107 (void)get_days_in_year(impl,
year);
3114 weekno += nweeks + 1;
3115 }
else if (weekno > nweeks) {
3119 doy = start_doy + 7 * (weekno - 1);
3121 daysmask_setbit(impl->days, doy, 1);
3122 if (doy < impl->days_index) {
3123 impl->days_index = doy;
3132 if (
month > 0 &&
month < ICAL_BY_MONTH_SIZE) {
3142 int first_dow, last_dow;
3144 impl->days_index = ICAL_YEARDAYS_MASK_SIZE;
3151 if (
month > 0 &&
month < ICAL_BY_MONTH_SIZE) {
3152 int doy_offset, days_in_month;
3156 get_day_of_year(impl,
year,
month, 1) - 1;
3157 first_dow = get_day_of_week_adjusted(impl,
year,
month, 1);
3160 days_in_month = get_days_in_month(impl,
month,
year);
3161 last_dow = get_day_of_week_adjusted(impl,
year,
3162 month, days_in_month);
3164 expand_by_day(impl,
year, doy_offset, days_in_month,
3165 first_dow, last_dow, limiting);
3170 short doy_offset = 0, last_day;
3176 (void)__icaltime_from_day_of_year(impl, 1,
year, &weekno);
3183 set_day_of_year(impl, 1);
3184 doy_offset += get_start_of_week(impl) - 1;
3185 last_day = (7 * weeks_in_year(
year)) - doy_offset - 1;
3187 first_dow = (int)impl->rule->week_start;
3188 last_dow = (first_dow + 6) % 7;
3191 first_dow = get_day_of_week_adjusted(impl,
year, 1, 1);
3194 set_day_of_year(impl, days_in_year);
3195 last_dow = get_day_of_week(impl);
3197 last_day = days_in_year;
3200 expand_by_day(impl,
year, doy_offset, last_day, first_dow, last_dow, limiting);
3205static void __next_year(icalrecur_iterator *impl,
int inc)
3210 increment_year(impl, inc);
3211 this = occurrence_as_icaltime(impl, 0);
3212 expand_year_days(impl, this.year);
3215static int next_year(icalrecur_iterator *impl)
3217 return next_yearday(impl, &__next_year);
3220static int prev_year(icalrecur_iterator *impl)
3222 return prev_yearday(impl, &__next_year);
3225static short daymask_find_next_bit(
const unsigned long *days,
short start_index)
3227 short days_index = start_index;
3229 short startBitIndex;
3230 unsigned short wordIdx;
3232 if (days_index >= ICAL_YEARDAYS_MASK_SIZE) {
3233 return ICAL_YEARDAYS_MASK_SIZE;
3237 startBitIndex = days_index + ICAL_YEARDAYS_MASK_OFFSET;
3238 wordIdx = (
unsigned short)(startBitIndex / BITS_PER_LONG);
3240 if (startBitIndex >= 0) {
3241 v >>= startBitIndex % BITS_PER_LONG;
3243 v <<= -startBitIndex % BITS_PER_LONG;
3248 days_index += BITS_PER_LONG - startBitIndex % BITS_PER_LONG;
3251 unsigned short maxWordIdx = (
unsigned short)(LONGS_PER_BITS(ICAL_YEARDAYS_MASK_SIZE)) - 1;
3252 while (days_index < ICAL_YEARDAYS_MASK_SIZE && wordIdx < maxWordIdx) {
3260 days_index += BITS_PER_LONG;
3269 int maskSize = (int)(BITS_PER_LONG / 2);
3270 mask = (((
unsigned long)1) << maskSize) - 1;
3273 if ((v & mask) == 0) {
3275 days_index += maskSize;
3285static short daymask_find_prev_bit(
const unsigned long *days,
short start_index)
3287 short days_index = start_index;
3289 short startBitIndex;
3292 if (days_index <= -ICAL_YEARDAYS_MASK_OFFSET) {
3293 return -ICAL_YEARDAYS_MASK_OFFSET;
3297 startBitIndex = days_index + ICAL_YEARDAYS_MASK_OFFSET;
3298 wordIdx = (int)(startBitIndex / BITS_PER_LONG);
3300 v <<= BITS_PER_LONG - (startBitIndex % BITS_PER_LONG) - 1;
3304 days_index -= (startBitIndex % BITS_PER_LONG) + 1;
3307 while (days_index > -ICAL_YEARDAYS_MASK_OFFSET) {
3315 days_index -= BITS_PER_LONG;
3324 int maskSize = (int)(BITS_PER_LONG / 2);
3325 mask = ((((
unsigned long)1) << maskSize) - 1) << maskSize;
3328 if ((v & mask) == 0) {
3330 days_index -= maskSize;
3341static int next_yearday(icalrecur_iterator *impl,
3342 void (*next_period)(icalrecur_iterator *,
int))
3344 if (next_hour(impl) == 0) {
3350 reset_period_start(impl);
3353 impl->days_index = daymask_find_next_bit(impl->days, impl->days_index + 1);
3357 if (impl->days_index >= ICAL_YEARDAYS_MASK_SIZE) {
3362 next_period(impl, impl->rule->interval);
3364 if (impl->days_index < ICAL_YEARDAYS_MASK_SIZE) {
3376 if (impl->days_index < 1) {
3378 increment_year(impl, -1);
3381 set_day_of_year(impl, impl->days_index);
3386static int prev_yearday(icalrecur_iterator *impl,
3387 void (*next_period)(icalrecur_iterator *,
int))
3389 if (prev_hour(impl) == 0) {
3395 reset_period_start(impl);
3398 impl->days_index = daymask_find_prev_bit(impl->days, impl->days_index - 1);
3402 while (impl->days_index <= -ICAL_YEARDAYS_MASK_OFFSET) {
3406 next_period(impl, -impl->rule->interval);
3408 impl->days_index = ICAL_YEARDAYS_MASK_SIZE;
3409 impl->days_index = daymask_find_prev_bit(impl->days, impl->days_index - 1);
3418 if (impl->days_index < 1) {
3420 increment_year(impl, -1);
3423 set_day_of_year(impl, impl->days_index);
3428static int days_in_current_month(icalrecur_iterator *impl)
3430 return get_days_in_month(impl, impl->last.month, impl->last.year);
3433static int days_in_current_year(icalrecur_iterator *impl)
3435 return get_days_in_year(impl, impl->last.year);
3438static inline int has_contract_restriction(icalrecur_iterator *impl,
3441 return impl->bydata[byrule].by.size > 0 &&
3442 expand_map[impl->rule->freq].map[byrule] == CONTRACT;
3445static bool check_contract_restriction(icalrecur_iterator *impl,
3447 int (*get_total)(icalrecur_iterator *))
3449 if (has_contract_restriction(impl, byrule)) {
3452 for (
int itr = 0; itr < impl->bydata[byrule].by.size; itr++) {
3453 short byval = impl->bydata[byrule].by.data[itr];
3454 if ((byval < 0) && (total == 0)) {
3457 total = get_total(impl);
3466 if (v == ((byval >= 0) ? byval : (total + 1 + byval))) {
3479static bool check_contracting_rules(icalrecur_iterator *impl)
3481 struct icaltimetype last = occurrence_as_icaltime(impl, 0);
3486#define CHECK_CONTRACT_RESTRICTION(by, v, get_total) \
3487 (!has_contract_restriction(impl, (by)) || check_contract_restriction(impl, (by), (v), (get_total)))
3495 CHECK_CONTRACT_RESTRICTION(
ICAL_BY_WEEK_NO, get_week_number(impl, last), NULL) &&
3496 CHECK_CONTRACT_RESTRICTION(
3498 CHECK_CONTRACT_RESTRICTION(
3503#undef CHECK_CONTRACT_RESTRICTION
3517static void setup_setpos(icalrecur_iterator *impl,
int next)
3520 int days_index = impl->days_index;
3523 bydata_indices[byrule] = impl->bydata[byrule].index;
3527 impl->recurrence_set_size = 1;
3528 int period_change = 1;
3530 switch (impl->rule->freq) {
3536 period_change = (next ? next_second : prev_second)(impl);
3539 period_change = (next ? next_minute : prev_minute)(impl);
3542 period_change = (next ? next_hour : prev_hour)(impl);
3545 period_change = (next ? next_weekday_by_week : prev_weekday_by_week)(impl);
3550 period_change = (next ? next_yearday : prev_yearday)(impl, NULL);
3553 period_change = (next ? next_yearday : prev_yearday)(impl, NULL);
3559 if (period_change == 0 && check_contracting_rules(impl)) {
3560 impl->recurrence_set_size++;
3562 }
while (period_change == 0);
3569 impl->set_pos = impl->recurrence_set_size;
3570 impl->sp_idxp = impl->sp_pmax;
3571 impl->sp_idxn = impl->sp_pmax + 1;
3577 set_datetime(impl, last);
3579 impl->days_index = days_index;
3581 impl->bydata[byrule].index = bydata_indices[byrule];
3589static inline int setpos_cmp(
int s1,
int s2,
int next)
3592 return (next ? -1 : 1);
3593 }
else if (s2 < s1) {
3594 return (next ? 1 : -1);
3605static bool check_setpos(icalrecur_iterator *impl,
int next)
3614 if (impl->sp_pmax >= 0) {
3615 set_pos = by->data[impl->sp_idxp];
3617 while (setpos_cmp(set_pos, impl->set_pos, next) < 0) {
3618 if (next && impl->sp_idxp < impl->sp_pmax) {
3620 }
else if (!next && impl->sp_idxp > 0) {
3625 set_pos = by->data[impl->sp_idxp];
3627 if (impl->set_pos == set_pos) {
3632 if (impl->sp_pmax < by->size - 1) {
3633 set_pos = by->data[impl->sp_idxn] + impl->recurrence_set_size + 1;
3634 while (setpos_cmp(set_pos, impl->set_pos, next) < 0) {
3635 if (next && impl->sp_idxn > impl->sp_pmax + 1) {
3637 }
else if (!next && impl->sp_idxn < by->size - 1) {
3642 set_pos = by->data[impl->sp_idxn] + impl->recurrence_set_size + 1;
3644 if (impl->set_pos == set_pos) {
3655 (impl->rule->count != 0 && impl->occurrence_no >= impl->rule->count) ||
3662 if ((impl->occurrence_no == 0) &&
3664 check_setpos(impl, 1) &&
3665 check_contracting_rules(impl)) {
3666 impl->occurrence_no++;
3670 int period_change = 1;
3673 icalrecur_iterator impl_last = *impl;
3676 size_t stalledCnt = 0;
3678 int lastTimeCompare = 0;
3679 bool hasByData =
false;
3680 int checkContractingRules = 0;
3681 size_t cntRecurrences = 0;
3684 switch (impl->rule->freq) {
3691 period_change = next_minute(impl);
3695 period_change = next_hour(impl);
3699 period_change = next_day(impl);
3703 period_change = next_week(impl);
3707 period_change = next_month(impl);
3711 period_change = next_year(impl);
3719 impl->last = occurrence_as_icaltime(impl, 1);
3723 if (impl->last.year > MAX_TIME_T_YEAR ||
3730 set_datetime(impl, impl_last.last);
3735 checkContractingRules = -1;
3737 checkContractingRules = check_contracting_rules(impl) ? 1 : 0;
3738 if (checkContractingRules == 1) {
3739 if (period_change) {
3740 setup_setpos(impl, 1);
3749 if (lastTimeCompare == 0) {
3750 if (stalledCnt++ == max_recurrence_time_count) {
3756 }
while ((cntRecurrences++ < max_recurrences) &&
3757 ((lastTimeCompare == 0) ||
3758 (hasByData && !check_setpos(impl, 1)) ||
3760 (checkContractingRules == 0) ||
3761 (checkContractingRules == -1 && !check_contracting_rules(impl))));
3763 impl->occurrence_no++;
3775 int period_change = 1;
3776 icalrecur_iterator impl_last = *impl;
3780 switch (impl->rule->freq) {
3786 period_change = prev_minute(impl);
3790 period_change = prev_hour(impl);
3794 period_change = prev_day(impl);
3798 period_change = prev_week(impl);
3802 period_change = prev_month(impl);
3806 period_change = prev_year(impl);
3814 impl->last = occurrence_as_icaltime(impl, 1);
3821 set_datetime(impl, impl_last.last);
3825 if (has_by_data(impl,
ICAL_BY_SET_POS) && check_contracting_rules(impl)) {
3826 if (period_change) {
3827 setup_setpos(impl, 0);
3833 }
while (impl->last.year > MAX_TIME_T_YEAR ||
3840 !check_contracting_rules(impl));
3842 impl->occurrence_no--;
3849static void set_bydata_start(icalrecurrence_iterator_by_data *bydata,
int tfield)
3853 bdi < bydata->by.size; bdi++) {
3854 if (bydata->by.data[bdi] == tfield) {
3855 bydata->index = bdi;
3861static bool __iterator_set_start(icalrecur_iterator *impl,
icaltimetype start)
3864 short interval = impl->rule->interval;
3867 impl->istart = start;
3868 impl->occurrence_no = 0;
3869 impl->days_index = ICAL_YEARDAYS_MASK_SIZE;
3872 set_start(impl, start);
3879 if ((interval > 1) &&
3880 (diff = (impl->istart.year - impl->rstart.year) % interval)) {
3883 set_day_of_year(impl, 1);
3884 increment_year(impl, interval - diff);
3888 start = occurrence_as_icaltime(impl, 0);
3891 int start_weekno = get_week_number(impl, start);
3892 if (start_weekno > 5 &&
3897 increment_year(impl, -1);
3898 expand_year_days(impl, start.
year - 1);
3899 int days_in_year = get_days_in_year(impl, start.
year - 1);
3900 impl->days_index = daymask_find_next_bit(impl->days, days_in_year + 1);
3901 if (impl->days_index >= ICAL_YEARDAYS_MASK_SIZE) {
3902 increment_year(impl, 1);
3904 }
else if (start_weekno < 45 &&
3905 start.
month == 12) {
3909 increment_year(impl, 1);
3910 expand_year_days(impl, start.
year + 1);
3911 impl->days_index = daymask_find_next_bit(impl->days, -ICAL_YEARDAYS_MASK_OFFSET);
3912 if (impl->days_index >= ICAL_YEARDAYS_MASK_SIZE) {
3913 increment_year(impl, -1);
3920 while (start.
year < MAX_TIME_T_YEAR && impl->days_index >= ICAL_YEARDAYS_MASK_SIZE) {
3921 expand_year_days(impl, start.
year);
3934 if (impl->days_index >= ICAL_YEARDAYS_MASK_SIZE) {
3935 increment_year(impl, interval);
3936 start = occurrence_as_icaltime(impl, 0);
3941 set_day_of_year(impl, impl->days_index);
3942 if (impl->days_index < 1) {
3943 increment_year(impl, -1);
3952 if ((interval > 1) &&
3953 (diff = month_diff(impl, impl->rstart, impl->istart) % interval)) {
3956 increment_monthday(impl, -impl->istart.day + 1);
3957 __increment_month(impl, interval - diff);
3961 start = occurrence_as_icaltime(impl, 0);
3965 while (start.
year < 20000) {
3966 expand_month_days(impl, start.
year, start.
month);
3967 if (impl->days_index < ICAL_YEARDAYS_MASK_SIZE) {
3970 increment_month(impl, impl->rule->interval);
3971 start = occurrence_as_icaltime(impl, 0);
3975 set_day_of_year(impl, impl->days_index);
3983 recur_iterator_set_static_single_by_value(impl,
ICAL_BY_DAY, (
short)get_day_of_week(impl));
3985 adjust_to_byday(impl);
3989 impl->rstart = occurrence_as_icaltime(impl, 0);
3993 start = occurrence_as_icaltime(impl, 0);
3995 if ((interval > 1) &&
3996 (diff = (day_diff(impl, impl->rstart, start) + 6) / 7) % interval) {
3999 increment_monthday(impl, 7 * (interval - diff));
4005 if ((interval > 1) &&
4006 (diff = day_diff(impl, impl->rstart, impl->istart) % interval)) {
4009 increment_monthday(impl, interval - diff);
4014 if ((interval > 1) &&
4015 (diff = abs(impl->istart.hour - impl->rstart.hour) % interval)) {
4018 increment_hour(impl, interval - diff);
4020 set_bydata_start(&impl->bydata[
ICAL_BY_HOUR], impl->istart.hour);
4024 if ((interval > 1) &&
4025 (diff = abs(impl->istart.minute - impl->rstart.minute) % interval)) {
4028 increment_minute(impl, interval - diff);
4030 set_bydata_start(&impl->bydata[
ICAL_BY_MINUTE], impl->istart.minute);
4034 if ((interval > 1) &&
4035 (diff = abs(impl->istart.second - impl->rstart.second) % interval)) {
4038 increment_second(impl, interval - diff);
4040 set_bydata_start(&impl->bydata[
ICAL_BY_SECOND], impl->istart.second);
4048 impl->last = occurrence_as_icaltime(impl, 1);
4050 setup_setpos(impl, 1);
4054 if (impl->last.year > MAX_TIME_T_YEAR) {
4066 if (impl->rule->count > 0) {
4076 start = impl->dtstart;
4084 return __iterator_set_start(impl, start);
4117 from = impl->rule->until;
4124 if (!__iterator_set_start(impl, from)) {
4145 impl->days_index = 0;
4163 icalrecurrencetype_free(recur, 0);
4165 memset(recur, 0,
sizeof(*recur));
4189 pos = (abs(
day) - wd) / 8 * ((
day < 0) ? -1 : 1);
4196 short s_weekday = (short)weekday;
4197 short a_position = (short)(8 * abs(position));
4198 return (s_weekday + a_position) * ((position < 0) ? -1 : 1);
4203 return (
month & LEAP_MONTH);
4208 return (
month & ~LEAP_MONTH);
4213 return (
short)
month | (is_leap ? LEAP_MONTH : 0);
4217 icaltime_t start,
int count, icaltime_t *array)
4220 icalrecur_iterator *ritr;
4223 memset(array, 0, (
size_t)count *
sizeof(icaltime_t));
4256#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD
4257 pthread_mutex_lock(&invalid_rrule_mutex);
4260 myHandling = invalidRruleHandling;
4262#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD
4263 pthread_mutex_unlock(&invalid_rrule_mutex);
4271#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD
4272 pthread_mutex_lock(&invalid_rrule_mutex);
4275 invalidRruleHandling = newSetting;
4277#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD
4278 pthread_mutex_unlock(&invalid_rrule_mutex);
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