diff options
Diffstat (limited to 'src/entry.c')
-rw-r--r-- | src/entry.c | 317 |
1 files changed, 268 insertions, 49 deletions
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 <assert.h> +#include <limits.h> 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 + ); } |