6d34eae4117cc08bd17a102cc6df612350729b6e
[dragonfly.git] / usr.sbin / pw / psdate.c
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  * $FreeBSD: src/usr.sbin/pw/psdate.c,v 1.6.2.1 2000/06/28 19:19:04 ache Exp $
27  * $DragonFly: src/usr.sbin/pw/psdate.c,v 1.2 2003/06/17 04:30:01 dillon Exp $
28  */
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <ctype.h>
34
35 #include "psdate.h"
36
37
38 static int
39 a2i(char const ** str)
40 {
41         int             i = 0;
42         char const     *s = *str;
43
44         if (isdigit((unsigned char)*s)) {
45                 i = atoi(s);
46                 while (isdigit((unsigned char)*s))
47                         ++s;
48                 *str = s;
49         }
50         return i;
51 }
52
53 static int
54 numerics(char const * str)
55 {
56         int             rc = isdigit((unsigned char)*str);
57
58         if (rc)
59                 while (isdigit((unsigned char)*str) || *str == 'x')
60                         ++str;
61         return rc && !*str;
62 }
63
64 static int
65 aindex(char const * arr[], char const ** str, int len)
66 {
67         int             l, i;
68         char            mystr[32];
69
70         mystr[len] = '\0';
71         l = strlen(strncpy(mystr, *str, len));
72         for (i = 0; i < l; i++)
73                 mystr[i] = (char) tolower((unsigned char)mystr[i]);
74         for (i = 0; arr[i] && strcmp(mystr, arr[i]) != 0; i++);
75         if (arr[i] == NULL)
76                 i = -1;
77         else {                  /* Skip past it */
78                 while (**str && isalpha((unsigned char)**str))
79                         ++(*str);
80                 /* And any following whitespace */
81                 while (**str && (**str == ',' || isspace((unsigned char)**str)))
82                         ++(*str);
83         }                       /* Return index */
84         return i;
85 }
86
87 static int
88 weekday(char const ** str)
89 {
90         static char const *days[] =
91         {"sun", "mon", "tue", "wed", "thu", "fri", "sat", NULL};
92
93         return aindex(days, str, 3);
94 }
95
96 static int
97 month(char const ** str)
98 {
99         static char const *months[] =
100         {"jan", "feb", "mar", "apr", "may", "jun", "jul",
101         "aug", "sep", "oct", "nov", "dec", NULL};
102
103         return aindex(months, str, 3);
104 }
105
106 static void
107 parse_time(char const * str, int *hour, int *min, int *sec)
108 {
109         *hour = a2i(&str);
110         if ((str = strchr(str, ':')) == NULL)
111                 *min = *sec = 0;
112         else {
113                 ++str;
114                 *min = a2i(&str);
115                 *sec = ((str = strchr(str, ':')) == NULL) ? 0 : atoi(++str);
116         }
117 }
118
119
120 static void
121 parse_datesub(char const * str, int *day, int *mon, int *year)
122 {
123         int             i;
124
125         static char const nchrs[] = "0123456789 \t,/-.";
126
127         if ((i = month(&str)) != -1) {
128                 *mon = i;
129                 if ((i = a2i(&str)) != 0)
130                         *day = i;
131         } else if ((i = a2i(&str)) != 0) {
132                 *day = i;
133                 while (*str && strchr(nchrs + 10, *str) != NULL)
134                         ++str;
135                 if ((i = month(&str)) != -1)
136                         *mon = i;
137                 else if ((i = a2i(&str)) != 0)
138                         *mon = i - 1;
139         } else
140                 return;
141
142         while (*str && strchr(nchrs + 10, *str) != NULL)
143                 ++str;
144         if (isdigit((unsigned char)*str)) {
145                 *year = atoi(str);
146                 if (*year > 1900)
147                         *year -= 1900;
148                 else if (*year < 32)
149                         *year += 100;
150         }
151 }
152
153
154 /*-
155  * Parse time must be flexible, it handles the following formats:
156  * nnnnnnnnnnn          UNIX timestamp (all numeric), 0 = now
157  * 0xnnnnnnnn           UNIX timestamp in hexadecimal
158  * 0nnnnnnnnn           UNIX timestamp in octal
159  * 0                    Given time
160  * +nnnn[smhdwoy]       Given time + nnnn hours, mins, days, weeks, months or years
161  * -nnnn[smhdwoy]       Given time - nnnn hours, mins, days, weeks, months or years
162  * dd[ ./-]mmm[ ./-]yy  Date }
163  * hh:mm:ss             Time } May be combined
164  */
165
166 time_t
167 parse_date(time_t dt, char const * str)
168 {
169         char           *p;
170         int             i;
171         long            val;
172         struct tm      *T;
173
174         if (dt == 0)
175                 dt = time(NULL);
176
177         while (*str && isspace((unsigned char)*str))
178                 ++str;
179
180         if (numerics(str)) {
181                 dt = strtol(str, &p, 0);
182         } else if (*str == '+' || *str == '-') {
183                 val = strtol(str, &p, 0);
184                 switch (*p) {
185                 case 'h':
186                 case 'H':       /* hours */
187                         dt += (val * 3600L);
188                         break;
189                 case '\0':
190                 case 'm':
191                 case 'M':       /* minutes */
192                         dt += (val * 60L);
193                         break;
194                 case 's':
195                 case 'S':       /* seconds */
196                         dt += val;
197                         break;
198                 case 'd':
199                 case 'D':       /* days */
200                         dt += (val * 86400L);
201                         break;
202                 case 'w':
203                 case 'W':       /* weeks */
204                         dt += (val * 604800L);
205                         break;
206                 case 'o':
207                 case 'O':       /* months */
208                         T = localtime(&dt);
209                         T->tm_mon += (int) val;
210                         i = T->tm_mday;
211                         goto fixday;
212                 case 'y':
213                 case 'Y':       /* years */
214                         T = localtime(&dt);
215                         T->tm_year += (int) val;
216                         i = T->tm_mday;
217         fixday:
218                         dt = mktime(T);
219                         T = localtime(&dt);
220                         if (T->tm_mday != i) {
221                                 T->tm_mday = 1;
222                                 dt = mktime(T);
223                                 dt -= (time_t) 86400L;
224                         }
225                 default:        /* unknown */
226                         break;  /* leave untouched */
227                 }
228         } else {
229                 char           *q, tmp[64];
230
231                 /*
232                  * Skip past any weekday prefix
233                  */
234                 weekday(&str);
235                 str = strncpy(tmp, str, sizeof tmp - 1);
236                 tmp[sizeof tmp - 1] = '\0';
237                 T = localtime(&dt);
238
239                 /*
240                  * See if we can break off any timezone
241                  */
242                 while ((q = strrchr(tmp, ' ')) != NULL) {
243                         if (strchr("(+-", q[1]) != NULL)
244                                 *q = '\0';
245                         else {
246                                 int             j = 1;
247
248                                 while (q[j] && isupper((unsigned char)q[j]))
249                                         ++j;
250                                 if (q[j] == '\0')
251                                         *q = '\0';
252                                 else
253                                         break;
254                         }
255                 }
256
257                 /*
258                  * See if there is a time hh:mm[:ss]
259                  */
260                 if ((p = strchr(tmp, ':')) == NULL) {
261
262                         /*
263                          * No time string involved
264                          */
265                         T->tm_hour = T->tm_min = T->tm_sec = 0;
266                         parse_datesub(tmp, &T->tm_mday, &T->tm_mon, &T->tm_year);
267                 } else {
268                         char            datestr[64], timestr[64];
269
270                         /*
271                          * Let's chip off the time string
272                          */
273                         if ((q = strpbrk(p, " \t")) != NULL) {  /* Time first? */
274                                 int             l = q - str;
275
276                                 strncpy(timestr, str, l);
277                                 timestr[l] = '\0';
278                                 strncpy(datestr, q + 1, sizeof datestr);
279                                 datestr[sizeof datestr - 1] = '\0';
280                                 parse_time(timestr, &T->tm_hour, &T->tm_min, &T->tm_sec);
281                                 parse_datesub(datestr, &T->tm_mday, &T->tm_mon, &T->tm_year);
282                         } else if ((q = strrchr(tmp, ' ')) != NULL) {   /* Time last */
283                                 int             l = q - tmp;
284
285                                 strncpy(timestr, q + 1, sizeof timestr);
286                                 timestr[sizeof timestr - 1] = '\0';
287                                 strncpy(datestr, tmp, l);
288                                 datestr[l] = '\0';
289                         } else  /* Bail out */
290                                 return dt;
291                         parse_time(timestr, &T->tm_hour, &T->tm_min, &T->tm_sec);
292                         parse_datesub(datestr, &T->tm_mday, &T->tm_mon, &T->tm_year);
293                 }
294                 dt = mktime(T);
295         }
296         return dt;
297 }