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