xref: /dragonfly/usr.sbin/pw/psdate.c (revision c1967b0a27a332710162c9a05add4673566bafc2)
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
a2i(char const ** str)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
numerics(char const * str)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
aindex(char const * arr[],char const ** str,int len)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
weekday(char const ** str)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
month(char const ** str)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
parse_time(char const * str,int * hour,int * min,int * sec)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
parse_datesub(char const * str,int * day,int * mon,int * year)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
parse_date(time_t dt,char const * str)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 }
293