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