1#include <stdlib.h>
2#include <langinfo.h>
3#include <time.h>
4#include <ctype.h>
5#include <stddef.h>
6#include <string.h>
7#include <strings.h>
8
9char *strptime(const char *restrict s, const char *restrict f, struct tm *restrict tm)
10{
11	int i, w, neg, adj, min, range, *dest, dummy;
12	const char *ex;
13	size_t len;
14	int want_century = 0, century = 0;
15	while (*f) {
16		if (*f != '%') {
17			if (isspace(*f)) for (; *s && isspace(*s); s++);
18			else if (*s != *f) return 0;
19			else s++;
20			f++;
21			continue;
22		}
23		f++;
24		if (*f == '+') f++;
25		if (isdigit(*f)) {
26			char *new_f;
27			w=strtoul(f, &new_f, 10);
28			f = new_f;
29		} else {
30			w=-1;
31		}
32		adj=0;
33		switch (*f++) {
34		case 'a': case 'A':
35			dest = &tm->tm_wday;
36			min = ABDAY_1;
37			range = 7;
38			goto symbolic_range;
39		case 'b': case 'B': case 'h':
40			dest = &tm->tm_mon;
41			min = ABMON_1;
42			range = 12;
43			goto symbolic_range;
44		case 'c':
45			s = strptime(s, nl_langinfo(D_T_FMT), tm);
46			if (!s) return 0;
47			break;
48		case 'C':
49			dest = &century;
50			if (w<0) w=2;
51			want_century |= 2;
52			goto numeric_digits;
53		case 'd': case 'e':
54			dest = &tm->tm_mday;
55			min = 1;
56			range = 31;
57			goto numeric_range;
58		case 'D':
59			s = strptime(s, "%m/%d/%y", tm);
60			if (!s) return 0;
61			break;
62		case 'H':
63			dest = &tm->tm_hour;
64			min = 0;
65			range = 24;
66			goto numeric_range;
67		case 'I':
68			dest = &tm->tm_hour;
69			min = 1;
70			range = 12;
71			goto numeric_range;
72		case 'j':
73			dest = &tm->tm_yday;
74			min = 1;
75			range = 366;
76			goto numeric_range;
77		case 'm':
78			dest = &tm->tm_mon;
79			min = 1;
80			range = 12;
81			adj = 1;
82			goto numeric_range;
83		case 'M':
84			dest = &tm->tm_min;
85			min = 0;
86			range = 60;
87			goto numeric_range;
88		case 'n': case 't':
89			for (; *s && isspace(*s); s++);
90			break;
91		case 'p':
92			ex = nl_langinfo(AM_STR);
93			len = strlen(ex);
94			if (!strncasecmp(s, ex, len)) {
95				tm->tm_hour %= 12;
96				break;
97			}
98			ex = nl_langinfo(PM_STR);
99			len = strlen(ex);
100			if (!strncasecmp(s, ex, len)) {
101				tm->tm_hour %= 12;
102				tm->tm_hour += 12;
103				break;
104			}
105			return 0;
106		case 'r':
107			s = strptime(s, nl_langinfo(T_FMT_AMPM), tm);
108			if (!s) return 0;
109			break;
110		case 'R':
111			s = strptime(s, "%H:%M", tm);
112			if (!s) return 0;
113			break;
114		case 'S':
115			dest = &tm->tm_sec;
116			min = 0;
117			range = 61;
118			goto numeric_range;
119		case 'T':
120			s = strptime(s, "%H:%M:%S", tm);
121			if (!s) return 0;
122			break;
123		case 'U':
124		case 'W':
125			/* Throw away result, for now. (FIXME?) */
126			dest = &dummy;
127			min = 0;
128			range = 54;
129			goto numeric_range;
130		case 'w':
131			dest = &tm->tm_wday;
132			min = 0;
133			range = 7;
134			goto numeric_range;
135		case 'x':
136			s = strptime(s, nl_langinfo(D_FMT), tm);
137			if (!s) return 0;
138			break;
139		case 'X':
140			s = strptime(s, nl_langinfo(T_FMT), tm);
141			if (!s) return 0;
142			break;
143		case 'y':
144			dest = &tm->tm_year;
145			w = 2;
146			want_century |= 1;
147			goto numeric_digits;
148		case 'Y':
149			dest = &tm->tm_year;
150			if (w<0) w=4;
151			adj = 1900;
152			want_century = 0;
153			goto numeric_digits;
154		case '%':
155			if (*s++ != '%') return 0;
156			break;
157		default:
158			return 0;
159		numeric_range:
160			if (!isdigit(*s)) return 0;
161			*dest = 0;
162			for (i=1; i<=min+range && isdigit(*s); i*=10)
163				*dest = *dest * 10 + *s++ - '0';
164			if (*dest - min >= (unsigned)range) return 0;
165			*dest -= adj;
166			switch((char *)dest - (char *)tm) {
167			case offsetof(struct tm, tm_yday):
168				;
169			}
170			goto update;
171		numeric_digits:
172			neg = 0;
173			if (*s == '+') s++;
174			else if (*s == '-') neg=1, s++;
175			if (!isdigit(*s)) return 0;
176			for (*dest=i=0; i<w && isdigit(*s); i++)
177				*dest = *dest * 10 + *s++ - '0';
178			if (neg) *dest = -*dest;
179			*dest -= adj;
180			goto update;
181		symbolic_range:
182			for (i=2*range-1; i>=0; i--) {
183				ex = nl_langinfo(min+i);
184				len = strlen(ex);
185				if (strncasecmp(s, ex, len)) continue;
186				s += len;
187				*dest = i % range;
188				break;
189			}
190			if (i<0) return 0;
191			goto update;
192		update:
193			//FIXME
194			;
195		}
196	}
197	if (want_century) {
198		if (want_century & 2) tm->tm_year += century * 100 - 1900;
199		else if (tm->tm_year <= 68) tm->tm_year += 100;
200	}
201	return (char *)s;
202}
203