14887Schin /*********************************************************************** 24887Schin * * 34887Schin * This software is part of the ast package * 4*8462SApril.Chin@Sun.COM * Copyright (c) 1985-2008 AT&T Intellectual Property * 54887Schin * and is licensed under the * 64887Schin * Common Public License, Version 1.0 * 7*8462SApril.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 744887Schin gen(register Tm_t* tm, register Set_t* set) 754887Schin { 764887Schin register int n; 774887Schin Time_t t; 784887Schin 794887Schin if (set->year >= 0) 804887Schin tm->tm_year = set->year; 814887Schin if (set->mon >= 0) 824887Schin { 834887Schin if (set->year < 0 && set->mon < tm->tm_mon) 844887Schin tm->tm_year++; 854887Schin tm->tm_mon = set->mon; 864887Schin if (set->yday < 0 && set->mday < 0) 874887Schin tm->tm_mday = set->mday = 1; 884887Schin } 894887Schin if (set->week >= 0) 904887Schin { 914887Schin if (set->mon < 0) 924887Schin { 934887Schin tmweek(tm, set->weektype, set->week, set->wday); 944887Schin set->wday = -1; 954887Schin } 964887Schin } 974887Schin else if (set->yday >= 0) 984887Schin { 994887Schin if (set->mon < 0) 1004887Schin { 1014887Schin tm->tm_mon = 0; 1024887Schin tm->tm_mday = set->yday + 1; 1034887Schin } 1044887Schin } 1054887Schin else if (set->mday >= 0) 1064887Schin tm->tm_mday = set->mday; 1074887Schin if (set->hour >= 0) 1084887Schin { 1094887Schin if (set->hour < tm->tm_hour && set->yday < 0 && set->mday < 0 && set->wday < 0) 1104887Schin tm->tm_mday++; 1114887Schin tm->tm_hour = set->hour; 1124887Schin tm->tm_min = (set->min >= 0) ? set->min : 0; 1134887Schin tm->tm_sec = (set->sec >= 0) ? set->sec : 0; 1144887Schin } 1154887Schin else if (set->min >= 0) 1164887Schin { 1174887Schin tm->tm_min = set->min; 1184887Schin tm->tm_sec = (set->sec >= 0) ? set->sec : 0; 1194887Schin } 1204887Schin else if (set->sec >= 0) 1214887Schin tm->tm_sec = set->sec; 1224887Schin if (set->nsec < 1000000000L) 1234887Schin tm->tm_nsec = set->nsec; 1244887Schin if (set->meridian > 0) 1254887Schin { 1264887Schin if (tm->tm_hour < 12) 1274887Schin tm->tm_hour += 12; 1284887Schin } 1294887Schin else if (set->meridian == 0) 1304887Schin { 1314887Schin if (tm->tm_hour >= 12) 1324887Schin tm->tm_hour -= 12; 1334887Schin } 1344887Schin t = tmxtime(tm, set->zone); 1354887Schin tm = 0; 1364887Schin if (set->yday >= 0) 1374887Schin { 1384887Schin tm = tmxmake(t); 1394887Schin tm->tm_mday += set->yday - tm->tm_yday; 1404887Schin } 1414887Schin else if (set->wday >= 0) 1424887Schin { 1434887Schin tm = tmxmake(t); 1444887Schin if ((n = set->wday - tm->tm_wday) < 0) 1454887Schin n += 7; 1464887Schin tm->tm_mday += n; 1474887Schin } 1484887Schin if (set->nsec < 1000000000L) 1494887Schin { 1504887Schin if (!tm) 1514887Schin tm = tmxmake(t); 1524887Schin tm->tm_nsec = set->nsec; 1534887Schin } 1544887Schin return tm ? tmxtime(tm, set->zone) : t; 1554887Schin } 1564887Schin 1574887Schin /* 1584887Schin * the format scan workhorse 1594887Schin */ 1604887Schin 1614887Schin static Time_t 1624887Schin scan(register const char* s, char** e, const char* format, char** f, Time_t t, long flags) 1634887Schin { 1644887Schin register int d; 1654887Schin register int n; 1664887Schin register char* p; 1674887Schin register Tm_t* tm; 1684887Schin const char* b; 1694887Schin char* u; 1704887Schin char* stack[4]; 1714887Schin int m; 1724887Schin int hi; 1734887Schin int lo; 1744887Schin int pedantic; 1754887Schin Time_t x; 1764887Schin Set_t set; 1774887Schin Tm_zone_t* zp; 1784887Schin 1794887Schin char** sp = &stack[0]; 1804887Schin 1814887Schin while (isspace(*s)) 1824887Schin s++; 1834887Schin b = s; 1844887Schin again: 1854887Schin CLEAR(set); 1864887Schin tm = tmxmake(t); 1874887Schin tm_info.date = tm_info.zone; 1884887Schin pedantic = (flags & TM_PEDANTIC) != 0; 1894887Schin for (;;) 1904887Schin { 1914887Schin if (!(d = *format++)) 1924887Schin { 1934887Schin if (sp <= &stack[0]) 1944887Schin { 1954887Schin format--; 1964887Schin break; 1974887Schin } 1984887Schin format = (const char*)*--sp; 1994887Schin } 2004887Schin else if (!*s) 2014887Schin { 2024887Schin format--; 2034887Schin break; 2044887Schin } 2054887Schin else if (d == '%' && (d = *format) && format++ && d != '%') 2064887Schin { 2074887Schin more: 2084887Schin switch (d) 2094887Schin { 2104887Schin case 'a': 2114887Schin lo = TM_DAY_ABBREV; 2124887Schin hi = pedantic ? TM_DAY : TM_TIME; 2134887Schin goto get_wday; 2144887Schin case 'A': 2154887Schin lo = pedantic ? TM_DAY : TM_DAY_ABBREV; 2164887Schin hi = TM_TIME; 2174887Schin get_wday: 2184887Schin if ((n = tmlex(s, &u, tm_info.format + lo, hi - lo, NiL, 0)) < 0) 2194887Schin goto next; 2204887Schin s = u; 2214887Schin INDEX(TM_DAY_ABBREV, TM_DAY); 2224887Schin set.wday = n; 2234887Schin continue; 2244887Schin case 'b': 2254887Schin case 'h': 2264887Schin lo = TM_MONTH_ABBREV; 2274887Schin hi = pedantic ? TM_MONTH : TM_DAY_ABBREV; 2284887Schin goto get_mon; 2294887Schin case 'B': 2304887Schin lo = pedantic ? TM_MONTH : TM_MONTH_ABBREV; 2314887Schin hi = TM_DAY_ABBREV; 2324887Schin get_mon: 2334887Schin if ((n = tmlex(s, &u, tm_info.format + lo, hi - lo, NiL, 0)) < 0) 2344887Schin goto next; 2354887Schin s = u; 2364887Schin INDEX(TM_MONTH_ABBREV, TM_MONTH); 2374887Schin set.mon = n; 2384887Schin continue; 2394887Schin case 'c': 2404887Schin p = "%a %b %e %T %Y"; 2414887Schin break; 2424887Schin case 'C': 2434887Schin NUMBER(2, 19, 99); 2444887Schin set.year = (n - 19) * 100 + tm->tm_year % 100; 2454887Schin continue; 2464887Schin case 'd': 2474887Schin if (pedantic && !isdigit(*s)) 2484887Schin goto next; 2494887Schin /*FALLTHROUGH*/ 2504887Schin case 'e': 2514887Schin NUMBER(2, 1, 31); 2524887Schin set.mday = n; 2534887Schin continue; 2544887Schin case 'D': 2554887Schin p = "%m/%d/%y"; 2564887Schin break; 2574887Schin case 'E': 2584887Schin case 'O': 2594887Schin if (*format) 2604887Schin { 2614887Schin d = *format++; 2624887Schin goto more; 2634887Schin } 2644887Schin continue; 265*8462SApril.Chin@Sun.COM case 'F': 266*8462SApril.Chin@Sun.COM p = "%Y-%m-%d"; 267*8462SApril.Chin@Sun.COM break; 2684887Schin case 'H': 2694887Schin case 'k': 2704887Schin NUMBER(2, 0, 23); 2714887Schin set.hour = n; 2724887Schin continue; 2734887Schin case 'I': 2744887Schin case 'l': 2754887Schin NUMBER(2, 1, 12); 2764887Schin set.hour = n; 2774887Schin continue; 2784887Schin case 'j': 2794887Schin NUMBER(3, 1, 366); 2804887Schin set.yday = n - 1; 2814887Schin continue; 2824887Schin case 'm': 2834887Schin NUMBER(2, 1, 12); 2844887Schin set.mon = n - 1; 2854887Schin continue; 2864887Schin case 'M': 2874887Schin NUMBER(2, 0, 59); 2884887Schin set.min = n; 2894887Schin continue; 2904887Schin case 'n': 2914887Schin if (pedantic) 2924887Schin while (*s == '\n') 2934887Schin s++; 2944887Schin else 2954887Schin while (isspace(*s)) 2964887Schin s++; 2974887Schin continue; 2984887Schin case 'N': 2994887Schin NUMBER(9, 0, 999999999L); 3004887Schin set.nsec = n; 3014887Schin continue; 3024887Schin case 'p': 3034887Schin if ((n = tmlex(s, &u, tm_info.format + TM_MERIDIAN, TM_UT - TM_MERIDIAN, NiL, 0)) < 0) 3044887Schin goto next; 3054887Schin set.meridian = n; 3064887Schin s = u; 3074887Schin continue; 3084887Schin case 'r': 3094887Schin p = "%I:%M:%S %p"; 3104887Schin break; 3114887Schin case 'R': 3124887Schin p = "%H:%M:%S"; 3134887Schin break; 3144887Schin case 's': 3154887Schin x = strtoul(s, &u, 0); 3164887Schin if (s == u) 3174887Schin goto next; 3184887Schin tm = tmxmake(tmxsns(x, 0)); 3194887Schin s = u; 3204887Schin CLEAR(set); 3214887Schin continue; 3224887Schin case 'S': 3234887Schin NUMBER(2, 0, 61); 3244887Schin set.sec = n; 3254887Schin continue; 3264887Schin case 'u': 3274887Schin NUMBER(2, 1, 7); 3284887Schin set.wday = n % 7; 3294887Schin continue; 3304887Schin case 'U': 3314887Schin NUMBER(2, 0, 52); 3324887Schin set.week = n; 3334887Schin set.weektype = 0; 3344887Schin continue; 3354887Schin case 'V': 3364887Schin NUMBER(2, 1, 53); 3374887Schin set.week = n; 3384887Schin set.weektype = 2; 3394887Schin continue; 3404887Schin case 'w': 3414887Schin NUMBER(2, 0, 6); 3424887Schin set.wday = n; 3434887Schin continue; 3444887Schin case 'W': 3454887Schin NUMBER(2, 0, 52); 3464887Schin set.week = n; 3474887Schin set.weektype = 1; 3484887Schin continue; 3494887Schin case 'x': 3504887Schin p = tm_info.format[TM_DATE]; 3514887Schin break; 3524887Schin case 'X': 3534887Schin p = tm_info.format[TM_TIME]; 3544887Schin break; 3554887Schin case 'y': 3564887Schin NUMBER(2, 0, 99); 3574887Schin if (n < TM_WINDOW) 3584887Schin n += 100; 3594887Schin set.year = n; 3604887Schin continue; 3614887Schin case 'Y': 3624887Schin NUMBER(4, 1969, 2100); 3634887Schin set.year = n - 1900; 3644887Schin continue; 3654887Schin case 'Z': 3664887Schin case 'q': 3674887Schin if (zp = tmtype(s, &u)) 3684887Schin { 3694887Schin s = u; 3704887Schin u = zp->type; 3714887Schin } 3724887Schin else 3734887Schin u = 0; 3744887Schin if (d == 'q') 3754887Schin continue; 3764887Schin case 'z': 3774887Schin if ((zp = tmzone(s, &u, u, &m))) 3784887Schin { 3794887Schin s = u; 3804887Schin set.zone = zp->west + m; 3814887Schin tm_info.date = zp; 3824887Schin } 3834887Schin continue; 3844887Schin case '|': 3854887Schin s = b; 3864887Schin goto again; 3874887Schin case '&': 3884887Schin x = gen(tm, &set); 3894887Schin x = tmxdate(s, e, t); 3904887Schin if (s == (const char*)*e) 3914887Schin goto next; 3924887Schin t = x; 3934887Schin s = (const char*)*e; 3944887Schin if (!*format || *format == '%' && *(format + 1) == '|') 3954887Schin goto done; 3964887Schin goto again; 3974887Schin default: 3984887Schin goto next; 3994887Schin } 4004887Schin if (sp >= &stack[elementsof(stack)]) 4014887Schin goto next; 4024887Schin *sp++ = (char*)format; 4034887Schin format = (const char*)p; 4044887Schin } 4054887Schin else if (isspace(d)) 4064887Schin while (isspace(*s)) 4074887Schin s++; 4084887Schin else if (*s != d) 4094887Schin break; 4104887Schin else 4114887Schin s++; 4124887Schin } 4134887Schin next: 4144887Schin if (sp > &stack[0]) 4154887Schin format = (const char*)stack[0]; 4164887Schin if (*format) 4174887Schin { 4184887Schin p = (char*)format; 4194887Schin if (!*s && *p == '%' && *(p + 1) == '|') 4204887Schin format += strlen(format); 4214887Schin else 4224887Schin while (*p) 4234887Schin if (*p++ == '%' && *p && *p++ == '|' && *p) 4244887Schin { 4254887Schin format = (const char*)p; 4264887Schin s = b; 4274887Schin goto again; 4284887Schin } 4294887Schin } 4304887Schin t = gen(tm, &set); 4314887Schin done: 4324887Schin if (e) 4334887Schin { 4344887Schin while (isspace(*s)) 4354887Schin s++; 4364887Schin *e = (char*)s; 4374887Schin } 4384887Schin if (f) 4394887Schin { 4404887Schin while (isspace(*format)) 4414887Schin format++; 4424887Schin *f = (char*)format; 4434887Schin } 4444887Schin return t; 4454887Schin } 4464887Schin 4474887Schin /* 4484887Schin * format==0 DATEMSK 4494887Schin * *format==0 DATEMSK and tmxdate() 4504887Schin * *format!=0 format 4514887Schin */ 4524887Schin 4534887Schin Time_t 4544887Schin tmxscan(const char* s, char** e, const char* format, char** f, Time_t t, long flags) 4554887Schin { 4564887Schin register char* v; 4574887Schin register char** p; 4584887Schin char* q; 4594887Schin char* r; 4604887Schin Time_t x; 4614887Schin 4624887Schin static int initialized; 4634887Schin static char** datemask; 4644887Schin 4654887Schin tmlocale(); 4664887Schin if (!format || !*format) 4674887Schin { 4684887Schin if (!initialized) 4694887Schin { 4704887Schin register Sfio_t* sp; 4714887Schin register int n; 4724887Schin off_t m; 4734887Schin 4744887Schin initialized = 1; 4754887Schin if ((v = getenv("DATEMSK")) && *v && (sp = sfopen(NiL, v, "r"))) 4764887Schin { 4774887Schin for (n = 1; sfgetr(sp, '\n', 0); n++); 4784887Schin m = sfseek(sp, 0L, SEEK_CUR); 4794887Schin if (p = newof(0, char*, n, m)) 4804887Schin { 4814887Schin sfseek(sp, 0L, SEEK_SET); 4824887Schin v = (char*)(p + n); 4834887Schin if (sfread(sp, v, m) != m) 4844887Schin { 4854887Schin free(p); 4864887Schin p = 0; 4874887Schin } 4884887Schin else 4894887Schin { 4904887Schin datemask = p; 4914887Schin v[m] = 0; 4924887Schin while (*v) 4934887Schin { 4944887Schin *p++ = v; 4954887Schin if (!(v = strchr(v, '\n'))) 4964887Schin break; 4974887Schin *v++ = 0; 4984887Schin } 4994887Schin *p = 0; 5004887Schin } 5014887Schin } 5024887Schin } 5034887Schin } 5044887Schin if (p = datemask) 5054887Schin while (v = *p++) 5064887Schin { 5074887Schin x = scan(s, &q, v, &r, t, flags); 5084887Schin if (!*q && !*r) 5094887Schin { 5104887Schin if (e) 5114887Schin *e = q; 5124887Schin if (f) 5134887Schin *f = r; 5144887Schin return x; 5154887Schin } 5164887Schin } 5174887Schin if (f) 5184887Schin *f = (char*)format; 5194887Schin if (format) 5204887Schin return tmxdate(s, e, t); 5214887Schin if (e) 5224887Schin *e = (char*)s; 5234887Schin return 0; 5244887Schin } 5254887Schin return scan(s, e, format, f, t, flags); 5264887Schin } 527