xref: /freebsd/usr.sbin/pw/psdate.c (revision 330e423084cb6e54d591a149120ed2ec36fec40d)
1d6f907dcSJoerg Wunsch /*-
2ad7cf975SJoerg Wunsch  * Copyright (C) 1996
3ad7cf975SJoerg Wunsch  *	David L. Nugent.  All rights reserved.
4d6f907dcSJoerg Wunsch  *
5d6f907dcSJoerg Wunsch  * Redistribution and use in source and binary forms, with or without
6d6f907dcSJoerg Wunsch  * modification, are permitted provided that the following conditions
7d6f907dcSJoerg Wunsch  * are met:
8d6f907dcSJoerg Wunsch  * 1. Redistributions of source code must retain the above copyright
9ad7cf975SJoerg Wunsch  *    notice, this list of conditions and the following disclaimer.
10d6f907dcSJoerg Wunsch  * 2. Redistributions in binary form must reproduce the above copyright
11d6f907dcSJoerg Wunsch  *    notice, this list of conditions and the following disclaimer in the
12d6f907dcSJoerg Wunsch  *    documentation and/or other materials provided with the distribution.
13d6f907dcSJoerg Wunsch  *
14ad7cf975SJoerg Wunsch  * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
15d6f907dcSJoerg Wunsch  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16d6f907dcSJoerg Wunsch  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17ad7cf975SJoerg Wunsch  * ARE DISCLAIMED.  IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
18d6f907dcSJoerg Wunsch  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19d6f907dcSJoerg Wunsch  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20d6f907dcSJoerg Wunsch  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21d6f907dcSJoerg Wunsch  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22d6f907dcSJoerg Wunsch  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23d6f907dcSJoerg Wunsch  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24d6f907dcSJoerg Wunsch  * SUCH DAMAGE.
25d6f907dcSJoerg Wunsch  */
26d6f907dcSJoerg Wunsch 
271dcc6ec7SPhilippe Charnier #ifndef lint
281dcc6ec7SPhilippe Charnier static const char rcsid[] =
2997d92980SPeter Wemm   "$FreeBSD$";
301dcc6ec7SPhilippe Charnier #endif /* not lint */
311dcc6ec7SPhilippe Charnier 
32d6f907dcSJoerg Wunsch #include <stdio.h>
33d6f907dcSJoerg Wunsch #include <stdlib.h>
34d6f907dcSJoerg Wunsch #include <string.h>
35d6f907dcSJoerg Wunsch #include <ctype.h>
36*330e4230SBaptiste Daroussin #include <xlocale.h>
37*330e4230SBaptiste Daroussin #include <err.h>
38d6f907dcSJoerg Wunsch 
39d6f907dcSJoerg Wunsch #include "psdate.h"
40d6f907dcSJoerg Wunsch 
41d6f907dcSJoerg Wunsch 
42d6f907dcSJoerg Wunsch static int
43d6f907dcSJoerg Wunsch a2i(char const ** str)
44d6f907dcSJoerg Wunsch {
45d6f907dcSJoerg Wunsch 	int             i = 0;
46d6f907dcSJoerg Wunsch 	char const     *s = *str;
47d6f907dcSJoerg Wunsch 
48e7161f36SAndrey A. Chernov 	if (isdigit((unsigned char)*s)) {
49d6f907dcSJoerg Wunsch 		i = atoi(s);
50e7161f36SAndrey A. Chernov 		while (isdigit((unsigned char)*s))
51d6f907dcSJoerg Wunsch 			++s;
52d6f907dcSJoerg Wunsch 		*str = s;
53d6f907dcSJoerg Wunsch 	}
54d6f907dcSJoerg Wunsch 	return i;
55d6f907dcSJoerg Wunsch }
56d6f907dcSJoerg Wunsch 
57d6f907dcSJoerg Wunsch static int
58d6f907dcSJoerg Wunsch numerics(char const * str)
59d6f907dcSJoerg Wunsch {
60e7161f36SAndrey A. Chernov 	int             rc = isdigit((unsigned char)*str);
61d6f907dcSJoerg Wunsch 
62d6f907dcSJoerg Wunsch 	if (rc)
63e7161f36SAndrey A. Chernov 		while (isdigit((unsigned char)*str) || *str == 'x')
64d6f907dcSJoerg Wunsch 			++str;
65d6f907dcSJoerg Wunsch 	return rc && !*str;
66d6f907dcSJoerg Wunsch }
67d6f907dcSJoerg Wunsch 
68d6f907dcSJoerg Wunsch static int
69d6f907dcSJoerg Wunsch aindex(char const * arr[], char const ** str, int len)
70d6f907dcSJoerg Wunsch {
71d6f907dcSJoerg Wunsch 	int             l, i;
72d6f907dcSJoerg Wunsch 	char            mystr[32];
73d6f907dcSJoerg Wunsch 
74d6f907dcSJoerg Wunsch 	mystr[len] = '\0';
75d6f907dcSJoerg Wunsch 	l = strlen(strncpy(mystr, *str, len));
76d6f907dcSJoerg Wunsch 	for (i = 0; i < l; i++)
77e7161f36SAndrey A. Chernov 		mystr[i] = (char) tolower((unsigned char)mystr[i]);
78d6f907dcSJoerg Wunsch 	for (i = 0; arr[i] && strcmp(mystr, arr[i]) != 0; i++);
79d6f907dcSJoerg Wunsch 	if (arr[i] == NULL)
80d6f907dcSJoerg Wunsch 		i = -1;
81d6f907dcSJoerg Wunsch 	else {			/* Skip past it */
82e7161f36SAndrey A. Chernov 		while (**str && isalpha((unsigned char)**str))
83d6f907dcSJoerg Wunsch 			++(*str);
84d6f907dcSJoerg Wunsch 		/* And any following whitespace */
85e7161f36SAndrey A. Chernov 		while (**str && (**str == ',' || isspace((unsigned char)**str)))
86d6f907dcSJoerg Wunsch 			++(*str);
87d6f907dcSJoerg Wunsch 	}			/* Return index */
88d6f907dcSJoerg Wunsch 	return i;
89d6f907dcSJoerg Wunsch }
90d6f907dcSJoerg Wunsch 
91d6f907dcSJoerg Wunsch static int
92d6f907dcSJoerg Wunsch weekday(char const ** str)
93d6f907dcSJoerg Wunsch {
94d6f907dcSJoerg Wunsch 	static char const *days[] =
95d6f907dcSJoerg Wunsch 	{"sun", "mon", "tue", "wed", "thu", "fri", "sat", NULL};
96d6f907dcSJoerg Wunsch 
97d6f907dcSJoerg Wunsch 	return aindex(days, str, 3);
98d6f907dcSJoerg Wunsch }
99d6f907dcSJoerg Wunsch 
100d6f907dcSJoerg Wunsch static void
101d6f907dcSJoerg Wunsch parse_time(char const * str, int *hour, int *min, int *sec)
102d6f907dcSJoerg Wunsch {
103d6f907dcSJoerg Wunsch 	*hour = a2i(&str);
104d6f907dcSJoerg Wunsch 	if ((str = strchr(str, ':')) == NULL)
105d6f907dcSJoerg Wunsch 		*min = *sec = 0;
106d6f907dcSJoerg Wunsch 	else {
107d6f907dcSJoerg Wunsch 		++str;
108d6f907dcSJoerg Wunsch 		*min = a2i(&str);
109d6f907dcSJoerg Wunsch 		*sec = ((str = strchr(str, ':')) == NULL) ? 0 : atoi(++str);
110d6f907dcSJoerg Wunsch 	}
111d6f907dcSJoerg Wunsch }
112d6f907dcSJoerg Wunsch 
113d6f907dcSJoerg Wunsch 
114d6f907dcSJoerg Wunsch static void
115d6f907dcSJoerg Wunsch parse_datesub(char const * str, int *day, int *mon, int *year)
116d6f907dcSJoerg Wunsch {
117*330e4230SBaptiste Daroussin 	struct tm	 tm;
118*330e4230SBaptiste Daroussin 	locale_t	 l;
119d6f907dcSJoerg Wunsch 	int		 i;
120*330e4230SBaptiste Daroussin 	char		*ret;
121*330e4230SBaptiste Daroussin 	const char	*valid_formats[] = {
122*330e4230SBaptiste Daroussin 		"%d-%b-%y",
123*330e4230SBaptiste Daroussin 		"%d-%b-%Y",
124*330e4230SBaptiste Daroussin 		"%d-%m-%y",
125*330e4230SBaptiste Daroussin 		"%d-%m-%Y",
126*330e4230SBaptiste Daroussin 		NULL,
127*330e4230SBaptiste Daroussin 	};
128d6f907dcSJoerg Wunsch 
129*330e4230SBaptiste Daroussin 	l = newlocale(LC_ALL_MASK, "C", NULL);
130d6f907dcSJoerg Wunsch 
131*330e4230SBaptiste Daroussin 	memset(&tm, 0, sizeof(tm));
132*330e4230SBaptiste Daroussin 	for (i=0; valid_formats[i] != NULL; i++) {
133*330e4230SBaptiste Daroussin 		ret = strptime_l(str, valid_formats[i], &tm, l);
134*330e4230SBaptiste Daroussin 		if (ret && *ret == '\0') {
135*330e4230SBaptiste Daroussin 			*day = tm.tm_mday;
136*330e4230SBaptiste Daroussin 			*mon = tm.tm_mon;
137*330e4230SBaptiste Daroussin 			*year = tm.tm_year;
138*330e4230SBaptiste Daroussin 			freelocale(l);
139d6f907dcSJoerg Wunsch 			return;
140d6f907dcSJoerg Wunsch 		}
141d6f907dcSJoerg Wunsch 	}
142d6f907dcSJoerg Wunsch 
143*330e4230SBaptiste Daroussin 	freelocale(l);
144*330e4230SBaptiste Daroussin 
145*330e4230SBaptiste Daroussin 	errx(EXIT_FAILURE, "Invalid date");
146*330e4230SBaptiste Daroussin }
147*330e4230SBaptiste Daroussin 
148d6f907dcSJoerg Wunsch 
149d6f907dcSJoerg Wunsch /*-
150d6f907dcSJoerg Wunsch  * Parse time must be flexible, it handles the following formats:
151d6f907dcSJoerg Wunsch  * nnnnnnnnnnn		UNIX timestamp (all numeric), 0 = now
152d6f907dcSJoerg Wunsch  * 0xnnnnnnnn		UNIX timestamp in hexadecimal
153d6f907dcSJoerg Wunsch  * 0nnnnnnnnn		UNIX timestamp in octal
154d6f907dcSJoerg Wunsch  * 0			Given time
155d6f907dcSJoerg Wunsch  * +nnnn[smhdwoy]	Given time + nnnn hours, mins, days, weeks, months or years
156d6f907dcSJoerg Wunsch  * -nnnn[smhdwoy]	Given time - nnnn hours, mins, days, weeks, months or years
157d6f907dcSJoerg Wunsch  * dd[ ./-]mmm[ ./-]yy	Date }
158d6f907dcSJoerg Wunsch  * hh:mm:ss		Time } May be combined
159d6f907dcSJoerg Wunsch  */
160d6f907dcSJoerg Wunsch 
161d6f907dcSJoerg Wunsch time_t
162d6f907dcSJoerg Wunsch parse_date(time_t dt, char const * str)
163d6f907dcSJoerg Wunsch {
164d6f907dcSJoerg Wunsch 	char           *p;
165d6f907dcSJoerg Wunsch 	int             i;
166d6f907dcSJoerg Wunsch 	long            val;
167d6f907dcSJoerg Wunsch 	struct tm      *T;
168d6f907dcSJoerg Wunsch 
169d6f907dcSJoerg Wunsch 	if (dt == 0)
170d6f907dcSJoerg Wunsch 		dt = time(NULL);
171d6f907dcSJoerg Wunsch 
172e7161f36SAndrey A. Chernov 	while (*str && isspace((unsigned char)*str))
173d6f907dcSJoerg Wunsch 		++str;
174d6f907dcSJoerg Wunsch 
175d6f907dcSJoerg Wunsch 	if (numerics(str)) {
1762bffe0d5SDavid Nugent 		dt = strtol(str, &p, 0);
177d6f907dcSJoerg Wunsch 	} else if (*str == '+' || *str == '-') {
178d6f907dcSJoerg Wunsch 		val = strtol(str, &p, 0);
179d6f907dcSJoerg Wunsch 		switch (*p) {
180d6f907dcSJoerg Wunsch 		case 'h':
181d6f907dcSJoerg Wunsch 		case 'H':	/* hours */
182d6f907dcSJoerg Wunsch 			dt += (val * 3600L);
183d6f907dcSJoerg Wunsch 			break;
184d6f907dcSJoerg Wunsch 		case '\0':
185d6f907dcSJoerg Wunsch 		case 'm':
186d6f907dcSJoerg Wunsch 		case 'M':	/* minutes */
187d6f907dcSJoerg Wunsch 			dt += (val * 60L);
188d6f907dcSJoerg Wunsch 			break;
189d6f907dcSJoerg Wunsch 		case 's':
190d6f907dcSJoerg Wunsch 		case 'S':	/* seconds */
191d6f907dcSJoerg Wunsch 			dt += val;
192d6f907dcSJoerg Wunsch 			break;
193d6f907dcSJoerg Wunsch 		case 'd':
194d6f907dcSJoerg Wunsch 		case 'D':	/* days */
195d6f907dcSJoerg Wunsch 			dt += (val * 86400L);
196d6f907dcSJoerg Wunsch 			break;
197d6f907dcSJoerg Wunsch 		case 'w':
198d6f907dcSJoerg Wunsch 		case 'W':	/* weeks */
199d6f907dcSJoerg Wunsch 			dt += (val * 604800L);
200d6f907dcSJoerg Wunsch 			break;
201d6f907dcSJoerg Wunsch 		case 'o':
202d6f907dcSJoerg Wunsch 		case 'O':	/* months */
203d6f907dcSJoerg Wunsch 			T = localtime(&dt);
204d6f907dcSJoerg Wunsch 			T->tm_mon += (int) val;
205d6f907dcSJoerg Wunsch 			i = T->tm_mday;
206d6f907dcSJoerg Wunsch 			goto fixday;
207d6f907dcSJoerg Wunsch 		case 'y':
208d6f907dcSJoerg Wunsch 		case 'Y':	/* years */
209d6f907dcSJoerg Wunsch 			T = localtime(&dt);
210d6f907dcSJoerg Wunsch 			T->tm_year += (int) val;
211d6f907dcSJoerg Wunsch 			i = T->tm_mday;
212d6f907dcSJoerg Wunsch 	fixday:
213d6f907dcSJoerg Wunsch 			dt = mktime(T);
214d6f907dcSJoerg Wunsch 			T = localtime(&dt);
215d6f907dcSJoerg Wunsch 			if (T->tm_mday != i) {
216d6f907dcSJoerg Wunsch 				T->tm_mday = 1;
217d6f907dcSJoerg Wunsch 				dt = mktime(T);
218d6f907dcSJoerg Wunsch 				dt -= (time_t) 86400L;
219d6f907dcSJoerg Wunsch 			}
220d6f907dcSJoerg Wunsch 		default:	/* unknown */
221d6f907dcSJoerg Wunsch 			break;	/* leave untouched */
222d6f907dcSJoerg Wunsch 		}
223d6f907dcSJoerg Wunsch 	} else {
224d6f907dcSJoerg Wunsch 		char           *q, tmp[64];
225d6f907dcSJoerg Wunsch 
226d6f907dcSJoerg Wunsch 		/*
227d6f907dcSJoerg Wunsch 		 * Skip past any weekday prefix
228d6f907dcSJoerg Wunsch 		 */
229d6f907dcSJoerg Wunsch 		weekday(&str);
230b8938b66SRobert Drehmel 		strlcpy(tmp, str, sizeof(tmp));
231b8938b66SRobert Drehmel 		str = tmp;
232d6f907dcSJoerg Wunsch 		T = localtime(&dt);
233d6f907dcSJoerg Wunsch 
234d6f907dcSJoerg Wunsch 		/*
235d6f907dcSJoerg Wunsch 		 * See if we can break off any timezone
236d6f907dcSJoerg Wunsch 		 */
237d6f907dcSJoerg Wunsch 		while ((q = strrchr(tmp, ' ')) != NULL) {
238d6f907dcSJoerg Wunsch 			if (strchr("(+-", q[1]) != NULL)
239d6f907dcSJoerg Wunsch 				*q = '\0';
240d6f907dcSJoerg Wunsch 			else {
241d6f907dcSJoerg Wunsch 				int             j = 1;
242d6f907dcSJoerg Wunsch 
243e7161f36SAndrey A. Chernov 				while (q[j] && isupper((unsigned char)q[j]))
244d6f907dcSJoerg Wunsch 					++j;
245d6f907dcSJoerg Wunsch 				if (q[j] == '\0')
246d6f907dcSJoerg Wunsch 					*q = '\0';
247d6f907dcSJoerg Wunsch 				else
248d6f907dcSJoerg Wunsch 					break;
249d6f907dcSJoerg Wunsch 			}
250d6f907dcSJoerg Wunsch 		}
251d6f907dcSJoerg Wunsch 
252d6f907dcSJoerg Wunsch 		/*
253d6f907dcSJoerg Wunsch 		 * See if there is a time hh:mm[:ss]
254d6f907dcSJoerg Wunsch 		 */
255d6f907dcSJoerg Wunsch 		if ((p = strchr(tmp, ':')) == NULL) {
256d6f907dcSJoerg Wunsch 
257d6f907dcSJoerg Wunsch 			/*
258d6f907dcSJoerg Wunsch 			 * No time string involved
259d6f907dcSJoerg Wunsch 			 */
260d6f907dcSJoerg Wunsch 			T->tm_hour = T->tm_min = T->tm_sec = 0;
261d6f907dcSJoerg Wunsch 			parse_datesub(tmp, &T->tm_mday, &T->tm_mon, &T->tm_year);
262d6f907dcSJoerg Wunsch 		} else {
263d6f907dcSJoerg Wunsch 			char            datestr[64], timestr[64];
264d6f907dcSJoerg Wunsch 
265d6f907dcSJoerg Wunsch 			/*
266d6f907dcSJoerg Wunsch 			 * Let's chip off the time string
267d6f907dcSJoerg Wunsch 			 */
268d6f907dcSJoerg Wunsch 			if ((q = strpbrk(p, " \t")) != NULL) {	/* Time first? */
269d6f907dcSJoerg Wunsch 				int             l = q - str;
270d6f907dcSJoerg Wunsch 
271b8938b66SRobert Drehmel 				strlcpy(timestr, str, l + 1);
272b8938b66SRobert Drehmel 				strlcpy(datestr, q + 1, sizeof(datestr));
273d6f907dcSJoerg Wunsch 				parse_time(timestr, &T->tm_hour, &T->tm_min, &T->tm_sec);
274d6f907dcSJoerg Wunsch 				parse_datesub(datestr, &T->tm_mday, &T->tm_mon, &T->tm_year);
275d6f907dcSJoerg Wunsch 			} else if ((q = strrchr(tmp, ' ')) != NULL) {	/* Time last */
276d6f907dcSJoerg Wunsch 				int             l = q - tmp;
277d6f907dcSJoerg Wunsch 
278b8938b66SRobert Drehmel 				strlcpy(timestr, q + 1, sizeof(timestr));
279b8938b66SRobert Drehmel 				strlcpy(datestr, tmp, l + 1);
280d6f907dcSJoerg Wunsch 			} else	/* Bail out */
281d6f907dcSJoerg Wunsch 				return dt;
282d6f907dcSJoerg Wunsch 			parse_time(timestr, &T->tm_hour, &T->tm_min, &T->tm_sec);
283d6f907dcSJoerg Wunsch 			parse_datesub(datestr, &T->tm_mday, &T->tm_mon, &T->tm_year);
284d6f907dcSJoerg Wunsch 		}
285d6f907dcSJoerg Wunsch 		dt = mktime(T);
286d6f907dcSJoerg Wunsch 	}
287d6f907dcSJoerg Wunsch 	return dt;
288d6f907dcSJoerg Wunsch }
289