]> git.cameronkatri.com Git - pw-darwin.git/blob - pw/psdate.c
pw(8) -- a backend utility to manage the user and group databases.
[pw-darwin.git] / pw / psdate.c
1 /*-
2 * Copyright (c) 1996 by David L. Nugent <davidn@blaze.net.au>.
3 * 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 as
10 * the first lines of this file unmodified.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 * must display the following acknowledgement:
16 * This product includes software developed by David L. Nugent.
17 * 4. The name of the author may not be used to endorse or promote products
18 * derived from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE DAVID L. NUGENT ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 * $Id$
33 */
34
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <ctype.h>
39
40 #include "psdate.h"
41
42
43 static int
44 a2i(char const ** str)
45 {
46 int i = 0;
47 char const *s = *str;
48
49 if (isdigit(*s)) {
50 i = atoi(s);
51 while (isdigit(*s))
52 ++s;
53 *str = s;
54 }
55 return i;
56 }
57
58 static int
59 numerics(char const * str)
60 {
61 int rc = isdigit(*str);
62
63 if (rc)
64 while (isdigit(*str) || *str == 'x')
65 ++str;
66 return rc && !*str;
67 }
68
69 static int
70 aindex(char const * arr[], char const ** str, int len)
71 {
72 int l, i;
73 char mystr[32];
74
75 mystr[len] = '\0';
76 l = strlen(strncpy(mystr, *str, len));
77 for (i = 0; i < l; i++)
78 mystr[i] = (char) tolower(mystr[i]);
79 for (i = 0; arr[i] && strcmp(mystr, arr[i]) != 0; i++);
80 if (arr[i] == NULL)
81 i = -1;
82 else { /* Skip past it */
83 while (**str && isalpha(**str))
84 ++(*str);
85 /* And any following whitespace */
86 while (**str && (**str == ',' || isspace(**str)))
87 ++(*str);
88 } /* Return index */
89 return i;
90 }
91
92 static int
93 weekday(char const ** str)
94 {
95 static char const *days[] =
96 {"sun", "mon", "tue", "wed", "thu", "fri", "sat", NULL};
97
98 return aindex(days, str, 3);
99 }
100
101 static int
102 month(char const ** str)
103 {
104 static char const *months[] =
105 {"jan", "feb", "mar", "apr", "may", "jun", "jul",
106 "aug", "sep", "oct", "nov", "dec", NULL};
107
108 return aindex(months, str, 3);
109 }
110
111 static void
112 parse_time(char const * str, int *hour, int *min, int *sec)
113 {
114 *hour = a2i(&str);
115 if ((str = strchr(str, ':')) == NULL)
116 *min = *sec = 0;
117 else {
118 ++str;
119 *min = a2i(&str);
120 *sec = ((str = strchr(str, ':')) == NULL) ? 0 : atoi(++str);
121 }
122 }
123
124
125 static void
126 parse_datesub(char const * str, int *day, int *mon, int *year)
127 {
128 int i;
129
130 static char const nchrs[] = "0123456789 \t,/-.";
131
132 if ((i = month(&str)) != -1) {
133 *mon = i;
134 if ((i = a2i(&str)) != 0)
135 *day = i;
136 } else if ((i = a2i(&str)) != 0) {
137 *day = i;
138 while (*str && strchr(nchrs + 10, *str) != NULL)
139 ++str;
140 if ((i = month(&str)) != -1)
141 *mon = i;
142 else if ((i = a2i(&str)) != 0)
143 *mon = i - 1;
144 } else
145 return;
146
147 while (*str && strchr(nchrs + 10, *str) != NULL)
148 ++str;
149 if (isdigit(*str)) {
150 *year = atoi(str);
151 if (*year > 1900)
152 *year -= 1900;
153 else if (*year < 32)
154 *year += 100;
155 }
156 }
157
158
159 /*-
160 * Parse time must be flexible, it handles the following formats:
161 * nnnnnnnnnnn UNIX timestamp (all numeric), 0 = now
162 * 0xnnnnnnnn UNIX timestamp in hexadecimal
163 * 0nnnnnnnnn UNIX timestamp in octal
164 * 0 Given time
165 * +nnnn[smhdwoy] Given time + nnnn hours, mins, days, weeks, months or years
166 * -nnnn[smhdwoy] Given time - nnnn hours, mins, days, weeks, months or years
167 * dd[ ./-]mmm[ ./-]yy Date }
168 * hh:mm:ss Time } May be combined
169 */
170
171 time_t
172 parse_date(time_t dt, char const * str)
173 {
174 char *p;
175 int i;
176 long val;
177 struct tm *T;
178
179 if (dt == 0)
180 dt = time(NULL);
181
182 while (*str && isspace(*str))
183 ++str;
184
185 if (numerics(str)) {
186 val = strtol(str, &p, 0);
187 dt = val ? val : dt;
188 } else if (*str == '+' || *str == '-') {
189 val = strtol(str, &p, 0);
190 switch (*p) {
191 case 'h':
192 case 'H': /* hours */
193 dt += (val * 3600L);
194 break;
195 case '\0':
196 case 'm':
197 case 'M': /* minutes */
198 dt += (val * 60L);
199 break;
200 case 's':
201 case 'S': /* seconds */
202 dt += val;
203 break;
204 case 'd':
205 case 'D': /* days */
206 dt += (val * 86400L);
207 break;
208 case 'w':
209 case 'W': /* weeks */
210 dt += (val * 604800L);
211 break;
212 case 'o':
213 case 'O': /* months */
214 T = localtime(&dt);
215 T->tm_mon += (int) val;
216 i = T->tm_mday;
217 goto fixday;
218 case 'y':
219 case 'Y': /* years */
220 T = localtime(&dt);
221 T->tm_year += (int) val;
222 i = T->tm_mday;
223 fixday:
224 dt = mktime(T);
225 T = localtime(&dt);
226 if (T->tm_mday != i) {
227 T->tm_mday = 1;
228 dt = mktime(T);
229 dt -= (time_t) 86400L;
230 }
231 default: /* unknown */
232 break; /* leave untouched */
233 }
234 } else {
235 char *q, tmp[64];
236
237 /*
238 * Skip past any weekday prefix
239 */
240 weekday(&str);
241 str = strncpy(tmp, str, sizeof tmp - 1);
242 tmp[sizeof tmp - 1] = '\0';
243 T = localtime(&dt);
244
245 /*
246 * See if we can break off any timezone
247 */
248 while ((q = strrchr(tmp, ' ')) != NULL) {
249 if (strchr("(+-", q[1]) != NULL)
250 *q = '\0';
251 else {
252 int j = 1;
253
254 while (q[j] && isupper(q[j]))
255 ++j;
256 if (q[j] == '\0')
257 *q = '\0';
258 else
259 break;
260 }
261 }
262
263 /*
264 * See if there is a time hh:mm[:ss]
265 */
266 if ((p = strchr(tmp, ':')) == NULL) {
267
268 /*
269 * No time string involved
270 */
271 T->tm_hour = T->tm_min = T->tm_sec = 0;
272 parse_datesub(tmp, &T->tm_mday, &T->tm_mon, &T->tm_year);
273 } else {
274 char datestr[64], timestr[64];
275
276 /*
277 * Let's chip off the time string
278 */
279 if ((q = strpbrk(p, " \t")) != NULL) { /* Time first? */
280 int l = q - str;
281
282 strncpy(timestr, str, l);
283 timestr[l] = '\0';
284 strncpy(datestr, q + 1, sizeof datestr);
285 datestr[sizeof datestr - 1] = '\0';
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 } else if ((q = strrchr(tmp, ' ')) != NULL) { /* Time last */
289 int l = q - tmp;
290
291 strncpy(timestr, q + 1, sizeof timestr);
292 timestr[sizeof timestr - 1] = '\0';
293 strncpy(datestr, tmp, l);
294 datestr[l] = '\0';
295 } else /* Bail out */
296 return dt;
297 parse_time(timestr, &T->tm_hour, &T->tm_min, &T->tm_sec);
298 parse_datesub(datestr, &T->tm_mday, &T->tm_mon, &T->tm_year);
299 }
300 dt = mktime(T);
301 }
302 return dt;
303 }