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