aboutsummaryrefslogtreecommitdiffstats
path: root/src/entry.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/entry.c')
-rw-r--r--src/entry.c317
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
+ );
}