/***********************************************************************
* *
* This software is part of the ast package *
* Copyright (c) 1985-2012 AT&T Intellectual Property *
* and is licensed under the *
* Eclipse Public License, Version 1.0 *
* by AT&T Intellectual Property *
* *
* A copy of the License is available at *
* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
* *
* Information and Software Systems Research *
* AT&T Research *
* Florham Park NJ *
* *
* Glenn Fowler <gsf@research.att.com> *
* David Korn <dgk@research.att.com> *
* Phong Vo <kpv@research.att.com> *
* *
***********************************************************************/
#pragma prototyped
/*
* Glenn Fowler
* AT&T Research
*
* Time_t conversion support
*
* relative times inspired by Steve Bellovin's netnews getdate(3)
*/
#include <tmx.h>
#include <ctype.h>
#include <debug.h>
#define dig4(s,n) ((n)=((*(s)++)-'0')*1000,(n)+=((*(s)++)-'0')*100,(n)+=((*(s)++)-'0')*10,(n)+=(*(s)++)-'0')
#define FLAGS(f) (f&EXACT)?"|EXACT":"",(f&LAST)?"|LAST":"",(f&THIS)?"|THIS":"",(f&NEXT)?"|NEXT":"",(f&ORDINAL)?"|ORDINAL":"",(f&FINAL)?"|FINAL":"",(f&WORK)?"|WORK":""
/*
* parse cron range into set
* return: -1:error 0:* 1:some
*/
static int
{
int n;
int m;
int i;
char* t;
while (isspace(*s) || *s == '_')
s++;
if (*s == '*')
{
*e = s + 1;
return 0;
}
for (;;)
{
n = strtol(s, &t, 10);
return -1;
i = 1;
if (*(s = t) == '-')
{
m = strtol(++s, &t, 10);
if (s == t || m < n || m > hi)
return -1;
if (*(s = t) == '/')
{
i = strtol(++s, &t, 10);
if (s == t || i < 1)
return -1;
s = t;
}
}
else
m = n;
for (; n <= m; n += i)
set[n] = 1;
if (*s != ',')
break;
s++;
}
*e = s;
return 1;
}
/*
* normalize <p,q> to power of 10 u in tm
*/
static void
{
Time_t t = p;
while (q > u)
{
q /= 10;
t /= 10;
}
while (q < u)
{
q *= 10;
t *= 10;
}
}
/*
* parse date expression in s and return Time_t value
*
* if non-null, e points to the first invalid sequence in s
* now provides default values
*/
{
register long n;
register int w;
unsigned long set;
unsigned long state;
unsigned long flags;
char* t;
char* u;
const char* o;
const char* x;
char* last;
char* type;
int day;
int dir;
int dst;
int zone;
int c;
int f;
int i;
int j;
int k;
int l;
long m;
unsigned long p;
unsigned long q;
/*
* check DATEMSK first
*/
if (t && !*last)
{
if (e)
*e = last;
return fix;
}
o = s;
/*
* use now for defaults
*/
day = -1;
dir = 0;
type = 0;
zone = TM_LOCALZONE;
skip[0] = 0;
for (n = 1; n <= UCHAR_MAX; n++)
/*
* get <weekday year month day hour minutes seconds ?[ds]t [ap]m>
*/
for (;;)
{
for (;;)
{
if (*s == '.' || *s == '-' || *s == '+')
{
if (((set|state) & (YEAR|MONTH|HOUR|MINUTE|ZONE)) == (YEAR|MONTH|HOUR|MINUTE) && (i = tmgoff(s, &t, TM_LOCALZONE)) != TM_LOCALZONE)
{
zone = i;
if (!*(s = t))
break;
}
else if (*s == '+')
break;
}
else if (!skip[*s])
break;
s++;
}
if (!*(last = (char*)s))
break;
if (*s == '#')
{
if (isdigit(*++s))
{
sns:
if (*(s = t) == '.')
{
fix = 0;
m = 1000000000;
while (isdigit(*++s))
}
else if (now <= 0x7fffffff)
goto reset;
}
else if (*s++ == '#')
{
goto reset;
}
break;
}
if ((*s == 'P' || *s == 'p') && (!isalpha(*(s + 1)) || (*(s + 1) == 'T' || *(s + 1) == 't') && !isalpha(*(s + 2))))
{
/*
* iso duration
*/
t = (char*)s;
m = 0;
P_INIT('Y');
do
{
c = *++s;
switch (c)
{
case 0:
m++;
if ((char*)s > u)
{
s--;
c = '_';
goto duration_next;
}
break;
case 'T':
case 't':
m++;
if ((char*)s > u)
{
s++;
c = 'D';
goto duration_next;
}
continue;
case 'Y':
case 'y':
m = 0;
if (q > 1)
else
P_INIT('M');
continue;
case 'm':
if (!m)
m = 1;
/*FALLTHROUGH*/
case 'M':
switch (*(s + 1))
{
case 'I':
case 'i':
s++;
m = 1;
w = 'S';
break;
case 'O':
case 'o':
s++;
m = 0;
w = 'H';
break;
case 'S':
case 's':
s++;
m = 2;
w = 's';
break;
}
switch (m)
{
case 0:
m = 1;
if (q > 1)
else
break;
case 1:
m = 2;
if (q > 1)
else
break;
default:
if (q > 1)
else
break;
}
P_INIT(w);
continue;
case 'W':
case 'w':
m = 0;
if (q > 1)
else
P_INIT('D');
continue;
case 'D':
case 'd':
m = 0;
if (q > 1)
else
P_INIT('H');
continue;
case 'H':
case 'h':
m = 1;
if (q > 1)
else
P_INIT('m');
continue;
case 'S':
case 's':
m = 2;
/*FALLTHROUGH*/
case ' ':
case '_':
case '\n':
case '\r':
case '\t':
case '\v':
if (q > 1)
else
P_INIT('U');
continue;
case 'U':
case 'u':
switch (*(s + 1))
{
case 'S':
case 's':
s++;
break;
}
m = 0;
if (q > 1)
else
P_INIT('N');
continue;
case 'N':
case 'n':
switch (*(s + 1))
{
case 'S':
case 's':
s++;
break;
}
m = 0;
if (q > 1)
else
P_INIT('Y');
continue;
case '.':
if (q)
goto exact;
q = 1;
continue;
case '-':
c = 'M';
u = (char*)s++;
while (*++u && *u != ':')
if (*u == '-')
{
c = 'Y';
break;
}
goto duration_next;
case ':':
c = 'm';
u = (char*)s++;
while (*++u)
if (*u == ':')
{
c = 'H';
break;
}
goto duration_next;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
q *= 10;
p = p * 10 + (c - '0');
continue;
default:
s = (const char*)t + 1;
if (*t == 'p')
{
}
goto again;
}
break;
} while (c);
continue;
}
f = -1;
if (*s == '+')
{
while (isspace(*++s) || *s == '_');
n = strtol(s, &t, 0);
if (w = t - s)
{
for (s = t; skip[*s]; s++);
}
else
s = last;
}
{
/*
* check for cron date
*
* min hour day-of-month month day-of-week
*
* if it's cron then determine the next time
* that satisfies the specification
*
* NOTE: the only spacing is ' '||'_'||';'
*/
i = 0;
n = *(t = (char*)s);
for (;;)
{
if (n == '*')
n = *++s;
else if (!isdigit(n))
break;
else
if (n != ' ' && n != '_' && n != ';')
{
if (!n)
i++;
break;
}
i++;
while ((n = *++s) == ' ' || n == '_');
}
if (i == 5)
{
flags = 0;
/*
* minute
*/
break;
{
hit[i] = 1;
do if (++i > 59)
{
i = 0;
{
}
} while (!hit[i]);
}
/*
* hour
*/
break;
{
hit[i] = 1;
do if (++i > 23)
{
i = 0;
{
}
} while (!hit[i]);
}
/*
* day of month
*/
break;
if (k)
/*
* month
*/
break;
if (k)
else
for (i = 1; i <= 12; i++)
mon[i] = 1;
/*
* day of week
*/
break;
if (k)
s = t;
{
for (;;)
{
if (!mon[i])
{
if (++i > 12)
{
i = 1;
}
goto done;
continue;
}
{
{
break;
}
break;
break;
if (++j > 28)
{
}
k = 0;
}
break;
}
}
continue;
}
s = t;
}
n = -1;
if (isdigit(*s))
{
n = strtol(s, &t, 10);
{
now = n;
goto sns;
}
if ((*t == 'T' || *t == 't') && ((set|state) & (YEAR|MONTH|DAY)) == (YEAR|MONTH) && isdigit(*(t + 1)))
t++;
u = t + (*t == '-');
{
if (w == 4)
{
if ((n -= 1900) < TM_WINDOW)
break;
}
else if (n < TM_WINDOW)
n += 100;
m = n;
n = strtol(++u, &t, 10);
if ((i = (t - u)) < 2 || i > 3)
break;
if (i == 3)
{
k = n % 10;
n /= 10;
}
else if (*t != '-')
k = 1;
break;
if (n < 0 || n > 53)
break;
if (k == 7)
k = 0;
s = t;
continue;
}
else if (w == 6 || w == 8 && (n / 1000000) > 12)
{
t = (char*)s;
flags = 0;
if (w == 8 || w == 6 && *u != 'T' && *u != 't')
{
dig4(t, m);
if ((m -= 1900) < TM_WINDOW)
break;
}
else
{
dig2(t, m);
if (m < TM_WINDOW)
m += 100;
}
if (dig2(t, l) <= 0 || l > 12)
break;
{
if (w == 6)
goto save_yymm;
break;
goto save_yymmdd;
}
n = strtol(s = t, &t, 0);
if ((t - s) < 2)
break;
if (dig2(s, j) > 24)
break;
if ((t - s) < 2)
{
if ((t - s) == 1 || *t++ != '-')
break;
n = strtol(s = t, &t, 0);
if ((t - s) < 2)
break;
}
if (dig2(s, i) > 59)
break;
if ((t - s) == 2)
{
break;
}
else if (t - s)
break;
else
n = 0;
p = 0;
if (*t == '.')
{
q = 1000000000;
while (isdigit(*++t))
p += (*t - '0') * (q /= 10);
}
if (n > (59 + TM_MAXLEAP))
break;
goto save;
}
else if (f == -1 && isalpha(*t) && tmlex(t, &t, tm_info.format + TM_ORDINAL, TM_ORDINALS - TM_ORDINAL, NiL, 0) >= 0)
{
if (n)
n--;
for (s = t; skip[*s]; s++);
if (isdigit(*s))
{
if (n = strtol(s, &t, 10))
n--;
s = t;
if (*s == '_')
s++;
}
else
n = -1;
dir = f;
}
else
{
for (u = t; isspace(*u); u++);
if ((j = tmlex(u, NiL, tm_info.format, TM_NFORM, tm_info.format + TM_SUFFIXES, TM_PARTS - TM_SUFFIXES)) >= 0 && tm_data.lex[j] == TM_PARTS)
s = u;
else
{
if (!(state & (LAST|NEXT|THIS)) && ((i = t - s) == 4 && (*t == '.' && isdigit(*(t + 1)) && isdigit(*(t + 2)) && *(t + 3) != '.' || (!*t || isspace(*t) || *t == '_' || isalnum(*t)) && n >= 0 && (n % 100) < 60 && ((m = (n / 100)) < 20 || m < 24 && !((set|state) & (YEAR|MONTH|HOUR|MINUTE)))) || i > 4 && i <= 12))
{
/*
* various { date(1) touch(1) } formats
*
* [[cc]yy[mm]]ddhhmm[.ss[.nn...]]
* [cc]yyjjj
* hhmm[.ss[.nn...]]
*/
flags = 0;
if (state & CCYYMMDDHHMMSS)
break;
state |= CCYYMMDDHHMMSS;
p = 0;
if ((i == 7 || i == 5) && (!*t || *t == 'Z' || *t == 'z'))
{
if (i == 7)
{
dig4(s, m);
if ((m -= 1900) < TM_WINDOW)
break;
}
m += 100;
dig3(s, k);
l = 1;
j = 0;
i = 0;
n = 0;
}
else if (i & 1)
break;
else
{
u = t;
if (i == 12)
{
x = s;
dig2(x, m);
if (m <= 12)
{
u -= 4;
i -= 4;
x = s + 8;
dig4(x, m);
}
else
dig4(s, m);
if (m < 1969 || m >= 3000)
break;
m -= 1900;
}
else if (i == 10)
{
x = s;
if (!dig2(x, m) || m > 12 || !dig2(x, m) || m > 31 || dig2(x, m) > 24 || dig2(x, m) > 60 || dig2(x, m) <= 60 && !(tm_info.flags & TM_DATESTYLE))
dig2(s, m);
else
{
u -= 2;
i -= 2;
x = s + 8;
dig2(x, m);
}
if (m < TM_WINDOW)
m += 100;
}
else
if ((u - s) < 8)
else if (dig2(s, l) <= 0 || l > 12)
break;
else
if ((u - s) < 6)
break;
else
if ((u - s) < 4)
break;
if (dig2(s, j) > 24)
break;
if (dig2(s, i) > 59)
break;
if ((u - s) == 2)
{
dig2(s, n);
}
else if (u - s)
break;
else if (*t != '.')
n = 0;
else
{
if (*t == '.')
{
q = 1000000000;
while (isdigit(*++t))
p += (*t - '0') * (q /= 10);
}
}
if (n > (59 + TM_MAXLEAP))
break;
}
save:
s = t;
continue;
}
for (s = t; skip[*s]; s++);
{
c = *s;
break;
while (isspace(*++s) || *s == '_');
if (!isdigit(*s))
break;
i = n;
n = strtol(s, &t, 10);
for (s = t; isspace(*s) || *s == '_'; s++);
if (n > 59)
break;
j = n;
m = 0;
if (*s == c)
{
while (isspace(*++s) || *s == '_');
if (!isdigit(*s))
break;
n = strtol(s, &t, 10);
s = t;
if (n > (59 + TM_MAXLEAP))
break;
while (isspace(*s))
s++;
if (*s == '.')
{
q = 1000000000;
while (isdigit(*++s))
m += (*s - '0') * (q /= 10);
}
}
else
n = 0;
while (isspace(*s))
s++;
{
case TM_MERIDIAN:
s = t;
if (i == 12)
break;
case TM_MERIDIAN+1:
if (i < 12)
break;
}
{
if (f < 0)
{
f = -1;
f = 1;
else
f = 0;
}
if (f > 0)
{
if (i > k || i == k && j > l)
f--;
}
else if (i < k || i == k && j < l)
f++;
if (f > 0)
{
{
}
}
else if (f < 0)
{
{
}
}
}
continue;
}
}
}
}
for (;;)
{
if (*s == '-' || *s == '+')
{
if (((set|state) & (MONTH|DAY|HOUR|MINUTE)) == (MONTH|DAY|HOUR|MINUTE) || *s == '+' && (!isdigit(s[1]) || !isdigit(s[2]) || s[3] != ':' && (s[3] != '.' || ((set|state) & (YEAR|MONTH)) != (YEAR|MONTH))))
break;
s++;
}
else if (skip[*s])
s++;
else
break;
}
if (isalpha(*s))
{
if (n > 0)
{
x = s;
q = *s++;
if (isalpha(*s))
{
q <<= 8;
q |= *s++;
if (isalpha(*s))
{
s = t;
if (isalpha(*s))
{
q <<= 8;
q |= *s++;
if (isalpha(*s))
{
q <<= 8;
q |= *s++;
if (isalpha(*s))
q = 0;
}
}
}
}
switch (q)
{
case K1('y'):
case K1('Y'):
continue;
case K1('M'):
continue;
case K1('w'):
case K1('W'):
continue;
case K1('d'):
case K1('D'):
continue;
case K1('h'):
case K1('H'):
continue;
case K1('m'):
continue;
case K1('s'):
case K1('S'):
continue;
continue;
case K1('u'):
case K1('U'):
continue;
continue;
}
s = x;
}
if ((j = tmlex(s, &t, tm_info.format, TM_NFORM, tm_info.format + TM_SUFFIXES, TM_PARTS - TM_SUFFIXES)) >= 0)
{
{
s = t;
{
case TM_EXACT:
continue;
case TM_LAST:
continue;
case TM_THIS:
n = 0;
continue;
case TM_NEXT:
/*
* disambiguate english "last ... in"
*/
{
continue;
}
/*FALLTHROUGH*/
case TM_FINAL:
continue;
case TM_WORK:
{
}
goto clear_hour;
case TM_ORDINAL:
j += TM_ORDINALS - TM_ORDINAL;
/*FALLTHROUGH*/
case TM_ORDINALS:
n = j - TM_ORDINALS + 1;
goto ordinal;
case TM_MERIDIAN:
if (f >= 0)
f++;
f = -1;
f = 1;
f = 2;
else
f = 0;
if (n > 0)
{
if (n > 24)
goto done;
}
k %= 24;
if (j == TM_MERIDIAN)
{
if (k == 12)
}
else if (k < 12)
if (n > 0)
goto clear_min;
continue;
case TM_DAY_ABBREV:
j += TM_DAY - TM_DAY_ABBREV;
/*FALLTHROUGH*/
case TM_DAY:
case TM_PARTS:
case TM_HOURS:
for (;;)
{
while (skip[*s])
s++;
{
s = t;
if (k <= 2)
else if (k <= 5)
else if (k <= 8)
else
}
else
{
break;
}
}
/*FALLTHROUGH*/
case TM_DAYS:
if (n == -1)
{
/*
* disambiguate english "second"
*/
if (j == TM_PARTS && f == -1)
{
n = 2;
goto ordinal;
}
n = 1;
}
/*
* disambiguate "last" vs. { "previous" "final" }
*/
while (isspace(*s))
s++;
if ((k = tmlex(s, &t, tm_info.format + TM_NEXT, TM_EXACT - TM_NEXT, NiL, 0)) >= 0 || (k = tmlex(s, &t, tm_info.format + TM_PARTS + 3, 1, NiL, 0)) >= 0)
{
s = t;
{
}
else
}
n = -n;
n--;
m = (f > 0) ? f * n : n;
message((-1, "AHA#%d f=%d n=%d i=%d j=%d k=%d l=%d m=%d state=" FFMT, __LINE__, f, n, i, j, k, l, m, FLAGS(state)));
switch (j)
{
case TM_DAYS+0:
goto clear_hour;
case TM_DAYS+1:
goto clear_hour;
case TM_DAYS+2:
goto clear_hour;
case TM_PARTS+0:
if ((m < 0 ? -m : m) > (365L*24L*60L*60L))
{
goto reset;
}
goto clear_nsec;
case TM_PARTS+1:
goto clear_sec;
case TM_PARTS+2:
goto clear_min;
case TM_PARTS+3:
else
goto clear_hour;
case TM_PARTS+4:
goto clear_hour;
case TM_PARTS+5:
goto clear_mday;
case TM_PARTS+6:
goto clear_mon;
case TM_HOURS+0:
goto clear_hour;
case TM_HOURS+1:
goto clear_min;
case TM_HOURS+2:
goto clear_min;
case TM_HOURS+3:
goto clear_min;
}
if (!dir)
dir = m;
message((-1, "AHA#%d mday=%d wday=%d day=%d dir=%d f=%d i=%d j=%d l=%d m=%d", __LINE__, tm->tm_mday, tm->tm_wday, day, dir, f, i, j, l, m));
{
{
while (isspace(*s))
s++;
{
goto clear_hour;
}
}
if (j < 0)
j += 7;
}
else if (j > 0)
j -= 7;
message((-1, "AHA#%d day=%d mday=%d f=%d m=%d j=%d state=" FFMT, __LINE__, day, tm->tm_mday, f, m, j, FLAGS(state)));
goto clear_hour;
{
if (f >= 0)
day = -1;
m--;
goto clear_hour;
}
continue;
case TM_MONTH_ABBREV:
j += TM_MONTH - TM_MONTH_ABBREV;
/*FALLTHROUGH*/
case TM_MONTH:
goto done;
if (n < 0)
{
while (skip[*s])
s++;
if (isdigit(*s))
{
n = strtol(s, &t, 10);
if (n <= 31 && *t != ':')
s = t;
else
n = -1;
}
}
if (n >= 0)
{
if (n > 31)
goto done;
if (f > 0)
}
{
n = i;
goto rel_month;
}
continue;
case TM_UT:
goto done;
s = t;
continue;
case TM_DT:
if (!dst)
goto done;
{
}
s = t;
dst = 0;
continue;
case TM_NOISE:
continue;
}
}
}
if (n < 1000)
{
{
s = t;
if (n < 0)
continue;
}
{
s = t;
if (n < 0)
continue;
}
}
}
else if (*s == '/')
{
{
s = t;
i--;
}
else
{
break;
if (isalpha(*++s))
{
break;
if (i >= TM_MONTH)
i -= TM_MONTH;
s = t;
}
else
{
i = n - 1;
n = strtol(s, &t, 10);
s = t;
if (n <= 0 || n > 31)
break;
break;
}
}
if (*s == '/')
{
n = strtol(++s, &t, 10);
w = t - s;
s = t;
if (*s == '/' || *s == ':' || *s == '-' || *s == '_')
s++;
}
else
{
{
else
goto clear_hour;
goto clear_mday;
}
continue;
}
}
if (n < 0 || w > 4)
break;
if (w == 4)
{
break;
}
else if (w == 3)
{
break;
}
{
if (n < TM_WINDOW)
n += 100;
}
{
}
{
}
else
break;
{
last = t;
break;
}
continue;
continue;
continue;
continue;
continue;
continue;
continue;
}
done:
{
m = dir;
else if (m < 0)
m++;
if (j < 0)
j += 7;
for (n = tm_data.days[tm->tm_mon] + (tm->tm_mon == 1 && tmisleapyear(tm->tm_year)); (tm->tm_mday + 7) <= n; tm->tm_mday += 7);
}
{
}
{
tm->tm_mday = (set & FINAL) ? (tm_data.days[tm->tm_mon] + (tm->tm_mon == 1 && tmisleapyear(tm->tm_year))) : 1;
{
if ((tm->tm_mday + j) > (tm_data.days[tm->tm_mon] + (tm->tm_mon == 1 && tmisleapyear(tm->tm_year))))
j -= 3;
}
}
{
now = 0;
last = (char*)o;
}
if (e)
*e = last;
return now;
}