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
50static 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;
85label:
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
537char *
538strptime(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 */
549char *
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}
556