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