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