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