From 1ccfd37f5513ed29a61325a81944386da4e2a737 Mon Sep 17 00:00:00 2001 From: katherine Date: Tue, 7 Jan 2020 14:51:09 -0700 Subject: more efficient entry checking it's a mess, but should work --- src/calendar.c | 23 +++-- src/entry.c | 317 ++++++++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 282 insertions(+), 58 deletions(-) diff --git a/src/calendar.c b/src/calendar.c index bcf06f6..b852b1c 100644 --- a/src/calendar.c +++ b/src/calendar.c @@ -146,7 +146,7 @@ static struct entry_interval_s sub_get_interval(struct state_s *s) return iv; } - if (l > INT_MAX) { + if (l > INT_MAX - 365) { ERRP("time interval too large"); s->err_flag = true; return iv; @@ -229,7 +229,7 @@ static time_t sub_get_date(struct state_s *s) int i; char c; time_t rt = 0; - struct tm tmp; + struct tm ttmp; struct tm t = { .tm_year = 2000, .tm_mday = 1, @@ -346,7 +346,7 @@ skip_num: } validate: - tmp = t; + ttmp = t; /* NOTE: timegm is non-POSIX, but commonplace */ if (s->cur_local) @@ -359,13 +359,18 @@ validate: s->err_flag = true; } + if (s->cur_local) + t = *localtime(&rt); + else + t = *gmtime(&rt); + if ( - tmp.tm_year != t.tm_year || - tmp.tm_mon != t.tm_mon || - tmp.tm_mday != t.tm_mday || - tmp.tm_hour != t.tm_hour || - tmp.tm_min != t.tm_min || - tmp.tm_sec != t.tm_sec + ttmp.tm_year != t.tm_year || + ttmp.tm_mon != t.tm_mon || + ttmp.tm_mday != t.tm_mday || + ttmp.tm_hour != t.tm_hour || + ttmp.tm_min != t.tm_min || + ttmp.tm_sec != t.tm_sec ) { ERRP("invalid date"); s->err_flag = true; diff --git a/src/entry.c b/src/entry.c index 13208ca..a2ad34e 100644 --- a/src/entry.c +++ b/src/entry.c @@ -1,22 +1,258 @@ #include "entry.h" #include +#include static time_t rt; -/* super inefficient, but should best-possible avoid any weird leap +static inline time_t* sub_seconds( + struct entry_s *e, + time_t *now, time_t *now_minus_stay, time_t *now_plus_warn, + struct tm *t, struct tm *ttmp, struct tm *torig +) +{ + unsigned mul, mul_last; + time_t rttmp; + struct tm *swap; + + rt = e->start; + + if (rt > *now_plus_warn) + return NULL; + + if (rt >= *now_minus_stay) + return &rt; + + for (mul = 1;;) { + mul_last = mul; + mul *= 2; + + if ((int)(mul * e->every.second) <= 0) { + mul = ((INT_MAX - 60) / e->every.second); + } + + *t = *ttmp; + + ttmp->tm_sec += mul * e->every.second; + + rttmp = rt; + + if (e->local) + rt = mktime(ttmp); + else + rt = timegm(ttmp); + + if (rt == -1) + return NULL; + + if (rt >= *now_minus_stay) + break; + + if (e->local) + swap = localtime(&rt); + else + swap = gmtime(&rt); + if (swap == NULL) + return NULL; + *ttmp = *swap; + } + + rt = rttmp; + + for (mul = mul_last; mul > 1; mul = mul / 2) { + *ttmp = *t; + ttmp->tm_sec += mul * e->every.second; + + if (e->local) + rttmp = mktime(ttmp); + else + rttmp = timegm(ttmp); + + if (rttmp == -1) + return NULL; + + if (rttmp >= *now_minus_stay) + continue; + + rt = rttmp; + if (e->local) + swap = localtime(&rt); + else + swap = gmtime(&rt); + if (swap == NULL) + return NULL; + *t = *swap; + } + + while (true) { + *ttmp = *t; + + ttmp->tm_sec += e->every.second; + + if (e->local) + rttmp = mktime(ttmp); + else + rttmp = timegm(ttmp); + + if (rttmp == -1) + return NULL; + + if (rttmp > *now_plus_warn) + return NULL; + + rt = rttmp; + + if (rt >= *now_minus_stay) + return &rt; + + if (e->local) + swap = localtime(&rt); + else + swap = gmtime(&rt); + if (swap == NULL) + return NULL; + *t = *swap; + } +} + +static inline time_t* sub_others( + struct entry_s *e, + time_t *now, time_t *now_minus_stay, time_t *now_plus_warn, + struct tm *t, struct tm *ttmp, struct tm *torig +) { + unsigned mul, mul_last, mul_max; + time_t rttmp; + struct tm *swap; + + mul_max = e->every.year; + + if (e->every.month > mul_max) + mul_max = e->every.month; + if (e->every.day > mul_max) + mul_max = e->every.day; + if (e->every.hour > mul_max) + mul_max = e->every.hour; + if (e->every.minute > mul_max) + mul_max = e->every.minute; + if (e->every.second > mul_max) + mul_max = e->every.second; + + mul_max = (INT_MAX - 365) / mul_max; + + for (mul = 1, mul_last = 1;;) { + if (mul == mul_max) + return NULL; + + *t = *torig; + + t->tm_year += mul * e->every.year; + t->tm_mon += mul * e->every.month; + t->tm_mday += mul * e->every.day; + t->tm_hour += mul * e->every.hour; + t->tm_min += mul * e->every.minute; + t->tm_sec += mul * e->every.second; + + if (e->local) + rt = mktime(t); + else + rt = timegm(t); + + if (rt == -1) + return NULL; + + if (rt >= *now_minus_stay) + break; + + mul_last = mul; + mul *= 2; + + if (mul > mul_max || mul <= mul_last) + break; + } + + for (mul = mul_last;;) { + mul++; + + if (mul > mul_max) + return NULL; + + *t = *torig; + + t->tm_year += mul * e->every.year; + t->tm_mon += mul * e->every.month; + t->tm_mday += mul * e->every.day; + t->tm_hour += mul * e->every.hour; + t->tm_min += mul * e->every.minute; + t->tm_sec += mul * e->every.second; + + if (e->local) + rt = mktime(t); + else + rt = timegm(t); + + if (rt == -1) + return NULL; + + if (rt > *now_plus_warn) + return NULL; + + if (rt >= *now_minus_stay) { + if (e->every.second) + return &rt; + + if (e->local) + swap = localtime(&rt); + else + swap = gmtime(&rt); + if (swap == NULL) + return NULL; + *ttmp = *swap; + + if (ttmp->tm_sec != torig->tm_sec) + continue; + + if (e->every.minute) + return &rt; + + if (ttmp->tm_min != torig->tm_min) + continue; + + if (e->every.hour) + return &rt; + + if (ttmp->tm_hour != torig->tm_hour) + continue; + + if (e->every.day) + return &rt; + + if (ttmp->tm_mday != torig->tm_mday) + continue; + + if (e->every.month) + return &rt; + + if (ttmp->tm_mon != torig->tm_mon) + continue; + + return &rt; + } + } +} + +/* super messy, but should best-possible avoid any weird leap * years/seconds/dst-problems/other future changes */ time_t* entry_is_active(struct entry_s *e, time_t now) { - struct tm t, ttmp, *tmp; - time_t rttmp, now_plus_warn, now_minus_stay; + struct tm t, ttmp, torig, *swap; + time_t now_plus_warn, now_minus_stay; assert(e != NULL); - tmp = localtime(&now); - if (tmp == NULL) + swap = localtime(&now); + if (swap == NULL) return NULL; - t = *tmp; + t = *swap; ttmp = t; t.tm_year += e->warn.year; @@ -52,50 +288,33 @@ time_t* entry_is_active(struct entry_s *e, time_t now) if (e->has_end && now_minus_stay > e->end) return NULL; - tmp = localtime( &(e->start) ); - if (tmp == NULL) + if (e->local) + swap = localtime( &(e->start) ); + else + swap = gmtime( &(e->start) ); + if (swap == NULL) return NULL; - t = *tmp; - - for (rt = e->start;;) { - if (rt > now_plus_warn) - return NULL; - - if (rt >= now_minus_stay) - return &rt; - - ttmp = t; - - t.tm_year += e->every.year; - t.tm_mon += e->every.month; - t.tm_mday += e->every.day; - t.tm_hour += e->every.hour; - t.tm_min += e->every.minute; - t.tm_sec += e->every.second; - - rt = mktime(&t); - - if (rt == -1) { - rttmp = mktime(&ttmp); - if (rttmp == -1) - return NULL; - - tmp = localtime(&rttmp); - if (tmp == NULL) - return NULL; - t = *tmp; - - t.tm_year += e->every.year; - t.tm_mon += e->every.month; - t.tm_mday += e->every.day; - t.tm_hour += e->every.hour; - t.tm_min += e->every.minute; - t.tm_sec += e->every.second; - - rt = mktime(&t); + t = *swap; + torig = t; + ttmp = t; - if (rt == -1) - return NULL; - } + if ( + e->every.year == 0 && + e->every.month == 0 && + e->every.day == 0 && + e->every.hour == 0 && + e->every.minute == 0 + ) { + return sub_seconds( + e, + &now, &now_minus_stay, &now_plus_warn, + &t, &ttmp, &torig + ); } + + return sub_others( + e, + &now, &now_minus_stay, &now_plus_warn, + &t, &ttmp, &torig + ); } -- cgit v1.2.3