|
|
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: static char rcsid[]=
30: "$Header: /usr/wft/RCS/SRC/RCS/partime.c,v 1.1 82/05/06 11:38:26 wft Exp $";
31:
32: /* $Log: partime.c,v $
33: * Revision 1.1 82/05/06 11:38:26 wft
34: * Initial revision
35: *
36: */
37:
38: #include <stdio.h>
39: #include <ctype.h>
40: #include "time.h"
41:
42: static char timeid[] = TIMEID;
43:
44: struct tmwent {
45: char *went;
46: int wval;
47: char wflgs;
48: char wtype;
49: };
50: /* wflgs */
51: #define TWSPEC 01 /* Word wants special processing */
52: #define TWTIME 02 /* Word is a time value (absence implies date) */
53: #define TWDST 04 /* Word is a DST-type timezone */
54: #define TW1200 010 /* Word is NOON or MIDNIGHT (sigh) */
55:
56: int pt12hack();
57: int ptnoise();
58: struct tmwent tmwords [] = {
59: {"january",
60: 0,
61: 0,
62: TM_MON},
63: {"february", 1, 0, TM_MON},
64: {"march", 2, 0, TM_MON},
65: {"april", 3, 0, TM_MON},
66: {"may", 4, 0, TM_MON},
67: {"june", 5, 0, TM_MON},
68: {"july", 6, 0, TM_MON},
69: {"august", 7, 0, TM_MON},
70: {"september", 8, 0, TM_MON},
71: {"october", 9, 0, TM_MON},
72: {"november", 10, 0, TM_MON},
73: {"december", 11, 0, TM_MON},
74:
75: {"sunday", 0, 0, TM_WDAY},
76: {"monday", 1, 0, TM_WDAY},
77: {"tuesday", 2, 0, TM_WDAY},
78: {"wednesday", 3, 0, TM_WDAY},
79: {"thursday", 4, 0, TM_WDAY},
80: {"friday", 5, 0, TM_WDAY},
81: {"saturday", 6, 0, TM_WDAY},
82:
83: {"gmt", 0*60, TWTIME, TM_ZON}, /* Greenwich */
84: {"gst", 0*60, TWTIME, TM_ZON},
85: {"gdt", 0*60, TWTIME+TWDST, TM_ZON}, /* ?? */
86:
87: {"ast", 4*60, TWTIME, TM_ZON}, /* Atlantic */
88: {"est", 5*60, TWTIME, TM_ZON}, /* Eastern */
89: {"cst", 6*60, TWTIME, TM_ZON}, /* Central */
90: {"mst", 7*60, TWTIME, TM_ZON}, /* Mountain */
91: {"pst", 8*60, TWTIME, TM_ZON}, /* Pacific */
92: {"yst", 9*60, TWTIME, TM_ZON}, /* Yukon */
93: {"hst", 10*60, TWTIME, TM_ZON}, /* Hawaii */
94: {"bst", 11*60, TWTIME, TM_ZON}, /* Bering */
95:
96: {"adt", 4*60, TWTIME+TWDST, TM_ZON}, /* Atlantic */
97: {"edt", 5*60, TWTIME+TWDST, TM_ZON}, /* Eastern */
98: {"cdt", 6*60, TWTIME+TWDST, TM_ZON}, /* Central */
99: {"mdt", 7*60, TWTIME+TWDST, TM_ZON}, /* Mountain */
100: {"pdt", 8*60, TWTIME+TWDST, TM_ZON}, /* Pacific */
101: {"ydt", 9*60, TWTIME+TWDST, TM_ZON}, /* Yukon */
102: {"hdt", 10*60, TWTIME+TWDST, TM_ZON}, /* Hawaii */
103: {"bdt", 11*60, TWTIME+TWDST, TM_ZON}, /* Bering */
104:
105: {"daylight", 1, TWTIME+TWDST, TM_ZON}, /* Local Daylight */
106: {"standard", 1, TWTIME, TM_ZON}, /* Local Standard */
107: {"std", 1, TWTIME, TM_ZON}, /* " " */
108:
109: {"am", 1, TWTIME, TM_AMPM},
110: {"pm", 2, TWTIME, TM_AMPM},
111: {"noon", 12,TWTIME+TW1200, 0}, /* Special frobs */
112: {"midnight", 0, TWTIME+TW1200, 0},
113: {"at", (int)ptnoise, TWSPEC, 0}, /* Noise word */
114:
115: {0, 0, 0, 0}, /* Zero entry to terminate searches */
116: };
117:
118: #define TMWILD (-2) /* Value meaning item specified as wild-card */
119: /* (May use someday...) */
120:
121: struct token {
122: char *tcp; /* pointer to string */
123: int tcnt; /* # chars */
124: char tbrk; /* "break" char */
125: char tbrkl; /* last break char */
126: char tflg; /* 0 = alpha, 1 = numeric */
127: union { /* Resulting value; */
128: int tnum;/* either a #, or */
129: struct tmwent *ttmw;/* ptr to a tmwent. */
130: } tval;
131: };
132:
133: partime(astr, atm)
134: char *astr;
135: struct tm *atm;
136: { register int *tp;
137: register struct tmwent *twp;
138: register int i;
139: struct token btoken, atoken;
140: char *cp, ch;
141: int ord, midnoon;
142: int (*aproc)();
143:
144: tp = (int *)atm;
145: zaptime(tp); /* Initialize the TM structure */
146: midnoon = TMNULL; /* and our own temp stuff */
147: btoken.tcnt = btoken.tbrkl = 0;
148: btoken.tcp = astr;
149:
150: domore:
151: if(!ptitoken(btoken.tcp+btoken.tcnt,&btoken)) /* Get a token */
152: { if(btoken.tval.tnum) return(0); /* Read error? */
153: if(midnoon != TMNULL) /* EOF, wrap up */
154: return(pt12hack(tp, midnoon));
155: return(1); /* Win return! */
156: }
157: if(btoken.tflg == 0) /* Alpha? */
158: { twp = btoken.tval.ttmw; /* Yes, get ptr to entry */
159: if(twp->wflgs&TWSPEC) /* Special alpha crock */
160: { aproc = (int (*) ()) (twp->wval);
161: if(!(*aproc)(tp, twp, &btoken))
162: return(0); /* ERR: special word err */
163: goto domore;
164: }
165: if(twp->wflgs&TW1200)
166: if(ptstash(&midnoon,twp->wval))
167: return(0); /* ERR: noon/midnite clash */
168: else goto domore;
169: if(ptstash(&tp[twp->wtype],twp->wval))
170: return(0); /* ERR: val already set */
171: if(twp->wtype == TM_ZON) /* If was zone, hack DST */
172: if(ptstash(&tp[TM_ISDST],(twp->wflgs&TWDST)))
173: return(0); /* ERR: DST conflict */
174: goto domore;
175: }
176:
177: /* Token is number. Lots of hairy heuristics. */
178: if(btoken.tcnt >= 7) /* More than 6 digits in string? */
179: return(0); /* ERR: number too big */
180: if(btoken.tcnt == 6) /* 6 digits = HHMMSS. Needs special crock */
181: { /* since 6 digits are too big for integer! */
182: i = (btoken.tcp[0]-'0')*10 /* Gobble 1st 2 digits */
183: + btoken.tcp[1]-'0';
184: btoken.tcnt = 2; /* re-read last 4 chars */
185: goto coltime;
186: }
187:
188: i = btoken.tval.tnum; /* Value now known to be valid; get it. */
189: if( btoken.tcnt == 5 /* 5 digits = HMMSS */
190: || btoken.tcnt == 3) /* 3 digits = HMM */
191: { if(btoken.tcnt != 3)
192: if(ptstash(&tp[TM_SEC], i%100))
193: return(0); /* ERR: sec conflict */
194: else i /= 100;
195: hhmm4: if(ptstash(&tp[TM_MIN], i%100))
196: return(0); /* ERR: min conflict */
197: i /= 100;
198: hh2: if(ptstash(&tp[TM_HOUR], i))
199: return(0); /* ERR: hour conflict */
200: goto domore;
201: }
202:
203: if(btoken.tcnt == 4) /* 4 digits = YEAR or HHMM */
204: { if(tp[TM_YEAR] != TMNULL) goto hhmm4; /* Already got yr? */
205: if(tp[TM_HOUR] != TMNULL) goto year4; /* Already got hr? */
206: if((i%100) > 59) goto year4; /* MM >= 60? */
207: if(btoken.tbrk == ':') /* HHMM:SS ? */
208: if( ptstash(&tp[TM_HOUR],i/100)
209: || ptstash(&tp[TM_MIN], i%100))
210: return(0); /* ERR: hr/min clash */
211: else goto coltm2; /* Go handle SS */
212: if(btoken.tbrk != ',' && btoken.tbrk != '/'
213: && ptitoken(btoken.tcp+btoken.tcnt,&atoken) /* Peek */
214: && atoken.tflg == 0 /* alpha */
215: && (atoken.tval.ttmw->wflgs&TWTIME)) /* HHMM-ZON */
216: goto hhmm4;
217: if(btoken.tbrkl == '-' /* DD-Mon-YYYY */
218: || btoken.tbrkl == ',' /* Mon DD, YYYY */
219: || btoken.tbrkl == '/' /* MM/DD/YYYY */
220: || btoken.tbrkl == '.' /* DD.MM.YYYY */
221: || btoken.tbrk == '-' /* YYYY-MM-DD */
222: ) goto year4;
223: goto hhmm4; /* Give up, assume HHMM. */
224: }
225:
226: /* From this point on, assume tcnt == 1 or 2 */
227: /* 2 digits = YY, MM, DD, or HH (MM and SS caught at coltime) */
228: if(btoken.tbrk == ':') /* HH:MM[:SS] */
229: goto coltime; /* must be part of time. */
230: if(i > 31) goto yy2; /* If >= 32, only YY poss. */
231:
232: /* Check for numerical-format date */
233: for (cp = "/-."; ch = *cp++;)
234: { ord = (ch == '.' ? 0 : 1); /* n/m = D/M or M/D */
235: if(btoken.tbrk == ch) /* "NN-" */
236: { if(btoken.tbrkl != ch)
237: { if(ptitoken(btoken.tcp+btoken.tcnt,&atoken)
238: && atoken.tflg == 0
239: && atoken.tval.ttmw->wtype == TM_MON)
240: goto dd2;
241: if(ord)goto mm2; else goto dd2; /* "NN-" */
242: } /* "-NN-" */
243: if(tp[TM_DAY] == TMNULL
244: && tp[TM_YEAR] != TMNULL) /* If "YY-NN-" */
245: goto mm2; /* then always MM */
246: if(ord)goto dd2; else goto mm2;
247: }
248: if(btoken.tbrkl == ch /* "-NN" */
249: && tp[ord ? TM_MON : TM_DAY] != TMNULL)
250: if(tp[ord ? TM_DAY : TM_MON] == TMNULL) /* MM/DD */
251: if(ord)goto dd2; else goto mm2;
252: else goto yy2; /* "-YY" */
253: }
254:
255: /* At this point only YY, DD, and HH are left.
256: * YY is very unlikely since value is <= 32 and there was
257: * no numerical format date. Make one last try at YY
258: * before dropping through to DD vs HH code.
259: */
260: if(btoken.tcnt == 2 /* If 2 digits */
261: && tp[TM_HOUR] != TMNULL /* and already have hour */
262: && tp[TM_DAY] != TMNULL /* and day, but */
263: && tp[TM_YEAR] == TMNULL) /* no year, then assume */
264: goto yy2; /* that's what we have. */
265:
266: /* Now reduced to choice between HH and DD */
267: if(tp[TM_HOUR] != TMNULL) goto dd2; /* Have hour? Assume day. */
268: if(tp[TM_DAY] != TMNULL) goto hh2; /* Have day? Assume hour. */
269: if(i > 24) goto dd2; /* Impossible HH means DD */
270: if(!ptitoken(btoken.tcp+btoken.tcnt, &atoken)) /* Read ahead! */
271: if(atoken.tval.tnum) return(0); /* ERR: bad token */
272: else goto dd2; /* EOF, assume day. */
273: if( atoken.tflg == 0 /* If next token is an alpha */
274: && atoken.tval.ttmw->wflgs&TWTIME) /* time-spec, assume hour */
275: goto hh2; /* e.g. "3 PM", "11-EDT" */
276:
277: dd2: if(ptstash(&tp[TM_DAY],i)) /* Store day (1 based) */
278: return(0);
279: goto domore;
280:
281: mm2: if(ptstash(&tp[TM_MON], i-1)) /* Store month (make zero based) */
282: return(0);
283: goto domore;
284:
285: yy2: i += 1900;
286: year4: if(ptstash(&tp[TM_YEAR],i)) /* Store year (full number) */
287: return(0); /* ERR: year conflict */
288: goto domore;
289:
290: /* Hack HH:MM[[:]SS] */
291: coltime:
292: if(ptstash(&tp[TM_HOUR],i)) return(0);
293: if(!ptitoken(btoken.tcp+btoken.tcnt,&btoken))
294: return(!btoken.tval.tnum);
295: if(!btoken.tflg) return(0); /* ERR: HH:<alpha> */
296: if(btoken.tcnt == 4) /* MMSS */
297: if(ptstash(&tp[TM_MIN],btoken.tval.tnum/100)
298: || ptstash(&tp[TM_SEC],btoken.tval.tnum%100))
299: return(0);
300: else goto domore;
301: if(btoken.tcnt != 2
302: || ptstash(&tp[TM_MIN],btoken.tval.tnum))
303: return(0); /* ERR: MM bad */
304: if(btoken.tbrk != ':') goto domore; /* Seconds follow? */
305: coltm2: if(!ptitoken(btoken.tcp+btoken.tcnt,&btoken))
306: return(!btoken.tval.tnum);
307: if(!btoken.tflg || btoken.tcnt != 2 /* Verify SS */
308: || ptstash(&tp[TM_SEC], btoken.tval.tnum))
309: return(0); /* ERR: SS bad */
310: goto domore;
311: }
312:
313: /* Store date/time value, return 0 if successful.
314: * Fails if entry already set to a different value.
315: */
316: ptstash(adr,val)
317: int *adr;
318: { register int *a;
319: if( *(a=adr) != TMNULL)
320: return(*a != val);
321: *a = val;
322: return(0);
323: }
324:
325: /* This subroutine is invoked for NOON or MIDNIGHT when wrapping up
326: * just prior to returning from partime.
327: */
328: pt12hack(atp, aval)
329: int *atp, aval;
330: { register int *tp, i, h;
331: tp = atp;
332: if (((i=tp[TM_MIN]) && i != TMNULL) /* Ensure mins, secs */
333: || ((i=tp[TM_SEC]) && i != TMNULL)) /* are 0 or unspec'd */
334: return(0); /* ERR: MM:SS not 00:00 */
335: i = aval; /* Get 0 or 12 (midnite or noon) */
336: if ((h = tp[TM_HOUR]) == TMNULL /* If hour unspec'd, win */
337: || h == 12) /* or if 12:00 (matches either) */
338: tp[TM_HOUR] = i; /* Then set time */
339: else if(!(i == 0 /* Nope, but if midnight and */
340: &&(h == 0 || h == 24))) /* time matches, can pass. */
341: return(0); /* ERR: HH conflicts */
342: tp[TM_AMPM] = TMNULL; /* Always reset this value if won */
343: return(1);
344: }
345:
346: /* Null routine for no-op tokens */
347:
348: ptnoise() { return(1); }
349:
350: /* Get a token and identify it to some degree.
351: * Returns 0 on failure; token.tval will be 0 for normal EOF, otherwise
352: * hit error of some sort
353: */
354:
355: ptitoken(astr, tkp)
356: register struct token *tkp;
357: struct token *astr;
358: {
359: register char *cp;
360: register int i;
361:
362: tkp->tval.tnum = 0;
363: if(pttoken(astr,tkp) == 0)
364: #ifdef DEBUG
365: printf("EOF\n");
366: #endif DEBUG
367: return(0);
368: cp = tkp->tcp;
369:
370: #ifdef DEBUG
371: i = cp[tkp->tcnt];
372: cp[tkp->tcnt] = 0;
373: printf("Token: \"%s\" ",cp);
374: cp[tkp->tcnt] = i;
375: #endif DEBUG
376:
377: if(tkp->tflg)
378: for(i = tkp->tcnt; i > 0; i--)
379: tkp->tval.tnum = (int)tkp->tval.tnum*10 + ((*cp++)-'0');
380: else
381: { i = ptmatchstr(cp, tkp->tcnt, tmwords, sizeof (struct tmwent));
382: tkp->tval.tnum = i ? i : -1; /* Set -1 for error */
383:
384: #ifdef DEBUG
385: if(!i) printf("Not found!\n");
386: #endif DEBUG
387:
388: if(!i) return(0);
389: }
390:
391: #ifdef DEBUG
392: if(tkp->tflg)
393: printf("Val: %d.\n",tkp->tval.tnum);
394: else printf("Found: \"%s\", val: %d., type %d\n",
395: tkp->tval.ttmw->went,tkp->tval.ttmw->wval,tkp->tval.ttmw->wtype);
396: #endif DEBUG
397:
398: return(1);
399: }
400:
401: /* Read token from input string into token structure */
402: pttoken(astr,tkp)
403: register struct token *tkp;
404: char *astr;
405: {
406: register char *cp;
407: register int c;
408:
409: tkp->tcp = cp = astr;
410: tkp->tbrkl = tkp->tbrk; /* Set "last break" */
411: tkp->tcnt = tkp->tbrk = tkp->tflg = 0;
412:
413: while(c = *cp++)
414: { switch(c)
415: { case ' ': case '\t': /* Flush all whitespace */
416: while((c = *cp++) && isspace(c));
417: cp--; /* Drop thru to handle brk */
418: case '(': case ')': /* Perhaps any non-alphanum */
419: case '-': case ',': /* shd qualify as break? */
420: case '/': case ':': case '.': /* Break chars */
421: if(tkp->tcnt == 0) /* If no token yet */
422: { tkp->tcp = cp; /* ignore the brk */
423: tkp->tbrkl = c;
424: continue; /* and go on. */
425: }
426: tkp->tbrk = c;
427: return(tkp->tcnt);
428: }
429: if(tkp->tcnt == 0) /* If first char of token, */
430: tkp->tflg = isdigit(c); /* determine type */
431: if(( isdigit(c) && tkp->tflg) /* If not first, make sure */
432: ||(!isdigit(c) && !tkp->tflg)) /* char matches type */
433: tkp->tcnt++; /* Win, add to token. */
434: else {
435: cp--; /* Wrong type, back up */
436: tkp->tbrk = c;
437: return(tkp->tcnt);
438: }
439: }
440: return(tkp->tcnt); /* When hit EOF */
441: }
442:
443:
444: ptmatchstr(astr,cnt,astruc,size)
445: char *astr;
446: int cnt,size;
447: struct tmwent *astruc;
448: { register char *cp, *mp;
449: register int c;
450: struct tmwent *lastptr;
451: struct integ { int word; }; /* For getting at array ptr */
452: int i;
453:
454: lastptr = 0;
455: for(;mp = (char *)((struct integ *)astruc)->word; astruc += 1)
456: { cp = astr;
457: for(i = cnt; i > 0; i--)
458: { switch((c = *cp++) ^ *mp++) /* XOR the chars */
459: { case 0: continue; /* Exact match */
460: case 040: if(isalpha(c))
461: continue;
462: }
463: break;
464: }
465: if(i==0)
466: if(*mp == 0) return((unsigned int)astruc); /* Exact match */
467: else if(lastptr) return(0); /* Ambiguous */
468: else lastptr = astruc; /* 1st ambig */
469: }
470: return((unsigned int)lastptr);
471: }
472:
473:
474:
475: zaptime(atm)
476: struct tm *atm;
477: /* clears atm */
478: { register int *tp, i;
479: tp = (int *)atm;
480: i = (sizeof (struct tm))/(sizeof (int));
481: do *tp++ = TMNULL; /* Set entry to "unspecified" */
482: while(--i); /* Faster than FOR */
483: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.