14887Schin /***********************************************************************
24887Schin * *
34887Schin * This software is part of the ast package *
4*12068SRoger.Faulkner@Oracle.COM * Copyright (c) 1985-2010 AT&T Intellectual Property *
54887Schin * and is licensed under the *
64887Schin * Common Public License, Version 1.0 *
78462SApril.Chin@Sun.COM * by AT&T Intellectual Property *
84887Schin * *
94887Schin * A copy of the License is available at *
104887Schin * http://www.opensource.org/licenses/cpl1.0.txt *
114887Schin * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
124887Schin * *
134887Schin * Information and Software Systems Research *
144887Schin * AT&T Research *
154887Schin * Florham Park NJ *
164887Schin * *
174887Schin * Glenn Fowler <gsf@research.att.com> *
184887Schin * David Korn <dgk@research.att.com> *
194887Schin * Phong Vo <kpv@research.att.com> *
204887Schin * *
214887Schin ***********************************************************************/
224887Schin #pragma prototyped
234887Schin /*
244887Schin * Glenn Fowler
254887Schin * AT&T Research
264887Schin *
274887Schin * Time_t conversion support
284887Schin *
294887Schin * scan date expression in s using format
304887Schin * if non-null, e points to the first invalid sequence in s
314887Schin * if non-null, f points to the first unused format char
324887Schin * t provides default values
334887Schin */
344887Schin
354887Schin #include <tmx.h>
364887Schin #include <ctype.h>
374887Schin
384887Schin typedef struct
394887Schin {
404887Schin int32_t nsec;
414887Schin int year;
424887Schin int mon;
434887Schin int week;
444887Schin int weektype;
454887Schin int yday;
464887Schin int mday;
474887Schin int wday;
484887Schin int hour;
494887Schin int min;
504887Schin int sec;
514887Schin int meridian;
524887Schin int zone;
534887Schin } Set_t;
544887Schin
554887Schin #define CLEAR(s) (s.year=s.mon=s.week=s.weektype=s.yday=s.mday=s.wday=s.hour=s.min=s.sec=s.meridian=(-1),s.nsec=1000000000L,s.zone=TM_LOCALZONE)
564887Schin
574887Schin #define INDEX(m,x) (((n)>=((x)-(m)))?((n)-=((x)-(m))):(n))
584887Schin
594887Schin #define NUMBER(d,m,x) do \
604887Schin { \
614887Schin n = 0; \
624887Schin u = (char*)s; \
634887Schin while (s < (const char*)(u + d) && *s >= '0' && *s <= '9') \
644887Schin n = n * 10 + *s++ - '0'; \
654887Schin if (u == (char*)s || n < m || n > x) \
664887Schin goto next; \
674887Schin } while (0)
684887Schin
694887Schin /*
704887Schin * generate a Time_t from tm + set
714887Schin */
724887Schin
734887Schin static Time_t
gen(register Tm_t * tm,register Set_t * set)744887Schin gen(register Tm_t* tm, register Set_t* set)
754887Schin {
764887Schin register int n;
7710898Sroland.mainz@nrubsig.org int z;
784887Schin Time_t t;
794887Schin
804887Schin if (set->year >= 0)
814887Schin tm->tm_year = set->year;
824887Schin if (set->mon >= 0)
834887Schin {
844887Schin if (set->year < 0 && set->mon < tm->tm_mon)
854887Schin tm->tm_year++;
864887Schin tm->tm_mon = set->mon;
874887Schin if (set->yday < 0 && set->mday < 0)
884887Schin tm->tm_mday = set->mday = 1;
894887Schin }
904887Schin if (set->week >= 0)
914887Schin {
924887Schin if (set->mon < 0)
934887Schin {
944887Schin tmweek(tm, set->weektype, set->week, set->wday);
954887Schin set->wday = -1;
964887Schin }
974887Schin }
984887Schin else if (set->yday >= 0)
994887Schin {
1004887Schin if (set->mon < 0)
1014887Schin {
1024887Schin tm->tm_mon = 0;
1034887Schin tm->tm_mday = set->yday + 1;
1044887Schin }
1054887Schin }
1064887Schin else if (set->mday >= 0)
1074887Schin tm->tm_mday = set->mday;
1084887Schin if (set->hour >= 0)
1094887Schin {
1104887Schin if (set->hour < tm->tm_hour && set->yday < 0 && set->mday < 0 && set->wday < 0)
1114887Schin tm->tm_mday++;
1124887Schin tm->tm_hour = set->hour;
1134887Schin tm->tm_min = (set->min >= 0) ? set->min : 0;
1144887Schin tm->tm_sec = (set->sec >= 0) ? set->sec : 0;
1154887Schin }
1164887Schin else if (set->min >= 0)
1174887Schin {
1184887Schin tm->tm_min = set->min;
1194887Schin tm->tm_sec = (set->sec >= 0) ? set->sec : 0;
1204887Schin }
1214887Schin else if (set->sec >= 0)
1224887Schin tm->tm_sec = set->sec;
1234887Schin if (set->nsec < 1000000000L)
1244887Schin tm->tm_nsec = set->nsec;
1254887Schin if (set->meridian > 0)
1264887Schin {
1274887Schin if (tm->tm_hour < 12)
1284887Schin tm->tm_hour += 12;
1294887Schin }
1304887Schin else if (set->meridian == 0)
1314887Schin {
1324887Schin if (tm->tm_hour >= 12)
1334887Schin tm->tm_hour -= 12;
1344887Schin }
1354887Schin t = tmxtime(tm, set->zone);
1364887Schin if (set->yday >= 0)
1374887Schin {
13810898Sroland.mainz@nrubsig.org z = 1;
13910898Sroland.mainz@nrubsig.org tm = tmxtm(tm, t, tm->tm_zone);
1404887Schin tm->tm_mday += set->yday - tm->tm_yday;
1414887Schin }
1424887Schin else if (set->wday >= 0)
1434887Schin {
14410898Sroland.mainz@nrubsig.org z = 1;
14510898Sroland.mainz@nrubsig.org tm = tmxtm(tm, t, tm->tm_zone);
1464887Schin if ((n = set->wday - tm->tm_wday) < 0)
1474887Schin n += 7;
1484887Schin tm->tm_mday += n;
1494887Schin }
15010898Sroland.mainz@nrubsig.org else
15110898Sroland.mainz@nrubsig.org z = 0;
1524887Schin if (set->nsec < 1000000000L)
1534887Schin {
15410898Sroland.mainz@nrubsig.org if (!z)
15510898Sroland.mainz@nrubsig.org {
15610898Sroland.mainz@nrubsig.org z = 1;
15710898Sroland.mainz@nrubsig.org tm = tmxtm(tm, t, tm->tm_zone);
15810898Sroland.mainz@nrubsig.org }
1594887Schin tm->tm_nsec = set->nsec;
1604887Schin }
16110898Sroland.mainz@nrubsig.org return z ? tmxtime(tm, set->zone) : t;
1624887Schin }
1634887Schin
1644887Schin /*
1654887Schin * the format scan workhorse
1664887Schin */
1674887Schin
1684887Schin static Time_t
scan(register const char * s,char ** e,const char * format,char ** f,Time_t t,long flags)1694887Schin scan(register const char* s, char** e, const char* format, char** f, Time_t t, long flags)
1704887Schin {
1714887Schin register int d;
1724887Schin register int n;
1734887Schin register char* p;
1744887Schin register Tm_t* tm;
1754887Schin const char* b;
1764887Schin char* u;
1774887Schin char* stack[4];
1784887Schin int m;
1794887Schin int hi;
1804887Schin int lo;
1814887Schin int pedantic;
1824887Schin Time_t x;
1834887Schin Set_t set;
1844887Schin Tm_zone_t* zp;
18510898Sroland.mainz@nrubsig.org Tm_t ts;
1864887Schin
1874887Schin char** sp = &stack[0];
1884887Schin
1894887Schin while (isspace(*s))
1904887Schin s++;
1914887Schin b = s;
1924887Schin again:
1934887Schin CLEAR(set);
19410898Sroland.mainz@nrubsig.org tm = tmxtm(&ts, t, NiL);
1954887Schin pedantic = (flags & TM_PEDANTIC) != 0;
1964887Schin for (;;)
1974887Schin {
1984887Schin if (!(d = *format++))
1994887Schin {
2004887Schin if (sp <= &stack[0])
2014887Schin {
2024887Schin format--;
2034887Schin break;
2044887Schin }
2054887Schin format = (const char*)*--sp;
2064887Schin }
2074887Schin else if (!*s)
2084887Schin {
2094887Schin format--;
2104887Schin break;
2114887Schin }
2124887Schin else if (d == '%' && (d = *format) && format++ && d != '%')
2134887Schin {
2144887Schin more:
2154887Schin switch (d)
2164887Schin {
2174887Schin case 'a':
2184887Schin lo = TM_DAY_ABBREV;
2194887Schin hi = pedantic ? TM_DAY : TM_TIME;
2204887Schin goto get_wday;
2214887Schin case 'A':
2224887Schin lo = pedantic ? TM_DAY : TM_DAY_ABBREV;
2234887Schin hi = TM_TIME;
2244887Schin get_wday:
2254887Schin if ((n = tmlex(s, &u, tm_info.format + lo, hi - lo, NiL, 0)) < 0)
2264887Schin goto next;
2274887Schin s = u;
2284887Schin INDEX(TM_DAY_ABBREV, TM_DAY);
2294887Schin set.wday = n;
2304887Schin continue;
2314887Schin case 'b':
2324887Schin case 'h':
2334887Schin lo = TM_MONTH_ABBREV;
2344887Schin hi = pedantic ? TM_MONTH : TM_DAY_ABBREV;
2354887Schin goto get_mon;
2364887Schin case 'B':
2374887Schin lo = pedantic ? TM_MONTH : TM_MONTH_ABBREV;
2384887Schin hi = TM_DAY_ABBREV;
2394887Schin get_mon:
2404887Schin if ((n = tmlex(s, &u, tm_info.format + lo, hi - lo, NiL, 0)) < 0)
2414887Schin goto next;
2424887Schin s = u;
2434887Schin INDEX(TM_MONTH_ABBREV, TM_MONTH);
2444887Schin set.mon = n;
2454887Schin continue;
2464887Schin case 'c':
2474887Schin p = "%a %b %e %T %Y";
2484887Schin break;
2494887Schin case 'C':
2504887Schin NUMBER(2, 19, 99);
2514887Schin set.year = (n - 19) * 100 + tm->tm_year % 100;
2524887Schin continue;
2534887Schin case 'd':
2544887Schin if (pedantic && !isdigit(*s))
2554887Schin goto next;
2564887Schin /*FALLTHROUGH*/
2574887Schin case 'e':
2584887Schin NUMBER(2, 1, 31);
2594887Schin set.mday = n;
2604887Schin continue;
2614887Schin case 'D':
2624887Schin p = "%m/%d/%y";
2634887Schin break;
2644887Schin case 'E':
2654887Schin case 'O':
2664887Schin if (*format)
2674887Schin {
2684887Schin d = *format++;
2694887Schin goto more;
2704887Schin }
2714887Schin continue;
2728462SApril.Chin@Sun.COM case 'F':
2738462SApril.Chin@Sun.COM p = "%Y-%m-%d";
2748462SApril.Chin@Sun.COM break;
2754887Schin case 'H':
2764887Schin case 'k':
2774887Schin NUMBER(2, 0, 23);
2784887Schin set.hour = n;
2794887Schin continue;
2804887Schin case 'I':
2814887Schin case 'l':
2824887Schin NUMBER(2, 1, 12);
2834887Schin set.hour = n;
2844887Schin continue;
2854887Schin case 'j':
2864887Schin NUMBER(3, 1, 366);
2874887Schin set.yday = n - 1;
2884887Schin continue;
2894887Schin case 'm':
2904887Schin NUMBER(2, 1, 12);
2914887Schin set.mon = n - 1;
2924887Schin continue;
2934887Schin case 'M':
2944887Schin NUMBER(2, 0, 59);
2954887Schin set.min = n;
2964887Schin continue;
2974887Schin case 'n':
2984887Schin if (pedantic)
2994887Schin while (*s == '\n')
3004887Schin s++;
3014887Schin else
3024887Schin while (isspace(*s))
3034887Schin s++;
3044887Schin continue;
3054887Schin case 'N':
3064887Schin NUMBER(9, 0, 999999999L);
3074887Schin set.nsec = n;
3084887Schin continue;
3094887Schin case 'p':
3104887Schin if ((n = tmlex(s, &u, tm_info.format + TM_MERIDIAN, TM_UT - TM_MERIDIAN, NiL, 0)) < 0)
3114887Schin goto next;
3124887Schin set.meridian = n;
3134887Schin s = u;
3144887Schin continue;
3154887Schin case 'r':
3164887Schin p = "%I:%M:%S %p";
3174887Schin break;
3184887Schin case 'R':
3194887Schin p = "%H:%M:%S";
3204887Schin break;
3214887Schin case 's':
3224887Schin x = strtoul(s, &u, 0);
3234887Schin if (s == u)
3244887Schin goto next;
32510898Sroland.mainz@nrubsig.org tm = tmxtm(tm, tmxsns(x, 0), tm->tm_zone);
3264887Schin s = u;
3274887Schin CLEAR(set);
3284887Schin continue;
3294887Schin case 'S':
3304887Schin NUMBER(2, 0, 61);
3314887Schin set.sec = n;
3324887Schin continue;
3334887Schin case 'u':
3344887Schin NUMBER(2, 1, 7);
3354887Schin set.wday = n % 7;
3364887Schin continue;
3374887Schin case 'U':
3384887Schin NUMBER(2, 0, 52);
3394887Schin set.week = n;
3404887Schin set.weektype = 0;
3414887Schin continue;
3424887Schin case 'V':
3434887Schin NUMBER(2, 1, 53);
3444887Schin set.week = n;
3454887Schin set.weektype = 2;
3464887Schin continue;
3474887Schin case 'w':
3484887Schin NUMBER(2, 0, 6);
3494887Schin set.wday = n;
3504887Schin continue;
3514887Schin case 'W':
3524887Schin NUMBER(2, 0, 52);
3534887Schin set.week = n;
3544887Schin set.weektype = 1;
3554887Schin continue;
3564887Schin case 'x':
3574887Schin p = tm_info.format[TM_DATE];
3584887Schin break;
3594887Schin case 'X':
3604887Schin p = tm_info.format[TM_TIME];
3614887Schin break;
3624887Schin case 'y':
3634887Schin NUMBER(2, 0, 99);
3644887Schin if (n < TM_WINDOW)
3654887Schin n += 100;
3664887Schin set.year = n;
3674887Schin continue;
3684887Schin case 'Y':
3694887Schin NUMBER(4, 1969, 2100);
3704887Schin set.year = n - 1900;
3714887Schin continue;
3724887Schin case 'Z':
3734887Schin case 'q':
3744887Schin if (zp = tmtype(s, &u))
3754887Schin {
3764887Schin s = u;
3774887Schin u = zp->type;
3784887Schin }
3794887Schin else
3804887Schin u = 0;
3814887Schin if (d == 'q')
3824887Schin continue;
3834887Schin case 'z':
3844887Schin if ((zp = tmzone(s, &u, u, &m)))
3854887Schin {
3864887Schin s = u;
3874887Schin set.zone = zp->west + m;
3884887Schin tm_info.date = zp;
3894887Schin }
3904887Schin continue;
3914887Schin case '|':
3924887Schin s = b;
3934887Schin goto again;
3944887Schin case '&':
3954887Schin x = gen(tm, &set);
3964887Schin x = tmxdate(s, e, t);
3974887Schin if (s == (const char*)*e)
3984887Schin goto next;
3994887Schin t = x;
4004887Schin s = (const char*)*e;
4014887Schin if (!*format || *format == '%' && *(format + 1) == '|')
4024887Schin goto done;
4034887Schin goto again;
4044887Schin default:
4054887Schin goto next;
4064887Schin }
4074887Schin if (sp >= &stack[elementsof(stack)])
4084887Schin goto next;
4094887Schin *sp++ = (char*)format;
4104887Schin format = (const char*)p;
4114887Schin }
4124887Schin else if (isspace(d))
4134887Schin while (isspace(*s))
4144887Schin s++;
4154887Schin else if (*s != d)
4164887Schin break;
4174887Schin else
4184887Schin s++;
4194887Schin }
4204887Schin next:
4214887Schin if (sp > &stack[0])
4224887Schin format = (const char*)stack[0];
4234887Schin if (*format)
4244887Schin {
4254887Schin p = (char*)format;
4264887Schin if (!*s && *p == '%' && *(p + 1) == '|')
4274887Schin format += strlen(format);
4284887Schin else
4294887Schin while (*p)
4304887Schin if (*p++ == '%' && *p && *p++ == '|' && *p)
4314887Schin {
4324887Schin format = (const char*)p;
4334887Schin s = b;
4344887Schin goto again;
4354887Schin }
4364887Schin }
4374887Schin t = gen(tm, &set);
4384887Schin done:
4394887Schin if (e)
4404887Schin {
4414887Schin while (isspace(*s))
4424887Schin s++;
4434887Schin *e = (char*)s;
4444887Schin }
4454887Schin if (f)
4464887Schin {
4474887Schin while (isspace(*format))
4484887Schin format++;
4494887Schin *f = (char*)format;
4504887Schin }
4514887Schin return t;
4524887Schin }
4534887Schin
4544887Schin /*
4554887Schin * format==0 DATEMSK
4564887Schin * *format==0 DATEMSK and tmxdate()
4574887Schin * *format!=0 format
4584887Schin */
4594887Schin
4604887Schin Time_t
tmxscan(const char * s,char ** e,const char * format,char ** f,Time_t t,long flags)4614887Schin tmxscan(const char* s, char** e, const char* format, char** f, Time_t t, long flags)
4624887Schin {
4634887Schin register char* v;
4644887Schin register char** p;
4654887Schin char* q;
4664887Schin char* r;
4674887Schin Time_t x;
4684887Schin
4694887Schin static int initialized;
4704887Schin static char** datemask;
4714887Schin
4724887Schin tmlocale();
4734887Schin if (!format || !*format)
4744887Schin {
4754887Schin if (!initialized)
4764887Schin {
4774887Schin register Sfio_t* sp;
4784887Schin register int n;
4794887Schin off_t m;
4804887Schin
4814887Schin initialized = 1;
4824887Schin if ((v = getenv("DATEMSK")) && *v && (sp = sfopen(NiL, v, "r")))
4834887Schin {
4844887Schin for (n = 1; sfgetr(sp, '\n', 0); n++);
4854887Schin m = sfseek(sp, 0L, SEEK_CUR);
4864887Schin if (p = newof(0, char*, n, m))
4874887Schin {
4884887Schin sfseek(sp, 0L, SEEK_SET);
4894887Schin v = (char*)(p + n);
4904887Schin if (sfread(sp, v, m) != m)
4914887Schin {
4924887Schin free(p);
4934887Schin p = 0;
4944887Schin }
4954887Schin else
4964887Schin {
4974887Schin datemask = p;
4984887Schin v[m] = 0;
4994887Schin while (*v)
5004887Schin {
5014887Schin *p++ = v;
5024887Schin if (!(v = strchr(v, '\n')))
5034887Schin break;
5044887Schin *v++ = 0;
5054887Schin }
5064887Schin *p = 0;
5074887Schin }
5084887Schin }
5094887Schin }
5104887Schin }
5114887Schin if (p = datemask)
5124887Schin while (v = *p++)
5134887Schin {
5144887Schin x = scan(s, &q, v, &r, t, flags);
5154887Schin if (!*q && !*r)
5164887Schin {
5174887Schin if (e)
5184887Schin *e = q;
5194887Schin if (f)
5204887Schin *f = r;
5214887Schin return x;
5224887Schin }
5234887Schin }
5244887Schin if (f)
5254887Schin *f = (char*)format;
5264887Schin if (format)
5274887Schin return tmxdate(s, e, t);
5284887Schin if (e)
5294887Schin *e = (char*)s;
5304887Schin return 0;
5314887Schin }
5324887Schin return scan(s, e, format, f, t, flags);
5334887Schin }
534