home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / RCS_SRC.ZIP / PARTIME.C < prev    next >
C/C++ Source or Header  |  1991-01-15  |  16KB  |  503 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: /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  eggert@sm.unisys.com (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. }
  503.