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