Module Name: othersrc
Committed By: simonb
Date: Thu Feb 25 07:52:27 UTC 2021
Modified Files:
othersrc/usr.bin/sleepto: parsetime.c
Added Files:
othersrc/usr.bin/sleepto: stime.h
Log Message:
Catch up on usr.bin/at/parsetime.c revs 1.16 to 1.20.
To generate a diff of this commit:
cvs rdiff -u -r1.1 -r1.2 othersrc/usr.bin/sleepto/parsetime.c
cvs rdiff -u -r0 -r1.1 othersrc/usr.bin/sleepto/stime.h
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: othersrc/usr.bin/sleepto/parsetime.c
diff -u othersrc/usr.bin/sleepto/parsetime.c:1.1 othersrc/usr.bin/sleepto/parsetime.c:1.2
--- othersrc/usr.bin/sleepto/parsetime.c:1.1 Thu Feb 25 07:03:57 2021
+++ othersrc/usr.bin/sleepto/parsetime.c Thu Feb 25 07:52:27 2021
@@ -1,6 +1,6 @@
-/* $NetBSD: parsetime.c,v 1.1 2021/02/25 07:03:57 simonb Exp $ */
+/* $NetBSD: parsetime.c,v 1.2 2021/02/25 07:52:27 simonb Exp $ */
-/*
+/*
* parsetime.c - parse time for at(1)
* Copyright (C) 1993, 1994 Thomas Koenig
*
@@ -38,8 +38,10 @@
/* System Headers */
#include <sys/types.h>
+#include <err.h>
#include <ctype.h>
#include <errno.h>
+#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -48,76 +50,94 @@
#include <unistd.h>
#include "parsetime.h"
+#include "stime.h"
/* Structures and unions */
-enum { /* symbols */
+typedef enum { /* symbols */
MIDNIGHT, NOON, TEATIME,
PM, AM, TOMORROW, TODAY, NOW,
- MINUTES, HOURS, DAYS, WEEKS,
+ MINUTES, HOURS, DAYS, WEEKS, MONTHS, YEARS,
NUMBER, PLUS, DOT, SLASH, ID, JUNK,
JAN, FEB, MAR, APR, MAY, JUN,
JUL, AUG, SEP, OCT, NOV, DEC,
- SUN, MON, TUE, WED, THU, FRI, SAT
-};
+ SUN, MON, TUE, WED, THU, FRI, SAT,
+ TOKEOF /* EOF marker */
+} tokid_t;
/*
* parse translation table - table driven parsers can be your FRIEND!
*/
-struct {
+static const struct {
const char *name; /* token name */
- int value; /* token id */
- int plural; /* is this plural? */
+ tokid_t value; /* token id */
+ bool plural; /* is this plural? */
} Specials[] = {
- { "midnight", MIDNIGHT, 0 }, /* 00:00:00 of today or tomorrow */
- { "noon", NOON, 0 }, /* 12:00:00 of today or tomorrow */
- { "teatime", TEATIME, 0 }, /* 16:00:00 of today or tomorrow */
- { "am", AM, 0 }, /* morning times for 0-12 clock */
- { "pm", PM, 0 }, /* evening times for 0-12 clock */
- { "tomorrow", TOMORROW, 0 }, /* execute 24 hours from time */
- { "today", TODAY, 0 }, /* execute today - don't advance time */
- { "now", NOW, 0 }, /* opt prefix for PLUS */
-
- { "minute", MINUTES, 0 }, /* minutes multiplier */
- { "min", MINUTES, 0 },
- { "m", MINUTES, 0 },
- { "minutes", MINUTES, 1 }, /* (pluralized) */
- { "hour", HOURS, 0 }, /* hours ... */
- { "hr", HOURS, 0 }, /* abbreviated */
- { "h", HOURS, 0 },
- { "hours", HOURS, 1 }, /* (pluralized) */
- { "day", DAYS, 0 }, /* days ... */
- { "d", DAYS, 0 },
- { "days", DAYS, 1 }, /* (pluralized) */
- { "week", WEEKS, 0 }, /* week ... */
- { "w", WEEKS, 0 },
- { "weeks", WEEKS, 1 }, /* (pluralized) */
- { "jan", JAN, 0 },
- { "feb", FEB, 0 },
- { "mar", MAR, 0 },
- { "apr", APR, 0 },
- { "may", MAY, 0 },
- { "jun", JUN, 0 },
- { "jul", JUL, 0 },
- { "aug", AUG, 0 },
- { "sep", SEP, 0 },
- { "oct", OCT, 0 },
- { "nov", NOV, 0 },
- { "dec", DEC, 0 },
- { "sunday", SUN, 0 },
- { "sun", SUN, 0 },
- { "monday", MON, 0 },
- { "mon", MON, 0 },
- { "tuesday", TUE, 0 },
- { "tue", TUE, 0 },
- { "wednesday", WED, 0 },
- { "wed", WED, 0 },
- { "thursday", THU, 0 },
- { "thu", THU, 0 },
- { "friday", FRI, 0 },
- { "fri", FRI, 0 },
- { "saturday", SAT, 0 },
- { "sat", SAT, 0 },
+ {"midnight", MIDNIGHT, false}, /* 00:00:00 of today or tomorrow */
+ {"noon", NOON, false}, /* 12:00:00 of today or tomorrow */
+ {"teatime", TEATIME, false}, /* 16:00:00 of today or tomorrow */
+ {"am", AM, false}, /* morning times for 0-12 clock */
+ {"pm", PM, false}, /* evening times for 0-12 clock */
+ {"tomorrow", TOMORROW, false}, /* execute 24 hours from time */
+ {"today", TODAY, false}, /* execute today - don't advance time */
+ {"now", NOW, false}, /* opt prefix for PLUS */
+
+ {"minute", MINUTES, false}, /* minutes multiplier */
+ {"min", MINUTES, false},
+ {"m", MINUTES, false},
+ {"minutes", MINUTES, true}, /* (pluralized) */
+ {"hour", HOURS, false}, /* hours ... */
+ {"hr", HOURS, false}, /* abbreviated */
+ {"h", HOURS, false},
+ {"hours", HOURS, true}, /* (pluralized) */
+ {"day", DAYS, false}, /* days ... */
+ {"d", DAYS, false},
+ {"days", DAYS, true}, /* (pluralized) */
+ {"week", WEEKS, false}, /* week ... */
+ {"w", WEEKS, false},
+ {"weeks", WEEKS, true}, /* (pluralized) */
+ { "month", MONTHS, 0 }, /* month ... */
+ { "months", MONTHS, 1 }, /* (pluralized) */
+ { "year", YEARS, 0 }, /* year ... */
+ { "years", YEARS, 1 }, /* (pluralized) */
+ {"jan", JAN, false},
+ {"feb", FEB, false},
+ {"mar", MAR, false},
+ {"apr", APR, false},
+ {"may", MAY, false},
+ {"jun", JUN, false},
+ {"jul", JUL, false},
+ {"aug", AUG, false},
+ {"sep", SEP, false},
+ {"oct", OCT, false},
+ {"nov", NOV, false},
+ {"dec", DEC, false},
+ {"january", JAN, false},
+ {"february", FEB, false},
+ {"march", MAR, false},
+ {"april", APR, false},
+ {"may", MAY, false},
+ {"june", JUN, false},
+ {"july", JUL, false},
+ {"august", AUG, false},
+ {"september", SEP, false},
+ {"october", OCT, false},
+ {"november", NOV, false},
+ {"december", DEC, false},
+ {"sunday", SUN, false},
+ {"sun", SUN, false},
+ {"monday", MON, false},
+ {"mon", MON, false},
+ {"tuesday", TUE, false},
+ {"tue", TUE, false},
+ {"wednesday", WED, false},
+ {"wed", WED, false},
+ {"thursday", THU, false},
+ {"thu", THU, false},
+ {"friday", FRI, false},
+ {"fri", FRI, false},
+ {"saturday", SAT, false},
+ {"sat", SAT, false}
};
/* File scope variables */
@@ -125,32 +145,31 @@ struct {
static char **scp; /* scanner - pointer at arglist */
static char scc; /* scanner - count of remaining arguments */
static char *sct; /* scanner - next char pointer in current argument */
-static int need; /* scanner - need to advance to next argument */
+static bool need; /* scanner - need to advance to next argument */
static char *sc_token; /* scanner - token buffer */
static size_t sc_len; /* scanner - length of token buffer */
-static int sc_tokid; /* scanner - token id */
-static int sc_tokplur; /* scanner - is token plural? */
+static tokid_t sc_tokid;/* scanner - token id */
+static bool sc_tokplur; /* scanner - is token plural? */
#ifndef lint
#if 0
static char rcsid[] = "$OpenBSD: parsetime.c,v 1.4 1997/03/01 23:40:10 millert Exp $";
#else
-__RCSID("$NetBSD: parsetime.c,v 1.1 2021/02/25 07:03:57 simonb Exp $");
+__RCSID("$NetBSD: parsetime.c,v 1.2 2021/02/25 07:52:27 simonb Exp $");
#endif
#endif
/* Local functions */
-static void assign_date (struct tm *, long, long, long);
-static void dateadd (int, struct tm *);
-static void expect (int);
-static void init_scanner (int, char **);
-static void month (struct tm *);
-static int parse_token (char *);
-static void plonk (int);
-static void plus (struct tm *);
-static void tod (struct tm *);
-static int token (void);
+static void assign_date(struct tm *, int, int, int);
+static void expect(tokid_t);
+static void init_scanner(int, char **);
+static void month(struct tm *);
+static tokid_t parse_token(char *);
+static void plonk(tokid_t) __dead;
+static void plus(struct tm *);
+static void tod(struct tm *);
+static tokid_t token(void);
static void panic (const char *);
static void
@@ -163,22 +182,21 @@ panic(const char *s)
/*
* parse a token, checking if it's something special to us
*/
-static int
+static tokid_t
parse_token(char *arg)
{
- int i;
+ size_t i;
- for (i=0; i < sizeof(Specials) / sizeof(Specials[0]); i++) {
+ for (i=0; i < __arraycount(Specials); i++) {
if (strcasecmp(Specials[i].name, arg) == 0) {
sc_tokplur = Specials[i].plural;
- return (sc_tokid = Specials[i].value);
+ return sc_tokid = Specials[i].value;
}
}
/* not special - must be some random id */
- return (ID);
-} /* parse_token */
-
+ return ID;
+}
/*
* init_scanner() sets up the scanner to eat arguments
@@ -186,43 +204,44 @@ parse_token(char *arg)
static void
init_scanner(int argc, char **argv)
{
+
scp = argv;
scc = argc;
- need = 1;
+ need = true;
sc_len = 1;
while (argc-- > 0)
sc_len += strlen(*argv++);
- if ((sc_token = (char *) malloc(sc_len)) == NULL)
+ if ((sc_token = malloc(sc_len)) == NULL)
panic("Insufficient virtual memory");
-} /* init_scanner */
+}
/*
* token() fetches a token from the input stream
*/
-static int
+static tokid_t
token(void)
{
int idx;
- while (1) {
+ for(;;) {
(void)memset(sc_token, 0, sc_len);
- sc_tokid = EOF;
- sc_tokplur = 0;
+ sc_tokid = TOKEOF;
+ sc_tokplur = false;
idx = 0;
/*
* if we need to read another argument, walk along the
* argument list; when we fall off the arglist, we'll
- * just return EOF forever
+ * just return TOKEOF forever
*/
if (need) {
if (scc < 1)
- return (sc_tokid);
+ return sc_tokid;
sct = *scp;
scp++;
scc--;
- need = 0;
+ need = false;
}
/*
* eat whitespace now - if we walk off the end of the argument,
@@ -232,7 +251,7 @@ token(void)
while (isspace((unsigned char)*sct))
++sct;
if (!*sct) {
- need = 1;
+ need = true;
continue;
}
@@ -248,90 +267,50 @@ token(void)
while (isdigit((unsigned char)*sct))
sc_token[++idx] = *sct++;
sc_token[++idx] = 0;
- return ((sc_tokid = NUMBER));
+ return sc_tokid = NUMBER;
} else if (isalpha((unsigned char)sc_token[0])) {
while (isalpha((unsigned char)*sct))
sc_token[++idx] = *sct++;
sc_token[++idx] = 0;
- return (parse_token(sc_token));
+ return parse_token(sc_token);
}
else if (sc_token[0] == ':' || sc_token[0] == '.')
- return ((sc_tokid = DOT));
+ return sc_tokid = DOT;
else if (sc_token[0] == '+')
- return ((sc_tokid = PLUS));
+ return sc_tokid = PLUS;
else if (sc_token[0] == '/')
- return ((sc_tokid = SLASH));
+ return sc_tokid = SLASH;
else
- return ((sc_tokid = JUNK));
- } /* while (1) */
-} /* token */
-
+ return sc_tokid = JUNK;
+ }
+}
/*
* plonk() gives an appropriate error message if a token is incorrect
*/
+__dead
static void
-plonk(int tok)
+plonk(tokid_t tok)
{
- panic((tok == EOF) ? "incomplete time" : "garbled time");
-} /* plonk */
+ panic(tok == TOKEOF ? "incomplete time" : "garbled time");
+}
-/*
+/*
* expect() gets a token and dies most horribly if it's not the token we want
*/
static void
-expect(int desired)
+expect(tokid_t desired)
{
+
if (token() != desired)
plonk(sc_tokid); /* and we die here... */
-} /* expect */
-
-
-/*
- * dateadd() adds a number of minutes to a date. It is extraordinarily
- * stupid regarding day-of-month overflow, and will most likely not
- * work properly
- */
-static void
-dateadd(int minutes, struct tm *tm)
-{
- /* increment days */
-
- while (minutes > 24*60) {
- minutes -= 24*60;
- tm->tm_mday++;
- }
-
- /* increment hours */
- while (minutes > 60) {
- minutes -= 60;
- tm->tm_hour++;
- if (tm->tm_hour > 23) {
- tm->tm_mday++;
- tm->tm_hour = 0;
- }
- }
-
- /* increment minutes */
- tm->tm_min += minutes;
-
- if (tm->tm_min > 59) {
- tm->tm_hour++;
- tm->tm_min -= 60;
-
- if (tm->tm_hour > 23) {
- tm->tm_mday++;
- tm->tm_hour = 0;
- }
- }
-} /* dateadd */
-
+}
/*
* plus() parses a now + time
*
- * at [NOW] PLUS NUMBER [MINUTES|HOURS|DAYS|WEEKS]
+ * at [NOW] PLUS NUMBER [MINUTES|HOURS|DAYS|WEEKS|MONTHS|YEARS]
*
*/
static void
@@ -343,25 +322,39 @@ plus(struct tm *tm)
expect(NUMBER);
delay = atoi(sc_token);
- expectplur = (delay != 1) ? 1 : 0;
+ expectplur = delay != 1;
switch (token()) {
+ case YEARS:
+ tm->tm_year += delay;
+ break;
+ case MONTHS:
+ tm->tm_mon += delay;
+ break;
case WEEKS:
delay *= 7;
+ /*FALLTHROUGH*/
case DAYS:
- delay *= 24;
+ tm->tm_mday += delay;
+ break;
case HOURS:
- delay *= 60;
+ tm->tm_hour += delay;
+ break;
case MINUTES:
- if (expectplur != sc_tokplur)
- (void)fprintf(stderr, "at: pluralization is wrong\n");
- dateadd(delay, tm);
- return;
+ tm->tm_min += delay;
+ break;
+ default:
+ plonk(sc_tokid);
+ break;
}
- plonk(sc_tokid);
-} /* plus */
+ if (expectplur != sc_tokplur)
+ warnx("pluralization is wrong");
+ tm->tm_isdst = -1;
+ if (mktime(tm) == -1)
+ plonk(sc_tokid);
+}
/*
* tod() computes the time of day
@@ -370,9 +363,10 @@ plus(struct tm *tm)
static void
tod(struct tm *tm)
{
- int hour, minute = 0;
+ int hour, minute;
size_t tlen;
+ minute = 0;
hour = atoi(sc_token);
tlen = strlen(sc_token);
@@ -383,7 +377,7 @@ tod(struct tm *tm)
if (token() == DOT) {
expect(NUMBER);
minute = atoi(sc_token);
- token();
+ (void)token();
} else if (tlen == 4) {
minute = hour % 100;
hour = hour / 100;
@@ -406,7 +400,7 @@ tod(struct tm *tm)
if (hour == 12) /* 12:xx AM is 00:xx, not 12:xx */
hour = 0;
}
- token();
+ (void)token();
} else if (hour > 23)
panic("garbled time");
@@ -415,47 +409,47 @@ tod(struct tm *tm)
* if we've gone past that time - but if we're specifying a time plus
* a relative offset, it's okay to bump things
*/
- if ((sc_tokid == EOF || sc_tokid == PLUS) && tm->tm_hour > hour) {
+ if ((sc_tokid == TOKEOF || sc_tokid == PLUS) && (tm->tm_hour > hour ||
+ (tm->tm_hour == hour && tm->tm_min > minute))) {
tm->tm_mday++;
tm->tm_wday++;
}
tm->tm_hour = hour;
tm->tm_min = minute;
-} /* tod */
-
+}
/*
- * assign_date() assigns a date, wrapping to next year if needed
+ * assign_date() assigns a date, wrapping to next year if needed.
+ * Accept years in 4-digit, 2-digit, or current year (-1).
*/
static void
-assign_date(struct tm *tm, long mday, long mon, long year)
+assign_date(struct tm *tm, int mday, int mon, int year)
{
- if (year > 99) {
- if (year >= TM_YEAR_BASE)
- year -= TM_YEAR_BASE;
- else
- panic("garbled time");
- }
- if (year >= 0) {
- if (year < 70)
- tm->tm_year = year + 2000 - TM_YEAR_BASE;
+ if (year > 99) { /* four digit year */
+ if (year >= TM_YEAR_BASE)
+ tm->tm_year = year - TM_YEAR_BASE;
else
- tm->tm_year = year + 1900 - TM_YEAR_BASE;
+ panic("garbled time");
}
- else { /* year < 0 */
+ else if (year >= 0) { /* two digit year */
+ tm->tm_year = conv_2dig_year(year) - TM_YEAR_BASE;
+ }
+ else if (year == -1) { /* year not given (use default in tm) */
+ /* allow for 1 year range from current date */
if (tm->tm_mon > mon ||
(tm->tm_mon == mon && tm->tm_mday > mday))
tm->tm_year++;
}
+ else
+ panic("invalid year");
tm->tm_mday = mday;
tm->tm_mon = mon;
-} /* assign_date */
-
+}
-/*
+/*
* month() picks apart a month specification
*
* /[<month> NUMBER [NUMBER]] \
@@ -467,10 +461,11 @@ assign_date(struct tm *tm, long mday, lo
static void
month(struct tm *tm)
{
- int year = (-1);
+ int year;
int mday, wday, mon;
size_t tlen;
+ year = -1;
mday = 0;
switch (sc_tokid) {
case PLUS:
@@ -481,9 +476,10 @@ month(struct tm *tm)
/* do something tomorrow */
tm->tm_mday++;
tm->tm_wday++;
+ /*FALLTHROUGH*/
case TODAY:
/* force ourselves to stay in today - no further processing */
- token();
+ (void)token();
break;
case JAN: case FEB: case MAR: case APR: case MAY: case JUN:
@@ -496,7 +492,7 @@ month(struct tm *tm)
mday = atoi(sc_token);
if (token() == NUMBER) {
year = atoi(sc_token);
- token();
+ (void)token();
}
assign_date(tm, mday, mon, year);
break;
@@ -526,10 +522,10 @@ month(struct tm *tm)
*/
tlen = strlen(sc_token);
mon = atoi(sc_token);
- token();
+ (void)token();
if (sc_tokid == SLASH || sc_tokid == DOT) {
- int sep;
+ tokid_t sep;
sep = sc_tokid;
expect(NUMBER);
@@ -537,7 +533,7 @@ month(struct tm *tm)
if (token() == sep) {
expect(NUMBER);
year = atoi(sc_token);
- token();
+ (void)token();
}
/*
@@ -567,8 +563,11 @@ month(struct tm *tm)
assign_date(tm, mday, mon, year);
break;
- } /* case */
-} /* month */
+ default:
+ /* plonk(sc_tokid); */ /* XXX - die here? */
+ break;
+ }
+}
/* Global functions */
@@ -584,12 +583,12 @@ parsetime(int argc, char **argv)
struct tm nowtime, runtime;
int hr = 0; /* this MUST be initialized to zero for
midnight/noon/teatime */
- int fulltime = 0;
nowtimer = time(NULL);
nowtime = *localtime(&nowtimer);
runtime = nowtime;
+ runtime.tm_sec = 0;
if (argc <= optind)
panic("bad arguments");
@@ -598,20 +597,12 @@ parsetime(int argc, char **argv)
switch (token()) {
case NOW:
- /* now is optional prefix for PLUS tree
- in this case the PLUS tree is optional. */
- switch (token()) {
- case PLUS:
- plus(&runtime);
- break;
- case EOF:
- break;
- default:
- plonk(sc_tokid);
- break;
- }
- fulltime = 1;
- break;
+ if (scc < 1)
+ return nowtimer;
+
+ /* now is optional prefix for PLUS tree */
+ expect(PLUS);
+ /*FALLTHROUGH*/
case PLUS:
plus(&runtime);
break;
@@ -631,8 +622,10 @@ parsetime(int argc, char **argv)
*/
case TEATIME:
hr += 4;
+ /*FALLTHROUGH*/
case NOON:
hr += 12;
+ /*FALLTHROUGH*/
case MIDNIGHT:
if (runtime.tm_hour >= hr) {
runtime.tm_mday++;
@@ -640,34 +633,25 @@ parsetime(int argc, char **argv)
}
runtime.tm_hour = hr;
runtime.tm_min = 0;
- token();
- /* fall through to month setting */
+ (void)token();
+ /*FALLTHROUGH*/ /* fall through to month setting */
default:
month(&runtime);
break;
- } /* ugly case statement */
- expect(EOF);
-
- if (!fulltime) {
- runtime.tm_sec = 0;
- runtime.tm_isdst = 0;
}
+ expect(TOKEOF);
/*
* adjust for daylight savings time
*/
runtime.tm_isdst = -1;
runtimer = mktime(&runtime);
- if (runtime.tm_isdst > 0) {
- runtimer -= 3600;
- runtimer = mktime(&runtime);
- }
- if (runtimer < 0)
- panic("Time not valid (possibly due to daylight saving time)");
+ if (runtimer == (time_t)-1)
+ panic("Invalid time");
if (nowtimer > runtimer)
panic("Trying to travel back in time");
- return (runtimer);
-} /* parsetime */
+ return runtimer;
+}
Added files:
Index: othersrc/usr.bin/sleepto/stime.h
diff -u /dev/null othersrc/usr.bin/sleepto/stime.h:1.1
--- /dev/null Thu Feb 25 07:52:27 2021
+++ othersrc/usr.bin/sleepto/stime.h Thu Feb 25 07:52:27 2021
@@ -0,0 +1,53 @@
+/* $NetBSD: stime.h,v 1.1 2021/02/25 07:52:27 simonb Exp $ */
+
+/*
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _STIME_H_
+#define _STIME_H_
+
+/*
+ * Convert a 2-digit year to a valid year as:
+ * 00-68 <-> 2000-2068
+ * 69-99 <-> 1969-1999
+ *
+ * Note: We choose the earliest year as 1969 rather than 1970 so that
+ * dates near the Epoch (Jan 1, 1970 00:00:00 UTC) can be represented
+ * with 2-digit years in all time zones. This is the convention used
+ * in parsedate(3), though it is not consistently applied in the
+ * source tree.
+ *
+ * XXX - move this (or something like it) to time.h and use it
+ * consistently in the tree.
+ */
+#define conv_2dig_year(y) ((y) < 69 ? (y) + 2000 : (y) + 1900)
+
+time_t stime(char *);
+
+#endif /* _STIME_H_ */