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