|
|
1.1 root 1: /*
2: * PARTIME parse date/time string into a TM structure
3: *
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)
19: */
20:
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?
27: */
28:
29: #ifndef lint
30: static char rcsid[]=
31: "$Header: /usr/src/local/bin/rcs/src/RCS/partime.c,v 1.4 89/05/01 14:48:46 narten Exp $";
32: #endif
33:
34: /* $Log: partime.c,v $
35: * Revision 1.4 89/05/01 14:48:46 narten
36: * fixed #ifdef DEBUG construct
37: *
38: * Revision 1.3 88/11/08 12:02:15 narten
39: * changes from [email protected] (Paul Eggert)
40: *
41: * Revision 1.3 88/08/28 14:53:40 eggert
42: * Remove unportable "#endif XXX"s.
43: *
44: * Revision 1.2 87/03/27 14:21:53 jenkins
45: * Port to suns
46: *
47: * Revision 1.1 84/01/23 14:50:07 kcs
48: * Initial revision
49: *
50: * Revision 1.1 82/05/06 11:38:26 wft
51: * Initial revision
52: *
53: */
54:
55: #include <stdio.h>
56: #include <ctype.h>
57: #include "time.h"
58:
59: #ifndef lint
60: static char timeid[] = TIMEID;
61: #endif
62:
63: struct tmwent {
64: char *went;
65: long wval; /* must be big enough to hold pointer or integer */
66: char wflgs;
67: char wtype;
68: };
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) */
74:
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},
90:
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},
98:
99: {"gmt", 0*60, TWTIME, TM_ZON}, /* Greenwich */
100: {"gst", 0*60, TWTIME, TM_ZON},
101: {"gdt", 0*60, TWTIME+TWDST, TM_ZON}, /* ?? */
102:
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 */
111:
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 */
120:
121: {"daylight", 1, TWTIME+TWDST, TM_ZON}, /* Local Daylight */
122: {"standard", 1, TWTIME, TM_ZON}, /* Local Standard */
123: {"std", 1, TWTIME, TM_ZON}, /* " " */
124:
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 */
130:
131: {0, 0, 0, 0}, /* Zero entry to terminate searches */
132: };
133:
134: #define TMWILD (-2) /* Value meaning item specified as wild-card */
135: /* (May use someday...) */
136:
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;
147: };
148:
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)();
159:
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;
165:
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! */
172: }
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;
180: }
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;
191: }
192:
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;
202: }
203:
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;
217: }
218:
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. */
240: }
241:
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. */
247:
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;
263: }
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" */
269: }
270:
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.
275: */
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. */
281:
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" */
292:
293: dd2: if(ptstash(&tp[TM_DAY],i)) /* Store day (1 based) */
294: return(0);
295: goto domore;
296:
297: mm2: if(ptstash(&tp[TM_MON], i-1)) /* Store month (make zero based) */
298: return(0);
299: goto domore;
300:
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;
305:
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;
327: }
328:
329: /* Store date/time value, return 0 if successful.
330: * Fails if entry already set to a different value.
331: */
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);
339: }
340:
341: /* This subroutine is invoked for NOON or MIDNIGHT when wrapping up
342: * just prior to returning from partime.
343: */
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);
360: }
361:
362: /* Null routine for no-op tokens */
363:
364: ptnoise() { return(1); }
365:
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
369: */
370:
371: ptitoken(astr, tkp)
372: register struct token *tkp;
373: char *astr;
374: {
375: register char *cp;
376: register int i;
377:
378: tkp->tval.tnum = 0;
379: if(pttoken(astr,tkp) == 0)
380: #ifdef DEBUG
381: {
382: VOID printf("EOF\n");
383: return(0);
384: }
385: #else
386: return(0);
387: #endif
388: cp = tkp->tcp;
389:
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
396:
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 */
403:
404: #ifdef DEBUG
405: if(!i) VOID printf("Not found!\n");
406: #endif
407:
408: if(!i) return(0);
409: }
410:
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
417:
418: return(1);
419: }
420:
421: /* Read token from input string into token structure */
422: pttoken(astr,tkp)
423: register struct token *tkp;
424: char *astr;
425: {
426: register char *cp;
427: register int c;
428:
429: tkp->tcp = cp = astr;
430: tkp->tbrkl = tkp->tbrk; /* Set "last break" */
431: tkp->tcnt = tkp->tbrk = tkp->tflg = 0;
432:
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. */
445: }
446: tkp->tbrk = c;
447: return(tkp->tcnt);
448: }
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);
458: }
459: }
460: return(tkp->tcnt); /* When hit EOF */
461: }
462:
463:
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;
473:
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;
482: }
483: break;
484: }
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 */
489: }
490: return((unsigned int)lastptr);
491: }
492:
493:
494:
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 */
502: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.