1N/A/***********************************************************************
1N/A* *
1N/A* This software is part of the ast package *
1N/A* Copyright (c) 1985-2011 AT&T Intellectual Property *
1N/A* and is licensed under the *
1N/A* Common Public License, Version 1.0 *
1N/A* by AT&T Intellectual Property *
1N/A* *
1N/A* A copy of the License is available at *
1N/A* http://www.opensource.org/licenses/cpl1.0.txt *
1N/A* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
1N/A* *
1N/A* Information and Software Systems Research *
1N/A* AT&T Research *
1N/A* Florham Park NJ *
1N/A* *
1N/A* Glenn Fowler <gsf@research.att.com> *
1N/A* David Korn <dgk@research.att.com> *
1N/A* Phong Vo <kpv@research.att.com> *
1N/A* *
1N/A***********************************************************************/
1N/A#pragma prototyped
1N/A/*
1N/A * Glenn Fowler
1N/A * AT&T Research
1N/A *
1N/A * Time_t conversion support
1N/A *
1N/A * relative times inspired by Steve Bellovin's netnews getdate(3)
1N/A */
1N/A
1N/A#include <tmx.h>
1N/A#include <ctype.h>
1N/A#include <debug.h>
1N/A
1N/A#define dig1(s,n) ((n)=((*(s)++)-'0'))
1N/A#define dig2(s,n) ((n)=((*(s)++)-'0')*10,(n)+=(*(s)++)-'0')
1N/A#define dig3(s,n) ((n)=((*(s)++)-'0')*100,(n)+=((*(s)++)-'0')*10,(n)+=(*(s)++)-'0')
1N/A#define dig4(s,n) ((n)=((*(s)++)-'0')*1000,(n)+=((*(s)++)-'0')*100,(n)+=((*(s)++)-'0')*10,(n)+=(*(s)++)-'0')
1N/A
1N/A#undef BREAK
1N/A
1N/A#define BREAK (1<<0)
1N/A#define CCYYMMDDHHMMSS (1<<1)
1N/A#define CRON (1<<2)
1N/A#define DAY (1<<3)
1N/A#define EXACT (1<<4)
1N/A#define FINAL (1<<5)
1N/A#define HOLD (1<<6)
1N/A#define HOUR (1<<7)
1N/A#define LAST (1<<8)
1N/A#define MDAY (1<<9)
1N/A#define MINUTE (1<<10)
1N/A#define MONTH (1<<11)
1N/A#define NEXT (1<<12)
1N/A#define NSEC (1<<13)
1N/A#define ORDINAL (1<<14)
1N/A#define SECOND (1<<15)
1N/A#define THIS (1L<<16)
1N/A#define WDAY (1L<<17)
1N/A#define WORK (1L<<18)
1N/A#define YEAR (1L<<19)
1N/A#define ZONE (1L<<20)
1N/A
1N/A#define FFMT "%s%s%s%s%s%s%s|"
1N/A#define FLAGS(f) (f&EXACT)?"|EXACT":"",(f&LAST)?"|LAST":"",(f&THIS)?"|THIS":"",(f&NEXT)?"|NEXT":"",(f&ORDINAL)?"|ORDINAL":"",(f&FINAL)?"|FINAL":"",(f&WORK)?"|WORK":""
1N/A/*
1N/A * parse cron range into set
1N/A * return: -1:error 0:* 1:some
1N/A */
1N/A
1N/Astatic int
1N/Arange(register char* s, char** e, char* set, int lo, int hi)
1N/A{
1N/A int n;
1N/A int m;
1N/A int i;
1N/A char* t;
1N/A
1N/A while (isspace(*s) || *s == '_')
1N/A s++;
1N/A if (*s == '*')
1N/A {
1N/A *e = s + 1;
1N/A return 0;
1N/A }
1N/A memset(set, 0, hi + 1);
1N/A for (;;)
1N/A {
1N/A n = strtol(s, &t, 10);
1N/A if (s == t || n < lo || n > hi)
1N/A return -1;
1N/A i = 1;
1N/A if (*(s = t) == '-')
1N/A {
1N/A m = strtol(++s, &t, 10);
1N/A if (s == t || m < n || m > hi)
1N/A return -1;
1N/A if (*(s = t) == '/')
1N/A {
1N/A i = strtol(++s, &t, 10);
1N/A if (s == t || i < 1)
1N/A return -1;
1N/A s = t;
1N/A }
1N/A }
1N/A else
1N/A m = n;
1N/A for (; n <= m; n += i)
1N/A set[n] = 1;
1N/A if (*s != ',')
1N/A break;
1N/A s++;
1N/A }
1N/A *e = s;
1N/A return 1;
1N/A}
1N/A
1N/A/*
1N/A * normalize <p,q> to power of 10 u in tm
1N/A */
1N/A
1N/Astatic void
1N/Apowerize(Tm_t* tm, unsigned long p, unsigned long q, unsigned long u)
1N/A{
1N/A Time_t t = p;
1N/A
1N/A while (q > u)
1N/A {
1N/A q /= 10;
1N/A t /= 10;
1N/A }
1N/A while (q < u)
1N/A {
1N/A q *= 10;
1N/A t *= 10;
1N/A }
1N/A tm->tm_nsec += (int)(t % TMX_RESOLUTION);
1N/A tm->tm_sec += (int)(t / TMX_RESOLUTION);
1N/A}
1N/A
1N/A#define K1(c1) (c1)
1N/A#define K2(c1,c2) (((c1)<<8)|(c2))
1N/A#define K3(c1,c2,c3) (((c1)<<16)|((c2)<<8)|(c3))
1N/A#define K4(c1,c2,c3,c4) (((c1)<<24)|((c2)<<16)|((c3)<<8)|(c4))
1N/A
1N/A#define P_INIT(n) w = n; p = q = 0; u = (char*)s + 1
1N/A
1N/A/*
1N/A * parse date expression in s and return Time_t value
1N/A *
1N/A * if non-null, e points to the first invalid sequence in s
1N/A * now provides default values
1N/A */
1N/A
1N/ATime_t
1N/Atmxdate(register const char* s, char** e, Time_t now)
1N/A{
1N/A register Tm_t* tm;
1N/A register long n;
1N/A register int w;
1N/A unsigned long set;
1N/A unsigned long state;
1N/A unsigned long flags;
1N/A Time_t fix;
1N/A char* t;
1N/A char* u;
1N/A const char* o;
1N/A const char* x;
1N/A char* last;
1N/A char* type;
1N/A int day;
1N/A int dir;
1N/A int dst;
1N/A int zone;
1N/A int c;
1N/A int f;
1N/A int i;
1N/A int j;
1N/A int k;
1N/A int l;
1N/A long m;
1N/A unsigned long p;
1N/A unsigned long q;
1N/A Tm_zone_t* zp;
1N/A Tm_t ts;
1N/A char skip[UCHAR_MAX + 1];
1N/A
1N/A /*
1N/A * check DATEMSK first
1N/A */
1N/A
1N/A debug((error(-1, "AHA tmxdate 2009-03-06")));
1N/A fix = tmxscan(s, &last, NiL, &t, now, 0);
1N/A if (t && !*last)
1N/A {
1N/A if (e)
1N/A *e = last;
1N/A return fix;
1N/A }
1N/A o = s;
1N/A
1N/A reset:
1N/A
1N/A /*
1N/A * use now for defaults
1N/A */
1N/A
1N/A tm = tmxtm(&ts, now, NiL);
1N/A tm_info.date = tm->tm_zone;
1N/A day = -1;
1N/A dir = 0;
1N/A dst = TM_DST;
1N/A set = state = 0;
1N/A type = 0;
1N/A zone = TM_LOCALZONE;
1N/A skip[0] = 0;
1N/A for (n = 1; n <= UCHAR_MAX; n++)
1N/A skip[n] = isspace(n) || strchr("_,;@=|!^()[]{}", n);
1N/A
1N/A /*
1N/A * get <weekday year month day hour minutes seconds ?[ds]t [ap]m>
1N/A */
1N/A
1N/A again:
1N/A for (;;)
1N/A {
1N/A state &= (state & HOLD) ? ~(HOLD) : ~(EXACT|LAST|NEXT|THIS);
1N/A if ((set|state) & (YEAR|MONTH|DAY))
1N/A skip['/'] = 1;
1N/A message((-1, "AHA#%d state=" FFMT " set=" FFMT, __LINE__, FLAGS(state), FLAGS(set)));
1N/A for (;;)
1N/A {
1N/A if (*s == '.' || *s == '-' || *s == '+')
1N/A {
1N/A if (((set|state) & (YEAR|MONTH|HOUR|MINUTE|ZONE)) == (YEAR|MONTH|HOUR|MINUTE) && (i = tmgoff(s, &t, TM_LOCALZONE)) != TM_LOCALZONE)
1N/A {
1N/A zone = i;
1N/A state |= ZONE;
1N/A if (!*(s = t))
1N/A break;
1N/A }
1N/A else if (*s == '+')
1N/A break;
1N/A }
1N/A else if (!skip[*s])
1N/A break;
1N/A s++;
1N/A }
1N/A if (!*(last = (char*)s))
1N/A break;
1N/A if (*s == '#')
1N/A {
1N/A if (isdigit(*++s))
1N/A {
1N/A now = strtoull(s, &t, 0);
1N/A sns:
1N/A if (*(s = t) == '.')
1N/A {
1N/A fix = 0;
1N/A m = 1000000000;
1N/A while (isdigit(*++s))
1N/A fix += (*s - '0') * (m /= 10);
1N/A now = tmxsns(now, fix);
1N/A }
1N/A else if (now <= 0x7fffffff)
1N/A now = tmxsns(now, 0);
1N/A goto reset;
1N/A }
1N/A else if (*s++ == '#')
1N/A {
1N/A now = tmxtime(tm, zone);
1N/A goto reset;
1N/A }
1N/A break;
1N/A }
1N/A if ((*s == 'P' || *s == 'p') && (!isalpha(*(s + 1)) || (*(s + 1) == 'T' || *(s + 1) == 't') && !isalpha(*(s + 2))))
1N/A {
1N/A Tm_t otm;
1N/A
1N/A /*
1N/A * iso duration
1N/A */
1N/A
1N/A otm = *tm;
1N/A t = (char*)s;
1N/A m = 0;
1N/A P_INIT('Y');
1N/A do
1N/A {
1N/A c = *++s;
1N/A duration_next:
1N/A switch (c)
1N/A {
1N/A case 0:
1N/A m++;
1N/A if ((char*)s > u)
1N/A {
1N/A s--;
1N/A c = '_';
1N/A goto duration_next;
1N/A }
1N/A break;
1N/A case 'T':
1N/A case 't':
1N/A m++;
1N/A if ((char*)s > u)
1N/A {
1N/A s++;
1N/A c = 'D';
1N/A goto duration_next;
1N/A }
1N/A continue;
1N/A case 'Y':
1N/A case 'y':
1N/A m = 0;
1N/A if (q > 1)
1N/A tm->tm_sec += (365L*24L*60L*60L) * p / q;
1N/A else
1N/A tm->tm_year += p;
1N/A P_INIT('M');
1N/A continue;
1N/A case 'm':
1N/A if (!m)
1N/A m = 1;
1N/A /*FALLTHROUGH*/
1N/A case 'M':
1N/A switch (*(s + 1))
1N/A {
1N/A case 'I':
1N/A case 'i':
1N/A s++;
1N/A m = 1;
1N/A w = 'S';
1N/A break;
1N/A case 'O':
1N/A case 'o':
1N/A s++;
1N/A m = 0;
1N/A w = 'H';
1N/A break;
1N/A case 'S':
1N/A case 's':
1N/A s++;
1N/A m = 2;
1N/A w = 's';
1N/A break;
1N/A }
1N/A switch (m)
1N/A {
1N/A case 0:
1N/A m = 1;
1N/A if (q > 1)
1N/A tm->tm_sec += (3042L*24L*60L*60L) * p / q / 100L;
1N/A else
1N/A tm->tm_mon += p;
1N/A break;
1N/A case 1:
1N/A m = 2;
1N/A if (q > 1)
1N/A tm->tm_sec += (60L) * p / q;
1N/A else
1N/A tm->tm_min += p;
1N/A break;
1N/A default:
1N/A if (q > 1)
1N/A powerize(tm, p, q, 1000UL);
1N/A else
1N/A tm->tm_nsec += p * 1000000L;
1N/A break;
1N/A }
1N/A P_INIT(w);
1N/A continue;
1N/A case 'W':
1N/A case 'w':
1N/A m = 0;
1N/A if (q > 1)
1N/A tm->tm_sec += (7L*24L*60L*60L) * p / q;
1N/A else
1N/A tm->tm_mday += 7 * p;
1N/A P_INIT('D');
1N/A continue;
1N/A case 'D':
1N/A case 'd':
1N/A m = 0;
1N/A if (q > 1)
1N/A tm->tm_sec += (24L*60L*60L) * p / q;
1N/A else
1N/A tm->tm_mday += p;
1N/A P_INIT('H');
1N/A continue;
1N/A case 'H':
1N/A case 'h':
1N/A m = 1;
1N/A if (q > 1)
1N/A tm->tm_sec += (60L*60L) * p / q;
1N/A else
1N/A tm->tm_hour += p;
1N/A P_INIT('m');
1N/A continue;
1N/A case 'S':
1N/A case 's':
1N/A m = 2;
1N/A /*FALLTHROUGH*/
1N/A case ' ':
1N/A case '_':
1N/A case '\n':
1N/A case '\r':
1N/A case '\t':
1N/A case '\v':
1N/A if (q > 1)
1N/A powerize(tm, p, q, 1000000000UL);
1N/A else
1N/A tm->tm_sec += p;
1N/A P_INIT('U');
1N/A continue;
1N/A case 'U':
1N/A case 'u':
1N/A switch (*(s + 1))
1N/A {
1N/A case 'S':
1N/A case 's':
1N/A s++;
1N/A break;
1N/A }
1N/A m = 0;
1N/A if (q > 1)
1N/A powerize(tm, p, q, 1000000UL);
1N/A else
1N/A tm->tm_nsec += p * 1000L;
1N/A P_INIT('N');
1N/A continue;
1N/A case 'N':
1N/A case 'n':
1N/A switch (*(s + 1))
1N/A {
1N/A case 'S':
1N/A case 's':
1N/A s++;
1N/A break;
1N/A }
1N/A m = 0;
1N/A if (q > 1)
1N/A powerize(tm, p, q, 1000000000UL);
1N/A else
1N/A tm->tm_nsec += p;
1N/A P_INIT('Y');
1N/A continue;
1N/A case '.':
1N/A if (q)
1N/A goto exact;
1N/A q = 1;
1N/A continue;
1N/A case '-':
1N/A c = 'M';
1N/A u = (char*)s++;
1N/A while (*++u && *u != ':')
1N/A if (*u == '-')
1N/A {
1N/A c = 'Y';
1N/A break;
1N/A }
1N/A goto duration_next;
1N/A case ':':
1N/A c = 'm';
1N/A u = (char*)s++;
1N/A while (*++u)
1N/A if (*u == ':')
1N/A {
1N/A c = 'H';
1N/A break;
1N/A }
1N/A goto duration_next;
1N/A case '0':
1N/A case '1':
1N/A case '2':
1N/A case '3':
1N/A case '4':
1N/A case '5':
1N/A case '6':
1N/A case '7':
1N/A case '8':
1N/A case '9':
1N/A q *= 10;
1N/A p = p * 10 + (c - '0');
1N/A continue;
1N/A default:
1N/A exact:
1N/A *tm = otm;
1N/A s = (const char*)t + 1;
1N/A if (*t == 'p')
1N/A {
1N/A state |= HOLD|EXACT;
1N/A set &= ~(EXACT|LAST|NEXT|THIS);
1N/A set |= state & (EXACT|LAST|NEXT|THIS);
1N/A }
1N/A goto again;
1N/A }
1N/A break;
1N/A } while (c);
1N/A continue;
1N/A }
1N/A f = -1;
1N/A if (*s == '+')
1N/A {
1N/A while (isspace(*++s) || *s == '_');
1N/A n = strtol(s, &t, 0);
1N/A if (w = t - s)
1N/A {
1N/A for (s = t; skip[*s]; s++);
1N/A state |= (f = n) ? NEXT : THIS;
1N/A set &= ~(EXACT|LAST|NEXT|THIS);
1N/A set |= state & (EXACT|LAST|NEXT|THIS);
1N/A }
1N/A else
1N/A s = last;
1N/A }
1N/A if (!(state & CRON))
1N/A {
1N/A /*
1N/A * check for cron date
1N/A *
1N/A * min hour day-of-month month day-of-week
1N/A *
1N/A * if it's cron then determine the next time
1N/A * that satisfies the specification
1N/A *
1N/A * NOTE: the only spacing is ' '||'_'||';'
1N/A */
1N/A
1N/A i = 0;
1N/A n = *(t = (char*)s);
1N/A for (;;)
1N/A {
1N/A if (n == '*')
1N/A n = *++s;
1N/A else if (!isdigit(n))
1N/A break;
1N/A else
1N/A while ((n = *++s) == ',' || n == '-' || n == '/' || isdigit(n));
1N/A if (n != ' ' && n != '_' && n != ';')
1N/A {
1N/A if (!n)
1N/A i++;
1N/A break;
1N/A }
1N/A i++;
1N/A while ((n = *++s) == ' ' || n == '_');
1N/A }
1N/A if (i == 5)
1N/A {
1N/A Time_t tt;
1N/A char hit[60];
1N/A char mon[13];
1N/A char day[7];
1N/A
1N/A state |= CRON;
1N/A flags = 0;
1N/A tm->tm_sec = 0;
1N/A tm->tm_min++;
1N/A tmfix(tm);
1N/A
1N/A /*
1N/A * minute
1N/A */
1N/A
1N/A if ((k = range(t, &t, hit, 0, 59)) < 0)
1N/A break;
1N/A if (k && !hit[i = tm->tm_min])
1N/A {
1N/A hit[i] = 1;
1N/A do if (++i > 59)
1N/A {
1N/A i = 0;
1N/A if (++tm->tm_hour > 59)
1N/A {
1N/A tm->tm_min = i;
1N/A tmfix(tm);
1N/A }
1N/A } while (!hit[i]);
1N/A tm->tm_min = i;
1N/A }
1N/A
1N/A /*
1N/A * hour
1N/A */
1N/A
1N/A if ((k = range(t, &t, hit, 0, 23)) < 0)
1N/A break;
1N/A if (k && !hit[i = tm->tm_hour])
1N/A {
1N/A hit[i] = 1;
1N/A do if (++i > 23)
1N/A {
1N/A i = 0;
1N/A if (++tm->tm_mday > 28)
1N/A {
1N/A tm->tm_hour = i;
1N/A tmfix(tm);
1N/A }
1N/A } while (!hit[i]);
1N/A tm->tm_hour = i;
1N/A }
1N/A
1N/A /*
1N/A * day of month
1N/A */
1N/A
1N/A if ((k = range(t, &t, hit, 1, 31)) < 0)
1N/A break;
1N/A if (k)
1N/A flags |= DAY|MDAY;
1N/A
1N/A /*
1N/A * month
1N/A */
1N/A
1N/A if ((k = range(t, &t, mon, 1, 12)) < 0)
1N/A break;
1N/A if (k)
1N/A flags |= MONTH;
1N/A else
1N/A for (i = 1; i <= 12; i++)
1N/A mon[i] = 1;
1N/A
1N/A /*
1N/A * day of week
1N/A */
1N/A
1N/A if ((k = range(t, &t, day, 0, 6)) < 0)
1N/A break;
1N/A if (k)
1N/A flags |= WDAY;
1N/A s = t;
1N/A if (flags & (MONTH|MDAY|WDAY))
1N/A {
1N/A fix = tmxtime(tm, zone);
1N/A tm = tmxtm(tm, fix, tm->tm_zone);
1N/A i = tm->tm_mon + 1;
1N/A j = tm->tm_mday;
1N/A k = tm->tm_wday;
1N/A for (;;)
1N/A {
1N/A if (!mon[i])
1N/A {
1N/A if (++i > 12)
1N/A {
1N/A i = 1;
1N/A tm->tm_year++;
1N/A }
1N/A tm->tm_mon = i - 1;
1N/A tm->tm_mday = 1;
1N/A tt = tmxtime(tm, zone);
1N/A if (tt < fix)
1N/A goto done;
1N/A tm = tmxtm(tm, tt, tm->tm_zone);
1N/A i = tm->tm_mon + 1;
1N/A j = tm->tm_mday;
1N/A k = tm->tm_wday;
1N/A continue;
1N/A }
1N/A if (flags & (MDAY|WDAY))
1N/A {
1N/A if ((flags & (MDAY|WDAY)) == (MDAY|WDAY))
1N/A {
1N/A if (hit[j] && day[k])
1N/A break;
1N/A }
1N/A else if ((flags & MDAY) && hit[j])
1N/A break;
1N/A else if ((flags & WDAY) && day[k])
1N/A break;
1N/A if (++j > 28)
1N/A {
1N/A tm->tm_mon = i - 1;
1N/A tm->tm_mday = j;
1N/A tm = tmxtm(tm, tmxtime(tm, zone), tm->tm_zone);
1N/A i = tm->tm_mon + 1;
1N/A j = tm->tm_mday;
1N/A k = tm->tm_wday;
1N/A }
1N/A else if ((flags & WDAY) && ++k > 6)
1N/A k = 0;
1N/A }
1N/A else if (flags & MONTH)
1N/A break;
1N/A }
1N/A tm->tm_mon = i - 1;
1N/A tm->tm_mday = j;
1N/A tm->tm_wday = k;
1N/A }
1N/A continue;
1N/A }
1N/A s = t;
1N/A }
1N/A n = -1;
1N/A if (isdigit(*s))
1N/A {
1N/A n = strtol(s, &t, 10);
1N/A if ((w = t - s) && *t == '.' && isdigit(*(t + 1)) && isdigit(*(t + 2)) && isdigit(*(t + 3)))
1N/A {
1N/A now = n;
1N/A goto sns;
1N/A }
1N/A if ((*t == 'T' || *t == 't') && ((set|state) & (YEAR|MONTH|DAY)) == (YEAR|MONTH) && isdigit(*(t + 1)))
1N/A t++;
1N/A u = t + (*t == '-');
1N/A if ((w == 2 || w == 4) && (*u == 'W' || *u == 'w') && isdigit(*(u + 1)))
1N/A {
1N/A if (w == 4)
1N/A {
1N/A if ((n -= 1900) < TM_WINDOW)
1N/A break;
1N/A }
1N/A else if (n < TM_WINDOW)
1N/A n += 100;
1N/A m = n;
1N/A n = strtol(++u, &t, 10);
1N/A if ((i = (t - u)) < 2 || i > 3)
1N/A break;
1N/A if (i == 3)
1N/A {
1N/A k = n % 10;
1N/A n /= 10;
1N/A }
1N/A else if (*t != '-')
1N/A k = 1;
1N/A else if (*++t && dig1(t, k) < 1 || k > 7)
1N/A break;
1N/A if (n < 0 || n > 53)
1N/A break;
1N/A if (k == 7)
1N/A k = 0;
1N/A tm->tm_year = m;
1N/A tmweek(tm, 2, n, k);
1N/A set |= YEAR|MONTH|DAY;
1N/A s = t;
1N/A continue;
1N/A }
1N/A else if (w == 6 || w == 8 && (n / 1000000) > 12)
1N/A {
1N/A t = (char*)s;
1N/A flags = 0;
1N/A if (w == 8 || w == 6 && *u != 'T' && *u != 't')
1N/A {
1N/A dig4(t, m);
1N/A if ((m -= 1900) < TM_WINDOW)
1N/A break;
1N/A }
1N/A else
1N/A {
1N/A dig2(t, m);
1N/A if (m < TM_WINDOW)
1N/A m += 100;
1N/A }
1N/A flags |= YEAR;
1N/A if (dig2(t, l) <= 0 || l > 12)
1N/A break;
1N/A flags |= MONTH;
1N/A if (*t != 'T' && *t != 't' || !isdigit(*++t))
1N/A {
1N/A if (w == 6)
1N/A goto save_yymm;
1N/A if (dig2(t, k) < 1 || k > 31)
1N/A break;
1N/A flags |= DAY;
1N/A goto save_yymmdd;
1N/A }
1N/A n = strtol(s = t, &t, 0);
1N/A if ((t - s) < 2)
1N/A break;
1N/A if (dig2(s, j) > 24)
1N/A break;
1N/A if ((t - s) < 2)
1N/A {
1N/A if ((t - s) == 1 || *t++ != '-')
1N/A break;
1N/A n = strtol(s = t, &t, 0);
1N/A if ((t - s) < 2)
1N/A break;
1N/A }
1N/A if (dig2(s, i) > 59)
1N/A break;
1N/A flags |= HOUR|MINUTE;
1N/A if ((t - s) == 2)
1N/A {
1N/A if (dig2(s, n) > (59 + TM_MAXLEAP))
1N/A break;
1N/A flags |= SECOND;
1N/A }
1N/A else if (t - s)
1N/A break;
1N/A else
1N/A n = 0;
1N/A p = 0;
1N/A if (*t == '.')
1N/A {
1N/A q = 1000000000;
1N/A while (isdigit(*++t))
1N/A p += (*t - '0') * (q /= 10);
1N/A set |= NSEC;
1N/A }
1N/A if (n > (59 + TM_MAXLEAP))
1N/A break;
1N/A goto save;
1N/A }
1N/A else if (f == -1 && isalpha(*t) && tmlex(t, &t, tm_info.format + TM_ORDINAL, TM_ORDINALS - TM_ORDINAL, NiL, 0) >= 0)
1N/A {
1N/A message((-1, "AHA#%d n=%d", __LINE__, n));
1N/A ordinal:
1N/A if (n)
1N/A n--;
1N/A message((-1, "AHA#%d n=%d", __LINE__, n));
1N/A state |= ((f = n) ? NEXT : THIS)|ORDINAL;
1N/A set &= ~(EXACT|LAST|NEXT|THIS);
1N/A set |= state & (EXACT|LAST|NEXT|THIS);
1N/A for (s = t; skip[*s]; s++);
1N/A if (isdigit(*s))
1N/A {
1N/A if (n = strtol(s, &t, 10))
1N/A n--;
1N/A s = t;
1N/A if (*s == '_')
1N/A s++;
1N/A }
1N/A else
1N/A n = -1;
1N/A dir = f;
1N/A message((-1, "AHA#%d f=%d n=%d state=" FFMT, __LINE__, f, n, FLAGS(state)));
1N/A }
1N/A else
1N/A {
1N/A 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))
1N/A {
1N/A /*
1N/A * various { date(1) touch(1) } formats
1N/A *
1N/A * [[cc]yy[mm]]ddhhmm[.ss[.nn...]]
1N/A * [cc]yyjjj
1N/A * hhmm[.ss[.nn...]]
1N/A */
1N/A
1N/A flags = 0;
1N/A if (state & CCYYMMDDHHMMSS)
1N/A break;
1N/A state |= CCYYMMDDHHMMSS;
1N/A p = 0;
1N/A if ((i == 7 || i == 5) && (!*t || *t == 'Z' || *t == 'z'))
1N/A {
1N/A if (i == 7)
1N/A {
1N/A dig4(s, m);
1N/A if ((m -= 1900) < TM_WINDOW)
1N/A break;
1N/A }
1N/A else if (dig2(s, m) < TM_WINDOW)
1N/A m += 100;
1N/A dig3(s, k);
1N/A l = 1;
1N/A j = 0;
1N/A i = 0;
1N/A n = 0;
1N/A flags |= MONTH;
1N/A }
1N/A else if (i & 1)
1N/A break;
1N/A else
1N/A {
1N/A u = t;
1N/A if (i == 12)
1N/A {
1N/A x = s;
1N/A dig2(x, m);
1N/A if (m <= 12)
1N/A {
1N/A u -= 4;
1N/A i -= 4;
1N/A x = s + 8;
1N/A dig4(x, m);
1N/A }
1N/A else
1N/A dig4(s, m);
1N/A if (m < 1969 || m >= 3000)
1N/A break;
1N/A m -= 1900;
1N/A }
1N/A else if (i == 10)
1N/A {
1N/A x = s;
1N/A 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))
1N/A dig2(s, m);
1N/A else
1N/A {
1N/A u -= 2;
1N/A i -= 2;
1N/A x = s + 8;
1N/A dig2(x, m);
1N/A }
1N/A if (m < TM_WINDOW)
1N/A m += 100;
1N/A }
1N/A else
1N/A m = tm->tm_year;
1N/A if ((u - s) < 8)
1N/A l = tm->tm_mon + 1;
1N/A else if (dig2(s, l) <= 0 || l > 12)
1N/A break;
1N/A else
1N/A flags |= MONTH;
1N/A if ((u - s) < 6)
1N/A k = tm->tm_mday;
1N/A else if (dig2(s, k) < 1 || k > 31)
1N/A break;
1N/A else
1N/A flags |= DAY;
1N/A if ((u - s) < 4)
1N/A break;
1N/A if (dig2(s, j) > 24)
1N/A break;
1N/A if (dig2(s, i) > 59)
1N/A break;
1N/A flags |= HOUR|MINUTE;
1N/A if ((u - s) == 2)
1N/A {
1N/A dig2(s, n);
1N/A flags |= SECOND;
1N/A }
1N/A else if (u - s)
1N/A break;
1N/A else if (*t != '.')
1N/A n = 0;
1N/A else
1N/A {
1N/A n = strtol(t + 1, &t, 10);
1N/A flags |= SECOND;
1N/A if (*t == '.')
1N/A {
1N/A q = 1000000000;
1N/A while (isdigit(*++t))
1N/A p += (*t - '0') * (q /= 10);
1N/A set |= NSEC;
1N/A }
1N/A }
1N/A if (n > (59 + TM_MAXLEAP))
1N/A break;
1N/A }
1N/A save:
1N/A tm->tm_hour = j;
1N/A tm->tm_min = i;
1N/A tm->tm_sec = n;
1N/A tm->tm_nsec = p;
1N/A save_yymmdd:
1N/A tm->tm_mday = k;
1N/A save_yymm:
1N/A tm->tm_mon = l - 1;
1N/A tm->tm_year = m;
1N/A s = t;
1N/A set |= flags;
1N/A continue;
1N/A }
1N/A for (s = t; skip[*s]; s++);
1N/A if (*s == ':' || *s == '.' && ((set|state) & (YEAR|MONTH|DAY|HOUR)) == (YEAR|MONTH|DAY))
1N/A {
1N/A c = *s;
1N/A if ((state & HOUR) || n > 24)
1N/A break;
1N/A while (isspace(*++s) || *s == '_');
1N/A if (!isdigit(*s))
1N/A break;
1N/A i = n;
1N/A n = strtol(s, &t, 10);
1N/A for (s = t; isspace(*s) || *s == '_'; s++);
1N/A if (n > 59)
1N/A break;
1N/A j = n;
1N/A m = 0;
1N/A if (*s == c)
1N/A {
1N/A while (isspace(*++s) || *s == '_');
1N/A if (!isdigit(*s))
1N/A break;
1N/A n = strtol(s, &t, 10);
1N/A s = t;
1N/A if (n > (59 + TM_MAXLEAP))
1N/A break;
1N/A set |= SECOND;
1N/A while (isspace(*s))
1N/A s++;
1N/A if (*s == '.')
1N/A {
1N/A q = 1000000000;
1N/A while (isdigit(*++s))
1N/A m += (*s - '0') * (q /= 10);
1N/A set |= NSEC;
1N/A }
1N/A }
1N/A else
1N/A n = 0;
1N/A set |= HOUR|MINUTE;
1N/A skip[':'] = 1;
1N/A k = tm->tm_hour;
1N/A tm->tm_hour = i;
1N/A l = tm->tm_min;
1N/A tm->tm_min = j;
1N/A tm->tm_sec = n;
1N/A tm->tm_nsec = m;
1N/A while (isspace(*s))
1N/A s++;
1N/A switch (tmlex(s, &t, tm_info.format, TM_NFORM, tm_info.format + TM_MERIDIAN, 2))
1N/A {
1N/A case TM_MERIDIAN:
1N/A s = t;
1N/A if (i == 12)
1N/A tm->tm_hour = i = 0;
1N/A break;
1N/A case TM_MERIDIAN+1:
1N/A if (i < 12)
1N/A tm->tm_hour = i += 12;
1N/A break;
1N/A }
1N/A if (f >= 0 || (state & (LAST|NEXT)))
1N/A {
1N/A message((-1, "AHA#%d f=%d i=%d j=%d k=%d l=%d", __LINE__, f, i, j, k, l));
1N/A state &= ~HOLD;
1N/A if (f < 0)
1N/A {
1N/A if (state & LAST)
1N/A f = -1;
1N/A else if (state & NEXT)
1N/A f = 1;
1N/A else
1N/A f = 0;
1N/A }
1N/A if (f > 0)
1N/A {
1N/A if (i > k || i == k && j > l)
1N/A f--;
1N/A }
1N/A else if (i < k || i == k && j < l)
1N/A f++;
1N/A if (f > 0)
1N/A {
1N/A tm->tm_hour += f * 24;
1N/A while (tm->tm_hour >= 24)
1N/A {
1N/A tm->tm_hour -= 24;
1N/A tm->tm_mday++;
1N/A }
1N/A }
1N/A else if (f < 0)
1N/A {
1N/A tm->tm_hour += f * 24;
1N/A while (tm->tm_hour < 24)
1N/A {
1N/A tm->tm_hour += 24;
1N/A tm->tm_mday--;
1N/A }
1N/A }
1N/A }
1N/A continue;
1N/A }
1N/A }
1N/A }
1N/A for (;;)
1N/A {
1N/A if (*s == '-' || *s == '+')
1N/A {
1N/A 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))))
1N/A break;
1N/A s++;
1N/A }
1N/A else if (skip[*s])
1N/A s++;
1N/A else
1N/A break;
1N/A }
1N/A if (isalpha(*s))
1N/A {
1N/A if (n > 0)
1N/A {
1N/A x = s;
1N/A q = *s++;
1N/A if (isalpha(*s))
1N/A {
1N/A q <<= 8;
1N/A q |= *s++;
1N/A if (isalpha(*s))
1N/A {
1N/A if (tmlex(s, &t, tm_info.format + TM_SUFFIXES, TM_PARTS - TM_SUFFIXES, NiL, 0) >= 0)
1N/A s = t;
1N/A if (isalpha(*s))
1N/A {
1N/A q <<= 8;
1N/A q |= *s++;
1N/A if (isalpha(*s))
1N/A {
1N/A q <<= 8;
1N/A q |= *s++;
1N/A if (isalpha(*s))
1N/A q = 0;
1N/A }
1N/A }
1N/A }
1N/A }
1N/A switch (q)
1N/A {
1N/A case K1('y'):
1N/A case K1('Y'):
1N/A case K2('y','r'):
1N/A case K2('Y','R'):
1N/A tm->tm_year += n;
1N/A set |= YEAR;
1N/A continue;
1N/A case K1('M'):
1N/A case K2('m','o'):
1N/A case K2('M','O'):
1N/A tm->tm_mon += n;
1N/A set |= MONTH;
1N/A continue;
1N/A case K1('w'):
1N/A case K1('W'):
1N/A case K2('w','k'):
1N/A case K2('W','K'):
1N/A tm->tm_mday += n * 7;
1N/A set |= DAY;
1N/A continue;
1N/A case K1('d'):
1N/A case K1('D'):
1N/A case K2('d','a'):
1N/A case K2('d','y'):
1N/A case K2('D','A'):
1N/A case K2('D','Y'):
1N/A tm->tm_mday += n;
1N/A set |= DAY;
1N/A continue;
1N/A case K1('h'):
1N/A case K1('H'):
1N/A case K2('h','r'):
1N/A case K2('H','R'):
1N/A tm->tm_hour += n;
1N/A set |= HOUR;
1N/A continue;
1N/A case K1('m'):
1N/A case K2('m','n'):
1N/A case K2('M','N'):
1N/A tm->tm_min += n;
1N/A set |= MINUTE;
1N/A continue;
1N/A case K1('s'):
1N/A case K2('s','c'):
1N/A case K1('S'):
1N/A case K2('S','C'):
1N/A tm->tm_sec += n;
1N/A set |= SECOND;
1N/A continue;
1N/A case K2('m','s'):
1N/A case K3('m','s','c'):
1N/A case K4('m','s','e','c'):
1N/A case K2('M','S'):
1N/A case K3('M','S','C'):
1N/A case K4('M','S','E','C'):
1N/A tm->tm_nsec += n * 1000000L;
1N/A continue;
1N/A case K1('u'):
1N/A case K2('u','s'):
1N/A case K3('u','s','c'):
1N/A case K4('u','s','e','c'):
1N/A case K1('U'):
1N/A case K2('U','S'):
1N/A case K3('U','S','C'):
1N/A case K4('U','S','E','C'):
1N/A tm->tm_nsec += n * 1000L;
1N/A continue;
1N/A case K2('n','s'):
1N/A case K3('n','s','c'):
1N/A case K4('n','s','e','c'):
1N/A case K2('N','S'):
1N/A case K3('N','S','C'):
1N/A case K4('N','S','E','C'):
1N/A tm->tm_nsec += n;
1N/A continue;
1N/A }
1N/A s = x;
1N/A }
1N/A if (n < 1000)
1N/A {
1N/A if ((j = tmlex(s, &t, tm_info.format, TM_NFORM, tm_info.format + TM_SUFFIXES, TM_PARTS - TM_SUFFIXES)) >= 0)
1N/A {
1N/A s = t;
1N/A switch (tm_data.lex[j])
1N/A {
1N/A case TM_EXACT:
1N/A state |= HOLD|EXACT;
1N/A set &= ~(EXACT|LAST|NEXT|THIS);
1N/A set |= state & (EXACT|LAST|NEXT|THIS);
1N/A continue;
1N/A case TM_LAST:
1N/A state |= HOLD|LAST;
1N/A set &= ~(EXACT|LAST|NEXT|THIS);
1N/A set |= state & (EXACT|LAST|NEXT|THIS);
1N/A continue;
1N/A case TM_THIS:
1N/A state |= HOLD|THIS;
1N/A set &= ~(EXACT|LAST|NEXT|THIS);
1N/A set |= state & (EXACT|LAST|NEXT|THIS);
1N/A n = 0;
1N/A continue;
1N/A case TM_NEXT:
1N/A /*
1N/A * disambiguate english "last ... in"
1N/A */
1N/A
1N/A if (!((state|set) & LAST))
1N/A {
1N/A state |= HOLD|NEXT;
1N/A set &= ~(EXACT|LAST|NEXT|THIS);
1N/A set |= state & (EXACT|LAST|NEXT|THIS);
1N/A continue;
1N/A }
1N/A /*FALLTHROUGH*/
1N/A case TM_FINAL:
1N/A state |= HOLD|THIS|FINAL;
1N/A set &= ~(EXACT|LAST|NEXT|THIS);
1N/A set |= state & (EXACT|LAST|NEXT|THIS|FINAL);
1N/A continue;
1N/A case TM_WORK:
1N/A message((-1, "AHA#%d WORK", __LINE__));
1N/A state |= WORK;
1N/A set |= DAY;
1N/A if (state & LAST)
1N/A {
1N/A state &= ~LAST;
1N/A set &= ~LAST;
1N/A state |= FINAL;
1N/A set |= FINAL;
1N/A }
1N/A goto clear_hour;
1N/A case TM_ORDINAL:
1N/A j += TM_ORDINALS - TM_ORDINAL;
1N/A message((-1, "AHA#%d j=%d", __LINE__, j));
1N/A /*FALLTHROUGH*/
1N/A case TM_ORDINALS:
1N/A n = j - TM_ORDINALS + 1;
1N/A message((-1, "AHA#%d n=%d", __LINE__, n));
1N/A goto ordinal;
1N/A case TM_MERIDIAN:
1N/A if (f >= 0)
1N/A f++;
1N/A else if (state & LAST)
1N/A f = -1;
1N/A else if (state & THIS)
1N/A f = 1;
1N/A else if (state & NEXT)
1N/A f = 2;
1N/A else
1N/A f = 0;
1N/A if (n > 0)
1N/A {
1N/A if (n > 24)
1N/A goto done;
1N/A tm->tm_hour = n;
1N/A }
1N/A for (k = tm->tm_hour; k < 0; k += 24);
1N/A k %= 24;
1N/A if (j == TM_MERIDIAN)
1N/A {
1N/A if (k == 12)
1N/A tm->tm_hour -= 12;
1N/A }
1N/A else if (k < 12)
1N/A tm->tm_hour += 12;
1N/A if (n > 0)
1N/A goto clear_min;
1N/A continue;
1N/A case TM_DAY_ABBREV:
1N/A j += TM_DAY - TM_DAY_ABBREV;
1N/A /*FALLTHROUGH*/
1N/A case TM_DAY:
1N/A case TM_PARTS:
1N/A case TM_HOURS:
1N/A state |= set & (EXACT|LAST|NEXT|THIS);
1N/A if (!(state & (LAST|NEXT|THIS)))
1N/A for (;;)
1N/A {
1N/A while (skip[*s])
1N/A s++;
1N/A if ((k = tmlex(s, &t, tm_info.format + TM_LAST, TM_NOISE - TM_LAST, NiL, 0)) >= 0)
1N/A {
1N/A s = t;
1N/A if (k <= 2)
1N/A state |= LAST;
1N/A else if (k <= 5)
1N/A state |= THIS;
1N/A else if (k <= 8)
1N/A state |= NEXT;
1N/A else
1N/A state |= EXACT;
1N/A }
1N/A else
1N/A {
1N/A state |= (n > 0) ? NEXT : THIS;
1N/A break;
1N/A }
1N/A set &= ~(EXACT|LAST|NEXT|THIS);
1N/A set |= state & (EXACT|LAST|NEXT|THIS);
1N/A }
1N/A /*FALLTHROUGH*/
1N/A case TM_DAYS:
1N/A message((-1, "AHA#%d n=%d j=%d f=%d state=" FFMT, __LINE__, n, j, f, FLAGS(state)));
1N/A if (n == -1)
1N/A {
1N/A /*
1N/A * disambiguate english "second"
1N/A */
1N/A
1N/A if (j == TM_PARTS && f == -1)
1N/A {
1N/A state &= ~(LAST|NEXT|THIS|ORDINAL); /*AHA*/
1N/A n = 2;
1N/A goto ordinal;
1N/A }
1N/A n = 1;
1N/A }
1N/A
1N/A /*
1N/A * disambiguate "last" vs. { "previous" "final" }
1N/A */
1N/A
1N/A while (isspace(*s))
1N/A s++;
1N/A message((-1, "AHA#%d disambiguate LAST s='%s'", __LINE__, s));
1N/A 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)
1N/A {
1N/A s = t;
1N/A if (state & LAST)
1N/A {
1N/A state &= ~LAST;
1N/A set &= ~LAST;
1N/A state |= FINAL;
1N/A set |= FINAL;
1N/A message((-1, "AHA#%d LAST => FINAL", __LINE__));
1N/A }
1N/A else
1N/A state &= ~(THIS|NEXT);
1N/A }
1N/A message((-1, "AHA#%d disambiguate LAST k=%d", __LINE__, k));
1N/A if (state & LAST)
1N/A n = -n;
1N/A else if (!(state & NEXT))
1N/A n--;
1N/A m = (f > 0) ? f * n : n;
1N/A 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)));
1N/A switch (j)
1N/A {
1N/A case TM_DAYS+0:
1N/A tm->tm_mday--;
1N/A set |= DAY;
1N/A goto clear_hour;
1N/A case TM_DAYS+1:
1N/A set |= DAY;
1N/A goto clear_hour;
1N/A case TM_DAYS+2:
1N/A tm->tm_mday++;
1N/A set |= DAY;
1N/A goto clear_hour;
1N/A case TM_PARTS+0:
1N/A set |= SECOND;
1N/A if ((m < 0 ? -m : m) > (365L*24L*60L*60L))
1N/A {
1N/A now = tmxtime(tm, zone) + tmxsns(m, 0);
1N/A goto reset;
1N/A }
1N/A tm->tm_sec += m;
1N/A goto clear_nsec;
1N/A case TM_PARTS+1:
1N/A tm->tm_min += m;
1N/A set |= MINUTE;
1N/A goto clear_sec;
1N/A case TM_PARTS+2:
1N/A tm->tm_hour += m;
1N/A set |= MINUTE;
1N/A goto clear_min;
1N/A case TM_PARTS+3:
1N/A message((-1, "AHA#%d DAY m=%d n=%d%s", __LINE__, m, n, (state & LAST) ? " LAST" : ""));
1N/A if ((state & (LAST|NEXT|THIS)) == LAST)
1N/A tm->tm_mday = tm_data.days[tm->tm_mon] + (tm->tm_mon == 1 && tmisleapyear(tm->tm_year));
1N/A else if (state & ORDINAL)
1N/A tm->tm_mday = m + 1;
1N/A else
1N/A tm->tm_mday += m;
1N/A if (!(set & (FINAL|WORK)))
1N/A set |= HOUR;
1N/A goto clear_hour;
1N/A case TM_PARTS+4:
1N/A tm = tmxtm(tm, tmxtime(tm, zone), tm->tm_zone);
1N/A tm->tm_mday += 7 * m - tm->tm_wday + 1;
1N/A set |= DAY;
1N/A goto clear_hour;
1N/A case TM_PARTS+5:
1N/A tm->tm_mon += m;
1N/A set |= MONTH;
1N/A goto clear_mday;
1N/A case TM_PARTS+6:
1N/A tm->tm_year += m;
1N/A goto clear_mon;
1N/A case TM_HOURS+0:
1N/A tm->tm_mday += m;
1N/A set |= DAY;
1N/A goto clear_hour;
1N/A case TM_HOURS+1:
1N/A tm->tm_mday += m;
1N/A tm->tm_hour = 6;
1N/A set |= HOUR;
1N/A goto clear_min;
1N/A case TM_HOURS+2:
1N/A tm->tm_mday += m;
1N/A tm->tm_hour = 12;
1N/A set |= HOUR;
1N/A goto clear_min;
1N/A case TM_HOURS+3:
1N/A tm->tm_mday += m;
1N/A tm->tm_hour = 18;
1N/A set |= HOUR;
1N/A goto clear_min;
1N/A }
1N/A if (m >= 0 && (state & ORDINAL))
1N/A tm->tm_mday = 1;
1N/A tm = tmxtm(tm, tmxtime(tm, zone), tm->tm_zone);
1N/A day = j -= TM_DAY;
1N/A if (!dir)
1N/A dir = m;
1N/A message((-1, "AHA#%d j=%d m=%d", __LINE__, j, m));
1N/A j -= tm->tm_wday;
1N/A 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));
1N/A if (state & (LAST|NEXT|THIS))
1N/A {
1N/A if (state & ORDINAL)
1N/A {
1N/A while (isspace(*s))
1N/A s++;
1N/A if (isdigit(*s) || tmlex(s, &t, tm_info.format, TM_DAY_ABBREV, NiL, 0) >= 0)
1N/A {
1N/A state &= ~(LAST|NEXT|THIS);
1N/A goto clear_hour;
1N/A }
1N/A }
1N/A if (j < 0)
1N/A j += 7;
1N/A }
1N/A else if (j > 0)
1N/A j -= 7;
1N/A 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)));
1N/A set |= DAY;
1N/A if (set & (FINAL|WORK))
1N/A goto clear_hour;
1N/A else if (state & (LAST|NEXT|THIS))
1N/A {
1N/A if (f >= 0)
1N/A day = -1;
1N/A else if (m > 0 && (state & (NEXT|YEAR|MONTH)) == NEXT && j >= 0)
1N/A m--;
1N/A tm->tm_mday += j + m * 7;
1N/A set &= ~(LAST|NEXT|THIS|ORDINAL); /*AHA*/
1N/A state &= ~(LAST|NEXT|THIS|ORDINAL); /*AHA*/
1N/A if (!(state & EXACT))
1N/A goto clear_hour;
1N/A }
1N/A continue;
1N/A case TM_MONTH_ABBREV:
1N/A j += TM_MONTH - TM_MONTH_ABBREV;
1N/A /*FALLTHROUGH*/
1N/A case TM_MONTH:
1N/A if (state & MONTH)
1N/A goto done;
1N/A state |= MONTH;
1N/A i = tm->tm_mon;
1N/A tm->tm_mon = j - TM_MONTH;
1N/A if (n < 0)
1N/A {
1N/A while (skip[*s])
1N/A s++;
1N/A if (isdigit(*s))
1N/A {
1N/A n = strtol(s, &t, 10);
1N/A if (n <= 31 && *t != ':')
1N/A s = t;
1N/A else
1N/A n = -1;
1N/A }
1N/A }
1N/A if (n >= 0)
1N/A {
1N/A if (n > 31)
1N/A goto done;
1N/A state |= DAY|MDAY;
1N/A tm->tm_mday = n;
1N/A if (f > 0)
1N/A tm->tm_year += f;
1N/A }
1N/A if (state & (LAST|NEXT|THIS))
1N/A {
1N/A n = i;
1N/A goto rel_month;
1N/A }
1N/A continue;
1N/A case TM_UT:
1N/A if (state & ZONE)
1N/A goto done;
1N/A state |= ZONE;
1N/A zone = tmgoff(s, &t, 0);
1N/A s = t;
1N/A continue;
1N/A case TM_DT:
1N/A if (!dst)
1N/A goto done;
1N/A if (!(state & ZONE))
1N/A {
1N/A dst = tm->tm_zone->dst;
1N/A zone = tm->tm_zone->west;
1N/A }
1N/A zone += tmgoff(s, &t, dst);
1N/A s = t;
1N/A dst = 0;
1N/A state |= ZONE;
1N/A continue;
1N/A case TM_NOISE:
1N/A continue;
1N/A }
1N/A }
1N/A if (!(state & ZONE) && (zp = tmzone(s, &t, type, &dst)))
1N/A {
1N/A s = t;
1N/A zone = zp->west + dst;
1N/A tm_info.date = zp;
1N/A state |= ZONE;
1N/A if (n < 0)
1N/A continue;
1N/A }
1N/A else if (!type && (zp = tmtype(s, &t)))
1N/A {
1N/A s = t;
1N/A type = zp->type;
1N/A if (n < 0)
1N/A continue;
1N/A }
1N/A state |= BREAK;
1N/A }
1N/A }
1N/A else if (*s == '/')
1N/A {
1N/A if (!(state & (YEAR|MONTH)) && n >= 1969 && n < 3000 && (i = strtol(s + 1, &t, 10)) > 0 && i <= 12)
1N/A {
1N/A state |= YEAR;
1N/A tm->tm_year = n - 1900;
1N/A s = t;
1N/A i--;
1N/A }
1N/A else
1N/A {
1N/A if ((state & MONTH) || n <= 0 || n > 31)
1N/A break;
1N/A if (isalpha(*++s))
1N/A {
1N/A if ((i = tmlex(s, &t, tm_info.format, TM_DAY_ABBREV, NiL, 0)) < 0)
1N/A break;
1N/A if (i >= TM_MONTH)
1N/A i -= TM_MONTH;
1N/A s = t;
1N/A }
1N/A else
1N/A {
1N/A i = n - 1;
1N/A n = strtol(s, &t, 10);
1N/A s = t;
1N/A if (n <= 0 || n > 31)
1N/A break;
1N/A if (*s == '/' && !isdigit(*(s + 1)))
1N/A break;
1N/A }
1N/A state |= DAY;
1N/A tm->tm_mday = n;
1N/A }
1N/A state |= MONTH;
1N/A n = tm->tm_mon;
1N/A tm->tm_mon = i;
1N/A if (*s == '/')
1N/A {
1N/A n = strtol(++s, &t, 10);
1N/A w = t - s;
1N/A s = t;
1N/A if (*s == '/' || *s == ':' || *s == '-' || *s == '_')
1N/A s++;
1N/A }
1N/A else
1N/A {
1N/A if (state & (LAST|NEXT|THIS))
1N/A {
1N/A rel_month:
1N/A if (state & LAST)
1N/A tm->tm_year -= (tm->tm_mon < n) ? 0 : 1;
1N/A else
1N/A tm->tm_year += ((state & NEXT) ? 1 : 0) + ((tm->tm_mon < n) ? 1 : 0);
1N/A if (state & MDAY)
1N/A goto clear_hour;
1N/A set &= ~(LAST|NEXT|THIS); /*AHA*/
1N/A state &= ~(LAST|NEXT|THIS); /*AHA*/
1N/A goto clear_mday;
1N/A }
1N/A continue;
1N/A }
1N/A }
1N/A if (n < 0 || w > 4)
1N/A break;
1N/A if (w == 4)
1N/A {
1N/A if ((state & YEAR) || n < 1969 || n >= 3000)
1N/A break;
1N/A state |= YEAR;
1N/A tm->tm_year = n - 1900;
1N/A }
1N/A else if (w == 3)
1N/A {
1N/A if (state & (MONTH|MDAY|WDAY))
1N/A break;
1N/A state |= MONTH|DAY|MDAY;
1N/A tm->tm_mon = 0;
1N/A tm->tm_mday = n;
1N/A }
1N/A else if (w == 2 && !(state & YEAR))
1N/A {
1N/A state |= YEAR;
1N/A if (n < TM_WINDOW)
1N/A n += 100;
1N/A tm->tm_year = n;
1N/A }
1N/A else if (!(state & MONTH) && n >= 1 && n <= 12)
1N/A {
1N/A state |= MONTH;
1N/A tm->tm_mon = n - 1;
1N/A }
1N/A else if (!(state & (MDAY|WDAY)) && n >= 1 && n <= 31)
1N/A {
1N/A state |= DAY|MDAY|WDAY;
1N/A tm->tm_mday = n;
1N/A }
1N/A else
1N/A break;
1N/A if (state & BREAK)
1N/A {
1N/A last = t;
1N/A break;
1N/A }
1N/A continue;
1N/A clear_mon:
1N/A if ((set|state) & (EXACT|MONTH))
1N/A continue;
1N/A tm->tm_mon = 0;
1N/A clear_mday:
1N/A set |= MONTH;
1N/A if ((set|state) & (EXACT|DAY|HOUR))
1N/A continue;
1N/A tm->tm_mday = 1;
1N/A clear_hour:
1N/A message((-1, "AHA#%d DAY", __LINE__));
1N/A set |= DAY;
1N/A if ((set|state) & (EXACT|HOUR))
1N/A continue;
1N/A tm->tm_hour = 0;
1N/A clear_min:
1N/A set |= HOUR;
1N/A if ((set|state) & (EXACT|MINUTE))
1N/A continue;
1N/A tm->tm_min = 0;
1N/A clear_sec:
1N/A set |= MINUTE;
1N/A if ((set|state) & (EXACT|SECOND))
1N/A continue;
1N/A tm->tm_sec = 0;
1N/A clear_nsec:
1N/A set |= SECOND;
1N/A if ((set|state) & (EXACT|NSEC))
1N/A continue;
1N/A tm->tm_nsec = 0;
1N/A }
1N/A done:
1N/A if (day >= 0 && !(state & (MDAY|WDAY)))
1N/A {
1N/A message((-1, "AHA#%d day=%d dir=%d state=" FFMT, __LINE__, day, dir, FLAGS(state)));
1N/A tmfix(tm);
1N/A m = dir;
1N/A if (state & MONTH)
1N/A tm->tm_mday = 1;
1N/A else if (m < 0)
1N/A m++;
1N/A tm = tmxtm(tm, tmxtime(tm, zone), tm->tm_zone);
1N/A j = day - tm->tm_wday;
1N/A if (j < 0)
1N/A j += 7;
1N/A tm->tm_mday += j + m * 7;
1N/A if (state & FINAL)
1N/A 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);
1N/A }
1N/A else if (day < 0 && (state & FINAL) && (set & DAY))
1N/A {
1N/A tmfix(tm);
1N/A tm->tm_mday = tm_data.days[tm->tm_mon] + (tm->tm_mon == 1 && tmisleapyear(tm->tm_year));
1N/A }
1N/A if (state & WORK)
1N/A {
1N/A tm->tm_mday = (set & FINAL) ? (tm_data.days[tm->tm_mon] + (tm->tm_mon == 1 && tmisleapyear(tm->tm_year))) : 1;
1N/A tmfix(tm);
1N/A message((-1, "AHA#%d WORK mday=%d wday=%d", __LINE__, tm->tm_mday, tm->tm_wday));
1N/A if (tm->tm_wday == 0 && (j = 1) || tm->tm_wday == 6 && (j = 2))
1N/A {
1N/A if ((tm->tm_mday + j) > (tm_data.days[tm->tm_mon] + (tm->tm_mon == 1 && tmisleapyear(tm->tm_year))))
1N/A j -= 3;
1N/A tm->tm_mday += j;
1N/A }
1N/A }
1N/A now = tmxtime(tm, zone);
1N/A if (tm->tm_year <= 70 && tmxsec(now) > 31536000)
1N/A {
1N/A now = 0;
1N/A last = (char*)o;
1N/A }
1N/A if (e)
1N/A *e = last;
1N/A return now;
1N/A}