|
|
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.