xref: /freebsd/usr.sbin/newsyslog/ptimes.c (revision 1d386b48a555f61cb7325543adbbb5c3f3407a66)
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 #include <ctype.h>
452f8d7c56SGarance A Drosehn #include <limits.h>
462f8d7c56SGarance A Drosehn #include <stdio.h>
472f8d7c56SGarance A Drosehn #include <stdlib.h>
486a1485e2SGarance A Drosehn #include <string.h>
492f8d7c56SGarance A Drosehn #include <time.h>
502f8d7c56SGarance A Drosehn 
512f8d7c56SGarance A Drosehn #include "extern.h"
522f8d7c56SGarance A Drosehn 
536a1485e2SGarance A Drosehn #define	SECS_PER_HOUR	3600
546a1485e2SGarance A Drosehn 
556a1485e2SGarance A Drosehn /*
566a1485e2SGarance A Drosehn  * Bit-values which indicate which components of time were specified
576a1485e2SGarance A Drosehn  * by the string given to parse8601 or parseDWM.  These are needed to
586a1485e2SGarance A Drosehn  * calculate what time-in-the-future will match that string.
596a1485e2SGarance A Drosehn  */
606a1485e2SGarance A Drosehn #define	TSPEC_YEAR		0x0001
616a1485e2SGarance A Drosehn #define	TSPEC_MONTHOFYEAR	0x0002
626a1485e2SGarance A Drosehn #define	TSPEC_LDAYOFMONTH	0x0004
636a1485e2SGarance A Drosehn #define	TSPEC_DAYOFMONTH	0x0008
646a1485e2SGarance A Drosehn #define	TSPEC_DAYOFWEEK		0x0010
656a1485e2SGarance A Drosehn #define	TSPEC_HOUROFDAY		0x0020
666a1485e2SGarance A Drosehn 
676a1485e2SGarance A Drosehn #define	TNYET_ADJ4DST		-10	/* DST has "not yet" been adjusted */
686a1485e2SGarance A Drosehn 
696a1485e2SGarance A Drosehn struct ptime_data {
706a1485e2SGarance A Drosehn 	time_t		 basesecs;	/* Base point for relative times */
716a1485e2SGarance A Drosehn 	time_t		 tsecs;		/* Time in seconds */
726a1485e2SGarance A Drosehn 	struct tm	 basetm;	/* Base Time expanded into fields */
736a1485e2SGarance A Drosehn 	struct tm	 tm;		/* Time expanded into fields */
746a1485e2SGarance A Drosehn 	int		 did_adj4dst;	/* Track calls to ptime_adjust4dst */
756a1485e2SGarance A Drosehn 	int		 parseopts;	/* Options given for parsing */
766a1485e2SGarance A Drosehn 	int		 tmspec;	/* Indicates which time fields had
776a1485e2SGarance A Drosehn 					 * been specified by the user */
786a1485e2SGarance A Drosehn };
796a1485e2SGarance A Drosehn 
80526d55a4SGarance A Drosehn static int	 days_pmonth(int month, int year);
816a1485e2SGarance A Drosehn static int	 parse8601(struct ptime_data *ptime, const char *str);
826a1485e2SGarance A Drosehn static int	 parseDWM(struct ptime_data *ptime, const char *str);
83526d55a4SGarance A Drosehn 
84526d55a4SGarance A Drosehn /*
85526d55a4SGarance A Drosehn  * Simple routine to calculate the number of days in a given month.
86526d55a4SGarance A Drosehn  */
87526d55a4SGarance A Drosehn static int
days_pmonth(int month,int year)88526d55a4SGarance A Drosehn days_pmonth(int month, int year)
89526d55a4SGarance A Drosehn {
90526d55a4SGarance A Drosehn 	static const int mtab[] = {31, 28, 31, 30, 31, 30, 31, 31,
91526d55a4SGarance A Drosehn 	    30, 31, 30, 31};
92526d55a4SGarance A Drosehn 	int ndays;
93526d55a4SGarance A Drosehn 
94526d55a4SGarance A Drosehn 	ndays = mtab[month];
95526d55a4SGarance A Drosehn 
96526d55a4SGarance A Drosehn 	if (month == 1) {
97526d55a4SGarance A Drosehn 		/*
98526d55a4SGarance A Drosehn 		 * We are usually called with a 'tm-year' value
99526d55a4SGarance A Drosehn 		 * (ie, the value = the number of years past 1900).
100526d55a4SGarance A Drosehn 		 */
101526d55a4SGarance A Drosehn 		if (year < 1900)
102526d55a4SGarance A Drosehn 			year += 1900;
103526d55a4SGarance A Drosehn 		if (year % 4 == 0) {
104526d55a4SGarance A Drosehn 			/*
105526d55a4SGarance A Drosehn 			 * This is a leap year, as long as it is not a
106526d55a4SGarance A Drosehn 			 * multiple of 100, or if it is a multiple of
107526d55a4SGarance A Drosehn 			 * both 100 and 400.
108526d55a4SGarance A Drosehn 			 */
109526d55a4SGarance A Drosehn 			if (year % 100 != 0)
110526d55a4SGarance A Drosehn 				ndays++;	/* not multiple of 100 */
111526d55a4SGarance A Drosehn 			else if (year % 400 == 0)
112526d55a4SGarance A Drosehn 				ndays++;	/* is multiple of 100 and 400 */
113526d55a4SGarance A Drosehn 		}
114526d55a4SGarance A Drosehn 	}
115526d55a4SGarance A Drosehn 	return (ndays);
116526d55a4SGarance A Drosehn }
117526d55a4SGarance A Drosehn 
1182f8d7c56SGarance A Drosehn /*-
1192f8d7c56SGarance A Drosehn  * Parse a limited subset of ISO 8601. The specific format is as follows:
1202f8d7c56SGarance A Drosehn  *
1212f8d7c56SGarance A Drosehn  * [CC[YY[MM[DD]]]][THH[MM[SS]]]	(where `T' is the literal letter)
1222f8d7c56SGarance A Drosehn  *
1232f8d7c56SGarance A Drosehn  * We don't accept a timezone specification; missing fields (including timezone)
1242f8d7c56SGarance A Drosehn  * are defaulted to the current date but time zero.
1252f8d7c56SGarance A Drosehn  */
1266a1485e2SGarance A Drosehn static int
parse8601(struct ptime_data * ptime,const char * s)1276a1485e2SGarance A Drosehn parse8601(struct ptime_data *ptime, const char *s)
1282f8d7c56SGarance A Drosehn {
1292f8d7c56SGarance A Drosehn 	char *t;
1302f8d7c56SGarance A Drosehn 	long l;
1316a1485e2SGarance A Drosehn 	struct tm tm;
1322f8d7c56SGarance A Drosehn 
1332f8d7c56SGarance A Drosehn 	l = strtol(s, &t, 10);
1342f8d7c56SGarance A Drosehn 	if (l < 0 || l >= INT_MAX || (*t != '\0' && *t != 'T'))
1352f8d7c56SGarance A Drosehn 		return (-1);
1362f8d7c56SGarance A Drosehn 
1372f8d7c56SGarance A Drosehn 	/*
1382f8d7c56SGarance A Drosehn 	 * Now t points either to the end of the string (if no time was
1392f8d7c56SGarance A Drosehn 	 * provided) or to the letter `T' which separates date and time in
1402f8d7c56SGarance A Drosehn 	 * ISO 8601.  The pointer arithmetic is the same for either case.
1412f8d7c56SGarance A Drosehn 	 */
1426a1485e2SGarance A Drosehn 	tm = ptime->tm;
1436a1485e2SGarance A Drosehn 	ptime->tmspec = TSPEC_HOUROFDAY;
1442f8d7c56SGarance A Drosehn 	switch (t - s) {
1452f8d7c56SGarance A Drosehn 	case 8:
1462f8d7c56SGarance A Drosehn 		tm.tm_year = ((l / 1000000) - 19) * 100;
1472f8d7c56SGarance A Drosehn 		l = l % 1000000;
1484c8f6471SMark Johnston 		/* FALLTHROUGH */
1492f8d7c56SGarance A Drosehn 	case 6:
1506a1485e2SGarance A Drosehn 		ptime->tmspec |= TSPEC_YEAR;
1512f8d7c56SGarance A Drosehn 		tm.tm_year -= tm.tm_year % 100;
1522f8d7c56SGarance A Drosehn 		tm.tm_year += l / 10000;
1532f8d7c56SGarance A Drosehn 		l = l % 10000;
1544c8f6471SMark Johnston 		/* FALLTHROUGH */
1552f8d7c56SGarance A Drosehn 	case 4:
1566a1485e2SGarance A Drosehn 		ptime->tmspec |= TSPEC_MONTHOFYEAR;
1572f8d7c56SGarance A Drosehn 		tm.tm_mon = (l / 100) - 1;
1582f8d7c56SGarance A Drosehn 		l = l % 100;
1594c8f6471SMark Johnston 		/* FALLTHROUGH */
1602f8d7c56SGarance A Drosehn 	case 2:
1616a1485e2SGarance A Drosehn 		ptime->tmspec |= TSPEC_DAYOFMONTH;
1622f8d7c56SGarance A Drosehn 		tm.tm_mday = l;
1634c8f6471SMark Johnston 		/* FALLTHROUGH */
1642f8d7c56SGarance A Drosehn 	case 0:
1652f8d7c56SGarance A Drosehn 		break;
1662f8d7c56SGarance A Drosehn 	default:
1672f8d7c56SGarance A Drosehn 		return (-1);
1682f8d7c56SGarance A Drosehn 	}
1692f8d7c56SGarance A Drosehn 
1702f8d7c56SGarance A Drosehn 	/* sanity check */
1712f8d7c56SGarance A Drosehn 	if (tm.tm_year < 70 || tm.tm_mon < 0 || tm.tm_mon > 12
1722f8d7c56SGarance A Drosehn 	    || tm.tm_mday < 1 || tm.tm_mday > 31)
1732f8d7c56SGarance A Drosehn 		return (-1);
1742f8d7c56SGarance A Drosehn 
1752f8d7c56SGarance A Drosehn 	if (*t != '\0') {
1762f8d7c56SGarance A Drosehn 		s = ++t;
1772f8d7c56SGarance A Drosehn 		l = strtol(s, &t, 10);
1782f8d7c56SGarance A Drosehn 		if (l < 0 || l >= INT_MAX || (*t != '\0' && !isspace(*t)))
1792f8d7c56SGarance A Drosehn 			return (-1);
1802f8d7c56SGarance A Drosehn 
1812f8d7c56SGarance A Drosehn 		switch (t - s) {
1822f8d7c56SGarance A Drosehn 		case 6:
1832f8d7c56SGarance A Drosehn 			tm.tm_sec = l % 100;
1842f8d7c56SGarance A Drosehn 			l /= 100;
1854c8f6471SMark Johnston 			/* FALLTHROUGH */
1862f8d7c56SGarance A Drosehn 		case 4:
1872f8d7c56SGarance A Drosehn 			tm.tm_min = l % 100;
1882f8d7c56SGarance A Drosehn 			l /= 100;
1894c8f6471SMark Johnston 			/* FALLTHROUGH */
1902f8d7c56SGarance A Drosehn 		case 2:
1916a1485e2SGarance A Drosehn 			ptime->tmspec |= TSPEC_HOUROFDAY;
1922f8d7c56SGarance A Drosehn 			tm.tm_hour = l;
1934c8f6471SMark Johnston 			/* FALLTHROUGH */
1942f8d7c56SGarance A Drosehn 		case 0:
1952f8d7c56SGarance A Drosehn 			break;
1962f8d7c56SGarance A Drosehn 		default:
1972f8d7c56SGarance A Drosehn 			return (-1);
1982f8d7c56SGarance A Drosehn 		}
1992f8d7c56SGarance A Drosehn 
2002f8d7c56SGarance A Drosehn 		/* sanity check */
2012f8d7c56SGarance A Drosehn 		if (tm.tm_sec < 0 || tm.tm_sec > 60 || tm.tm_min < 0
2022f8d7c56SGarance A Drosehn 		    || tm.tm_min > 59 || tm.tm_hour < 0 || tm.tm_hour > 23)
2032f8d7c56SGarance A Drosehn 			return (-1);
2042f8d7c56SGarance A Drosehn 	}
2052f8d7c56SGarance A Drosehn 
2066a1485e2SGarance A Drosehn 	ptime->tm = tm;
2076a1485e2SGarance A Drosehn 	return (0);
2082f8d7c56SGarance A Drosehn }
2092f8d7c56SGarance A Drosehn 
2102f8d7c56SGarance A Drosehn /*-
2112f8d7c56SGarance A Drosehn  * Parse a cyclic time specification, the format is as follows:
2122f8d7c56SGarance A Drosehn  *
2132f8d7c56SGarance A Drosehn  *	[Dhh] or [Wd[Dhh]] or [Mdd[Dhh]]
2142f8d7c56SGarance A Drosehn  *
2152f8d7c56SGarance A Drosehn  * to rotate a logfile cyclic at
2162f8d7c56SGarance A Drosehn  *
2172f8d7c56SGarance A Drosehn  *	- every day (D) within a specific hour (hh)	(hh = 0...23)
2182f8d7c56SGarance A Drosehn  *	- once a week (W) at a specific day (d)     OR	(d = 0..6, 0 = Sunday)
2192f8d7c56SGarance A Drosehn  *	- once a month (M) at a specific day (d)	(d = 1..31,l|L)
2202f8d7c56SGarance A Drosehn  *
2212f8d7c56SGarance A Drosehn  * We don't accept a timezone specification; missing fields
2222f8d7c56SGarance A Drosehn  * are defaulted to the current date but time zero.
2232f8d7c56SGarance A Drosehn  */
2246a1485e2SGarance A Drosehn static int
parseDWM(struct ptime_data * ptime,const char * s)2256a1485e2SGarance A Drosehn parseDWM(struct ptime_data *ptime, const char *s)
2262f8d7c56SGarance A Drosehn {
2276a1485e2SGarance A Drosehn 	int daysmon, Dseen, WMseen;
228c3033287SGarance A Drosehn 	const char *endval;
229c3033287SGarance A Drosehn 	char *tmp;
2302f8d7c56SGarance A Drosehn 	long l;
2316a1485e2SGarance A Drosehn 	struct tm tm;
2322f8d7c56SGarance A Drosehn 
233526d55a4SGarance A Drosehn 	/* Save away the number of days in this month */
2346a1485e2SGarance A Drosehn 	tm = ptime->tm;
235526d55a4SGarance A Drosehn 	daysmon = days_pmonth(tm.tm_mon, tm.tm_year);
2362f8d7c56SGarance A Drosehn 
2376a1485e2SGarance A Drosehn 	WMseen = Dseen = 0;
2386a1485e2SGarance A Drosehn 	ptime->tmspec = TSPEC_HOUROFDAY;
2392f8d7c56SGarance A Drosehn 	for (;;) {
240c3033287SGarance A Drosehn 		endval = NULL;
2412f8d7c56SGarance A Drosehn 		switch (*s) {
2422f8d7c56SGarance A Drosehn 		case 'D':
2432f8d7c56SGarance A Drosehn 			if (Dseen)
2442f8d7c56SGarance A Drosehn 				return (-1);
2452f8d7c56SGarance A Drosehn 			Dseen++;
2466a1485e2SGarance A Drosehn 			ptime->tmspec |= TSPEC_HOUROFDAY;
2472f8d7c56SGarance A Drosehn 			s++;
248c3033287SGarance A Drosehn 			l = strtol(s, &tmp, 10);
2492f8d7c56SGarance A Drosehn 			if (l < 0 || l > 23)
2502f8d7c56SGarance A Drosehn 				return (-1);
251c3033287SGarance A Drosehn 			endval = tmp;
2522f8d7c56SGarance A Drosehn 			tm.tm_hour = l;
2532f8d7c56SGarance A Drosehn 			break;
2542f8d7c56SGarance A Drosehn 
2552f8d7c56SGarance A Drosehn 		case 'W':
2562f8d7c56SGarance A Drosehn 			if (WMseen)
2572f8d7c56SGarance A Drosehn 				return (-1);
2582f8d7c56SGarance A Drosehn 			WMseen++;
2596a1485e2SGarance A Drosehn 			ptime->tmspec |= TSPEC_DAYOFWEEK;
2602f8d7c56SGarance A Drosehn 			s++;
261c3033287SGarance A Drosehn 			l = strtol(s, &tmp, 10);
2622f8d7c56SGarance A Drosehn 			if (l < 0 || l > 6)
2632f8d7c56SGarance A Drosehn 				return (-1);
264c3033287SGarance A Drosehn 			endval = tmp;
2652f8d7c56SGarance A Drosehn 			if (l != tm.tm_wday) {
2662f8d7c56SGarance A Drosehn 				int save;
2672f8d7c56SGarance A Drosehn 
2682f8d7c56SGarance A Drosehn 				if (l < tm.tm_wday) {
2692f8d7c56SGarance A Drosehn 					save = 6 - tm.tm_wday;
2702f8d7c56SGarance A Drosehn 					save += (l + 1);
2712f8d7c56SGarance A Drosehn 				} else {
2722f8d7c56SGarance A Drosehn 					save = l - tm.tm_wday;
2732f8d7c56SGarance A Drosehn 				}
2742f8d7c56SGarance A Drosehn 
2752f8d7c56SGarance A Drosehn 				tm.tm_mday += save;
2762f8d7c56SGarance A Drosehn 
277526d55a4SGarance A Drosehn 				if (tm.tm_mday > daysmon) {
2782f8d7c56SGarance A Drosehn 					tm.tm_mon++;
279526d55a4SGarance A Drosehn 					tm.tm_mday = tm.tm_mday - daysmon;
280*b7b447fdSGarance A Drosehn 					if (tm.tm_mon >= 12) {
281*b7b447fdSGarance A Drosehn 						tm.tm_mon = 0;
282*b7b447fdSGarance A Drosehn 						tm.tm_year++;
283*b7b447fdSGarance A Drosehn 					}
2842f8d7c56SGarance A Drosehn 				}
2852f8d7c56SGarance A Drosehn 			}
2862f8d7c56SGarance A Drosehn 			break;
2872f8d7c56SGarance A Drosehn 
2882f8d7c56SGarance A Drosehn 		case 'M':
2892f8d7c56SGarance A Drosehn 			if (WMseen)
2902f8d7c56SGarance A Drosehn 				return (-1);
2912f8d7c56SGarance A Drosehn 			WMseen++;
2926a1485e2SGarance A Drosehn 			ptime->tmspec |= TSPEC_DAYOFMONTH;
2932f8d7c56SGarance A Drosehn 			s++;
2942f8d7c56SGarance A Drosehn 			if (tolower(*s) == 'l') {
2956a1485e2SGarance A Drosehn 				/* User wants the last day of the month. */
2966a1485e2SGarance A Drosehn 				ptime->tmspec |= TSPEC_LDAYOFMONTH;
297526d55a4SGarance A Drosehn 				tm.tm_mday = daysmon;
298c3033287SGarance A Drosehn 				endval = s + 1;
2992f8d7c56SGarance A Drosehn 			} else {
300c3033287SGarance A Drosehn 				l = strtol(s, &tmp, 10);
3012f8d7c56SGarance A Drosehn 				if (l < 1 || l > 31)
3022f8d7c56SGarance A Drosehn 					return (-1);
3032f8d7c56SGarance A Drosehn 
304526d55a4SGarance A Drosehn 				if (l > daysmon)
3052f8d7c56SGarance A Drosehn 					return (-1);
306c3033287SGarance A Drosehn 				endval = tmp;
3072f8d7c56SGarance A Drosehn 				tm.tm_mday = l;
3082f8d7c56SGarance A Drosehn 			}
3092f8d7c56SGarance A Drosehn 			break;
3102f8d7c56SGarance A Drosehn 
3112f8d7c56SGarance A Drosehn 		default:
3122f8d7c56SGarance A Drosehn 			return (-1);
3132f8d7c56SGarance A Drosehn 			break;
3142f8d7c56SGarance A Drosehn 		}
3152f8d7c56SGarance A Drosehn 
316c3033287SGarance A Drosehn 		if (endval == NULL)
317c3033287SGarance A Drosehn 			return (-1);
318c3033287SGarance A Drosehn 		else if (*endval == '\0' || isspace(*endval))
3192f8d7c56SGarance A Drosehn 			break;
3202f8d7c56SGarance A Drosehn 		else
321c3033287SGarance A Drosehn 			s = endval;
3222f8d7c56SGarance A Drosehn 	}
3232f8d7c56SGarance A Drosehn 
3246a1485e2SGarance A Drosehn 	ptime->tm = tm;
3256a1485e2SGarance A Drosehn 	return (0);
3266a1485e2SGarance A Drosehn }
3276a1485e2SGarance A Drosehn 
3282f8d7c56SGarance A Drosehn /*
3296a1485e2SGarance A Drosehn  * Initialize a new ptime-related data area.
3302f8d7c56SGarance A Drosehn  */
3316a1485e2SGarance A Drosehn struct ptime_data *
ptime_init(const struct ptime_data * optsrc)3326a1485e2SGarance A Drosehn ptime_init(const struct ptime_data *optsrc)
3336a1485e2SGarance A Drosehn {
3346a1485e2SGarance A Drosehn 	struct ptime_data *newdata;
3356a1485e2SGarance A Drosehn 
3366a1485e2SGarance A Drosehn 	newdata = malloc(sizeof(struct ptime_data));
3376a1485e2SGarance A Drosehn 	if (optsrc != NULL) {
3386a1485e2SGarance A Drosehn 		memcpy(newdata, optsrc, sizeof(struct ptime_data));
3396a1485e2SGarance A Drosehn 	} else {
3406a1485e2SGarance A Drosehn 		memset(newdata, '\0', sizeof(struct ptime_data));
3416a1485e2SGarance A Drosehn 		newdata->did_adj4dst = TNYET_ADJ4DST;
3426a1485e2SGarance A Drosehn 	}
3436a1485e2SGarance A Drosehn 
3446a1485e2SGarance A Drosehn 	return (newdata);
3456a1485e2SGarance A Drosehn }
3466a1485e2SGarance A Drosehn 
3476a1485e2SGarance A Drosehn /*
3486a1485e2SGarance A Drosehn  * Adjust a given time if that time is in a different timezone than
3496a1485e2SGarance A Drosehn  * some other time.
3506a1485e2SGarance A Drosehn  */
3516a1485e2SGarance A Drosehn int
ptime_adjust4dst(struct ptime_data * ptime,const struct ptime_data * dstsrc)3526a1485e2SGarance A Drosehn ptime_adjust4dst(struct ptime_data *ptime, const struct ptime_data *dstsrc)
3536a1485e2SGarance A Drosehn {
3546a1485e2SGarance A Drosehn 	struct ptime_data adjtime;
3556a1485e2SGarance A Drosehn 
3566a1485e2SGarance A Drosehn 	if (ptime == NULL)
3576a1485e2SGarance A Drosehn 		return (-1);
3586a1485e2SGarance A Drosehn 
3596a1485e2SGarance A Drosehn 	/*
3606a1485e2SGarance A Drosehn 	 * Changes are not made to the given time until after all
3616a1485e2SGarance A Drosehn 	 * of the calculations have been successful.
3626a1485e2SGarance A Drosehn 	 */
3636a1485e2SGarance A Drosehn 	adjtime = *ptime;
3646a1485e2SGarance A Drosehn 
3656a1485e2SGarance A Drosehn 	/* Check to see if this adjustment was already made */
3666a1485e2SGarance A Drosehn 	if ((adjtime.did_adj4dst != TNYET_ADJ4DST) &&
3676a1485e2SGarance A Drosehn 	    (adjtime.did_adj4dst == dstsrc->tm.tm_isdst))
3686a1485e2SGarance A Drosehn 		return (0);		/* yes, so don't make it twice */
3696a1485e2SGarance A Drosehn 
3706a1485e2SGarance A Drosehn 	/* See if daylight-saving has changed between the two times. */
3716a1485e2SGarance A Drosehn 	if (dstsrc->tm.tm_isdst != adjtime.tm.tm_isdst) {
3726a1485e2SGarance A Drosehn 		if (adjtime.tm.tm_isdst == 1)
3736a1485e2SGarance A Drosehn 			adjtime.tsecs -= SECS_PER_HOUR;
3746a1485e2SGarance A Drosehn 		else if (adjtime.tm.tm_isdst == 0)
3756a1485e2SGarance A Drosehn 			adjtime.tsecs += SECS_PER_HOUR;
3766a1485e2SGarance A Drosehn 		adjtime.tm = *(localtime(&adjtime.tsecs));
3776a1485e2SGarance A Drosehn 		/* Remember that this adjustment has been made */
3786a1485e2SGarance A Drosehn 		adjtime.did_adj4dst = dstsrc->tm.tm_isdst;
3796a1485e2SGarance A Drosehn 		/*
3806a1485e2SGarance A Drosehn 		 * XXX - Should probably check to see if changing the
3816a1485e2SGarance A Drosehn 		 *	hour also changed the value of is_dst.  What
3826a1485e2SGarance A Drosehn 		 *	should we do in that case?
3836a1485e2SGarance A Drosehn 		 */
3846a1485e2SGarance A Drosehn 	}
3856a1485e2SGarance A Drosehn 
3866a1485e2SGarance A Drosehn 	*ptime = adjtime;
3876a1485e2SGarance A Drosehn 	return (0);
3886a1485e2SGarance A Drosehn }
3896a1485e2SGarance A Drosehn 
3906a1485e2SGarance A Drosehn int
ptime_relparse(struct ptime_data * ptime,int parseopts,time_t basetime,const char * str)3916a1485e2SGarance A Drosehn ptime_relparse(struct ptime_data *ptime, int parseopts, time_t basetime,
3926a1485e2SGarance A Drosehn     const char *str)
3936a1485e2SGarance A Drosehn {
3946a1485e2SGarance A Drosehn 	int dpm, pres;
3956a1485e2SGarance A Drosehn 	struct tm temp_tm;
3966a1485e2SGarance A Drosehn 
3976a1485e2SGarance A Drosehn 	ptime->parseopts = parseopts;
3986a1485e2SGarance A Drosehn 	ptime->basesecs = basetime;
3996a1485e2SGarance A Drosehn 	ptime->basetm = *(localtime(&ptime->basesecs));
4006a1485e2SGarance A Drosehn 	ptime->tm = ptime->basetm;
4016a1485e2SGarance A Drosehn 	ptime->tm.tm_hour = ptime->tm.tm_min = ptime->tm.tm_sec = 0;
4026a1485e2SGarance A Drosehn 
4036a1485e2SGarance A Drosehn 	/*
4046a1485e2SGarance A Drosehn 	 * Call a routine which sets ptime.tm and ptime.tspecs based
4056a1485e2SGarance A Drosehn 	 * on the given string and parsing-options.  Note that the
4066a1485e2SGarance A Drosehn 	 * routine should not call mktime to set ptime.tsecs.
4076a1485e2SGarance A Drosehn 	 */
4086a1485e2SGarance A Drosehn 	if (parseopts & PTM_PARSE_DWM)
4096a1485e2SGarance A Drosehn 		pres = parseDWM(ptime, str);
4106a1485e2SGarance A Drosehn 	else
4116a1485e2SGarance A Drosehn 		pres = parse8601(ptime, str);
4126a1485e2SGarance A Drosehn 	if (pres < 0) {
4136a1485e2SGarance A Drosehn 		ptime->tsecs = (time_t)pres;
4146a1485e2SGarance A Drosehn 		return (pres);
4156a1485e2SGarance A Drosehn 	}
4166a1485e2SGarance A Drosehn 
4176a1485e2SGarance A Drosehn 	/*
4186a1485e2SGarance A Drosehn 	 * Before calling mktime, check to see if we ended up with a
4196a1485e2SGarance A Drosehn 	 * "day-of-month" that does not exist in the selected month.
4206a1485e2SGarance A Drosehn 	 * If we did call mktime with that info, then mktime will
4216a1485e2SGarance A Drosehn 	 * make it look like the user specifically requested a day
4226a1485e2SGarance A Drosehn 	 * in the following month (eg: Feb 31 turns into Mar 3rd).
4236a1485e2SGarance A Drosehn 	 */
4246a1485e2SGarance A Drosehn 	dpm = days_pmonth(ptime->tm.tm_mon, ptime->tm.tm_year);
4256a1485e2SGarance A Drosehn 	if ((parseopts & PTM_PARSE_MATCHDOM) &&
4266a1485e2SGarance A Drosehn 	    (ptime->tmspec & TSPEC_DAYOFMONTH) &&
4276a1485e2SGarance A Drosehn 	    (ptime->tm.tm_mday> dpm)) {
4286a1485e2SGarance A Drosehn 		/*
4296a1485e2SGarance A Drosehn 		 * ptime_nxtime() will want a ptime->tsecs value,
4306a1485e2SGarance A Drosehn 		 * but we need to avoid mktime resetting all the
4316a1485e2SGarance A Drosehn 		 * ptime->tm values.
4326a1485e2SGarance A Drosehn 		 */
4336a1485e2SGarance A Drosehn 		if (verbose && dbg_at_times > 1)
4346a1485e2SGarance A Drosehn 			fprintf(stderr,
4356a1485e2SGarance A Drosehn 			    "\t-- dom fixed: %4d/%02d/%02d %02d:%02d (%02d)",
4366a1485e2SGarance A Drosehn 			    ptime->tm.tm_year, ptime->tm.tm_mon,
4376a1485e2SGarance A Drosehn 			    ptime->tm.tm_mday, ptime->tm.tm_hour,
4386a1485e2SGarance A Drosehn 			    ptime->tm.tm_min, dpm);
4396a1485e2SGarance A Drosehn 		temp_tm = ptime->tm;
4406a1485e2SGarance A Drosehn 		ptime->tsecs = mktime(&temp_tm);
4416a1485e2SGarance A Drosehn 		if (ptime->tsecs > (time_t)-1)
4426a1485e2SGarance A Drosehn 			ptimeset_nxtime(ptime);
4436a1485e2SGarance A Drosehn 		if (verbose && dbg_at_times > 1)
4446a1485e2SGarance A Drosehn 			fprintf(stderr,
4456a1485e2SGarance A Drosehn 			    " to: %4d/%02d/%02d %02d:%02d\n",
4466a1485e2SGarance A Drosehn 			    ptime->tm.tm_year, ptime->tm.tm_mon,
4476a1485e2SGarance A Drosehn 			    ptime->tm.tm_mday, ptime->tm.tm_hour,
4486a1485e2SGarance A Drosehn 			    ptime->tm.tm_min);
4496a1485e2SGarance A Drosehn 	}
4506a1485e2SGarance A Drosehn 
4516a1485e2SGarance A Drosehn 	/*
4526a1485e2SGarance A Drosehn 	 * Convert the ptime.tm into standard time_t seconds.  Check
4536a1485e2SGarance A Drosehn 	 * for invalid times, which includes things like the hour lost
4546a1485e2SGarance A Drosehn 	 * when switching from "standard time" to "daylight saving".
4556a1485e2SGarance A Drosehn 	 */
4566a1485e2SGarance A Drosehn 	ptime->tsecs = mktime(&ptime->tm);
4576a1485e2SGarance A Drosehn 	if (ptime->tsecs == (time_t)-1) {
4586a1485e2SGarance A Drosehn 		ptime->tsecs = (time_t)-2;
4596a1485e2SGarance A Drosehn 		return (-2);
4606a1485e2SGarance A Drosehn 	}
4616a1485e2SGarance A Drosehn 
4626a1485e2SGarance A Drosehn 	return (0);
4636a1485e2SGarance A Drosehn }
4646a1485e2SGarance A Drosehn 
4656a1485e2SGarance A Drosehn int
ptime_free(struct ptime_data * ptime)4666a1485e2SGarance A Drosehn ptime_free(struct ptime_data *ptime)
4676a1485e2SGarance A Drosehn {
4686a1485e2SGarance A Drosehn 
4696a1485e2SGarance A Drosehn 	if (ptime == NULL)
4706a1485e2SGarance A Drosehn 		return (-1);
4716a1485e2SGarance A Drosehn 
4726a1485e2SGarance A Drosehn 	free(ptime);
4736a1485e2SGarance A Drosehn 	return (0);
4746a1485e2SGarance A Drosehn }
4756a1485e2SGarance A Drosehn 
4766a1485e2SGarance A Drosehn /*
4776a1485e2SGarance A Drosehn  * Some trivial routines so ptime_data can remain a completely
4786a1485e2SGarance A Drosehn  * opaque type.
4796a1485e2SGarance A Drosehn  */
4806a1485e2SGarance A Drosehn const char *
ptimeget_ctime(const struct ptime_data * ptime)4816a1485e2SGarance A Drosehn ptimeget_ctime(const struct ptime_data *ptime)
4826a1485e2SGarance A Drosehn {
4836a1485e2SGarance A Drosehn 
4846a1485e2SGarance A Drosehn 	if (ptime == NULL)
4856a1485e2SGarance A Drosehn 		return ("Null time in ptimeget_ctime()\n");
4866a1485e2SGarance A Drosehn 
4876a1485e2SGarance A Drosehn 	return (ctime(&ptime->tsecs));
4886a1485e2SGarance A Drosehn }
4896a1485e2SGarance A Drosehn 
490b326fec4SDavid Bright /*
491b326fec4SDavid Bright  * Generate a time of day string in an RFC5424 compatible format. Return a
492b326fec4SDavid Bright  * pointer to the buffer with the timestamp string or NULL if an error. If the
493b326fec4SDavid Bright  * time is not supplied, cannot be converted to local time, or the resulting
494b326fec4SDavid Bright  * string would overflow the buffer, the returned string will be the RFC5424
495b326fec4SDavid Bright  * NILVALUE.
496b326fec4SDavid Bright  */
497b326fec4SDavid Bright char *
ptimeget_ctime_rfc5424(const struct ptime_data * ptime,char * timebuf,size_t bufsize)498b326fec4SDavid Bright ptimeget_ctime_rfc5424(const struct ptime_data *ptime,
499b326fec4SDavid Bright     char *timebuf, size_t bufsize)
500b326fec4SDavid Bright {
501b326fec4SDavid Bright 	static const char NILVALUE[] = {"-"};	/* RFC5424 specified NILVALUE */
502b326fec4SDavid Bright 	int chars;
503b326fec4SDavid Bright 	struct tm tm;
504b326fec4SDavid Bright 	int tz_hours;
505b326fec4SDavid Bright 	int tz_mins;
506b326fec4SDavid Bright 	long tz_offset;
507b326fec4SDavid Bright 	char tz_sign;
508b326fec4SDavid Bright 
509b326fec4SDavid Bright 	if (timebuf == NULL) {
510b326fec4SDavid Bright 		return (NULL);
511b326fec4SDavid Bright 	}
512b326fec4SDavid Bright 
513b326fec4SDavid Bright 	if (bufsize < sizeof(NILVALUE)) {
514b326fec4SDavid Bright 		return (NULL);
515b326fec4SDavid Bright 	}
516b326fec4SDavid Bright 
517b326fec4SDavid Bright 	/*
518b326fec4SDavid Bright 	 * Convert to localtime. RFC5424 mandates the use of the NILVALUE if
519b326fec4SDavid Bright 	 * the time cannot be obtained, so use that if there is an error in the
520b326fec4SDavid Bright 	 * conversion.
521b326fec4SDavid Bright 	 */
522b326fec4SDavid Bright 	if (ptime == NULL || localtime_r(&(ptime->tsecs), &tm) == NULL) {
523b326fec4SDavid Bright 		strlcpy(timebuf, NILVALUE, bufsize);
524b326fec4SDavid Bright 		return (timebuf);
525b326fec4SDavid Bright 	}
526b326fec4SDavid Bright 
527b326fec4SDavid Bright 	/*
528b326fec4SDavid Bright 	 * Convert the time to a string in RFC5424 format. The conversion
529b326fec4SDavid Bright 	 * cannot be done with strftime() because it cannot produce the correct
530b326fec4SDavid Bright 	 * timezone offset format.
531b326fec4SDavid Bright 	 */
532b326fec4SDavid Bright 	if (tm.tm_gmtoff < 0) {
533b326fec4SDavid Bright 		tz_sign = '-';
534b326fec4SDavid Bright 		tz_offset = -tm.tm_gmtoff;
535b326fec4SDavid Bright 	} else {
536b326fec4SDavid Bright 		tz_sign = '+';
537b326fec4SDavid Bright 		tz_offset = tm.tm_gmtoff;
538b326fec4SDavid Bright 	}
539b326fec4SDavid Bright 
540b326fec4SDavid Bright 	tz_hours = tz_offset / 3600;
541b326fec4SDavid Bright 	tz_mins = (tz_offset % 3600) / 60;
542b326fec4SDavid Bright 
543b326fec4SDavid Bright 	chars = snprintf(timebuf, bufsize,
544b326fec4SDavid Bright 	    "%04d-%02d-%02d"	/* date */
545b326fec4SDavid Bright 	    "T%02d:%02d:%02d"	/* time */
546b326fec4SDavid Bright 	    "%c%02d:%02d",	/* time zone offset */
547b326fec4SDavid Bright 	    tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
548b326fec4SDavid Bright 	    tm.tm_hour, tm.tm_min, tm.tm_sec,
549b326fec4SDavid Bright 	    tz_sign, tz_hours, tz_mins);
550b326fec4SDavid Bright 
551b326fec4SDavid Bright 	/* If the timestamp is too big for timebuf, return the NILVALUE. */
552b326fec4SDavid Bright 	if (chars >= (int)bufsize) {
553b326fec4SDavid Bright 		strlcpy(timebuf, NILVALUE, bufsize);
554b326fec4SDavid Bright 	}
555b326fec4SDavid Bright 
556b326fec4SDavid Bright 	return (timebuf);
557b326fec4SDavid Bright }
558b326fec4SDavid Bright 
5596a1485e2SGarance A Drosehn double
ptimeget_diff(const struct ptime_data * minuend,const struct ptime_data * subtrahend)5606a1485e2SGarance A Drosehn ptimeget_diff(const struct ptime_data *minuend, const struct
5616a1485e2SGarance A Drosehn     ptime_data *subtrahend)
5626a1485e2SGarance A Drosehn {
5636a1485e2SGarance A Drosehn 
5646a1485e2SGarance A Drosehn 	/* Just like difftime(), we have no good error-return */
5656a1485e2SGarance A Drosehn 	if (minuend == NULL || subtrahend == NULL)
5666a1485e2SGarance A Drosehn 		return (0.0);
5676a1485e2SGarance A Drosehn 
5686a1485e2SGarance A Drosehn 	return (difftime(minuend->tsecs, subtrahend->tsecs));
5696a1485e2SGarance A Drosehn }
5706a1485e2SGarance A Drosehn 
5716a1485e2SGarance A Drosehn time_t
ptimeget_secs(const struct ptime_data * ptime)5726a1485e2SGarance A Drosehn ptimeget_secs(const struct ptime_data *ptime)
5736a1485e2SGarance A Drosehn {
5746a1485e2SGarance A Drosehn 
5756a1485e2SGarance A Drosehn 	if (ptime == NULL)
5766a1485e2SGarance A Drosehn 		return (-1);
5776a1485e2SGarance A Drosehn 
5786a1485e2SGarance A Drosehn 	return (ptime->tsecs);
5796a1485e2SGarance A Drosehn }
5806a1485e2SGarance A Drosehn 
5816a1485e2SGarance A Drosehn /*
5826a1485e2SGarance A Drosehn  * Generate an approximate timestamp for the next event, based on
5836a1485e2SGarance A Drosehn  * what parts of time were specified by the original parameter to
5846a1485e2SGarance A Drosehn  * ptime_relparse(). The result may be -1 if there is no obvious
5856a1485e2SGarance A Drosehn  * "next time" which will work.
5866a1485e2SGarance A Drosehn  */
5876a1485e2SGarance A Drosehn int
ptimeset_nxtime(struct ptime_data * ptime)5886a1485e2SGarance A Drosehn ptimeset_nxtime(struct ptime_data *ptime)
5896a1485e2SGarance A Drosehn {
5906a1485e2SGarance A Drosehn 	int moredays, tdpm, tmon, tyear;
5916a1485e2SGarance A Drosehn 	struct ptime_data nextmatch;
5926a1485e2SGarance A Drosehn 
5936a1485e2SGarance A Drosehn 	if (ptime == NULL)
5946a1485e2SGarance A Drosehn 		return (-1);
5956a1485e2SGarance A Drosehn 
5966a1485e2SGarance A Drosehn 	/*
5976a1485e2SGarance A Drosehn 	 * Changes are not made to the given time until after all
5986a1485e2SGarance A Drosehn 	 * of the calculations have been successful.
5996a1485e2SGarance A Drosehn 	 */
6006a1485e2SGarance A Drosehn 	nextmatch = *ptime;
6016a1485e2SGarance A Drosehn 	/*
6026a1485e2SGarance A Drosehn 	 * If the user specified a year and we're already past that
6036a1485e2SGarance A Drosehn 	 * time, then there will never be another one!
6046a1485e2SGarance A Drosehn 	 */
6056a1485e2SGarance A Drosehn 	if (ptime->tmspec & TSPEC_YEAR)
6066a1485e2SGarance A Drosehn 		return (-1);
6076a1485e2SGarance A Drosehn 
6086a1485e2SGarance A Drosehn 	/*
6096a1485e2SGarance A Drosehn 	 * The caller gave us a time in the past.  Calculate how much
6106a1485e2SGarance A Drosehn 	 * time is needed to go from that valid rotate time to the
6116a1485e2SGarance A Drosehn 	 * next valid rotate time.  We only need to get to the nearest
6126a1485e2SGarance A Drosehn 	 * hour, because newsyslog is only run once per hour.
6136a1485e2SGarance A Drosehn 	 */
6146a1485e2SGarance A Drosehn 	moredays = 0;
6156a1485e2SGarance A Drosehn 	if (ptime->tmspec & TSPEC_MONTHOFYEAR) {
6166a1485e2SGarance A Drosehn 		/* Special case: Feb 29th does not happen every year. */
6176a1485e2SGarance A Drosehn 		if (ptime->tm.tm_mon == 1 && ptime->tm.tm_mday == 29) {
6186a1485e2SGarance A Drosehn 			nextmatch.tm.tm_year += 4;
6196a1485e2SGarance A Drosehn 			if (days_pmonth(1, nextmatch.tm.tm_year) < 29)
6206a1485e2SGarance A Drosehn 				nextmatch.tm.tm_year += 4;
6216a1485e2SGarance A Drosehn 		} else {
6226a1485e2SGarance A Drosehn 			nextmatch.tm.tm_year += 1;
6236a1485e2SGarance A Drosehn 		}
6246a1485e2SGarance A Drosehn 		nextmatch.tm.tm_isdst = -1;
6256a1485e2SGarance A Drosehn 		nextmatch.tsecs = mktime(&nextmatch.tm);
6266a1485e2SGarance A Drosehn 
6276a1485e2SGarance A Drosehn 	} else if (ptime->tmspec & TSPEC_LDAYOFMONTH) {
6286a1485e2SGarance A Drosehn 		/*
6296a1485e2SGarance A Drosehn 		 * Need to get to the last day of next month.  Origtm is
6306a1485e2SGarance A Drosehn 		 * already at the last day of this month, so just add to
6316a1485e2SGarance A Drosehn 		 * it number of days in the next month.
6326a1485e2SGarance A Drosehn 		 */
6336a1485e2SGarance A Drosehn 		if (ptime->tm.tm_mon < 11)
6346a1485e2SGarance A Drosehn 			moredays = days_pmonth(ptime->tm.tm_mon + 1,
6356a1485e2SGarance A Drosehn 			    ptime->tm.tm_year);
6366a1485e2SGarance A Drosehn 		else
6376a1485e2SGarance A Drosehn 			moredays = days_pmonth(0, ptime->tm.tm_year + 1);
6386a1485e2SGarance A Drosehn 
6396a1485e2SGarance A Drosehn 	} else if (ptime->tmspec & TSPEC_DAYOFMONTH) {
6406a1485e2SGarance A Drosehn 		/* Jump to the same day in the next month */
6416a1485e2SGarance A Drosehn 		moredays = days_pmonth(ptime->tm.tm_mon, ptime->tm.tm_year);
6426a1485e2SGarance A Drosehn 		/*
6436a1485e2SGarance A Drosehn 		 * In some cases, the next month may not *have* the
6446a1485e2SGarance A Drosehn 		 * desired day-of-the-month.  If that happens, then
6456a1485e2SGarance A Drosehn 		 * move to the next month that does have enough days.
6466a1485e2SGarance A Drosehn 		 */
6476a1485e2SGarance A Drosehn 		tmon = ptime->tm.tm_mon;
6486a1485e2SGarance A Drosehn 		tyear = ptime->tm.tm_year;
6496a1485e2SGarance A Drosehn 		for (;;) {
6506a1485e2SGarance A Drosehn 			if (tmon < 11)
6516a1485e2SGarance A Drosehn 				tmon += 1;
6526a1485e2SGarance A Drosehn 			else {
6536a1485e2SGarance A Drosehn 				tmon = 0;
6546a1485e2SGarance A Drosehn 				tyear += 1;
6556a1485e2SGarance A Drosehn 			}
6566a1485e2SGarance A Drosehn 			tdpm = days_pmonth(tmon, tyear);
6576a1485e2SGarance A Drosehn 			if (tdpm >= ptime->tm.tm_mday)
6586a1485e2SGarance A Drosehn 				break;
6596a1485e2SGarance A Drosehn 			moredays += tdpm;
6606a1485e2SGarance A Drosehn 		}
6616a1485e2SGarance A Drosehn 
6626a1485e2SGarance A Drosehn 	} else if (ptime->tmspec & TSPEC_DAYOFWEEK) {
6636a1485e2SGarance A Drosehn 		moredays = 7;
6646a1485e2SGarance A Drosehn 	} else if (ptime->tmspec & TSPEC_HOUROFDAY) {
6656a1485e2SGarance A Drosehn 		moredays = 1;
6666a1485e2SGarance A Drosehn 	}
6676a1485e2SGarance A Drosehn 
6686a1485e2SGarance A Drosehn 	if (moredays != 0) {
6696a1485e2SGarance A Drosehn 		nextmatch.tsecs += SECS_PER_HOUR * 24 * moredays;
6706a1485e2SGarance A Drosehn 		nextmatch.tm = *(localtime(&nextmatch.tsecs));
6716a1485e2SGarance A Drosehn 	}
6726a1485e2SGarance A Drosehn 
6736a1485e2SGarance A Drosehn 	/*
6746a1485e2SGarance A Drosehn 	 * The new time will need to be adjusted if the setting of
6756a1485e2SGarance A Drosehn 	 * daylight-saving has changed between the two times.
6766a1485e2SGarance A Drosehn 	 */
6776a1485e2SGarance A Drosehn 	ptime_adjust4dst(&nextmatch, ptime);
6786a1485e2SGarance A Drosehn 
6796a1485e2SGarance A Drosehn 	/* Everything worked.  Update the given time and return. */
6806a1485e2SGarance A Drosehn 	*ptime = nextmatch;
6816a1485e2SGarance A Drosehn 	return (0);
6826a1485e2SGarance A Drosehn }
6836a1485e2SGarance A Drosehn 
6846a1485e2SGarance A Drosehn int
ptimeset_time(struct ptime_data * ptime,time_t secs)6856a1485e2SGarance A Drosehn ptimeset_time(struct ptime_data *ptime, time_t secs)
6866a1485e2SGarance A Drosehn {
6876a1485e2SGarance A Drosehn 
6886a1485e2SGarance A Drosehn 	if (ptime == NULL)
6896a1485e2SGarance A Drosehn 		return (-1);
6906a1485e2SGarance A Drosehn 
6916a1485e2SGarance A Drosehn 	ptime->tsecs = secs;
6926a1485e2SGarance A Drosehn 	ptime->tm = *(localtime(&ptime->tsecs));
6936a1485e2SGarance A Drosehn 	ptime->parseopts = 0;
6946a1485e2SGarance A Drosehn 	/* ptime->tmspec = ? */
6956a1485e2SGarance A Drosehn 	return (0);
6962f8d7c56SGarance A Drosehn }
697