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