home *** CD-ROM | disk | FTP | other *** search
/ rtsi.com / 2014.01.www.rtsi.com.tar / www.rtsi.com / OS9 / OSK / CMDS / rcs4.lha / partime.c < prev    next >
Text File  |  1993-03-03  |  15KB  |  492 lines

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