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