vfs: check userland buffers before reading them.
[haiku.git] / src / system / libroot / posix / time / strptime.c
blob9e5a96e6f876bd844a5b24abbe92b3e8acdd3d4a
1 /*
2 * Copyright (c) 1994 Powerdog Industries. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer
11 * in the documentation and/or other materials provided with the
12 * distribution.
13 * 3. All advertising materials mentioning features or use of this
14 * software must display the following acknowledgement:
15 * This product includes software developed by Powerdog Industries.
16 * 4. The name of Powerdog Industries may not be used to endorse or
17 * promote products derived from this software without specific prior
18 * written permission.
20 * THIS SOFTWARE IS PROVIDED BY POWERDOG INDUSTRIES ``AS IS'' AND ANY
21 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE POWERDOG INDUSTRIES BE
24 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
27 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
29 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
30 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 #include <time.h>
34 #include <ctype.h>
35 #include <errno.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include "timelocal.h"
40 #include <errno_private.h>
42 static char * _strptime(const char *, const char *, struct tm *, int *);
44 #define asizeof(a) (sizeof (a) / sizeof ((a)[0]))
46 static char *
47 _strptime(const char *buf, const char *fmt, struct tm *tm, int *GMTp)
49 char c;
50 const char *ptr;
51 int i,
52 len;
53 int Ealternative, Oalternative;
54 struct lc_time_t *tptr = __get_current_time_locale();
56 ptr = fmt;
57 while (*ptr != 0) {
58 if (*buf == 0)
59 break;
61 c = *ptr++;
63 if (c != '%') {
64 if (isspace((unsigned char)c))
65 while (*buf != 0 && isspace((unsigned char)*buf))
66 buf++;
67 else if (c != *buf++)
68 return 0;
69 continue;
72 Ealternative = 0;
73 Oalternative = 0;
74 label:
75 c = *ptr++;
76 switch (c) {
77 case 0:
78 case '%':
79 if (*buf++ != '%')
80 return 0;
81 break;
83 case '+':
84 buf = _strptime(buf, tptr->date_fmt, tm, GMTp);
85 if (buf == 0)
86 return 0;
87 break;
89 case 'C':
90 if (!isdigit((unsigned char)*buf))
91 return 0;
93 /* XXX This will break for 3-digit centuries. */
94 len = 2;
95 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
96 i *= 10;
97 i += *buf - '0';
98 len--;
100 if (i < 19)
101 return 0;
103 tm->tm_year = i * 100 - 1900;
104 break;
106 case 'c':
107 buf = _strptime(buf, tptr->c_fmt, tm, GMTp);
108 if (buf == 0)
109 return 0;
110 break;
112 case 'D':
113 buf = _strptime(buf, "%m/%d/%y", tm, GMTp);
114 if (buf == 0)
115 return 0;
116 break;
118 case 'E':
119 if (Ealternative || Oalternative)
120 break;
121 Ealternative++;
122 goto label;
124 case 'O':
125 if (Ealternative || Oalternative)
126 break;
127 Oalternative++;
128 goto label;
130 case 'F':
131 buf = _strptime(buf, "%Y-%m-%d", tm, GMTp);
132 if (buf == 0)
133 return 0;
134 break;
136 case 'R':
137 buf = _strptime(buf, "%H:%M", tm, GMTp);
138 if (buf == 0)
139 return 0;
140 break;
142 case 'r':
143 buf = _strptime(buf, tptr->ampm_fmt, tm, GMTp);
144 if (buf == 0)
145 return 0;
146 break;
148 case 'T':
149 buf = _strptime(buf, "%H:%M:%S", tm, GMTp);
150 if (buf == 0)
151 return 0;
152 break;
154 case 'X':
155 buf = _strptime(buf, tptr->X_fmt, tm, GMTp);
156 if (buf == 0)
157 return 0;
158 break;
160 case 'x':
161 buf = _strptime(buf, tptr->x_fmt, tm, GMTp);
162 if (buf == 0)
163 return 0;
164 break;
166 case 'j':
167 if (!isdigit((unsigned char)*buf))
168 return 0;
170 len = 3;
171 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
172 i *= 10;
173 i += *buf - '0';
174 len--;
176 if (i < 1 || i > 366)
177 return 0;
179 tm->tm_yday = i - 1;
180 break;
182 case 'M':
183 case 'S':
184 if (*buf == 0 || isspace((unsigned char)*buf))
185 break;
187 if (!isdigit((unsigned char)*buf))
188 return 0;
190 len = 2;
191 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
192 i *= 10;
193 i += *buf - '0';
194 len--;
197 if (c == 'M') {
198 if (i > 59)
199 return 0;
200 tm->tm_min = i;
201 } else {
202 if (i > 60)
203 return 0;
204 tm->tm_sec = i;
207 if (*buf != 0 && isspace((unsigned char)*buf))
208 while (*ptr != 0 && !isspace((unsigned char)*ptr))
209 ptr++;
210 break;
212 case 'H':
213 case 'I':
214 case 'k':
215 case 'l':
217 * Of these, %l is the only specifier explicitly
218 * documented as not being zero-padded. However,
219 * there is no harm in allowing zero-padding.
221 * XXX The %l specifier may gobble one too many
222 * digits if used incorrectly.
224 if (!isdigit((unsigned char)*buf))
225 return 0;
227 len = 2;
228 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
229 i *= 10;
230 i += *buf - '0';
231 len--;
233 if (c == 'H' || c == 'k') {
234 if (i > 23)
235 return 0;
236 } else if (i > 12)
237 return 0;
239 tm->tm_hour = i;
241 if (*buf != 0 && isspace((unsigned char)*buf))
242 while (*ptr != 0 && !isspace((unsigned char)*ptr))
243 ptr++;
244 break;
246 case 'p':
248 * XXX This is bogus if parsed before hour-related
249 * specifiers.
251 len = strlen(tptr->am);
252 if (strncasecmp(buf, tptr->am, len) == 0) {
253 if (tm->tm_hour > 12)
254 return 0;
255 if (tm->tm_hour == 12)
256 tm->tm_hour = 0;
257 buf += len;
258 break;
261 len = strlen(tptr->pm);
262 if (strncasecmp(buf, tptr->pm, len) == 0) {
263 if (tm->tm_hour > 12)
264 return 0;
265 if (tm->tm_hour != 12)
266 tm->tm_hour += 12;
267 buf += len;
268 break;
271 return 0;
273 case 'A':
274 case 'a':
275 for (i = 0; i < asizeof(tptr->weekday); i++) {
276 len = strlen(tptr->weekday[i]);
277 if (strncasecmp(buf, tptr->weekday[i],
278 len) == 0)
279 break;
280 len = strlen(tptr->wday[i]);
281 if (strncasecmp(buf, tptr->wday[i],
282 len) == 0)
283 break;
285 if (i == asizeof(tptr->weekday))
286 return 0;
288 tm->tm_wday = i;
289 buf += len;
290 break;
292 case 'U':
293 case 'W':
295 * XXX This is bogus, as we can not assume any valid
296 * information present in the tm structure at this
297 * point to calculate a real value, so just check the
298 * range for now.
300 if (!isdigit((unsigned char)*buf))
301 return 0;
303 len = 2;
304 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
305 i *= 10;
306 i += *buf - '0';
307 len--;
309 if (i > 53)
310 return 0;
312 if (*buf != 0 && isspace((unsigned char)*buf))
313 while (*ptr != 0 && !isspace((unsigned char)*ptr))
314 ptr++;
315 break;
317 case 'w':
318 if (!isdigit((unsigned char)*buf))
319 return 0;
321 i = *buf - '0';
322 if (i > 6)
323 return 0;
325 tm->tm_wday = i;
327 if (*buf != 0 && isspace((unsigned char)*buf))
328 while (*ptr != 0 && !isspace((unsigned char)*ptr))
329 ptr++;
330 break;
332 case 'd':
333 case 'e':
335 * The %e specifier is explicitly documented as not
336 * being zero-padded but there is no harm in allowing
337 * such padding.
339 * XXX The %e specifier may gobble one too many
340 * digits if used incorrectly.
342 if (!isdigit((unsigned char)*buf))
343 return 0;
345 len = 2;
346 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
347 i *= 10;
348 i += *buf - '0';
349 len--;
351 if (i > 31)
352 return 0;
354 tm->tm_mday = i;
356 if (*buf != 0 && isspace((unsigned char)*buf))
357 while (*ptr != 0 && !isspace((unsigned char)*ptr))
358 ptr++;
359 break;
361 case 'B':
362 case 'b':
363 case 'h':
364 for (i = 0; i < asizeof(tptr->month); i++) {
365 if (Oalternative) {
366 if (c == 'B') {
367 len = strlen(tptr->alt_month[i]);
368 if (strncasecmp(buf,
369 tptr->alt_month[i],
370 len) == 0)
371 break;
373 } else {
374 len = strlen(tptr->month[i]);
375 if (strncasecmp(buf, tptr->month[i],
376 len) == 0)
377 break;
378 len = strlen(tptr->mon[i]);
379 if (strncasecmp(buf, tptr->mon[i],
380 len) == 0)
381 break;
384 if (i == asizeof(tptr->month))
385 return 0;
387 tm->tm_mon = i;
388 buf += len;
389 break;
391 case 'm':
392 if (!isdigit((unsigned char)*buf))
393 return 0;
395 len = 2;
396 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
397 i *= 10;
398 i += *buf - '0';
399 len--;
401 if (i < 1 || i > 12)
402 return 0;
404 tm->tm_mon = i - 1;
406 if (*buf != 0 && isspace((unsigned char)*buf))
407 while (*ptr != 0 && !isspace((unsigned char)*ptr))
408 ptr++;
409 break;
411 case 's':
413 char *cp;
414 int sverrno;
415 long n;
416 time_t t;
418 sverrno = errno;
419 __set_errno(0);
420 n = strtol(buf, &cp, 10);
421 if (errno == ERANGE || (long)(t = n) != n) {
422 __set_errno(sverrno);
423 return 0;
425 __set_errno(sverrno);
426 buf = cp;
427 gmtime_r(&t, tm);
428 *GMTp = 1;
430 break;
432 case 'Y':
433 case 'y':
434 if (*buf == 0 || isspace((unsigned char)*buf))
435 break;
437 if (!isdigit((unsigned char)*buf))
438 return 0;
440 len = (c == 'Y') ? 4 : 2;
441 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
442 i *= 10;
443 i += *buf - '0';
444 len--;
446 if (c == 'Y')
447 i -= 1900;
448 if (c == 'y' && i < 69)
449 i += 100;
451 tm->tm_year = i;
453 if (*buf != 0 && isspace((unsigned char)*buf))
454 while (*ptr != 0 && !isspace((unsigned char)*ptr))
455 ptr++;
456 break;
458 case 'Z':
460 const char *cp;
461 char *zonestr;
463 for (cp = buf; *cp && isupper((unsigned char)*cp); ++cp) {/*empty*/}
464 if (cp - buf) {
465 zonestr = alloca(cp - buf + 1);
466 strncpy(zonestr, buf, cp - buf);
467 zonestr[cp - buf] = '\0';
468 tzset();
469 if (0 == strcmp(zonestr, "GMT")) {
470 *GMTp = 1;
471 } else if (0 == strcmp(zonestr, tzname[0])) {
472 tm->tm_isdst = 0;
473 } else if (0 == strcmp(zonestr, tzname[1])) {
474 tm->tm_isdst = 1;
475 } else {
476 return 0;
478 buf += cp - buf;
481 break;
484 return (char *)buf;
488 char *
489 strptime(const char * __restrict buf, const char * __restrict fmt,
490 struct tm * __restrict tm)
492 char *ret;
493 int gmt;
495 gmt = 0;
496 ret = _strptime(buf, fmt, tm, &gmt);
497 if (ret && gmt) {
498 time_t t = timegm(tm);
499 localtime_r(&t, tm);
502 return (ret);