psdate.c revision 326849
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 326849 2017-12-14 13:10:22Z eugen $";
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
41int
42numerics(char const * str)
43{
44
45	return (str[strspn(str, "0123456789x")] == '\0');
46}
47
48static int
49aindex(char const * arr[], char const ** str, int len)
50{
51	int             l, i;
52	char            mystr[32];
53
54	mystr[len] = '\0';
55	l = strlen(strncpy(mystr, *str, len));
56	for (i = 0; i < l; i++)
57		mystr[i] = (char) tolower((unsigned char)mystr[i]);
58	for (i = 0; arr[i] && strcmp(mystr, arr[i]) != 0; i++);
59	if (arr[i] == NULL)
60		i = -1;
61	else {			/* Skip past it */
62		while (**str && isalpha((unsigned char)**str))
63			++(*str);
64		/* And any following whitespace */
65		while (**str && (**str == ',' || isspace((unsigned char)**str)))
66			++(*str);
67	}			/* Return index */
68	return i;
69}
70
71static int
72weekday(char const ** str)
73{
74	static char const *days[] =
75	{"sun", "mon", "tue", "wed", "thu", "fri", "sat", NULL};
76
77	return aindex(days, str, 3);
78}
79
80static void
81parse_datesub(char const * str, struct tm *t)
82{
83	struct tm	 tm;
84	locale_t	 l;
85	int		 i;
86	char		*ret;
87	const char	*valid_formats[] = {
88		"%d-%b-%y",
89		"%d-%b-%Y",
90		"%d-%m-%y",
91		"%d-%m-%Y",
92		"%H:%M %d-%b-%y",
93		"%H:%M %d-%b-%Y",
94		"%H:%M %d-%m-%y",
95		"%H:%M %d-%m-%Y",
96		"%H:%M:%S %d-%b-%y",
97		"%H:%M:%S %d-%b-%Y",
98		"%H:%M:%S %d-%m-%y",
99		"%H:%M:%S %d-%m-%Y",
100		"%d-%b-%y %H:%M",
101		"%d-%b-%Y %H:%M",
102		"%d-%m-%y %H:%M",
103		"%d-%m-%Y %H:%M",
104		"%d-%b-%y %H:%M:%S",
105		"%d-%b-%Y %H:%M:%S",
106		"%d-%m-%y %H:%M:%S",
107		"%d-%m-%Y %H:%M:%S",
108		"%H:%M\t%d-%b-%y",
109		"%H:%M\t%d-%b-%Y",
110		"%H:%M\t%d-%m-%y",
111		"%H:%M\t%d-%m-%Y",
112		"%H:%M\t%S %d-%b-%y",
113		"%H:%M\t%S %d-%b-%Y",
114		"%H:%M\t%S %d-%m-%y",
115		"%H:%M\t%S %d-%m-%Y",
116		"%d-%b-%y\t%H:%M",
117		"%d-%b-%Y\t%H:%M",
118		"%d-%m-%y\t%H:%M",
119		"%d-%m-%Y\t%H:%M",
120		"%d-%b-%y\t%H:%M:%S",
121		"%d-%b-%Y\t%H:%M:%S",
122		"%d-%m-%y\t%H:%M:%S",
123		"%d-%m-%Y\t%H:%M:%S",
124		NULL,
125	};
126
127	l = newlocale(LC_ALL_MASK, "C", NULL);
128
129	memset(&tm, 0, sizeof(tm));
130	for (i=0; valid_formats[i] != NULL; i++) {
131		ret = strptime_l(str, valid_formats[i], &tm, l);
132		if (ret && *ret == '\0') {
133			t->tm_mday = tm.tm_mday;
134			t->tm_mon = tm.tm_mon;
135			t->tm_year = tm.tm_year;
136			t->tm_hour = tm.tm_hour;
137			t->tm_min = tm.tm_min;
138			t->tm_sec = tm.tm_sec;
139			freelocale(l);
140			return;
141		}
142	}
143
144	freelocale(l);
145
146	errx(EXIT_FAILURE, "Invalid date");
147}
148
149
150/*-
151 * Parse time must be flexible, it handles the following formats:
152 * nnnnnnnnnnn		UNIX timestamp (all numeric), 0 = now
153 * 0xnnnnnnnn		UNIX timestamp in hexadecimal
154 * 0nnnnnnnnn		UNIX timestamp in octal
155 * 0			Given time
156 * +nnnn[smhdwoy]	Given time + nnnn hours, mins, days, weeks, months or years
157 * -nnnn[smhdwoy]	Given time - nnnn hours, mins, days, weeks, months or years
158 * dd[ ./-]mmm[ ./-]yy	Date }
159 * hh:mm:ss		Time } May be combined
160 */
161
162time_t
163parse_date(time_t dt, char const * str)
164{
165	char           *p;
166	int             i;
167	long            val;
168	struct tm      *T;
169
170	if (dt == 0)
171		dt = time(NULL);
172
173	while (*str && isspace((unsigned char)*str))
174		++str;
175
176	if (numerics(str)) {
177		dt = strtol(str, &p, 0);
178	} else if (*str == '+' || *str == '-') {
179		val = strtol(str, &p, 0);
180		switch (*p) {
181		case 'h':
182		case 'H':	/* hours */
183			dt += (val * 3600L);
184			break;
185		case '\0':
186		case 'm':
187		case 'M':	/* minutes */
188			dt += (val * 60L);
189			break;
190		case 's':
191		case 'S':	/* seconds */
192			dt += val;
193			break;
194		case 'd':
195		case 'D':	/* days */
196			dt += (val * 86400L);
197			break;
198		case 'w':
199		case 'W':	/* weeks */
200			dt += (val * 604800L);
201			break;
202		case 'o':
203		case 'O':	/* months */
204			T = localtime(&dt);
205			T->tm_mon += (int) val;
206			i = T->tm_mday;
207			goto fixday;
208		case 'y':
209		case 'Y':	/* years */
210			T = localtime(&dt);
211			T->tm_year += (int) val;
212			i = T->tm_mday;
213	fixday:
214			dt = mktime(T);
215			T = localtime(&dt);
216			if (T->tm_mday != i) {
217				T->tm_mday = 1;
218				dt = mktime(T);
219				dt -= (time_t) 86400L;
220			}
221		default:	/* unknown */
222			break;	/* leave untouched */
223		}
224	} else {
225		char           *q, tmp[64];
226
227		/*
228		 * Skip past any weekday prefix
229		 */
230		weekday(&str);
231		strlcpy(tmp, str, sizeof(tmp));
232		str = tmp;
233		T = localtime(&dt);
234
235		/*
236		 * See if we can break off any timezone
237		 */
238		while ((q = strrchr(tmp, ' ')) != NULL) {
239			if (strchr("(+-", q[1]) != NULL)
240				*q = '\0';
241			else {
242				int             j = 1;
243
244				while (q[j] && isupper((unsigned char)q[j]))
245					++j;
246				if (q[j] == '\0')
247					*q = '\0';
248				else
249					break;
250			}
251		}
252
253		parse_datesub(tmp, T);
254		dt = mktime(T);
255	}
256	return dt;
257}
258