home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Professional / OS2PRO194.ISO / os2 / prgramer / rcs / sources / partime.c < prev    next >
C/C++ Source or Header  |  1992-01-14  |  21KB  |  663 lines

  1. /*
  2.  * PARTIME        parse date/time string into a TM structure
  3.  *
  4.  * Returns:
  5.  *    0 if parsing failed
  6.  *    else time values in specified TM structure and zone (unspecified values
  7.  *        set to TMNULL)
  8.  * Notes:
  9.  *    This code is quasi-public; it may be used freely in like software.
  10.  *    It is not to be sold, nor used in licensed software without
  11.  *    permission of the author.
  12.  *    For everyone's benefit, please report bugs and improvements!
  13.  *     Copyright 1980 by Ken Harrenstien, SRI International.
  14.  *    (ARPANET: KLH @ SRI)
  15.  */
  16.  
  17. /* Hacknotes:
  18.  *    If parsing changed so that no backup needed, could perhaps modify
  19.  *        to use a FILE input stream.  Need terminator, though.
  20.  *    Perhaps should return 0 on success, else a non-zero error val?
  21.  */
  22.  
  23. /* $Log:    partime.c,v $
  24.  * Revision 1.1.1.1  91/01/18  12:18:12  berliner
  25.  * For CVS 1.2, contributed by Paul Eggert
  26.  *
  27.  * Revision 1.2  1991/01/18  00:14:49  eggert
  28.  * Make minimal changes to interface to CVS 1.0.
  29.  *
  30.  * Revision 5.4  1990/10/04  06:30:15  eggert
  31.  * Remove date vs time heuristics that fail between 2000 and 2400.
  32.  * Check for overflow when lexing an integer.
  33.  * Parse 'Jan 10 LT' as 'Jan 10, LT', not 'Jan, 10 LT'.
  34.  *
  35.  * Revision 5.3  1990/09/24  18:56:31  eggert
  36.  * Update timezones.
  37.  *
  38.  * Revision 5.2  1990/09/04  08:02:16  eggert
  39.  * Don't parse two-digit years, because it won't work after 1999/12/31.
  40.  * Don't permit 'Aug Aug'.
  41.  *
  42.  * Revision 5.1  1990/08/29  07:13:49  eggert
  43.  * Be able to parse our own date format.  Don't assume year<10000.
  44.  *
  45.  * Revision 5.0  1990/08/22  08:12:40  eggert
  46.  * Switch to GMT and fix the bugs exposed thereby.  Update timezones.
  47.  * Ansify and Posixate.  Fix peekahead and int-size bugs.
  48.  *
  49.  * Revision 1.4  89/05/01  14:48:46  narten
  50.  * fixed #ifdef DEBUG construct
  51.  *
  52.  * Revision 1.3  88/08/28  14:53:40  eggert
  53.  * Remove unportable "#endif XXX"s.
  54.  *
  55.  * Revision 1.2  87/03/27  14:21:53  jenkins
  56.  * Port to suns
  57.  *
  58.  * Revision 1.1  82/05/06  11:38:26  wft
  59.  * Initial revision
  60.  *
  61.  */
  62.  
  63. /* minimal changes needed to get this file to work for CVS rather than RCS */
  64. /* #include "rcsbase.h" */
  65. #include <ctype.h>
  66. #include <time.h>
  67. #include "cvs.h"
  68. #define libId(x,y) static char x[] = y;
  69. #define P(x) ()
  70. #if !__STDC__
  71. #    define const
  72. #endif
  73. /* see also `RCS' in comment below */
  74.  
  75. libId(partId, "$Id: partime.c,v 1.1.1.1 91/01/18 12:18:12 berliner Exp $")
  76.  
  77. #define given(v) (0 <= (v))
  78. #define TMNULL (-1) /* Items not given are given this value */
  79. #define TZ_OFFSET (24*60) /* TMNULL  <  zone_offset - TZ_OFFSET */
  80.  
  81. struct tmwent {
  82.     const char *went;
  83.     short wval;
  84.     char wflgs;
  85.     char wtype;
  86. };
  87.     /* wflgs */
  88. #define TWTIME 02    /* Word is a time value (absence implies date) */
  89. #define TWDST  04    /* Word is a DST-type timezone */
  90.     /* wtype */
  91. #define TM_MON    1    /* month name */
  92. #define TM_WDAY    2    /* weekday name */
  93. #define TM_ZON    3    /* time zone name */
  94. #define TM_LT    4    /* local time */
  95. #define TM_DST    5    /* daylight savings time */
  96. #define TM_12    6    /* AM, PM, NOON, or MIDNIGHT */
  97.     /* wval (for wtype==TM_12) */
  98. #define T12_AM 1
  99. #define T12_PM 2
  100. #define T12_NOON 12
  101. #define T12_MIDNIGHT 0
  102.  
  103. static const struct tmwent tmwords [] = {
  104.     {"january",      0, 0, TM_MON},
  105.     {"february",     1, 0, TM_MON},
  106.     {"march",        2, 0, TM_MON},
  107.     {"april",        3, 0, TM_MON},
  108.     {"may",          4, 0, TM_MON},
  109.     {"june",         5, 0, TM_MON},
  110.     {"july",         6, 0, TM_MON},
  111.     {"august",       7, 0, TM_MON},
  112.     {"september",    8, 0, TM_MON},
  113.     {"october",      9, 0, TM_MON},
  114.     {"november",     10, 0, TM_MON},
  115.     {"december",     11, 0, TM_MON},
  116.  
  117.     {"sunday",       0, 0, TM_WDAY},
  118.     {"monday",       1, 0, TM_WDAY},
  119.     {"tuesday",      2, 0, TM_WDAY},
  120.     {"wednesday",    3, 0, TM_WDAY},
  121.     {"thursday",     4, 0, TM_WDAY},
  122.     {"friday",       5, 0, TM_WDAY},
  123.     {"saturday",     6, 0, TM_WDAY},
  124.  
  125.     {"gmt",          0*60, TWTIME, TM_ZON},   /* Greenwich */
  126.     {"utc",          0*60, TWTIME, TM_ZON},
  127.     {"ut",           0*60, TWTIME, TM_ZON},
  128.  
  129.     {"nzst",        -12*60, TWTIME, TM_ZON},  /* New Zealand */
  130.     {"jst",         -9*60, TWTIME, TM_ZON},   /* Japan */
  131.     {"kst",         -9*60, TWTIME, TM_ZON},   /* Korea */
  132.     {"ist",         -5*60-30, TWTIME, TM_ZON},/* India */
  133.     {"eet",         -2*60, TWTIME, TM_ZON},   /* Eastern Europe */
  134.     {"cet",         -1*60, TWTIME, TM_ZON},   /* Central Europe */
  135.     {"met",         -1*60, TWTIME, TM_ZON},   /* Middle Europe */
  136.     {"wet",          0*60, TWTIME, TM_ZON},   /* Western Europe */
  137.     {"nst",          3*60+30, TWTIME, TM_ZON},/* Newfoundland */
  138.     {"ast",          4*60, TWTIME, TM_ZON},   /* Atlantic */
  139.     {"est",          5*60, TWTIME, TM_ZON},   /* Eastern */
  140.     {"cst",          6*60, TWTIME, TM_ZON},   /* Central */
  141.     {"mst",          7*60, TWTIME, TM_ZON},   /* Mountain */
  142.     {"pst",          8*60, TWTIME, TM_ZON},   /* Pacific */
  143.     {"akst",         9*60, TWTIME, TM_ZON},   /* Alaska */
  144.     {"hast",         10*60, TWTIME, TM_ZON},  /* Hawaii-Aleutian */
  145.     {"hst",          10*60, TWTIME, TM_ZON},  /* Hawaii */
  146.     {"sst",          11*60, TWTIME, TM_ZON},  /* Samoa */
  147.  
  148.     {"nzdt",        -12*60, TWTIME+TWDST, TM_ZON},    /* New Zealand */
  149.     {"kdt",         -9*60, TWTIME+TWDST, TM_ZON},     /* Korea */
  150.     {"bst",          0*60, TWTIME+TWDST, TM_ZON},     /* Britain */
  151.     {"ndt",          2*60+30, TWTIME+TWDST, TM_ZON}, /*Newfoundland (DDST)*/
  152.     {"adt",          4*60, TWTIME+TWDST, TM_ZON},     /* Atlantic */
  153.     {"edt",          5*60, TWTIME+TWDST, TM_ZON},     /* Eastern */
  154.     {"cdt",          6*60, TWTIME+TWDST, TM_ZON},     /* Central */
  155.     {"mdt",          7*60, TWTIME+TWDST, TM_ZON},     /* Mountain */
  156.     {"pdt",          8*60, TWTIME+TWDST, TM_ZON},     /* Pacific */
  157.     {"akdt",         9*60, TWTIME+TWDST, TM_ZON},     /* Alaska */
  158.     {"hadt",         10*60, TWTIME+TWDST, TM_ZON},    /* Hawaii-Aleutian */
  159.  
  160. #if 0
  161.     /*
  162.      * The following names are duplicates or are not well attested.
  163.      * A standard is needed.
  164.      */
  165.     {"?st",         -13*60, TWTIME, TM_ZON},  /* Uelen */
  166.     {"?st",         -11*60, TWTIME, TM_ZON},  /* Magadan */
  167.     {"east",        -10*60, TWTIME, TM_ZON},  /* Eastern Australia */
  168.     {"cast",        -9*60-30, TWTIME, TM_ZON},/* Central Australia */
  169.     {"cst",         -8*60, TWTIME, TM_ZON},   /* China */
  170.     {"hkt",         -8*60, TWTIME, TM_ZON},   /* Hong Kong */
  171.     {"sst",         -8*60, TWTIME, TM_ZON},   /* Singapore */
  172.     {"wast",        -8*60, TWTIME, TM_ZON},   /* Western Australia */
  173.     {"?st",         -7*60, TWTIME, TM_ZON},   /* Novosibirsk */
  174.     {"jt",          -7*60-30, TWTIME, TM_ZON},/* Java */
  175.     {"nst",         -6*60-30, TWTIME, TM_ZON},/* North Sumatra */
  176.     {"?st",         -6*60, TWTIME, TM_ZON},   /* Tashkent */
  177.     {"?st",         -5*60, TWTIME, TM_ZON},      /* Sverdlovsk */
  178.     {"?",           -4*60-30, TWTIME, TM_ZON},/* Afghanistan */
  179.     {"?st",         -4*60, TWTIME, TM_ZON},      /* Rostov */
  180.     {"it",          -3*60-30, TWTIME, TM_ZON},/* Iran */
  181.     {"?st",         -3*60, TWTIME, TM_ZON},   /* Moscow */
  182.     {"ist",         -2*60, TWTIME, TM_ZON},   /* Israel */
  183.     {"ast",          1*60, TWTIME, TM_ZON},   /* Azores */
  184.     {"fst",          2*60, TWTIME, TM_ZON},   /* Fernando de Noronha */
  185.     {"bst",          3*60, TWTIME, TM_ZON},   /* Brazil */
  186.     {"wst",          4*60, TWTIME, TM_ZON},   /* Western Brazil */
  187.     {"ast",          5*60, TWTIME, TM_ZON},   /* Acre Brazil */
  188.     {"?",            9*60+30, TWTIME, TM_ZON},/* Marquesas */
  189.     {"?st",          12*60, TWTIME, TM_ZON},  /* Kwajalein */
  190.  
  191.     {"?dt",         -13*60, TWTIME+TWDST, TM_ZON},      /* Uelen */
  192.     {"?dt",         -11*60, TWTIME+TWDST, TM_ZON},      /* Magadan */
  193.     {"eadt",        -10*60, TWTIME+TWDST, TM_ZON},    /* Eastern Australia */
  194.     {"cadt",        -9*60-30, TWTIME+TWDST, TM_ZON},  /* Central Australia */
  195.     {"cdt",         -8*60, TWTIME+TWDST, TM_ZON},     /* China */
  196.     {"wadt",        -8*60, TWTIME+TWDST, TM_ZON},     /* Western Australia */
  197.     {"?dt",         -7*60, TWTIME+TWDST, TM_ZON},      /* Novosibirsk */
  198.     {"?dt",         -6*60, TWTIME+TWDST, TM_ZON},      /* Tashkent */
  199.     {"?dt",         -5*60, TWTIME+TWDST, TM_ZON},      /* Sverdlovsk */
  200.     {"?dt",         -4*60, TWTIME+TWDST, TM_ZON},      /* Rostov */
  201.     {"?dt",         -3*60, TWTIME+TWDST, TM_ZON},     /* Moscow */
  202.     {"idt",         -2*60, TWTIME+TWDST, TM_ZON},     /* Israel */
  203.     {"eest",        -2*60, TWTIME+TWDST, TM_ZON},     /* Eastern Europe */
  204.     {"cest",        -1*60, TWTIME+TWDST, TM_ZON},     /* Central Europe */
  205.     {"mest",        -1*60, TWTIME+TWDST, TM_ZON},     /* Middle Europe */
  206.     {"west",         0*60, TWTIME+TWDST, TM_ZON},     /* Western Europe */
  207.     {"adt",          1*60, TWTIME+TWDST, TM_ZON},      /* Azores */
  208.     {"fdt",          2*60, TWTIME+TWDST, TM_ZON},     /* Fernando de Noronha */
  209.     {"edt",          3*60, TWTIME+TWDST, TM_ZON},     /* Eastern Brazil */
  210.     {"wdt",          4*60, TWTIME+TWDST, TM_ZON},     /* Western Brazil */
  211.     {"adt",          5*60, TWTIME+TWDST, TM_ZON},     /* Acre Brazil */
  212. #endif
  213.  
  214.     {"lt",           0, TWTIME, TM_LT},       /* local time */
  215.     {"dst",          1*60, TWTIME, TM_DST},      /* daylight savings time */
  216.     {"ddst",         2*60, TWTIME, TM_DST},      /* double dst */
  217.  
  218.     {"am",           T12_AM,    TWTIME, TM_12},
  219.     {"pm",           T12_PM,    TWTIME, TM_12},
  220.     {"noon",         T12_NOON,    TWTIME, TM_12},
  221.     {"midnight",     T12_MIDNIGHT,    TWTIME, TM_12},
  222.  
  223.     {0, 0, 0, 0},             /* Zero entry to terminate searches */
  224. };
  225.  
  226. struct token {
  227.     const char *tcp;/* pointer to string */
  228.     int tcnt;    /* # chars */
  229.     char tbrk;    /* "break" char */
  230.     char tbrkl;    /* last break char */
  231.     char tflg;    /* 0 = alpha, 1 = numeric */
  232.     union {         /* Resulting value; */
  233.         int tnum;/* either a #, or */
  234.         const struct tmwent *ttmw;/* ptr to a tmwent. */
  235.     } tval;
  236. };
  237.  
  238. static const struct tmwent*ptmatchstr P((const char*,int,const struct tmwent*));
  239. static int pt12hack P((struct tm *,int));
  240. static int ptitoken P((struct token *));
  241. static int ptstash P((int *,int));
  242. static int pttoken P((struct token *));
  243.  
  244.     static int
  245. goodzone(t, offset, am)
  246.     register const struct token *t;
  247.     int offset;
  248.     int *am;
  249. {
  250.     register int m;
  251.     if (
  252.         t->tflg  &&
  253.         t->tcnt == 4+offset  &&
  254.         (m = t->tval.tnum) <= 2400  &&
  255.         isdigit(t->tcp[offset]) &&
  256.         (m%=100) < 60
  257.     ) {
  258.         m += t->tval.tnum/100 * 60;
  259.         if (t->tcp[offset-1]=='+')
  260.             m = -m;
  261.         *am = m;
  262.         return 1;
  263.     }
  264.     return 0;
  265. }
  266.  
  267.     int
  268. partime(astr, atm, zone)
  269. const char *astr;
  270. register struct tm *atm;
  271. int *zone;
  272. {
  273.     register int i;
  274.     struct token btoken, atoken;
  275.     int zone_offset; /* minutes west of GMT, plus TZ_OFFSET */
  276.     register const char *cp;
  277.     register char ch;
  278.     int ord, midnoon;
  279.     int *atmfield, dst, m;
  280.     int got1 = 0;
  281.  
  282.     atm->tm_sec = TMNULL;
  283.     atm->tm_min = TMNULL;
  284.     atm->tm_hour = TMNULL;
  285.     atm->tm_mday = TMNULL;
  286.     atm->tm_mon = TMNULL;
  287.     atm->tm_year = TMNULL;
  288.     atm->tm_wday = TMNULL;
  289.     atm->tm_yday = TMNULL;
  290.     midnoon = TMNULL;        /* and our own temp stuff */
  291.     zone_offset = TMNULL;
  292.     dst = TMNULL;
  293.     btoken.tcnt = btoken.tbrk = 0;
  294.     btoken.tcp = astr;
  295.  
  296.     for (;; got1=1) {
  297.     if (!ptitoken(&btoken))                /* Get a token */
  298.       {     if(btoken.tval.tnum) return(0);         /* Read error? */
  299.         if (given(midnoon))            /* EOF, wrap up */
  300.             if (!pt12hack(atm, midnoon))
  301.                 return 0;
  302.         if (!given(atm->tm_min))
  303.             atm->tm_min = 0;
  304.         *zone  =
  305.                 (given(zone_offset) ? zone_offset-TZ_OFFSET : 0)
  306.             -    (given(dst) ? dst : 0);
  307.         return got1;
  308.       }
  309.     if(btoken.tflg == 0)        /* Alpha? */
  310.       {     i = btoken.tval.ttmw->wval;
  311.         switch (btoken.tval.ttmw->wtype) {
  312.           default:
  313.             return 0;
  314.           case TM_MON:
  315.             atmfield = &atm->tm_mon;
  316.             break;
  317.           case TM_WDAY:
  318.             atmfield = &atm->tm_wday;
  319.             break;
  320.           case TM_DST:
  321.             atmfield = &dst;
  322.             break;
  323.           case TM_LT:
  324.             if (ptstash(&dst, 0))
  325.                 return 0;
  326.             i = 48*60; /* local time magic number -- see maketime() */
  327.             /* fall into */
  328.           case TM_ZON:
  329.             i += TZ_OFFSET;
  330.             if (btoken.tval.ttmw->wflgs & TWDST)
  331.                 if (ptstash(&dst, 60))
  332.                     return 0;
  333.             /* Peek ahead for offset immediately afterwards. */
  334.             if (
  335.                 (btoken.tbrk=='-' || btoken.tbrk=='+') &&
  336.                 (atoken=btoken, ++atoken.tcnt, ptitoken(&atoken)) &&
  337.                 goodzone(&atoken, 0, &m)
  338.             ) {
  339.                 i += m;
  340.                 btoken = atoken;
  341.             }
  342.             atmfield = &zone_offset;
  343.             break;
  344.           case TM_12:
  345.             atmfield = &midnoon;
  346.         }
  347.         if (ptstash(atmfield, i))
  348.             return(0);        /* ERR: val already set */
  349.         continue;
  350.       }
  351.  
  352.     /* Token is number.  Lots of hairy heuristics. */
  353.     if (!isdigit(*btoken.tcp)) {
  354.         if (!goodzone(&btoken, 1, &m))
  355.             return 0;
  356.         zone_offset = TZ_OFFSET + m;
  357.         continue;
  358.     }
  359.  
  360.     i = btoken.tval.tnum;   /* Value now known to be valid; get it. */
  361.     if (btoken.tcnt == 3)    /*  3 digits = HMM   */
  362.       {
  363. hhmm4:        if (ptstash(&atm->tm_min, i%100))
  364.             return(0);        /* ERR: min conflict */
  365.         i /= 100;
  366. hh2:            if (ptstash(&atm->tm_hour, i))
  367.             return(0);        /* ERR: hour conflict */
  368.         continue;
  369.       }
  370.  
  371.     if (4 < btoken.tcnt)
  372.         goto year4; /* far in the future */
  373.     if(btoken.tcnt == 4)    /* 4 digits = YEAR or HHMM */
  374.       {    if (given(atm->tm_year)) goto hhmm4;    /* Already got yr? */
  375.         if (given(atm->tm_hour)) goto year4;    /* Already got hr? */
  376.         if(btoken.tbrk == ':')            /* HHMM:SS ? */
  377.             if ( ptstash(&atm->tm_hour, i/100)
  378.               || ptstash(&atm->tm_min, i%100))
  379.                 return(0);        /* ERR: hr/min clash */
  380.             else goto coltm2;        /* Go handle SS */
  381.         if(btoken.tbrk != ',' && btoken.tbrk != '/'
  382.           && (atoken=btoken, ptitoken(&atoken))    /* Peek */
  383.           && ( atoken.tflg
  384.              ? !isdigit(*atoken.tcp)
  385.              : atoken.tval.ttmw->wflgs & TWTIME)) /* HHMM-ZON */
  386.             goto hhmm4;
  387.         goto year4;            /* Give up, assume year. */
  388.       }
  389.  
  390.     /* From this point on, assume tcnt == 1 or 2 */
  391.     /* 2 digits = MM, DD, or HH (MM and SS caught at coltime) */
  392.     if(btoken.tbrk == ':')        /* HH:MM[:SS] */
  393.         goto coltime;        /*  must be part of time. */
  394.     if (31 < i)
  395.         return 0;
  396.  
  397.     /* Check for numerical-format date */
  398.     for (cp = "/-."; ch = *cp++;)
  399.       {    ord = (ch == '.' ? 0 : 1);    /* n/m = D/M or M/D */
  400.         if(btoken.tbrk == ch)            /* "NN-" */
  401.           {    if(btoken.tbrkl != ch)
  402.               {
  403.                 atoken = btoken;
  404.                 atoken.tcnt++;
  405.                 if (ptitoken(&atoken)
  406.                   && atoken.tflg == 0
  407.                   && atoken.tval.ttmw->wtype == TM_MON)
  408.                     goto dd2;
  409.                 if(ord)goto mm2; else goto dd2; /* "NN-" */
  410.               }                /* "-NN-" */
  411.             if (!given(atm->tm_mday)
  412.               && given(atm->tm_year))    /* If "YYYY-NN-" */
  413.                 goto mm2;        /* then always MM */
  414.             if(ord)goto dd2; else goto mm2;
  415.           }
  416.         if(btoken.tbrkl == ch            /* "-NN" */
  417.           && given(ord ? atm->tm_mon : atm->tm_mday))
  418.             if (!given(ord ? atm->tm_mday : atm->tm_mon)) /* MM/DD */
  419.                 if(ord)goto dd2; else goto mm2;
  420.       }
  421.  
  422.     /* Now reduced to choice between HH and DD */
  423.     if (given(atm->tm_hour)) goto dd2;    /* Have hour? Assume day. */
  424.     if (given(atm->tm_mday)) goto hh2;    /* Have day? Assume hour. */
  425.     if (given(atm->tm_mon)) goto dd2;    /* Have month? Assume day. */
  426.     if(i > 24) goto dd2;            /* Impossible HH means DD */
  427.     atoken = btoken;
  428.     if (!ptitoken(&atoken))            /* Read ahead! */
  429.         if(atoken.tval.tnum) return(0); /* ERR: bad token */
  430.         else goto dd2;            /* EOF, assume day. */
  431.     if ( atoken.tflg
  432.        ? !isdigit(*atoken.tcp)
  433.        : atoken.tval.ttmw->wflgs & TWTIME)
  434.         /* If next token is a time spec, assume hour */
  435.         goto hh2;        /* e.g. "3 PM", "11-EDT"  */
  436.  
  437. dd2:    if (ptstash(&atm->tm_mday, i))    /* Store day (1 based) */
  438.         return(0);
  439.     continue;
  440.  
  441. mm2:    if (ptstash(&atm->tm_mon, i-1))    /* Store month (make zero based) */
  442.         return(0);
  443.     continue;
  444.  
  445. year4:    if ((i-=1900) < 0  ||  ptstash(&atm->tm_year, i)) /* Store year-1900 */
  446.         return(0);        /* ERR: year conflict */
  447.     continue;
  448.  
  449.     /* Hack HH:MM[[:]SS] */
  450. coltime:
  451.     if (ptstash(&atm->tm_hour, i)) return 0;
  452.     if (!ptitoken(&btoken))
  453.         return(!btoken.tval.tnum);
  454.     if(!btoken.tflg) return(0);    /* ERR: HH:<alpha> */
  455.     if(btoken.tcnt == 4)        /* MMSS */
  456.         if (ptstash(&atm->tm_min, btoken.tval.tnum/100)
  457.           || ptstash(&atm->tm_sec, btoken.tval.tnum%100))
  458.             return(0);
  459.         else continue;
  460.     if(btoken.tcnt != 2
  461.       || ptstash(&atm->tm_min, btoken.tval.tnum))
  462.         return(0);        /* ERR: MM bad */
  463.     if (btoken.tbrk != ':') continue;    /* Seconds follow? */
  464. coltm2:    if (!ptitoken(&btoken))
  465.         return(!btoken.tval.tnum);
  466.     if(!btoken.tflg || btoken.tcnt != 2    /* Verify SS */
  467.       || ptstash(&atm->tm_sec, btoken.tval.tnum))
  468.         return(0);        /* ERR: SS bad */
  469.     }
  470. }
  471.  
  472. /* Store date/time value, return 0 if successful.
  473.  * Fail if entry is already set.
  474.  */
  475.     static int
  476. ptstash(adr,val)
  477. int *adr;
  478. int val;
  479. {    register int *a;
  480.     if (given(*(a=adr)))
  481.         return 1;
  482.     *a = val;
  483.     return(0);
  484. }
  485.  
  486. /* This subroutine is invoked for AM, PM, NOON and MIDNIGHT when wrapping up
  487.  * just prior to returning from partime.
  488.  */
  489.     static int
  490. pt12hack(tm, aval)
  491. register struct tm *tm;
  492. register int aval;
  493. {    register int h = tm->tm_hour;
  494.     switch (aval) {
  495.       case T12_AM:
  496.       case T12_PM:
  497.         if (h > 12)
  498.             return 0;
  499.         if (h == 12)
  500.             tm->tm_hour = 0;
  501.         if (aval == T12_PM)
  502.             tm->tm_hour += 12;
  503.         break;
  504.       default:
  505.         if (0 < tm->tm_min  ||  0 < tm->tm_sec)
  506.             return 0;
  507.         if (!given(h) || h==12)
  508.             tm->tm_hour = aval;
  509.         else if (aval==T12_MIDNIGHT  &&  (h==0 || h==24))
  510.             return 0;
  511.     }
  512.     return 1;
  513. }
  514.  
  515. /* Get a token and identify it to some degree.
  516.  * Returns 0 on failure; token.tval will be 0 for normal EOF, otherwise
  517.  * hit error of some sort
  518.  */
  519.  
  520.     static int
  521. ptitoken(tkp)
  522. register struct token *tkp;
  523. {
  524.     register const char *cp;
  525.     register int i, j, k;
  526.  
  527.     if (!pttoken(tkp))
  528. #ifdef DEBUG
  529.         {
  530.         VOID printf("EOF\n");
  531.         return(0);
  532.         }
  533. #else
  534.         return(0);
  535. #endif
  536.     cp = tkp->tcp;
  537.  
  538. #ifdef DEBUG
  539.     VOID printf("Token: \"%.*s\" ", tkp->tcnt, cp);
  540. #endif
  541.  
  542.     if (tkp->tflg) {
  543.         i = tkp->tcnt;
  544.         if (*cp == '+' || *cp == '-') {
  545.             cp++;
  546.             i--;
  547.         }
  548.         while (0 <= --i) {
  549.             j = tkp->tval.tnum*10;
  550.             k = j + (*cp++ - '0');
  551.             if (j/10 != tkp->tval.tnum  ||  k < j) {
  552.                 /* arithmetic overflow */
  553.                 tkp->tval.tnum = 1;
  554.                 return 0;
  555.             }
  556.             tkp->tval.tnum = k;
  557.         }
  558.     } else if (!(tkp->tval.ttmw  =  ptmatchstr(cp, tkp->tcnt, tmwords)))
  559.       {
  560. #ifdef DEBUG
  561.         VOID printf("Not found!\n");
  562. #endif
  563.         tkp->tval.tnum = 1;
  564.         return 0;
  565.       }
  566.  
  567. #ifdef DEBUG
  568.     if(tkp->tflg)
  569.         VOID printf("Val: %d.\n",tkp->tval.tnum);
  570.     else VOID printf("Found: \"%s\", val: %d, type %d\n",
  571.         tkp->tval.ttmw->went,tkp->tval.ttmw->wval,tkp->tval.ttmw->wtype);
  572. #endif
  573.  
  574.     return(1);
  575. }
  576.  
  577. /* Read token from input string into token structure */
  578.     static int
  579. pttoken(tkp)
  580. register struct token *tkp;
  581. {
  582.     register const char *cp;
  583.     register int c;
  584.     const char *astr;
  585.  
  586.     tkp->tcp = astr = cp = tkp->tcp + tkp->tcnt;
  587.     tkp->tbrkl = tkp->tbrk;        /* Set "last break" */
  588.     tkp->tcnt = tkp->tbrk = tkp->tflg = 0;
  589.     tkp->tval.tnum = 0;
  590.  
  591.     while(c = *cp++)
  592.       {    switch(c)
  593.           {    case ' ': case '\t':    /* Flush all whitespace */
  594.             case '\r': case '\n':
  595.             case '\v': case '\f':
  596.                 if (!tkp->tcnt) {    /* If no token yet */
  597.                     tkp->tcp = cp;    /* ignore the brk */
  598.                     continue;    /* and go on. */
  599.                 }
  600.                 /* fall into */
  601.             case '(': case ')':    /* Perhaps any non-alphanum */
  602.             case '-': case ',':    /* shd qualify as break? */
  603.             case '+':
  604.             case '/': case ':': case '.':    /* Break chars */
  605.                 if(tkp->tcnt == 0)    /* If no token yet */
  606.                   {    tkp->tcp = cp;    /* ignore the brk */
  607.                     tkp->tbrkl = c;
  608.                       continue;    /* and go on. */
  609.                   }
  610.                 tkp->tbrk = c;
  611.                 return(tkp->tcnt);
  612.           }
  613.         if (!tkp->tcnt++) {        /* If first char of token, */
  614.             if (isdigit(c)) {
  615.                 tkp->tflg = 1;
  616.                 if (astr<cp-2 && (cp[-2]=='-'||cp[-2]=='+')) {
  617.                     /* timezone is break+sign+digit */
  618.                     tkp->tcp--;
  619.                     tkp->tcnt++;
  620.                 }
  621.             }
  622.         } else if ((isdigit(c)!=0) != tkp->tflg) { /* else check type */
  623.             tkp->tbrk = c;
  624.             return --tkp->tcnt;    /* Wrong type, back up */
  625.         }
  626.       }
  627.     return(tkp->tcnt);        /* When hit EOF */
  628. }
  629.  
  630.  
  631.     static const struct tmwent *
  632. ptmatchstr(astr,cnt,astruc)
  633.     const char *astr;
  634.     int cnt;
  635.     const struct tmwent *astruc;
  636. {
  637.     register const char *cp, *mp;
  638.     register int c;
  639.     const struct tmwent *lastptr;
  640.     int i;
  641.  
  642.     lastptr = 0;
  643.     for(;mp = astruc->went; astruc += 1)
  644.       {    cp = astr;
  645.         for(i = cnt; i > 0; i--)
  646.           {
  647.             switch (*cp++ - (c = *mp++))
  648.               {    case 0: continue;    /* Exact match */
  649.                 case 'A'-'a':
  650.                     /* `if (ctab[c]==Letter)' in RCS */
  651.                     if (islower(c))
  652.                     continue;
  653.               }
  654.             break;
  655.           }
  656.         if(i==0)
  657.             if (!*mp) return astruc;    /* Exact match */
  658.             else if(lastptr) return(0);    /* Ambiguous */
  659.             else lastptr = astruc;        /* 1st ambig */
  660.       }
  661.     return lastptr;
  662. }
  663.