Article View: netcom.programmers
Article #1Re: wanted: public domain strptime(3)
From: D. Jason Penney
Date: Tue, 17 May 1994 16:45
Date: Tue, 17 May 1994 16:45
1296 lines
41113 bytes
41113 bytes
Eric De Mund <ead@netcom.com> writes:
>Good People,
>Can anyone point me towards a public domain strptime(3), a la SunOS's
>strptime(3)? [1]
>Thanks,
>Eric De Mund <ead@netcom.com>
The date/time parsing used by RCS is not perfect, but a pretty decent
hack for most of us. I include my mangled version.
--j
----[cut here]
#! /bin/sh
# This is a shell archive. Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file". To overwrite existing
# files, type "sh file -c". You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g.. If this archive is complete, you
# will see the following message at the end:
# "End of shell archive."
# Contents: partime.c global.ht partime.hf
# Wrapped by jason@denim on Tue May 17 09:53:04 1994
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'partime.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'partime.c'\"
else
echo shar: Extracting \"'partime.c'\" \(34282 characters\)
sed "s/^X//" >'partime.c' <<'END_OF_FILE'
X#define PARTIME_C TRUE
X/*========================================================================
X * Name - partime.c (stolen from RCS 5.5 sources)
X *
X * $Id: partime.c,v 35.7 1994/04/30 08:52:01 darrel Exp $
X *
X * $Log: partime.c,v $
X *
X * (ARPANET: KLH @ SRI)
X */
X
X/* Hacknotes:
X * If parsing changed so that no backup needed, could perhaps modify
X * to use a FILE input stream. Need terminator, though.
X * Perhaps should return 0 on success, else a non-zero error val?
X */
X
X/*
X *
X * Revision 5.4 1990/10/04 06:30:15 eggert
X * Remove date vs time heuristics that fail between 2000 and 2400.
X * Check for overflow when lexing an integer.
X * Parse 'Jan 10 LT' as 'Jan 10, LT', not 'Jan, 10 LT'.
X *
X * Revision 5.3 1990/09/24 18:56:31 eggert
X * Update timezones.
X *
X * Revision 5.2 1990/09/04 08:02:16 eggert
X * Don't parse two-digit years, because it won't work after 1999/12/31.
X * Don't permit 'Aug Aug'.
X *
X * Revision 5.1 1990/08/29 07:13:49 eggert
X * Be able to parse our own date format. Don't assume year<10000.
X *
X * Revision 5.0 1990/08/22 08:12:40 eggert
X * Switch to GMT and fix the bugs exposed thereby. Update timezones.
X * Ansify and Posixate. Fix peekahead and int-size bugs.
X *
X * Revision 1.4 89/05/01 14:48:46 narten
X * fixed #ifdef DEBUG construct
X *
X * Revision 1.3 88/08/28 14:53:40 eggert
X * Remove unportable "#endif XXX"s.
X *
X * Revision 1.2 87/03/27 14:21:53 jenkins
X * Port to suns
X *
X * Revision 1.1 82/05/06 11:38:26 wft
X * Initial revision
X *
X */
X
X#include "global.ht"
X
X#include "partime.hf"
X
X/* no more includes. KEEP THIS LINE */
X
X#define TZ_OFFSET (24*60) /* PARTIME_TMNULL < zone_offset - TZ_OFFSET */
X
Xstruct tmwent {
X const char *went;
X short wval;
X char wflgs;
X char wtype;
X };
X
X/* wflgs */
X#define TWTIME 02 /* Word is a time value (absence implies date) */
X#define TWDST 04 /* Word is a DST-type timezone */
X /* wtype */
X#define TM_MON 1 /* month name */
X#define TM_WDAY 2 /* weekday name */
X#define TM_ZON 3 /* time zone name */
X#define TM_LT 4 /* local time */
X#define TM_DST 5 /* daylight savings time */
X#define TM_12 6 /* AM, PM, NOON, or MIDNIGHT */
X /* wval (for wtype==TM_12) */
X#define T12_AM 1
X#define T12_PM 2
X#define T12_NOON 12
X#define T12_MIDNIGHT 0
X
Xstatic const struct tmwent tmwords [] = {
X {"january", 0, 0, TM_MON},
X {"february", 1, 0, TM_MON},
X {"march", 2, 0, TM_MON},
X {"april", 3, 0, TM_MON},
X {"may", 4, 0, TM_MON},
X {"june", 5, 0, TM_MON},
X {"july", 6, 0, TM_MON},
X {"august", 7, 0, TM_MON},
X {"september", 8, 0, TM_MON},
X {"october", 9, 0, TM_MON},
X {"november", 10, 0, TM_MON},
X {"december", 11, 0, TM_MON},
X
X {"sunday", 0, 0, TM_WDAY},
X {"monday", 1, 0, TM_WDAY},
X {"tuesday", 2, 0, TM_WDAY},
X {"wednesday", 3, 0, TM_WDAY},
X {"thursday", 4, 0, TM_WDAY},
X {"friday", 5, 0, TM_WDAY},
X {"saturday", 6, 0, TM_WDAY},
X
X {"gmt", 0*60, TWTIME, TM_ZON}, /* Greenwich */
X {"utc", 0*60, TWTIME, TM_ZON},
X {"ut", 0*60, TWTIME, TM_ZON},
X
X {"nzst", -12*60, TWTIME, TM_ZON}, /* New Zealand */
X {"jst", -9*60, TWTIME, TM_ZON}, /* Japan */
X {"kst", -9*60, TWTIME, TM_ZON}, /* Korea */
X {"ist", -5*60-30, TWTIME, TM_ZON},/* India */
X {"eet", -2*60, TWTIME, TM_ZON}, /* Eastern Europe */
X {"cet", -1*60, TWTIME, TM_ZON}, /* Central Europe */
X {"met", -1*60, TWTIME, TM_ZON}, /* Middle Europe */
X {"wet", 0*60, TWTIME, TM_ZON}, /* Western Europe */
X {"nst", 3*60+30, TWTIME, TM_ZON},/* Newfoundland */
X {"ast", 4*60, TWTIME, TM_ZON}, /* Atlantic */
X {"est", 5*60, TWTIME, TM_ZON}, /* Eastern */
X {"cst", 6*60, TWTIME, TM_ZON}, /* Central */
X {"mst", 7*60, TWTIME, TM_ZON}, /* Mountain */
X {"pst", 8*60, TWTIME, TM_ZON}, /* Pacific */
X {"akst", 9*60, TWTIME, TM_ZON}, /* Alaska */
X {"hast", 10*60, TWTIME, TM_ZON}, /* Hawaii-Aleutian */
X {"hst", 10*60, TWTIME, TM_ZON}, /* Hawaii */
X {"sst", 11*60, TWTIME, TM_ZON}, /* Samoa */
X
X {"nzdt", -12*60, TWTIME+TWDST, TM_ZON}, /* New Zealand */
X {"kdt", -9*60, TWTIME+TWDST, TM_ZON}, /* Korea */
X {"bst", 0*60, TWTIME+TWDST, TM_ZON}, /* Britain */
X {"ndt", 2*60+30, TWTIME+TWDST, TM_ZON}, /*Newfoundland (DDST)*/
X {"adt", 4*60, TWTIME+TWDST, TM_ZON}, /* Atlantic */
X {"edt", 5*60, TWTIME+TWDST, TM_ZON}, /* Eastern */
X {"cdt", 6*60, TWTIME+TWDST, TM_ZON}, /* Central */
X {"mdt", 7*60, TWTIME+TWDST, TM_ZON}, /* Mountain */
X {"pdt", 8*60, TWTIME+TWDST, TM_ZON}, /* Pacific */
X {"akdt", 9*60, TWTIME+TWDST, TM_ZON}, /* Alaska */
X {"hadt", 10*60, TWTIME+TWDST, TM_ZON}, /* Hawaii-Aleutian */
X
X#if 0
X /*
X * The following names are duplicates or are not well attested.
X * A standard is needed.
X */
X {"?st", -13*60, TWTIME, TM_ZON}, /* Uelen */
X {"?st", -11*60, TWTIME, TM_ZON}, /* Magadan */
X {"east", -10*60, TWTIME, TM_ZON}, /* Eastern Australia */
X {"cast", -9*60-30, TWTIME, TM_ZON},/* Central Australia */
X {"cst", -8*60, TWTIME, TM_ZON}, /* China */
X {"hkt", -8*60, TWTIME, TM_ZON}, /* Hong Kong */
X {"sst", -8*60, TWTIME, TM_ZON}, /* Singapore */
X {"wast", -8*60, TWTIME, TM_ZON}, /* Western Australia */
X {"?st", -7*60, TWTIME, TM_ZON}, /* Novosibirsk */
X {"jt", -7*60-30, TWTIME, TM_ZON},/* Java */
X {"nst", -6*60-30, TWTIME, TM_ZON},/* North Sumatra */
X {"?st", -6*60, TWTIME, TM_ZON}, /* Tashkent */
X {"?st", -5*60, TWTIME, TM_ZON}, /* Sverdlovsk */
X {"?", -4*60-30, TWTIME, TM_ZON},/* Afghanistan */
X {"?st", -4*60, TWTIME, TM_ZON}, /* Rostov */
X {"it", -3*60-30, TWTIME, TM_ZON},/* Iran */
X {"?st", -3*60, TWTIME, TM_ZON}, /* Moscow */
X {"ist", -2*60, TWTIME, TM_ZON}, /* Israel */
X {"ast", 1*60, TWTIME, TM_ZON}, /* Azores */
X {"fst", 2*60, TWTIME, TM_ZON}, /* Fernando de Noronha */
X {"bst", 3*60, TWTIME, TM_ZON}, /* Brazil */
X {"wst", 4*60, TWTIME, TM_ZON}, /* Western Brazil */
X {"ast", 5*60, TWTIME, TM_ZON}, /* Acre Brazil */
X {"?", 9*60+30, TWTIME, TM_ZON},/* Marquesas */
X {"?st", 12*60, TWTIME, TM_ZON}, /* Kwajalein */
X
X {"?dt", -13*60, TWTIME+TWDST, TM_ZON}, /* Uelen */
X {"?dt", -11*60, TWTIME+TWDST, TM_ZON}, /* Magadan */
X {"eadt", -10*60, TWTIME+TWDST, TM_ZON}, /* Eastern Australia */
X {"cadt", -9*60-30, TWTIME+TWDST, TM_ZON}, /* Central Australia */
X {"cdt", -8*60, TWTIME+TWDST, TM_ZON}, /* China */
X {"wadt", -8*60, TWTIME+TWDST, TM_ZON}, /* Western Australia */
X {"?dt", -7*60, TWTIME+TWDST, TM_ZON}, /* Novosibirsk */
X {"?dt", -6*60, TWTIME+TWDST, TM_ZON}, /* Tashkent */
X {"?dt", -5*60, TWTIME+TWDST, TM_ZON}, /* Sverdlovsk */
X {"?dt", -4*60, TWTIME+TWDST, TM_ZON}, /* Rostov */
X {"?dt", -3*60, TWTIME+TWDST, TM_ZON}, /* Moscow */
X {"idt", -2*60, TWTIME+TWDST, TM_ZON}, /* Israel */
X {"eest", -2*60, TWTIME+TWDST, TM_ZON}, /* Eastern Europe */
X {"cest", -1*60, TWTIME+TWDST, TM_ZON}, /* Central Europe */
X {"mest", -1*60, TWTIME+TWDST, TM_ZON}, /* Middle Europe */
X {"west", 0*60, TWTIME+TWDST, TM_ZON}, /* Western Europe */
X {"adt", 1*60, TWTIME+TWDST, TM_ZON}, /* Azores */
X {"fdt", 2*60, TWTIME+TWDST, TM_ZON}, /* Fernando de Noronha */
X {"edt", 3*60, TWTIME+TWDST, TM_ZON}, /* Eastern Brazil */
X {"wdt", 4*60, TWTIME+TWDST, TM_ZON}, /* Western Brazil */
X {"adt", 5*60, TWTIME+TWDST, TM_ZON}, /* Acre Brazil */
X#endif
X
X {"lt", 0, TWTIME, TM_LT}, /* local time */
X {"dst", 1*60, TWTIME, TM_DST}, /* daylight savings time */
X {"ddst", 2*60, TWTIME, TM_DST}, /* double dst */
X
X {"am", T12_AM, TWTIME, TM_12},
X {"pm", T12_PM, TWTIME, TM_12},
X {"noon", T12_NOON, TWTIME, TM_12},
X {"midnight", T12_MIDNIGHT, TWTIME, TM_12},
X
X {0, 0, 0, 0}, /* Zero entry to terminate searches */
X};
X
Xstruct token {
X const char *tokStart;/* pointer to string */
X int tokSize; /* # chars */
X char nextDelim; /* "break" char */
X char prevDelim; /* last break char */
X BoolType isNumeric;/* 0 = alpha, 1 = numeric */
X union { /* Resulting value; */
X int tnum; /* either a #, or */
X const struct tmwent *ttmw;/* ptr to a tmwent. */
X } tval;
X };
X
Xstatic const struct tmwent *ptmatchstr(
X#if defined(FLG_PROTOTYPE)
X const char *astr, int cnt, const struct tmwent *astruc
X#endif
X );
X
Xstatic BoolType pt12hack(
X#if defined(FLG_PROTOTYPE)
X struct tm *,int
X#endif
X );
X
Xstatic BoolType ptitoken(
X#if defined(FLG_PROTOTYPE)
X register struct token *tkp
X#endif
X );
X
Xstatic BoolType ptstash(
X#if defined(FLG_PROTOTYPE)
X int *adr, int val
X#endif
X );
X
Xstatic int pttoken(
X#if defined(FLG_PROTOTYPE)
X register struct token *tkp
X#endif
X );
X
X#if defined(FLG_PROTOTYPE)
Xstatic BoolType goodzone(register const struct token *t, int offset, int *am)
X#else
Xstatic BoolType goodzone(t, offset, am)
X register const struct token *t;
X int offset;
X int *am;
X#endif
X{
X register int m;
X
X if (
X t->isNumeric &&
X t->tokSize == 4+offset &&
X (m = t->tval.tnum) <= 2400 &&
X isdigit(t->tokStart[offset]) &&
X (m %= 100) < 60 ) {
X m += t->tval.tnum / 100 * 60;
X if (t->tokStart[offset - 1] == '+')
X m = -m;
X *am = m;
X return TRUE;
X }
X return FALSE;
X}
X
X#if defined(FLG_PROTOTYPE)
XBoolType ParseTime(const char *astr, register struct tm *atm, int *zone)
X#else
XBoolType ParseTime(astr, atm, zone)
Xconst char *astr;
Xregister struct tm *atm;
Xint *zone;
X#endif
X{
X register int i;
X struct token curToken, nextToken;
X int zone_offset; /* minutes west of GMT, plus TZ_OFFSET */
X register const char *cp;
X register char ch;
X BoolType numericMonth = TRUE;
X int midnoon;
X int *atmfield, dst, m;
X BoolType got1 = FALSE;
X BoolType result = FALSE;
X
X *zone = MAKETIME_LOCALZONE;
X
X atm->tm_sec = PARTIME_TMNULL;
X atm->tm_min = PARTIME_TMNULL;
X atm->tm_hour = PARTIME_TMNULL;
X atm->tm_mday = PARTIME_TMNULL;
X atm->tm_mon = PARTIME_TMNULL;
X atm->tm_year = PARTIME_TMNULL;
X atm->tm_wday = PARTIME_TMNULL;
X atm->tm_yday = PARTIME_TMNULL;
X midnoon = PARTIME_TMNULL; /* and our own temp stuff */
X zone_offset = PARTIME_TMNULL;
X dst = PARTIME_TMNULL;
X curToken.tokSize = 0;
X curToken.nextDelim = '\0';
X curToken.tokStart = astr;
X
X for (;; got1 = TRUE) {
X if (!ptitoken(&curToken)) { /* Get a token */
X if (curToken.tval.tnum)
X goto LABEL_DONE; /* Read error? */
X if (MAKETIME_GIVEN(midnoon)) /* EOF, wrap up */
X if (!pt12hack(atm, midnoon))
X goto LABEL_DONE;
X if (!MAKETIME_GIVEN(atm->tm_min))
X atm->tm_min = 0;
X result = got1;
X goto LABEL_DONE;
X }
X if (curToken.isNumeric == FALSE) { /* Alpha? */
X i = curToken.tval.ttmw->wval;
X switch (curToken.tval.ttmw->wtype) {
X default:
X goto LABEL_DONE;
X
X case TM_MON:
X numericMonth = FALSE;
X atmfield = &atm->tm_mon;
X break;
X
X case TM_WDAY:
X atmfield = &atm->tm_wday;
X break;
X
X case TM_DST:
X atmfield = &dst;
X break;
X
X case TM_LT:
X if (ptstash(&dst, 0))
X goto LABEL_DONE;
X i = MAKETIME_LOCALZONE;
X /* fall into */
X#if defined(FLG_CSET2)
X goto FALL_THROUGH;
X#endif
X
X case TM_ZON:
X#if defined(FLG_CSET2)
XFALL_THROUGH:
X#endif
X i += TZ_OFFSET;
X if (curToken.tval.ttmw->wflgs & TWDST)
X if (ptstash(&dst, 60))
X goto LABEL_DONE;
X /* Peek ahead for offset immediately afterwards. */
X if (
X (curToken.nextDelim == '-' || curToken.nextDelim == '+') &&
X (nextToken=curToken, ++nextToken.tokSize, ptitoken(&nextToken)) &&
X goodzone(&nextToken, 0, &m)) {
X i += m;
X curToken = nextToken;
X }
X atmfield = &zone_offset;
X break;
X
X case TM_12:
X atmfield = &midnoon;
X }
X if (ptstash(atmfield, i))
X goto LABEL_DONE; /* ERR: val already set */
X continue;
X }
X
X /* Token is number. Lots of hairy heuristics. */
X if (!isdigit(*curToken.tokStart)) {
X if (!goodzone(&curToken, 1, &m))
X goto LABEL_DONE;
X zone_offset = TZ_OFFSET + m;
X continue;
X }
X
X i = curToken.tval.tnum; /* Value now known to be valid; get it. */
X if (curToken.tokSize == 3) { /* 3 digits = HMM */
Xhhmm4:
X if (ptstash(&atm->tm_min, i % 100))
X goto LABEL_DONE; /* ERR: min conflict */
X i /= 100;
Xhh2:
X if (ptstash(&atm->tm_hour, i))
X goto LABEL_DONE; /* ERR: hour conflict */
X continue;
X }
X
X if (4 < curToken.tokSize)
X goto year4; /* far in the future */
X if (curToken.tokSize == 4) { /* 4 digits = YEAR or HHMM */
X if (MAKETIME_GIVEN(atm->tm_year))
X goto hhmm4; /* Already got yr? */
X if (MAKETIME_GIVEN(atm->tm_hour))
X goto year4; /* Already got hr? */
X if (curToken.nextDelim == ':') /* HHMM:SS ? */
X if (ptstash(&atm->tm_hour, i / 100)
X || ptstash(&atm->tm_min, i % 100))
X goto LABEL_DONE; /* ERR: hr/min clash */
X else
X goto coltm2; /* Go handle SS */
X if (curToken.nextDelim != ',' && curToken.nextDelim != '/'
X && (nextToken=curToken, ptitoken(&nextToken)) /* Peek */
X && ( nextToken.isNumeric
X ? !isdigit(*nextToken.tokStart)
X : nextToken.tval.ttmw->wflgs & TWTIME)) /* HHMM-ZON */
X goto hhmm4;
X goto year4; /* Give up, assume year. */
X }
X
X /* From this point on, assume tokSize == 1 or 2 */
X /* 2 digits = MM, DD, or HH (MM and SS caught at coltime) */
X if (curToken.nextDelim == ':') /* HH:MM[:SS] */
X goto coltime; /* must be part of time. */
X if (31 < i)
X goto LABEL_DONE;
X
X /* Check for numerical-format date */
X for (cp = "/-."; (ch = *cp++) != 0;) {
X if (curToken.nextDelim == ch) { /* "NN-" */
X if (curToken.prevDelim != ch) {
X nextToken = curToken;
X nextToken.tokSize++;
X if (ptitoken(&nextToken) /* peek */
X && nextToken.isNumeric == FALSE
X && nextToken.tval.ttmw->wtype == TM_MON) {
X numericMonth = FALSE;
X goto dd2;
X }
X goto monthOrDay;
X } /* "NN-" */
X /* "-NN-" */
X if (!MAKETIME_GIVEN(atm->tm_mday) && MAKETIME_GIVEN(atm->tm_year)) {
X /* If "YYYY-NN-" then always MM */
X numericMonth = FALSE;
X goto mm2;
X }
X goto monthOrDay;
X }
X if (curToken.prevDelim == ch) /* "-NN" */
X goto monthOrDay;
X }
X
X /* Now reduced to choice between HH and DD */
X if (MAKETIME_GIVEN(atm->tm_hour))
X goto dd2; /* Have hour? Assume day. */
X if (MAKETIME_GIVEN(atm->tm_mday))
X goto hh2; /* Have day? Assume hour. */
X if (MAKETIME_GIVEN(atm->tm_mon))
X goto dd2; /* Have month? Assume day. */
X if (i > 24)
X goto dd2; /* Impossible HH means DD */
X nextToken = curToken;
X if (!ptitoken(&nextToken)) /* Read ahead! */
X if (nextToken.tval.tnum)
X goto LABEL_DONE; /* ERR: bad token */
X else
X goto dd2; /* EOF, assume day. */
X if ( nextToken.isNumeric ? !isdigit(*nextToken.tokStart)
X : nextToken.tval.ttmw->wflgs & TWTIME)
X /* If next token is a time spec, assume hour */
X goto hh2; /* e.g. "3 PM", "11-EDT" */
X
Xdd2:
X if (ptstash(&atm->tm_mday, i)) /* Store day (1 based) */
X goto LABEL_DONE;
X continue;
X /* NOTREACHED */
X
Xmm2:
X if (ptstash(&atm->tm_mon, i-1)) /* Store month (make zero based) */
X goto LABEL_DONE;
X continue;
X /* NOTREACHED */
X
XmonthOrDay:
X /* we have 2 digit numeral. Could be either day or month... */
X if (!numericMonth)
X goto dd2; /* current int can't be a month. */
X if (!MAKETIME_GIVEN(atm->tm_mday))
X goto dd2; /* shove numeral into day slot; we'll fix it later if needed */
X if (MAKETIME_GIVEN(atm->tm_mon))
X goto LABEL_DONE; /* overspecified */
X
X /* we have atm->tm_mday versus i. What's right? */
X if (i > 12) { /* we clearly have a day; tm_mday is really a month */
X if (atm->tm_mday > 12)
X goto LABEL_DONE; /* two days specified? yucch. */
X /* we assumed another numeral was a day, but it was a month.
X back it out. */
X atm->tm_mon = atm->tm_mday - 1; /* zero-bias */
X atm->tm_mday = PARTIME_TMNULL;
X goto dd2;
X }
X else
X goto mm2;
X /* NOTREACHED */
X
Xyear4:
X if ((i-=1900) < 0 || ptstash(&atm->tm_year, i)) /* Store year-1900 */
X goto LABEL_DONE; /* ERR: year conflict */
X continue;
X
X /* Hack HH:MM[[:]SS] */
Xcoltime:
X if (ptstash(&atm->tm_hour, i))
X goto LABEL_DONE;
X if (!ptitoken(&curToken)) {
X result = !curToken.tval.tnum;
X goto LABEL_DONE;
X }
X if (!curToken.isNumeric)
X goto LABEL_DONE; /* ERR: HH:<alpha> */
X if (curToken.tokSize == 4) /* MMSS */
X if (ptstash(&atm->tm_min, curToken.tval.tnum / 100)
X || ptstash(&atm->tm_sec, curToken.tval.tnum%100))
X goto LABEL_DONE;
X else
X continue;
X if (curToken.tokSize != 2 || ptstash(&atm->tm_min, curToken.tval.tnum))
X goto LABEL_DONE; /* ERR: MM bad */
X if (curToken.nextDelim != ':')
X continue; /* Seconds follow? */
X
Xcoltm2:
X if (!ptitoken(&curToken)) {
X result = !curToken.tval.tnum;
X goto LABEL_DONE;
X }
X if (!curToken.isNumeric || curToken.tokSize != 2 /* Verify SS */
X || ptstash(&atm->tm_sec, curToken.tval.tnum))
X goto LABEL_DONE; /* ERR: SS bad */
X }
X
XLABEL_DONE:
X if (result) {
X if (!MAKETIME_GIVEN(zone_offset))
X *zone = MAKETIME_LOCALZONE;
X else {
X *zone = zone_offset - TZ_OFFSET;
X if (MAKETIME_GIVEN(dst))
X *zone -= dst;
X }
X }
X return result;
X}
X
X/* Store date/time value, return 0 if successful.
X * Fail if entry is already set.
X */
X#if defined(FLG_PROTOTYPE)
Xstatic BoolType ptstash(int *adr, int val)
X#else
Xstatic BoolType ptstash(adr,val)
Xint *adr;
Xint val;
X#endif
X{
X register int *a;
X
X if (MAKETIME_GIVEN(*(a=adr)))
X return TRUE;
X *a = val;
X return FALSE;
X}
X
X/* This subroutine is invoked for AM, PM, NOON and MIDNIGHT when wrapping up
X * just prior to returning from partime.
X */
X
X#if defined(FLG_PROTOTYPE)
Xstatic BoolType pt12hack(register struct tm *tm, register int aval)
X#else
Xstatic BoolType pt12hack(tm, aval)
Xregister struct tm *tm;
Xregister int aval;
X#endif
X{
X register int h = tm->tm_hour;
X
X switch (aval) {
X case T12_AM:
X case T12_PM:
X if (h > 12)
X return FALSE;
X if (h == 12)
X tm->tm_hour = 0;
X if (aval == T12_PM)
X tm->tm_hour += 12;
X break;
X
X default:
X if (0 < tm->tm_min || 0 < tm->tm_sec)
X return FALSE;
X if (!MAKETIME_GIVEN(h) || h==12)
X tm->tm_hour = aval;
X else
X if (aval==T12_MIDNIGHT && (h==0 || h==24))
X return FALSE;
X }
X return TRUE;
X}
X
X/* Get a token and identify it to some degree.
X * Returns 0 on failure; token.tval will be 0 for normal EOF, otherwise
X * hit error of some sort
X */
X
X#if defined(FLG_PROTOTYPE)
Xstatic BoolType ptitoken(register struct token *tkp)
X#else
Xstatic BoolType ptitoken(tkp)
Xregister struct token *tkp;
X#endif
X{
X register const char *cp;
X register int i, j, k;
X
X if (pttoken(tkp) == 0) {
X#ifdef TRACING
X printf("EOF from pttoken\n");
X#endif
X return FALSE;
X }
X cp = tkp->tokStart;
X
X#ifdef TRACING
X VOID printf("Token: \"%.*s\" ", tkp->tokSize, cp);
X#endif
X
X if (tkp->isNumeric) {
X i = tkp->tokSize;
X if (*cp == '+' || *cp == '-') {
X cp++;
X i--;
X }
X while (0 <= --i) {
X j = tkp->tval.tnum*10;
X k = j + (*cp++ - '0');
X if (j / 10 != tkp->tval.tnum || k < j) { /* arithmetic overflow */
X tkp->tval.tnum = 1;
X return FALSE;
X }
X tkp->tval.tnum = k;
X }
X }
X else
X if (!(tkp->tval.ttmw = ptmatchstr(cp, tkp->tokSize, tmwords))) {
X#ifdef TRACING
X printf("Not found!\n");
X#endif
X tkp->tval.tnum = 1;
X return FALSE;
X }
X
X#ifdef TRACING
X if (tkp->isNumeric)
X printf("Val: %d.\n",tkp->tval.tnum);
X else
X printf("Found: \"%s\", val: %d, type %d\n",
X tkp->tval.ttmw->went,tkp->tval.ttmw->wval,tkp->tval.ttmw->wtype);
X#endif
X
X return TRUE;
X}
X
X/* Read token from input string into token structure */
X#if defined(FLG_PROTOTYPE)
Xstatic int pttoken(register struct token *tkp)
X#else
Xstatic int pttoken(tkp)
Xregister struct token *tkp;
X#endif
X{
X register const char *cp;
X register int c;
X const char *astr;
X
X tkp->tokStart = astr = cp = tkp->tokStart + tkp->tokSize;
X tkp->prevDelim = tkp->nextDelim; /* Set "last break" */
X tkp->tokSize = tkp->nextDelim = 0;
X tkp->isNumeric = FALSE;
X tkp->tval.tnum = 0;
X
X while((c = *cp++) != 0) {
X switch(c) {
X case ' ':
X case '\t': /* Flush all whitespace */
X case '\r':
X case '\n':
X case '\v':
X case '\f':
X if (!tkp->tokSize) { /* If no token yet */
X tkp->tokStart = cp; /* ignore the brk */
X continue; /* and go on. */
X }
X /* fall into */
X#if defined(FLG_CSET2)
X goto FALL_THROUGH;
X#endif
X
X case '(':
X case ')': /* Perhaps any non-alphanum */
X case '-':
X case ',': /* shd qualify as break? */
X case '+':
X case '/':
X case ':':
X case '.': /* Break chars */
X#if defined(FLG_CSET2)
XFALL_THROUGH:
X#endif
X if (tkp->tokSize == 0) { /* If no token yet */
X tkp->tokStart = cp; /* ignore the brk */
X tkp->prevDelim = (char)c;
X continue; /* and go on. */
X }
X tkp->nextDelim = (char)c;
X return tkp->tokSize;
X }
X if (!tkp->tokSize++) { /* If first char of token, */
X if (isdigit(c)) {
X tkp->isNumeric = TRUE;
X if (astr < cp - 2 && (cp[-2] == '-' || cp[-2] == '+')) {
X /* timezone is break+sign+digit */
X tkp->tokStart--;
X tkp->tokSize++;
X }
X }
X }
X else
X if ((isdigit(c) != 0) != tkp->isNumeric) { /* else check type */
X tkp->nextDelim = (char)c;
X return --tkp->tokSize; /* Wrong type, back up */
X }
X }
X return tkp->tokSize; /* When hit EOF */
X}
X
X
X#if defined(FLG_PROTOTYPE)
Xstatic const struct tmwent *ptmatchstr(const char *astr, int cnt,
X const struct tmwent *astruc)
X#else
Xstatic const struct tmwent *ptmatchstr(astr,cnt,astruc)
X const char *astr;
X int cnt;
X const struct tmwent *astruc;
X#endif
X{
X register const char *cp, *mp;
X register int c;
X const struct tmwent *lastptr;
X int i;
X
X lastptr = 0;
X for (;(mp = astruc->went) != 0; astruc += 1) {
X cp = astr;
X for (i = cnt; i > 0; i--) {
X c = *mp++;
X switch (*cp++ - c) {
X case 0:
X continue; /* Exact match */
X case 'A'-'a':
X if (isalpha(c))
X continue;
X }
X break;
X }
X if (i == 0)
X if (*mp == '\0')
X return astruc; /* Exact match */
X else
X if (lastptr)
X return NULL; /* Ambiguous */
X else
X lastptr = astruc; /* 1st ambig */
X }
X return lastptr;
X}
X
X/*========================================================================
X * Semi-public software; should only be used in gemcopy!
X *
X * Name - maketime.c (stolen from RCS 5.5 sources)
X *
X * $Id: partime.c,v 35.7 1994/04/30 08:52:01 darrel Exp $
X *
X * $Log: partime.c,v $
X * Revision 35.7 1994/04/30 08:52:01 darrel
X * Added "cheat" line for quick dependency analysis.
X *
X * Revision 35.6 1994/04/06 17:47:12 otisa
X * lint fixes for OS/2
X *
X * Revision 35.5 1994/02/10 00:56:59 darrel
X * 1. BoolType typedef changed from 'unsigned char' to 'int'.
X * 2. Added NRS modifiers #netldi, #dir, and #log.
X * 3. GcGem's and SharedPCmonitors no longer need a netldi to start them.
X * They will be started by either the stone or pgsvr.
X *
X * Revision 35.4 1993/12/31 00:04:51 darrel
X * purged the rcs log comment.
X *
X * Revision 35.3 1993/08/27 02:14:55 otisa
X * merge fixes from 3.2
X *
X * Revision 35.2 1993/08/03 00:01:17 garyh
X * Changes for Sequent and Solaris.
X *
X * Revision 35.1.1.2 1993/07/23 20:23:17 garyh
X * Bringing up to date per 23 July
X *
X * Revision 3.2.1.6 1993/08/08 23:48:50 penneyj
X * Fix Sequent and Solaris problems. Make coredump more cautious.
X *
X * Revision 3.2.1.5 1993/07/30 01:43:55 darrel
X * 1. backend will now compile cleanly with gcc 2.4.5 and SunOs 4.1
X * 2. added netldi "guest mode" and "captive account mode"
X * 3. Introduced the typedef ArraySizeType that should be used for
X * variables that represent the size of an array.
X *
X * Revision 3.2.1.4 1993/06/28 18:09:39 darrel
X * Commit of Windows NT changes.
X *
X * Revision 3.2.1.3 1993/03/16 06:37:55 penneyj
X * Change includes to double quotes (for the Macintosh)
X * Revived Ultrix-32 (DECstation) port. Removed warnings from DYNIX port.
X *
X * Revision 3.2.1.2 1992/08/11 19:47:50 penneyj
X * many fixes
X *
X * Revision 1.3.1.1 1992/07/21 21:46:25 penneyj
X * Fix bug5414. Add env expansion to HostExpandFileName (386 platforms).
X * Extend logic for sunset dates (more friendly).
X * Fix port to MS Windows (utl.c)
X *
X * Revision 1.3 1992/07/21 21:46:24 penneyj
X * Fix bug5414. Add env expansion to HostExpandFileName (386 platforms).
X * Extend logic for sunset dates (more friendly).
X * Fix port to MS Windows (utl.c)
X *
X *
X *
X * MAKETIME derive 32-bit time value from TM structure.
X *
X * Usage:
X * int zone; Minutes west of GMT, or
X * 48*60 for localtime
X * time_t t;
X * struct tm *tp; Pointer to TM structure from <time.h>
X * t = maketime(tp,zone);
X *
X * Returns:
X * -1 if failure; parameter out of range or nonsensical.
X * else time-value.
X * Notes:
X * This code is quasi-public; it may be used freely in like software.
X * It is not to be sold, nor used in licensed software without
X * permission of the author.
X * For everyone's benefit, please report bugs and improvements!
X * Copyright 1981 by Ken Harrenstien, SRI International.
X * (ARPANET: KLH @ SRI)
X *
X *
X * Revision 5.2 1990/11/01 05:03:30 eggert
X * Remove lint.
X *
X * Revision 5.1 1990/10/04 06:30:13 eggert
X * Calculate the GMT offset of 'xxx LT' as of xxx, not as of now.
X * Don't assume time_t is 32 bits. Fix bugs near epoch and near end of time.
X *
X * Revision 5.0 1990/08/22 08:12:38 eggert
X * Switch to GMT and fix the bugs exposed thereby.
X * Permit dates past 1999/12/31. Ansify and Posixate.
X *
X * Revision 1.8 88/11/08 13:54:53 narten
X * allow negative timezones (-24h <= x <= 24h)
X *
X * Revision 1.7 88/08/28 14:47:52 eggert
X * Allow cc -R. Remove unportable "#endif XXX"s.
X *
X * Revision 1.6 87/12/18 17:05:58 narten
X * include rcsparam.h
X *
X * Revision 1.5 87/12/18 11:35:51 narten
X * maketime.c: fixed USG code - you have tgo call "tzset" in order to have
X * "timezone" set. ("localtime" calls it, but it's probably better not to
X * count on "localtime" having been called.)
X *
X * Revision 1.4 87/10/18 10:26:57 narten
X * Updating version numbers. Changes relative to 1.0 are actually
X * relative to 1.2
X *
X * Revision 1.3 87/09/24 13:58:45 narten
X * Sources now pass through lint (if you ignore printf/sprintf/fprintf
X * warnings)
X *
X * Revision 1.2 87/03/27 14:21:48 jenkins
X * Port to suns
X *
X * Revision 1.2 83/12/05 10:12:56 wft
X * added cond. compilation for USG Unix; long timezone;
X *
X * Revision 1.1 82/05/06 11:38:00 wft
X * Initial revision
X *
X */
X
X/*
X* Convert Unix time to struct tm format.
X*/
X#define time2tm(x) gmtime(&x)
X
Xstatic const int daytb[] = {
X /* # days in year thus far, indexed by month (0-12!!) */
X 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365
X};
X
X#if defined(FLG_PROTOTYPE)
Xtime_t MakeTime(const struct tm *atm, int zone, const char **errReason)
X#else
Xtime_t MakeTime(atm,zone, errReason)
X const struct tm *atm;
X int zone;
X const char **errReason;
X#endif
X{
X register const struct tm *tp;
X register int i;
X BoolType localzone, leap;
X int year, yday, mon, day, hour, min, sec;
X int attempts;
X time_t t, tres;
X
X *errReason = "internal error"; /* be safe! */
X attempts = 2;
X localzone = (zone == MAKETIME_LOCALZONE);
X tres = -1;
X year = mon = day = 0; /* Keep lint happy. */
X
X do {
X if (localzone || !MAKETIME_GIVEN(atm->tm_year)) {
X if (tres == -1)
X if ((tres = time(NULL)) == -1) {
X *errReason = "can't get current time";
X return -1;
X }
X tp = time2tm(tres);
X /* Get breakdowns of default time, adjusting to zone. */
X year = tp->tm_year; /* Use to set up defaults */
X yday = tp->tm_yday;
X mon = tp->tm_mon;
X day = tp->tm_mday;
X hour = tp->tm_hour;
X min = tp->tm_min;
X if (localzone) {
X tp = localtime(&tres);
X zone = min - tp->tm_min + 60 * (
X hour - tp->tm_hour + 24 * (
X /* If years differ, it's by one day. */
X year - tp->tm_year
X ? year - tp->tm_year
X : yday - tp->tm_yday));
X }
X /* Adjust the default day, month and year according to zone. */
X min -= zone;
X if (min < 0) {
X if (hour - (59 - min) / 60 < 0 && --day <= 0) {
X mon --;
X if (mon < 0) {
X --year;
X mon = 11;
X }
X day = daytb[mon + 1] - daytb[mon] +
X (mon == 1 && !((year & 3) != 0));
X }
X }
X else
X if (24 <= hour + min / 60 &&
X daytb[mon + 1] - daytb[mon] + (mon == 1&& !((year & 3) != 0)) < ++day
X ) {
X mon ++;
X if (11 < mon) {
X ++year;
X mon = 0;
X }
X day = 1;
X }
X }
X if (zone < -24*60 || 24*60 < zone) {
X *errReason = "timezone error";
X return -1;
X }
X
X
X#ifdef TRACING
Xprintf("first YMD: %d %d %d\n",year,mon,day);
X#endif
X tp = atm;
X
X /* First must find date, using specified year, month, day.
X * If one of these is unspecified, it defaults either to the
X * current date (if no more global spec was given) or to the
X * zero-value for that spec (i.e. a more global spec was seen).
X * Reject times that do not fit in time_t,
X * without assuming that time_t is 32 bits or is signed.
X */
X if (MAKETIME_GIVEN(tp->tm_year)) {
X year = tp->tm_year;
X mon = 0; /* Since year was given, default */
X day = 1; /* for remaining specs is zero */
X }
X if (year < 69) { /* 1969/12/31 OK in some timezones. */
X *errReason = "year out of range";
X return -1; /* ERR: year out of range */
X }
X leap = (!(year&3) && (year%100 || !((year+300)%400)));
X year -= 70; /* UNIX time starts at 1970 */
X
X /*
X * Find day of year.
X */
X if (MAKETIME_GIVEN(tp->tm_mon)) {
X mon = tp->tm_mon; /* Month was specified */
X day = 1; /* so set remaining default */
X }
X if (11 < (unsigned)mon) {
X *errReason = "month out of range";
X return -1; /* ERR: bad month */
X }
X if (MAKETIME_GIVEN(tp->tm_mday))
X day = tp->tm_mday;
X if (day < 1 || (((daytb[mon + 1] - daytb[mon]) < day)
X && (day != 29 || mon != 1 || !leap) )) {
X *errReason = "day out of range";
X return -1; /* ERR: bad day */
X }
X yday = daytb[mon] /* Add # of days in months so far */
X + ((leap /* Leap year, and past Feb? If */
X && mon > 1) ? 1 :0) /* so, add leap day for this year */
X + day-1; /* And finally add # days this mon */
X
X if ((int)leap + 365 <= yday) {
X *errReason = "bad yday";
X return -1; /* ERR: bad YDAY */
X }
X
X if (year < 0) {
X if (yday != 364) {
X *errReason = "too early";
X return -1; /* ERR: too early */
X }
X t = -1;
X }
X else {
X tres = year * 365; /* Get # days of years so far */
X if (tres / 365 != year) {
X *errReason = "overflow coverting year to days";
X return -1; /* ERR: overflow */
X }
X t = tres
X + ((year+1)>>2) /* plus # of leap days since 1970 */
X + yday; /* and finally add # days this year */
X if (t + 4 < tres) {
X *errReason = "overflow coverting year to days";
X return -1; /* ERR: overflow */
X }
X }
X tres = t;
X
X i = tp->tm_wday; /* Check WDAY if present */
X if (MAKETIME_GIVEN(i)) /* Check WDAY if present */
X if (i != (tres + 4) % 7) { /* 1970/01/01 was Thu = 4 */
X *errReason = "bad wday";
X return -1; /* ERR: bad WDAY */
X }
X
X#ifdef TRACING
Xprintf("YMD: %d %d %d, T=%ld\n",year,mon,day,tres);
X#endif
X /*
X * Now determine time. If not given, default to zeros
X * (since time is always the least global spec)
X */
X tres *= 86400L; /* Get # seconds (24*60*60) */
X if (tres / 86400L != t) {
X *errReason = "overflow converting to seconds";
X return -1; /* ERR: overflow */
X }
X hour = min = sec = 0;
X if (MAKETIME_GIVEN(tp->tm_hour))
X hour = tp->tm_hour;
X if (MAKETIME_GIVEN(tp->tm_min))
X min = tp->tm_min;
X if (MAKETIME_GIVEN(tp->tm_sec))
X sec = tp->tm_sec;
X if (60 <= (unsigned)min || 60 < (unsigned)sec) {
X *errReason = "minutes/seconds out of range";
X return -1; /* ERR: MS out of range */
X }
X if (24 <= (unsigned)hour)
X if (hour != 24 || (min + sec) != 0) { /* Allow 24:00 */
X *errReason = "hour out of range";
X return -1; /* ERR: H out of range */
X }
X
X t = tres;
X tres += sec + 60L*(zone + min + 60 * hour);
X
X#ifdef TRACING
Xprintf("HMS: %d %d %d T=%ld\n",hour,min,sec,tres);
X#endif
X
X *errReason = "obscure overflow";
X if (!localzone) { /* check for overflow */
X#if 0 /* ugly atrocity, isn't even right */
X return (year < 0 ? (tres < 0 || 86400L <= tres) : tres < t)
X ? -1 : tres;
X#endif
X if (year < 0) {
X if (tres < 0 || 86400L <= tres)
X return -1;
X }
X#if 0
X else {
X if (tres < t)
X return -1;
X }
X#endif
X return tres;
X }
X *errReason = NULL;
X
X /* Check results; LT may have had a different GMT offset back then. */
X tp = localtime(&tres);
X if (MAKETIME_GIVEN(atm->tm_sec) && atm->tm_sec != tp->tm_sec) {
X *errReason = "localtime mismatch";
X return -1; /* If seconds don't match, we're in trouble. */
X }
X if (!(
X (MAKETIME_GIVEN(atm->tm_min) && atm->tm_min != tp->tm_min) ||
X (MAKETIME_GIVEN(atm->tm_hour) && atm->tm_hour != tp->tm_hour) ||
X (MAKETIME_GIVEN(atm->tm_mday) && atm->tm_mday != tp->tm_mday) ||
X (MAKETIME_GIVEN(atm->tm_mon) && atm->tm_mon != tp->tm_mon) ||
X (MAKETIME_GIVEN(atm->tm_year) && atm->tm_year != tp->tm_year)
X ))
X return tres; /* Everything matches. */
X
X } while (--attempts != 0);
X
X *errReason = "too many attempts";
X return -1;
X}
END_OF_FILE
if test 34282 -ne `wc -c <'partime.c'`; then
echo shar: \"'partime.c'\" unpacked with wrong size!
fi
# end of 'partime.c'
fi
if test -f 'global.ht' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'global.ht'\"
else
echo shar: Extracting \"'global.ht'\" \(194 characters\)
sed "s/^X//" >'global.ht' <<'END_OF_FILE'
Xtypedef int BoolType;
X#define FALSE 0
X#define TRUE 1
X
X#include <stdio.h>
X#include <stdlib.h>
X#include <time.h>
X
X/* Define this if you are using function prototypes */
X#define FLG_PROTOTYPE TRUE
END_OF_FILE
if test 194 -ne `wc -c <'global.ht'`; then
echo shar: \"'global.ht'\" unpacked with wrong size!
fi
# end of 'global.ht'
fi
if test -f 'partime.hf' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'partime.hf'\"
else
echo shar: Extracting \"'partime.hf'\" \(1370 characters\)
sed "s/^X//" >'partime.hf' <<'END_OF_FILE'
X#if !defined(PARTIME_HF)
X#define PARTIME_HF TRUE
X/*========================================================================
X * Copyright (C) Servio Corp. 1986-1994. All Rights Reserved.
X *
X * Name - partime.hf.hf
X *
X * Description:
X *
X * $Id: partime.hf,v 35.4 1994/04/30 08:52:04 darrel Exp $
X *
X * $Log: partime.hf,v $
X * Revision 35.4 1994/04/30 08:52:04 darrel
X * Added "cheat" line for quick dependency analysis.
X *
X * Revision 35.3 1993/12/31 00:04:53 darrel
X * purged the rcs log comment.
X *
X *========================================================================
X */
X
X/* no more includes. KEEP THIS LINE */
X
X#define PARTIME_TMNULL (-1) /* Items not given are given this value */
X
X#define MAKETIME_GIVEN(v) (0 <= (long)(v))/* Negative values are unspecified. */
X
X/* local time magic number -- see maketime() */
X#define MAKETIME_LOCALZONE (48 * 60)
X
X/*
X * Returns:
X * 0 if parsing failed
X * else time values in specified TM structure and zone (unspecified values
X * set to PARTIME_TMNULL)
X */
X
X#if defined(FLG_PROTOTYPE)
XBoolType ParseTime(const char *astr, register struct tm *atm, int *zone);
X#else
XBoolType ParseTime();
X#endif
X
X/* mktime. returns -1 in case of error, and sets *errReason */
X#if defined(FLG_PROTOTYPE)
Xtime_t MakeTime(const struct tm *atm, int zone, const char **errReason);
X#else
Xtime_t MakeTime();
X#endif
X
X#endif /* PARTIME_HF */
END_OF_FILE
if test 1370 -ne `wc -c <'partime.hf'`; then
echo shar: \"'partime.hf'\" unpacked with wrong size!
fi
# end of 'partime.hf'
fi
echo shar: End of shell archive.
exit 0
----[cut here]
--
D. Jason Penney (jason@slc.com), Servio Corporation
Work: 15400 NW Greenbrier Pkwy #280; Aloha, OR 97006; (503)690-3636 FAX:629-8556
Home: POB 5342; Aloha, OR 97006-0342; (503)848-9219
--
Technical Details
Message-ID:
<jason.769193127@servio>Path:
arc1-ca.pugleaf.net!wupost!crcnis1.unl.edu!ace.mid.net!newsfeed.ksu.ksu.edu!moe.ksu.ksu.edu!vixen.cso.uiuc.edu!sdd.hp.com!ihnp4.ucsd.edu!library.ucla.edu!csulb.edu!csus.edu!netcom.com!netcomsv!slcgate!servio!servio!jasonReferences:
<eadCpx95q.6xr@netcom.com>Show Full Headers
Complete Article Headers
Newsgroups: netcom.programmers,alt.sources Path: arc1-ca.pugleaf.net!wupost!crcnis1.unl.edu!ace.mid.net!newsfeed.ksu.ksu.edu!moe.ksu.ksu.edu!vixen.cso.uiuc.edu!sdd.hp.com!ihnp4.ucsd.edu!library.ucla.edu!csulb.edu!csus.edu!netcom.com!netcomsv!slcgate!servio!servio!jason From: jason@slc.com (D. Jason Penney) Subject: Re: wanted: public domain strptime(3) Message-ID: <jason.769193127@servio> Sender: news@slc.com (USENET News) Nntp-Posting-Host: servio Organization: Servio Corporation, Beaverton OR, USA References: <eadCpx95q.6xr@netcom.com> Date: 17 May 94 16:45:27 GMT Lines: 1296