1*4887Schin /*********************************************************************** 2*4887Schin * * 3*4887Schin * This software is part of the ast package * 4*4887Schin * Copyright (c) 1985-2007 AT&T Knowledge Ventures * 5*4887Schin * and is licensed under the * 6*4887Schin * Common Public License, Version 1.0 * 7*4887Schin * by AT&T Knowledge Ventures * 8*4887Schin * * 9*4887Schin * A copy of the License is available at * 10*4887Schin * http://www.opensource.org/licenses/cpl1.0.txt * 11*4887Schin * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * 12*4887Schin * * 13*4887Schin * Information and Software Systems Research * 14*4887Schin * AT&T Research * 15*4887Schin * Florham Park NJ * 16*4887Schin * * 17*4887Schin * Glenn Fowler <gsf@research.att.com> * 18*4887Schin * David Korn <dgk@research.att.com> * 19*4887Schin * Phong Vo <kpv@research.att.com> * 20*4887Schin * * 21*4887Schin ***********************************************************************/ 22*4887Schin #pragma prototyped 23*4887Schin /* 24*4887Schin * Glenn Fowler 25*4887Schin * AT&T Research 26*4887Schin * 27*4887Schin * Time_t conversion support 28*4887Schin * 29*4887Schin * scan date expression in s using format 30*4887Schin * if non-null, e points to the first invalid sequence in s 31*4887Schin * if non-null, f points to the first unused format char 32*4887Schin * t provides default values 33*4887Schin */ 34*4887Schin 35*4887Schin #include <tmx.h> 36*4887Schin #include <ctype.h> 37*4887Schin 38*4887Schin typedef struct 39*4887Schin { 40*4887Schin int32_t nsec; 41*4887Schin int year; 42*4887Schin int mon; 43*4887Schin int week; 44*4887Schin int weektype; 45*4887Schin int yday; 46*4887Schin int mday; 47*4887Schin int wday; 48*4887Schin int hour; 49*4887Schin int min; 50*4887Schin int sec; 51*4887Schin int meridian; 52*4887Schin int zone; 53*4887Schin } Set_t; 54*4887Schin 55*4887Schin #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) 56*4887Schin 57*4887Schin #define INDEX(m,x) (((n)>=((x)-(m)))?((n)-=((x)-(m))):(n)) 58*4887Schin 59*4887Schin #define NUMBER(d,m,x) do \ 60*4887Schin { \ 61*4887Schin n = 0; \ 62*4887Schin u = (char*)s; \ 63*4887Schin while (s < (const char*)(u + d) && *s >= '0' && *s <= '9') \ 64*4887Schin n = n * 10 + *s++ - '0'; \ 65*4887Schin if (u == (char*)s || n < m || n > x) \ 66*4887Schin goto next; \ 67*4887Schin } while (0) 68*4887Schin 69*4887Schin /* 70*4887Schin * generate a Time_t from tm + set 71*4887Schin */ 72*4887Schin 73*4887Schin static Time_t 74*4887Schin gen(register Tm_t* tm, register Set_t* set) 75*4887Schin { 76*4887Schin register int n; 77*4887Schin Time_t t; 78*4887Schin 79*4887Schin if (set->year >= 0) 80*4887Schin tm->tm_year = set->year; 81*4887Schin if (set->mon >= 0) 82*4887Schin { 83*4887Schin if (set->year < 0 && set->mon < tm->tm_mon) 84*4887Schin tm->tm_year++; 85*4887Schin tm->tm_mon = set->mon; 86*4887Schin if (set->yday < 0 && set->mday < 0) 87*4887Schin tm->tm_mday = set->mday = 1; 88*4887Schin } 89*4887Schin if (set->week >= 0) 90*4887Schin { 91*4887Schin if (set->mon < 0) 92*4887Schin { 93*4887Schin tmweek(tm, set->weektype, set->week, set->wday); 94*4887Schin set->wday = -1; 95*4887Schin } 96*4887Schin } 97*4887Schin else if (set->yday >= 0) 98*4887Schin { 99*4887Schin if (set->mon < 0) 100*4887Schin { 101*4887Schin tm->tm_mon = 0; 102*4887Schin tm->tm_mday = set->yday + 1; 103*4887Schin } 104*4887Schin } 105*4887Schin else if (set->mday >= 0) 106*4887Schin tm->tm_mday = set->mday; 107*4887Schin if (set->hour >= 0) 108*4887Schin { 109*4887Schin if (set->hour < tm->tm_hour && set->yday < 0 && set->mday < 0 && set->wday < 0) 110*4887Schin tm->tm_mday++; 111*4887Schin tm->tm_hour = set->hour; 112*4887Schin tm->tm_min = (set->min >= 0) ? set->min : 0; 113*4887Schin tm->tm_sec = (set->sec >= 0) ? set->sec : 0; 114*4887Schin } 115*4887Schin else if (set->min >= 0) 116*4887Schin { 117*4887Schin tm->tm_min = set->min; 118*4887Schin tm->tm_sec = (set->sec >= 0) ? set->sec : 0; 119*4887Schin } 120*4887Schin else if (set->sec >= 0) 121*4887Schin tm->tm_sec = set->sec; 122*4887Schin if (set->nsec < 1000000000L) 123*4887Schin tm->tm_nsec = set->nsec; 124*4887Schin if (set->meridian > 0) 125*4887Schin { 126*4887Schin if (tm->tm_hour < 12) 127*4887Schin tm->tm_hour += 12; 128*4887Schin } 129*4887Schin else if (set->meridian == 0) 130*4887Schin { 131*4887Schin if (tm->tm_hour >= 12) 132*4887Schin tm->tm_hour -= 12; 133*4887Schin } 134*4887Schin t = tmxtime(tm, set->zone); 135*4887Schin tm = 0; 136*4887Schin if (set->yday >= 0) 137*4887Schin { 138*4887Schin tm = tmxmake(t); 139*4887Schin tm->tm_mday += set->yday - tm->tm_yday; 140*4887Schin } 141*4887Schin else if (set->wday >= 0) 142*4887Schin { 143*4887Schin tm = tmxmake(t); 144*4887Schin if ((n = set->wday - tm->tm_wday) < 0) 145*4887Schin n += 7; 146*4887Schin tm->tm_mday += n; 147*4887Schin } 148*4887Schin if (set->nsec < 1000000000L) 149*4887Schin { 150*4887Schin if (!tm) 151*4887Schin tm = tmxmake(t); 152*4887Schin tm->tm_nsec = set->nsec; 153*4887Schin } 154*4887Schin return tm ? tmxtime(tm, set->zone) : t; 155*4887Schin } 156*4887Schin 157*4887Schin /* 158*4887Schin * the format scan workhorse 159*4887Schin */ 160*4887Schin 161*4887Schin static Time_t 162*4887Schin scan(register const char* s, char** e, const char* format, char** f, Time_t t, long flags) 163*4887Schin { 164*4887Schin register int d; 165*4887Schin register int n; 166*4887Schin register char* p; 167*4887Schin register Tm_t* tm; 168*4887Schin const char* b; 169*4887Schin char* u; 170*4887Schin char* stack[4]; 171*4887Schin int m; 172*4887Schin int hi; 173*4887Schin int lo; 174*4887Schin int pedantic; 175*4887Schin Time_t x; 176*4887Schin Set_t set; 177*4887Schin Tm_zone_t* zp; 178*4887Schin 179*4887Schin char** sp = &stack[0]; 180*4887Schin 181*4887Schin while (isspace(*s)) 182*4887Schin s++; 183*4887Schin b = s; 184*4887Schin again: 185*4887Schin CLEAR(set); 186*4887Schin tm = tmxmake(t); 187*4887Schin tm_info.date = tm_info.zone; 188*4887Schin pedantic = (flags & TM_PEDANTIC) != 0; 189*4887Schin for (;;) 190*4887Schin { 191*4887Schin if (!(d = *format++)) 192*4887Schin { 193*4887Schin if (sp <= &stack[0]) 194*4887Schin { 195*4887Schin format--; 196*4887Schin break; 197*4887Schin } 198*4887Schin format = (const char*)*--sp; 199*4887Schin } 200*4887Schin else if (!*s) 201*4887Schin { 202*4887Schin format--; 203*4887Schin break; 204*4887Schin } 205*4887Schin else if (d == '%' && (d = *format) && format++ && d != '%') 206*4887Schin { 207*4887Schin more: 208*4887Schin switch (d) 209*4887Schin { 210*4887Schin case 'a': 211*4887Schin lo = TM_DAY_ABBREV; 212*4887Schin hi = pedantic ? TM_DAY : TM_TIME; 213*4887Schin goto get_wday; 214*4887Schin case 'A': 215*4887Schin lo = pedantic ? TM_DAY : TM_DAY_ABBREV; 216*4887Schin hi = TM_TIME; 217*4887Schin get_wday: 218*4887Schin if ((n = tmlex(s, &u, tm_info.format + lo, hi - lo, NiL, 0)) < 0) 219*4887Schin goto next; 220*4887Schin s = u; 221*4887Schin INDEX(TM_DAY_ABBREV, TM_DAY); 222*4887Schin set.wday = n; 223*4887Schin continue; 224*4887Schin case 'b': 225*4887Schin case 'h': 226*4887Schin lo = TM_MONTH_ABBREV; 227*4887Schin hi = pedantic ? TM_MONTH : TM_DAY_ABBREV; 228*4887Schin goto get_mon; 229*4887Schin case 'B': 230*4887Schin lo = pedantic ? TM_MONTH : TM_MONTH_ABBREV; 231*4887Schin hi = TM_DAY_ABBREV; 232*4887Schin get_mon: 233*4887Schin if ((n = tmlex(s, &u, tm_info.format + lo, hi - lo, NiL, 0)) < 0) 234*4887Schin goto next; 235*4887Schin s = u; 236*4887Schin INDEX(TM_MONTH_ABBREV, TM_MONTH); 237*4887Schin set.mon = n; 238*4887Schin continue; 239*4887Schin case 'c': 240*4887Schin p = "%a %b %e %T %Y"; 241*4887Schin break; 242*4887Schin case 'C': 243*4887Schin NUMBER(2, 19, 99); 244*4887Schin set.year = (n - 19) * 100 + tm->tm_year % 100; 245*4887Schin continue; 246*4887Schin case 'd': 247*4887Schin if (pedantic && !isdigit(*s)) 248*4887Schin goto next; 249*4887Schin /*FALLTHROUGH*/ 250*4887Schin case 'e': 251*4887Schin NUMBER(2, 1, 31); 252*4887Schin set.mday = n; 253*4887Schin continue; 254*4887Schin case 'D': 255*4887Schin p = "%m/%d/%y"; 256*4887Schin break; 257*4887Schin case 'E': 258*4887Schin case 'O': 259*4887Schin if (*format) 260*4887Schin { 261*4887Schin d = *format++; 262*4887Schin goto more; 263*4887Schin } 264*4887Schin continue; 265*4887Schin case 'H': 266*4887Schin case 'k': 267*4887Schin NUMBER(2, 0, 23); 268*4887Schin set.hour = n; 269*4887Schin continue; 270*4887Schin case 'I': 271*4887Schin case 'l': 272*4887Schin NUMBER(2, 1, 12); 273*4887Schin set.hour = n; 274*4887Schin continue; 275*4887Schin case 'j': 276*4887Schin NUMBER(3, 1, 366); 277*4887Schin set.yday = n - 1; 278*4887Schin continue; 279*4887Schin case 'm': 280*4887Schin NUMBER(2, 1, 12); 281*4887Schin set.mon = n - 1; 282*4887Schin continue; 283*4887Schin case 'M': 284*4887Schin NUMBER(2, 0, 59); 285*4887Schin set.min = n; 286*4887Schin continue; 287*4887Schin case 'n': 288*4887Schin if (pedantic) 289*4887Schin while (*s == '\n') 290*4887Schin s++; 291*4887Schin else 292*4887Schin while (isspace(*s)) 293*4887Schin s++; 294*4887Schin continue; 295*4887Schin case 'N': 296*4887Schin NUMBER(9, 0, 999999999L); 297*4887Schin set.nsec = n; 298*4887Schin continue; 299*4887Schin case 'p': 300*4887Schin if ((n = tmlex(s, &u, tm_info.format + TM_MERIDIAN, TM_UT - TM_MERIDIAN, NiL, 0)) < 0) 301*4887Schin goto next; 302*4887Schin set.meridian = n; 303*4887Schin s = u; 304*4887Schin continue; 305*4887Schin case 'r': 306*4887Schin p = "%I:%M:%S %p"; 307*4887Schin break; 308*4887Schin case 'R': 309*4887Schin p = "%H:%M:%S"; 310*4887Schin break; 311*4887Schin case 's': 312*4887Schin x = strtoul(s, &u, 0); 313*4887Schin if (s == u) 314*4887Schin goto next; 315*4887Schin tm = tmxmake(tmxsns(x, 0)); 316*4887Schin s = u; 317*4887Schin CLEAR(set); 318*4887Schin continue; 319*4887Schin case 'S': 320*4887Schin NUMBER(2, 0, 61); 321*4887Schin set.sec = n; 322*4887Schin continue; 323*4887Schin case 'u': 324*4887Schin NUMBER(2, 1, 7); 325*4887Schin set.wday = n % 7; 326*4887Schin continue; 327*4887Schin case 'U': 328*4887Schin NUMBER(2, 0, 52); 329*4887Schin set.week = n; 330*4887Schin set.weektype = 0; 331*4887Schin continue; 332*4887Schin case 'V': 333*4887Schin NUMBER(2, 1, 53); 334*4887Schin set.week = n; 335*4887Schin set.weektype = 2; 336*4887Schin continue; 337*4887Schin case 'w': 338*4887Schin NUMBER(2, 0, 6); 339*4887Schin set.wday = n; 340*4887Schin continue; 341*4887Schin case 'W': 342*4887Schin NUMBER(2, 0, 52); 343*4887Schin set.week = n; 344*4887Schin set.weektype = 1; 345*4887Schin continue; 346*4887Schin case 'x': 347*4887Schin p = tm_info.format[TM_DATE]; 348*4887Schin break; 349*4887Schin case 'X': 350*4887Schin p = tm_info.format[TM_TIME]; 351*4887Schin break; 352*4887Schin case 'y': 353*4887Schin NUMBER(2, 0, 99); 354*4887Schin if (n < TM_WINDOW) 355*4887Schin n += 100; 356*4887Schin set.year = n; 357*4887Schin continue; 358*4887Schin case 'Y': 359*4887Schin NUMBER(4, 1969, 2100); 360*4887Schin set.year = n - 1900; 361*4887Schin continue; 362*4887Schin case 'Z': 363*4887Schin case 'q': 364*4887Schin if (zp = tmtype(s, &u)) 365*4887Schin { 366*4887Schin s = u; 367*4887Schin u = zp->type; 368*4887Schin } 369*4887Schin else 370*4887Schin u = 0; 371*4887Schin if (d == 'q') 372*4887Schin continue; 373*4887Schin case 'z': 374*4887Schin if ((zp = tmzone(s, &u, u, &m))) 375*4887Schin { 376*4887Schin s = u; 377*4887Schin set.zone = zp->west + m; 378*4887Schin tm_info.date = zp; 379*4887Schin } 380*4887Schin continue; 381*4887Schin case '|': 382*4887Schin s = b; 383*4887Schin goto again; 384*4887Schin case '&': 385*4887Schin x = gen(tm, &set); 386*4887Schin x = tmxdate(s, e, t); 387*4887Schin if (s == (const char*)*e) 388*4887Schin goto next; 389*4887Schin t = x; 390*4887Schin s = (const char*)*e; 391*4887Schin if (!*format || *format == '%' && *(format + 1) == '|') 392*4887Schin goto done; 393*4887Schin goto again; 394*4887Schin default: 395*4887Schin goto next; 396*4887Schin } 397*4887Schin if (sp >= &stack[elementsof(stack)]) 398*4887Schin goto next; 399*4887Schin *sp++ = (char*)format; 400*4887Schin format = (const char*)p; 401*4887Schin } 402*4887Schin else if (isspace(d)) 403*4887Schin while (isspace(*s)) 404*4887Schin s++; 405*4887Schin else if (*s != d) 406*4887Schin break; 407*4887Schin else 408*4887Schin s++; 409*4887Schin } 410*4887Schin next: 411*4887Schin if (sp > &stack[0]) 412*4887Schin format = (const char*)stack[0]; 413*4887Schin if (*format) 414*4887Schin { 415*4887Schin p = (char*)format; 416*4887Schin if (!*s && *p == '%' && *(p + 1) == '|') 417*4887Schin format += strlen(format); 418*4887Schin else 419*4887Schin while (*p) 420*4887Schin if (*p++ == '%' && *p && *p++ == '|' && *p) 421*4887Schin { 422*4887Schin format = (const char*)p; 423*4887Schin s = b; 424*4887Schin goto again; 425*4887Schin } 426*4887Schin } 427*4887Schin t = gen(tm, &set); 428*4887Schin done: 429*4887Schin if (e) 430*4887Schin { 431*4887Schin while (isspace(*s)) 432*4887Schin s++; 433*4887Schin *e = (char*)s; 434*4887Schin } 435*4887Schin if (f) 436*4887Schin { 437*4887Schin while (isspace(*format)) 438*4887Schin format++; 439*4887Schin *f = (char*)format; 440*4887Schin } 441*4887Schin return t; 442*4887Schin } 443*4887Schin 444*4887Schin /* 445*4887Schin * format==0 DATEMSK 446*4887Schin * *format==0 DATEMSK and tmxdate() 447*4887Schin * *format!=0 format 448*4887Schin */ 449*4887Schin 450*4887Schin Time_t 451*4887Schin tmxscan(const char* s, char** e, const char* format, char** f, Time_t t, long flags) 452*4887Schin { 453*4887Schin register char* v; 454*4887Schin register char** p; 455*4887Schin char* q; 456*4887Schin char* r; 457*4887Schin Time_t x; 458*4887Schin 459*4887Schin static int initialized; 460*4887Schin static char** datemask; 461*4887Schin 462*4887Schin tmlocale(); 463*4887Schin if (!format || !*format) 464*4887Schin { 465*4887Schin if (!initialized) 466*4887Schin { 467*4887Schin register Sfio_t* sp; 468*4887Schin register int n; 469*4887Schin off_t m; 470*4887Schin 471*4887Schin initialized = 1; 472*4887Schin if ((v = getenv("DATEMSK")) && *v && (sp = sfopen(NiL, v, "r"))) 473*4887Schin { 474*4887Schin for (n = 1; sfgetr(sp, '\n', 0); n++); 475*4887Schin m = sfseek(sp, 0L, SEEK_CUR); 476*4887Schin if (p = newof(0, char*, n, m)) 477*4887Schin { 478*4887Schin sfseek(sp, 0L, SEEK_SET); 479*4887Schin v = (char*)(p + n); 480*4887Schin if (sfread(sp, v, m) != m) 481*4887Schin { 482*4887Schin free(p); 483*4887Schin p = 0; 484*4887Schin } 485*4887Schin else 486*4887Schin { 487*4887Schin datemask = p; 488*4887Schin v[m] = 0; 489*4887Schin while (*v) 490*4887Schin { 491*4887Schin *p++ = v; 492*4887Schin if (!(v = strchr(v, '\n'))) 493*4887Schin break; 494*4887Schin *v++ = 0; 495*4887Schin } 496*4887Schin *p = 0; 497*4887Schin } 498*4887Schin } 499*4887Schin } 500*4887Schin } 501*4887Schin if (p = datemask) 502*4887Schin while (v = *p++) 503*4887Schin { 504*4887Schin x = scan(s, &q, v, &r, t, flags); 505*4887Schin if (!*q && !*r) 506*4887Schin { 507*4887Schin if (e) 508*4887Schin *e = q; 509*4887Schin if (f) 510*4887Schin *f = r; 511*4887Schin return x; 512*4887Schin } 513*4887Schin } 514*4887Schin if (f) 515*4887Schin *f = (char*)format; 516*4887Schin if (format) 517*4887Schin return tmxdate(s, e, t); 518*4887Schin if (e) 519*4887Schin *e = (char*)s; 520*4887Schin return 0; 521*4887Schin } 522*4887Schin return scan(s, e, format, f, t, flags); 523*4887Schin } 524