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