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

Defined functions

pt12hack defined in line 338; used 2 times
ptitoken defined in line 365; used 6 times
ptmatchstr defined in line 454; used 1 times
ptnoise defined in line 358; used 2 times
ptstash defined in line 326; used 16 times
pttoken defined in line 412; used 1 times
zaptime defined in line 485; used 1 times

Defined variables

rcsid defined in line 30; never used
timeid defined in line 51; never used
tmwords defined in line 71; used 1 times

Defined struct's

integ defined in line 461; used 2 times
  • in line 465(2)
tmwent defined in line 54; used 10 times
token defined in line 131; used 6 times

Defined macros

TMWILD defined in line 128; never used
TW1200 defined in line 67; used 3 times
TWDST defined in line 66; used 11 times
TWSPEC defined in line 64; used 2 times
TWTIME defined in line 65; used 28 times
Last modified: 1988-08-13
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 2684
Valid CSS Valid XHTML 1.0 Strict