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