xref: /haiku/src/system/libroot/posix/musl/time/strptime.c (revision 4a55cc230cf7566cadcbb23b1928eefff8aea9a2)
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 
10 char *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