aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2014-01-20 22:07:54 +0000
committerRon Yorston <rmy@pobox.com>2014-01-20 22:07:54 +0000
commit61a0fc571381226c48728b262495c1c5eafc4fed (patch)
tree42989d08f6926b73d6ed03ef92f8f1ee4b5062f9
parent19148b1e7ebb279bfbc1ea8be329f795fa2c1eb0 (diff)
downloadbusybox-w32-61a0fc571381226c48728b262495c1c5eafc4fed.tar.gz
busybox-w32-61a0fc571381226c48728b262495c1c5eafc4fed.tar.bz2
busybox-w32-61a0fc571381226c48728b262495c1c5eafc4fed.zip
win32: use strptime from gnulib
-rw-r--r--win32/Kbuild1
-rw-r--r--win32/mingw.c5
-rw-r--r--win32/strptime.c648
3 files changed, 649 insertions, 5 deletions
diff --git a/win32/Kbuild b/win32/Kbuild
index 564498c78..c1005bd11 100644
--- a/win32/Kbuild
+++ b/win32/Kbuild
@@ -15,6 +15,7 @@ lib-$(CONFIG_PLATFORM_MINGW32) += poll.o
15lib-$(CONFIG_PLATFORM_MINGW32) += popen.o 15lib-$(CONFIG_PLATFORM_MINGW32) += popen.o
16lib-$(CONFIG_PLATFORM_MINGW32) += statfs.o 16lib-$(CONFIG_PLATFORM_MINGW32) += statfs.o
17lib-$(CONFIG_PLATFORM_MINGW32) += mntent.o 17lib-$(CONFIG_PLATFORM_MINGW32) += mntent.o
18lib-$(CONFIG_PLATFORM_MINGW32) += strptime.o
18lib-$(CONFIG_PLATFORM_MINGW32) += system.o 19lib-$(CONFIG_PLATFORM_MINGW32) += system.o
19lib-$(CONFIG_PLATFORM_MINGW32) += termios.o 20lib-$(CONFIG_PLATFORM_MINGW32) += termios.o
20lib-$(CONFIG_PLATFORM_MINGW32) += uname.o 21lib-$(CONFIG_PLATFORM_MINGW32) += uname.o
diff --git a/win32/mingw.c b/win32/mingw.c
index b5b3576eb..810cce604 100644
--- a/win32/mingw.c
+++ b/win32/mingw.c
@@ -799,11 +799,6 @@ int mingw_unlink(const char *pathname)
799 return unlink(pathname); 799 return unlink(pathname);
800} 800}
801 801
802char *strptime(const char *s UNUSED_PARAM, const char *format UNUSED_PARAM, struct tm *tm UNUSED_PARAM)
803{
804 return NULL;
805}
806
807#undef strftime 802#undef strftime
808size_t mingw_strftime(const char *buf, size_t max, const char *format, const struct tm *tm) 803size_t mingw_strftime(const char *buf, size_t max, const char *format, const struct tm *tm)
809{ 804{
diff --git a/win32/strptime.c b/win32/strptime.c
new file mode 100644
index 000000000..64fd37779
--- /dev/null
+++ b/win32/strptime.c
@@ -0,0 +1,648 @@
1/* Copyright (C) 2002, 2004-2005, 2007, 2009-2014 Free Software Foundation,
2 Inc.
3 This file is part of the GNU C Library.
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2, or (at your option)
8 any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, see <http://www.gnu.org/licenses/>. */
17
18/*
19 * File from gnulib (http://www.gnu.org/software/gnulib/), processed with
20 * coan source -U_LIBC -U_NL_CURRENT -UHAVE_TM_GMTOFF strptime.c
21 * and lightly edited.
22 */
23
24#include <time.h>
25
26#include <assert.h>
27#include <ctype.h>
28#include <limits.h>
29#include <string.h>
30#include <stdbool.h>
31
32
33enum ptime_locale_status { not, loc, raw };
34
35
36
37#define match_char(ch1, ch2) if (ch1 != ch2) return NULL
38/* Oh come on. Get a reasonable compiler. */
39# define match_string(cs1, s2) \
40 (strncasecmp ((cs1), (s2), strlen (cs1)) ? 0 : ((s2) += strlen (cs1), 1))
41/* We intentionally do not use isdigit() for testing because this will
42 lead to problems with the wide character version. */
43#define get_number(from, to, n) \
44 do { \
45 int __n = n; \
46 val = 0; \
47 while (*rp == ' ') \
48 ++rp; \
49 if (*rp < '0' || *rp > '9') \
50 return NULL; \
51 do { \
52 val *= 10; \
53 val += *rp++ - '0'; \
54 } while (--__n > 0 && val * 10 <= to && *rp >= '0' && *rp <= '9'); \
55 if (val < from || val > to) \
56 return NULL; \
57 } while (0)
58# define get_alt_number(from, to, n) \
59 /* We don't have the alternate representation. */ \
60 get_number(from, to, n)
61#define recursive(new_fmt) \
62 (*(new_fmt) != '\0' \
63 && (rp = __strptime_internal (rp, (new_fmt), tm, \
64 decided, era_cnt LOCALE_ARG)) != NULL)
65
66
67static char const weekday_name[][10] =
68 {
69 "Sunday", "Monday", "Tuesday", "Wednesday",
70 "Thursday", "Friday", "Saturday"
71 };
72static char const ab_weekday_name[][4] =
73 {
74 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
75 };
76static char const month_name[][10] =
77 {
78 "January", "February", "March", "April", "May", "June",
79 "July", "August", "September", "October", "November", "December"
80 };
81static char const ab_month_name[][4] =
82 {
83 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
84 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
85 };
86# define HERE_D_T_FMT "%a %b %e %H:%M:%S %Y"
87# define HERE_D_FMT "%m/%d/%y"
88# define HERE_AM_STR "AM"
89# define HERE_PM_STR "PM"
90# define HERE_T_FMT_AMPM "%I:%M:%S %p"
91# define HERE_T_FMT "%H:%M:%S"
92
93static const unsigned short int __mon_yday[2][13] =
94 {
95 /* Normal years. */
96 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
97 /* Leap years. */
98 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
99 };
100
101# define LOCALE_PARAM
102# define LOCALE_ARG
103# define LOCALE_PARAM_DECL
104# define LOCALE_PARAM_PROTO
105# define HELPER_LOCALE_ARG
106# define ISSPACE(Ch) isspace (Ch)
107
108
109
110
111#ifndef __isleap
112/* Nonzero if YEAR is a leap year (every 4 years,
113 except every 100th isn't, and every 400th is). */
114# define __isleap(year) \
115 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
116#endif
117
118/* Compute the day of the week. */
119static void
120day_of_the_week (struct tm *tm)
121{
122 /* We know that January 1st 1970 was a Thursday (= 4). Compute the
123 difference between this data in the one on TM and so determine
124 the weekday. */
125 int corr_year = 1900 + tm->tm_year - (tm->tm_mon < 2);
126 int wday = (-473
127 + (365 * (tm->tm_year - 70))
128 + (corr_year / 4)
129 - ((corr_year / 4) / 25) + ((corr_year / 4) % 25 < 0)
130 + (((corr_year / 4) / 25) / 4)
131 + __mon_yday[0][tm->tm_mon]
132 + tm->tm_mday - 1);
133 tm->tm_wday = ((wday % 7) + 7) % 7;
134}
135
136/* Compute the day of the year. */
137static void
138day_of_the_year (struct tm *tm)
139{
140 tm->tm_yday = (__mon_yday[__isleap (1900 + tm->tm_year)][tm->tm_mon]
141 + (tm->tm_mday - 1));
142}
143
144
145static char *
146__strptime_internal (rp, fmt, tm, decided, era_cnt LOCALE_PARAM)
147 const char *rp;
148 const char *fmt;
149 struct tm *tm;
150 enum ptime_locale_status *decided;
151 int era_cnt;
152 LOCALE_PARAM_DECL
153{
154
155 int cnt;
156 size_t val;
157 int have_I, is_pm;
158 int century, want_century;
159 int want_era;
160 int have_wday, want_xday;
161 int have_yday;
162 int have_mon, have_mday;
163 int have_uweek, have_wweek;
164 int week_no;
165
166 have_I = is_pm = 0;
167 century = -1;
168 want_century = 0;
169 want_era = 0;
170 week_no = 0;
171
172 have_wday = want_xday = have_yday = have_mon = have_mday = have_uweek = 0;
173 have_wweek = 0;
174
175 while (*fmt != '\0')
176 {
177 /* A white space in the format string matches 0 more or white
178 space in the input string. */
179 if (ISSPACE (*fmt))
180 {
181 while (ISSPACE (*rp))
182 ++rp;
183 ++fmt;
184 continue;
185 }
186
187 /* Any character but '%' must be matched by the same character
188 in the iput string. */
189 if (*fmt != '%')
190 {
191 match_char (*fmt++, *rp++);
192 continue;
193 }
194
195 ++fmt;
196 /* We need this for handling the 'E' modifier. */
197 start_over:
198
199 switch (*fmt++)
200 {
201 case '%':
202 /* Match the '%' character itself. */
203 match_char ('%', *rp++);
204 break;
205 case 'a':
206 case 'A':
207 /* Match day of week. */
208 for (cnt = 0; cnt < 7; ++cnt)
209 {
210 if (*decided != loc
211 && (match_string (weekday_name[cnt], rp)
212 || match_string (ab_weekday_name[cnt], rp)))
213 {
214 *decided = raw;
215 break;
216 }
217 }
218 if (cnt == 7)
219 /* Does not match a weekday name. */
220 return NULL;
221 tm->tm_wday = cnt;
222 have_wday = 1;
223 break;
224 case 'b':
225 case 'B':
226 case 'h':
227 /* Match month name. */
228 for (cnt = 0; cnt < 12; ++cnt)
229 {
230 if (match_string (month_name[cnt], rp)
231 || match_string (ab_month_name[cnt], rp))
232 {
233 *decided = raw;
234 break;
235 }
236 }
237 if (cnt == 12)
238 /* Does not match a month name. */
239 return NULL;
240 tm->tm_mon = cnt;
241 want_xday = 1;
242 break;
243 case 'c':
244 /* Match locale's date and time format. */
245 if (!recursive (HERE_D_T_FMT))
246 return NULL;
247 want_xday = 1;
248 break;
249 case 'C':
250 /* Match century number. */
251 get_number (0, 99, 2);
252 century = val;
253 want_xday = 1;
254 break;
255 case 'd':
256 case 'e':
257 /* Match day of month. */
258 get_number (1, 31, 2);
259 tm->tm_mday = val;
260 have_mday = 1;
261 want_xday = 1;
262 break;
263 case 'F':
264 if (!recursive ("%Y-%m-%d"))
265 return NULL;
266 want_xday = 1;
267 break;
268 case 'x':
269 /* Fall through. */
270 case 'D':
271 /* Match standard day format. */
272 if (!recursive (HERE_D_FMT))
273 return NULL;
274 want_xday = 1;
275 break;
276 case 'k':
277 case 'H':
278 /* Match hour in 24-hour clock. */
279 get_number (0, 23, 2);
280 tm->tm_hour = val;
281 have_I = 0;
282 break;
283 case 'l':
284 /* Match hour in 12-hour clock. GNU extension. */
285 case 'I':
286 /* Match hour in 12-hour clock. */
287 get_number (1, 12, 2);
288 tm->tm_hour = val % 12;
289 have_I = 1;
290 break;
291 case 'j':
292 /* Match day number of year. */
293 get_number (1, 366, 3);
294 tm->tm_yday = val - 1;
295 have_yday = 1;
296 break;
297 case 'm':
298 /* Match number of month. */
299 get_number (1, 12, 2);
300 tm->tm_mon = val - 1;
301 have_mon = 1;
302 want_xday = 1;
303 break;
304 case 'M':
305 /* Match minute. */
306 get_number (0, 59, 2);
307 tm->tm_min = val;
308 break;
309 case 'n':
310 case 't':
311 /* Match any white space. */
312 while (ISSPACE (*rp))
313 ++rp;
314 break;
315 case 'p':
316 /* Match locale's equivalent of AM/PM. */
317 if (!match_string (HERE_AM_STR, rp))
318 {
319 if (match_string (HERE_PM_STR, rp))
320 is_pm = 1;
321 else
322 return NULL;
323 }
324 break;
325 case 'r':
326 if (!recursive (HERE_T_FMT_AMPM))
327 return NULL;
328 break;
329 case 'R':
330 if (!recursive ("%H:%M"))
331 return NULL;
332 break;
333 case 's':
334 {
335 /* The number of seconds may be very high so we cannot use
336 the 'get_number' macro. Instead read the number
337 character for character and construct the result while
338 doing this. */
339 time_t secs = 0;
340 struct tm *mytm;
341 if (*rp < '0' || *rp > '9')
342 /* We need at least one digit. */
343 return NULL;
344
345 do
346 {
347 secs *= 10;
348 secs += *rp++ - '0';
349 }
350 while (*rp >= '0' && *rp <= '9');
351
352 if ((mytm=localtime(&secs)) == NULL)
353 /* Error in function. */
354 return NULL;
355 *tm = *mytm;
356 }
357 break;
358 case 'S':
359 get_number (0, 61, 2);
360 tm->tm_sec = val;
361 break;
362 case 'X':
363 /* Fall through. */
364 case 'T':
365 if (!recursive (HERE_T_FMT))
366 return NULL;
367 break;
368 case 'u':
369 get_number (1, 7, 1);
370 tm->tm_wday = val % 7;
371 have_wday = 1;
372 break;
373 case 'g':
374 get_number (0, 99, 2);
375 /* XXX This cannot determine any field in TM. */
376 break;
377 case 'G':
378 if (*rp < '0' || *rp > '9')
379 return NULL;
380 /* XXX Ignore the number since we would need some more
381 information to compute a real date. */
382 do
383 ++rp;
384 while (*rp >= '0' && *rp <= '9');
385 break;
386 case 'U':
387 get_number (0, 53, 2);
388 week_no = val;
389 have_uweek = 1;
390 break;
391 case 'W':
392 get_number (0, 53, 2);
393 week_no = val;
394 have_wweek = 1;
395 break;
396 case 'V':
397 get_number (0, 53, 2);
398 /* XXX This cannot determine any field in TM without some
399 information. */
400 break;
401 case 'w':
402 /* Match number of weekday. */
403 get_number (0, 6, 1);
404 tm->tm_wday = val;
405 have_wday = 1;
406 break;
407 case 'y':
408 /* Match year within century. */
409 get_number (0, 99, 2);
410 /* The "Year 2000: The Millennium Rollover" paper suggests that
411 values in the range 69-99 refer to the twentieth century. */
412 tm->tm_year = val >= 69 ? val : val + 100;
413 /* Indicate that we want to use the century, if specified. */
414 want_century = 1;
415 want_xday = 1;
416 break;
417 case 'Y':
418 /* Match year including century number. */
419 get_number (0, 9999, 4);
420 tm->tm_year = val - 1900;
421 want_century = 0;
422 want_xday = 1;
423 break;
424 case 'Z':
425 /* XXX How to handle this? */
426 break;
427 case 'z':
428 /* We recognize two formats: if two digits are given, these
429 specify hours. If fours digits are used, minutes are
430 also specified. */
431 {
432 bool neg;
433 int n;
434
435 val = 0;
436 while (*rp == ' ')
437 ++rp;
438 if (*rp != '+' && *rp != '-')
439 return NULL;
440 neg = *rp++ == '-';
441 n = 0;
442 while (n < 4 && *rp >= '0' && *rp <= '9')
443 {
444 val = val * 10 + *rp++ - '0';
445 ++n;
446 }
447 if (n == 2)
448 val *= 100;
449 else if (n != 4)
450 /* Only two or four digits recognized. */
451 return NULL;
452 else
453 {
454 /* We have to convert the minutes into decimal. */
455 if (val % 100 >= 60)
456 return NULL;
457 val = (val / 100) * 100 + ((val % 100) * 50) / 30;
458 }
459 if (val > 1200)
460 return NULL;
461 }
462 break;
463 case 'E':
464 /* We have no information about the era format. Just use
465 the normal format. */
466 if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y'
467 && *fmt != 'x' && *fmt != 'X')
468 /* This is an illegal format. */
469 return NULL;
470
471 goto start_over;
472 case 'O':
473 switch (*fmt++)
474 {
475 case 'd':
476 case 'e':
477 /* Match day of month using alternate numeric symbols. */
478 get_alt_number (1, 31, 2);
479 tm->tm_mday = val;
480 have_mday = 1;
481 want_xday = 1;
482 break;
483 case 'H':
484 /* Match hour in 24-hour clock using alternate numeric
485 symbols. */
486 get_alt_number (0, 23, 2);
487 tm->tm_hour = val;
488 have_I = 0;
489 break;
490 case 'I':
491 /* Match hour in 12-hour clock using alternate numeric
492 symbols. */
493 get_alt_number (1, 12, 2);
494 tm->tm_hour = val % 12;
495 have_I = 1;
496 break;
497 case 'm':
498 /* Match month using alternate numeric symbols. */
499 get_alt_number (1, 12, 2);
500 tm->tm_mon = val - 1;
501 have_mon = 1;
502 want_xday = 1;
503 break;
504 case 'M':
505 /* Match minutes using alternate numeric symbols. */
506 get_alt_number (0, 59, 2);
507 tm->tm_min = val;
508 break;
509 case 'S':
510 /* Match seconds using alternate numeric symbols. */
511 get_alt_number (0, 61, 2);
512 tm->tm_sec = val;
513 break;
514 case 'U':
515 get_alt_number (0, 53, 2);
516 week_no = val;
517 have_uweek = 1;
518 break;
519 case 'W':
520 get_alt_number (0, 53, 2);
521 week_no = val;
522 have_wweek = 1;
523 break;
524 case 'V':
525 get_alt_number (0, 53, 2);
526 /* XXX This cannot determine any field in TM without
527 further information. */
528 break;
529 case 'w':
530 /* Match number of weekday using alternate numeric symbols. */
531 get_alt_number (0, 6, 1);
532 tm->tm_wday = val;
533 have_wday = 1;
534 break;
535 case 'y':
536 /* Match year within century using alternate numeric symbols. */
537 get_alt_number (0, 99, 2);
538 tm->tm_year = val >= 69 ? val : val + 100;
539 want_xday = 1;
540 break;
541 default:
542 return NULL;
543 }
544 break;
545 default:
546 return NULL;
547 }
548 }
549
550 if (have_I && is_pm)
551 tm->tm_hour += 12;
552
553 if (century != -1)
554 {
555 if (want_century)
556 tm->tm_year = tm->tm_year % 100 + (century - 19) * 100;
557 else
558 /* Only the century, but not the year. Strange, but so be it. */
559 tm->tm_year = (century - 19) * 100;
560 }
561
562 if (era_cnt != -1)
563 {
564 }
565 else
566 if (want_era)
567 {
568 /* No era found but we have seen an E modifier. Rectify some
569 values. */
570 if (want_century && century == -1 && tm->tm_year < 69)
571 tm->tm_year += 100;
572 }
573
574 if (want_xday && !have_wday)
575 {
576 if ( !(have_mon && have_mday) && have_yday)
577 {
578 /* We don't have tm_mon and/or tm_mday, compute them. */
579 int t_mon = 0;
580 while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon] <= tm->tm_yday)
581 t_mon++;
582 if (!have_mon)
583 tm->tm_mon = t_mon - 1;
584 if (!have_mday)
585 tm->tm_mday =
586 (tm->tm_yday
587 - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
588 }
589 day_of_the_week (tm);
590 }
591
592 if (want_xday && !have_yday)
593 day_of_the_year (tm);
594
595 if ((have_uweek || have_wweek) && have_wday)
596 {
597 int save_wday = tm->tm_wday;
598 int save_mday = tm->tm_mday;
599 int save_mon = tm->tm_mon;
600 int w_offset = have_uweek ? 0 : 1;
601
602 tm->tm_mday = 1;
603 tm->tm_mon = 0;
604 day_of_the_week (tm);
605 if (have_mday)
606 tm->tm_mday = save_mday;
607 if (have_mon)
608 tm->tm_mon = save_mon;
609
610 if (!have_yday)
611 tm->tm_yday = ((7 - (tm->tm_wday - w_offset)) % 7
612 + (week_no - 1) *7
613 + save_wday - w_offset);
614
615 if (!have_mday || !have_mon)
616 {
617 int t_mon = 0;
618 while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon]
619 <= tm->tm_yday)
620 t_mon++;
621 if (!have_mon)
622 tm->tm_mon = t_mon - 1;
623 if (!have_mday)
624 tm->tm_mday =
625 (tm->tm_yday
626 - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
627 }
628
629 tm->tm_wday = save_wday;
630 }
631
632 return (char *) rp;
633}
634
635
636char *
637strptime (buf, format, tm LOCALE_PARAM)
638 const char *buf;
639 const char *format;
640 struct tm *tm;
641 LOCALE_PARAM_DECL
642{
643 enum ptime_locale_status decided;
644
645 decided = raw;
646 return __strptime_internal (buf, format, tm, &decided, -1 LOCALE_ARG);
647}
648