386BSD 0.1 development
authorWilliam F. Jolitz <wjolitz@soda.berkeley.edu>
Wed, 16 Aug 1989 04:35:08 +0000 (20:35 -0800)
committerWilliam F. Jolitz <wjolitz@soda.berkeley.edu>
Wed, 16 Aug 1989 04:35:08 +0000 (20:35 -0800)
Work on file usr/src/usr.bin/rcs/src/partime.c

Co-Authored-By: Lynne Greer Jolitz <ljolitz@cardio.ucsf.edu>
Synthesized-from: 386BSD-0.1

usr/src/usr.bin/rcs/src/partime.c [new file with mode: 0644]

diff --git a/usr/src/usr.bin/rcs/src/partime.c b/usr/src/usr.bin/rcs/src/partime.c
new file mode 100644 (file)
index 0000000..a8303ed
--- /dev/null
@@ -0,0 +1,502 @@
+/*
+ * PARTIME             parse date/time string into a TM structure
+ *
+ * Usage:
+ *      #include "time.h"             -- expanded tm structure
+ *     char *str; struct tm *tp;
+ *     partime(str,tp);
+ * Returns:
+ *     0 if parsing failed
+ *     else time values in specified TM structure (unspecified values
+ *             set to TMNULL)
+ * Notes:
+ *     This code is quasi-public; it may be used freely in like software.
+ *     It is not to be sold, nor used in licensed software without
+ *     permission of the author.
+ *     For everyone's benefit, please report bugs and improvements!
+ *     Copyright 1980 by Ken Harrenstien, SRI International.
+ *     (ARPANET: KLH @ SRI)
+ */
+
+/* Hacknotes:
+ *     If parsing changed so that no backup needed, could perhaps modify
+ *             to use a FILE input stream.  Need terminator, though.
+ *     Perhaps should return 0 on success, else a non-zero error val?
+ *     Flush AMPM from TM structure and handle locally within PARTIME,
+ *             like midnight/noon?
+ */
+
+#ifndef lint
+static char rcsid[]=
+"$Header: /usr/src/local/bin/rcs/src/RCS/partime.c,v 1.4 89/05/01 14:48:46 narten Exp $";
+#endif
+
+/* $Log:       partime.c,v $
+ * Revision 1.4  89/05/01  14:48:46  narten
+ * fixed #ifdef DEBUG construct
+ * 
+ * Revision 1.3  88/11/08  12:02:15  narten
+ * changes from  eggert@sm.unisys.com (Paul Eggert)
+ * 
+ * Revision 1.3  88/08/28  14:53:40  eggert
+ * Remove unportable "#endif XXX"s.
+ * 
+ * Revision 1.2  87/03/27  14:21:53  jenkins
+ * Port to suns
+ * 
+ * Revision 1.1  84/01/23  14:50:07  kcs
+ * Initial revision
+ * 
+ * Revision 1.1  82/05/06  11:38:26  wft
+ * Initial revision
+ * 
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include "time.h"
+
+#ifndef lint
+static char timeid[] = TIMEID;
+#endif
+
+struct tmwent {
+       char *went;
+       long wval;      /* must be big enough to hold pointer or integer */
+       char wflgs;
+       char wtype;
+};
+       /* wflgs */
+#define TWSPEC 01      /* Word wants special processing */
+#define TWTIME 02      /* Word is a time value (absence implies date) */
+#define TWDST  04      /* Word is a DST-type timezone */
+#define TW1200 010     /* Word is NOON or MIDNIGHT (sigh) */
+
+int pt12hack();
+int ptnoise();
+struct tmwent tmwords [] = {
+       {"january",      0, 0, TM_MON},
+       {"february",     1, 0, TM_MON},
+       {"march",        2, 0, TM_MON},
+       {"april",        3, 0, TM_MON},
+       {"may",          4, 0, TM_MON},
+       {"june",         5, 0, TM_MON},
+       {"july",         6, 0, TM_MON},
+       {"august",       7, 0, TM_MON},
+       {"september",    8, 0, TM_MON},
+       {"october",      9, 0, TM_MON},
+       {"november",     10, 0, TM_MON},
+       {"december",     11, 0, TM_MON},
+
+       {"sunday",       0, 0, TM_WDAY},
+       {"monday",       1, 0, TM_WDAY},
+       {"tuesday",      2, 0, TM_WDAY},
+       {"wednesday",    3, 0, TM_WDAY},
+       {"thursday",     4, 0, TM_WDAY},
+       {"friday",       5, 0, TM_WDAY},
+       {"saturday",     6, 0, TM_WDAY},
+
+       {"gmt",          0*60, TWTIME, TM_ZON},   /* Greenwich */
+       {"gst",          0*60, TWTIME, TM_ZON},
+       {"gdt",          0*60, TWTIME+TWDST, TM_ZON},     /* ?? */
+
+       {"ast",          4*60, TWTIME, TM_ZON},   /* Atlantic */
+       {"est",          5*60, TWTIME, TM_ZON},   /* Eastern */
+       {"cst",          6*60, TWTIME, TM_ZON},   /* Central */
+       {"mst",          7*60, TWTIME, TM_ZON},   /* Mountain */
+       {"pst",          8*60, TWTIME, TM_ZON},   /* Pacific */
+       {"yst",          9*60, TWTIME, TM_ZON},   /* Yukon */
+       {"hst",          10*60, TWTIME, TM_ZON},  /* Hawaii */
+       {"bst",          11*60, TWTIME, TM_ZON},  /* Bering */
+
+       {"adt",          4*60, TWTIME+TWDST, TM_ZON},     /* Atlantic */
+       {"edt",          5*60, TWTIME+TWDST, TM_ZON},     /* Eastern */
+       {"cdt",          6*60, TWTIME+TWDST, TM_ZON},     /* Central */
+       {"mdt",          7*60, TWTIME+TWDST, TM_ZON},     /* Mountain */
+       {"pdt",          8*60, TWTIME+TWDST, TM_ZON},     /* Pacific */
+       {"ydt",          9*60, TWTIME+TWDST, TM_ZON},     /* Yukon */
+       {"hdt",          10*60, TWTIME+TWDST, TM_ZON},    /* Hawaii */
+       {"bdt",          11*60, TWTIME+TWDST, TM_ZON},    /* Bering */
+
+       {"daylight",     1, TWTIME+TWDST, TM_ZON},        /* Local Daylight */
+       {"standard",     1, TWTIME, TM_ZON},      /* Local Standard */
+       {"std",          1, TWTIME, TM_ZON},      /*   "       "    */
+
+       {"am",           1, TWTIME, TM_AMPM},
+       {"pm",           2, TWTIME, TM_AMPM},
+       {"noon",         12,TWTIME+TW1200, 0},    /* Special frobs */
+       {"midnight",     0, TWTIME+TW1200, 0},
+       {"at",           (long)ptnoise, TWSPEC, 0},    /* Noise word */
+
+       {0, 0, 0, 0},             /* Zero entry to terminate searches */
+};
+
+#define TMWILD (-2)    /* Value meaning item specified as wild-card */
+                       /* (May use someday...) */
+
+struct token {
+       char *tcp;      /* pointer to string */
+       int tcnt;       /* # chars */
+       char tbrk;      /* "break" char */
+       char tbrkl;     /* last break char */
+       char tflg;      /* 0 = alpha, 1 = numeric */
+       union {         /* Resulting value; */
+               int tnum;/* either a #, or */
+               struct tmwent *ttmw;/* ptr to a tmwent. */
+       } tval;
+};
+
+partime(astr, atm)
+char *astr;
+struct tm *atm;
+{      register int *tp;
+       register struct tmwent *twp;
+       register int i;
+       struct token btoken, atoken;
+       char *cp, ch;
+       int ord, midnoon;
+       int (*aproc)();
+
+       tp = (int *)atm;
+       zaptime(tp);                     /* Initialize the TM structure */
+       midnoon = TMNULL;               /* and our own temp stuff */
+       btoken.tcnt = btoken.tbrkl = 0;
+       btoken.tcp = astr;
+
+domore:
+       if(!ptitoken(btoken.tcp+btoken.tcnt,&btoken))   /* Get a token */
+         {     if(btoken.tval.tnum) return(0);         /* Read error? */
+               if(midnoon != TMNULL)                   /* EOF, wrap up */
+                       return(pt12hack(tp, midnoon));
+               return(1);                              /* Win return! */
+         }
+       if(btoken.tflg == 0)            /* Alpha? */
+         {     twp = btoken.tval.ttmw;         /* Yes, get ptr to entry */
+               if(twp->wflgs&TWSPEC)           /* Special alpha crock */
+                 {     aproc = (int (*) ()) (twp->wval);
+                       if(!(*aproc)(tp, twp, &btoken))
+                               return(0);      /* ERR: special word err */
+                       goto domore;
+                 }
+               if(twp->wflgs&TW1200)
+                       if(ptstash(&midnoon,(int)twp->wval))
+                               return(0);      /* ERR: noon/midnite clash */
+                       else goto domore;
+               if(ptstash(&tp[twp->wtype],(int)twp->wval))
+                       return(0);              /* ERR: val already set */
+               if(twp->wtype == TM_ZON)        /* If was zone, hack DST */
+                       if(ptstash(&tp[TM_ISDST],(twp->wflgs&TWDST)))
+                               return(0);      /* ERR: DST conflict */
+               goto domore;
+         }
+
+       /* Token is number.  Lots of hairy heuristics. */
+       if(btoken.tcnt >= 7)    /* More than 6 digits in string? */
+               return(0);      /* ERR: number too big */
+       if(btoken.tcnt == 6)    /* 6 digits = HHMMSS.  Needs special crock */
+         {                     /* since 6 digits are too big for integer! */
+               i = (btoken.tcp[0]-'0')*10      /* Gobble 1st 2 digits */
+                  + btoken.tcp[1]-'0';
+               btoken.tcnt = 2;                /* re-read last 4 chars */
+               goto coltime;
+         }
+
+       i = btoken.tval.tnum;   /* Value now known to be valid; get it. */
+       if( btoken.tcnt == 5    /*  5 digits = HMMSS */
+        || btoken.tcnt == 3)   /*  3 digits = HMM   */
+         {     if(btoken.tcnt != 3)
+                       if(ptstash(&tp[TM_SEC], i%100))
+                               return(0);      /* ERR: sec conflict */
+                       else i /= 100;
+hhmm4:         if(ptstash(&tp[TM_MIN], i%100))
+                       return(0);              /* ERR: min conflict */
+               i /= 100;
+hh2:            if(ptstash(&tp[TM_HOUR], i))
+                       return(0);              /* ERR: hour conflict */
+               goto domore;
+         }
+
+       if(btoken.tcnt == 4)    /* 4 digits = YEAR or HHMM */
+         {     if(tp[TM_YEAR] != TMNULL) goto hhmm4;   /* Already got yr? */
+               if(tp[TM_HOUR] != TMNULL) goto year4;   /* Already got hr? */
+               if((i%100) > 59) goto year4;            /* MM >= 60? */
+               if(btoken.tbrk == ':')                  /* HHMM:SS ? */
+                       if( ptstash(&tp[TM_HOUR],i/100)
+                        || ptstash(&tp[TM_MIN], i%100))
+                               return(0);              /* ERR: hr/min clash */
+                       else goto coltm2;               /* Go handle SS */
+               if(btoken.tbrk != ',' && btoken.tbrk != '/'
+                 && ptitoken(btoken.tcp+btoken.tcnt,&atoken)   /* Peek */
+                 && atoken.tflg == 0                   /* alpha */
+                 && (atoken.tval.ttmw->wflgs&TWTIME))  /* HHMM-ZON */
+                       goto hhmm4;
+               if(btoken.tbrkl == '-'          /* DD-Mon-YYYY */
+                 || btoken.tbrkl == ','        /* Mon DD, YYYY */
+                 || btoken.tbrkl == '/'        /* MM/DD/YYYY */
+                 || btoken.tbrkl == '.'        /* DD.MM.YYYY */
+                 || btoken.tbrk == '-'         /* YYYY-MM-DD */
+                       ) goto year4;
+               goto hhmm4;                     /* Give up, assume HHMM. */
+         }
+
+       /* From this point on, assume tcnt == 1 or 2 */
+       /* 2 digits = YY, MM, DD, or HH (MM and SS caught at coltime) */
+       if(btoken.tbrk == ':')          /* HH:MM[:SS] */
+               goto coltime;           /*  must be part of time. */
+       if(i > 31) goto yy2;            /* If >= 32, only YY poss. */
+
+       /* Check for numerical-format date */
+       for (cp = "/-."; ch = *cp++;)
+         {     ord = (ch == '.' ? 0 : 1);      /* n/m = D/M or M/D */
+               if(btoken.tbrk == ch)                   /* "NN-" */
+                 {     if(btoken.tbrkl != ch)
+                         {     if(ptitoken(btoken.tcp+btoken.tcnt,&atoken)
+                                 && atoken.tflg == 0
+                                 && atoken.tval.ttmw->wtype == TM_MON)
+                                       goto dd2;
+                               if(ord)goto mm2; else goto dd2; /* "NN-" */
+                         }                             /* "-NN-" */
+                       if(tp[TM_DAY] == TMNULL
+                       && tp[TM_YEAR] != TMNULL)       /* If "YY-NN-" */
+                               goto mm2;               /* then always MM */
+                       if(ord)goto dd2; else goto mm2;
+                 }
+               if(btoken.tbrkl == ch                   /* "-NN" */
+                 && tp[ord ? TM_MON : TM_DAY] != TMNULL)
+                       if(tp[ord ? TM_DAY : TM_MON] == TMNULL) /* MM/DD */
+                               if(ord)goto dd2; else goto mm2;
+                       else goto yy2;                  /* "-YY" */
+         }
+
+       /* At this point only YY, DD, and HH are left.
+        * YY is very unlikely since value is <= 32 and there was
+        * no numerical format date.  Make one last try at YY
+        * before dropping through to DD vs HH code.
+        */
+       if(btoken.tcnt == 2             /* If 2 digits */
+         && tp[TM_HOUR] != TMNULL      /* and already have hour */
+         && tp[TM_DAY] != TMNULL       /* and day, but  */
+         && tp[TM_YEAR] == TMNULL)     /* no year, then assume */
+               goto yy2;               /* that's what we have. */
+
+       /* Now reduced to choice between HH and DD */
+       if(tp[TM_HOUR] != TMNULL) goto dd2;     /* Have hour? Assume day. */
+       if(tp[TM_DAY] != TMNULL) goto hh2;      /* Have day? Assume hour. */
+       if(i > 24) goto dd2;                    /* Impossible HH means DD */
+       if(!ptitoken(btoken.tcp+btoken.tcnt, &atoken))  /* Read ahead! */
+               if(atoken.tval.tnum) return(0); /* ERR: bad token */
+               else goto dd2;                  /* EOF, assume day. */
+       if( atoken.tflg == 0            /* If next token is an alpha */
+        && atoken.tval.ttmw->wflgs&TWTIME)  /* time-spec, assume hour */
+               goto hh2;               /* e.g. "3 PM", "11-EDT"  */
+
+dd2:   if(ptstash(&tp[TM_DAY],i))      /* Store day (1 based) */
+               return(0);
+       goto domore;
+
+mm2:   if(ptstash(&tp[TM_MON], i-1))   /* Store month (make zero based) */
+               return(0);
+       goto domore;
+
+yy2:   i += 1900;
+year4: if(ptstash(&tp[TM_YEAR],i))     /* Store year (full number) */
+               return(0);              /* ERR: year conflict */
+       goto domore;
+
+       /* Hack HH:MM[[:]SS] */
+coltime:
+       if(ptstash(&tp[TM_HOUR],i)) return(0);
+       if(!ptitoken(btoken.tcp+btoken.tcnt,&btoken))
+               return(!btoken.tval.tnum);
+       if(!btoken.tflg) return(0);     /* ERR: HH:<alpha> */
+       if(btoken.tcnt == 4)            /* MMSS */
+               if(ptstash(&tp[TM_MIN],btoken.tval.tnum/100)
+                 || ptstash(&tp[TM_SEC],btoken.tval.tnum%100))
+                       return(0);
+               else goto domore;
+       if(btoken.tcnt != 2
+         || ptstash(&tp[TM_MIN],btoken.tval.tnum))
+               return(0);              /* ERR: MM bad */
+       if(btoken.tbrk != ':') goto domore;     /* Seconds follow? */
+coltm2:        if(!ptitoken(btoken.tcp+btoken.tcnt,&btoken))
+               return(!btoken.tval.tnum);
+       if(!btoken.tflg || btoken.tcnt != 2     /* Verify SS */
+         || ptstash(&tp[TM_SEC], btoken.tval.tnum))
+               return(0);              /* ERR: SS bad */
+       goto domore;
+}
+
+/* Store date/time value, return 0 if successful.
+ * Fails if entry already set to a different value.
+ */
+ptstash(adr,val)
+int *adr;
+{      register int *a;
+       if( *(a=adr) != TMNULL)
+               return(*a != val);
+       *a = val;
+       return(0);
+}
+
+/* This subroutine is invoked for NOON or MIDNIGHT when wrapping up
+ * just prior to returning from partime.
+ */
+pt12hack(atp, aval)
+int *atp, aval;
+{      register int *tp, i, h;
+       tp = atp;
+       if (((i=tp[TM_MIN]) && i != TMNULL)     /* Ensure mins, secs */
+        || ((i=tp[TM_SEC]) && i != TMNULL))    /* are 0 or unspec'd */
+               return(0);                      /* ERR: MM:SS not 00:00 */
+       i = aval;                       /* Get 0 or 12 (midnite or noon) */
+       if ((h = tp[TM_HOUR]) == TMNULL /* If hour unspec'd, win */
+        || h == 12)                    /* or if 12:00 (matches either) */
+               tp[TM_HOUR] = i;        /* Then set time */
+       else if(!(i == 0                /* Nope, but if midnight and */
+               &&(h == 0 || h == 24))) /* time matches, can pass. */
+                       return(0);      /* ERR: HH conflicts */
+       tp[TM_AMPM] = TMNULL;           /* Always reset this value if won */
+       return(1);
+}
+
+/* Null routine for no-op tokens */
+
+ptnoise() { return(1); }
+
+/* Get a token and identify it to some degree.
+ * Returns 0 on failure; token.tval will be 0 for normal EOF, otherwise
+ * hit error of some sort
+ */
+
+ptitoken(astr, tkp)
+register struct token *tkp;
+char *astr;
+{
+       register char *cp;
+       register int i;
+
+       tkp->tval.tnum = 0;
+       if(pttoken(astr,tkp) == 0)
+#ifdef DEBUG
+           {
+               VOID printf("EOF\n");
+               return(0);
+           }
+#else
+               return(0);
+#endif 
+       cp = tkp->tcp;
+
+#ifdef DEBUG
+       i = cp[tkp->tcnt];
+       cp[tkp->tcnt] = 0;
+       VOID printf("Token: \"%s\" ",cp);
+       cp[tkp->tcnt] = i;
+#endif
+
+       if(tkp->tflg)
+               for(i = tkp->tcnt; i > 0; i--)
+                       tkp->tval.tnum = (int)tkp->tval.tnum*10 + ((*cp++)-'0');
+       else
+         {     i = ptmatchstr(cp, tkp->tcnt, tmwords);
+               tkp->tval.tnum = i ? i : -1;         /* Set -1 for error */
+
+#ifdef DEBUG
+               if(!i) VOID printf("Not found!\n");
+#endif
+
+               if(!i) return(0);
+         }
+
+#ifdef DEBUG
+       if(tkp->tflg)
+               VOID printf("Val: %d.\n",tkp->tval.tnum);
+       else VOID printf("Found: \"%s\", val: %d., type %d\n",
+               tkp->tval.ttmw->went,tkp->tval.ttmw->wval,tkp->tval.ttmw->wtype);
+#endif
+
+       return(1);
+}
+
+/* Read token from input string into token structure */
+pttoken(astr,tkp)
+register struct token *tkp;
+char *astr;
+{
+       register char *cp;
+       register int c;
+
+       tkp->tcp = cp = astr;
+       tkp->tbrkl = tkp->tbrk;         /* Set "last break" */
+       tkp->tcnt = tkp->tbrk = tkp->tflg = 0;
+
+       while(c = *cp++)
+         {     switch(c)
+                 {     case ' ': case '\t':    /* Flush all whitespace */
+                               while((c = *cp++) && isspace(c));
+                               cp--;           /* Drop thru to handle brk */
+                       case '(': case ')':     /* Perhaps any non-alphanum */
+                       case '-': case ',':     /* shd qualify as break? */
+                       case '/': case ':': case '.':   /* Break chars */
+                               if(tkp->tcnt == 0)      /* If no token yet */
+                                 {     tkp->tcp = cp;  /* ignore the brk */
+                                       tkp->tbrkl = c;
+                                       continue;       /* and go on. */
+                                 }
+                               tkp->tbrk = c;
+                               return(tkp->tcnt);
+                 }
+               if(tkp->tcnt == 0)              /* If first char of token, */
+                       tkp->tflg = isdigit(c); /*    determine type */
+               if(( isdigit(c) &&  tkp->tflg)  /* If not first, make sure */
+                ||(!isdigit(c) && !tkp->tflg)) /*    char matches type */
+                       tkp->tcnt++;            /* Win, add to token. */
+               else {
+                       cp--;                   /* Wrong type, back up */
+                       tkp->tbrk = c;
+                       return(tkp->tcnt);
+                 }
+         }
+       return(tkp->tcnt);              /* When hit EOF */
+}
+
+
+ptmatchstr(astr,cnt,astruc)
+char *astr;
+int cnt;
+struct tmwent *astruc;
+{      register char *cp, *mp;
+       register int c;
+       struct tmwent *lastptr;
+       struct integ { int word; };   /* For getting at array ptr */
+       int i;
+
+       lastptr = 0;
+       for(;mp = (char *)((struct integ *)astruc)->word; astruc += 1)
+         {     cp = astr;
+               for(i = cnt; i > 0; i--)
+                 {     switch((c = *cp++) ^ *mp++)     /* XOR the chars */
+                         {     case 0: continue;       /* Exact match */
+                               case 040: if(isalpha(c))
+                                       continue;
+                         }
+                       break;
+                 }
+               if(i==0)
+                       if(*mp == 0) return((unsigned int)astruc);    /* Exact match */
+                       else if(lastptr) return(0);     /* Ambiguous */
+                       else lastptr = astruc;          /* 1st ambig */
+         }
+       return((unsigned int)lastptr);
+}
+
+
+
+zaptime(tp)
+register int *tp;
+/* clears tm structure pointed to by tp */
+{      register int i;
+       i = (sizeof (struct tm))/(sizeof (int));
+       do *tp++ = TMNULL;              /* Set entry to "unspecified" */
+       while(--i);                     /* Faster than FOR */
+}