parsedate.y (23910B)
1 %{ 2 /* $Id: parsedate.y 647 2003-10-25 23:34:13Z weasel $ 3 ** 4 ** Originally written by Steven M. Bellovin <smb@research.att.com> while 5 ** at the University of North Carolina at Chapel Hill. Later tweaked by 6 ** a couple of people on Usenet. Completely overhauled by Rich $alz 7 ** <rsalz@osf.org> and Jim Berets <jberets@bbn.com> in August, 1990. 8 ** Further revised (removed obsolete constructs and cleaned up timezone 9 ** names) in August, 1991, by Rich. Paul Eggert <eggert@twinsun.com> 10 ** helped in September, 1992. 11 ** 12 ** This grammar has six shift/reduce conflicts. 13 ** 14 ** This code is in the public domain and has no copyright. 15 */ 16 17 #include <stdio.h> 18 #include <string.h> 19 20 // #include "config.h" 21 // #include "clibrary.h" 22 #include <ctype.h> 23 24 #if defined(_HPUX_SOURCE) 25 # include <alloca.h> 26 #endif 27 28 #ifdef TM_IN_SYS_TIME 29 # include <sys/time.h> 30 #else 31 # include <time.h> 32 #endif 33 34 // #include "libinn.h" 35 36 /* Used for iterating through arrays. ARRAY_SIZE returns the number of 37 elements in the array (useful for a < upper bound in a for loop) and 38 ARRAY_END returns a pointer to the element past the end (ISO C99 makes it 39 legal to refer to such a pointer as long as it's never dereferenced). */ 40 #define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0])) 41 #define ARRAY_END(array) (&(array)[ARRAY_SIZE(array)]) 42 43 /* On some systems, the macros defined by <ctype.h> are only vaild on ASCII 44 characters (those characters that isascii() says are ASCII). This comes 45 into play when applying <ctype.h> macros to eight-bit data. autoconf 46 checks for this with as part of AC_HEADER_STDC, so if autoconf doesn't 47 think our headers are standard, check isascii() first. */ 48 #if STDC_HEADERS 49 # define CTYPE(isXXXXX, c) (isXXXXX((unsigned char)(c))) 50 #else 51 # define CTYPE(isXXXXX, c) \ 52 (isascii((unsigned char)(c)) && isXXXXX((unsigned char)(c))) 53 #endif 54 55 56 #define yylhs date_yylhs 57 #define yylen date_yylen 58 #define yydefred date_yydefred 59 #define yydgoto date_yydgoto 60 #define yysindex date_yysindex 61 #define yyrindex date_yyrindex 62 #define yygindex date_yygindex 63 #define yytable date_yytable 64 #define yycheck date_yycheck 65 #define yyparse date_parse 66 #define yylex date_lex 67 #define yyerror date_error 68 #define yymaxdepth date_yymaxdepth 69 70 71 static int date_lex(void); 72 73 74 /* See the LeapYears table in Convert. */ 75 #define EPOCH 1970 76 #define END_OF_TIME 2038 77 /* Constants for general time calculations. */ 78 #define DST_OFFSET 1 79 #define SECSPERDAY (24L * 60L * 60L) 80 /* Readability for TABLE stuff. */ 81 #define HOUR(x) (x * 60) 82 83 #define LPAREN '(' 84 #define RPAREN ')' 85 #define IS7BIT(x) ((unsigned int)(x) < 0200) 86 87 88 /* 89 ** An entry in the lexical lookup table. 90 */ 91 typedef struct _TABLE { 92 const char * name; 93 int type; 94 time_t value; 95 } TABLE; 96 97 /* 98 ** Daylight-savings mode: on, off, or not yet known. 99 */ 100 typedef enum _DSTMODE { 101 DSTon, DSToff, DSTmaybe 102 } DSTMODE; 103 104 /* 105 ** Meridian: am, pm, or 24-hour style. 106 */ 107 typedef enum _MERIDIAN { 108 MERam, MERpm, MER24 109 } MERIDIAN; 110 111 112 /* 113 ** Global variables. We could get rid of most of them by using a yacc 114 ** union, but this is more efficient. (This routine predates the 115 ** yacc %union construct.) 116 */ 117 static char *yyInput; 118 static DSTMODE yyDSTmode; 119 static int yyHaveDate; 120 static int yyHaveRel; 121 static int yyHaveTime; 122 static time_t yyTimezone; 123 static time_t yyDay; 124 static time_t yyHour; 125 static time_t yyMinutes; 126 static time_t yyMonth; 127 static time_t yySeconds; 128 static time_t yyYear; 129 static MERIDIAN yyMeridian; 130 static time_t yyRelMonth; 131 static time_t yyRelSeconds; 132 133 134 /* extern struct tm *localtime(); */ 135 136 static void date_error(const char *s); 137 %} 138 139 %union { 140 time_t Number; 141 enum _MERIDIAN Meridian; 142 } 143 144 %token tDAY tDAYZONE tMERIDIAN tMONTH tMONTH_UNIT tSEC_UNIT tSNUMBER 145 %token tUNUMBER tZONE 146 147 %type <Number> tDAYZONE tMONTH tMONTH_UNIT tSEC_UNIT 148 %type <Number> tSNUMBER tUNUMBER tZONE numzone zone 149 %type <Meridian> tMERIDIAN o_merid 150 151 %% 152 153 spec : /* NULL */ 154 | spec item 155 ; 156 157 item : time { 158 yyHaveTime++; 159 #if defined(lint) 160 /* I am compulsive about lint natterings... */ 161 if (yyHaveTime == -1) { 162 YYERROR; 163 } 164 #endif /* defined(lint) */ 165 } 166 | time zone { 167 yyHaveTime++; 168 yyTimezone = $2; 169 } 170 | date { 171 yyHaveDate++; 172 } 173 | rel { 174 yyHaveRel = 1; 175 } 176 ; 177 178 time : tUNUMBER o_merid { 179 if ($1 < 100) { 180 yyHour = $1; 181 yyMinutes = 0; 182 } 183 else { 184 yyHour = $1 / 100; 185 yyMinutes = $1 % 100; 186 } 187 yySeconds = 0; 188 yyMeridian = $2; 189 } 190 | tUNUMBER ':' tUNUMBER o_merid { 191 yyHour = $1; 192 yyMinutes = $3; 193 yySeconds = 0; 194 yyMeridian = $4; 195 } 196 | tUNUMBER ':' tUNUMBER numzone { 197 yyHour = $1; 198 yyMinutes = $3; 199 yyTimezone = $4; 200 yyMeridian = MER24; 201 yyDSTmode = DSToff; 202 } 203 | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid { 204 yyHour = $1; 205 yyMinutes = $3; 206 yySeconds = $5; 207 yyMeridian = $6; 208 } 209 | tUNUMBER ':' tUNUMBER ':' tUNUMBER numzone { 210 yyHour = $1; 211 yyMinutes = $3; 212 yySeconds = $5; 213 yyTimezone = $6; 214 yyMeridian = MER24; 215 yyDSTmode = DSToff; 216 } 217 ; 218 219 zone : tZONE { 220 $$ = $1; 221 yyDSTmode = DSToff; 222 } 223 | tDAYZONE { 224 $$ = $1; 225 yyDSTmode = DSTon; 226 } 227 | tZONE numzone { 228 /* Only allow "GMT+300" and "GMT-0800" */ 229 if ($1 != 0) { 230 YYABORT; 231 } 232 $$ = $2; 233 yyDSTmode = DSToff; 234 } 235 | numzone { 236 $$ = $1; 237 yyDSTmode = DSToff; 238 } 239 ; 240 241 numzone : tSNUMBER { 242 int i; 243 244 /* Unix and GMT and numeric timezones -- a little confusing. */ 245 if ($1 < 0) { 246 /* Don't work with negative modulus. */ 247 $1 = -$1; 248 if ($1 > 9999 || (i = $1 % 100) >= 60) { 249 YYABORT; 250 } 251 $$ = ($1 / 100) * 60 + i; 252 } 253 else { 254 if ($1 > 9999 || (i = $1 % 100) >= 60) { 255 YYABORT; 256 } 257 $$ = -(($1 / 100) * 60 + i); 258 } 259 } 260 ; 261 262 date : tUNUMBER '/' tUNUMBER { 263 yyMonth = $1; 264 yyDay = $3; 265 } 266 | tUNUMBER '/' tUNUMBER '/' tUNUMBER { 267 if ($1 > 100) { 268 /* assume YYYY/MM/DD format, so need not to add 1900 */ 269 if ($1 > 999) { 270 yyYear = $1; 271 } else { 272 yyYear = 1900 + $1; 273 } 274 yyMonth = $3; 275 yyDay = $5; 276 } 277 else { 278 /* assume MM/DD/YY* format */ 279 yyMonth = $1; 280 yyDay = $3; 281 if ($5 > 999) { 282 /* assume year is YYYY format, so need not to add 1900 */ 283 yyYear = $5; 284 } else if ($5 < 100) { 285 /* assume year is YY format, so need to add 1900 */ 286 yyYear = $5 + (yyYear / 100 + (yyYear % 100 - $5) / 50) * 100; 287 } else { 288 yyYear = 1900 + $5; 289 } 290 } 291 } 292 | tMONTH tUNUMBER { 293 yyMonth = $1; 294 yyDay = $2; 295 } 296 | tMONTH tUNUMBER ',' tUNUMBER { 297 yyMonth = $1; 298 yyDay = $2; 299 if ($4 > 999) { 300 /* assume year is YYYY format, so need not to add 1900 */ 301 yyYear = $4; 302 } else if ($4 < 100) { 303 /* assume year is YY format, so need to add 1900 */ 304 yyYear = $4 + (yyYear / 100 + (yyYear % 100 - $4) / 50) * 100; 305 } else { 306 yyYear = 1900 + $4; 307 } 308 } 309 | tUNUMBER tMONTH { 310 yyDay = $1; 311 yyMonth = $2; 312 } 313 | tUNUMBER tMONTH tUNUMBER { 314 yyDay = $1; 315 yyMonth = $2; 316 if ($3 > 999) { 317 /* assume year is YYYY format, so need not to add 1900 */ 318 yyYear = $3; 319 } else if ($3 < 100) { 320 /* assume year is YY format, so need to add 1900 */ 321 yyYear = $3 + (yyYear / 100 + (yyYear % 100 - $3) / 50) * 100; 322 } else { 323 yyYear = 1900 + $3; 324 } 325 } 326 | tDAY ',' tUNUMBER tMONTH tUNUMBER { 327 yyDay = $3; 328 yyMonth = $4; 329 if ($5 > 999) { 330 /* assume year is YYYY format, so need not to add 1900 */ 331 yyYear = $5; 332 } else if ($5 < 100) { 333 /* assume year is YY format, so need to add 1900 */ 334 yyYear = $5 + (yyYear / 100 + (yyYear % 100 - $5) / 50) * 100; 335 } else { 336 yyYear = 1900 + $5; 337 } 338 } 339 ; 340 341 rel : tSNUMBER tSEC_UNIT { 342 yyRelSeconds += $1 * $2; 343 } 344 | tUNUMBER tSEC_UNIT { 345 yyRelSeconds += $1 * $2; 346 } 347 | tSNUMBER tMONTH_UNIT { 348 yyRelMonth += $1 * $2; 349 } 350 | tUNUMBER tMONTH_UNIT { 351 yyRelMonth += $1 * $2; 352 } 353 ; 354 355 o_merid : /* NULL */ { 356 $$ = MER24; 357 } 358 | tMERIDIAN { 359 $$ = $1; 360 } 361 ; 362 363 %% 364 365 /* Month and day table. */ 366 static TABLE MonthDayTable[] = { 367 { "january", tMONTH, 1 }, 368 { "february", tMONTH, 2 }, 369 { "march", tMONTH, 3 }, 370 { "april", tMONTH, 4 }, 371 { "may", tMONTH, 5 }, 372 { "june", tMONTH, 6 }, 373 { "july", tMONTH, 7 }, 374 { "august", tMONTH, 8 }, 375 { "september", tMONTH, 9 }, 376 { "october", tMONTH, 10 }, 377 { "november", tMONTH, 11 }, 378 { "december", tMONTH, 12 }, 379 /* The value of the day isn't used... */ 380 { "sunday", tDAY, 0 }, 381 { "monday", tDAY, 0 }, 382 { "tuesday", tDAY, 0 }, 383 { "wednesday", tDAY, 0 }, 384 { "thursday", tDAY, 0 }, 385 { "friday", tDAY, 0 }, 386 { "saturday", tDAY, 0 }, 387 }; 388 389 /* Time units table. */ 390 static TABLE UnitsTable[] = { 391 { "year", tMONTH_UNIT, 12 }, 392 { "month", tMONTH_UNIT, 1 }, 393 { "week", tSEC_UNIT, 7 * 24 * 60 * 60 }, 394 { "day", tSEC_UNIT, 1 * 24 * 60 * 60 }, 395 { "hour", tSEC_UNIT, 60 * 60 }, 396 { "minute", tSEC_UNIT, 60 }, 397 { "min", tSEC_UNIT, 60 }, 398 { "second", tSEC_UNIT, 1 }, 399 { "sec", tSEC_UNIT, 1 }, 400 }; 401 402 /* Timezone table. */ 403 static TABLE TimezoneTable[] = { 404 { "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */ 405 { "ut", tZONE, HOUR( 0) }, /* Universal */ 406 { "utc", tZONE, HOUR( 0) }, /* Universal Coordinated */ 407 { "cut", tZONE, HOUR( 0) }, /* Coordinated Universal */ 408 { "z", tZONE, HOUR( 0) }, /* Greenwich Mean */ 409 { "wet", tZONE, HOUR( 0) }, /* Western European */ 410 { "bst", tDAYZONE, HOUR( 0) }, /* British Summer */ 411 { "nst", tZONE, HOUR(3)+30 }, /* Newfoundland Standard */ 412 { "ndt", tDAYZONE, HOUR(3)+30 }, /* Newfoundland Daylight */ 413 { "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */ 414 { "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */ 415 { "est", tZONE, HOUR( 5) }, /* Eastern Standard */ 416 { "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */ 417 { "cst", tZONE, HOUR( 6) }, /* Central Standard */ 418 { "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */ 419 { "mst", tZONE, HOUR( 7) }, /* Mountain Standard */ 420 { "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */ 421 { "pst", tZONE, HOUR( 8) }, /* Pacific Standard */ 422 { "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */ 423 { "yst", tZONE, HOUR( 9) }, /* Yukon Standard */ 424 { "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */ 425 { "akst", tZONE, HOUR( 9) }, /* Alaska Standard */ 426 { "akdt", tDAYZONE, HOUR( 9) }, /* Alaska Daylight */ 427 { "hst", tZONE, HOUR(10) }, /* Hawaii Standard */ 428 { "hast", tZONE, HOUR(10) }, /* Hawaii-Aleutian Standard */ 429 { "hadt", tDAYZONE, HOUR(10) }, /* Hawaii-Aleutian Daylight */ 430 { "ces", tDAYZONE, -HOUR(1) }, /* Central European Summer */ 431 { "cest", tDAYZONE, -HOUR(1) }, /* Central European Summer */ 432 { "mez", tZONE, -HOUR(1) }, /* Middle European */ 433 { "mezt", tDAYZONE, -HOUR(1) }, /* Middle European Summer */ 434 { "cet", tZONE, -HOUR(1) }, /* Central European */ 435 { "met", tZONE, -HOUR(1) }, /* Middle European */ 436 { "eet", tZONE, -HOUR(2) }, /* Eastern Europe */ 437 { "msk", tZONE, -HOUR(3) }, /* Moscow Winter */ 438 { "msd", tDAYZONE, -HOUR(3) }, /* Moscow Summer */ 439 { "wast", tZONE, -HOUR(8) }, /* West Australian Standard */ 440 { "wadt", tDAYZONE, -HOUR(8) }, /* West Australian Daylight */ 441 { "hkt", tZONE, -HOUR(8) }, /* Hong Kong */ 442 { "cct", tZONE, -HOUR(8) }, /* China Coast */ 443 { "jst", tZONE, -HOUR(9) }, /* Japan Standard */ 444 { "kst", tZONE, -HOUR(9) }, /* Korean Standard */ 445 { "kdt", tZONE, -HOUR(9) }, /* Korean Daylight */ 446 { "cast", tZONE, -(HOUR(9)+30) }, /* Central Australian Standard */ 447 { "cadt", tDAYZONE, -(HOUR(9)+30) }, /* Central Australian Daylight */ 448 { "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */ 449 { "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */ 450 { "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */ 451 { "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */ 452 453 /* For completeness we include the following entries. */ 454 #if 0 455 456 /* Duplicate names. Either they conflict with a zone listed above 457 * (which is either more likely to be seen or just been in circulation 458 * longer), or they conflict with another zone in this section and 459 * we could not reasonably choose one over the other. */ 460 { "fst", tZONE, HOUR( 2) }, /* Fernando De Noronha Standard */ 461 { "fdt", tDAYZONE, HOUR( 2) }, /* Fernando De Noronha Daylight */ 462 { "bst", tZONE, HOUR( 3) }, /* Brazil Standard */ 463 { "est", tZONE, HOUR( 3) }, /* Eastern Standard (Brazil) */ 464 { "edt", tDAYZONE, HOUR( 3) }, /* Eastern Daylight (Brazil) */ 465 { "wst", tZONE, HOUR( 4) }, /* Western Standard (Brazil) */ 466 { "wdt", tDAYZONE, HOUR( 4) }, /* Western Daylight (Brazil) */ 467 { "cst", tZONE, HOUR( 5) }, /* Chile Standard */ 468 { "cdt", tDAYZONE, HOUR( 5) }, /* Chile Daylight */ 469 { "ast", tZONE, HOUR( 5) }, /* Acre Standard */ 470 { "adt", tDAYZONE, HOUR( 5) }, /* Acre Daylight */ 471 { "cst", tZONE, HOUR( 5) }, /* Cuba Standard */ 472 { "cdt", tDAYZONE, HOUR( 5) }, /* Cuba Daylight */ 473 { "est", tZONE, HOUR( 6) }, /* Easter Island Standard */ 474 { "edt", tDAYZONE, HOUR( 6) }, /* Easter Island Daylight */ 475 { "sst", tZONE, HOUR(11) }, /* Samoa Standard */ 476 { "ist", tZONE, -HOUR(2) }, /* Israel Standard */ 477 { "idt", tDAYZONE, -HOUR(2) }, /* Israel Daylight */ 478 { "idt", tDAYZONE, -(HOUR(3)+30) }, /* Iran Daylight */ 479 { "ist", tZONE, -(HOUR(3)+30) }, /* Iran Standard */ 480 { "cst", tZONE, -HOUR(8) }, /* China Standard */ 481 { "cdt", tDAYZONE, -HOUR(8) }, /* China Daylight */ 482 { "sst", tZONE, -HOUR(8) }, /* Singapore Standard */ 483 484 /* Dubious (e.g., not in Olson's TIMEZONE package) or obsolete. */ 485 { "gst", tZONE, HOUR( 3) }, /* Greenland Standard */ 486 { "wat", tZONE, -HOUR(1) }, /* West Africa */ 487 { "at", tZONE, HOUR( 2) }, /* Azores */ 488 { "gst", tZONE, -HOUR(10) }, /* Guam Standard */ 489 { "nft", tZONE, HOUR(3)+30 }, /* Newfoundland */ 490 { "idlw", tZONE, HOUR(12) }, /* International Date Line West */ 491 { "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */ 492 { "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */ 493 { "swt", tZONE, -HOUR(1) }, /* Swedish Winter */ 494 { "sst", tDAYZONE, -HOUR(1) }, /* Swedish Summer */ 495 { "fwt", tZONE, -HOUR(1) }, /* French Winter */ 496 { "fst", tDAYZONE, -HOUR(1) }, /* French Summer */ 497 { "bt", tZONE, -HOUR(3) }, /* Baghdad */ 498 { "it", tZONE, -(HOUR(3)+30) }, /* Iran */ 499 { "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */ 500 { "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */ 501 { "ist", tZONE, -(HOUR(5)+30) }, /* Indian Standard */ 502 { "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */ 503 { "nst", tZONE, -HOUR(7) }, /* North Sumatra */ 504 { "sst", tZONE, -HOUR(7) }, /* South Sumatra */ 505 { "jt", tZONE, -(HOUR(7)+30) }, /* Java (3pm in Cronusland!) */ 506 { "nzt", tZONE, -HOUR(12) }, /* New Zealand */ 507 { "idle", tZONE, -HOUR(12) }, /* International Date Line East */ 508 { "cat", tZONE, HOUR(10) }, /* -- expired 1967 */ 509 { "nt", tZONE, HOUR(11) }, /* -- expired 1967 */ 510 { "ahst", tZONE, HOUR(10) }, /* -- expired 1983 */ 511 { "hdt", tDAYZONE, HOUR(10) }, /* -- expired 1986 */ 512 #endif /* 0 */ 513 }; 514 515 516 517 static void 518 date_error(const char *s) 519 { 520 s = s; /* ARGSUSED */ 521 /* NOTREACHED */ 522 } 523 524 525 static time_t 526 ToSeconds(time_t Hours, time_t Minutes, time_t Seconds, MERIDIAN Meridian) 527 { 528 if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 61) 529 return -1; 530 if (Meridian == MER24) { 531 if (Hours < 0 || Hours > 23) 532 return -1; 533 } 534 else { 535 if (Hours < 1 || Hours > 12) 536 return -1; 537 if (Hours == 12) 538 Hours = 0; 539 if (Meridian == MERpm) 540 Hours += 12; 541 } 542 return (Hours * 60L + Minutes) * 60L + Seconds; 543 } 544 545 546 static time_t 547 Convert(time_t Month, time_t Day, time_t Year, time_t Hours, time_t Minutes, 548 time_t Seconds, MERIDIAN Meridian, DSTMODE dst) 549 { 550 static int DaysNormal[13] = { 551 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 552 }; 553 static int DaysLeap[13] = { 554 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 555 }; 556 static int LeapYears[] = { 557 1972, 1976, 1980, 1984, 1988, 1992, 1996, 558 2000, 2004, 2008, 2012, 2016, 2020, 2024, 2028, 2032, 2036 559 }; 560 int *yp; 561 int *mp; 562 time_t Julian; 563 int i; 564 time_t tod; 565 566 /* Year should not be passed as a relative value, but absolute one. 567 so this should not happen, but just ensure it */ 568 if (Year < 0) 569 Year = -Year; 570 if (Year < 100) { 571 Year += 1900; 572 if (Year < EPOCH) 573 Year += 100; 574 } 575 for (mp = DaysNormal, yp = LeapYears; yp < ARRAY_END(LeapYears); yp++) 576 if (Year == *yp) { 577 mp = DaysLeap; 578 break; 579 } 580 if (Year < EPOCH || Year > END_OF_TIME 581 || Month < 1 || Month > 12 582 /* NOSTRICT *//* conversion from long may lose accuracy */ 583 || Day < 1 || Day > mp[(int)Month]) 584 return -1; 585 586 Julian = Day - 1 + (Year - EPOCH) * 365; 587 for (yp = LeapYears; yp < ARRAY_END(LeapYears); yp++, Julian++) 588 if (Year <= *yp) 589 break; 590 for (i = 1; i < Month; i++) 591 Julian += *++mp; 592 Julian *= SECSPERDAY; 593 Julian += yyTimezone * 60L; 594 if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0) 595 return -1; 596 Julian += tod; 597 tod = Julian; 598 if (dst == DSTon || (dst == DSTmaybe && localtime(&tod)->tm_isdst)) 599 Julian -= DST_OFFSET * 60 * 60; 600 return Julian; 601 } 602 603 604 static time_t 605 DSTcorrect(time_t Start, time_t Future) 606 { 607 time_t StartDay; 608 time_t FutureDay; 609 610 StartDay = (localtime(&Start)->tm_hour + 1) % 24; 611 FutureDay = (localtime(&Future)->tm_hour + 1) % 24; 612 return (Future - Start) + (StartDay - FutureDay) * DST_OFFSET * 60 * 60; 613 } 614 615 616 static time_t 617 RelativeMonth(time_t Start, time_t RelMonth) 618 { 619 struct tm *tm; 620 time_t Month; 621 time_t Year; 622 623 tm = localtime(&Start); 624 Month = 12 * tm->tm_year + tm->tm_mon + RelMonth; 625 Year = Month / 12; 626 Year += 1900; 627 Month = Month % 12 + 1; 628 return DSTcorrect(Start, 629 Convert(Month, (time_t)tm->tm_mday, Year, 630 (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec, 631 MER24, DSTmaybe)); 632 } 633 634 635 static int 636 LookupWord(char *buff, int length) 637 { 638 char *p; 639 const char *q; 640 TABLE *tp; 641 int c; 642 643 p = buff; 644 c = p[0]; 645 646 /* See if we have an abbreviation for a month. */ 647 if (length == 3 || (length == 4 && p[3] == '.')) 648 for (tp = MonthDayTable; tp < ARRAY_END(MonthDayTable); tp++) { 649 q = tp->name; 650 if (c == q[0] && p[1] == q[1] && p[2] == q[2]) { 651 yylval.Number = tp->value; 652 return tp->type; 653 } 654 } 655 else 656 for (tp = MonthDayTable; tp < ARRAY_END(MonthDayTable); tp++) 657 if (c == tp->name[0] && strcmp(p, tp->name) == 0) { 658 yylval.Number = tp->value; 659 return tp->type; 660 } 661 662 /* Try for a timezone. */ 663 for (tp = TimezoneTable; tp < ARRAY_END(TimezoneTable); tp++) 664 if (c == tp->name[0] && p[1] == tp->name[1] 665 && strcmp(p, tp->name) == 0) { 666 yylval.Number = tp->value; 667 return tp->type; 668 } 669 670 /* Try the units table. */ 671 for (tp = UnitsTable; tp < ARRAY_END(UnitsTable); tp++) 672 if (c == tp->name[0] && strcmp(p, tp->name) == 0) { 673 yylval.Number = tp->value; 674 return tp->type; 675 } 676 677 /* Strip off any plural and try the units table again. */ 678 if (--length > 0 && p[length] == 's') { 679 p[length] = '\0'; 680 for (tp = UnitsTable; tp < ARRAY_END(UnitsTable); tp++) 681 if (c == tp->name[0] && strcmp(p, tp->name) == 0) { 682 p[length] = 's'; 683 yylval.Number = tp->value; 684 return tp->type; 685 } 686 p[length] = 's'; 687 } 688 length++; 689 690 /* Drop out any periods. */ 691 for (p = buff, q = buff; *q; q++) 692 if (*q != '.') 693 *p++ = *q; 694 *p = '\0'; 695 696 /* Try the meridians. */ 697 if (buff[1] == 'm' && buff[2] == '\0') { 698 if (buff[0] == 'a') { 699 yylval.Meridian = MERam; 700 return tMERIDIAN; 701 } 702 if (buff[0] == 'p') { 703 yylval.Meridian = MERpm; 704 return tMERIDIAN; 705 } 706 } 707 708 /* If we saw any periods, try the timezones again. */ 709 if (p - buff != length) { 710 c = buff[0]; 711 for (p = buff, tp = TimezoneTable; tp < ARRAY_END(TimezoneTable); tp++) 712 if (c == tp->name[0] && p[1] == tp->name[1] 713 && strcmp(p, tp->name) == 0) { 714 yylval.Number = tp->value; 715 return tp->type; 716 } 717 } 718 719 /* Unknown word -- assume GMT timezone. */ 720 yylval.Number = 0; 721 return tZONE; 722 } 723 724 725 static int 726 date_lex(void) 727 { 728 char c; 729 char *p; 730 char buff[20]; 731 int sign; 732 int i; 733 int nesting; 734 735 for ( ; ; ) { 736 /* Get first character after the whitespace. */ 737 for ( ; ; ) { 738 while (CTYPE(isspace, (int)*yyInput)) 739 yyInput++; 740 c = *yyInput; 741 742 /* Ignore RFC 822 comments, typically time zone names. */ 743 if (c != LPAREN) 744 break; 745 for (nesting = 1; (c = *++yyInput) != RPAREN || --nesting; ) 746 if (c == LPAREN) 747 nesting++; 748 else if (!IS7BIT(c) || c == '\0' || c == '\r' 749 || (c == '\\' && ((c = *++yyInput) == '\0' || !IS7BIT(c)))) 750 /* Lexical error: bad comment. */ 751 return '?'; 752 yyInput++; 753 } 754 755 /* A number? */ 756 if (CTYPE(isdigit, (int)c) || c == '-' || c == '+') { 757 if (c == '-' || c == '+') { 758 sign = c == '-' ? -1 : 1; 759 yyInput++; 760 if (!CTYPE(isdigit, (int)*yyInput)) 761 /* Skip the plus or minus sign. */ 762 continue; 763 } 764 else 765 sign = 0; 766 for (i = 0; (c = *yyInput++) != '\0' && CTYPE(isdigit, (int)c); ) 767 i = 10 * i + c - '0'; 768 yyInput--; 769 yylval.Number = sign < 0 ? -i : i; 770 return sign ? tSNUMBER : tUNUMBER; 771 } 772 773 /* A word? */ 774 if (CTYPE(isalpha, (int)c)) { 775 for (p = buff; (c = *yyInput++) == '.' || CTYPE(isalpha, (int)c); ) 776 if (p < &buff[sizeof buff - 1]) 777 *p++ = CTYPE(isupper, (int)c) ? tolower(c) : c; 778 *p = '\0'; 779 yyInput--; 780 return LookupWord(buff, p - buff); 781 } 782 783 return *yyInput++; 784 } 785 } 786 787 788 time_t 789 parsedate(char *p) 790 { 791 time_t now; 792 struct tm *tm; 793 time_t Start; 794 795 now = time(NULL); 796 yyInput = p; 797 798 tm = gmtime(&now); 799 yyYear = tm->tm_year + 1900; 800 yyMonth = tm->tm_mon + 1; 801 yyDay = tm->tm_mday; 802 yyTimezone = 0; 803 yyDSTmode = DSTmaybe; 804 yyHour = 0; 805 yyMinutes = 0; 806 yySeconds = 0; 807 yyMeridian = MER24; 808 yyRelSeconds = 0; 809 yyRelMonth = 0; 810 yyHaveDate = 0; 811 yyHaveRel = 0; 812 yyHaveTime = 0; 813 814 if (date_parse() || yyHaveTime > 1 || yyHaveDate > 1) 815 return -1; 816 817 if (yyHaveDate || yyHaveTime) { 818 Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds, 819 yyMeridian, yyDSTmode); 820 if (Start < 0) 821 return -1; 822 } 823 else { 824 Start = now; 825 if (!yyHaveRel) 826 Start -= (tm->tm_hour * 60L + tm->tm_min) * 60L + tm->tm_sec; 827 } 828 829 Start += yyRelSeconds; 830 if (yyRelMonth) 831 Start += RelativeMonth(Start, yyRelMonth); 832 833 /* Have to do *something* with a legitimate -1 so it's distinguishable 834 * from the error return value. (Alternately could set errno on error.) */ 835 return Start == -1 ? 0 : Start; 836 } 837 838 839 #if defined(TEST) 840 841 #if YYDEBUG 842 extern int yydebug; 843 #endif /* YYDEBUG */ 844 845 /* ARGSUSED */ 846 int 847 main(int ac, char *av[]) 848 { 849 char buff[128]; 850 time_t d; 851 852 #if YYDEBUG 853 yydebug = 1; 854 #endif /* YYDEBUG */ 855 856 printf("Enter date, or blank line to exit.\n\t> "); 857 for ( ; ; ) { 858 printf("\t> "); 859 fflush(stdout); 860 if (gets(buff) == NULL || buff[0] == '\n') 861 break; 862 #if YYDEBUG 863 if (strcmp(buff, "yydebug") == 0) { 864 yydebug = !yydebug; 865 printf("yydebug = %s\n", yydebug ? "on" : "off"); 866 continue; 867 } 868 #endif /* YYDEBUG */ 869 d = parsedate(buff); 870 if (d == -1) 871 printf("Bad format - couldn't convert.\n"); 872 else 873 printf("%s", ctime(&d)); 874 } 875 876 exit(0); 877 /* NOTREACHED */ 878 } 879 #endif /* defined(TEST) */