]> git.saurik.com Git - apple/shell_cmds.git/blame - find/getdate.y
shell_cmds-116.tar.gz
[apple/shell_cmds.git] / find / getdate.y
CommitLineData
e1a085ba
A
1%{
2/*
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;
7**
8** This grammar has 10 shift/reduce conflicts.
9**
10** This code is in the public domain and has no copyright.
11*/
12/* SUPPRESS 287 on yaccpar_sccsid *//* Unused static variable */
13/* SUPPRESS 288 on yyerrlab *//* Label unused */
14
15#include <sys/cdefs.h>
16__FBSDID("$FreeBSD: src/usr.bin/find/getdate.y,v 1.4 2005/08/25 13:44:02 roberto Exp $");
17
18#include <stdio.h>
19#include <ctype.h>
20
21/* The code at the top of get_date which figures out the offset of the
22 current time zone checks various CPP symbols to see if special
23 tricks are need, but defaults to using the gettimeofday system call.
24 Include <sys/time.h> if that will be used. */
25
26#if defined(vms)
27# include <types.h>
28#else /* defined(vms) */
29# include <sys/types.h>
30# include <sys/time.h>
31# include <sys/timeb.h>
32#endif /* !defined(vms) */
33
34#if defined (__STDC__) || defined (USG)
35#include <string.h>
36#endif
37
38/* Some old versions of bison generate parsers that use bcopy.
39 That loses on systems that don't provide the function, so we have
40 to redefine it here. */
41#if !defined (HAVE_BCOPY) && defined (HAVE_MEMCPY) && !defined (bcopy)
42#define bcopy(from, to, len) memcpy ((to), (from), (len))
43#endif
44
45#if defined (__STDC__)
46#include <stdlib.h>
47#endif
48
49/* NOTES on rebuilding getdate.c (particularly for inclusion in CVS
50 releases):
51
52 We don't want to mess with all the portability hassles of alloca.
53 In particular, most (all?) versions of bison will use alloca in
54 their parser. If bison works on your system (e.g. it should work
55 with gcc), then go ahead and use it, but the more general solution
56 is to use byacc instead of bison, which should generate a portable
57 parser. I played with adding "#define alloca dont_use_alloca", to
58 give an error if the parser generator uses alloca (and thus detect
59 unportable getdate.c's), but that seems to cause as many problems
60 as it solves. */
61
62#include <time.h>
63
64#define yyparse getdate_yyparse
65#define yylex getdate_yylex
66#define yyerror getdate_yyerror
67
68static int yyparse(void);
69static int yylex(void);
70static int yyerror(const char *);
71
72time_t get_date(char *, struct timeb *);
73
74#define EPOCH 1970
75#define HOUR(x) ((time_t)(x) * 60)
76#define SECSPERDAY (24L * 60L * 60L)
77
78
79/*
80** An entry in the lexical lookup table.
81*/
82typedef struct _TABLE {
83 const char *name;
84 int type;
85 time_t value;
86} TABLE;
87
88
89/*
90** Daylight-savings mode: on, off, or not yet known.
91*/
92typedef enum _DSTMODE {
93 DSTon, DSToff, DSTmaybe
94} DSTMODE;
95
96/*
97** Meridian: am, pm, or 24-hour style.
98*/
99typedef enum _MERIDIAN {
100 MERam, MERpm, MER24
101} MERIDIAN;
102
103
104/*
105** Global variables. We could get rid of most of these by using a good
106** union as the yacc stack. (This routine was originally written before
107** yacc had the %union construct.) Maybe someday; right now we only use
108** the %union very rarely.
109*/
110static char *yyInput;
111static DSTMODE yyDSTmode;
112static time_t yyDayOrdinal;
113static time_t yyDayNumber;
114static int yyHaveDate;
115static int yyHaveDay;
116static int yyHaveRel;
117static int yyHaveTime;
118static int yyHaveZone;
119static time_t yyTimezone;
120static time_t yyDay;
121static time_t yyHour;
122static time_t yyMinutes;
123static time_t yyMonth;
124static time_t yySeconds;
125static time_t yyYear;
126static MERIDIAN yyMeridian;
127static time_t yyRelMonth;
128static time_t yyRelSeconds;
129
130%}
131
132%union {
133 time_t Number;
134 enum _MERIDIAN Meridian;
135}
136
137%token tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
138%token tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST
139
140%type <Number> tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT
141%type <Number> tSEC_UNIT tSNUMBER tUNUMBER tZONE
142%type <Meridian> tMERIDIAN o_merid
143
144%%
145
146spec : /* NULL */
147 | spec item
148 ;
149
150item : time {
151 yyHaveTime++;
152 }
153 | zone {
154 yyHaveZone++;
155 }
156 | date {
157 yyHaveDate++;
158 }
159 | day {
160 yyHaveDay++;
161 }
162 | rel {
163 yyHaveRel++;
164 }
165 | number
166 ;
167
168time : tUNUMBER tMERIDIAN {
169 yyHour = $1;
170 yyMinutes = 0;
171 yySeconds = 0;
172 yyMeridian = $2;
173 }
174 | tUNUMBER ':' tUNUMBER o_merid {
175 yyHour = $1;
176 yyMinutes = $3;
177 yySeconds = 0;
178 yyMeridian = $4;
179 }
180 | tUNUMBER ':' tUNUMBER tSNUMBER {
181 yyHour = $1;
182 yyMinutes = $3;
183 yyMeridian = MER24;
184 yyDSTmode = DSToff;
185 yyTimezone = - ($4 % 100 + ($4 / 100) * 60);
186 }
187 | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
188 yyHour = $1;
189 yyMinutes = $3;
190 yySeconds = $5;
191 yyMeridian = $6;
192 }
193 | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
194 yyHour = $1;
195 yyMinutes = $3;
196 yySeconds = $5;
197 yyMeridian = MER24;
198 yyDSTmode = DSToff;
199 yyTimezone = - ($6 % 100 + ($6 / 100) * 60);
200 }
201 ;
202
203zone : tZONE {
204 yyTimezone = $1;
205 yyDSTmode = DSToff;
206 }
207 | tDAYZONE {
208 yyTimezone = $1;
209 yyDSTmode = DSTon;
210 }
211 |
212 tZONE tDST {
213 yyTimezone = $1;
214 yyDSTmode = DSTon;
215 }
216 ;
217
218day : tDAY {
219 yyDayOrdinal = 1;
220 yyDayNumber = $1;
221 }
222 | tDAY ',' {
223 yyDayOrdinal = 1;
224 yyDayNumber = $1;
225 }
226 | tUNUMBER tDAY {
227 yyDayOrdinal = $1;
228 yyDayNumber = $2;
229 }
230 ;
231
232date : tUNUMBER '/' tUNUMBER {
233 yyMonth = $1;
234 yyDay = $3;
235 }
236 | tUNUMBER '/' tUNUMBER '/' tUNUMBER {
237 if ($1 >= 100) {
238 yyYear = $1;
239 yyMonth = $3;
240 yyDay = $5;
241 } else {
242 yyMonth = $1;
243 yyDay = $3;
244 yyYear = $5;
245 }
246 }
247 | tUNUMBER tSNUMBER tSNUMBER {
248 /* ISO 8601 format. yyyy-mm-dd. */
249 yyYear = $1;
250 yyMonth = -$2;
251 yyDay = -$3;
252 }
253 | tUNUMBER tMONTH tSNUMBER {
254 /* e.g. 17-JUN-1992. */
255 yyDay = $1;
256 yyMonth = $2;
257 yyYear = -$3;
258 }
259 | tMONTH tUNUMBER {
260 yyMonth = $1;
261 yyDay = $2;
262 }
263 | tMONTH tUNUMBER ',' tUNUMBER {
264 yyMonth = $1;
265 yyDay = $2;
266 yyYear = $4;
267 }
268 | tUNUMBER tMONTH {
269 yyMonth = $2;
270 yyDay = $1;
271 }
272 | tUNUMBER tMONTH tUNUMBER {
273 yyMonth = $2;
274 yyDay = $1;
275 yyYear = $3;
276 }
277 ;
278
279rel : relunit tAGO {
280 yyRelSeconds = -yyRelSeconds;
281 yyRelMonth = -yyRelMonth;
282 }
283 | relunit
284 ;
285
286relunit : tUNUMBER tMINUTE_UNIT {
287 yyRelSeconds += $1 * $2 * 60L;
288 }
289 | tSNUMBER tMINUTE_UNIT {
290 yyRelSeconds += $1 * $2 * 60L;
291 }
292 | tMINUTE_UNIT {
293 yyRelSeconds += $1 * 60L;
294 }
295 | tSNUMBER tSEC_UNIT {
296 yyRelSeconds += $1;
297 }
298 | tUNUMBER tSEC_UNIT {
299 yyRelSeconds += $1;
300 }
301 | tSEC_UNIT {
302 yyRelSeconds++;
303 }
304 | tSNUMBER tMONTH_UNIT {
305 yyRelMonth += $1 * $2;
306 }
307 | tUNUMBER tMONTH_UNIT {
308 yyRelMonth += $1 * $2;
309 }
310 | tMONTH_UNIT {
311 yyRelMonth += $1;
312 }
313 ;
314
315number : tUNUMBER {
316 if (yyHaveTime && yyHaveDate && !yyHaveRel)
317 yyYear = $1;
318 else {
319 if($1>10000) {
320 yyHaveDate++;
321 yyDay= ($1)%100;
322 yyMonth= ($1/100)%100;
323 yyYear = $1/10000;
324 }
325 else {
326 yyHaveTime++;
327 if ($1 < 100) {
328 yyHour = $1;
329 yyMinutes = 0;
330 }
331 else {
332 yyHour = $1 / 100;
333 yyMinutes = $1 % 100;
334 }
335 yySeconds = 0;
336 yyMeridian = MER24;
337 }
338 }
339 }
340 ;
341
342o_merid : /* NULL */ {
343 $$ = MER24;
344 }
345 | tMERIDIAN {
346 $$ = $1;
347 }
348 ;
349
350%%
351
352/* Month and day table. */
353static TABLE const MonthDayTable[] = {
354 { "january", tMONTH, 1 },
355 { "february", tMONTH, 2 },
356 { "march", tMONTH, 3 },
357 { "april", tMONTH, 4 },
358 { "may", tMONTH, 5 },
359 { "june", tMONTH, 6 },
360 { "july", tMONTH, 7 },
361 { "august", tMONTH, 8 },
362 { "september", tMONTH, 9 },
363 { "sept", tMONTH, 9 },
364 { "october", tMONTH, 10 },
365 { "november", tMONTH, 11 },
366 { "december", tMONTH, 12 },
367 { "sunday", tDAY, 0 },
368 { "monday", tDAY, 1 },
369 { "tuesday", tDAY, 2 },
370 { "tues", tDAY, 2 },
371 { "wednesday", tDAY, 3 },
372 { "wednes", tDAY, 3 },
373 { "thursday", tDAY, 4 },
374 { "thur", tDAY, 4 },
375 { "thurs", tDAY, 4 },
376 { "friday", tDAY, 5 },
377 { "saturday", tDAY, 6 },
378 { NULL, 0, 0 }
379};
380
381/* Time units table. */
382static TABLE const UnitsTable[] = {
383 { "year", tMONTH_UNIT, 12 },
384 { "month", tMONTH_UNIT, 1 },
385 { "fortnight", tMINUTE_UNIT, 14 * 24 * 60 },
386 { "week", tMINUTE_UNIT, 7 * 24 * 60 },
387 { "day", tMINUTE_UNIT, 1 * 24 * 60 },
388 { "hour", tMINUTE_UNIT, 60 },
389 { "minute", tMINUTE_UNIT, 1 },
390 { "min", tMINUTE_UNIT, 1 },
391 { "second", tSEC_UNIT, 1 },
392 { "sec", tSEC_UNIT, 1 },
393 { NULL, 0, 0 }
394};
395
396/* Assorted relative-time words. */
397static TABLE const OtherTable[] = {
398 { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 },
399 { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 },
400 { "today", tMINUTE_UNIT, 0 },
401 { "now", tMINUTE_UNIT, 0 },
402 { "last", tUNUMBER, -1 },
403 { "this", tMINUTE_UNIT, 0 },
404 { "next", tUNUMBER, 2 },
405 { "first", tUNUMBER, 1 },
406/* { "second", tUNUMBER, 2 }, */
407 { "third", tUNUMBER, 3 },
408 { "fourth", tUNUMBER, 4 },
409 { "fifth", tUNUMBER, 5 },
410 { "sixth", tUNUMBER, 6 },
411 { "seventh", tUNUMBER, 7 },
412 { "eighth", tUNUMBER, 8 },
413 { "ninth", tUNUMBER, 9 },
414 { "tenth", tUNUMBER, 10 },
415 { "eleventh", tUNUMBER, 11 },
416 { "twelfth", tUNUMBER, 12 },
417 { "ago", tAGO, 1 },
418 { NULL, 0, 0 }
419};
420
421/* The timezone table. */
422/* Some of these are commented out because a time_t can't store a float. */
423static TABLE const TimezoneTable[] = {
424 { "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */
425 { "ut", tZONE, HOUR( 0) }, /* Universal (Coordinated) */
426 { "utc", tZONE, HOUR( 0) },
427 { "wet", tZONE, HOUR( 0) }, /* Western European */
428 { "bst", tDAYZONE, HOUR( 0) }, /* British Summer */
429 { "wat", tZONE, HOUR( 1) }, /* West Africa */
430 { "at", tZONE, HOUR( 2) }, /* Azores */
431#if 0
432 /* For completeness. BST is also British Summer, and GST is
433 * also Guam Standard. */
434 { "bst", tZONE, HOUR( 3) }, /* Brazil Standard */
435 { "gst", tZONE, HOUR( 3) }, /* Greenland Standard */
436#endif
437#if 0
438 { "nft", tZONE, HOUR(3.5) }, /* Newfoundland */
439 { "nst", tZONE, HOUR(3.5) }, /* Newfoundland Standard */
440 { "ndt", tDAYZONE, HOUR(3.5) }, /* Newfoundland Daylight */
441#endif
442 { "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */
443 { "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */
444 { "est", tZONE, HOUR( 5) }, /* Eastern Standard */
445 { "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */
446 { "cst", tZONE, HOUR( 6) }, /* Central Standard */
447 { "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */
448 { "mst", tZONE, HOUR( 7) }, /* Mountain Standard */
449 { "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */
450 { "pst", tZONE, HOUR( 8) }, /* Pacific Standard */
451 { "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */
452 { "yst", tZONE, HOUR( 9) }, /* Yukon Standard */
453 { "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */
454 { "hst", tZONE, HOUR(10) }, /* Hawaii Standard */
455 { "hdt", tDAYZONE, HOUR(10) }, /* Hawaii Daylight */
456 { "cat", tZONE, HOUR(10) }, /* Central Alaska */
457 { "ahst", tZONE, HOUR(10) }, /* Alaska-Hawaii Standard */
458 { "nt", tZONE, HOUR(11) }, /* Nome */
459 { "idlw", tZONE, HOUR(12) }, /* International Date Line West */
460 { "cet", tZONE, -HOUR(1) }, /* Central European */
461 { "met", tZONE, -HOUR(1) }, /* Middle European */
462 { "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */
463 { "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */
464 { "swt", tZONE, -HOUR(1) }, /* Swedish Winter */
465 { "sst", tDAYZONE, -HOUR(1) }, /* Swedish Summer */
466 { "fwt", tZONE, -HOUR(1) }, /* French Winter */
467 { "fst", tDAYZONE, -HOUR(1) }, /* French Summer */
468 { "eet", tZONE, -HOUR(2) }, /* Eastern Europe, USSR Zone 1 */
469 { "bt", tZONE, -HOUR(3) }, /* Baghdad, USSR Zone 2 */
470#if 0
471 { "it", tZONE, -HOUR(3.5) },/* Iran */
472#endif
473 { "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */
474 { "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */
475#if 0
476 { "ist", tZONE, -HOUR(5.5) },/* Indian Standard */
477#endif
478 { "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */
479#if 0
480 /* For completeness. NST is also Newfoundland Stanard, and SST is
481 * also Swedish Summer. */
482 { "nst", tZONE, -HOUR(6.5) },/* North Sumatra */
483 { "sst", tZONE, -HOUR(7) }, /* South Sumatra, USSR Zone 6 */
484#endif /* 0 */
485 { "wast", tZONE, -HOUR(7) }, /* West Australian Standard */
486 { "wadt", tDAYZONE, -HOUR(7) }, /* West Australian Daylight */
487#if 0
488 { "jt", tZONE, -HOUR(7.5) },/* Java (3pm in Cronusland!) */
489#endif
490 { "cct", tZONE, -HOUR(8) }, /* China Coast, USSR Zone 7 */
491 { "jst", tZONE, -HOUR(9) }, /* Japan Standard, USSR Zone 8 */
492#if 0
493 { "cast", tZONE, -HOUR(9.5) },/* Central Australian Standard */
494 { "cadt", tDAYZONE, -HOUR(9.5) },/* Central Australian Daylight */
495#endif
496 { "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */
497 { "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */
498 { "gst", tZONE, -HOUR(10) }, /* Guam Standard, USSR Zone 9 */
499 { "nzt", tZONE, -HOUR(12) }, /* New Zealand */
500 { "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */
501 { "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */
502 { "idle", tZONE, -HOUR(12) }, /* International Date Line East */
503 { NULL, 0, 0 }
504};
505
506/* Military timezone table. */
507static TABLE const MilitaryTable[] = {
508 { "a", tZONE, HOUR( 1) },
509 { "b", tZONE, HOUR( 2) },
510 { "c", tZONE, HOUR( 3) },
511 { "d", tZONE, HOUR( 4) },
512 { "e", tZONE, HOUR( 5) },
513 { "f", tZONE, HOUR( 6) },
514 { "g", tZONE, HOUR( 7) },
515 { "h", tZONE, HOUR( 8) },
516 { "i", tZONE, HOUR( 9) },
517 { "k", tZONE, HOUR( 10) },
518 { "l", tZONE, HOUR( 11) },
519 { "m", tZONE, HOUR( 12) },
520 { "n", tZONE, HOUR(- 1) },
521 { "o", tZONE, HOUR(- 2) },
522 { "p", tZONE, HOUR(- 3) },
523 { "q", tZONE, HOUR(- 4) },
524 { "r", tZONE, HOUR(- 5) },
525 { "s", tZONE, HOUR(- 6) },
526 { "t", tZONE, HOUR(- 7) },
527 { "u", tZONE, HOUR(- 8) },
528 { "v", tZONE, HOUR(- 9) },
529 { "w", tZONE, HOUR(-10) },
530 { "x", tZONE, HOUR(-11) },
531 { "y", tZONE, HOUR(-12) },
532 { "z", tZONE, HOUR( 0) },
533 { NULL, 0, 0 }
534};
535
536\f
537
538
539/* ARGSUSED */
540static int
541yyerror(const char *s __unused)
542{
543 return 0;
544}
545
546
547static time_t
548ToSeconds(time_t Hours, time_t Minutes, time_t Seconds, MERIDIAN Meridian)
549{
550 if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59)
551 return -1;
552 switch (Meridian) {
553 case MER24:
554 if (Hours < 0 || Hours > 23)
555 return -1;
556 return (Hours * 60L + Minutes) * 60L + Seconds;
557 case MERam:
558 if (Hours < 1 || Hours > 12)
559 return -1;
560 if (Hours == 12)
561 Hours = 0;
562 return (Hours * 60L + Minutes) * 60L + Seconds;
563 case MERpm:
564 if (Hours < 1 || Hours > 12)
565 return -1;
566 if (Hours == 12)
567 Hours = 0;
568 return ((Hours + 12) * 60L + Minutes) * 60L + Seconds;
569 default:
570 abort ();
571 }
572 /* NOTREACHED */
573}
574
575
576/* Year is either
577 * A negative number, which means to use its absolute value (why?)
578 * A number from 0 to 99, which means a year from 1900 to 1999, or
579 * The actual year (>=100). */
580static time_t
581Convert(time_t Month, time_t Day, time_t Year,
582 time_t Hours, time_t Minutes, time_t Seconds,
583 MERIDIAN Meridian, DSTMODE DSTmode)
584{
585 static int DaysInMonth[12] = {
586 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
587 };
588 time_t tod;
589 time_t Julian;
590 int i;
591
592 if (Year < 0)
593 Year = -Year;
594 if (Year < 69)
595 Year += 2000;
596 else if (Year < 100)
597 Year += 1900;
598 DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)
599 ? 29 : 28;
600 /* Checking for 2038 bogusly assumes that time_t is 32 bits. But
601 I'm too lazy to try to check for time_t overflow in another way. */
602 if (Year < EPOCH || Year > 2038
603 || Month < 1 || Month > 12
604 /* Lint fluff: "conversion from long may lose accuracy" */
605 || Day < 1 || Day > DaysInMonth[(int)--Month])
606 return -1;
607
608 for (Julian = Day - 1, i = 0; i < Month; i++)
609 Julian += DaysInMonth[i];
610 for (i = EPOCH; i < Year; i++)
611 Julian += 365 + (i % 4 == 0);
612 Julian *= SECSPERDAY;
613 Julian += yyTimezone * 60L;
614 if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0)
615 return -1;
616 Julian += tod;
617 if (DSTmode == DSTon
618 || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst))
619 Julian -= 60 * 60;
620 return Julian;
621}
622
623
624static time_t
625DSTcorrect(time_t Start, time_t Future)
626{
627 time_t StartDay;
628 time_t FutureDay;
629
630 StartDay = (localtime(&Start)->tm_hour + 1) % 24;
631 FutureDay = (localtime(&Future)->tm_hour + 1) % 24;
632 return (Future - Start) + (StartDay - FutureDay) * 60L * 60L;
633}
634
635
636static time_t
637RelativeDate(time_t Start, time_t DayOrdinal, time_t DayNumber)
638{
639 struct tm *tm;
640 time_t now;
641
642 now = Start;
643 tm = localtime(&now);
644 now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7);
645 now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
646 return DSTcorrect(Start, now);
647}
648
649
650static time_t
651RelativeMonth(time_t Start, time_t RelMonth)
652{
653 struct tm *tm;
654 time_t Month;
655 time_t Year;
656
657 if (RelMonth == 0)
658 return 0;
659 tm = localtime(&Start);
660 Month = 12 * (tm->tm_year + 1900) + tm->tm_mon + RelMonth;
661 Year = Month / 12;
662 Month = Month % 12 + 1;
663 return DSTcorrect(Start,
664 Convert(Month, (time_t)tm->tm_mday, Year,
665 (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
666 MER24, DSTmaybe));
667}
668
669
670static int
671LookupWord(char *buff)
672{
673 char *p;
674 char *q;
675 const TABLE *tp;
676 int i;
677 int abbrev;
678
679 /* Make it lowercase. */
680 for (p = buff; *p; p++)
681 if (isupper(*p))
682 *p = tolower(*p);
683
684 if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) {
685 yylval.Meridian = MERam;
686 return tMERIDIAN;
687 }
688 if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) {
689 yylval.Meridian = MERpm;
690 return tMERIDIAN;
691 }
692
693 /* See if we have an abbreviation for a month. */
694 if (strlen(buff) == 3)
695 abbrev = 1;
696 else if (strlen(buff) == 4 && buff[3] == '.') {
697 abbrev = 1;
698 buff[3] = '\0';
699 }
700 else
701 abbrev = 0;
702
703 for (tp = MonthDayTable; tp->name; tp++) {
704 if (abbrev) {
705 if (strncmp(buff, tp->name, 3) == 0) {
706 yylval.Number = tp->value;
707 return tp->type;
708 }
709 }
710 else if (strcmp(buff, tp->name) == 0) {
711 yylval.Number = tp->value;
712 return tp->type;
713 }
714 }
715
716 for (tp = TimezoneTable; tp->name; tp++)
717 if (strcmp(buff, tp->name) == 0) {
718 yylval.Number = tp->value;
719 return tp->type;
720 }
721
722 if (strcmp(buff, "dst") == 0)
723 return tDST;
724
725 for (tp = UnitsTable; tp->name; tp++)
726 if (strcmp(buff, tp->name) == 0) {
727 yylval.Number = tp->value;
728 return tp->type;
729 }
730
731 /* Strip off any plural and try the units table again. */
732 i = strlen(buff) - 1;
733 if (buff[i] == 's') {
734 buff[i] = '\0';
735 for (tp = UnitsTable; tp->name; tp++)
736 if (strcmp(buff, tp->name) == 0) {
737 yylval.Number = tp->value;
738 return tp->type;
739 }
740 buff[i] = 's'; /* Put back for "this" in OtherTable. */
741 }
742
743 for (tp = OtherTable; tp->name; tp++)
744 if (strcmp(buff, tp->name) == 0) {
745 yylval.Number = tp->value;
746 return tp->type;
747 }
748
749 /* Military timezones. */
750 if (buff[1] == '\0' && isalpha(*buff)) {
751 for (tp = MilitaryTable; tp->name; tp++)
752 if (strcmp(buff, tp->name) == 0) {
753 yylval.Number = tp->value;
754 return tp->type;
755 }
756 }
757
758 /* Drop out any periods and try the timezone table again. */
759 for (i = 0, p = q = buff; *q; q++)
760 if (*q != '.')
761 *p++ = *q;
762 else
763 i++;
764 *p = '\0';
765 if (i)
766 for (tp = TimezoneTable; tp->name; tp++)
767 if (strcmp(buff, tp->name) == 0) {
768 yylval.Number = tp->value;
769 return tp->type;
770 }
771
772 return tID;
773}
774
775
776static int
777yylex(void)
778{
779 char c;
780 char *p;
781 char buff[20];
782 int Count;
783 int sign;
784
785 for ( ; ; ) {
786 while (isspace(*yyInput))
787 yyInput++;
788
789 if (isdigit(c = *yyInput) || c == '-' || c == '+') {
790 if (c == '-' || c == '+') {
791 sign = c == '-' ? -1 : 1;
792 if (!isdigit(*++yyInput))
793 /* skip the '-' sign */
794 continue;
795 }
796 else
797 sign = 0;
798 for (yylval.Number = 0; isdigit(c = *yyInput++); )
799 yylval.Number = 10 * yylval.Number + c - '0';
800 yyInput--;
801 if (sign < 0)
802 yylval.Number = -yylval.Number;
803 return sign ? tSNUMBER : tUNUMBER;
804 }
805 if (isalpha(c)) {
806 for (p = buff; isalpha(c = *yyInput++) || c == '.'; )
807 if (p < &buff[sizeof buff - 1])
808 *p++ = c;
809 *p = '\0';
810 yyInput--;
811 return LookupWord(buff);
812 }
813 if (c != '(')
814 return *yyInput++;
815 Count = 0;
816 do {
817 c = *yyInput++;
818 if (c == '\0')
819 return c;
820 if (c == '(')
821 Count++;
822 else if (c == ')')
823 Count--;
824 } while (Count > 0);
825 }
826}
827
828#define TM_YEAR_ORIGIN 1900
829
830/* Yield A - B, measured in seconds. */
831static long
832difftm (struct tm *a, struct tm *b)
833{
834 int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
835 int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
836 int days = (
837 /* difference in day of year */
838 a->tm_yday - b->tm_yday
839 /* + intervening leap days */
840 + ((ay >> 2) - (by >> 2))
841 - (ay/100 - by/100)
842 + ((ay/100 >> 2) - (by/100 >> 2))
843 /* + difference in years * 365 */
844 + (long)(ay-by) * 365
845 );
846 return (60*(60*(24*days + (a->tm_hour - b->tm_hour))
847 + (a->tm_min - b->tm_min))
848 + (a->tm_sec - b->tm_sec));
849}
850
851time_t
852get_date(char *p, struct timeb *now)
853{
854 struct tm *tm, gmt;
855 struct timeb ftz;
856 time_t Start;
857 time_t tod;
858 time_t nowtime;
859
860 bzero (&gmt, sizeof(struct tm));
861 yyInput = p;
862 if (now == NULL) {
863 struct tm *gmt_ptr;
864
865 now = &ftz;
866 (void)time (&nowtime);
867
868 gmt_ptr = gmtime (&nowtime);
869 if (gmt_ptr != NULL)
870 {
871 /* Make a copy, in case localtime modifies *tm (I think
872 that comment now applies to *gmt_ptr, but I am too
873 lazy to dig into how gmtime and locatime allocate the
874 structures they return pointers to). */
875 gmt = *gmt_ptr;
876 }
877
878 if (! (tm = localtime (&nowtime)))
879 return -1;
880
881 if (gmt_ptr != NULL)
882 ftz.timezone = difftm (&gmt, tm) / 60;
883 else
884 /* We are on a system like VMS, where the system clock is
885 in local time and the system has no concept of timezones.
886 Hopefully we can fake this out (for the case in which the
887 user specifies no timezone) by just saying the timezone
888 is zero. */
889 ftz.timezone = 0;
890
891 if(tm->tm_isdst)
892 ftz.timezone += 60;
893 }
894 else
895 {
896 nowtime = now->time;
897 }
898
899 tm = localtime(&nowtime);
900 yyYear = tm->tm_year + 1900;
901 yyMonth = tm->tm_mon + 1;
902 yyDay = tm->tm_mday;
903 yyTimezone = now->timezone;
904 yyDSTmode = DSTmaybe;
905 yyHour = 0;
906 yyMinutes = 0;
907 yySeconds = 0;
908 yyMeridian = MER24;
909 yyRelSeconds = 0;
910 yyRelMonth = 0;
911 yyHaveDate = 0;
912 yyHaveDay = 0;
913 yyHaveRel = 0;
914 yyHaveTime = 0;
915 yyHaveZone = 0;
916
917 if (yyparse()
918 || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1)
919 return -1;
920
921 if (yyHaveDate || yyHaveTime || yyHaveDay) {
922 Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
923 yyMeridian, yyDSTmode);
924 if (Start < 0)
925 return -1;
926 }
927 else {
928 Start = nowtime;
929 if (!yyHaveRel)
930 Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec;
931 }
932
933 Start += yyRelSeconds;
934 Start += RelativeMonth(Start, yyRelMonth);
935
936 if (yyHaveDay && !yyHaveDate) {
937 tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber);
938 Start += tod;
939 }
940
941 /* Have to do *something* with a legitimate -1 so it's distinguishable
942 * from the error return value. (Alternately could set errno on error.) */
943 return Start == -1 ? 0 : Start;
944}
945
946
947#if defined(TEST)
948
949/* ARGSUSED */
950int
951main(int ac, char *av[])
952{
953 char buff[128];
954 time_t d;
955
956 (void)printf("Enter date, or blank line to exit.\n\t> ");
957 (void)fflush(stdout);
958 while (gets(buff) && buff[0]) {
959 d = get_date(buff, (struct timeb *)NULL);
960 if (d == -1)
961 (void)printf("Bad format - couldn't convert.\n");
962 else
963 (void)printf("%s", ctime(&d));
964 (void)printf("\t> ");
965 (void)fflush(stdout);
966 }
967 exit(0);
968 /* NOTREACHED */
969}
970#endif /* defined(TEST) */