@@ -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+
33003310static 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
33073317static 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