xref: /freebsd/usr.sbin/pw/psdate.c (revision eba230afba4932f02a1ca44efc797cf7499a5cb0)
1d6f907dcSJoerg Wunsch /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
31de7b4b8SPedro F. Giffuni  *
4ad7cf975SJoerg Wunsch  * Copyright (C) 1996
5ad7cf975SJoerg Wunsch  *	David L. Nugent.  All rights reserved.
6d6f907dcSJoerg Wunsch  *
7d6f907dcSJoerg Wunsch  * Redistribution and use in source and binary forms, with or without
8d6f907dcSJoerg Wunsch  * modification, are permitted provided that the following conditions
9d6f907dcSJoerg Wunsch  * are met:
10d6f907dcSJoerg Wunsch  * 1. Redistributions of source code must retain the above copyright
11ad7cf975SJoerg Wunsch  *    notice, this list of conditions and the following disclaimer.
12d6f907dcSJoerg Wunsch  * 2. Redistributions in binary form must reproduce the above copyright
13d6f907dcSJoerg Wunsch  *    notice, this list of conditions and the following disclaimer in the
14d6f907dcSJoerg Wunsch  *    documentation and/or other materials provided with the distribution.
15d6f907dcSJoerg Wunsch  *
16ad7cf975SJoerg Wunsch  * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
17d6f907dcSJoerg Wunsch  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18d6f907dcSJoerg Wunsch  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19ad7cf975SJoerg Wunsch  * ARE DISCLAIMED.  IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
20d6f907dcSJoerg Wunsch  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21d6f907dcSJoerg Wunsch  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22d6f907dcSJoerg Wunsch  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23d6f907dcSJoerg Wunsch  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24d6f907dcSJoerg Wunsch  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25d6f907dcSJoerg Wunsch  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26d6f907dcSJoerg Wunsch  * SUCH DAMAGE.
27d6f907dcSJoerg Wunsch  */
28d6f907dcSJoerg Wunsch 
29bcbdb01eSBaptiste Daroussin #include <ctype.h>
30bcbdb01eSBaptiste Daroussin #include <err.h>
31d6f907dcSJoerg Wunsch #include <stdlib.h>
32d6f907dcSJoerg Wunsch #include <string.h>
33330e4230SBaptiste Daroussin #include <xlocale.h>
34d6f907dcSJoerg Wunsch 
35d6f907dcSJoerg Wunsch #include "psdate.h"
36d6f907dcSJoerg Wunsch 
37d6f907dcSJoerg Wunsch 
38d30f122fSEugene Grosbein int
numerics(char const * str)39d6f907dcSJoerg Wunsch numerics(char const * str)
40d6f907dcSJoerg Wunsch {
41d6f907dcSJoerg Wunsch 
42b11244e7SBaptiste Daroussin 	return (str[strspn(str, "0123456789x")] == '\0');
43d6f907dcSJoerg Wunsch }
44d6f907dcSJoerg Wunsch 
45d6f907dcSJoerg Wunsch static int
aindex(char const * arr[],char const ** str,int len)46d6f907dcSJoerg Wunsch aindex(char const * arr[], char const ** str, int len)
47d6f907dcSJoerg Wunsch {
48d6f907dcSJoerg Wunsch 	int             l, i;
49d6f907dcSJoerg Wunsch 	char            mystr[32];
50d6f907dcSJoerg Wunsch 
51d6f907dcSJoerg Wunsch 	mystr[len] = '\0';
52d6f907dcSJoerg Wunsch 	l = strlen(strncpy(mystr, *str, len));
53d6f907dcSJoerg Wunsch 	for (i = 0; i < l; i++)
54e7161f36SAndrey A. Chernov 		mystr[i] = (char) tolower((unsigned char)mystr[i]);
55d6f907dcSJoerg Wunsch 	for (i = 0; arr[i] && strcmp(mystr, arr[i]) != 0; i++);
56d6f907dcSJoerg Wunsch 	if (arr[i] == NULL)
57d6f907dcSJoerg Wunsch 		i = -1;
58d6f907dcSJoerg Wunsch 	else {			/* Skip past it */
59e7161f36SAndrey A. Chernov 		while (**str && isalpha((unsigned char)**str))
60d6f907dcSJoerg Wunsch 			++(*str);
61d6f907dcSJoerg Wunsch 		/* And any following whitespace */
62e7161f36SAndrey A. Chernov 		while (**str && (**str == ',' || isspace((unsigned char)**str)))
63d6f907dcSJoerg Wunsch 			++(*str);
64d6f907dcSJoerg Wunsch 	}			/* Return index */
65d6f907dcSJoerg Wunsch 	return i;
66d6f907dcSJoerg Wunsch }
67d6f907dcSJoerg Wunsch 
68d6f907dcSJoerg Wunsch static int
weekday(char const ** str)69d6f907dcSJoerg Wunsch weekday(char const ** str)
70d6f907dcSJoerg Wunsch {
71d6f907dcSJoerg Wunsch 	static char const *days[] =
72d6f907dcSJoerg Wunsch 	{"sun", "mon", "tue", "wed", "thu", "fri", "sat", NULL};
73d6f907dcSJoerg Wunsch 
74d6f907dcSJoerg Wunsch 	return aindex(days, str, 3);
75d6f907dcSJoerg Wunsch }
76d6f907dcSJoerg Wunsch 
77d6f907dcSJoerg Wunsch static void
parse_datesub(char const * str,struct tm * t)78f6eae381SBaptiste Daroussin parse_datesub(char const * str, struct tm *t)
79d6f907dcSJoerg Wunsch {
80330e4230SBaptiste Daroussin 	struct tm	 tm;
81330e4230SBaptiste Daroussin 	locale_t	 l;
82d6f907dcSJoerg Wunsch 	int		 i;
83330e4230SBaptiste Daroussin 	char		*ret;
84330e4230SBaptiste Daroussin 	const char	*valid_formats[] = {
85330e4230SBaptiste Daroussin 		"%d-%b-%y",
86330e4230SBaptiste Daroussin 		"%d-%b-%Y",
87330e4230SBaptiste Daroussin 		"%d-%m-%y",
88330e4230SBaptiste Daroussin 		"%d-%m-%Y",
89f6eae381SBaptiste Daroussin 		"%H:%M %d-%b-%y",
90f6eae381SBaptiste Daroussin 		"%H:%M %d-%b-%Y",
91f6eae381SBaptiste Daroussin 		"%H:%M %d-%m-%y",
92f6eae381SBaptiste Daroussin 		"%H:%M %d-%m-%Y",
93f6eae381SBaptiste Daroussin 		"%H:%M:%S %d-%b-%y",
94f6eae381SBaptiste Daroussin 		"%H:%M:%S %d-%b-%Y",
95f6eae381SBaptiste Daroussin 		"%H:%M:%S %d-%m-%y",
96f6eae381SBaptiste Daroussin 		"%H:%M:%S %d-%m-%Y",
97f6eae381SBaptiste Daroussin 		"%d-%b-%y %H:%M",
98f6eae381SBaptiste Daroussin 		"%d-%b-%Y %H:%M",
99f6eae381SBaptiste Daroussin 		"%d-%m-%y %H:%M",
100f6eae381SBaptiste Daroussin 		"%d-%m-%Y %H:%M",
101f6eae381SBaptiste Daroussin 		"%d-%b-%y %H:%M:%S",
102f6eae381SBaptiste Daroussin 		"%d-%b-%Y %H:%M:%S",
103f6eae381SBaptiste Daroussin 		"%d-%m-%y %H:%M:%S",
104f6eae381SBaptiste Daroussin 		"%d-%m-%Y %H:%M:%S",
105f6eae381SBaptiste Daroussin 		"%H:%M\t%d-%b-%y",
106f6eae381SBaptiste Daroussin 		"%H:%M\t%d-%b-%Y",
107f6eae381SBaptiste Daroussin 		"%H:%M\t%d-%m-%y",
108f6eae381SBaptiste Daroussin 		"%H:%M\t%d-%m-%Y",
109f6eae381SBaptiste Daroussin 		"%H:%M\t%S %d-%b-%y",
110f6eae381SBaptiste Daroussin 		"%H:%M\t%S %d-%b-%Y",
111f6eae381SBaptiste Daroussin 		"%H:%M\t%S %d-%m-%y",
112f6eae381SBaptiste Daroussin 		"%H:%M\t%S %d-%m-%Y",
113f6eae381SBaptiste Daroussin 		"%d-%b-%y\t%H:%M",
114f6eae381SBaptiste Daroussin 		"%d-%b-%Y\t%H:%M",
115f6eae381SBaptiste Daroussin 		"%d-%m-%y\t%H:%M",
116f6eae381SBaptiste Daroussin 		"%d-%m-%Y\t%H:%M",
117f6eae381SBaptiste Daroussin 		"%d-%b-%y\t%H:%M:%S",
118f6eae381SBaptiste Daroussin 		"%d-%b-%Y\t%H:%M:%S",
119f6eae381SBaptiste Daroussin 		"%d-%m-%y\t%H:%M:%S",
120f6eae381SBaptiste Daroussin 		"%d-%m-%Y\t%H:%M:%S",
121330e4230SBaptiste Daroussin 		NULL,
122330e4230SBaptiste Daroussin 	};
123d6f907dcSJoerg Wunsch 
124330e4230SBaptiste Daroussin 	l = newlocale(LC_ALL_MASK, "C", NULL);
125d6f907dcSJoerg Wunsch 
126330e4230SBaptiste Daroussin 	for (i=0; valid_formats[i] != NULL; i++) {
1270a9541d9SBaptiste Daroussin 		memset(&tm, 0, sizeof(tm));
128330e4230SBaptiste Daroussin 		ret = strptime_l(str, valid_formats[i], &tm, l);
129330e4230SBaptiste Daroussin 		if (ret && *ret == '\0') {
130f6eae381SBaptiste Daroussin 			t->tm_mday = tm.tm_mday;
131f6eae381SBaptiste Daroussin 			t->tm_mon = tm.tm_mon;
132f6eae381SBaptiste Daroussin 			t->tm_year = tm.tm_year;
133f6eae381SBaptiste Daroussin 			t->tm_hour = tm.tm_hour;
134f6eae381SBaptiste Daroussin 			t->tm_min = tm.tm_min;
135f6eae381SBaptiste Daroussin 			t->tm_sec = tm.tm_sec;
136330e4230SBaptiste Daroussin 			freelocale(l);
137d6f907dcSJoerg Wunsch 			return;
138d6f907dcSJoerg Wunsch 		}
139d6f907dcSJoerg Wunsch 	}
140d6f907dcSJoerg Wunsch 
141330e4230SBaptiste Daroussin 	freelocale(l);
142330e4230SBaptiste Daroussin 
143330e4230SBaptiste Daroussin 	errx(EXIT_FAILURE, "Invalid date");
144330e4230SBaptiste Daroussin }
145330e4230SBaptiste Daroussin 
146d6f907dcSJoerg Wunsch 
147d6f907dcSJoerg Wunsch /*-
148d6f907dcSJoerg Wunsch  * Parse time must be flexible, it handles the following formats:
149d6f907dcSJoerg Wunsch  * nnnnnnnnnnn		UNIX timestamp (all numeric), 0 = now
150d6f907dcSJoerg Wunsch  * 0xnnnnnnnn		UNIX timestamp in hexadecimal
151d6f907dcSJoerg Wunsch  * 0nnnnnnnnn		UNIX timestamp in octal
152d6f907dcSJoerg Wunsch  * 0			Given time
153d6f907dcSJoerg Wunsch  * +nnnn[smhdwoy]	Given time + nnnn hours, mins, days, weeks, months or years
154d6f907dcSJoerg Wunsch  * -nnnn[smhdwoy]	Given time - nnnn hours, mins, days, weeks, months or years
155d6f907dcSJoerg Wunsch  * dd[ ./-]mmm[ ./-]yy	Date }
156d6f907dcSJoerg Wunsch  * hh:mm:ss		Time } May be combined
157d6f907dcSJoerg Wunsch  */
158d6f907dcSJoerg Wunsch 
159d6f907dcSJoerg Wunsch time_t
parse_date(time_t dt,char const * str)160d6f907dcSJoerg Wunsch parse_date(time_t dt, char const * str)
161d6f907dcSJoerg Wunsch {
162d6f907dcSJoerg Wunsch 	char           *p;
163d6f907dcSJoerg Wunsch 	int             i;
164d6f907dcSJoerg Wunsch 	long            val;
165d6f907dcSJoerg Wunsch 	struct tm      *T;
166d6f907dcSJoerg Wunsch 
167d6f907dcSJoerg Wunsch 	if (dt == 0)
168d6f907dcSJoerg Wunsch 		dt = time(NULL);
169d6f907dcSJoerg Wunsch 
170e7161f36SAndrey A. Chernov 	while (*str && isspace((unsigned char)*str))
171d6f907dcSJoerg Wunsch 		++str;
172d6f907dcSJoerg Wunsch 
173d6f907dcSJoerg Wunsch 	if (numerics(str)) {
1742bffe0d5SDavid Nugent 		dt = strtol(str, &p, 0);
175d6f907dcSJoerg Wunsch 	} else if (*str == '+' || *str == '-') {
176d6f907dcSJoerg Wunsch 		val = strtol(str, &p, 0);
177d6f907dcSJoerg Wunsch 		switch (*p) {
178d6f907dcSJoerg Wunsch 		case 'h':
179d6f907dcSJoerg Wunsch 		case 'H':	/* hours */
180d6f907dcSJoerg Wunsch 			dt += (val * 3600L);
181d6f907dcSJoerg Wunsch 			break;
182d6f907dcSJoerg Wunsch 		case '\0':
183d6f907dcSJoerg Wunsch 		case 'm':
184d6f907dcSJoerg Wunsch 		case 'M':	/* minutes */
185d6f907dcSJoerg Wunsch 			dt += (val * 60L);
186d6f907dcSJoerg Wunsch 			break;
187d6f907dcSJoerg Wunsch 		case 's':
188d6f907dcSJoerg Wunsch 		case 'S':	/* seconds */
189d6f907dcSJoerg Wunsch 			dt += val;
190d6f907dcSJoerg Wunsch 			break;
191d6f907dcSJoerg Wunsch 		case 'd':
192d6f907dcSJoerg Wunsch 		case 'D':	/* days */
193d6f907dcSJoerg Wunsch 			dt += (val * 86400L);
194d6f907dcSJoerg Wunsch 			break;
195d6f907dcSJoerg Wunsch 		case 'w':
196d6f907dcSJoerg Wunsch 		case 'W':	/* weeks */
197d6f907dcSJoerg Wunsch 			dt += (val * 604800L);
198d6f907dcSJoerg Wunsch 			break;
199d6f907dcSJoerg Wunsch 		case 'o':
200d6f907dcSJoerg Wunsch 		case 'O':	/* months */
201d6f907dcSJoerg Wunsch 			T = localtime(&dt);
202d6f907dcSJoerg Wunsch 			T->tm_mon += (int) val;
203d6f907dcSJoerg Wunsch 			i = T->tm_mday;
204d6f907dcSJoerg Wunsch 			goto fixday;
205d6f907dcSJoerg Wunsch 		case 'y':
206d6f907dcSJoerg Wunsch 		case 'Y':	/* years */
207d6f907dcSJoerg Wunsch 			T = localtime(&dt);
208d6f907dcSJoerg Wunsch 			T->tm_year += (int) val;
209d6f907dcSJoerg Wunsch 			i = T->tm_mday;
210d6f907dcSJoerg Wunsch 	fixday:
211d6f907dcSJoerg Wunsch 			dt = mktime(T);
212d6f907dcSJoerg Wunsch 			T = localtime(&dt);
213d6f907dcSJoerg Wunsch 			if (T->tm_mday != i) {
214d6f907dcSJoerg Wunsch 				T->tm_mday = 1;
215d6f907dcSJoerg Wunsch 				dt = mktime(T);
216d6f907dcSJoerg Wunsch 				dt -= (time_t) 86400L;
217d6f907dcSJoerg Wunsch 			}
218d6f907dcSJoerg Wunsch 		default:	/* unknown */
219d6f907dcSJoerg Wunsch 			break;	/* leave untouched */
220d6f907dcSJoerg Wunsch 		}
221d6f907dcSJoerg Wunsch 	} else {
222d6f907dcSJoerg Wunsch 		char           *q, tmp[64];
223d6f907dcSJoerg Wunsch 
224d6f907dcSJoerg Wunsch 		/*
225d6f907dcSJoerg Wunsch 		 * Skip past any weekday prefix
226d6f907dcSJoerg Wunsch 		 */
227d6f907dcSJoerg Wunsch 		weekday(&str);
228b8938b66SRobert Drehmel 		strlcpy(tmp, str, sizeof(tmp));
229b8938b66SRobert Drehmel 		str = tmp;
230d6f907dcSJoerg Wunsch 		T = localtime(&dt);
231d6f907dcSJoerg Wunsch 
232d6f907dcSJoerg Wunsch 		/*
233d6f907dcSJoerg Wunsch 		 * See if we can break off any timezone
234d6f907dcSJoerg Wunsch 		 */
235d6f907dcSJoerg Wunsch 		while ((q = strrchr(tmp, ' ')) != NULL) {
236d6f907dcSJoerg Wunsch 			if (strchr("(+-", q[1]) != NULL)
237d6f907dcSJoerg Wunsch 				*q = '\0';
238d6f907dcSJoerg Wunsch 			else {
239d6f907dcSJoerg Wunsch 				int             j = 1;
240d6f907dcSJoerg Wunsch 
241e7161f36SAndrey A. Chernov 				while (q[j] && isupper((unsigned char)q[j]))
242d6f907dcSJoerg Wunsch 					++j;
243d6f907dcSJoerg Wunsch 				if (q[j] == '\0')
244d6f907dcSJoerg Wunsch 					*q = '\0';
245d6f907dcSJoerg Wunsch 				else
246d6f907dcSJoerg Wunsch 					break;
247d6f907dcSJoerg Wunsch 			}
248d6f907dcSJoerg Wunsch 		}
249d6f907dcSJoerg Wunsch 
250f6eae381SBaptiste Daroussin 		parse_datesub(tmp, T);
251d6f907dcSJoerg Wunsch 		dt = mktime(T);
252d6f907dcSJoerg Wunsch 	}
253d6f907dcSJoerg Wunsch 	return dt;
254d6f907dcSJoerg Wunsch }
255