xref: /freebsd/usr.sbin/newsyslog/ptimes.c (revision b7b447fd4ca327faa99b2f16e6cbd61c86c75f04)
12f8d7c56SGarance A Drosehn /*-
22f8d7c56SGarance A Drosehn  * ------+---------+---------+---------+---------+---------+---------+---------*
32f8d7c56SGarance A Drosehn  * Initial version of parse8601 was originally added to newsyslog.c in
42f8d7c56SGarance A Drosehn  *     FreeBSD on Jan 22, 1999 by Garrett Wollman <wollman@FreeBSD.org>.
52f8d7c56SGarance A Drosehn  * Initial version of parseDWM was originally added to newsyslog.c in
62f8d7c56SGarance A Drosehn  *     FreeBSD on Apr  4, 2000 by Hellmuth Michaelis <hm@FreeBSD.org>.
72f8d7c56SGarance A Drosehn  *
82f8d7c56SGarance A Drosehn  * Copyright (c) 2003  - Garance Alistair Drosehn <gad@FreeBSD.org>.
92f8d7c56SGarance A Drosehn  * All rights reserved.
102f8d7c56SGarance A Drosehn  *
112f8d7c56SGarance A Drosehn  * Redistribution and use in source and binary forms, with or without
122f8d7c56SGarance A Drosehn  * modification, are permitted provided that the following conditions
132f8d7c56SGarance A Drosehn  * are met:
142f8d7c56SGarance A Drosehn  *   1. Redistributions of source code must retain the above copyright
152f8d7c56SGarance A Drosehn  *      notice, this list of conditions and the following disclaimer.
162f8d7c56SGarance A Drosehn  *   2. Redistributions in binary form must reproduce the above copyright
172f8d7c56SGarance A Drosehn  *      notice, this list of conditions and the following disclaimer in the
182f8d7c56SGarance A Drosehn  *      documentation and/or other materials provided with the distribution.
192f8d7c56SGarance A Drosehn  *
202f8d7c56SGarance A Drosehn  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
212f8d7c56SGarance A Drosehn  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
222f8d7c56SGarance A Drosehn  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
232f8d7c56SGarance A Drosehn  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
242f8d7c56SGarance A Drosehn  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
252f8d7c56SGarance A Drosehn  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
262f8d7c56SGarance A Drosehn  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
272f8d7c56SGarance A Drosehn  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
282f8d7c56SGarance A Drosehn  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
292f8d7c56SGarance A Drosehn  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
302f8d7c56SGarance A Drosehn  * SUCH DAMAGE.
312f8d7c56SGarance A Drosehn  *
322f8d7c56SGarance A Drosehn  * The views and conclusions contained in the software and documentation
332f8d7c56SGarance A Drosehn  * are those of the authors and should not be interpreted as representing
342f8d7c56SGarance A Drosehn  * official policies, either expressed or implied, of the FreeBSD Project.
352f8d7c56SGarance A Drosehn  *
362f8d7c56SGarance A Drosehn  * ------+---------+---------+---------+---------+---------+---------+---------*
376a1485e2SGarance A Drosehn  * This is intended to be a set of general-purpose routines to process times.
386a1485e2SGarance A Drosehn  * Right now it probably still has a number of assumptions in it, such that
396a1485e2SGarance A Drosehn  * it works fine for newsyslog but might not work for other uses.
406a1485e2SGarance A Drosehn  * ------+---------+---------+---------+---------+---------+---------+---------*
412f8d7c56SGarance A Drosehn  */
422f8d7c56SGarance A Drosehn 
432f8d7c56SGarance A Drosehn #include <sys/cdefs.h>
442f8d7c56SGarance A Drosehn __FBSDID("$FreeBSD$");
452f8d7c56SGarance A Drosehn 
462f8d7c56SGarance A Drosehn #include <ctype.h>
472f8d7c56SGarance A Drosehn #include <limits.h>
482f8d7c56SGarance A Drosehn #include <stdio.h>
492f8d7c56SGarance A Drosehn #include <stdlib.h>
506a1485e2SGarance A Drosehn #include <string.h>
512f8d7c56SGarance A Drosehn #include <time.h>
522f8d7c56SGarance A Drosehn 
532f8d7c56SGarance A Drosehn #include "extern.h"
542f8d7c56SGarance A Drosehn 
556a1485e2SGarance A Drosehn #define	SECS_PER_HOUR	3600
566a1485e2SGarance A Drosehn 
576a1485e2SGarance A Drosehn /*
586a1485e2SGarance A Drosehn  * Bit-values which indicate which components of time were specified
596a1485e2SGarance A Drosehn  * by the string given to parse8601 or parseDWM.  These are needed to
606a1485e2SGarance A Drosehn  * calculate what time-in-the-future will match that string.
616a1485e2SGarance A Drosehn  */
626a1485e2SGarance A Drosehn #define	TSPEC_YEAR		0x0001
636a1485e2SGarance A Drosehn #define	TSPEC_MONTHOFYEAR	0x0002
646a1485e2SGarance A Drosehn #define	TSPEC_LDAYOFMONTH	0x0004
656a1485e2SGarance A Drosehn #define	TSPEC_DAYOFMONTH	0x0008
666a1485e2SGarance A Drosehn #define	TSPEC_DAYOFWEEK		0x0010
676a1485e2SGarance A Drosehn #define	TSPEC_HOUROFDAY		0x0020
686a1485e2SGarance A Drosehn 
696a1485e2SGarance A Drosehn #define	TNYET_ADJ4DST		-10	/* DST has "not yet" been adjusted */
706a1485e2SGarance A Drosehn 
716a1485e2SGarance A Drosehn struct ptime_data {
726a1485e2SGarance A Drosehn 	time_t		 basesecs;	/* Base point for relative times */
736a1485e2SGarance A Drosehn 	time_t		 tsecs;		/* Time in seconds */
746a1485e2SGarance A Drosehn 	struct tm	 basetm;	/* Base Time expanded into fields */
756a1485e2SGarance A Drosehn 	struct tm	 tm;		/* Time expanded into fields */
766a1485e2SGarance A Drosehn 	int		 did_adj4dst;	/* Track calls to ptime_adjust4dst */
776a1485e2SGarance A Drosehn 	int		 parseopts;	/* Options given for parsing */
786a1485e2SGarance A Drosehn 	int		 tmspec;	/* Indicates which time fields had
796a1485e2SGarance A Drosehn 					 * been specified by the user */
806a1485e2SGarance A Drosehn };
816a1485e2SGarance A Drosehn 
82526d55a4SGarance A Drosehn static int	 days_pmonth(int month, int year);
836a1485e2SGarance A Drosehn static int	 parse8601(struct ptime_data *ptime, const char *str);
846a1485e2SGarance A Drosehn static int	 parseDWM(struct ptime_data *ptime, const char *str);
85526d55a4SGarance A Drosehn 
86526d55a4SGarance A Drosehn /*
87526d55a4SGarance A Drosehn  * Simple routine to calculate the number of days in a given month.
88526d55a4SGarance A Drosehn  */
89526d55a4SGarance A Drosehn static int
90526d55a4SGarance A Drosehn days_pmonth(int month, int year)
91526d55a4SGarance A Drosehn {
92526d55a4SGarance A Drosehn 	static const int mtab[] = {31, 28, 31, 30, 31, 30, 31, 31,
93526d55a4SGarance A Drosehn 	    30, 31, 30, 31};
94526d55a4SGarance A Drosehn 	int ndays;
95526d55a4SGarance A Drosehn 
96526d55a4SGarance A Drosehn 	ndays = mtab[month];
97526d55a4SGarance A Drosehn 
98526d55a4SGarance A Drosehn 	if (month == 1) {
99526d55a4SGarance A Drosehn 		/*
100526d55a4SGarance A Drosehn 		 * We are usually called with a 'tm-year' value
101526d55a4SGarance A Drosehn 		 * (ie, the value = the number of years past 1900).
102526d55a4SGarance A Drosehn 		 */
103526d55a4SGarance A Drosehn 		if (year < 1900)
104526d55a4SGarance A Drosehn 			year += 1900;
105526d55a4SGarance A Drosehn 		if (year % 4 == 0) {
106526d55a4SGarance A Drosehn 			/*
107526d55a4SGarance A Drosehn 			 * This is a leap year, as long as it is not a
108526d55a4SGarance A Drosehn 			 * multiple of 100, or if it is a multiple of
109526d55a4SGarance A Drosehn 			 * both 100 and 400.
110526d55a4SGarance A Drosehn 			 */
111526d55a4SGarance A Drosehn 			if (year % 100 != 0)
112526d55a4SGarance A Drosehn 				ndays++;	/* not multiple of 100 */
113526d55a4SGarance A Drosehn 			else if (year % 400 == 0)
114526d55a4SGarance A Drosehn 				ndays++;	/* is multiple of 100 and 400 */
115526d55a4SGarance A Drosehn 		}
116526d55a4SGarance A Drosehn 	}
117526d55a4SGarance A Drosehn 	return (ndays);
118526d55a4SGarance A Drosehn }
119526d55a4SGarance A Drosehn 
1202f8d7c56SGarance A Drosehn /*-
1212f8d7c56SGarance A Drosehn  * Parse a limited subset of ISO 8601. The specific format is as follows:
1222f8d7c56SGarance A Drosehn  *
1232f8d7c56SGarance A Drosehn  * [CC[YY[MM[DD]]]][THH[MM[SS]]]	(where `T' is the literal letter)
1242f8d7c56SGarance A Drosehn  *
1252f8d7c56SGarance A Drosehn  * We don't accept a timezone specification; missing fields (including timezone)
1262f8d7c56SGarance A Drosehn  * are defaulted to the current date but time zero.
1272f8d7c56SGarance A Drosehn  */
1286a1485e2SGarance A Drosehn static int
1296a1485e2SGarance A Drosehn parse8601(struct ptime_data *ptime, const char *s)
1302f8d7c56SGarance A Drosehn {
1312f8d7c56SGarance A Drosehn 	char *t;
1322f8d7c56SGarance A Drosehn 	long l;
1336a1485e2SGarance A Drosehn 	struct tm tm;
1342f8d7c56SGarance A Drosehn 
1352f8d7c56SGarance A Drosehn 	l = strtol(s, &t, 10);
1362f8d7c56SGarance A Drosehn 	if (l < 0 || l >= INT_MAX || (*t != '\0' && *t != 'T'))
1372f8d7c56SGarance A Drosehn 		return (-1);
1382f8d7c56SGarance A Drosehn 
1392f8d7c56SGarance A Drosehn 	/*
1402f8d7c56SGarance A Drosehn 	 * Now t points either to the end of the string (if no time was
1412f8d7c56SGarance A Drosehn 	 * provided) or to the letter `T' which separates date and time in
1422f8d7c56SGarance A Drosehn 	 * ISO 8601.  The pointer arithmetic is the same for either case.
1432f8d7c56SGarance A Drosehn 	 */
1446a1485e2SGarance A Drosehn 	tm = ptime->tm;
1456a1485e2SGarance A Drosehn 	ptime->tmspec = TSPEC_HOUROFDAY;
1462f8d7c56SGarance A Drosehn 	switch (t - s) {
1472f8d7c56SGarance A Drosehn 	case 8:
1482f8d7c56SGarance A Drosehn 		tm.tm_year = ((l / 1000000) - 19) * 100;
1492f8d7c56SGarance A Drosehn 		l = l % 1000000;
1504c8f6471SMark Johnston 		/* FALLTHROUGH */
1512f8d7c56SGarance A Drosehn 	case 6:
1526a1485e2SGarance A Drosehn 		ptime->tmspec |= TSPEC_YEAR;
1532f8d7c56SGarance A Drosehn 		tm.tm_year -= tm.tm_year % 100;
1542f8d7c56SGarance A Drosehn 		tm.tm_year += l / 10000;
1552f8d7c56SGarance A Drosehn 		l = l % 10000;
1564c8f6471SMark Johnston 		/* FALLTHROUGH */
1572f8d7c56SGarance A Drosehn 	case 4:
1586a1485e2SGarance A Drosehn 		ptime->tmspec |= TSPEC_MONTHOFYEAR;
1592f8d7c56SGarance A Drosehn 		tm.tm_mon = (l / 100) - 1;
1602f8d7c56SGarance A Drosehn 		l = l % 100;
1614c8f6471SMark Johnston 		/* FALLTHROUGH */
1622f8d7c56SGarance A Drosehn 	case 2:
1636a1485e2SGarance A Drosehn 		ptime->tmspec |= TSPEC_DAYOFMONTH;
1642f8d7c56SGarance A Drosehn 		tm.tm_mday = l;
1654c8f6471SMark Johnston 		/* FALLTHROUGH */
1662f8d7c56SGarance A Drosehn 	case 0:
1672f8d7c56SGarance A Drosehn 		break;
1682f8d7c56SGarance A Drosehn 	default:
1692f8d7c56SGarance A Drosehn 		return (-1);
1702f8d7c56SGarance A Drosehn 	}
1712f8d7c56SGarance A Drosehn 
1722f8d7c56SGarance A Drosehn 	/* sanity check */
1732f8d7c56SGarance A Drosehn 	if (tm.tm_year < 70 || tm.tm_mon < 0 || tm.tm_mon > 12
1742f8d7c56SGarance A Drosehn 	    || tm.tm_mday < 1 || tm.tm_mday > 31)
1752f8d7c56SGarance A Drosehn 		return (-1);
1762f8d7c56SGarance A Drosehn 
1772f8d7c56SGarance A Drosehn 	if (*t != '\0') {
1782f8d7c56SGarance A Drosehn 		s = ++t;
1792f8d7c56SGarance A Drosehn 		l = strtol(s, &t, 10);
1802f8d7c56SGarance A Drosehn 		if (l < 0 || l >= INT_MAX || (*t != '\0' && !isspace(*t)))
1812f8d7c56SGarance A Drosehn 			return (-1);
1822f8d7c56SGarance A Drosehn 
1832f8d7c56SGarance A Drosehn 		switch (t - s) {
1842f8d7c56SGarance A Drosehn 		case 6:
1852f8d7c56SGarance A Drosehn 			tm.tm_sec = l % 100;
1862f8d7c56SGarance A Drosehn 			l /= 100;
1874c8f6471SMark Johnston 			/* FALLTHROUGH */
1882f8d7c56SGarance A Drosehn 		case 4:
1892f8d7c56SGarance A Drosehn 			tm.tm_min = l % 100;
1902f8d7c56SGarance A Drosehn 			l /= 100;
1914c8f6471SMark Johnston 			/* FALLTHROUGH */
1922f8d7c56SGarance A Drosehn 		case 2:
1936a1485e2SGarance A Drosehn 			ptime->tmspec |= TSPEC_HOUROFDAY;
1942f8d7c56SGarance A Drosehn 			tm.tm_hour = l;
1954c8f6471SMark Johnston 			/* FALLTHROUGH */
1962f8d7c56SGarance A Drosehn 		case 0:
1972f8d7c56SGarance A Drosehn 			break;
1982f8d7c56SGarance A Drosehn 		default:
1992f8d7c56SGarance A Drosehn 			return (-1);
2002f8d7c56SGarance A Drosehn 		}
2012f8d7c56SGarance A Drosehn 
2022f8d7c56SGarance A Drosehn 		/* sanity check */
2032f8d7c56SGarance A Drosehn 		if (tm.tm_sec < 0 || tm.tm_sec > 60 || tm.tm_min < 0
2042f8d7c56SGarance A Drosehn 		    || tm.tm_min > 59 || tm.tm_hour < 0 || tm.tm_hour > 23)
2052f8d7c56SGarance A Drosehn 			return (-1);
2062f8d7c56SGarance A Drosehn 	}
2072f8d7c56SGarance A Drosehn 
2086a1485e2SGarance A Drosehn 	ptime->tm = tm;
2096a1485e2SGarance A Drosehn 	return (0);
2102f8d7c56SGarance A Drosehn }
2112f8d7c56SGarance A Drosehn 
2122f8d7c56SGarance A Drosehn /*-
2132f8d7c56SGarance A Drosehn  * Parse a cyclic time specification, the format is as follows:
2142f8d7c56SGarance A Drosehn  *
2152f8d7c56SGarance A Drosehn  *	[Dhh] or [Wd[Dhh]] or [Mdd[Dhh]]
2162f8d7c56SGarance A Drosehn  *
2172f8d7c56SGarance A Drosehn  * to rotate a logfile cyclic at
2182f8d7c56SGarance A Drosehn  *
2192f8d7c56SGarance A Drosehn  *	- every day (D) within a specific hour (hh)	(hh = 0...23)
2202f8d7c56SGarance A Drosehn  *	- once a week (W) at a specific day (d)     OR	(d = 0..6, 0 = Sunday)
2212f8d7c56SGarance A Drosehn  *	- once a month (M) at a specific day (d)	(d = 1..31,l|L)
2222f8d7c56SGarance A Drosehn  *
2232f8d7c56SGarance A Drosehn  * We don't accept a timezone specification; missing fields
2242f8d7c56SGarance A Drosehn  * are defaulted to the current date but time zero.
2252f8d7c56SGarance A Drosehn  */
2266a1485e2SGarance A Drosehn static int
2276a1485e2SGarance A Drosehn parseDWM(struct ptime_data *ptime, const char *s)
2282f8d7c56SGarance A Drosehn {
2296a1485e2SGarance A Drosehn 	int daysmon, Dseen, WMseen;
230c3033287SGarance A Drosehn 	const char *endval;
231c3033287SGarance A Drosehn 	char *tmp;
2322f8d7c56SGarance A Drosehn 	long l;
2336a1485e2SGarance A Drosehn 	struct tm tm;
2342f8d7c56SGarance A Drosehn 
235526d55a4SGarance A Drosehn 	/* Save away the number of days in this month */
2366a1485e2SGarance A Drosehn 	tm = ptime->tm;
237526d55a4SGarance A Drosehn 	daysmon = days_pmonth(tm.tm_mon, tm.tm_year);
2382f8d7c56SGarance A Drosehn 
2396a1485e2SGarance A Drosehn 	WMseen = Dseen = 0;
2406a1485e2SGarance A Drosehn 	ptime->tmspec = TSPEC_HOUROFDAY;
2412f8d7c56SGarance A Drosehn 	for (;;) {
242c3033287SGarance A Drosehn 		endval = NULL;
2432f8d7c56SGarance A Drosehn 		switch (*s) {
2442f8d7c56SGarance A Drosehn 		case 'D':
2452f8d7c56SGarance A Drosehn 			if (Dseen)
2462f8d7c56SGarance A Drosehn 				return (-1);
2472f8d7c56SGarance A Drosehn 			Dseen++;
2486a1485e2SGarance A Drosehn 			ptime->tmspec |= TSPEC_HOUROFDAY;
2492f8d7c56SGarance A Drosehn 			s++;
250c3033287SGarance A Drosehn 			l = strtol(s, &tmp, 10);
2512f8d7c56SGarance A Drosehn 			if (l < 0 || l > 23)
2522f8d7c56SGarance A Drosehn 				return (-1);
253c3033287SGarance A Drosehn 			endval = tmp;
2542f8d7c56SGarance A Drosehn 			tm.tm_hour = l;
2552f8d7c56SGarance A Drosehn 			break;
2562f8d7c56SGarance A Drosehn 
2572f8d7c56SGarance A Drosehn 		case 'W':
2582f8d7c56SGarance A Drosehn 			if (WMseen)
2592f8d7c56SGarance A Drosehn 				return (-1);
2602f8d7c56SGarance A Drosehn 			WMseen++;
2616a1485e2SGarance A Drosehn 			ptime->tmspec |= TSPEC_DAYOFWEEK;
2622f8d7c56SGarance A Drosehn 			s++;
263c3033287SGarance A Drosehn 			l = strtol(s, &tmp, 10);
2642f8d7c56SGarance A Drosehn 			if (l < 0 || l > 6)
2652f8d7c56SGarance A Drosehn 				return (-1);
266c3033287SGarance A Drosehn 			endval = tmp;
2672f8d7c56SGarance A Drosehn 			if (l != tm.tm_wday) {
2682f8d7c56SGarance A Drosehn 				int save;
2692f8d7c56SGarance A Drosehn 
2702f8d7c56SGarance A Drosehn 				if (l < tm.tm_wday) {
2712f8d7c56SGarance A Drosehn 					save = 6 - tm.tm_wday;
2722f8d7c56SGarance A Drosehn 					save += (l + 1);
2732f8d7c56SGarance A Drosehn 				} else {
2742f8d7c56SGarance A Drosehn 					save = l - tm.tm_wday;
2752f8d7c56SGarance A Drosehn 				}
2762f8d7c56SGarance A Drosehn 
2772f8d7c56SGarance A Drosehn 				tm.tm_mday += save;
2782f8d7c56SGarance A Drosehn 
279526d55a4SGarance A Drosehn 				if (tm.tm_mday > daysmon) {
2802f8d7c56SGarance A Drosehn 					tm.tm_mon++;
281526d55a4SGarance A Drosehn 					tm.tm_mday = tm.tm_mday - daysmon;
282*b7b447fdSGarance A Drosehn 					if (tm.tm_mon >= 12) {
283*b7b447fdSGarance A Drosehn 						tm.tm_mon = 0;
284*b7b447fdSGarance A Drosehn 						tm.tm_year++;
285*b7b447fdSGarance A Drosehn 					}
2862f8d7c56SGarance A Drosehn 				}
2872f8d7c56SGarance A Drosehn 			}
2882f8d7c56SGarance A Drosehn 			break;
2892f8d7c56SGarance A Drosehn 
2902f8d7c56SGarance A Drosehn 		case 'M':
2912f8d7c56SGarance A Drosehn 			if (WMseen)
2922f8d7c56SGarance A Drosehn 				return (-1);
2932f8d7c56SGarance A Drosehn 			WMseen++;
2946a1485e2SGarance A Drosehn 			ptime->tmspec |= TSPEC_DAYOFMONTH;
2952f8d7c56SGarance A Drosehn 			s++;
2962f8d7c56SGarance A Drosehn 			if (tolower(*s) == 'l') {
2976a1485e2SGarance A Drosehn 				/* User wants the last day of the month. */
2986a1485e2SGarance A Drosehn 				ptime->tmspec |= TSPEC_LDAYOFMONTH;
299526d55a4SGarance A Drosehn 				tm.tm_mday = daysmon;
300c3033287SGarance A Drosehn 				endval = s + 1;
3012f8d7c56SGarance A Drosehn 			} else {
302c3033287SGarance A Drosehn 				l = strtol(s, &tmp, 10);
3032f8d7c56SGarance A Drosehn 				if (l < 1 || l > 31)
3042f8d7c56SGarance A Drosehn 					return (-1);
3052f8d7c56SGarance A Drosehn 
306526d55a4SGarance A Drosehn 				if (l > daysmon)
3072f8d7c56SGarance A Drosehn 					return (-1);
308c3033287SGarance A Drosehn 				endval = tmp;
3092f8d7c56SGarance A Drosehn 				tm.tm_mday = l;
3102f8d7c56SGarance A Drosehn 			}
3112f8d7c56SGarance A Drosehn 			break;
3122f8d7c56SGarance A Drosehn 
3132f8d7c56SGarance A Drosehn 		default:
3142f8d7c56SGarance A Drosehn 			return (-1);
3152f8d7c56SGarance A Drosehn 			break;
3162f8d7c56SGarance A Drosehn 		}
3172f8d7c56SGarance A Drosehn 
318c3033287SGarance A Drosehn 		if (endval == NULL)
319c3033287SGarance A Drosehn 			return (-1);
320c3033287SGarance A Drosehn 		else if (*endval == '\0' || isspace(*endval))
3212f8d7c56SGarance A Drosehn 			break;
3222f8d7c56SGarance A Drosehn 		else
323c3033287SGarance A Drosehn 			s = endval;
3242f8d7c56SGarance A Drosehn 	}
3252f8d7c56SGarance A Drosehn 
3266a1485e2SGarance A Drosehn 	ptime->tm = tm;
3276a1485e2SGarance A Drosehn 	return (0);
3286a1485e2SGarance A Drosehn }
3296a1485e2SGarance A Drosehn 
3302f8d7c56SGarance A Drosehn /*
3316a1485e2SGarance A Drosehn  * Initialize a new ptime-related data area.
3322f8d7c56SGarance A Drosehn  */
3336a1485e2SGarance A Drosehn struct ptime_data *
3346a1485e2SGarance A Drosehn ptime_init(const struct ptime_data *optsrc)
3356a1485e2SGarance A Drosehn {
3366a1485e2SGarance A Drosehn 	struct ptime_data *newdata;
3376a1485e2SGarance A Drosehn 
3386a1485e2SGarance A Drosehn 	newdata = malloc(sizeof(struct ptime_data));
3396a1485e2SGarance A Drosehn 	if (optsrc != NULL) {
3406a1485e2SGarance A Drosehn 		memcpy(newdata, optsrc, sizeof(struct ptime_data));
3416a1485e2SGarance A Drosehn 	} else {
3426a1485e2SGarance A Drosehn 		memset(newdata, '\0', sizeof(struct ptime_data));
3436a1485e2SGarance A Drosehn 		newdata->did_adj4dst = TNYET_ADJ4DST;
3446a1485e2SGarance A Drosehn 	}
3456a1485e2SGarance A Drosehn 
3466a1485e2SGarance A Drosehn 	return (newdata);
3476a1485e2SGarance A Drosehn }
3486a1485e2SGarance A Drosehn 
3496a1485e2SGarance A Drosehn /*
3506a1485e2SGarance A Drosehn  * Adjust a given time if that time is in a different timezone than
3516a1485e2SGarance A Drosehn  * some other time.
3526a1485e2SGarance A Drosehn  */
3536a1485e2SGarance A Drosehn int
3546a1485e2SGarance A Drosehn ptime_adjust4dst(struct ptime_data *ptime, const struct ptime_data *dstsrc)
3556a1485e2SGarance A Drosehn {
3566a1485e2SGarance A Drosehn 	struct ptime_data adjtime;
3576a1485e2SGarance A Drosehn 
3586a1485e2SGarance A Drosehn 	if (ptime == NULL)
3596a1485e2SGarance A Drosehn 		return (-1);
3606a1485e2SGarance A Drosehn 
3616a1485e2SGarance A Drosehn 	/*
3626a1485e2SGarance A Drosehn 	 * Changes are not made to the given time until after all
3636a1485e2SGarance A Drosehn 	 * of the calculations have been successful.
3646a1485e2SGarance A Drosehn 	 */
3656a1485e2SGarance A Drosehn 	adjtime = *ptime;
3666a1485e2SGarance A Drosehn 
3676a1485e2SGarance A Drosehn 	/* Check to see if this adjustment was already made */
3686a1485e2SGarance A Drosehn 	if ((adjtime.did_adj4dst != TNYET_ADJ4DST) &&
3696a1485e2SGarance A Drosehn 	    (adjtime.did_adj4dst == dstsrc->tm.tm_isdst))
3706a1485e2SGarance A Drosehn 		return (0);		/* yes, so don't make it twice */
3716a1485e2SGarance A Drosehn 
3726a1485e2SGarance A Drosehn 	/* See if daylight-saving has changed between the two times. */
3736a1485e2SGarance A Drosehn 	if (dstsrc->tm.tm_isdst != adjtime.tm.tm_isdst) {
3746a1485e2SGarance A Drosehn 		if (adjtime.tm.tm_isdst == 1)
3756a1485e2SGarance A Drosehn 			adjtime.tsecs -= SECS_PER_HOUR;
3766a1485e2SGarance A Drosehn 		else if (adjtime.tm.tm_isdst == 0)
3776a1485e2SGarance A Drosehn 			adjtime.tsecs += SECS_PER_HOUR;
3786a1485e2SGarance A Drosehn 		adjtime.tm = *(localtime(&adjtime.tsecs));
3796a1485e2SGarance A Drosehn 		/* Remember that this adjustment has been made */
3806a1485e2SGarance A Drosehn 		adjtime.did_adj4dst = dstsrc->tm.tm_isdst;
3816a1485e2SGarance A Drosehn 		/*
3826a1485e2SGarance A Drosehn 		 * XXX - Should probably check to see if changing the
3836a1485e2SGarance A Drosehn 		 *	hour also changed the value of is_dst.  What
3846a1485e2SGarance A Drosehn 		 *	should we do in that case?
3856a1485e2SGarance A Drosehn 		 */
3866a1485e2SGarance A Drosehn 	}
3876a1485e2SGarance A Drosehn 
3886a1485e2SGarance A Drosehn 	*ptime = adjtime;
3896a1485e2SGarance A Drosehn 	return (0);
3906a1485e2SGarance A Drosehn }
3916a1485e2SGarance A Drosehn 
3926a1485e2SGarance A Drosehn int
3936a1485e2SGarance A Drosehn ptime_relparse(struct ptime_data *ptime, int parseopts, time_t basetime,
3946a1485e2SGarance A Drosehn     const char *str)
3956a1485e2SGarance A Drosehn {
3966a1485e2SGarance A Drosehn 	int dpm, pres;
3976a1485e2SGarance A Drosehn 	struct tm temp_tm;
3986a1485e2SGarance A Drosehn 
3996a1485e2SGarance A Drosehn 	ptime->parseopts = parseopts;
4006a1485e2SGarance A Drosehn 	ptime->basesecs = basetime;
4016a1485e2SGarance A Drosehn 	ptime->basetm = *(localtime(&ptime->basesecs));
4026a1485e2SGarance A Drosehn 	ptime->tm = ptime->basetm;
4036a1485e2SGarance A Drosehn 	ptime->tm.tm_hour = ptime->tm.tm_min = ptime->tm.tm_sec = 0;
4046a1485e2SGarance A Drosehn 
4056a1485e2SGarance A Drosehn 	/*
4066a1485e2SGarance A Drosehn 	 * Call a routine which sets ptime.tm and ptime.tspecs based
4076a1485e2SGarance A Drosehn 	 * on the given string and parsing-options.  Note that the
4086a1485e2SGarance A Drosehn 	 * routine should not call mktime to set ptime.tsecs.
4096a1485e2SGarance A Drosehn 	 */
4106a1485e2SGarance A Drosehn 	if (parseopts & PTM_PARSE_DWM)
4116a1485e2SGarance A Drosehn 		pres = parseDWM(ptime, str);
4126a1485e2SGarance A Drosehn 	else
4136a1485e2SGarance A Drosehn 		pres = parse8601(ptime, str);
4146a1485e2SGarance A Drosehn 	if (pres < 0) {
4156a1485e2SGarance A Drosehn 		ptime->tsecs = (time_t)pres;
4166a1485e2SGarance A Drosehn 		return (pres);
4176a1485e2SGarance A Drosehn 	}
4186a1485e2SGarance A Drosehn 
4196a1485e2SGarance A Drosehn 	/*
4206a1485e2SGarance A Drosehn 	 * Before calling mktime, check to see if we ended up with a
4216a1485e2SGarance A Drosehn 	 * "day-of-month" that does not exist in the selected month.
4226a1485e2SGarance A Drosehn 	 * If we did call mktime with that info, then mktime will
4236a1485e2SGarance A Drosehn 	 * make it look like the user specifically requested a day
4246a1485e2SGarance A Drosehn 	 * in the following month (eg: Feb 31 turns into Mar 3rd).
4256a1485e2SGarance A Drosehn 	 */
4266a1485e2SGarance A Drosehn 	dpm = days_pmonth(ptime->tm.tm_mon, ptime->tm.tm_year);
4276a1485e2SGarance A Drosehn 	if ((parseopts & PTM_PARSE_MATCHDOM) &&
4286a1485e2SGarance A Drosehn 	    (ptime->tmspec & TSPEC_DAYOFMONTH) &&
4296a1485e2SGarance A Drosehn 	    (ptime->tm.tm_mday> dpm)) {
4306a1485e2SGarance A Drosehn 		/*
4316a1485e2SGarance A Drosehn 		 * ptime_nxtime() will want a ptime->tsecs value,
4326a1485e2SGarance A Drosehn 		 * but we need to avoid mktime resetting all the
4336a1485e2SGarance A Drosehn 		 * ptime->tm values.
4346a1485e2SGarance A Drosehn 		 */
4356a1485e2SGarance A Drosehn 		if (verbose && dbg_at_times > 1)
4366a1485e2SGarance A Drosehn 			fprintf(stderr,
4376a1485e2SGarance A Drosehn 			    "\t-- dom fixed: %4d/%02d/%02d %02d:%02d (%02d)",
4386a1485e2SGarance A Drosehn 			    ptime->tm.tm_year, ptime->tm.tm_mon,
4396a1485e2SGarance A Drosehn 			    ptime->tm.tm_mday, ptime->tm.tm_hour,
4406a1485e2SGarance A Drosehn 			    ptime->tm.tm_min, dpm);
4416a1485e2SGarance A Drosehn 		temp_tm = ptime->tm;
4426a1485e2SGarance A Drosehn 		ptime->tsecs = mktime(&temp_tm);
4436a1485e2SGarance A Drosehn 		if (ptime->tsecs > (time_t)-1)
4446a1485e2SGarance A Drosehn 			ptimeset_nxtime(ptime);
4456a1485e2SGarance A Drosehn 		if (verbose && dbg_at_times > 1)
4466a1485e2SGarance A Drosehn 			fprintf(stderr,
4476a1485e2SGarance A Drosehn 			    " to: %4d/%02d/%02d %02d:%02d\n",
4486a1485e2SGarance A Drosehn 			    ptime->tm.tm_year, ptime->tm.tm_mon,
4496a1485e2SGarance A Drosehn 			    ptime->tm.tm_mday, ptime->tm.tm_hour,
4506a1485e2SGarance A Drosehn 			    ptime->tm.tm_min);
4516a1485e2SGarance A Drosehn 	}
4526a1485e2SGarance A Drosehn 
4536a1485e2SGarance A Drosehn 	/*
4546a1485e2SGarance A Drosehn 	 * Convert the ptime.tm into standard time_t seconds.  Check
4556a1485e2SGarance A Drosehn 	 * for invalid times, which includes things like the hour lost
4566a1485e2SGarance A Drosehn 	 * when switching from "standard time" to "daylight saving".
4576a1485e2SGarance A Drosehn 	 */
4586a1485e2SGarance A Drosehn 	ptime->tsecs = mktime(&ptime->tm);
4596a1485e2SGarance A Drosehn 	if (ptime->tsecs == (time_t)-1) {
4606a1485e2SGarance A Drosehn 		ptime->tsecs = (time_t)-2;
4616a1485e2SGarance A Drosehn 		return (-2);
4626a1485e2SGarance A Drosehn 	}
4636a1485e2SGarance A Drosehn 
4646a1485e2SGarance A Drosehn 	return (0);
4656a1485e2SGarance A Drosehn }
4666a1485e2SGarance A Drosehn 
4676a1485e2SGarance A Drosehn int
4686a1485e2SGarance A Drosehn ptime_free(struct ptime_data *ptime)
4696a1485e2SGarance A Drosehn {
4706a1485e2SGarance A Drosehn 
4716a1485e2SGarance A Drosehn 	if (ptime == NULL)
4726a1485e2SGarance A Drosehn 		return (-1);
4736a1485e2SGarance A Drosehn 
4746a1485e2SGarance A Drosehn 	free(ptime);
4756a1485e2SGarance A Drosehn 	return (0);
4766a1485e2SGarance A Drosehn }
4776a1485e2SGarance A Drosehn 
4786a1485e2SGarance A Drosehn /*
4796a1485e2SGarance A Drosehn  * Some trivial routines so ptime_data can remain a completely
4806a1485e2SGarance A Drosehn  * opaque type.
4816a1485e2SGarance A Drosehn  */
4826a1485e2SGarance A Drosehn const char *
4836a1485e2SGarance A Drosehn ptimeget_ctime(const struct ptime_data *ptime)
4846a1485e2SGarance A Drosehn {
4856a1485e2SGarance A Drosehn 
4866a1485e2SGarance A Drosehn 	if (ptime == NULL)
4876a1485e2SGarance A Drosehn 		return ("Null time in ptimeget_ctime()\n");
4886a1485e2SGarance A Drosehn 
4896a1485e2SGarance A Drosehn 	return (ctime(&ptime->tsecs));
4906a1485e2SGarance A Drosehn }
4916a1485e2SGarance A Drosehn 
492b326fec4SDavid Bright /*
493b326fec4SDavid Bright  * Generate a time of day string in an RFC5424 compatible format. Return a
494b326fec4SDavid Bright  * pointer to the buffer with the timestamp string or NULL if an error. If the
495b326fec4SDavid Bright  * time is not supplied, cannot be converted to local time, or the resulting
496b326fec4SDavid Bright  * string would overflow the buffer, the returned string will be the RFC5424
497b326fec4SDavid Bright  * NILVALUE.
498b326fec4SDavid Bright  */
499b326fec4SDavid Bright char *
500b326fec4SDavid Bright ptimeget_ctime_rfc5424(const struct ptime_data *ptime,
501b326fec4SDavid Bright     char *timebuf, size_t bufsize)
502b326fec4SDavid Bright {
503b326fec4SDavid Bright 	static const char NILVALUE[] = {"-"};	/* RFC5424 specified NILVALUE */
504b326fec4SDavid Bright 	int chars;
505b326fec4SDavid Bright 	struct tm tm;
506b326fec4SDavid Bright 	int tz_hours;
507b326fec4SDavid Bright 	int tz_mins;
508b326fec4SDavid Bright 	long tz_offset;
509b326fec4SDavid Bright 	char tz_sign;
510b326fec4SDavid Bright 
511b326fec4SDavid Bright 	if (timebuf == NULL) {
512b326fec4SDavid Bright 		return (NULL);
513b326fec4SDavid Bright 	}
514b326fec4SDavid Bright 
515b326fec4SDavid Bright 	if (bufsize < sizeof(NILVALUE)) {
516b326fec4SDavid Bright 		return (NULL);
517b326fec4SDavid Bright 	}
518b326fec4SDavid Bright 
519b326fec4SDavid Bright 	/*
520b326fec4SDavid Bright 	 * Convert to localtime. RFC5424 mandates the use of the NILVALUE if
521b326fec4SDavid Bright 	 * the time cannot be obtained, so use that if there is an error in the
522b326fec4SDavid Bright 	 * conversion.
523b326fec4SDavid Bright 	 */
524b326fec4SDavid Bright 	if (ptime == NULL || localtime_r(&(ptime->tsecs), &tm) == NULL) {
525b326fec4SDavid Bright 		strlcpy(timebuf, NILVALUE, bufsize);
526b326fec4SDavid Bright 		return (timebuf);
527b326fec4SDavid Bright 	}
528b326fec4SDavid Bright 
529b326fec4SDavid Bright 	/*
530b326fec4SDavid Bright 	 * Convert the time to a string in RFC5424 format. The conversion
531b326fec4SDavid Bright 	 * cannot be done with strftime() because it cannot produce the correct
532b326fec4SDavid Bright 	 * timezone offset format.
533b326fec4SDavid Bright 	 */
534b326fec4SDavid Bright 	if (tm.tm_gmtoff < 0) {
535b326fec4SDavid Bright 		tz_sign = '-';
536b326fec4SDavid Bright 		tz_offset = -tm.tm_gmtoff;
537b326fec4SDavid Bright 	} else {
538b326fec4SDavid Bright 		tz_sign = '+';
539b326fec4SDavid Bright 		tz_offset = tm.tm_gmtoff;
540b326fec4SDavid Bright 	}
541b326fec4SDavid Bright 
542b326fec4SDavid Bright 	tz_hours = tz_offset / 3600;
543b326fec4SDavid Bright 	tz_mins = (tz_offset % 3600) / 60;
544b326fec4SDavid Bright 
545b326fec4SDavid Bright 	chars = snprintf(timebuf, bufsize,
546b326fec4SDavid Bright 	    "%04d-%02d-%02d"	/* date */
547b326fec4SDavid Bright 	    "T%02d:%02d:%02d"	/* time */
548b326fec4SDavid Bright 	    "%c%02d:%02d",	/* time zone offset */
549b326fec4SDavid Bright 	    tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
550b326fec4SDavid Bright 	    tm.tm_hour, tm.tm_min, tm.tm_sec,
551b326fec4SDavid Bright 	    tz_sign, tz_hours, tz_mins);
552b326fec4SDavid Bright 
553b326fec4SDavid Bright 	/* If the timestamp is too big for timebuf, return the NILVALUE. */
554b326fec4SDavid Bright 	if (chars >= (int)bufsize) {
555b326fec4SDavid Bright 		strlcpy(timebuf, NILVALUE, bufsize);
556b326fec4SDavid Bright 	}
557b326fec4SDavid Bright 
558b326fec4SDavid Bright 	return (timebuf);
559b326fec4SDavid Bright }
560b326fec4SDavid Bright 
5616a1485e2SGarance A Drosehn double
5626a1485e2SGarance A Drosehn ptimeget_diff(const struct ptime_data *minuend, const struct
5636a1485e2SGarance A Drosehn     ptime_data *subtrahend)
5646a1485e2SGarance A Drosehn {
5656a1485e2SGarance A Drosehn 
5666a1485e2SGarance A Drosehn 	/* Just like difftime(), we have no good error-return */
5676a1485e2SGarance A Drosehn 	if (minuend == NULL || subtrahend == NULL)
5686a1485e2SGarance A Drosehn 		return (0.0);
5696a1485e2SGarance A Drosehn 
5706a1485e2SGarance A Drosehn 	return (difftime(minuend->tsecs, subtrahend->tsecs));
5716a1485e2SGarance A Drosehn }
5726a1485e2SGarance A Drosehn 
5736a1485e2SGarance A Drosehn time_t
5746a1485e2SGarance A Drosehn ptimeget_secs(const struct ptime_data *ptime)
5756a1485e2SGarance A Drosehn {
5766a1485e2SGarance A Drosehn 
5776a1485e2SGarance A Drosehn 	if (ptime == NULL)
5786a1485e2SGarance A Drosehn 		return (-1);
5796a1485e2SGarance A Drosehn 
5806a1485e2SGarance A Drosehn 	return (ptime->tsecs);
5816a1485e2SGarance A Drosehn }
5826a1485e2SGarance A Drosehn 
5836a1485e2SGarance A Drosehn /*
5846a1485e2SGarance A Drosehn  * Generate an approximate timestamp for the next event, based on
5856a1485e2SGarance A Drosehn  * what parts of time were specified by the original parameter to
5866a1485e2SGarance A Drosehn  * ptime_relparse(). The result may be -1 if there is no obvious
5876a1485e2SGarance A Drosehn  * "next time" which will work.
5886a1485e2SGarance A Drosehn  */
5896a1485e2SGarance A Drosehn int
5906a1485e2SGarance A Drosehn ptimeset_nxtime(struct ptime_data *ptime)
5916a1485e2SGarance A Drosehn {
5926a1485e2SGarance A Drosehn 	int moredays, tdpm, tmon, tyear;
5936a1485e2SGarance A Drosehn 	struct ptime_data nextmatch;
5946a1485e2SGarance A Drosehn 
5956a1485e2SGarance A Drosehn 	if (ptime == NULL)
5966a1485e2SGarance A Drosehn 		return (-1);
5976a1485e2SGarance A Drosehn 
5986a1485e2SGarance A Drosehn 	/*
5996a1485e2SGarance A Drosehn 	 * Changes are not made to the given time until after all
6006a1485e2SGarance A Drosehn 	 * of the calculations have been successful.
6016a1485e2SGarance A Drosehn 	 */
6026a1485e2SGarance A Drosehn 	nextmatch = *ptime;
6036a1485e2SGarance A Drosehn 	/*
6046a1485e2SGarance A Drosehn 	 * If the user specified a year and we're already past that
6056a1485e2SGarance A Drosehn 	 * time, then there will never be another one!
6066a1485e2SGarance A Drosehn 	 */
6076a1485e2SGarance A Drosehn 	if (ptime->tmspec & TSPEC_YEAR)
6086a1485e2SGarance A Drosehn 		return (-1);
6096a1485e2SGarance A Drosehn 
6106a1485e2SGarance A Drosehn 	/*
6116a1485e2SGarance A Drosehn 	 * The caller gave us a time in the past.  Calculate how much
6126a1485e2SGarance A Drosehn 	 * time is needed to go from that valid rotate time to the
6136a1485e2SGarance A Drosehn 	 * next valid rotate time.  We only need to get to the nearest
6146a1485e2SGarance A Drosehn 	 * hour, because newsyslog is only run once per hour.
6156a1485e2SGarance A Drosehn 	 */
6166a1485e2SGarance A Drosehn 	moredays = 0;
6176a1485e2SGarance A Drosehn 	if (ptime->tmspec & TSPEC_MONTHOFYEAR) {
6186a1485e2SGarance A Drosehn 		/* Special case: Feb 29th does not happen every year. */
6196a1485e2SGarance A Drosehn 		if (ptime->tm.tm_mon == 1 && ptime->tm.tm_mday == 29) {
6206a1485e2SGarance A Drosehn 			nextmatch.tm.tm_year += 4;
6216a1485e2SGarance A Drosehn 			if (days_pmonth(1, nextmatch.tm.tm_year) < 29)
6226a1485e2SGarance A Drosehn 				nextmatch.tm.tm_year += 4;
6236a1485e2SGarance A Drosehn 		} else {
6246a1485e2SGarance A Drosehn 			nextmatch.tm.tm_year += 1;
6256a1485e2SGarance A Drosehn 		}
6266a1485e2SGarance A Drosehn 		nextmatch.tm.tm_isdst = -1;
6276a1485e2SGarance A Drosehn 		nextmatch.tsecs = mktime(&nextmatch.tm);
6286a1485e2SGarance A Drosehn 
6296a1485e2SGarance A Drosehn 	} else if (ptime->tmspec & TSPEC_LDAYOFMONTH) {
6306a1485e2SGarance A Drosehn 		/*
6316a1485e2SGarance A Drosehn 		 * Need to get to the last day of next month.  Origtm is
6326a1485e2SGarance A Drosehn 		 * already at the last day of this month, so just add to
6336a1485e2SGarance A Drosehn 		 * it number of days in the next month.
6346a1485e2SGarance A Drosehn 		 */
6356a1485e2SGarance A Drosehn 		if (ptime->tm.tm_mon < 11)
6366a1485e2SGarance A Drosehn 			moredays = days_pmonth(ptime->tm.tm_mon + 1,
6376a1485e2SGarance A Drosehn 			    ptime->tm.tm_year);
6386a1485e2SGarance A Drosehn 		else
6396a1485e2SGarance A Drosehn 			moredays = days_pmonth(0, ptime->tm.tm_year + 1);
6406a1485e2SGarance A Drosehn 
6416a1485e2SGarance A Drosehn 	} else if (ptime->tmspec & TSPEC_DAYOFMONTH) {
6426a1485e2SGarance A Drosehn 		/* Jump to the same day in the next month */
6436a1485e2SGarance A Drosehn 		moredays = days_pmonth(ptime->tm.tm_mon, ptime->tm.tm_year);
6446a1485e2SGarance A Drosehn 		/*
6456a1485e2SGarance A Drosehn 		 * In some cases, the next month may not *have* the
6466a1485e2SGarance A Drosehn 		 * desired day-of-the-month.  If that happens, then
6476a1485e2SGarance A Drosehn 		 * move to the next month that does have enough days.
6486a1485e2SGarance A Drosehn 		 */
6496a1485e2SGarance A Drosehn 		tmon = ptime->tm.tm_mon;
6506a1485e2SGarance A Drosehn 		tyear = ptime->tm.tm_year;
6516a1485e2SGarance A Drosehn 		for (;;) {
6526a1485e2SGarance A Drosehn 			if (tmon < 11)
6536a1485e2SGarance A Drosehn 				tmon += 1;
6546a1485e2SGarance A Drosehn 			else {
6556a1485e2SGarance A Drosehn 				tmon = 0;
6566a1485e2SGarance A Drosehn 				tyear += 1;
6576a1485e2SGarance A Drosehn 			}
6586a1485e2SGarance A Drosehn 			tdpm = days_pmonth(tmon, tyear);
6596a1485e2SGarance A Drosehn 			if (tdpm >= ptime->tm.tm_mday)
6606a1485e2SGarance A Drosehn 				break;
6616a1485e2SGarance A Drosehn 			moredays += tdpm;
6626a1485e2SGarance A Drosehn 		}
6636a1485e2SGarance A Drosehn 
6646a1485e2SGarance A Drosehn 	} else if (ptime->tmspec & TSPEC_DAYOFWEEK) {
6656a1485e2SGarance A Drosehn 		moredays = 7;
6666a1485e2SGarance A Drosehn 	} else if (ptime->tmspec & TSPEC_HOUROFDAY) {
6676a1485e2SGarance A Drosehn 		moredays = 1;
6686a1485e2SGarance A Drosehn 	}
6696a1485e2SGarance A Drosehn 
6706a1485e2SGarance A Drosehn 	if (moredays != 0) {
6716a1485e2SGarance A Drosehn 		nextmatch.tsecs += SECS_PER_HOUR * 24 * moredays;
6726a1485e2SGarance A Drosehn 		nextmatch.tm = *(localtime(&nextmatch.tsecs));
6736a1485e2SGarance A Drosehn 	}
6746a1485e2SGarance A Drosehn 
6756a1485e2SGarance A Drosehn 	/*
6766a1485e2SGarance A Drosehn 	 * The new time will need to be adjusted if the setting of
6776a1485e2SGarance A Drosehn 	 * daylight-saving has changed between the two times.
6786a1485e2SGarance A Drosehn 	 */
6796a1485e2SGarance A Drosehn 	ptime_adjust4dst(&nextmatch, ptime);
6806a1485e2SGarance A Drosehn 
6816a1485e2SGarance A Drosehn 	/* Everything worked.  Update the given time and return. */
6826a1485e2SGarance A Drosehn 	*ptime = nextmatch;
6836a1485e2SGarance A Drosehn 	return (0);
6846a1485e2SGarance A Drosehn }
6856a1485e2SGarance A Drosehn 
6866a1485e2SGarance A Drosehn int
6876a1485e2SGarance A Drosehn ptimeset_time(struct ptime_data *ptime, time_t secs)
6886a1485e2SGarance A Drosehn {
6896a1485e2SGarance A Drosehn 
6906a1485e2SGarance A Drosehn 	if (ptime == NULL)
6916a1485e2SGarance A Drosehn 		return (-1);
6926a1485e2SGarance A Drosehn 
6936a1485e2SGarance A Drosehn 	ptime->tsecs = secs;
6946a1485e2SGarance A Drosehn 	ptime->tm = *(localtime(&ptime->tsecs));
6956a1485e2SGarance A Drosehn 	ptime->parseopts = 0;
6966a1485e2SGarance A Drosehn 	/* ptime->tmspec = ? */
6976a1485e2SGarance A Drosehn 	return (0);
6982f8d7c56SGarance A Drosehn }
699