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
strptime(const char * restrict s,const char * restrict f,struct tm * restrict tm)9 char *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, relyear = 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 = ¢ury;
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 'F':
63 /* Use temp buffer to implement the odd requirement
64 * that entire field be width-limited but the year
65 * subfield not itself be limited. */
66 i = 0;
67 char tmp[20];
68 if (*s == '-' || *s == '+') tmp[i++] = *s++;
69 while (*s=='0' && isdigit(s[1])) s++;
70 for (; *s && i<(size_t)w && i+1<sizeof tmp; i++) {
71 tmp[i] = *s++;
72 }
73 tmp[i] = 0;
74 char *p = strptime(tmp, "%12Y-%m-%d", tm);
75 if (!p) return 0;
76 s -= tmp+i-p;
77 break;
78 case 'H':
79 dest = &tm->tm_hour;
80 min = 0;
81 range = 24;
82 goto numeric_range;
83 case 'I':
84 dest = &tm->tm_hour;
85 min = 1;
86 range = 12;
87 goto numeric_range;
88 case 'j':
89 dest = &tm->tm_yday;
90 min = 1;
91 range = 366;
92 adj = 1;
93 goto numeric_range;
94 case 'm':
95 dest = &tm->tm_mon;
96 min = 1;
97 range = 12;
98 adj = 1;
99 goto numeric_range;
100 case 'M':
101 dest = &tm->tm_min;
102 min = 0;
103 range = 60;
104 goto numeric_range;
105 case 'n': case 't':
106 for (; *s && isspace(*s); s++);
107 break;
108 case 'p':
109 ex = nl_langinfo(AM_STR);
110 len = strlen(ex);
111 if (!strncasecmp(s, ex, len)) {
112 tm->tm_hour %= 12;
113 s += len;
114 break;
115 }
116 ex = nl_langinfo(PM_STR);
117 len = strlen(ex);
118 if (!strncasecmp(s, ex, len)) {
119 tm->tm_hour %= 12;
120 tm->tm_hour += 12;
121 s += len;
122 break;
123 }
124 return 0;
125 case 'r':
126 s = strptime(s, nl_langinfo(T_FMT_AMPM), tm);
127 if (!s) return 0;
128 break;
129 case 'R':
130 s = strptime(s, "%H:%M", tm);
131 if (!s) return 0;
132 break;
133 case 's':
134 /* Parse only. Effect on tm is unspecified
135 * and presently no effect is implemented.. */
136 if (*s == '-') s++;
137 if (!isdigit(*s)) return 0;
138 while (isdigit(*s)) s++;
139 break;
140 case 'S':
141 dest = &tm->tm_sec;
142 min = 0;
143 range = 61;
144 goto numeric_range;
145 case 'T':
146 s = strptime(s, "%H:%M:%S", tm);
147 if (!s) return 0;
148 break;
149 case 'U':
150 case 'W':
151 /* Throw away result of %U, %V, %W, %g, and %G. Effect
152 * is unspecified and there is no clear right choice. */
153 dest = &dummy;
154 min = 0;
155 range = 54;
156 goto numeric_range;
157 case 'V':
158 dest = &dummy;
159 min = 1;
160 range = 53;
161 goto numeric_range;
162 case 'g':
163 dest = &dummy;
164 w = 2;
165 goto numeric_digits;
166 case 'G':
167 dest = &dummy;
168 if (w<0) w=4;
169 goto numeric_digits;
170 case 'u':
171 dest = &tm->tm_wday;
172 min = 1;
173 range = 7;
174 goto numeric_range;
175 case 'w':
176 dest = &tm->tm_wday;
177 min = 0;
178 range = 7;
179 goto numeric_range;
180 case 'x':
181 s = strptime(s, nl_langinfo(D_FMT), tm);
182 if (!s) return 0;
183 break;
184 case 'X':
185 s = strptime(s, nl_langinfo(T_FMT), tm);
186 if (!s) return 0;
187 break;
188 case 'y':
189 dest = &relyear;
190 w = 2;
191 want_century |= 1;
192 goto numeric_digits;
193 case 'Y':
194 dest = &tm->tm_year;
195 if (w<0) w=4;
196 adj = 1900;
197 want_century = 0;
198 goto numeric_digits;
199 case 'z':
200 if (*s == '+') neg = 0;
201 else if (*s == '-') neg = 1;
202 else return 0;
203 for (i=0; i<4; i++) if (!isdigit(s[1+i])) return 0;
204 tm->__tm_gmtoff = (s[1]-'0')*36000+(s[2]-'0')*3600
205 + (s[3]-'0')*600 + (s[4]-'0')*60;
206 if (neg) tm->__tm_gmtoff = -tm->__tm_gmtoff;
207 s += 5;
208 break;
209 case 'Z':
210 if (!strncmp(s, tzname[0], len = strlen(tzname[0]))) {
211 tm->tm_isdst = 0;
212 s += len;
213 } else if (!strncmp(s, tzname[1], len=strlen(tzname[1]))) {
214 tm->tm_isdst = 1;
215 s += len;
216 } else {
217 /* FIXME: is this supposed to be an error? */
218 while ((*s|32)-'a' <= 'z'-'a') s++;
219 }
220 break;
221 case '%':
222 if (*s++ != '%') return 0;
223 break;
224 default:
225 return 0;
226 numeric_range:
227 if (!isdigit(*s)) return 0;
228 *dest = 0;
229 for (i=1; i<=min+range && isdigit(*s); i*=10)
230 *dest = *dest * 10 + *s++ - '0';
231 if (*dest - min >= (unsigned)range) return 0;
232 *dest -= adj;
233 switch((char *)dest - (char *)tm) {
234 case offsetof(struct tm, tm_yday):
235 ;
236 }
237 goto update;
238 numeric_digits:
239 neg = 0;
240 if (*s == '+') s++;
241 else if (*s == '-') neg=1, s++;
242 if (!isdigit(*s)) return 0;
243 for (*dest=i=0; i<w && isdigit(*s); i++)
244 *dest = *dest * 10 + *s++ - '0';
245 if (neg) *dest = -*dest;
246 *dest -= adj;
247 goto update;
248 symbolic_range:
249 for (i=2*range-1; i>=0; i--) {
250 ex = nl_langinfo(min+i);
251 len = strlen(ex);
252 if (strncasecmp(s, ex, len)) continue;
253 s += len;
254 *dest = i % range;
255 break;
256 }
257 if (i<0) return 0;
258 goto update;
259 update:
260 //FIXME
261 ;
262 }
263 }
264 if (want_century) {
265 tm->tm_year = relyear;
266 if (want_century & 2) tm->tm_year += century * 100 - 1900;
267 else if (tm->tm_year <= 68) tm->tm_year += 100;
268 }
269 return (char *)s;
270 }
271