home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume24 / cvs1.2upgrade / part02 / Patch02.02
Encoding:
Text File  |  1991-03-05  |  47.8 KB  |  1,625 lines

  1. !  * Initial revision
  2. !  * 
  3. !  */
  4.   
  5. ! /* minimal changes needed to get this file to work for CVS rather than RCS */
  6. ! /* #include "rcsbase.h" */
  7. ! #include <ctype.h>
  8. ! #include <time.h>
  9. ! #include "cvs.h"
  10. ! #define libId(x,y) static char x[] = y;
  11. ! #define P(x) ()
  12. ! #if !__STDC__
  13. ! #    define const
  14.   #endif
  15. + /* see also `RCS' in comment below */
  16. + libId(partId, "$Id: partime.c,v 1.1.1.1 91/01/18 12:18:12 berliner Exp $")
  17.   
  18. + #define given(v) (0 <= (v))
  19. + #define TMNULL (-1) /* Items not given are given this value */
  20. + #define TZ_OFFSET (24*60) /* TMNULL  <  zone_offset - TZ_OFFSET */
  21.   struct tmwent {
  22. !     const char *went;
  23. !     short wval;
  24.       char wflgs;
  25.       char wtype;
  26.   };
  27.       /* wflgs */
  28.   #define TWTIME 02    /* Word is a time value (absence implies date) */
  29.   #define TWDST  04    /* Word is a DST-type timezone */
  30. !     /* wtype */
  31. ! #define TM_MON    1    /* month name */
  32. ! #define TM_WDAY    2    /* weekday name */
  33. ! #define TM_ZON    3    /* time zone name */
  34. ! #define TM_LT    4    /* local time */
  35. ! #define TM_DST    5    /* daylight savings time */
  36. ! #define TM_12    6    /* AM, PM, NOON, or MIDNIGHT */
  37. !     /* wval (for wtype==TM_12) */
  38. ! #define T12_AM 1
  39. ! #define T12_PM 2
  40. ! #define T12_NOON 12
  41. ! #define T12_MIDNIGHT 0
  42.   
  43. ! static const struct tmwent tmwords [] = {
  44.       {"january",      0, 0, TM_MON},
  45.       {"february",     1, 0, TM_MON},
  46.       {"march",        2, 0, TM_MON},
  47. ***************
  48. *** 75,119 ****
  49.       {"saturday",     6, 0, TM_WDAY},
  50.   
  51.       {"gmt",          0*60, TWTIME, TM_ZON},   /* Greenwich */
  52. !     {"gst",          0*60, TWTIME, TM_ZON},
  53. !     {"gdt",          0*60, TWTIME+TWDST, TM_ZON},     /* ?? */
  54.   
  55.       {"ast",          4*60, TWTIME, TM_ZON},   /* Atlantic */
  56.       {"est",          5*60, TWTIME, TM_ZON},   /* Eastern */
  57.       {"cst",          6*60, TWTIME, TM_ZON},   /* Central */
  58.       {"mst",          7*60, TWTIME, TM_ZON},   /* Mountain */
  59.       {"pst",          8*60, TWTIME, TM_ZON},   /* Pacific */
  60. !     {"yst",          9*60, TWTIME, TM_ZON},   /* Yukon */
  61.       {"hst",          10*60, TWTIME, TM_ZON},  /* Hawaii */
  62. !     {"bst",          11*60, TWTIME, TM_ZON},  /* Bering */
  63.   
  64.       {"adt",          4*60, TWTIME+TWDST, TM_ZON},     /* Atlantic */
  65.       {"edt",          5*60, TWTIME+TWDST, TM_ZON},     /* Eastern */
  66.       {"cdt",          6*60, TWTIME+TWDST, TM_ZON},     /* Central */
  67.       {"mdt",          7*60, TWTIME+TWDST, TM_ZON},     /* Mountain */
  68.       {"pdt",          8*60, TWTIME+TWDST, TM_ZON},     /* Pacific */
  69. !     {"ydt",          9*60, TWTIME+TWDST, TM_ZON},     /* Yukon */
  70. !     {"hdt",          10*60, TWTIME+TWDST, TM_ZON},    /* Hawaii */
  71. !     {"bdt",          11*60, TWTIME+TWDST, TM_ZON},    /* Bering */
  72. !     {"daylight",     1, TWTIME+TWDST, TM_ZON},        /* Local Daylight */
  73. !     {"standard",     1, TWTIME, TM_ZON},      /* Local Standard */
  74. !     {"std",          1, TWTIME, TM_ZON},      /*   "       "    */
  75. !     {"am",           1, TWTIME, TM_AMPM},
  76. !     {"pm",           2, TWTIME, TM_AMPM},
  77. !     {"noon",         12,TWTIME+TW1200, 0},    /* Special frobs */
  78. !     {"midnight",     0, TWTIME+TW1200, 0},
  79. !     {"at",           (long)ptnoise, TWSPEC, 0},    /* Noise word */
  80.   
  81.       {0, 0, 0, 0},             /* Zero entry to terminate searches */
  82.   };
  83.   
  84. - #define TMWILD (-2)    /* Value meaning item specified as wild-card */
  85. -             /* (May use someday...) */
  86.   struct token {
  87. !     char *tcp;    /* pointer to string */
  88.       int tcnt;    /* # chars */
  89.       char tbrk;    /* "break" char */
  90.       char tbrkl;    /* last break char */
  91. --- 123,230 ----
  92.       {"saturday",     6, 0, TM_WDAY},
  93.   
  94.       {"gmt",          0*60, TWTIME, TM_ZON},   /* Greenwich */
  95. !     {"utc",          0*60, TWTIME, TM_ZON},
  96. !     {"ut",           0*60, TWTIME, TM_ZON},
  97.   
  98. +     {"nzst",        -12*60, TWTIME, TM_ZON},  /* New Zealand */
  99. +     {"jst",         -9*60, TWTIME, TM_ZON},   /* Japan */
  100. +     {"kst",         -9*60, TWTIME, TM_ZON},   /* Korea */
  101. +     {"ist",         -5*60-30, TWTIME, TM_ZON},/* India */
  102. +     {"eet",         -2*60, TWTIME, TM_ZON},   /* Eastern Europe */
  103. +     {"cet",         -1*60, TWTIME, TM_ZON},   /* Central Europe */
  104. +     {"met",         -1*60, TWTIME, TM_ZON},   /* Middle Europe */
  105. +     {"wet",          0*60, TWTIME, TM_ZON},   /* Western Europe */
  106. +     {"nst",          3*60+30, TWTIME, TM_ZON},/* Newfoundland */
  107.       {"ast",          4*60, TWTIME, TM_ZON},   /* Atlantic */
  108.       {"est",          5*60, TWTIME, TM_ZON},   /* Eastern */
  109.       {"cst",          6*60, TWTIME, TM_ZON},   /* Central */
  110.       {"mst",          7*60, TWTIME, TM_ZON},   /* Mountain */
  111.       {"pst",          8*60, TWTIME, TM_ZON},   /* Pacific */
  112. !     {"akst",         9*60, TWTIME, TM_ZON},   /* Alaska */
  113. !     {"hast",         10*60, TWTIME, TM_ZON},  /* Hawaii-Aleutian */
  114.       {"hst",          10*60, TWTIME, TM_ZON},  /* Hawaii */
  115. !     {"sst",          11*60, TWTIME, TM_ZON},  /* Samoa */
  116.   
  117. +     {"nzdt",        -12*60, TWTIME+TWDST, TM_ZON},    /* New Zealand */
  118. +     {"kdt",         -9*60, TWTIME+TWDST, TM_ZON},     /* Korea */
  119. +     {"bst",          0*60, TWTIME+TWDST, TM_ZON},     /* Britain */
  120. +     {"ndt",          2*60+30, TWTIME+TWDST, TM_ZON}, /*Newfoundland (DDST)*/
  121.       {"adt",          4*60, TWTIME+TWDST, TM_ZON},     /* Atlantic */
  122.       {"edt",          5*60, TWTIME+TWDST, TM_ZON},     /* Eastern */
  123.       {"cdt",          6*60, TWTIME+TWDST, TM_ZON},     /* Central */
  124.       {"mdt",          7*60, TWTIME+TWDST, TM_ZON},     /* Mountain */
  125.       {"pdt",          8*60, TWTIME+TWDST, TM_ZON},     /* Pacific */
  126. !     {"akdt",         9*60, TWTIME+TWDST, TM_ZON},     /* Alaska */
  127. !     {"hadt",         10*60, TWTIME+TWDST, TM_ZON},    /* Hawaii-Aleutian */
  128. ! #if 0
  129. !     /*
  130. !      * The following names are duplicates or are not well attested.
  131. !      * A standard is needed.
  132. !      */
  133. !     {"?st",         -13*60, TWTIME, TM_ZON},  /* Uelen */
  134. !     {"?st",         -11*60, TWTIME, TM_ZON},  /* Magadan */
  135. !     {"east",        -10*60, TWTIME, TM_ZON},  /* Eastern Australia */
  136. !     {"cast",        -9*60-30, TWTIME, TM_ZON},/* Central Australia */
  137. !     {"cst",         -8*60, TWTIME, TM_ZON},   /* China */
  138. !     {"hkt",         -8*60, TWTIME, TM_ZON},   /* Hong Kong */
  139. !     {"sst",         -8*60, TWTIME, TM_ZON},   /* Singapore */
  140. !     {"wast",        -8*60, TWTIME, TM_ZON},   /* Western Australia */
  141. !     {"?st",         -7*60, TWTIME, TM_ZON},   /* Novosibirsk */
  142. !     {"jt",          -7*60-30, TWTIME, TM_ZON},/* Java */
  143. !     {"nst",         -6*60-30, TWTIME, TM_ZON},/* North Sumatra */
  144. !     {"?st",         -6*60, TWTIME, TM_ZON},   /* Tashkent */
  145. !     {"?st",         -5*60, TWTIME, TM_ZON},      /* Sverdlovsk */
  146. !     {"?",           -4*60-30, TWTIME, TM_ZON},/* Afghanistan */
  147. !     {"?st",         -4*60, TWTIME, TM_ZON},      /* Rostov */
  148. !     {"it",          -3*60-30, TWTIME, TM_ZON},/* Iran */
  149. !     {"?st",         -3*60, TWTIME, TM_ZON},   /* Moscow */
  150. !     {"ist",         -2*60, TWTIME, TM_ZON},   /* Israel */
  151. !     {"ast",          1*60, TWTIME, TM_ZON},   /* Azores */
  152. !     {"fst",          2*60, TWTIME, TM_ZON},   /* Fernando de Noronha */
  153. !     {"bst",          3*60, TWTIME, TM_ZON},   /* Brazil */
  154. !     {"wst",          4*60, TWTIME, TM_ZON},   /* Western Brazil */
  155. !     {"ast",          5*60, TWTIME, TM_ZON},   /* Acre Brazil */
  156. !     {"?",            9*60+30, TWTIME, TM_ZON},/* Marquesas */
  157. !     {"?st",          12*60, TWTIME, TM_ZON},  /* Kwajalein */
  158. !     {"?dt",         -13*60, TWTIME+TWDST, TM_ZON},      /* Uelen */
  159. !     {"?dt",         -11*60, TWTIME+TWDST, TM_ZON},      /* Magadan */
  160. !     {"eadt",        -10*60, TWTIME+TWDST, TM_ZON},    /* Eastern Australia */
  161. !     {"cadt",        -9*60-30, TWTIME+TWDST, TM_ZON},  /* Central Australia */
  162. !     {"cdt",         -8*60, TWTIME+TWDST, TM_ZON},     /* China */
  163. !     {"wadt",        -8*60, TWTIME+TWDST, TM_ZON},     /* Western Australia */
  164. !     {"?dt",         -7*60, TWTIME+TWDST, TM_ZON},      /* Novosibirsk */
  165. !     {"?dt",         -6*60, TWTIME+TWDST, TM_ZON},      /* Tashkent */
  166. !     {"?dt",         -5*60, TWTIME+TWDST, TM_ZON},      /* Sverdlovsk */
  167. !     {"?dt",         -4*60, TWTIME+TWDST, TM_ZON},      /* Rostov */
  168. !     {"?dt",         -3*60, TWTIME+TWDST, TM_ZON},     /* Moscow */
  169. !     {"idt",         -2*60, TWTIME+TWDST, TM_ZON},     /* Israel */
  170. !     {"eest",        -2*60, TWTIME+TWDST, TM_ZON},     /* Eastern Europe */
  171. !     {"cest",        -1*60, TWTIME+TWDST, TM_ZON},     /* Central Europe */
  172. !     {"mest",        -1*60, TWTIME+TWDST, TM_ZON},     /* Middle Europe */
  173. !     {"west",         0*60, TWTIME+TWDST, TM_ZON},     /* Western Europe */
  174. !     {"adt",          1*60, TWTIME+TWDST, TM_ZON},      /* Azores */
  175. !     {"fdt",          2*60, TWTIME+TWDST, TM_ZON},     /* Fernando de Noronha */
  176. !     {"edt",          3*60, TWTIME+TWDST, TM_ZON},     /* Eastern Brazil */
  177. !     {"wdt",          4*60, TWTIME+TWDST, TM_ZON},     /* Western Brazil */
  178. !     {"adt",          5*60, TWTIME+TWDST, TM_ZON},     /* Acre Brazil */
  179. ! #endif
  180. !     {"lt",           0, TWTIME, TM_LT},       /* local time */
  181. !     {"dst",          1*60, TWTIME, TM_DST},      /* daylight savings time */
  182. !     {"ddst",         2*60, TWTIME, TM_DST},      /* double dst */
  183. !     {"am",           T12_AM,    TWTIME, TM_12},
  184. !     {"pm",           T12_PM,    TWTIME, TM_12},
  185. !     {"noon",         T12_NOON,    TWTIME, TM_12},
  186. !     {"midnight",     T12_MIDNIGHT,    TWTIME, TM_12},
  187.   
  188.       {0, 0, 0, 0},             /* Zero entry to terminate searches */
  189.   };
  190.   
  191.   struct token {
  192. !     const char *tcp;/* pointer to string */
  193.       int tcnt;    /* # chars */
  194.       char tbrk;    /* "break" char */
  195.       char tbrkl;    /* last break char */
  196. ***************
  197. *** 120,227 ****
  198.       char tflg;    /* 0 = alpha, 1 = numeric */
  199.       union {         /* Resulting value; */
  200.           int tnum;/* either a #, or */
  201. !         struct tmwent *ttmw;/* ptr to a tmwent. */
  202.       } tval;
  203.   };
  204.   
  205. ! partime(astr, atm)
  206. ! char *astr;
  207. ! struct tm *atm;
  208. ! {    register int *tp;
  209. !     register struct tmwent *twp;
  210. !     register int i;
  211. !     struct token btoken, atoken;
  212. !     char *cp, ch;
  213. !     int ord, midnoon;
  214. !     int (*aproc)();
  215. !     tp = (int *)atm;
  216. !     zaptime(tp);             /* Initialize the TM structure */
  217. !     midnoon = TMNULL;        /* and our own temp stuff */
  218. !     btoken.tcnt = btoken.tbrkl = 0;
  219. !     btoken.tcp = astr;
  220.   
  221. ! domore:
  222. !     if(!ptitoken(btoken.tcp+btoken.tcnt,&btoken))    /* Get a token */
  223.         {     if(btoken.tval.tnum) return(0);         /* Read error? */
  224. !         if(midnoon != TMNULL)            /* EOF, wrap up */
  225. !             return(pt12hack(tp, midnoon));
  226. !         return(1);                /* Win return! */
  227.         }
  228.       if(btoken.tflg == 0)        /* Alpha? */
  229. !       {     twp = btoken.tval.ttmw;         /* Yes, get ptr to entry */
  230. !         if(twp->wflgs&TWSPEC)        /* Special alpha crock */
  231. !           {     aproc = (int (*) ()) (twp->wval);
  232. !             if(!(*aproc)(tp, twp, &btoken))
  233. !                 return(0);    /* ERR: special word err */
  234. !             goto domore;
  235. !           }
  236. !         if(twp->wflgs&TW1200)
  237. !             if(ptstash(&midnoon,(int)twp->wval))
  238. !                 return(0);    /* ERR: noon/midnite clash */
  239. !             else goto domore;
  240. !         if(ptstash(&tp[twp->wtype],(int)twp->wval))
  241.               return(0);        /* ERR: val already set */
  242. !         if(twp->wtype == TM_ZON)    /* If was zone, hack DST */
  243. !             if(ptstash(&tp[TM_ISDST],(twp->wflgs&TWDST)))
  244. !                 return(0);    /* ERR: DST conflict */
  245. !         goto domore;
  246.         }
  247.   
  248.       /* Token is number.  Lots of hairy heuristics. */
  249. !     if(btoken.tcnt >= 7)    /* More than 6 digits in string? */
  250. !         return(0);    /* ERR: number too big */
  251. !     if(btoken.tcnt == 6)    /* 6 digits = HHMMSS.  Needs special crock */
  252. !       {            /* since 6 digits are too big for integer! */
  253. !         i = (btoken.tcp[0]-'0')*10    /* Gobble 1st 2 digits */
  254. !            + btoken.tcp[1]-'0';
  255. !         btoken.tcnt = 2;        /* re-read last 4 chars */
  256. !         goto coltime;
  257. !       }
  258.   
  259.       i = btoken.tval.tnum;   /* Value now known to be valid; get it. */
  260. !     if( btoken.tcnt == 5    /*  5 digits = HMMSS */
  261. !      || btoken.tcnt == 3)    /*  3 digits = HMM   */
  262. !       {    if(btoken.tcnt != 3)
  263. !             if(ptstash(&tp[TM_SEC], i%100))
  264. !                 return(0);    /* ERR: sec conflict */
  265. !             else i /= 100;
  266. ! hhmm4:        if(ptstash(&tp[TM_MIN], i%100))
  267.               return(0);        /* ERR: min conflict */
  268.           i /= 100;
  269. ! hh2:            if(ptstash(&tp[TM_HOUR], i))
  270.               return(0);        /* ERR: hour conflict */
  271. !         goto domore;
  272.         }
  273.   
  274.       if(btoken.tcnt == 4)    /* 4 digits = YEAR or HHMM */
  275. !       {    if(tp[TM_YEAR] != TMNULL) goto hhmm4;    /* Already got yr? */
  276. !         if(tp[TM_HOUR] != TMNULL) goto year4;    /* Already got hr? */
  277. !         if((i%100) > 59) goto year4;        /* MM >= 60? */
  278.           if(btoken.tbrk == ':')            /* HHMM:SS ? */
  279. !             if( ptstash(&tp[TM_HOUR],i/100)
  280. !              || ptstash(&tp[TM_MIN], i%100))
  281.                   return(0);        /* ERR: hr/min clash */
  282.               else goto coltm2;        /* Go handle SS */
  283.           if(btoken.tbrk != ',' && btoken.tbrk != '/'
  284. !           && ptitoken(btoken.tcp+btoken.tcnt,&atoken)    /* Peek */
  285. !           && atoken.tflg == 0            /* alpha */
  286. !           && (atoken.tval.ttmw->wflgs&TWTIME))  /* HHMM-ZON */
  287.               goto hhmm4;
  288. !         if(btoken.tbrkl == '-'        /* DD-Mon-YYYY */
  289. !           || btoken.tbrkl == ','    /* Mon DD, YYYY */
  290. !           || btoken.tbrkl == '/'    /* MM/DD/YYYY */
  291. !           || btoken.tbrkl == '.'    /* DD.MM.YYYY */
  292. !           || btoken.tbrk == '-'        /* YYYY-MM-DD */
  293. !             ) goto year4;
  294. !         goto hhmm4;            /* Give up, assume HHMM. */
  295.         }
  296.   
  297.       /* From this point on, assume tcnt == 1 or 2 */
  298. !     /* 2 digits = YY, MM, DD, or HH (MM and SS caught at coltime) */
  299.       if(btoken.tbrk == ':')        /* HH:MM[:SS] */
  300.           goto coltime;        /*  must be part of time. */
  301. !     if(i > 31) goto yy2;        /* If >= 32, only YY poss. */
  302.   
  303.       /* Check for numerical-format date */
  304.       for (cp = "/-."; ch = *cp++;)
  305. --- 231,398 ----
  306.       char tflg;    /* 0 = alpha, 1 = numeric */
  307.       union {         /* Resulting value; */
  308.           int tnum;/* either a #, or */
  309. !         const struct tmwent *ttmw;/* ptr to a tmwent. */
  310.       } tval;
  311.   };
  312.   
  313. ! static const struct tmwent*ptmatchstr P((const char*,int,const struct tmwent*));
  314. ! static int pt12hack P((struct tm *,int));
  315. ! static int ptitoken P((struct token *));
  316. ! static int ptstash P((int *,int));
  317. ! static int pttoken P((struct token *));
  318. !     static int
  319. ! goodzone(t, offset, am)
  320. !     register const struct token *t;
  321. !     int offset;
  322. !     int *am;
  323. ! {
  324. !     register int m;
  325. !     if (
  326. !         t->tflg  &&
  327. !         t->tcnt == 4+offset  &&
  328. !         (m = t->tval.tnum) <= 2400  &&
  329. !         isdigit(t->tcp[offset]) &&
  330. !         (m%=100) < 60
  331. !     ) {
  332. !         m += t->tval.tnum/100 * 60;
  333. !         if (t->tcp[offset-1]=='+')
  334. !             m = -m;
  335. !         *am = m;
  336. !         return 1;
  337. !     }
  338. !     return 0;
  339. ! }
  340. !     int
  341. ! partime(astr, atm, zone)
  342. ! const char *astr;
  343. ! register struct tm *atm;
  344. ! int *zone;
  345. ! {
  346. !     register int i;
  347. !     struct token btoken, atoken;
  348. !     int zone_offset; /* minutes west of GMT, plus TZ_OFFSET */
  349. !     register const char *cp;
  350. !     register char ch;
  351. !     int ord, midnoon;
  352. !     int *atmfield, dst, m;
  353. !     int got1 = 0;
  354. !     atm->tm_sec = TMNULL;
  355. !     atm->tm_min = TMNULL;
  356. !     atm->tm_hour = TMNULL;
  357. !     atm->tm_mday = TMNULL;
  358. !     atm->tm_mon = TMNULL;
  359. !     atm->tm_year = TMNULL;
  360. !     atm->tm_wday = TMNULL;
  361. !     atm->tm_yday = TMNULL;
  362. !     midnoon = TMNULL;        /* and our own temp stuff */
  363. !     zone_offset = TMNULL;
  364. !     dst = TMNULL;
  365. !     btoken.tcnt = btoken.tbrk = 0;
  366. !     btoken.tcp = astr;
  367.   
  368. !     for (;; got1=1) {
  369. !     if (!ptitoken(&btoken))                /* Get a token */
  370.         {     if(btoken.tval.tnum) return(0);         /* Read error? */
  371. !         if (given(midnoon))            /* EOF, wrap up */
  372. !             if (!pt12hack(atm, midnoon))
  373. !                 return 0;
  374. !         if (!given(atm->tm_min))
  375. !             atm->tm_min = 0;
  376. !         *zone  =
  377. !                 (given(zone_offset) ? zone_offset-TZ_OFFSET : 0)
  378. !             -    (given(dst) ? dst : 0);
  379. !         return got1;
  380.         }
  381.       if(btoken.tflg == 0)        /* Alpha? */
  382. !       {     i = btoken.tval.ttmw->wval;
  383. !         switch (btoken.tval.ttmw->wtype) {
  384. !           default:
  385. !             return 0;
  386. !           case TM_MON:
  387. !             atmfield = &atm->tm_mon;
  388. !             break;
  389. !           case TM_WDAY:
  390. !             atmfield = &atm->tm_wday;
  391. !             break;
  392. !           case TM_DST:
  393. !             atmfield = &dst;
  394. !             break;
  395. !           case TM_LT:
  396. !             if (ptstash(&dst, 0))
  397. !                 return 0;
  398. !             i = 48*60; /* local time magic number -- see maketime() */
  399. !             /* fall into */
  400. !           case TM_ZON:
  401. !             i += TZ_OFFSET;
  402. !             if (btoken.tval.ttmw->wflgs & TWDST)
  403. !                 if (ptstash(&dst, 60))
  404. !                     return 0;
  405. !             /* Peek ahead for offset immediately afterwards. */
  406. !             if (
  407. !                 (btoken.tbrk=='-' || btoken.tbrk=='+') &&
  408. !                 (atoken=btoken, ++atoken.tcnt, ptitoken(&atoken)) &&
  409. !                 goodzone(&atoken, 0, &m)
  410. !             ) {
  411. !                 i += m;
  412. !                 btoken = atoken;
  413. !             }
  414. !             atmfield = &zone_offset;
  415. !             break;
  416. !           case TM_12:
  417. !             atmfield = &midnoon;
  418. !         }
  419. !         if (ptstash(atmfield, i))
  420.               return(0);        /* ERR: val already set */
  421. !         continue;
  422.         }
  423.   
  424.       /* Token is number.  Lots of hairy heuristics. */
  425. !     if (!isdigit(*btoken.tcp)) {
  426. !         if (!goodzone(&btoken, 1, &m))
  427. !             return 0;
  428. !         zone_offset = TZ_OFFSET + m;
  429. !         continue;
  430. !     }
  431.   
  432.       i = btoken.tval.tnum;   /* Value now known to be valid; get it. */
  433. !     if (btoken.tcnt == 3)    /*  3 digits = HMM   */
  434. !       {
  435. ! hhmm4:        if (ptstash(&atm->tm_min, i%100))
  436.               return(0);        /* ERR: min conflict */
  437.           i /= 100;
  438. ! hh2:            if (ptstash(&atm->tm_hour, i))
  439.               return(0);        /* ERR: hour conflict */
  440. !         continue;
  441.         }
  442.   
  443. +     if (4 < btoken.tcnt)
  444. +         goto year4; /* far in the future */
  445.       if(btoken.tcnt == 4)    /* 4 digits = YEAR or HHMM */
  446. !       {    if (given(atm->tm_year)) goto hhmm4;    /* Already got yr? */
  447. !         if (given(atm->tm_hour)) goto year4;    /* Already got hr? */
  448.           if(btoken.tbrk == ':')            /* HHMM:SS ? */
  449. !             if ( ptstash(&atm->tm_hour, i/100)
  450. !               || ptstash(&atm->tm_min, i%100))
  451.                   return(0);        /* ERR: hr/min clash */
  452.               else goto coltm2;        /* Go handle SS */
  453.           if(btoken.tbrk != ',' && btoken.tbrk != '/'
  454. !           && (atoken=btoken, ptitoken(&atoken))    /* Peek */
  455. !           && ( atoken.tflg
  456. !              ? !isdigit(*atoken.tcp)
  457. !              : atoken.tval.ttmw->wflgs & TWTIME)) /* HHMM-ZON */
  458.               goto hhmm4;
  459. !         goto year4;            /* Give up, assume year. */
  460.         }
  461.   
  462.       /* From this point on, assume tcnt == 1 or 2 */
  463. !     /* 2 digits = MM, DD, or HH (MM and SS caught at coltime) */
  464.       if(btoken.tbrk == ':')        /* HH:MM[:SS] */
  465.           goto coltime;        /*  must be part of time. */
  466. !     if (31 < i)
  467. !         return 0;
  468.   
  469.       /* Check for numerical-format date */
  470.       for (cp = "/-."; ch = *cp++;)
  471. ***************
  472. *** 228,416 ****
  473.         {    ord = (ch == '.' ? 0 : 1);    /* n/m = D/M or M/D */
  474.           if(btoken.tbrk == ch)            /* "NN-" */
  475.             {    if(btoken.tbrkl != ch)
  476. !               {    if(ptitoken(btoken.tcp+btoken.tcnt,&atoken)
  477.                     && atoken.tflg == 0
  478.                     && atoken.tval.ttmw->wtype == TM_MON)
  479.                       goto dd2;
  480.                   if(ord)goto mm2; else goto dd2; /* "NN-" */
  481.                 }                /* "-NN-" */
  482. !             if(tp[TM_DAY] == TMNULL
  483. !             && tp[TM_YEAR] != TMNULL)    /* If "YY-NN-" */
  484.                   goto mm2;        /* then always MM */
  485.               if(ord)goto dd2; else goto mm2;
  486.             }
  487.           if(btoken.tbrkl == ch            /* "-NN" */
  488. !           && tp[ord ? TM_MON : TM_DAY] != TMNULL)
  489. !             if(tp[ord ? TM_DAY : TM_MON] == TMNULL)    /* MM/DD */
  490.                   if(ord)goto dd2; else goto mm2;
  491. -             else goto yy2;            /* "-YY" */
  492.         }
  493.   
  494. -     /* At this point only YY, DD, and HH are left.
  495. -      * YY is very unlikely since value is <= 32 and there was
  496. -      * no numerical format date.  Make one last try at YY
  497. -      * before dropping through to DD vs HH code.
  498. -      */
  499. -     if(btoken.tcnt == 2        /* If 2 digits */
  500. -       && tp[TM_HOUR] != TMNULL    /* and already have hour */
  501. -       && tp[TM_DAY] != TMNULL    /* and day, but  */
  502. -       && tp[TM_YEAR] == TMNULL)    /* no year, then assume */
  503. -         goto yy2;        /* that's what we have. */
  504.       /* Now reduced to choice between HH and DD */
  505. !     if(tp[TM_HOUR] != TMNULL) goto dd2;    /* Have hour? Assume day. */
  506. !     if(tp[TM_DAY] != TMNULL) goto hh2;    /* Have day? Assume hour. */
  507.       if(i > 24) goto dd2;            /* Impossible HH means DD */
  508. !     if(!ptitoken(btoken.tcp+btoken.tcnt, &atoken))    /* Read ahead! */
  509.           if(atoken.tval.tnum) return(0); /* ERR: bad token */
  510.           else goto dd2;            /* EOF, assume day. */
  511. !     if( atoken.tflg == 0        /* If next token is an alpha */
  512. !      && atoken.tval.ttmw->wflgs&TWTIME)  /* time-spec, assume hour */
  513.           goto hh2;        /* e.g. "3 PM", "11-EDT"  */
  514.   
  515. ! dd2:    if(ptstash(&tp[TM_DAY],i))    /* Store day (1 based) */
  516.           return(0);
  517. !     goto domore;
  518.   
  519. ! mm2:    if(ptstash(&tp[TM_MON], i-1))    /* Store month (make zero based) */
  520.           return(0);
  521. !     goto domore;
  522.   
  523. ! yy2:    i += 1900;
  524. ! year4:    if(ptstash(&tp[TM_YEAR],i))    /* Store year (full number) */
  525.           return(0);        /* ERR: year conflict */
  526. !     goto domore;
  527.   
  528.       /* Hack HH:MM[[:]SS] */
  529.   coltime:
  530. !     if(ptstash(&tp[TM_HOUR],i)) return(0);
  531. !     if(!ptitoken(btoken.tcp+btoken.tcnt,&btoken))
  532.           return(!btoken.tval.tnum);
  533.       if(!btoken.tflg) return(0);    /* ERR: HH:<alpha> */
  534.       if(btoken.tcnt == 4)        /* MMSS */
  535. !         if(ptstash(&tp[TM_MIN],btoken.tval.tnum/100)
  536. !           || ptstash(&tp[TM_SEC],btoken.tval.tnum%100))
  537.               return(0);
  538. !         else goto domore;
  539.       if(btoken.tcnt != 2
  540. !       || ptstash(&tp[TM_MIN],btoken.tval.tnum))
  541.           return(0);        /* ERR: MM bad */
  542. !     if(btoken.tbrk != ':') goto domore;    /* Seconds follow? */
  543. ! coltm2:    if(!ptitoken(btoken.tcp+btoken.tcnt,&btoken))
  544.           return(!btoken.tval.tnum);
  545.       if(!btoken.tflg || btoken.tcnt != 2    /* Verify SS */
  546. !       || ptstash(&tp[TM_SEC], btoken.tval.tnum))
  547.           return(0);        /* ERR: SS bad */
  548. !     goto domore;
  549.   }
  550.   
  551.   /* Store date/time value, return 0 if successful.
  552. !  * Fails if entry already set to a different value.
  553.    */
  554.   ptstash(adr,val)
  555.   int *adr;
  556.   {    register int *a;
  557. !     if( *(a=adr) != TMNULL)
  558. !         return(*a != val);
  559.       *a = val;
  560.       return(0);
  561.   }
  562.   
  563. ! /* This subroutine is invoked for NOON or MIDNIGHT when wrapping up
  564.    * just prior to returning from partime.
  565.    */
  566. ! pt12hack(atp, aval)
  567. ! int *atp, aval;
  568. ! {    register int *tp, i, h;
  569. !     tp = atp;
  570. !     if (((i=tp[TM_MIN]) && i != TMNULL)    /* Ensure mins, secs */
  571. !      || ((i=tp[TM_SEC]) && i != TMNULL))    /* are 0 or unspec'd */
  572. !         return(0);            /* ERR: MM:SS not 00:00 */
  573. !     i = aval;            /* Get 0 or 12 (midnite or noon) */
  574. !     if ((h = tp[TM_HOUR]) == TMNULL    /* If hour unspec'd, win */
  575. !      || h == 12)            /* or if 12:00 (matches either) */
  576. !         tp[TM_HOUR] = i;    /* Then set time */
  577. !     else if(!(i == 0        /* Nope, but if midnight and */
  578. !         &&(h == 0 || h == 24)))    /* time matches, can pass. */
  579. !             return(0);    /* ERR: HH conflicts */
  580. !     tp[TM_AMPM] = TMNULL;        /* Always reset this value if won */
  581. !     return(1);
  582.   }
  583.   
  584. - /* Null routine for no-op tokens */
  585. - ptnoise() { return(1); }
  586.   /* Get a token and identify it to some degree.
  587.    * Returns 0 on failure; token.tval will be 0 for normal EOF, otherwise
  588.    * hit error of some sort
  589.    */
  590.   
  591. ! ptitoken(astr, tkp)
  592.   register struct token *tkp;
  593. - char *astr;
  594.   {
  595. !     register char *cp;
  596. !     register int i;
  597.   
  598. !     tkp->tval.tnum = 0;
  599. !     if(pttoken(astr,tkp) == 0)
  600.   #ifdef DEBUG
  601. !     VOID printf("EOF\n");
  602. ! #endif DEBUG
  603.           return(0);
  604.       cp = tkp->tcp;
  605.   
  606.   #ifdef DEBUG
  607. !     i = cp[tkp->tcnt];
  608. !     cp[tkp->tcnt] = 0;
  609. !     VOID printf("Token: \"%s\" ",cp);
  610. !     cp[tkp->tcnt] = i;
  611. ! #endif DEBUG
  612. !     if(tkp->tflg)
  613. !         for(i = tkp->tcnt; i > 0; i--)
  614. !             tkp->tval.tnum = (int)tkp->tval.tnum*10 + ((*cp++)-'0');
  615. !     else
  616. !       {     i = ptmatchstr(cp, tkp->tcnt, tmwords);
  617. !         tkp->tval.tnum = i ? i : -1;         /* Set -1 for error */
  618.   
  619.   #ifdef DEBUG
  620. !         if(!i) VOID printf("Not found!\n");
  621. ! #endif DEBUG
  622. !         if(!i) return(0);
  623.         }
  624.   
  625.   #ifdef DEBUG
  626.       if(tkp->tflg)
  627.           VOID printf("Val: %d.\n",tkp->tval.tnum);
  628. !     else VOID printf("Found: \"%s\", val: %d., type %d\n",
  629.           tkp->tval.ttmw->went,tkp->tval.ttmw->wval,tkp->tval.ttmw->wtype);
  630. ! #endif DEBUG
  631.   
  632.       return(1);
  633.   }
  634.   
  635.   /* Read token from input string into token structure */
  636. ! pttoken(astr,tkp)
  637.   register struct token *tkp;
  638. - char *astr;
  639.   {
  640. !     register char *cp;
  641.       register int c;
  642.   
  643. !     tkp->tcp = cp = astr;
  644.       tkp->tbrkl = tkp->tbrk;        /* Set "last break" */
  645.       tkp->tcnt = tkp->tbrk = tkp->tflg = 0;
  646.   
  647.       while(c = *cp++)
  648.         {    switch(c)
  649.             {    case ' ': case '\t':    /* Flush all whitespace */
  650. !                 while((c = *cp++) && isspace(c));
  651. !                 cp--;        /* Drop thru to handle brk */
  652.               case '(': case ')':    /* Perhaps any non-alphanum */
  653.               case '-': case ',':    /* shd qualify as break? */
  654.               case '/': case ':': case '.':    /* Break chars */
  655.                   if(tkp->tcnt == 0)    /* If no token yet */
  656.                     {    tkp->tcp = cp;    /* ignore the brk */
  657. --- 399,606 ----
  658.         {    ord = (ch == '.' ? 0 : 1);    /* n/m = D/M or M/D */
  659.           if(btoken.tbrk == ch)            /* "NN-" */
  660.             {    if(btoken.tbrkl != ch)
  661. !               {
  662. !                 atoken = btoken;
  663. !                 atoken.tcnt++;
  664. !                 if (ptitoken(&atoken)
  665.                     && atoken.tflg == 0
  666.                     && atoken.tval.ttmw->wtype == TM_MON)
  667.                       goto dd2;
  668.                   if(ord)goto mm2; else goto dd2; /* "NN-" */
  669.                 }                /* "-NN-" */
  670. !             if (!given(atm->tm_mday)
  671. !               && given(atm->tm_year))    /* If "YYYY-NN-" */
  672.                   goto mm2;        /* then always MM */
  673.               if(ord)goto dd2; else goto mm2;
  674.             }
  675.           if(btoken.tbrkl == ch            /* "-NN" */
  676. !           && given(ord ? atm->tm_mon : atm->tm_mday))
  677. !             if (!given(ord ? atm->tm_mday : atm->tm_mon)) /* MM/DD */
  678.                   if(ord)goto dd2; else goto mm2;
  679.         }
  680.   
  681.       /* Now reduced to choice between HH and DD */
  682. !     if (given(atm->tm_hour)) goto dd2;    /* Have hour? Assume day. */
  683. !     if (given(atm->tm_mday)) goto hh2;    /* Have day? Assume hour. */
  684. !     if (given(atm->tm_mon)) goto dd2;    /* Have month? Assume day. */
  685.       if(i > 24) goto dd2;            /* Impossible HH means DD */
  686. !     atoken = btoken;
  687. !     if (!ptitoken(&atoken))            /* Read ahead! */
  688.           if(atoken.tval.tnum) return(0); /* ERR: bad token */
  689.           else goto dd2;            /* EOF, assume day. */
  690. !     if ( atoken.tflg
  691. !        ? !isdigit(*atoken.tcp)
  692. !        : atoken.tval.ttmw->wflgs & TWTIME)
  693. !         /* If next token is a time spec, assume hour */
  694.           goto hh2;        /* e.g. "3 PM", "11-EDT"  */
  695.   
  696. ! dd2:    if (ptstash(&atm->tm_mday, i))    /* Store day (1 based) */
  697.           return(0);
  698. !     continue;
  699.   
  700. ! mm2:    if (ptstash(&atm->tm_mon, i-1))    /* Store month (make zero based) */
  701.           return(0);
  702. !     continue;
  703.   
  704. ! year4:    if ((i-=1900) < 0  ||  ptstash(&atm->tm_year, i)) /* Store year-1900 */
  705.           return(0);        /* ERR: year conflict */
  706. !     continue;
  707.   
  708.       /* Hack HH:MM[[:]SS] */
  709.   coltime:
  710. !     if (ptstash(&atm->tm_hour, i)) return 0;
  711. !     if (!ptitoken(&btoken))
  712.           return(!btoken.tval.tnum);
  713.       if(!btoken.tflg) return(0);    /* ERR: HH:<alpha> */
  714.       if(btoken.tcnt == 4)        /* MMSS */
  715. !         if (ptstash(&atm->tm_min, btoken.tval.tnum/100)
  716. !           || ptstash(&atm->tm_sec, btoken.tval.tnum%100))
  717.               return(0);
  718. !         else continue;
  719.       if(btoken.tcnt != 2
  720. !       || ptstash(&atm->tm_min, btoken.tval.tnum))
  721.           return(0);        /* ERR: MM bad */
  722. !     if (btoken.tbrk != ':') continue;    /* Seconds follow? */
  723. ! coltm2:    if (!ptitoken(&btoken))
  724.           return(!btoken.tval.tnum);
  725.       if(!btoken.tflg || btoken.tcnt != 2    /* Verify SS */
  726. !       || ptstash(&atm->tm_sec, btoken.tval.tnum))
  727.           return(0);        /* ERR: SS bad */
  728. !     }
  729.   }
  730.   
  731.   /* Store date/time value, return 0 if successful.
  732. !  * Fail if entry is already set.
  733.    */
  734. +     static int
  735.   ptstash(adr,val)
  736.   int *adr;
  737. + int val;
  738.   {    register int *a;
  739. !     if (given(*(a=adr)))
  740. !         return 1;
  741.       *a = val;
  742.       return(0);
  743.   }
  744.   
  745. ! /* This subroutine is invoked for AM, PM, NOON and MIDNIGHT when wrapping up
  746.    * just prior to returning from partime.
  747.    */
  748. !     static int
  749. ! pt12hack(tm, aval)
  750. ! register struct tm *tm;
  751. ! register int aval;
  752. ! {    register int h = tm->tm_hour;
  753. !     switch (aval) {
  754. !       case T12_AM:
  755. !       case T12_PM:
  756. !         if (h > 12)
  757. !             return 0;
  758. !         if (h == 12)
  759. !             tm->tm_hour = 0;
  760. !         if (aval == T12_PM)
  761. !             tm->tm_hour += 12;
  762. !         break;
  763. !       default:
  764. !         if (0 < tm->tm_min  ||  0 < tm->tm_sec)
  765. !             return 0;
  766. !         if (!given(h) || h==12)
  767. !             tm->tm_hour = aval;
  768. !         else if (aval==T12_MIDNIGHT  &&  (h==0 || h==24))
  769. !             return 0;
  770. !     }
  771. !     return 1;
  772.   }
  773.   
  774.   /* Get a token and identify it to some degree.
  775.    * Returns 0 on failure; token.tval will be 0 for normal EOF, otherwise
  776.    * hit error of some sort
  777.    */
  778.   
  779. !     static int
  780. ! ptitoken(tkp)
  781.   register struct token *tkp;
  782.   {
  783. !     register const char *cp;
  784. !     register int i, j, k;
  785.   
  786. !     if (!pttoken(tkp))
  787.   #ifdef DEBUG
  788. !         {
  789. !         VOID printf("EOF\n");
  790. !         return(0);
  791. !         }
  792. ! #else
  793.           return(0);
  794. + #endif    
  795.       cp = tkp->tcp;
  796.   
  797.   #ifdef DEBUG
  798. !     VOID printf("Token: \"%.*s\" ", tkp->tcnt, cp);
  799. ! #endif
  800.   
  801. +     if (tkp->tflg) {
  802. +         i = tkp->tcnt;
  803. +         if (*cp == '+' || *cp == '-') {
  804. +             cp++;
  805. +             i--;
  806. +         }
  807. +         while (0 <= --i) {
  808. +             j = tkp->tval.tnum*10;
  809. +             k = j + (*cp++ - '0');
  810. +             if (j/10 != tkp->tval.tnum  ||  k < j) {
  811. +                 /* arithmetic overflow */
  812. +                 tkp->tval.tnum = 1;
  813. +                 return 0;
  814. +             }
  815. +             tkp->tval.tnum = k;
  816. +         }
  817. +     } else if (!(tkp->tval.ttmw  =  ptmatchstr(cp, tkp->tcnt, tmwords)))
  818. +       {
  819.   #ifdef DEBUG
  820. !         VOID printf("Not found!\n");
  821. ! #endif
  822. !         tkp->tval.tnum = 1;
  823. !         return 0;
  824.         }
  825.   
  826.   #ifdef DEBUG
  827.       if(tkp->tflg)
  828.           VOID printf("Val: %d.\n",tkp->tval.tnum);
  829. !     else VOID printf("Found: \"%s\", val: %d, type %d\n",
  830.           tkp->tval.ttmw->went,tkp->tval.ttmw->wval,tkp->tval.ttmw->wtype);
  831. ! #endif
  832.   
  833.       return(1);
  834.   }
  835.   
  836.   /* Read token from input string into token structure */
  837. !     static int
  838. ! pttoken(tkp)
  839.   register struct token *tkp;
  840.   {
  841. !     register const char *cp;
  842.       register int c;
  843. +     const char *astr;
  844.   
  845. !     tkp->tcp = astr = cp = tkp->tcp + tkp->tcnt;
  846.       tkp->tbrkl = tkp->tbrk;        /* Set "last break" */
  847.       tkp->tcnt = tkp->tbrk = tkp->tflg = 0;
  848. +     tkp->tval.tnum = 0;
  849.   
  850.       while(c = *cp++)
  851.         {    switch(c)
  852.             {    case ' ': case '\t':    /* Flush all whitespace */
  853. !             case '\r': case '\n':
  854. !             case '\v': case '\f':
  855. !                 if (!tkp->tcnt) {    /* If no token yet */
  856. !                     tkp->tcp = cp;    /* ignore the brk */
  857. !                     continue;    /* and go on. */
  858. !                 }
  859. !                 /* fall into */
  860.               case '(': case ')':    /* Perhaps any non-alphanum */
  861.               case '-': case ',':    /* shd qualify as break? */
  862. +             case '+':
  863.               case '/': case ':': case '.':    /* Break chars */
  864.                   if(tkp->tcnt == 0)    /* If no token yet */
  865.                     {    tkp->tcp = cp;    /* ignore the brk */
  866. ***************
  867. *** 420,476 ****
  868.                   tkp->tbrk = c;
  869.                   return(tkp->tcnt);
  870.             }
  871. !         if(tkp->tcnt == 0)        /* If first char of token, */
  872. !             tkp->tflg = isdigit(c);    /*    determine type */
  873. !           if(( isdigit(c) &&  tkp->tflg)    /* If not first, make sure */
  874. !          ||(!isdigit(c) && !tkp->tflg))    /*    char matches type */
  875. !             tkp->tcnt++;        /* Win, add to token. */
  876. !         else {
  877. !             cp--;            /* Wrong type, back up */
  878.               tkp->tbrk = c;
  879. !             return(tkp->tcnt);
  880. !           }
  881.         }
  882.       return(tkp->tcnt);        /* When hit EOF */
  883.   }
  884.   
  885.   
  886.   ptmatchstr(astr,cnt,astruc)
  887. ! char *astr;
  888. ! int cnt;
  889. ! struct tmwent *astruc;
  890. ! {    register char *cp, *mp;
  891.       register int c;
  892. !     struct tmwent *lastptr;
  893. !     struct integ { int word; };   /* For getting at array ptr */
  894.       int i;
  895.   
  896.       lastptr = 0;
  897. !     for(;mp = (char *)((struct integ *)astruc)->word; astruc += 1)
  898.         {    cp = astr;
  899.           for(i = cnt; i > 0; i--)
  900. !           {    switch((c = *cp++) ^ *mp++)    /* XOR the chars */
  901.                 {    case 0: continue;    /* Exact match */
  902. !                 case 040: if(isalpha(c))
  903.                       continue;
  904.                 }
  905.               break;
  906.             }
  907.           if(i==0)
  908. !             if(*mp == 0) return((unsigned int)astruc);    /* Exact match */
  909.               else if(lastptr) return(0);    /* Ambiguous */
  910.               else lastptr = astruc;        /* 1st ambig */
  911.         }
  912. !     return((unsigned int)lastptr);
  913. ! }
  914. ! zaptime(tp)
  915. ! register int *tp;
  916. ! /* clears tm structure pointed to by tp */
  917. ! {    register int i;
  918. !     i = (sizeof (struct tm))/(sizeof (int));
  919. !     do *tp++ = TMNULL;        /* Set entry to "unspecified" */
  920. !     while(--i);            /* Faster than FOR */
  921.   }
  922. --- 610,662 ----
  923.                   tkp->tbrk = c;
  924.                   return(tkp->tcnt);
  925.             }
  926. !         if (!tkp->tcnt++) {        /* If first char of token, */
  927. !             if (isdigit(c)) {
  928. !                 tkp->tflg = 1;
  929. !                 if (astr<cp-2 && (cp[-2]=='-'||cp[-2]=='+')) {
  930. !                     /* timezone is break+sign+digit */
  931. !                     tkp->tcp--;
  932. !                     tkp->tcnt++;
  933. !                 }
  934. !             }
  935. !         } else if ((isdigit(c)!=0) != tkp->tflg) { /* else check type */
  936.               tkp->tbrk = c;
  937. !             return --tkp->tcnt;    /* Wrong type, back up */
  938. !         }
  939.         }
  940.       return(tkp->tcnt);        /* When hit EOF */
  941.   }
  942.   
  943.   
  944. +     static const struct tmwent *
  945.   ptmatchstr(astr,cnt,astruc)
  946. !     const char *astr;
  947. !     int cnt;
  948. !     const struct tmwent *astruc;
  949. ! {
  950. !     register const char *cp, *mp;
  951.       register int c;
  952. !     const struct tmwent *lastptr;
  953.       int i;
  954.   
  955.       lastptr = 0;
  956. !     for(;mp = astruc->went; astruc += 1)
  957.         {    cp = astr;
  958.           for(i = cnt; i > 0; i--)
  959. !           {
  960. !             switch (*cp++ - (c = *mp++))
  961.                 {    case 0: continue;    /* Exact match */
  962. !                 case 'A'-'a':
  963. !                     /* `if (ctab[c]==Letter)' in RCS */
  964. !                     if (islower(c))
  965.                       continue;
  966.                 }
  967.               break;
  968.             }
  969.           if(i==0)
  970. !             if (!*mp) return astruc;    /* Exact match */
  971.               else if(lastptr) return(0);    /* Ambiguous */
  972.               else lastptr = astruc;        /* 1st ambig */
  973.         }
  974. !     return lastptr;
  975.   }
  976. diff -c src/patch.c:1.6 src/patch.c:1.6.1.1
  977. *** src/patch.c:1.6    Wed Feb  6 11:32:13 1991
  978. --- src/patch.c    Wed Feb  6 11:32:14 1991
  979. ***************
  980. *** 1,5 ****
  981.   #ifndef lint
  982. ! static char rcsid[] = "$Id: patch.c,v 1.6 90/02/14 10:01:33 berliner Exp $";
  983.   #endif !lint
  984.   
  985.   /*
  986. --- 1,5 ----
  987.   #ifndef lint
  988. ! static char rcsid[] = "$Id: patch.c,v 1.6.1.1 91/01/18 12:18:45 berliner Exp $";
  989.   #endif !lint
  990.   
  991.   /*
  992. ***************
  993. *** 22,29 ****
  994.   #include <ctype.h>
  995.   #include "cvs.h"
  996.   
  997. - extern long maketime();
  998.   extern char update_dir[];
  999.   extern DBM *open_module();
  1000.   extern int force_tag_match;
  1001. --- 22,27 ----
  1002. ***************
  1003. *** 106,112 ****
  1004.       error(0, "must specify at least one revision/date!");
  1005.       }
  1006.       if (date1[0] != '\0' && date2[0] != '\0') {
  1007. !     if (strcmp(date1, date2) >= 0)
  1008.           error(0, "second date must come after first date!");
  1009.       }
  1010.       (void) signal(SIGHUP, patch_cleanup);
  1011. --- 104,110 ----
  1012.       error(0, "must specify at least one revision/date!");
  1013.       }
  1014.       if (date1[0] != '\0' && date2[0] != '\0') {
  1015. !     if (datecmp(date1, date2) >= 0)
  1016.           error(0, "second date must come after first date!");
  1017.       }
  1018.       (void) signal(SIGHUP, patch_cleanup);
  1019. ***************
  1020. *** 379,395 ****
  1021.           if (*cp && (semi = index(cp, ';')) != NULL) {
  1022.           ret = 0;
  1023.           *semi = '\0';
  1024. !         ftm = &tm;
  1025. !         zaptime((int *)ftm);
  1026. !         (void) sscanf(cp, DATEFORM, &ftm->tm_year, &ftm->tm_mon,
  1027. !                   &ftm->tm_mday, &ftm->tm_hour, &ftm->tm_min,
  1028. !                   &ftm->tm_sec);
  1029. !         ftm->tm_mon--;
  1030. !         revdate = (time_t)maketime(ftm) - 1;
  1031. !         ftm = localtime(&revdate);
  1032. !         (void) sprintf(date, DATEFORM, ftm->tm_year, ftm->tm_mon+1,
  1033. !                    ftm->tm_mday, ftm->tm_hour, ftm->tm_min,
  1034. !                    ftm->tm_sec);
  1035.           }
  1036.           break;
  1037.       }
  1038. --- 377,383 ----
  1039.           if (*cp && (semi = index(cp, ';')) != NULL) {
  1040.           ret = 0;
  1041.           *semi = '\0';
  1042. !         semi[-1]--;    /* This works even if semi[-1] was '0'.  */
  1043.           }
  1044.           break;
  1045.       }
  1046. diff -c src/patchlevel.h:1.1 src/patchlevel.h:1.1.1.1
  1047. *** src/patchlevel.h:1.1    Wed Feb  6 11:32:12 1991
  1048. --- src/patchlevel.h    Wed Feb  6 11:32:12 1991
  1049. ***************
  1050. *** 1,3 ****
  1051. ! /*    $Id: patchlevel.h,v 1.1 89/11/20 00:06:30 berliner Exp $    */
  1052.   
  1053. ! #define    PATCHLEVEL    0
  1054. --- 1,3 ----
  1055. ! /*    $Id: patchlevel.h,v 1.1.1.1 91/01/18 12:19:46 berliner Exp $    */
  1056.   
  1057. ! #define    PATCHLEVEL    2
  1058. diff -c src/rcstime.h:1.1 src/rcstime.h:removed
  1059. *** src/rcstime.h:1.1    Wed Feb  6 11:32:08 1991
  1060. --- src/rcstime.h    Wed Feb  6 11:32:08 1991
  1061. ***************
  1062. *** 1,42 ****
  1063. - #define TIMEID "$Id: rcstime.h,v 1.1 89/05/09 11:51:03 berliner Exp $"
  1064. - /* Structure for use by time manipulating subroutines.
  1065. -  * The following library routines use it:
  1066. -  *    libc: ctime, localtime, gmtime, asctime
  1067. -  *    libcx: partime, maketime (may not be installed yet)
  1068. -  */
  1069. - struct tm {     /* See defines below for allowable ranges */
  1070. -     int tm_sec;
  1071. -     int tm_min;
  1072. -     int tm_hour;
  1073. -     int tm_mday;
  1074. -     int tm_mon;
  1075. -     int tm_year;
  1076. -     int tm_wday;
  1077. -     int tm_yday;
  1078. -     int tm_isdst;
  1079. -     int tm_zon;    /* NEW: mins westward of Greenwich */
  1080. -     int tm_ampm;    /* NEW: 1 if AM, 2 if PM */
  1081. - };
  1082. - #define LCLZONE (5*60)    /* Until V7 ftime(2) works, this defines local zone*/
  1083. - #define TMNULL (-1)    /* Items not specified are given this value
  1084. -              * in order to distinguish null specs from zero
  1085. -              * specs.  This is only used by partime and
  1086. -              * maketime. */
  1087. -     /* Indices into TM structure */
  1088. - #define TM_SEC 0    /* 0-59            */
  1089. - #define TM_MIN 1    /* 0-59            */
  1090. - #define TM_HOUR 2    /* 0-23            */
  1091. - #define TM_MDAY 3    /* 1-31            day of month */
  1092. - #define TM_DAY TM_MDAY    /*  "            synonym      */
  1093. - #define TM_MON 4    /* 0-11            */
  1094. - #define TM_YEAR 5    /* (year-1900) (year)    */
  1095. - #define TM_WDAY 6    /* 0-6            day of week (0 = Sunday) */
  1096. - #define TM_YDAY 7    /* 0-365        day of year */
  1097. - #define TM_ISDST 8    /* 0 Std, 1 DST        */
  1098. -     /* New stuff */
  1099. - #define TM_ZON 9    /* 0-(24*60) minutes west of Greenwich */
  1100. - #define TM_AMPM 10    /* 1 AM, 2 PM        */
  1101. --- 0 ----
  1102. diff -c src/subr.c:1.14 src/subr.c:1.14.1.2
  1103. *** src/subr.c:1.14    Wed Feb  6 11:32:11 1991
  1104. --- src/subr.c    Wed Feb  6 11:32:11 1991
  1105. ***************
  1106. *** 1,5 ****
  1107.   #ifndef lint
  1108. ! static char rcsid[] = "$Id: subr.c,v 1.14 89/11/20 09:51:10 berliner Exp $";
  1109.   #endif !lint
  1110.   
  1111.   /*
  1112. --- 1,5 ----
  1113.   #ifndef lint
  1114. ! static char rcsid[] = "$Id: subr.c,v 1.14.1.2 91/01/29 19:46:20 berliner Exp $";
  1115.   #endif !lint
  1116.   
  1117.   /*
  1118. ***************
  1119. *** 114,120 ****
  1120.   
  1121.       if (stat(file, &sb) < 0)
  1122.       return (0);
  1123. !     return ((sb.st_mode & S_IFMT) & S_IFDIR);
  1124.   }
  1125.   
  1126.   /*
  1127. --- 114,120 ----
  1128.   
  1129.       if (stat(file, &sb) < 0)
  1130.       return (0);
  1131. !     return ((sb.st_mode & S_IFMT) == S_IFDIR);
  1132.   }
  1133.   
  1134.   /*
  1135. ***************
  1136. *** 125,133 ****
  1137.   {
  1138.       struct stat sb;
  1139.   
  1140.       if (lstat(file, &sb) < 0)
  1141.       return (0);
  1142. !     return ((sb.st_mode & S_IFMT) & S_IFLNK);
  1143.   }
  1144.   
  1145.   /*
  1146. --- 125,137 ----
  1147.   {
  1148.       struct stat sb;
  1149.   
  1150. + #ifdef S_IFLNK
  1151.       if (lstat(file, &sb) < 0)
  1152.       return (0);
  1153. !     return ((sb.st_mode & S_IFMT) == S_IFLNK);
  1154. ! #else
  1155. !     return (0);
  1156. ! #endif
  1157.   }
  1158.   
  1159.   /*
  1160. diff -c src/tag.c:1.19 src/tag.c:1.19.1.2
  1161. *** src/tag.c:1.19    Wed Feb  6 11:32:10 1991
  1162. --- src/tag.c    Wed Feb  6 11:32:10 1991
  1163. ***************
  1164. *** 1,5 ****
  1165.   #ifndef lint
  1166. ! static char rcsid[] = "$Id: tag.c,v 1.19 89/11/19 23:40:46 berliner Exp $";
  1167.   #endif !lint
  1168.   
  1169.   /*
  1170. --- 1,5 ----
  1171.   #ifndef lint
  1172. ! static char rcsid[] = "$Id: tag.c,v 1.19.1.2 91/01/29 19:45:54 berliner Exp $";
  1173.   #endif !lint
  1174.   
  1175.   /*
  1176. ***************
  1177. *** 180,185 ****
  1178. --- 180,205 ----
  1179.   {
  1180.       char version[50];
  1181.   
  1182. + #ifdef S_IFLNK
  1183. +     /*
  1184. +      * Ooops..  if there is a symbolic link in the source repository
  1185. +      * which points to a normal file, rcs will do its file renaming
  1186. +      * mumbo-jumbo and blow away the symbolic link; this is essentially
  1187. +      * like a copy-on-commit RCS revision control file, but I consider
  1188. +      * it a bug.  Instead, read the contents of the link and recursively
  1189. +      * tag the link contents (which may be a symlink itself...).
  1190. +      */
  1191. +     if (islink(rcs) && isfile(rcs)) {
  1192. +     char link_name[MAXPATHLEN];
  1193. +     int count;
  1194. +     if ((count = readlink(rcs, link_name, sizeof(link_name))) != -1) {
  1195. +         link_name[count] = '\0';
  1196. +         return (tag_file(link_name));
  1197. +     }
  1198. +     }
  1199. + #endif
  1200.       if (delete) {
  1201.       /*
  1202.        * If -d is specified, "force_tag_match" is set, so that this call
  1203. ***************
  1204. *** 210,220 ****
  1205.       }
  1206.       Version_Number(rcs, numtag, Date, version);
  1207.       if (version[0] == '\0') {
  1208. !     if (!really_quiet) {
  1209.           warn(0, "cannot find tag '%s' for %s", numtag[0] ? numtag : "head",
  1210.            rcs);
  1211.       }
  1212. !     return (1);
  1213.       }
  1214.       if (isdigit(numtag[0]) && strcmp(numtag, version) != 0) {
  1215.       /*
  1216. --- 230,241 ----
  1217.       }
  1218.       Version_Number(rcs, numtag, Date, version);
  1219.       if (version[0] == '\0') {
  1220. !     if (!quiet) {
  1221.           warn(0, "cannot find tag '%s' for %s", numtag[0] ? numtag : "head",
  1222.            rcs);
  1223. +         return (1);
  1224.       }
  1225. !     return (0);
  1226.       }
  1227.       if (isdigit(numtag[0]) && strcmp(numtag, version) != 0) {
  1228.       /*
  1229. diff -c src/update.c:1.27 src/update.c:1.27.1.2
  1230. *** src/update.c:1.27    Wed Feb  6 11:32:16 1991
  1231. --- src/update.c    Wed Feb  6 11:32:16 1991
  1232. ***************
  1233. *** 1,5 ****
  1234.   #ifndef lint
  1235. ! static char rcsid[] = "$Id: update.c,v 1.27 89/11/19 23:40:48 berliner Exp $";
  1236.   #endif !lint
  1237.   
  1238.   /*
  1239. --- 1,5 ----
  1240.   #ifndef lint
  1241. ! static char rcsid[] = "$Id: update.c,v 1.27.1.2 91/02/06 18:30:07 berliner Exp $";
  1242.   #endif !lint
  1243.   
  1244.   /*
  1245. ***************
  1246. *** 111,117 ****
  1247.       if (!isdir(CVSADM)) {
  1248.       if (!quiet)
  1249.           warn(0, "warning: no %s directory found", CVSADM);
  1250. !     err += update_descend(update_recursive);
  1251.       return (err);
  1252.       }
  1253.       Name_Repository();
  1254. --- 111,132 ----
  1255.       if (!isdir(CVSADM)) {
  1256.       if (!quiet)
  1257.           warn(0, "warning: no %s directory found", CVSADM);
  1258. !     if (argc <= 0) {
  1259. !         err += update_descend(update_recursive);
  1260. !     } else {
  1261. !         int i;
  1262. !         for (i = 0; i < argc; i++) {
  1263. !         if (isdir(argv[i])) {
  1264. !             (void) strcat(Dlist, " ");
  1265. !             (void) strcat(Dlist, argv[i]);
  1266. !         } else {
  1267. !             warn(0, "nothing known about %s", argv[i]);
  1268. !             err++;
  1269. !         }
  1270. !         }
  1271. !         err += update_process_lists();
  1272. !     }
  1273.       return (err);
  1274.       }
  1275.       Name_Repository();
  1276. ***************
  1277. *** 175,180 ****
  1278. --- 190,196 ----
  1279.       update_Files = 1;
  1280.       (void) strcpy(User, cp);
  1281.       Scratch_Entry(User);
  1282. +     (void) unlink(User);
  1283.       }
  1284.       /*
  1285.        * Olist is the "needs checking out" list.
  1286. ***************
  1287. *** 349,359 ****
  1288.           strcmp(dp->d_name, CVSLCK) == 0)
  1289.           continue;
  1290.           (void) sprintf(fname, "%s/%s", Repository, dp->d_name);
  1291.           if (!isdir(fname))
  1292.           continue;
  1293. !         if (isdir(dp->d_name))
  1294.           continue;
  1295. !         if (isfile(dp->d_name)) {
  1296.           warn(0, "file %s should be a directory; please move it", dp->d_name);
  1297.           err++;
  1298.           } else {
  1299. --- 365,376 ----
  1300.           strcmp(dp->d_name, CVSLCK) == 0)
  1301.           continue;
  1302.           (void) sprintf(fname, "%s/%s", Repository, dp->d_name);
  1303. +         (void) sprintf(tmp, "%s/%s", dp->d_name, CVSADM);
  1304.           if (!isdir(fname))
  1305.           continue;
  1306. !         if (islink(dp->d_name) || isdir(tmp))
  1307.           continue;
  1308. !         if (!isdir(dp->d_name) && isfile(dp->d_name)) {
  1309.           warn(0, "file %s should be a directory; please move it", dp->d_name);
  1310.           err++;
  1311.           } else {
  1312. diff -c src/version_number.c:1.16 src/version_number.c:1.16.1.2
  1313. *** src/version_number.c:1.16    Wed Feb  6 11:32:09 1991
  1314. --- src/version_number.c    Wed Feb  6 11:32:09 1991
  1315. ***************
  1316. *** 1,5 ****
  1317.   #ifndef lint
  1318. ! static char rcsid[] = "$Id: version_number.c,v 1.16 89/11/19 23:20:35 berliner Exp $";
  1319.   #endif !lint
  1320.   
  1321.   /*
  1322. --- 1,5 ----
  1323.   #ifndef lint
  1324. ! static char rcsid[] = "$Id: version_number.c,v 1.16.1.2 91/01/29 07:20:26 berliner Exp $";
  1325.   #endif !lint
  1326.   
  1327.   /*
  1328. ***************
  1329. *** 67,72 ****
  1330. --- 67,73 ----
  1331.           (void) strcpy(vers, rev);
  1332.           }
  1333.       } else {
  1334. +         if (!force_tag_match)
  1335.           (void) strcpy(vers, rev);
  1336.       }
  1337.       }
  1338. ***************
  1339. *** 100,105 ****
  1340. --- 101,107 ----
  1341.       if (fgets(line, sizeof(line), fp) == NULL)
  1342.       return;
  1343.       if (strncmp(line, RCSHEAD, sizeof(RCSHEAD) - 1) != 0 ||
  1344. +     !isspace(line[sizeof(RCSHEAD) - 1]) ||
  1345.       (cp = rindex(line, ';')) == NULL)
  1346.       return;
  1347.       *cp = '\0';                /* strip the ';' */
  1348. ***************
  1349. *** 119,124 ****
  1350. --- 121,127 ----
  1351.       if (fgets(line, sizeof(line), fp) == NULL)
  1352.       return;
  1353.       if (strncmp(line, RCSBRANCH, sizeof(RCSBRANCH) - 1) == 0 &&
  1354. +     isspace(line[sizeof(RCSBRANCH) - 1]) &&
  1355.       (cp = rindex(line, ';')) != NULL) {
  1356.       *cp = '\0';            /* strip the ';' */
  1357.       if ((cp = rindex(line, ' ')) == NULL &&
  1358. ***************
  1359. *** 149,156 ****
  1360.        * the highest numbered branch off "rev", if necessary.
  1361.        */
  1362.       get_branch(fp, rev);
  1363. !     if (tag[0] == '\0' || isdigit(tag[0]) || symtag_matched < 0)
  1364. !     (void) strcpy(vers, rev);
  1365.   }
  1366.   
  1367.   /*
  1368. --- 152,161 ----
  1369.        * the highest numbered branch off "rev", if necessary.
  1370.        */
  1371.       get_branch(fp, rev);
  1372. !     if (tag[0] == '\0' || isdigit(tag[0]) || symtag_matched < 0) {
  1373. !     if ((numdots(rev) & 1) != 0)
  1374. !         (void) strcpy(vers, rev);
  1375. !     }
  1376.   }
  1377.   
  1378.   /*
  1379. ***************
  1380. *** 172,196 ****
  1381.       char *rev;
  1382.       char *vers;
  1383.   {
  1384. !     char line[MAXLINELEN], tagdot[50];
  1385.       char *cp, *cprev;
  1386. -     int tagdots, tagdotlen;
  1387.   
  1388.       if (isdigit(tag[0])) {
  1389. !     while (tag[strlen(tag)] == '.')
  1390. !         tag[strlen(tag)] = '\0';    /* strip trailing dots */
  1391. !     (void) sprintf(tagdot, "%s.", tag);
  1392. !     tagdotlen = strlen(tagdot);
  1393. !     tagdots = numdots(tag);
  1394.       }
  1395.       while (fgets(line, sizeof(line), fp) != NULL) {
  1396.       if (strncmp(line, RCSDESC, sizeof(RCSDESC) - 1) == 0) {
  1397. -         /*
  1398. -          * Use head, or a partial branch match found with the strncmp
  1399. -          * call with tagdot below
  1400. -          */
  1401.           rewind(fp);
  1402. !         return (1);
  1403.       }
  1404.       /*
  1405.        * For numeric tags, the RCS file contains the revision
  1406. --- 177,197 ----
  1407.       char *rev;
  1408.       char *vers;
  1409.   {
  1410. !     char line[MAXLINELEN];
  1411.       char *cp, *cprev;
  1412.   
  1413.       if (isdigit(tag[0])) {
  1414. !     while (tag[strlen(tag)-1] == '.')
  1415. !         tag[strlen(tag)-1] = '\0';    /* strip trailing dots */
  1416. !     if ((numdots(tag) & 1) == 0) {
  1417. !         (void) strcpy(rev, tag);
  1418. !         return (1);            /* let get_branch() figure it out */
  1419. !     }
  1420.       }
  1421.       while (fgets(line, sizeof(line), fp) != NULL) {
  1422.       if (strncmp(line, RCSDESC, sizeof(RCSDESC) - 1) == 0) {
  1423.           rewind(fp);
  1424. !         return (1);            /* use head */
  1425.       }
  1426.       /*
  1427.        * For numeric tags, the RCS file contains the revision
  1428. ***************
  1429. *** 204,221 ****
  1430.           (void) strcpy(vers, line);
  1431.           return (0);        /* a match for a numeric tag */
  1432.           }
  1433. -         if (strncmp(tagdot, line, tagdotlen) == 0) {
  1434. -         if ((tagdots & 1) == 0 && numdots(line) == tagdots+1)
  1435. -             (void) strcpy(rev, line);
  1436. -         }
  1437.           continue;
  1438.       }
  1439.       if (strncmp(line, RCSSYMBOL, sizeof(RCSSYMBOL)-1)!=0 ||
  1440. !         (cp = rindex(line, ';')) == NULL)
  1441. !         continue;
  1442. !     *cp = ' ';            /* strip the ';' */
  1443. !     if ((cp = index(line, ' ')) == NULL &&
  1444. !         (cp = index(line, '\t')) == NULL)
  1445.           continue;
  1446.       /*
  1447.        * A rather ugly loop to process the "symbols" line.  I would
  1448. --- 205,214 ----
  1449.           (void) strcpy(vers, line);
  1450.           return (0);        /* a match for a numeric tag */
  1451.           }
  1452.           continue;
  1453.       }
  1454.       if (strncmp(line, RCSSYMBOL, sizeof(RCSSYMBOL)-1)!=0 ||
  1455. !         !isspace(line[sizeof(RCSSYMBOL) - 1]))
  1456.           continue;
  1457.       /*
  1458.        * A rather ugly loop to process the "symbols" line.  I would
  1459. ***************
  1460. *** 222,233 ****
  1461.        * really rather use strtok(), but other above me already are,
  1462.        * and strtok() blows up in this case.
  1463.        */
  1464. !     while (cp && *cp) {
  1465.           while (isspace(*cp))
  1466.           cp++;
  1467.           /* symbols and revisions are separated by a colon */
  1468.           if ((cprev = index(cp, ':')) == NULL) {
  1469. !         while (*cp && !isspace(*cp))
  1470.               cp++;
  1471.           continue;
  1472.           }
  1473. --- 215,234 ----
  1474.        * really rather use strtok(), but other above me already are,
  1475.        * and strtok() blows up in this case.
  1476.        */
  1477. !     for (cp = line + sizeof(RCSSYMBOL);  cp;  ) {
  1478.           while (isspace(*cp))
  1479.           cp++;
  1480. +         if (*cp == ';')
  1481. +         break;
  1482. +         if (!*cp) {
  1483. +             if (fgets(line, sizeof(line), fp) == NULL)
  1484. +             return 0;
  1485. +         cp = line;
  1486. +         continue;
  1487. +         }
  1488.           /* symbols and revisions are separated by a colon */
  1489.           if ((cprev = index(cp, ':')) == NULL) {
  1490. !         while (*cp && !isspace(*cp) && *cp!=';')
  1491.               cp++;
  1492.           continue;
  1493.           }
  1494. ***************
  1495. *** 245,253 ****
  1496.           (void) strcpy(rev, cprev);
  1497.           return (-1);        /* look for branches off rev */
  1498.           } else {
  1499. !         while (!isspace(*cp))
  1500.               cp++;
  1501. -         cp++;
  1502.           }
  1503.       }
  1504.       return (1);
  1505. --- 246,253 ----
  1506.           (void) strcpy(rev, cprev);
  1507.           return (-1);        /* look for branches off rev */
  1508.           } else {
  1509. !         while (!isspace(*cp) && *cp!=';')
  1510.               cp++;
  1511.           }
  1512.       }
  1513.       return (1);
  1514. ***************
  1515. *** 282,288 ****
  1516.           *cp = '\0';        /* strip the newline */
  1517.           if (numdots(line) == dots+1 &&
  1518.           strncmp(line, branch, len) == 0) {
  1519. !         if (strcmp(branch, line) <= 0)
  1520.               (void) strcpy(rev, line);
  1521.           }
  1522.       }
  1523. --- 282,288 ----
  1524.           *cp = '\0';        /* strip the newline */
  1525.           if (numdots(line) == dots+1 &&
  1526.           strncmp(line, branch, len) == 0) {
  1527. !         if ((numdots(rev) & 1) == 0 || strcmp(rev, line) <= 0)
  1528.               (void) strcpy(rev, line);
  1529.           }
  1530.       }
  1531. ***************
  1532. *** 342,348 ****
  1533.       char *cp, *semi;
  1534.   
  1535.       last_rev[0] = '\0';
  1536. !     (void) strcpy(curdate, "00");    /* what happens at 2000 ad? */
  1537.       (void) sprintf(date_dot, "%s.", rev);
  1538.       date_dotlen = strlen(date_dot);
  1539.       date_dots = numdots(rev);
  1540. --- 342,348 ----
  1541.       char *cp, *semi;
  1542.   
  1543.       last_rev[0] = '\0';
  1544. !     curdate[0] = '\0';
  1545.       (void) sprintf(date_dot, "%s.", rev);
  1546.       date_dotlen = strlen(date_dot);
  1547.       date_dots = numdots(rev);
  1548. ***************
  1549. *** 360,365 ****
  1550. --- 360,366 ----
  1551.           continue;
  1552.       }
  1553.       if (strncmp(line, RCSDATE, sizeof(RCSDATE) - 1) == 0 &&
  1554. +         isspace(line[sizeof(RCSDATE) - 1]) &&
  1555.           last_rev[0] != '\0') {
  1556.           for (cp = line; *cp && !isspace(*cp); cp++)
  1557.           ;
  1558. ***************
  1559. *** 367,373 ****
  1560.           cp++;
  1561.           if (*cp && (semi = index(cp, ';')) != NULL) {
  1562.           *semi = '\0';        /* strip the semicolon */
  1563. !         if (strcmp(cp, date) <= 0 && strcmp(cp, curdate) >= 0) {
  1564.               (void) strcpy(curdate, cp);
  1565.               (void) strcpy(version, last_rev);
  1566.           }
  1567. --- 368,374 ----
  1568.           cp++;
  1569.           if (*cp && (semi = index(cp, ';')) != NULL) {
  1570.           *semi = '\0';        /* strip the semicolon */
  1571. !         if (datecmp(cp, date) <= 0 && datecmp(cp, curdate) >= 0) {
  1572.               (void) strcpy(curdate, cp);
  1573.               (void) strcpy(version, last_rev);
  1574.           }
  1575. ***************
  1576. *** 374,377 ****
  1577. --- 375,391 ----
  1578.           }
  1579.       }
  1580.       }
  1581. + }
  1582. + /*
  1583. +  * Compare two dates in RCS format.
  1584. +  * Beware the change in format on January 1, 2000,
  1585. +  * when years go from 2-digit to full format.
  1586. +  */
  1587. + int
  1588. + datecmp(date1, date2)
  1589. +     char *date1, *date2;
  1590. + {
  1591. +     int length_diff = strlen(date1) - strlen(date2);
  1592. +     return length_diff ? length_diff : strcmp(date1, date2);
  1593.   }
  1594.  
  1595.