1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1985-2008 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 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 'F': 266 p = "%Y-%m-%d"; 267 break; 268 case 'H': 269 case 'k': 270 NUMBER(2, 0, 23); 271 set.hour = n; 272 continue; 273 case 'I': 274 case 'l': 275 NUMBER(2, 1, 12); 276 set.hour = n; 277 continue; 278 case 'j': 279 NUMBER(3, 1, 366); 280 set.yday = n - 1; 281 continue; 282 case 'm': 283 NUMBER(2, 1, 12); 284 set.mon = n - 1; 285 continue; 286 case 'M': 287 NUMBER(2, 0, 59); 288 set.min = n; 289 continue; 290 case 'n': 291 if (pedantic) 292 while (*s == '\n') 293 s++; 294 else 295 while (isspace(*s)) 296 s++; 297 continue; 298 case 'N': 299 NUMBER(9, 0, 999999999L); 300 set.nsec = n; 301 continue; 302 case 'p': 303 if ((n = tmlex(s, &u, tm_info.format + TM_MERIDIAN, TM_UT - TM_MERIDIAN, NiL, 0)) < 0) 304 goto next; 305 set.meridian = n; 306 s = u; 307 continue; 308 case 'r': 309 p = "%I:%M:%S %p"; 310 break; 311 case 'R': 312 p = "%H:%M:%S"; 313 break; 314 case 's': 315 x = strtoul(s, &u, 0); 316 if (s == u) 317 goto next; 318 tm = tmxmake(tmxsns(x, 0)); 319 s = u; 320 CLEAR(set); 321 continue; 322 case 'S': 323 NUMBER(2, 0, 61); 324 set.sec = n; 325 continue; 326 case 'u': 327 NUMBER(2, 1, 7); 328 set.wday = n % 7; 329 continue; 330 case 'U': 331 NUMBER(2, 0, 52); 332 set.week = n; 333 set.weektype = 0; 334 continue; 335 case 'V': 336 NUMBER(2, 1, 53); 337 set.week = n; 338 set.weektype = 2; 339 continue; 340 case 'w': 341 NUMBER(2, 0, 6); 342 set.wday = n; 343 continue; 344 case 'W': 345 NUMBER(2, 0, 52); 346 set.week = n; 347 set.weektype = 1; 348 continue; 349 case 'x': 350 p = tm_info.format[TM_DATE]; 351 break; 352 case 'X': 353 p = tm_info.format[TM_TIME]; 354 break; 355 case 'y': 356 NUMBER(2, 0, 99); 357 if (n < TM_WINDOW) 358 n += 100; 359 set.year = n; 360 continue; 361 case 'Y': 362 NUMBER(4, 1969, 2100); 363 set.year = n - 1900; 364 continue; 365 case 'Z': 366 case 'q': 367 if (zp = tmtype(s, &u)) 368 { 369 s = u; 370 u = zp->type; 371 } 372 else 373 u = 0; 374 if (d == 'q') 375 continue; 376 case 'z': 377 if ((zp = tmzone(s, &u, u, &m))) 378 { 379 s = u; 380 set.zone = zp->west + m; 381 tm_info.date = zp; 382 } 383 continue; 384 case '|': 385 s = b; 386 goto again; 387 case '&': 388 x = gen(tm, &set); 389 x = tmxdate(s, e, t); 390 if (s == (const char*)*e) 391 goto next; 392 t = x; 393 s = (const char*)*e; 394 if (!*format || *format == '%' && *(format + 1) == '|') 395 goto done; 396 goto again; 397 default: 398 goto next; 399 } 400 if (sp >= &stack[elementsof(stack)]) 401 goto next; 402 *sp++ = (char*)format; 403 format = (const char*)p; 404 } 405 else if (isspace(d)) 406 while (isspace(*s)) 407 s++; 408 else if (*s != d) 409 break; 410 else 411 s++; 412 } 413 next: 414 if (sp > &stack[0]) 415 format = (const char*)stack[0]; 416 if (*format) 417 { 418 p = (char*)format; 419 if (!*s && *p == '%' && *(p + 1) == '|') 420 format += strlen(format); 421 else 422 while (*p) 423 if (*p++ == '%' && *p && *p++ == '|' && *p) 424 { 425 format = (const char*)p; 426 s = b; 427 goto again; 428 } 429 } 430 t = gen(tm, &set); 431 done: 432 if (e) 433 { 434 while (isspace(*s)) 435 s++; 436 *e = (char*)s; 437 } 438 if (f) 439 { 440 while (isspace(*format)) 441 format++; 442 *f = (char*)format; 443 } 444 return t; 445 } 446 447 /* 448 * format==0 DATEMSK 449 * *format==0 DATEMSK and tmxdate() 450 * *format!=0 format 451 */ 452 453 Time_t 454 tmxscan(const char* s, char** e, const char* format, char** f, Time_t t, long flags) 455 { 456 register char* v; 457 register char** p; 458 char* q; 459 char* r; 460 Time_t x; 461 462 static int initialized; 463 static char** datemask; 464 465 tmlocale(); 466 if (!format || !*format) 467 { 468 if (!initialized) 469 { 470 register Sfio_t* sp; 471 register int n; 472 off_t m; 473 474 initialized = 1; 475 if ((v = getenv("DATEMSK")) && *v && (sp = sfopen(NiL, v, "r"))) 476 { 477 for (n = 1; sfgetr(sp, '\n', 0); n++); 478 m = sfseek(sp, 0L, SEEK_CUR); 479 if (p = newof(0, char*, n, m)) 480 { 481 sfseek(sp, 0L, SEEK_SET); 482 v = (char*)(p + n); 483 if (sfread(sp, v, m) != m) 484 { 485 free(p); 486 p = 0; 487 } 488 else 489 { 490 datemask = p; 491 v[m] = 0; 492 while (*v) 493 { 494 *p++ = v; 495 if (!(v = strchr(v, '\n'))) 496 break; 497 *v++ = 0; 498 } 499 *p = 0; 500 } 501 } 502 } 503 } 504 if (p = datemask) 505 while (v = *p++) 506 { 507 x = scan(s, &q, v, &r, t, flags); 508 if (!*q && !*r) 509 { 510 if (e) 511 *e = q; 512 if (f) 513 *f = r; 514 return x; 515 } 516 } 517 if (f) 518 *f = (char*)format; 519 if (format) 520 return tmxdate(s, e, t); 521 if (e) 522 *e = (char*)s; 523 return 0; 524 } 525 return scan(s, e, format, f, t, flags); 526 } 527