Skip to content

Commit 14e3c02

Browse files
committed
icalrecur: Allow limiting using negative BY* values. See libical#791
1 parent 0dc0fb9 commit 14e3c02

File tree

1 file changed

+36
-11
lines changed

1 file changed

+36
-11
lines changed

src/libical/icalrecur.c

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3297,6 +3297,16 @@ int icalrecur_check_rulepart(icalrecur_iterator *impl,
32973297
return 0;
32983298
}
32993299

3300+
static int days_in_current_month(icalrecur_iterator *impl)
3301+
{
3302+
return get_days_in_month(impl, impl->last.month, impl->last.year);
3303+
}
3304+
3305+
static int days_in_current_year(icalrecur_iterator *impl)
3306+
{
3307+
return get_days_in_year(impl, impl->last.year);
3308+
}
3309+
33003310
static inline int has_contract_restriction(icalrecur_iterator *impl,
33013311
icalrecurrencetype_byrule byrule)
33023312
{
@@ -3305,14 +3315,29 @@ static inline int has_contract_restriction(icalrecur_iterator *impl,
33053315
}
33063316

33073317
static int check_contract_restriction(icalrecur_iterator *impl,
3308-
icalrecurrencetype_byrule byrule, int v)
3318+
icalrecurrencetype_byrule byrule, int v,
3319+
int (*get_total)(icalrecur_iterator *))
33093320
{
33103321
int pass = 0;
33113322
int itr;
3323+
int total = 0;
33123324

33133325
if (has_contract_restriction(impl, byrule)) {
33143326
for (itr = 0; itr < impl->bydata[byrule].by.size; itr++) {
3315-
if (impl->bydata[byrule].by.data[itr] == v) {
3327+
short byval = impl->bydata[byrule].by.data[itr];
3328+
if ((byval < 0) && (total == 0)) {
3329+
if (get_total)
3330+
// load total value lazily only when needed
3331+
total = get_total(impl);
3332+
else {
3333+
// limiting by negative values is only allowed for
3334+
// BYMONTHDAY, BYYEARDAY (BYDAY is handled separately)
3335+
icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
3336+
continue;
3337+
}
3338+
}
3339+
3340+
if (v == ((byval >= 0) ? byval : (total + 1 + byval))) {
33163341
pass = 1;
33173342
break;
33183343
}
@@ -3332,17 +3357,17 @@ static int check_contracting_rules(icalrecur_iterator *impl)
33323357

33333358
// Check `has_contract_restriction` before calling `check_contract_restriction` to avoid
33343359
// evaluating potentially expensive `v` if not needed.
3335-
#define CHECK_CONTRACT_RESTRICTION(by, v) (!has_contract_restriction(impl, (by)) || check_contract_restriction(impl, (by), (v)))
3360+
#define CHECK_CONTRACT_RESTRICTION(by, v, get_total) (!has_contract_restriction(impl, (by)) || check_contract_restriction(impl, (by), (v), (get_total)))
33363361

33373362
if (
3338-
CHECK_CONTRACT_RESTRICTION(ICAL_BY_SECOND, last.second) &&
3339-
CHECK_CONTRACT_RESTRICTION(ICAL_BY_MINUTE, last.minute) &&
3340-
CHECK_CONTRACT_RESTRICTION(ICAL_BY_HOUR, last.hour) &&
3341-
CHECK_CONTRACT_RESTRICTION(ICAL_BY_MONTH_DAY, last.day) &&
3342-
CHECK_CONTRACT_RESTRICTION(ICAL_BY_MONTH, last.month) &&
3343-
CHECK_CONTRACT_RESTRICTION(ICAL_BY_WEEK_NO, get_week_number(impl, last)) &&
3344-
CHECK_CONTRACT_RESTRICTION(ICAL_BY_DAY, get_day_of_week_adjusted(impl, last.year, last.month, last.day)) &&
3345-
CHECK_CONTRACT_RESTRICTION(ICAL_BY_YEAR_DAY, get_day_of_year(impl, last.year, last.month, last.day))) {
3363+
CHECK_CONTRACT_RESTRICTION(ICAL_BY_SECOND, last.second, NULL) &&
3364+
CHECK_CONTRACT_RESTRICTION(ICAL_BY_MINUTE, last.minute, NULL) &&
3365+
CHECK_CONTRACT_RESTRICTION(ICAL_BY_HOUR, last.hour, NULL) &&
3366+
CHECK_CONTRACT_RESTRICTION(ICAL_BY_MONTH_DAY, last.day, days_in_current_month) &&
3367+
CHECK_CONTRACT_RESTRICTION(ICAL_BY_MONTH, last.month, NULL) &&
3368+
CHECK_CONTRACT_RESTRICTION(ICAL_BY_WEEK_NO, get_week_number(impl, last), NULL) &&
3369+
CHECK_CONTRACT_RESTRICTION(ICAL_BY_DAY, get_day_of_week_adjusted(impl, last.year, last.month, last.day), NULL) &&
3370+
CHECK_CONTRACT_RESTRICTION(ICAL_BY_YEAR_DAY, get_day_of_year(impl, last.year, last.month, last.day), days_in_current_year)) {
33463371
return 1;
33473372
}
33483373

0 commit comments

Comments
 (0)