1 /*
2 * Copyright 2011, Nexenta Systems, Inc. All rights reserved.
3 * Copyright (c) 1994 Powerdog Industries. 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 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer
14 * in the documentation and/or other materials provided with the
15 * distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY POWERDOG INDUSTRIES ``AS IS'' AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE POWERDOG INDUSTRIES BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
24 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
26 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 *
29 * The views and conclusions contained in the software and documentation
30 * are those of the authors and should not be interpreted as representing
31 * official policies, either expressed or implied, of Powerdog Industries.
32 */
33
34 #include "lint.h"
35 #include <time.h>
36 #include <ctype.h>
37 #include <errno.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <pthread.h>
41 #include <alloca.h>
42 #include "timelocal.h"
43
44 #define asizeof(a) (sizeof (a) / sizeof ((a)[0]))
45
46 #define F_GMT (1 << 0)
47 #define F_ZERO (1 << 1)
48 #define F_RECURSE (1 << 2)
49
50 static char *
51 __strptime(const char *buf, const char *fmt, struct tm *tm, int *flagsp)
52 {
53 char c;
54 const char *ptr;
55 int i, len, recurse = 0;
56 int Ealternative, Oalternative;
57 struct lc_time_T *tptr = __get_current_time_locale();
58
59 if (*flagsp & F_RECURSE)
60 recurse = 1;
61 *flagsp |= F_RECURSE;
62
63 if (*flagsp & F_ZERO)
64 (void) memset(tm, 0, sizeof (*tm));
65 *flagsp &= ~F_ZERO;
66
67 ptr = fmt;
68 while (*ptr != 0) {
69 if (*buf == 0)
70 break;
71
72 c = *ptr++;
73
74 if (c != '%') {
75 if (isspace(c))
76 while (isspace(*buf))
77 buf++;
78 else if (c != *buf++)
79 return (NULL);
80 continue;
81 }
82
83 Ealternative = 0;
84 Oalternative = 0;
85 label:
86 c = *ptr++;
87 switch (c) {
88 case 0:
89 case '%':
90 if (*buf++ != '%')
91 return (NULL);
92 break;
93
94 case '+':
95 buf = __strptime(buf, tptr->date_fmt, tm, flagsp);
96 if (buf == NULL)
97 return (NULL);
98 break;
99
100 case 'C':
101 if (!isdigit(*buf))
102 return (NULL);
103
104 /* XXX This will break for 3-digit centuries. */
105 len = 2;
106 for (i = 0; len && isdigit(*buf); buf++) {
107 i *= 10;
108 i += *buf - '0';
109 len--;
110 }
111 if (i < 19)
112 return (NULL);
113
114 tm->tm_year = i * 100 - 1900;
115 break;
116
117 case 'c':
118 buf = __strptime(buf, tptr->c_fmt, tm, flagsp);
119 if (buf == NULL)
120 return (NULL);
121 break;
122
123 case 'D':
124 buf = __strptime(buf, "%m/%d/%y", tm, flagsp);
125 if (buf == NULL)
126 return (NULL);
127 break;
128
129 case 'E':
130 if (Ealternative || Oalternative)
131 break;
132 Ealternative++;
133 goto label;
134
135 case 'O':
136 if (Ealternative || Oalternative)
137 break;
138 Oalternative++;
139 goto label;
140
141 case 'F':
142 buf = __strptime(buf, "%Y-%m-%d", tm, flagsp);
143 if (buf == NULL)
144 return (NULL);
145 break;
146
147 case 'R':
148 buf = __strptime(buf, "%H:%M", tm, flagsp);
149 if (buf == NULL)
150 return (NULL);
151 break;
152
153 case 'r':
154 buf = __strptime(buf, tptr->ampm_fmt, tm, flagsp);
155 if (buf == NULL)
156 return (NULL);
157 break;
158
159 case 'T':
160 buf = __strptime(buf, "%H:%M:%S", tm, flagsp);
161 if (buf == NULL)
162 return (NULL);
163 break;
164
165 case 'X':
166 buf = __strptime(buf, tptr->X_fmt, tm, flagsp);
167 if (buf == NULL)
168 return (NULL);
169 break;
170
171 case 'x':
172 buf = __strptime(buf, tptr->x_fmt, tm, flagsp);
173 if (buf == NULL)
174 return (NULL);
175 break;
176
177 case 'j':
178 if (!isdigit(*buf))
179 return (NULL);
180
181 len = 3;
182 for (i = 0; len && isdigit(*buf); buf++) {
183 i *= 10;
184 i += *buf - '0';
185 len--;
186 }
187 if (i < 1 || i > 366)
188 return (NULL);
189
190 tm->tm_yday = i - 1;
191 break;
192
193 case 'M':
194 case 'S':
195 if (*buf == 0 || isspace(*buf))
196 break;
197
198 if (!isdigit(*buf))
199 return (NULL);
200
201 len = 2;
202 for (i = 0; len && isdigit(*buf); buf++) {
203 i *= 10;
204 i += *buf - '0';
205 len--;
206 }
207
208 if (c == 'M') {
209 if (i > 59)
210 return (NULL);
211 tm->tm_min = i;
212 } else {
213 if (i > 60)
214 return (NULL);
215 tm->tm_sec = i;
216 }
217
218 if (isspace(*buf))
219 while (*ptr != 0 && !isspace(*ptr))
220 ptr++;
221 break;
222
223 case 'H':
224 case 'I':
225 case 'k':
226 case 'l':
227 /*
228 * Of these, %l is the only specifier explicitly
229 * documented as not being zero-padded. However,
230 * there is no harm in allowing zero-padding.
231 *
232 * XXX The %l specifier may gobble one too many
233 * digits if used incorrectly.
234 */
235 if (!isdigit(*buf))
236 return (NULL);
237
238 len = 2;
239 for (i = 0; len && isdigit(*buf); buf++) {
240 i *= 10;
241 i += *buf - '0';
242 len--;
243 }
244 if (c == 'H' || c == 'k') {
245 if (i > 23)
246 return (NULL);
247 } else if (i > 12)
248 return (NULL);
249
250 tm->tm_hour = i;
251
252 if (isspace(*buf))
253 while (*ptr != 0 && !isspace(*ptr))
254 ptr++;
255 break;
256
257 case 'p':
258 /*
259 * XXX This is bogus if parsed before hour-related
260 * specifiers.
261 */
262 len = strlen(tptr->am);
263 if (strncasecmp(buf, tptr->am, len) == 0) {
264 if (tm->tm_hour > 12)
265 return (NULL);
266 if (tm->tm_hour == 12)
267 tm->tm_hour = 0;
268 buf += len;
269 break;
270 }
271
272 len = strlen(tptr->pm);
273 if (strncasecmp(buf, tptr->pm, len) == 0) {
274 if (tm->tm_hour > 12)
275 return (NULL);
276 if (tm->tm_hour != 12)
277 tm->tm_hour += 12;
278 buf += len;
279 break;
280 }
281
282 return (NULL);
283
284 case 'A':
285 case 'a':
286 for (i = 0; i < asizeof(tptr->weekday); i++) {
287 len = strlen(tptr->weekday[i]);
288 if (strncasecmp(buf, tptr->weekday[i], len) ==
289 0)
290 break;
291 len = strlen(tptr->wday[i]);
292 if (strncasecmp(buf, tptr->wday[i], len) == 0)
293 break;
294 }
295 if (i == asizeof(tptr->weekday))
296 return (NULL);
297
298 tm->tm_wday = i;
299 buf += len;
300 break;
301
302 case 'U':
303 case 'W':
304 /*
305 * XXX This is bogus, as we can not assume any valid
306 * information present in the tm structure at this
307 * point to calculate a real value, so just check the
308 * range for now.
309 */
310 if (!isdigit(*buf))
311 return (NULL);
312
313 len = 2;
314 for (i = 0; len && isdigit(*buf); buf++) {
315 i *= 10;
316 i += *buf - '0';
317 len--;
318 }
319 if (i > 53)
320 return (NULL);
321
322 if (isspace(*buf))
323 while (*ptr != 0 && !isspace(*ptr))
324 ptr++;
325 break;
326
327 case 'w':
328 if (!isdigit(*buf))
329 return (NULL);
330
331 i = *buf - '0';
332 if (i > 6)
333 return (NULL);
334
335 tm->tm_wday = i;
336
337 if (isspace(*buf))
338 while (*ptr != 0 && !isspace(*ptr))
339 ptr++;
340 break;
341
342 case 'e':
343 /*
344 * The %e format has a space before single digits
345 * which we need to skip.
346 */
347 if (isspace(*buf))
348 buf++;
349 /* FALLTHROUGH */
350 case 'd':
351 /*
352 * The %e specifier is explicitly documented as not
353 * being zero-padded but there is no harm in allowing
354 * such padding.
355 *
356 * XXX The %e specifier may gobble one too many
357 * digits if used incorrectly.
358 */
359 if (!isdigit(*buf))
360 return (NULL);
361
362 len = 2;
363 for (i = 0; len && isdigit(*buf); buf++) {
364 i *= 10;
365 i += *buf - '0';
366 len--;
367 }
368 if (i > 31)
369 return (NULL);
370
371 tm->tm_mday = i;
372
373 if (isspace(*buf))
374 while (*ptr != 0 && !isspace(*ptr))
375 ptr++;
376 break;
377
378 case 'B':
379 case 'b':
380 case 'h':
381 for (i = 0; i < asizeof(tptr->month); i++) {
382 len = strlen(tptr->month[i]);
383 if (strncasecmp(buf, tptr->month[i], len) == 0)
384 break;
385 }
386 /*
387 * Try the abbreviated month name if the full name
388 * wasn't found.
389 */
390 if (i == asizeof(tptr->month)) {
391 for (i = 0; i < asizeof(tptr->month); i++) {
392 len = strlen(tptr->mon[i]);
393 if (strncasecmp(buf, tptr->mon[i],
394 len) == 0)
395 break;
396 }
397 }
398 if (i == asizeof(tptr->month))
399 return (NULL);
400
401 tm->tm_mon = i;
402 buf += len;
403 break;
404
405 case 'm':
406 if (!isdigit(*buf))
407 return (NULL);
408
409 len = 2;
410 for (i = 0; len && isdigit(*buf); buf++) {
411 i *= 10;
412 i += *buf - '0';
413 len--;
414 }
415 if (i < 1 || i > 12)
416 return (NULL);
417
418 tm->tm_mon = i - 1;
419
420 if (isspace(*buf))
421 while (*ptr != NULL && !isspace(*ptr))
422 ptr++;
423 break;
424
425 case 's':
426 {
427 char *cp;
428 int sverrno;
429 time_t t;
430
431 sverrno = errno;
432 errno = 0;
433 t = strtol(buf, &cp, 10);
434 if (errno == ERANGE) {
435 errno = sverrno;
436 return (NULL);
437 }
438 errno = sverrno;
439 buf = cp;
440 (void) gmtime_r(&t, tm);
441 *flagsp |= F_GMT;
442 }
443 break;
444
445 case 'Y':
446 case 'y':
447 if (*buf == NULL || isspace(*buf))
448 break;
449
450 if (!isdigit(*buf))
451 return (NULL);
452
453 len = (c == 'Y') ? 4 : 2;
454 for (i = 0; len && isdigit(*buf); buf++) {
455 i *= 10;
456 i += *buf - '0';
457 len--;
458 }
459 if (c == 'Y')
460 i -= 1900;
461 if (c == 'y' && i < 69)
462 i += 100;
463 if (i < 0)
464 return (NULL);
465
466 tm->tm_year = i;
467
468 if (isspace(*buf))
469 while (*ptr != 0 && !isspace(*ptr))
470 ptr++;
471 break;
472
473 case 'Z':
474 {
475 const char *cp = buf;
476 char *zonestr;
477
478 while (isupper(*cp))
479 ++cp;
480 if (cp - buf) {
481 zonestr = alloca(cp - buf + 1);
482 (void) strncpy(zonestr, buf, cp - buf);
483 zonestr[cp - buf] = '\0';
484 tzset();
485 if (strcmp(zonestr, "GMT") == 0) {
486 *flagsp |= F_GMT;
487 } else if (0 == strcmp(zonestr, tzname[0])) {
488 tm->tm_isdst = 0;
489 } else if (0 == strcmp(zonestr, tzname[1])) {
490 tm->tm_isdst = 1;
491 } else {
492 return (NULL);
493 }
494 buf += cp - buf;
495 }
496 }
497 break;
498
499 case 'z':
500 {
501 int sign = 1;
502
503 if (*buf != '+') {
504 if (*buf == '-')
505 sign = -1;
506 else
507 return (NULL);
508 }
509 buf++;
510 i = 0;
511 for (len = 4; len > 0; len--) {
512 if (!isdigit(*buf))
513 return (NULL);
514 i *= 10;
515 i += *buf - '0';
516 buf++;
517 }
518
519 tm->tm_hour -= sign * (i / 100);
520 tm->tm_min -= sign * (i % 100);
521 *flagsp |= F_GMT;
522 }
523 break;
524 }
525 }
526
527 if (!recurse) {
528 if (buf && (*flagsp & F_GMT)) {
529 time_t t = timegm(tm);
530 (void) localtime_r(&t, tm);
531 }
532 }
533
534 return ((char *)buf);
535 }
536
537 char *
538 strptime(const char *buf, const char *fmt, struct tm *tm)
539 {
540 int flags = F_ZERO;
541
542 return (__strptime(buf, fmt, tm, &flags));
543 }
544
545 /*
546 * This is used by Solaris, and is a variant that does not clear the
547 * incoming tm. It is triggered by -D_STRPTIME_DONTZERO.
548 */
549 char *
550 __strptime_dontzero(const char *buf, const char *fmt, struct tm *tm)
551 {
552 int flags = 0;
553
554 return (__strptime(buf, fmt, tm, &flags));
555 }