120253Sjoerg/*-
220302Sjoerg * Copyright (C) 1996
320302Sjoerg *	David L. Nugent.  All rights reserved.
420253Sjoerg *
520253Sjoerg * Redistribution and use in source and binary forms, with or without
620253Sjoerg * modification, are permitted provided that the following conditions
720253Sjoerg * are met:
820253Sjoerg * 1. Redistributions of source code must retain the above copyright
920302Sjoerg *    notice, this list of conditions and the following disclaimer.
1020253Sjoerg * 2. Redistributions in binary form must reproduce the above copyright
1120253Sjoerg *    notice, this list of conditions and the following disclaimer in the
1220253Sjoerg *    documentation and/or other materials provided with the distribution.
1320253Sjoerg *
1420302Sjoerg * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
1520253Sjoerg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1620253Sjoerg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1720302Sjoerg * ARE DISCLAIMED.  IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
1820253Sjoerg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1920253Sjoerg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2020253Sjoerg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2120253Sjoerg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2220253Sjoerg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2320253Sjoerg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2420253Sjoerg * SUCH DAMAGE.
2520253Sjoerg */
2620253Sjoerg
2730259Scharnier#ifndef lint
2830259Scharnierstatic const char rcsid[] =
2950479Speter  "$FreeBSD: stable/10/usr.sbin/pw/psdate.c 326849 2017-12-14 13:10:22Z eugen $";
3030259Scharnier#endif /* not lint */
3130259Scharnier
32287084Sbapt#include <ctype.h>
33287084Sbapt#include <err.h>
3420253Sjoerg#include <stdlib.h>
3520253Sjoerg#include <string.h>
36287084Sbapt#include <xlocale.h>
3720253Sjoerg
3820253Sjoerg#include "psdate.h"
3920253Sjoerg
4020253Sjoerg
41326849Seugenint
4220253Sjoergnumerics(char const * str)
4320253Sjoerg{
4420253Sjoerg
45314277Sbapt	return (str[strspn(str, "0123456789x")] == '\0');
4620253Sjoerg}
4720253Sjoerg
4820253Sjoergstatic int
4920253Sjoergaindex(char const * arr[], char const ** str, int len)
5020253Sjoerg{
5120253Sjoerg	int             l, i;
5220253Sjoerg	char            mystr[32];
5320253Sjoerg
5420253Sjoerg	mystr[len] = '\0';
5520253Sjoerg	l = strlen(strncpy(mystr, *str, len));
5620253Sjoerg	for (i = 0; i < l; i++)
5761957Sache		mystr[i] = (char) tolower((unsigned char)mystr[i]);
5820253Sjoerg	for (i = 0; arr[i] && strcmp(mystr, arr[i]) != 0; i++);
5920253Sjoerg	if (arr[i] == NULL)
6020253Sjoerg		i = -1;
6120253Sjoerg	else {			/* Skip past it */
6261957Sache		while (**str && isalpha((unsigned char)**str))
6320253Sjoerg			++(*str);
6420253Sjoerg		/* And any following whitespace */
6561957Sache		while (**str && (**str == ',' || isspace((unsigned char)**str)))
6620253Sjoerg			++(*str);
6720253Sjoerg	}			/* Return index */
6820253Sjoerg	return i;
6920253Sjoerg}
7020253Sjoerg
7120253Sjoergstatic int
7220253Sjoergweekday(char const ** str)
7320253Sjoerg{
7420253Sjoerg	static char const *days[] =
7520253Sjoerg	{"sun", "mon", "tue", "wed", "thu", "fri", "sat", NULL};
7620253Sjoerg
7720253Sjoerg	return aindex(days, str, 3);
7820253Sjoerg}
7920253Sjoerg
80287084Sbaptstatic void
81287084Sbaptparse_datesub(char const * str, struct tm *t)
8220253Sjoerg{
83287084Sbapt	struct tm	 tm;
84287084Sbapt	locale_t	 l;
85287084Sbapt	int		 i;
86287084Sbapt	char		*ret;
87287084Sbapt	const char	*valid_formats[] = {
88287084Sbapt		"%d-%b-%y",
89287084Sbapt		"%d-%b-%Y",
90287084Sbapt		"%d-%m-%y",
91287084Sbapt		"%d-%m-%Y",
92287084Sbapt		"%H:%M %d-%b-%y",
93287084Sbapt		"%H:%M %d-%b-%Y",
94287084Sbapt		"%H:%M %d-%m-%y",
95287084Sbapt		"%H:%M %d-%m-%Y",
96287084Sbapt		"%H:%M:%S %d-%b-%y",
97287084Sbapt		"%H:%M:%S %d-%b-%Y",
98287084Sbapt		"%H:%M:%S %d-%m-%y",
99287084Sbapt		"%H:%M:%S %d-%m-%Y",
100287084Sbapt		"%d-%b-%y %H:%M",
101287084Sbapt		"%d-%b-%Y %H:%M",
102287084Sbapt		"%d-%m-%y %H:%M",
103287084Sbapt		"%d-%m-%Y %H:%M",
104287084Sbapt		"%d-%b-%y %H:%M:%S",
105287084Sbapt		"%d-%b-%Y %H:%M:%S",
106287084Sbapt		"%d-%m-%y %H:%M:%S",
107287084Sbapt		"%d-%m-%Y %H:%M:%S",
108287084Sbapt		"%H:%M\t%d-%b-%y",
109287084Sbapt		"%H:%M\t%d-%b-%Y",
110287084Sbapt		"%H:%M\t%d-%m-%y",
111287084Sbapt		"%H:%M\t%d-%m-%Y",
112287084Sbapt		"%H:%M\t%S %d-%b-%y",
113287084Sbapt		"%H:%M\t%S %d-%b-%Y",
114287084Sbapt		"%H:%M\t%S %d-%m-%y",
115287084Sbapt		"%H:%M\t%S %d-%m-%Y",
116287084Sbapt		"%d-%b-%y\t%H:%M",
117287084Sbapt		"%d-%b-%Y\t%H:%M",
118287084Sbapt		"%d-%m-%y\t%H:%M",
119287084Sbapt		"%d-%m-%Y\t%H:%M",
120287084Sbapt		"%d-%b-%y\t%H:%M:%S",
121287084Sbapt		"%d-%b-%Y\t%H:%M:%S",
122287084Sbapt		"%d-%m-%y\t%H:%M:%S",
123287084Sbapt		"%d-%m-%Y\t%H:%M:%S",
124287084Sbapt		NULL,
125287084Sbapt	};
12620253Sjoerg
127287084Sbapt	l = newlocale(LC_ALL_MASK, "C", NULL);
12820253Sjoerg
129287084Sbapt	memset(&tm, 0, sizeof(tm));
130287084Sbapt	for (i=0; valid_formats[i] != NULL; i++) {
131287084Sbapt		ret = strptime_l(str, valid_formats[i], &tm, l);
132287084Sbapt		if (ret && *ret == '\0') {
133287084Sbapt			t->tm_mday = tm.tm_mday;
134287084Sbapt			t->tm_mon = tm.tm_mon;
135287084Sbapt			t->tm_year = tm.tm_year;
136287084Sbapt			t->tm_hour = tm.tm_hour;
137287084Sbapt			t->tm_min = tm.tm_min;
138287084Sbapt			t->tm_sec = tm.tm_sec;
139287084Sbapt			freelocale(l);
140287084Sbapt			return;
141287084Sbapt		}
14220253Sjoerg	}
14320253Sjoerg
144287084Sbapt	freelocale(l);
14520253Sjoerg
146287084Sbapt	errx(EXIT_FAILURE, "Invalid date");
14720253Sjoerg}
14820253Sjoerg
14920253Sjoerg
15020253Sjoerg/*-
15120253Sjoerg * Parse time must be flexible, it handles the following formats:
15220253Sjoerg * nnnnnnnnnnn		UNIX timestamp (all numeric), 0 = now
15320253Sjoerg * 0xnnnnnnnn		UNIX timestamp in hexadecimal
15420253Sjoerg * 0nnnnnnnnn		UNIX timestamp in octal
15520253Sjoerg * 0			Given time
15620253Sjoerg * +nnnn[smhdwoy]	Given time + nnnn hours, mins, days, weeks, months or years
15720253Sjoerg * -nnnn[smhdwoy]	Given time - nnnn hours, mins, days, weeks, months or years
15820253Sjoerg * dd[ ./-]mmm[ ./-]yy	Date }
15920253Sjoerg * hh:mm:ss		Time } May be combined
16020253Sjoerg */
16120253Sjoerg
16220253Sjoergtime_t
16320253Sjoergparse_date(time_t dt, char const * str)
16420253Sjoerg{
16520253Sjoerg	char           *p;
16620253Sjoerg	int             i;
16720253Sjoerg	long            val;
16820253Sjoerg	struct tm      *T;
16920253Sjoerg
17020253Sjoerg	if (dt == 0)
17120253Sjoerg		dt = time(NULL);
17220253Sjoerg
17361957Sache	while (*str && isspace((unsigned char)*str))
17420253Sjoerg		++str;
17520253Sjoerg
17620253Sjoerg	if (numerics(str)) {
17744775Sdavidn		dt = strtol(str, &p, 0);
17820253Sjoerg	} else if (*str == '+' || *str == '-') {
17920253Sjoerg		val = strtol(str, &p, 0);
18020253Sjoerg		switch (*p) {
18120253Sjoerg		case 'h':
18220253Sjoerg		case 'H':	/* hours */
18320253Sjoerg			dt += (val * 3600L);
18420253Sjoerg			break;
18520253Sjoerg		case '\0':
18620253Sjoerg		case 'm':
18720253Sjoerg		case 'M':	/* minutes */
18820253Sjoerg			dt += (val * 60L);
18920253Sjoerg			break;
19020253Sjoerg		case 's':
19120253Sjoerg		case 'S':	/* seconds */
19220253Sjoerg			dt += val;
19320253Sjoerg			break;
19420253Sjoerg		case 'd':
19520253Sjoerg		case 'D':	/* days */
19620253Sjoerg			dt += (val * 86400L);
19720253Sjoerg			break;
19820253Sjoerg		case 'w':
19920253Sjoerg		case 'W':	/* weeks */
20020253Sjoerg			dt += (val * 604800L);
20120253Sjoerg			break;
20220253Sjoerg		case 'o':
20320253Sjoerg		case 'O':	/* months */
20420253Sjoerg			T = localtime(&dt);
20520253Sjoerg			T->tm_mon += (int) val;
20620253Sjoerg			i = T->tm_mday;
20720253Sjoerg			goto fixday;
20820253Sjoerg		case 'y':
20920253Sjoerg		case 'Y':	/* years */
21020253Sjoerg			T = localtime(&dt);
21120253Sjoerg			T->tm_year += (int) val;
21220253Sjoerg			i = T->tm_mday;
21320253Sjoerg	fixday:
21420253Sjoerg			dt = mktime(T);
21520253Sjoerg			T = localtime(&dt);
21620253Sjoerg			if (T->tm_mday != i) {
21720253Sjoerg				T->tm_mday = 1;
21820253Sjoerg				dt = mktime(T);
21920253Sjoerg				dt -= (time_t) 86400L;
22020253Sjoerg			}
22120253Sjoerg		default:	/* unknown */
22220253Sjoerg			break;	/* leave untouched */
22320253Sjoerg		}
22420253Sjoerg	} else {
22520253Sjoerg		char           *q, tmp[64];
22620253Sjoerg
22720253Sjoerg		/*
22820253Sjoerg		 * Skip past any weekday prefix
22920253Sjoerg		 */
23020253Sjoerg		weekday(&str);
231130633Srobert		strlcpy(tmp, str, sizeof(tmp));
232130633Srobert		str = tmp;
23320253Sjoerg		T = localtime(&dt);
23420253Sjoerg
23520253Sjoerg		/*
23620253Sjoerg		 * See if we can break off any timezone
23720253Sjoerg		 */
23820253Sjoerg		while ((q = strrchr(tmp, ' ')) != NULL) {
23920253Sjoerg			if (strchr("(+-", q[1]) != NULL)
24020253Sjoerg				*q = '\0';
24120253Sjoerg			else {
24220253Sjoerg				int             j = 1;
24320253Sjoerg
24461957Sache				while (q[j] && isupper((unsigned char)q[j]))
24520253Sjoerg					++j;
24620253Sjoerg				if (q[j] == '\0')
24720253Sjoerg					*q = '\0';
24820253Sjoerg				else
24920253Sjoerg					break;
25020253Sjoerg			}
25120253Sjoerg		}
25220253Sjoerg
253287084Sbapt		parse_datesub(tmp, T);
25420253Sjoerg		dt = mktime(T);
25520253Sjoerg	}
25620253Sjoerg	return dt;
25720253Sjoerg}
258