aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/calendar.c23
-rw-r--r--src/entry.c317
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 <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
+ );
}