Don't use .Xo/.Xc. Fix date format.
[netbsd-mini2440.git] / usr.bin / rcs / src / partime.c
blob8eae5ca90ec19575eb56e6b617bac9aaa5d5b3a0
1 /*
2 * PARTIME parse date/time string into a TM structure
4 * Usage:
5 * #include "time.h" -- expanded tm structure
6 * char *str; struct tm *tp;
7 * partime(str,tp);
8 * Returns:
9 * 0 if parsing failed
10 * else time values in specified TM structure (unspecified values
11 * set to TMNULL)
12 * Notes:
13 * This code is quasi-public; it may be used freely in like software.
14 * It is not to be sold, nor used in licensed software without
15 * permission of the author.
16 * For everyone's benefit, please report bugs and improvements!
17 * Copyright 1980 by Ken Harrenstien, SRI International.
18 * (ARPANET: KLH @ SRI)
21 /* Hacknotes:
22 * If parsing changed so that no backup needed, could perhaps modify
23 * to use a FILE input stream. Need terminator, though.
24 * Perhaps should return 0 on success, else a non-zero error val?
25 * Flush AMPM from TM structure and handle locally within PARTIME,
26 * like midnight/noon?
29 #ifndef lint
30 static char rcsid[]=
31 "$Header: /pub/NetBSD/misc/repositories/cvsroot/src/usr.bin/rcs/src/Attic/partime.c,v 1.1 1993/03/21 09:58:06 cgd Exp $";
32 #endif
34 /* $Log: partime.c,v $
35 * Revision 1.4 89/05/01 14:48:46 narten
36 * fixed #ifdef DEBUG construct
38 * Revision 1.3 88/11/08 12:02:15 narten
39 * changes from eggert@sm.unisys.com (Paul Eggert)
41 * Revision 1.3 88/08/28 14:53:40 eggert
42 * Remove unportable "#endif XXX"s.
44 * Revision 1.2 87/03/27 14:21:53 jenkins
45 * Port to suns
47 * Revision 1.1 84/01/23 14:50:07 kcs
48 * Initial revision
50 * Revision 1.1 82/05/06 11:38:26 wft
51 * Initial revision
55 #include <stdio.h>
56 #include <ctype.h>
57 #include "time.h"
59 #ifndef lint
60 static char timeid[] = TIMEID;
61 #endif
63 struct tmwent {
64 char *went;
65 long wval; /* must be big enough to hold pointer or integer */
66 char wflgs;
67 char wtype;
69 /* wflgs */
70 #define TWSPEC 01 /* Word wants special processing */
71 #define TWTIME 02 /* Word is a time value (absence implies date) */
72 #define TWDST 04 /* Word is a DST-type timezone */
73 #define TW1200 010 /* Word is NOON or MIDNIGHT (sigh) */
75 int pt12hack();
76 int ptnoise();
77 struct tmwent tmwords [] = {
78 {"january", 0, 0, TM_MON},
79 {"february", 1, 0, TM_MON},
80 {"march", 2, 0, TM_MON},
81 {"april", 3, 0, TM_MON},
82 {"may", 4, 0, TM_MON},
83 {"june", 5, 0, TM_MON},
84 {"july", 6, 0, TM_MON},
85 {"august", 7, 0, TM_MON},
86 {"september", 8, 0, TM_MON},
87 {"october", 9, 0, TM_MON},
88 {"november", 10, 0, TM_MON},
89 {"december", 11, 0, TM_MON},
91 {"sunday", 0, 0, TM_WDAY},
92 {"monday", 1, 0, TM_WDAY},
93 {"tuesday", 2, 0, TM_WDAY},
94 {"wednesday", 3, 0, TM_WDAY},
95 {"thursday", 4, 0, TM_WDAY},
96 {"friday", 5, 0, TM_WDAY},
97 {"saturday", 6, 0, TM_WDAY},
99 {"gmt", 0*60, TWTIME, TM_ZON}, /* Greenwich */
100 {"gst", 0*60, TWTIME, TM_ZON},
101 {"gdt", 0*60, TWTIME+TWDST, TM_ZON}, /* ?? */
103 {"ast", 4*60, TWTIME, TM_ZON}, /* Atlantic */
104 {"est", 5*60, TWTIME, TM_ZON}, /* Eastern */
105 {"cst", 6*60, TWTIME, TM_ZON}, /* Central */
106 {"mst", 7*60, TWTIME, TM_ZON}, /* Mountain */
107 {"pst", 8*60, TWTIME, TM_ZON}, /* Pacific */
108 {"yst", 9*60, TWTIME, TM_ZON}, /* Yukon */
109 {"hst", 10*60, TWTIME, TM_ZON}, /* Hawaii */
110 {"bst", 11*60, TWTIME, TM_ZON}, /* Bering */
112 {"adt", 4*60, TWTIME+TWDST, TM_ZON}, /* Atlantic */
113 {"edt", 5*60, TWTIME+TWDST, TM_ZON}, /* Eastern */
114 {"cdt", 6*60, TWTIME+TWDST, TM_ZON}, /* Central */
115 {"mdt", 7*60, TWTIME+TWDST, TM_ZON}, /* Mountain */
116 {"pdt", 8*60, TWTIME+TWDST, TM_ZON}, /* Pacific */
117 {"ydt", 9*60, TWTIME+TWDST, TM_ZON}, /* Yukon */
118 {"hdt", 10*60, TWTIME+TWDST, TM_ZON}, /* Hawaii */
119 {"bdt", 11*60, TWTIME+TWDST, TM_ZON}, /* Bering */
121 {"daylight", 1, TWTIME+TWDST, TM_ZON}, /* Local Daylight */
122 {"standard", 1, TWTIME, TM_ZON}, /* Local Standard */
123 {"std", 1, TWTIME, TM_ZON}, /* " " */
125 {"am", 1, TWTIME, TM_AMPM},
126 {"pm", 2, TWTIME, TM_AMPM},
127 {"noon", 12,TWTIME+TW1200, 0}, /* Special frobs */
128 {"midnight", 0, TWTIME+TW1200, 0},
129 {"at", (long)ptnoise, TWSPEC, 0}, /* Noise word */
131 {0, 0, 0, 0}, /* Zero entry to terminate searches */
134 #define TMWILD (-2) /* Value meaning item specified as wild-card */
135 /* (May use someday...) */
137 struct token {
138 char *tcp; /* pointer to string */
139 int tcnt; /* # chars */
140 char tbrk; /* "break" char */
141 char tbrkl; /* last break char */
142 char tflg; /* 0 = alpha, 1 = numeric */
143 union { /* Resulting value; */
144 int tnum;/* either a #, or */
145 struct tmwent *ttmw;/* ptr to a tmwent. */
146 } tval;
149 partime(astr, atm)
150 char *astr;
151 struct tm *atm;
152 { register int *tp;
153 register struct tmwent *twp;
154 register int i;
155 struct token btoken, atoken;
156 char *cp, ch;
157 int ord, midnoon;
158 int (*aproc)();
160 tp = (int *)atm;
161 zaptime(tp); /* Initialize the TM structure */
162 midnoon = TMNULL; /* and our own temp stuff */
163 btoken.tcnt = btoken.tbrkl = 0;
164 btoken.tcp = astr;
166 domore:
167 if(!ptitoken(btoken.tcp+btoken.tcnt,&btoken)) /* Get a token */
168 { if(btoken.tval.tnum) return(0); /* Read error? */
169 if(midnoon != TMNULL) /* EOF, wrap up */
170 return(pt12hack(tp, midnoon));
171 return(1); /* Win return! */
173 if(btoken.tflg == 0) /* Alpha? */
174 { twp = btoken.tval.ttmw; /* Yes, get ptr to entry */
175 if(twp->wflgs&TWSPEC) /* Special alpha crock */
176 { aproc = (int (*) ()) (twp->wval);
177 if(!(*aproc)(tp, twp, &btoken))
178 return(0); /* ERR: special word err */
179 goto domore;
181 if(twp->wflgs&TW1200)
182 if(ptstash(&midnoon,(int)twp->wval))
183 return(0); /* ERR: noon/midnite clash */
184 else goto domore;
185 if(ptstash(&tp[twp->wtype],(int)twp->wval))
186 return(0); /* ERR: val already set */
187 if(twp->wtype == TM_ZON) /* If was zone, hack DST */
188 if(ptstash(&tp[TM_ISDST],(twp->wflgs&TWDST)))
189 return(0); /* ERR: DST conflict */
190 goto domore;
193 /* Token is number. Lots of hairy heuristics. */
194 if(btoken.tcnt >= 7) /* More than 6 digits in string? */
195 return(0); /* ERR: number too big */
196 if(btoken.tcnt == 6) /* 6 digits = HHMMSS. Needs special crock */
197 { /* since 6 digits are too big for integer! */
198 i = (btoken.tcp[0]-'0')*10 /* Gobble 1st 2 digits */
199 + btoken.tcp[1]-'0';
200 btoken.tcnt = 2; /* re-read last 4 chars */
201 goto coltime;
204 i = btoken.tval.tnum; /* Value now known to be valid; get it. */
205 if( btoken.tcnt == 5 /* 5 digits = HMMSS */
206 || btoken.tcnt == 3) /* 3 digits = HMM */
207 { if(btoken.tcnt != 3)
208 if(ptstash(&tp[TM_SEC], i%100))
209 return(0); /* ERR: sec conflict */
210 else i /= 100;
211 hhmm4: if(ptstash(&tp[TM_MIN], i%100))
212 return(0); /* ERR: min conflict */
213 i /= 100;
214 hh2: if(ptstash(&tp[TM_HOUR], i))
215 return(0); /* ERR: hour conflict */
216 goto domore;
219 if(btoken.tcnt == 4) /* 4 digits = YEAR or HHMM */
220 { if(tp[TM_YEAR] != TMNULL) goto hhmm4; /* Already got yr? */
221 if(tp[TM_HOUR] != TMNULL) goto year4; /* Already got hr? */
222 if((i%100) > 59) goto year4; /* MM >= 60? */
223 if(btoken.tbrk == ':') /* HHMM:SS ? */
224 if( ptstash(&tp[TM_HOUR],i/100)
225 || ptstash(&tp[TM_MIN], i%100))
226 return(0); /* ERR: hr/min clash */
227 else goto coltm2; /* Go handle SS */
228 if(btoken.tbrk != ',' && btoken.tbrk != '/'
229 && ptitoken(btoken.tcp+btoken.tcnt,&atoken) /* Peek */
230 && atoken.tflg == 0 /* alpha */
231 && (atoken.tval.ttmw->wflgs&TWTIME)) /* HHMM-ZON */
232 goto hhmm4;
233 if(btoken.tbrkl == '-' /* DD-Mon-YYYY */
234 || btoken.tbrkl == ',' /* Mon DD, YYYY */
235 || btoken.tbrkl == '/' /* MM/DD/YYYY */
236 || btoken.tbrkl == '.' /* DD.MM.YYYY */
237 || btoken.tbrk == '-' /* YYYY-MM-DD */
238 ) goto year4;
239 goto hhmm4; /* Give up, assume HHMM. */
242 /* From this point on, assume tcnt == 1 or 2 */
243 /* 2 digits = YY, MM, DD, or HH (MM and SS caught at coltime) */
244 if(btoken.tbrk == ':') /* HH:MM[:SS] */
245 goto coltime; /* must be part of time. */
246 if(i > 31) goto yy2; /* If >= 32, only YY poss. */
248 /* Check for numerical-format date */
249 for (cp = "/-."; ch = *cp++;)
250 { ord = (ch == '.' ? 0 : 1); /* n/m = D/M or M/D */
251 if(btoken.tbrk == ch) /* "NN-" */
252 { if(btoken.tbrkl != ch)
253 { if(ptitoken(btoken.tcp+btoken.tcnt,&atoken)
254 && atoken.tflg == 0
255 && atoken.tval.ttmw->wtype == TM_MON)
256 goto dd2;
257 if(ord)goto mm2; else goto dd2; /* "NN-" */
258 } /* "-NN-" */
259 if(tp[TM_DAY] == TMNULL
260 && tp[TM_YEAR] != TMNULL) /* If "YY-NN-" */
261 goto mm2; /* then always MM */
262 if(ord)goto dd2; else goto mm2;
264 if(btoken.tbrkl == ch /* "-NN" */
265 && tp[ord ? TM_MON : TM_DAY] != TMNULL)
266 if(tp[ord ? TM_DAY : TM_MON] == TMNULL) /* MM/DD */
267 if(ord)goto dd2; else goto mm2;
268 else goto yy2; /* "-YY" */
271 /* At this point only YY, DD, and HH are left.
272 * YY is very unlikely since value is <= 32 and there was
273 * no numerical format date. Make one last try at YY
274 * before dropping through to DD vs HH code.
276 if(btoken.tcnt == 2 /* If 2 digits */
277 && tp[TM_HOUR] != TMNULL /* and already have hour */
278 && tp[TM_DAY] != TMNULL /* and day, but */
279 && tp[TM_YEAR] == TMNULL) /* no year, then assume */
280 goto yy2; /* that's what we have. */
282 /* Now reduced to choice between HH and DD */
283 if(tp[TM_HOUR] != TMNULL) goto dd2; /* Have hour? Assume day. */
284 if(tp[TM_DAY] != TMNULL) goto hh2; /* Have day? Assume hour. */
285 if(i > 24) goto dd2; /* Impossible HH means DD */
286 if(!ptitoken(btoken.tcp+btoken.tcnt, &atoken)) /* Read ahead! */
287 if(atoken.tval.tnum) return(0); /* ERR: bad token */
288 else goto dd2; /* EOF, assume day. */
289 if( atoken.tflg == 0 /* If next token is an alpha */
290 && atoken.tval.ttmw->wflgs&TWTIME) /* time-spec, assume hour */
291 goto hh2; /* e.g. "3 PM", "11-EDT" */
293 dd2: if(ptstash(&tp[TM_DAY],i)) /* Store day (1 based) */
294 return(0);
295 goto domore;
297 mm2: if(ptstash(&tp[TM_MON], i-1)) /* Store month (make zero based) */
298 return(0);
299 goto domore;
301 yy2: i += 1900;
302 year4: if(ptstash(&tp[TM_YEAR],i)) /* Store year (full number) */
303 return(0); /* ERR: year conflict */
304 goto domore;
306 /* Hack HH:MM[[:]SS] */
307 coltime:
308 if(ptstash(&tp[TM_HOUR],i)) return(0);
309 if(!ptitoken(btoken.tcp+btoken.tcnt,&btoken))
310 return(!btoken.tval.tnum);
311 if(!btoken.tflg) return(0); /* ERR: HH:<alpha> */
312 if(btoken.tcnt == 4) /* MMSS */
313 if(ptstash(&tp[TM_MIN],btoken.tval.tnum/100)
314 || ptstash(&tp[TM_SEC],btoken.tval.tnum%100))
315 return(0);
316 else goto domore;
317 if(btoken.tcnt != 2
318 || ptstash(&tp[TM_MIN],btoken.tval.tnum))
319 return(0); /* ERR: MM bad */
320 if(btoken.tbrk != ':') goto domore; /* Seconds follow? */
321 coltm2: if(!ptitoken(btoken.tcp+btoken.tcnt,&btoken))
322 return(!btoken.tval.tnum);
323 if(!btoken.tflg || btoken.tcnt != 2 /* Verify SS */
324 || ptstash(&tp[TM_SEC], btoken.tval.tnum))
325 return(0); /* ERR: SS bad */
326 goto domore;
329 /* Store date/time value, return 0 if successful.
330 * Fails if entry already set to a different value.
332 ptstash(adr,val)
333 int *adr;
334 { register int *a;
335 if( *(a=adr) != TMNULL)
336 return(*a != val);
337 *a = val;
338 return(0);
341 /* This subroutine is invoked for NOON or MIDNIGHT when wrapping up
342 * just prior to returning from partime.
344 pt12hack(atp, aval)
345 int *atp, aval;
346 { register int *tp, i, h;
347 tp = atp;
348 if (((i=tp[TM_MIN]) && i != TMNULL) /* Ensure mins, secs */
349 || ((i=tp[TM_SEC]) && i != TMNULL)) /* are 0 or unspec'd */
350 return(0); /* ERR: MM:SS not 00:00 */
351 i = aval; /* Get 0 or 12 (midnite or noon) */
352 if ((h = tp[TM_HOUR]) == TMNULL /* If hour unspec'd, win */
353 || h == 12) /* or if 12:00 (matches either) */
354 tp[TM_HOUR] = i; /* Then set time */
355 else if(!(i == 0 /* Nope, but if midnight and */
356 &&(h == 0 || h == 24))) /* time matches, can pass. */
357 return(0); /* ERR: HH conflicts */
358 tp[TM_AMPM] = TMNULL; /* Always reset this value if won */
359 return(1);
362 /* Null routine for no-op tokens */
364 ptnoise() { return(1); }
366 /* Get a token and identify it to some degree.
367 * Returns 0 on failure; token.tval will be 0 for normal EOF, otherwise
368 * hit error of some sort
371 ptitoken(astr, tkp)
372 register struct token *tkp;
373 char *astr;
375 register char *cp;
376 register int i;
378 tkp->tval.tnum = 0;
379 if(pttoken(astr,tkp) == 0)
380 #ifdef DEBUG
382 VOID printf("EOF\n");
383 return(0);
385 #else
386 return(0);
387 #endif
388 cp = tkp->tcp;
390 #ifdef DEBUG
391 i = cp[tkp->tcnt];
392 cp[tkp->tcnt] = 0;
393 VOID printf("Token: \"%s\" ",cp);
394 cp[tkp->tcnt] = i;
395 #endif
397 if(tkp->tflg)
398 for(i = tkp->tcnt; i > 0; i--)
399 tkp->tval.tnum = (int)tkp->tval.tnum*10 + ((*cp++)-'0');
400 else
401 { i = ptmatchstr(cp, tkp->tcnt, tmwords);
402 tkp->tval.tnum = i ? i : -1; /* Set -1 for error */
404 #ifdef DEBUG
405 if(!i) VOID printf("Not found!\n");
406 #endif
408 if(!i) return(0);
411 #ifdef DEBUG
412 if(tkp->tflg)
413 VOID printf("Val: %d.\n",tkp->tval.tnum);
414 else VOID printf("Found: \"%s\", val: %d., type %d\n",
415 tkp->tval.ttmw->went,tkp->tval.ttmw->wval,tkp->tval.ttmw->wtype);
416 #endif
418 return(1);
421 /* Read token from input string into token structure */
422 pttoken(astr,tkp)
423 register struct token *tkp;
424 char *astr;
426 register char *cp;
427 register int c;
429 tkp->tcp = cp = astr;
430 tkp->tbrkl = tkp->tbrk; /* Set "last break" */
431 tkp->tcnt = tkp->tbrk = tkp->tflg = 0;
433 while(c = *cp++)
434 { switch(c)
435 { case ' ': case '\t': /* Flush all whitespace */
436 while((c = *cp++) && isspace(c));
437 cp--; /* Drop thru to handle brk */
438 case '(': case ')': /* Perhaps any non-alphanum */
439 case '-': case ',': /* shd qualify as break? */
440 case '/': case ':': case '.': /* Break chars */
441 if(tkp->tcnt == 0) /* If no token yet */
442 { tkp->tcp = cp; /* ignore the brk */
443 tkp->tbrkl = c;
444 continue; /* and go on. */
446 tkp->tbrk = c;
447 return(tkp->tcnt);
449 if(tkp->tcnt == 0) /* If first char of token, */
450 tkp->tflg = isdigit(c); /* determine type */
451 if(( isdigit(c) && tkp->tflg) /* If not first, make sure */
452 ||(!isdigit(c) && !tkp->tflg)) /* char matches type */
453 tkp->tcnt++; /* Win, add to token. */
454 else {
455 cp--; /* Wrong type, back up */
456 tkp->tbrk = c;
457 return(tkp->tcnt);
460 return(tkp->tcnt); /* When hit EOF */
464 ptmatchstr(astr,cnt,astruc)
465 char *astr;
466 int cnt;
467 struct tmwent *astruc;
468 { register char *cp, *mp;
469 register int c;
470 struct tmwent *lastptr;
471 struct integ { int word; }; /* For getting at array ptr */
472 int i;
474 lastptr = 0;
475 for(;mp = (char *)((struct integ *)astruc)->word; astruc += 1)
476 { cp = astr;
477 for(i = cnt; i > 0; i--)
478 { switch((c = *cp++) ^ *mp++) /* XOR the chars */
479 { case 0: continue; /* Exact match */
480 case 040: if(isalpha(c))
481 continue;
483 break;
485 if(i==0)
486 if(*mp == 0) return((unsigned int)astruc); /* Exact match */
487 else if(lastptr) return(0); /* Ambiguous */
488 else lastptr = astruc; /* 1st ambig */
490 return((unsigned int)lastptr);
495 zaptime(tp)
496 register int *tp;
497 /* clears tm structure pointed to by tp */
498 { register int i;
499 i = (sizeof (struct tm))/(sizeof (int));
500 do *tp++ = TMNULL; /* Set entry to "unspecified" */
501 while(--i); /* Faster than FOR */