3 ** Originally written by Steven M. Bellovin <smb@research.att.com> while
4 ** at the University of North Carolina at Chapel Hill. Later tweaked by
5 ** a couple of people on Usenet. Completely overhauled by Rich $alz
6 ** <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990;
8 ** This grammar has 10 shift/reduce conflicts.
10 ** This code is in the public domain and has no copyright.
12 /* SUPPRESS 287 on yaccpar_sccsid *//* Unused static variable */
13 /* SUPPRESS 288 on yyerrlab *//* Label unused */
22 /* NOTES on rebuilding parsedate.c (particularly for inclusion in CVS
25 We don't want to mess with all the portability hassles of alloca.
26 In particular, most (all?) versions of bison will use alloca in
27 their parser. If bison works on your system (e.g. it should work
28 with gcc), then go ahead and use it, but the more general solution
29 is to use byacc instead of bison, which should generate a portable
30 parser. I played with adding "#define alloca dont_use_alloca", to
31 give an error if the parser generator uses alloca (and thus detect
32 unportable parsedate.c's), but that seems to cause as many problems
36 #define HOUR(x) ((time_t)(x) * 60)
37 #define SECSPERDAY (24L * 60L * 60L)
41 ** An entry in the lexical lookup table.
43 typedef
struct _TABLE
{
51 ** Daylight-savings mode: on, off, or not yet known.
53 typedef
enum _DSTMODE
{
54 DSTon
, DSToff
, DSTmaybe
58 ** Meridian: am, pm, or 24-hour style.
60 typedef
enum _MERIDIAN
{
89 enum _MERIDIAN Meridian
;
92 %token tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
93 %token tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST AT_SIGN
95 %type
<Number
> tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT
96 %type
<Number
> tSEC_UNIT tSNUMBER tUNUMBER tZONE
97 %type
<Meridian
> tMERIDIAN o_merid
99 %parse
-param
{ struct dateinfo
*param
}
100 %parse
-param
{ const char **yyInput
}
101 %lex
-param
{ const char **yyInput
}
138 cvsstamp: tUNUMBER
'.' tUNUMBER
'.' tUNUMBER
'.' tUNUMBER
'.' tUNUMBER
'.' tUNUMBER
{
140 if
(param
->yyYear
< 100) param
->yyYear
+= 1900;
144 param
->yyMinutes
= $9;
145 param
->yySeconds
= $11;
146 param
->yyDSTmode
= DSToff
;
147 param
->yyTimezone
= 0;
151 epochdate: AT_SIGN tUNUMBER
{
154 if
(gmtime_r
(&when
, &tmbuf
) != NULL
) {
155 param
->yyYear
= tmbuf.tm_year
+ 1900;
156 param
->yyMonth
= tmbuf.tm_mon
+ 1;
157 param
->yyDay
= tmbuf.tm_mday
;
159 param
->yyHour
= tmbuf.tm_hour
;
160 param
->yyMinutes
= tmbuf.tm_min
;
161 param
->yySeconds
= tmbuf.tm_sec
;
163 param
->yyYear
= EPOCH
;
168 param
->yyMinutes
= 0;
169 param
->yySeconds
= 0;
171 param
->yyDSTmode
= DSToff
;
172 param
->yyTimezone
= 0;
176 time
: tUNUMBER tMERIDIAN
{
178 param
->yyMinutes
= 0;
179 param
->yySeconds
= 0;
180 param
->yyMeridian
= $2;
182 | tUNUMBER
':' tUNUMBER o_merid
{
184 param
->yyMinutes
= $3;
185 param
->yySeconds
= 0;
186 param
->yyMeridian
= $4;
188 | tUNUMBER
':' tUNUMBER tSNUMBER
{
190 param
->yyMinutes
= $3;
191 param
->yyMeridian
= MER24
;
192 param
->yyDSTmode
= DSToff
;
193 param
->yyTimezone
= - ($4 %
100 + ($4 / 100) * 60);
195 | tUNUMBER
':' tUNUMBER
':' tUNUMBER o_merid
{
197 param
->yyMinutes
= $3;
198 param
->yySeconds
= $5;
199 param
->yyMeridian
= $6;
201 | tUNUMBER
':' tUNUMBER
':' tUNUMBER tSNUMBER
{
203 param
->yyMinutes
= $3;
204 param
->yySeconds
= $5;
205 param
->yyMeridian
= MER24
;
206 param
->yyDSTmode
= DSToff
;
207 param
->yyTimezone
= - ($6 %
100 + ($6 / 100) * 60);
209 | tUNUMBER
':' tUNUMBER
':' tUNUMBER
'.' tUNUMBER
{
211 param
->yyMinutes
= $3;
212 param
->yySeconds
= $5;
213 param
->yyMeridian
= MER24
;
214 param
->yyDSTmode
= DSToff
;
215 /* XXX: Do nothing with millis */
216 /* param->yyTimezone = ($7 % 100 + ($7 / 100) * 60); */
221 param
->yyTimezone
= $1;
222 param
->yyDSTmode
= DSToff
;
225 param
->yyTimezone
= $1;
226 param
->yyDSTmode
= DSTon
;
230 param
->yyTimezone
= $1;
231 param
->yyDSTmode
= DSTon
;
236 param
->yyDayOrdinal
= 1;
237 param
->yyDayNumber
= $1;
240 param
->yyDayOrdinal
= 1;
241 param
->yyDayNumber
= $1;
244 param
->yyDayOrdinal
= $1;
245 param
->yyDayNumber
= $2;
249 date
: tUNUMBER
'/' tUNUMBER
{
253 | tUNUMBER
'/' tUNUMBER
'/' tUNUMBER
{
264 | tUNUMBER tSNUMBER tSNUMBER
{
265 /* ISO 8601 format. yyyy-mm-dd. */
267 param
->yyMonth
= -$2;
270 | tUNUMBER tMONTH tSNUMBER
{
271 /* e.g. 17-JUN-1992. */
280 | tMONTH tUNUMBER
',' tUNUMBER
{
289 | tUNUMBER tMONTH tUNUMBER
{
297 param
->yyRelSeconds
= -param
->yyRelSeconds
;
298 param
->yyRelMonth
= -param
->yyRelMonth
;
303 relunit
: tUNUMBER tMINUTE_UNIT
{
304 param
->yyRelSeconds
+= $1 * $2 * 60L;
306 | tSNUMBER tMINUTE_UNIT
{
307 param
->yyRelSeconds
+= $1 * $2 * 60L;
310 param
->yyRelSeconds
+= $1 * 60L;
312 | tSNUMBER tSEC_UNIT
{
313 param
->yyRelSeconds
+= $1;
315 | tUNUMBER tSEC_UNIT
{
316 param
->yyRelSeconds
+= $1;
319 param
->yyRelSeconds
++;
321 | tSNUMBER tMONTH_UNIT
{
322 param
->yyRelMonth
+= $1 * $2;
324 | tUNUMBER tMONTH_UNIT
{
325 param
->yyRelMonth
+= $1 * $2;
328 param
->yyRelMonth
+= $1;
333 if
(param
->yyHaveTime
&& param
->yyHaveDate
&& !param
->yyHaveRel
)
338 param
->yyDay
= ($1)%
100;
339 param
->yyMonth
= ($1/100)%
100;
340 param
->yyYear
= $1/10000;
346 param
->yyMinutes
= 0;
349 param
->yyHour
= $1 / 100;
350 param
->yyMinutes
= $1 %
100;
352 param
->yySeconds
= 0;
353 param
->yyMeridian
= MER24
;
359 o_merid
: /* NULL */ {
369 /* Month and day table. */
370 static const TABLE
const MonthDayTable
[] = {
371 { "january", tMONTH
, 1 },
372 { "february", tMONTH
, 2 },
373 { "march", tMONTH
, 3 },
374 { "april", tMONTH
, 4 },
375 { "may", tMONTH
, 5 },
376 { "june", tMONTH
, 6 },
377 { "july", tMONTH
, 7 },
378 { "august", tMONTH
, 8 },
379 { "september", tMONTH
, 9 },
380 { "sept", tMONTH
, 9 },
381 { "october", tMONTH
, 10 },
382 { "november", tMONTH
, 11 },
383 { "december", tMONTH
, 12 },
384 { "sunday", tDAY
, 0 },
385 { "monday", tDAY
, 1 },
386 { "tuesday", tDAY
, 2 },
388 { "wednesday", tDAY
, 3 },
389 { "wednes", tDAY
, 3 },
390 { "thursday", tDAY
, 4 },
392 { "thurs", tDAY
, 4 },
393 { "friday", tDAY
, 5 },
394 { "saturday", tDAY
, 6 },
398 /* Time units table. */
399 static const TABLE
const UnitsTable
[] = {
400 { "year", tMONTH_UNIT
, 12 },
401 { "month", tMONTH_UNIT
, 1 },
402 { "fortnight", tMINUTE_UNIT
, 14 * 24 * 60 },
403 { "week", tMINUTE_UNIT
, 7 * 24 * 60 },
404 { "day", tMINUTE_UNIT
, 1 * 24 * 60 },
405 { "hour", tMINUTE_UNIT
, 60 },
406 { "minute", tMINUTE_UNIT
, 1 },
407 { "min", tMINUTE_UNIT
, 1 },
408 { "second", tSEC_UNIT
, 1 },
409 { "sec", tSEC_UNIT
, 1 },
413 /* Assorted relative-time words. */
414 static const TABLE
const OtherTable
[] = {
415 { "tomorrow", tMINUTE_UNIT
, 1 * 24 * 60 },
416 { "yesterday", tMINUTE_UNIT
, -1 * 24 * 60 },
417 { "today", tMINUTE_UNIT
, 0 },
418 { "now", tMINUTE_UNIT
, 0 },
419 { "last", tUNUMBER
, -1 },
420 { "this", tMINUTE_UNIT
, 0 },
421 { "next", tUNUMBER
, 2 },
422 { "first", tUNUMBER
, 1 },
423 { "one", tUNUMBER
, 1 },
424 /* { "second", tUNUMBER, 2 }, */
425 { "two", tUNUMBER
, 2 },
426 { "third", tUNUMBER
, 3 },
427 { "three", tUNUMBER
, 3 },
428 { "fourth", tUNUMBER
, 4 },
429 { "four", tUNUMBER
, 4 },
430 { "fifth", tUNUMBER
, 5 },
431 { "five", tUNUMBER
, 5 },
432 { "sixth", tUNUMBER
, 6 },
433 { "six", tUNUMBER
, 6 },
434 { "seventh", tUNUMBER
, 7 },
435 { "seven", tUNUMBER
, 7 },
436 { "eighth", tUNUMBER
, 8 },
437 { "eight", tUNUMBER
, 8 },
438 { "ninth", tUNUMBER
, 9 },
439 { "nine", tUNUMBER
, 9 },
440 { "tenth", tUNUMBER
, 10 },
441 { "ten", tUNUMBER
, 10 },
442 { "eleventh", tUNUMBER
, 11 },
443 { "eleven", tUNUMBER
, 11 },
444 { "twelfth", tUNUMBER
, 12 },
445 { "twelve", tUNUMBER
, 12 },
450 /* The timezone table. */
451 /* Some of these are commented out because a time_t can't store a float. */
452 static const TABLE
const TimezoneTable
[] = {
453 { "gmt", tZONE
, HOUR
( 0) }, /* Greenwich Mean */
454 { "ut", tZONE
, HOUR
( 0) }, /* Universal (Coordinated) */
455 { "utc", tZONE
, HOUR
( 0) },
456 { "wet", tZONE
, HOUR
( 0) }, /* Western European */
457 { "bst", tDAYZONE
, HOUR
( 0) }, /* British Summer */
458 { "wat", tZONE
, HOUR
( 1) }, /* West Africa */
459 { "at", tZONE
, HOUR
( 2) }, /* Azores */
461 /* For completeness. BST is also British Summer, and GST is
462 * also Guam Standard. */
463 { "bst", tZONE
, HOUR
( 3) }, /* Brazil Standard */
464 { "gst", tZONE
, HOUR
( 3) }, /* Greenland Standard */
467 { "nft", tZONE
, HOUR
(3.5) }, /* Newfoundland */
468 { "nst", tZONE
, HOUR
(3.5) }, /* Newfoundland Standard */
469 { "ndt", tDAYZONE
, HOUR
(3.5) }, /* Newfoundland Daylight */
471 { "ast", tZONE
, HOUR
( 4) }, /* Atlantic Standard */
472 { "adt", tDAYZONE
, HOUR
( 4) }, /* Atlantic Daylight */
473 { "est", tZONE
, HOUR
( 5) }, /* Eastern Standard */
474 { "edt", tDAYZONE
, HOUR
( 5) }, /* Eastern Daylight */
475 { "cst", tZONE
, HOUR
( 6) }, /* Central Standard */
476 { "cdt", tDAYZONE
, HOUR
( 6) }, /* Central Daylight */
477 { "mst", tZONE
, HOUR
( 7) }, /* Mountain Standard */
478 { "mdt", tDAYZONE
, HOUR
( 7) }, /* Mountain Daylight */
479 { "pst", tZONE
, HOUR
( 8) }, /* Pacific Standard */
480 { "pdt", tDAYZONE
, HOUR
( 8) }, /* Pacific Daylight */
481 { "yst", tZONE
, HOUR
( 9) }, /* Yukon Standard */
482 { "ydt", tDAYZONE
, HOUR
( 9) }, /* Yukon Daylight */
483 { "hst", tZONE
, HOUR
(10) }, /* Hawaii Standard */
484 { "hdt", tDAYZONE
, HOUR
(10) }, /* Hawaii Daylight */
485 { "cat", tZONE
, HOUR
(10) }, /* Central Alaska */
486 { "ahst", tZONE
, HOUR
(10) }, /* Alaska-Hawaii Standard */
487 { "nt", tZONE
, HOUR
(11) }, /* Nome */
488 { "idlw", tZONE
, HOUR
(12) }, /* International Date Line West */
489 { "cet", tZONE
, -HOUR
(1) }, /* Central European */
490 { "met", tZONE
, -HOUR
(1) }, /* Middle European */
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 { "eet", tZONE
, -HOUR
(2) }, /* Eastern Europe, USSR Zone 1 */
498 { "bt", tZONE
, -HOUR
(3) }, /* Baghdad, USSR Zone 2 */
500 { "it", tZONE
, -HOUR
(3.5) },/* Iran */
502 { "zp4", tZONE
, -HOUR
(4) }, /* USSR Zone 3 */
503 { "zp5", tZONE
, -HOUR
(5) }, /* USSR Zone 4 */
505 { "ist", tZONE
, -HOUR
(5.5) },/* Indian Standard */
507 { "zp6", tZONE
, -HOUR
(6) }, /* USSR Zone 5 */
509 /* For completeness. NST is also Newfoundland Stanard, and SST is
510 * also Swedish Summer. */
511 { "nst", tZONE
, -HOUR
(6.5) },/* North Sumatra */
512 { "sst", tZONE
, -HOUR
(7) }, /* South Sumatra, USSR Zone 6 */
514 { "wast", tZONE
, -HOUR
(7) }, /* West Australian Standard */
515 { "wadt", tDAYZONE
, -HOUR
(7) }, /* West Australian Daylight */
517 { "jt", tZONE
, -HOUR
(7.5) },/* Java (3pm in Cronusland!) */
519 { "cct", tZONE
, -HOUR
(8) }, /* China Coast, USSR Zone 7 */
520 { "jst", tZONE
, -HOUR
(9) }, /* Japan Standard, USSR Zone 8 */
522 { "cast", tZONE
, -HOUR
(9.5) },/* Central Australian Standard */
523 { "cadt", tDAYZONE
, -HOUR
(9.5) },/* Central Australian Daylight */
525 { "east", tZONE
, -HOUR
(10) }, /* Eastern Australian Standard */
526 { "eadt", tDAYZONE
, -HOUR
(10) }, /* Eastern Australian Daylight */
527 { "gst", tZONE
, -HOUR
(10) }, /* Guam Standard, USSR Zone 9 */
528 { "nzt", tZONE
, -HOUR
(12) }, /* New Zealand */
529 { "nzst", tZONE
, -HOUR
(12) }, /* New Zealand Standard */
530 { "nzdt", tDAYZONE
, -HOUR
(12) }, /* New Zealand Daylight */
531 { "idle", tZONE
, -HOUR
(12) }, /* International Date Line East */
535 /* Military timezone table. */
536 static const TABLE
const MilitaryTable
[] = {
537 { "a", tZONE
, HOUR
( 1) },
538 { "b", tZONE
, HOUR
( 2) },
539 { "c", tZONE
, HOUR
( 3) },
540 { "d", tZONE
, HOUR
( 4) },
541 { "e", tZONE
, HOUR
( 5) },
542 { "f", tZONE
, HOUR
( 6) },
543 { "g", tZONE
, HOUR
( 7) },
544 { "h", tZONE
, HOUR
( 8) },
545 { "i", tZONE
, HOUR
( 9) },
546 { "k", tZONE
, HOUR
( 10) },
547 { "l", tZONE
, HOUR
( 11) },
548 { "m", tZONE
, HOUR
( 12) },
549 { "n", tZONE
, HOUR
(- 1) },
550 { "o", tZONE
, HOUR
(- 2) },
551 { "p", tZONE
, HOUR
(- 3) },
552 { "q", tZONE
, HOUR
(- 4) },
553 { "r", tZONE
, HOUR
(- 5) },
554 { "s", tZONE
, HOUR
(- 6) },
555 { "t", tZONE
, HOUR
(- 7) },
556 { "u", tZONE
, HOUR
(- 8) },
557 { "v", tZONE
, HOUR
(- 9) },
558 { "w", tZONE
, HOUR
(-10) },
559 { "x", tZONE
, HOUR
(-11) },
560 { "y", tZONE
, HOUR
(-12) },
561 { "z", tZONE
, HOUR
( 0) },
570 yyerror(struct dateinfo
*param
, const char **inp
, const char *s __unused
)
584 if
(Minutes
< 0 || Minutes
> 59 || Seconds
< 0 || Seconds
> 59)
588 if
(Hours
< 0 || Hours
> 23)
590 return
(Hours
* 60L + Minutes
) * 60L + Seconds
;
592 if
(Hours
< 1 || Hours
> 12)
596 return
(Hours
* 60L + Minutes
) * 60L + Seconds
;
598 if
(Hours
< 1 || Hours
> 12)
602 return
((Hours
+ 12) * 60L + Minutes
) * 60L + Seconds
;
612 return year %
4 == 0 && (year %
100 != 0 || year %
400 == 0);
617 * A negative number, which means to use its absolute value (why?)
618 * A number from 0 to 99, which means a year from 1900 to 1999, or
619 * The actual year (>=100). */
633 static int DaysInMonth
[12] = {
634 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
637 time_t Julian
, oJulian
;
647 DaysInMonth
[1] = isLeap
(Year
) ?
29 : 28;
648 if
(Year
< EPOCH || Month
< 1 || Month
> 12
649 /* Lint fluff: "conversion from long may lose accuracy" */
650 || Day
< 1 || Day
> DaysInMonth
[(int)--Month
])
652 * It would be nice to set a global error string here.
653 * "February 30 is not a valid date" is much more informative than
654 * "Can't parse date/time: 100 months" when the user input was
655 * "100 months" and addition resolved that to February 30, for
656 * example. See rcs2-7 in src/sanity.sh for more. */
659 for
(Julian
= Day
- 1, i
= 0; i
< Month
; i
++)
660 Julian
+= DaysInMonth
[i
];
663 for
(i
= EPOCH
; i
< Year
; i
++) {
664 Julian
+= 365 + isLeap
(i
);
665 if
(oJulian
> Julian
)
670 Julian
*= SECSPERDAY
;
671 if
(oJulian
> Julian
)
674 Julian
+= Timezone
* 60L;
675 if
(Timezone
> 0 && oJulian
> Julian
)
679 if
((tod
= ToSeconds
(Hours
, Minutes
, Seconds
, Meridian
)) < 0)
683 if
(oJulian
> Julian
)
686 if
(DSTmode
== DSTon ||
(DSTmode
== DSTmaybe
)) {
688 if
((tm
= localtime
(&Julian
)) == NULL
)
707 if
((tm
= localtime
(&Start
)) == NULL
)
709 StartDay
= (tm
->tm_hour
+ 1) %
24;
711 if
((tm
= localtime
(&Future
)) == NULL
)
713 FutureDay
= (tm
->tm_hour
+ 1) %
24;
715 return
(Future
- Start
) + (StartDay
- FutureDay
) * 60L * 60L;
730 tm
= localtime
(&now
);
731 now
+= SECSPERDAY
* ((DayNumber
- tm
->tm_wday
+ 7) %
7);
732 now
+= 7 * SECSPERDAY
* (DayOrdinal
<= 0 ? DayOrdinal
: DayOrdinal
- 1);
733 return DSTcorrect
(Start
, now
);
750 tm
= localtime
(&Start
);
753 Month
= 12 * (tm
->tm_year
+ 1900) + tm
->tm_mon
+ RelMonth
;
755 Month
= Month %
12 + 1;
756 return DSTcorrect
(Start
,
757 Convert
(Month
, (time_t)tm
->tm_mday
, Year
,
758 (time_t)tm
->tm_hour
, (time_t)tm
->tm_min
, (time_t)tm
->tm_sec
,
759 Timezone
, MER24
, DSTmaybe
));
764 LookupWord
(YYSTYPE *yylval, char *buff
)
768 register
const TABLE
*tp
;
772 /* Make it lowercase. */
773 for
(p
= buff
; *p
; p
++)
774 if
(isupper
((unsigned char)*p
))
775 *p
= tolower
((unsigned char)*p
);
777 if
(strcmp
(buff
, "am") == 0 || strcmp
(buff
, "a.m.") == 0) {
778 yylval->Meridian
= MERam
;
781 if
(strcmp
(buff
, "pm") == 0 || strcmp
(buff
, "p.m.") == 0) {
782 yylval->Meridian
= MERpm
;
786 /* See if we have an abbreviation for a month. */
787 if
(strlen
(buff
) == 3)
789 else if
(strlen
(buff
) == 4 && buff
[3] == '.') {
796 for
(tp
= MonthDayTable
; tp
->name
; tp
++) {
798 if
(strncmp
(buff
, tp
->name
, 3) == 0) {
799 yylval->Number
= tp
->value
;
803 else if
(strcmp
(buff
, tp
->name
) == 0) {
804 yylval->Number
= tp
->value
;
809 for
(tp
= TimezoneTable
; tp
->name
; tp
++)
810 if
(strcmp
(buff
, tp
->name
) == 0) {
811 yylval->Number
= tp
->value
;
815 if
(strcmp
(buff
, "dst") == 0)
818 for
(tp
= UnitsTable
; tp
->name
; tp
++)
819 if
(strcmp
(buff
, tp
->name
) == 0) {
820 yylval->Number
= tp
->value
;
824 /* Strip off any plural and try the units table again. */
825 i
= strlen
(buff
) - 1;
826 if
(buff
[i
] == 's') {
828 for
(tp
= UnitsTable
; tp
->name
; tp
++)
829 if
(strcmp
(buff
, tp
->name
) == 0) {
830 yylval->Number
= tp
->value
;
833 buff
[i
] = 's'; /* Put back for "this" in OtherTable. */
836 for
(tp
= OtherTable
; tp
->name
; tp
++)
837 if
(strcmp
(buff
, tp
->name
) == 0) {
838 yylval->Number
= tp
->value
;
842 /* Military timezones. */
843 if
(buff
[1] == '\0' && isalpha
((unsigned char)*buff
)) {
844 for
(tp
= MilitaryTable
; tp
->name
; tp
++)
845 if
(strcmp
(buff
, tp
->name
) == 0) {
846 yylval->Number
= tp
->value
;
851 /* Drop out any periods and try the timezone table again. */
852 for
(i
= 0, p
= q
= buff
; *q
; q
++)
859 for
(tp
= TimezoneTable
; tp
->name
; tp
++)
860 if
(strcmp
(buff
, tp
->name
) == 0) {
861 yylval->Number
= tp
->value
;
870 yylex(YYSTYPE *yylval, const char **yyInput
)
877 const char *inp
= *yyInput
;
880 while
(isspace
((unsigned char)*inp
))
883 if
(isdigit
((unsigned char)(c
= *inp
)) || c
== '-' || c
== '+') {
884 if
(c
== '-' || c
== '+') {
885 sign
= c
== '-' ?
-1 : 1;
886 if
(!isdigit
((unsigned char)*++inp
))
887 /* skip the '-' sign */
892 for
(yylval->Number
= 0; isdigit
((unsigned char)(c
= *inp
++)); )
893 yylval->Number
= 10 * yylval->Number
+ c
- '0';
895 yylval->Number
= -yylval->Number
;
897 return sign ? tSNUMBER
: tUNUMBER
;
899 if
(isalpha
((unsigned char)c
)) {
900 for
(p
= buff
; isalpha
((unsigned char)(c
= *inp
++)) || c
== '.'; )
901 if
(p
< &buff
[sizeof buff
- 1])
905 return LookupWord
(yylval, buff
);
928 #define TM_YEAR_ORIGIN 1900
930 /* Yield A - B, measured in seconds. */
932 difftm
(struct tm
*a
, struct tm
*b
)
934 int ay
= a
->tm_year
+ (TM_YEAR_ORIGIN
- 1);
935 int by
= b
->tm_year
+ (TM_YEAR_ORIGIN
- 1);
937 /* difference in day of year */
938 a
->tm_yday
- b
->tm_yday
939 /* + intervening leap days */
940 + ((ay
>> 2) - (by
>> 2))
942 + ((ay
/100 >> 2) - (by
/100 >> 2))
943 /* + difference in years * 365 */
944 + (long)(ay
-by
) * 365
946 return
((time_t)60*(60*(24*days
+ (a
->tm_hour
- b
->tm_hour
))
947 + (a
->tm_min
- b
->tm_min
))
948 + (a
->tm_sec
- b
->tm_sec
));
952 parsedate
(const char *p
, const time_t *now
, const int *zone
)
954 struct tm gmt
, local
, *gmt_ptr
, *tm
;
959 struct dateinfo param
;
961 if
(now
== NULL || zone
== NULL
) {
966 gmt_ptr
= gmtime_r
(now
, &gmt
);
967 if
((tm
= localtime_r
(now
, &local
)) == NULL
)
971 zonet
= difftm
(&gmt
, &local
) / 60;
973 /* We are on a system like VMS, where the system clock is
974 in local time and the system has no concept of timezones.
975 Hopefully we can fake this out (for the case in which the
976 user specifies no timezone) by just saying the timezone
983 if
((tm
= localtime_r
(now
, &local
)) == NULL
)
986 param.yyYear
= tm
->tm_year
+ 1900;
987 param.yyMonth
= tm
->tm_mon
+ 1;
988 param.yyDay
= tm
->tm_mday
;
989 param.yyTimezone
= *zone
;
990 param.yyDSTmode
= DSTmaybe
;
994 param.yyMeridian
= MER24
;
995 param.yyRelSeconds
= 0;
996 param.yyRelMonth
= 0;
997 param.yyHaveDate
= 0;
1000 param.yyHaveTime
= 0;
1001 param.yyHaveZone
= 0;
1003 if
(yyparse(¶m
, &p
) || param.yyHaveTime
> 1 || param.yyHaveZone
> 1 ||
1004 param.yyHaveDate
> 1 || param.yyHaveDay
> 1)
1007 if
(param.yyHaveDate || param.yyHaveTime || param.yyHaveDay
) {
1008 Start
= Convert
(param.yyMonth
, param.yyDay
, param.yyYear
, param.yyHour
,
1009 param.yyMinutes
, param.yySeconds
, param.yyTimezone
,
1010 param.yyMeridian
, param.yyDSTmode
);
1016 if
(!param.yyHaveRel
)
1017 Start
-= ((tm
->tm_hour
* 60L + tm
->tm_min
) * 60L) + tm
->tm_sec
;
1020 Start
+= param.yyRelSeconds
;
1021 rm
= RelativeMonth
(Start
, param.yyRelMonth
, param.yyTimezone
);
1026 if
(param.yyHaveDay
&& !param.yyHaveDate
) {
1027 tod
= RelativeDate
(Start
, param.yyDayOrdinal
, param.yyDayNumber
);
1046 (void)printf
("Enter date, or blank line to exit.\n\t> ");
1047 (void)fflush
(stdout
);
1048 while
(gets
(buff
) && buff
[0]) {
1049 d
= parsedate
(buff
, NULL
, NULL
);
1051 (void)printf
("Bad format - couldn't convert.\n");
1053 (void)printf
("%s", ctime
(&d
));
1054 (void)printf
("\t> ");
1055 (void)fflush
(stdout
);
1060 #endif /* defined(TEST) */