source: Main/trunk/Server/strptime.c@ 118

Last change on this file since 118 was 44, checked in by Nishi, on Sep 18, 2024 at 6:37:09 PM

fix strptime

  • Property svn:keywords set to Id
File size: 24.4 KB
RevLine 
[32]1/* $NetBSD: strptime.c,v 1.62 2017/08/24 01:01:09 ginsbach Exp $ */
2/* http://cvsweb.netbsd.org/bsdweb.cgi/~checkout~/src/lib/libc/time/strptime.c?only_with_tag=HEAD
3 * NetBSD implementation strptime().
4 * Format description: https://netbsd.gw.com/cgi-bin/man-cgi?strptime+3+NetBSD-current
5*/
6/*-
7 * Copyright (c) 1997, 1998, 2005, 2008 The NetBSD Foundation, Inc.
8 * All rights reserved.
9 *
10 * This code was contributed to The NetBSD Foundation by Klaus Klein.
11 * Heavily optimised by David Laight
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
23 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
24 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
25 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
26 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 */
34
35//#include <sys/cdefs.h>
36//__RCSID("$NetBSD: strptime.c,v 1.62 2017/08/24 01:01:09 ginsbach Exp $");
37
[34]38#ifdef __MINGW32__
39
[32]40#include <ctype.h>
41#include <string.h>
42#include <time.h>
43#include <stdint.h>
44
45static const unsigned char *conv_num(const unsigned char *, int *, unsigned int, unsigned int);
46static const unsigned char *find_string(const unsigned char *, int *, const char * const *, const char * const *, int);
47
48/*
49 * We do not implement alternate representations. However, we always
50 * check whether a given modifier is allowed for a certain conversion.
51 */
52#define ALT_E 0x01
53#define ALT_O 0x02
54#define LEGAL_ALT(x) { if (alt_format & ~(x)) return NULL; }
55
56#define TM_YEAR_BASE 1900
57
58#define TM_SUNDAY 0
59#define TM_MONDAY 1
60#define TM_TUESDAY 2
61#define TM_WEDNESDAY 3
62#define TM_THURSDAY 4
63#define TM_FRIDAY 5
64#define TM_SATURDAY 6
65
66#define S_YEAR (1 << 0)
67#define S_MON (1 << 1)
68#define S_YDAY (1 << 2)
69#define S_MDAY (1 << 3)
70#define S_WDAY (1 << 4)
71#define S_HOUR (1 << 5)
72
73#define HAVE_MDAY(s) (s & S_MDAY)
74#define HAVE_MON(s) (s & S_MON)
75#define HAVE_WDAY(s) (s & S_WDAY)
76#define HAVE_YDAY(s) (s & S_YDAY)
77#define HAVE_YEAR(s) (s & S_YEAR)
78#define HAVE_HOUR(s) (s & S_HOUR)
79
80#define SECSPERMIN 60
81#define MINSPERHOUR 60
82#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR)
83#define HOURSPERDAY 24
84
85#define HERE_D_T_FMT "%a %b %e %H:%M:%S %Y"
86#define HERE_D_FMT "%y/%m/%d"
87#define HERE_T_FMT_AMPM "%I:%M:%S %p"
88#define HERE_T_FMT "%H:%M:%S"
89
90#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
91
92/*
93** Since everything in isleap is modulo 400 (or a factor of 400), we know that
94** isleap(y) == isleap(y % 400)
95** and so
96** isleap(a + b) == isleap((a + b) % 400)
97** or
98** isleap(a + b) == isleap(a % 400 + b % 400)
99** This is true even if % means modulo rather than Fortran remainder
100** (which is allowed by C89 but not by C99 or later).
101** We use this to avoid addition overflow problems.
102*/
103
104#define isleap_sum(a, b) isleap((a) % 400 + (b) % 400)
105
106#ifdef _MSC_VER
107#define tzname _tzname
108#define strncasecmp _strnicmp
109#endif
110
111#ifdef TM_ZONE
112static char* utc = "UTC";
113#endif
114/* RFC-822/RFC-2822 */
115static const char* const nast[] = {
116 "EST", "CST", "MST", "PST", "\0\0\0"
117};
118static const char* const nadt[] = {
119 "EDT", "CDT", "MDT", "PDT", "\0\0\0"
120};
121static const char* weekday_name[] =
122{
123 "Sunday", "Monday", "Tuesday", "Wednesday",
124 "Thursday", "Friday", "Saturday"
125};
126static const char* ab_weekday_name[] =
127{
128 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
129};
130static const char* month_name[] =
131{
132 "January", "February", "March", "April", "May", "June",
133 "July", "August", "September", "October", "November", "December"
134};
135static const char* ab_month_name[] =
136{
137 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
138 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
139};
140static const char* am_pm[] = {"AM", "PM"};
141
142
143/*
144 * Table to determine the ordinal date for the start of a month.
145 * Ref: http://en.wikipedia.org/wiki/ISO_week_date
146 */
147static const int start_of_month[2][13] = {
148 /* non-leap year */
149 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
150 /* leap year */
151 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
152};
153
154/*
155 * Calculate the week day of the first day of a year. Valid for
156 * the Gregorian calendar, which began Sept 14, 1752 in the UK
157 * and its colonies. Ref:
158 * http://en.wikipedia.org/wiki/Determination_of_the_day_of_the_week
159 */
160
161static int
162first_wday_of(int yr)
163{
164 return ((2 * (3 - (yr / 100) % 4)) + (yr % 100) + ((yr % 100) / 4) +
165 (isleap(yr) ? 6 : 0) + 1) % 7;
166}
167
168#define delim(p) ((p) == '\0' || isspace((unsigned char)(p)))
169
170static int
171fromzone(const unsigned char **bp, struct tm *tm, int mandatory)
172{
173// timezone_t tz;
174 char buf[512], *p;
175 const unsigned char *rp;
176
177 for (p = buf, rp = *bp; !delim(*rp) && p < &buf[sizeof(buf) - 1]; rp++)
178 *p++ = *rp;
179 *p = '\0';
180
181 if (mandatory)
182 *bp = rp;
183 if (!isalnum((unsigned char)*buf))
184 return 0;
185// tz = tzalloc(buf);
186// if (tz == NULL)
187// return 0;
188
189 *bp = rp;
190 tm->tm_isdst = 0; /* XXX */
191#ifdef TM_GMTOFF
192 tm->TM_GMTOFF = tzgetgmtoff(tz, tm->tm_isdst);
193#endif
194#ifdef TM_ZONE
195 // Can't use tzgetname() here because we are going to free()
196 tm->TM_ZONE = NULL; /* XXX */
197#endif
198// tzfree(tz);
199 return 1;
200}
201
202char* strptime(const char *buf, const char *fmt, struct tm *tm)
203{
204 unsigned char c;
205 const unsigned char *bp, *ep, *zname;
206 int alt_format, i, split_year = 0, neg = 0, state = 0,
207 day_offset = -1, week_offset = 0, offs, mandatory;
208 const char *new_fmt;
209
210 bp = (const unsigned char *)buf;
211
212 while (bp != NULL && (c = *fmt++) != '\0') {
213 /* Clear `alternate' modifier prior to new conversion. */
214 alt_format = 0;
215 i = 0;
216
217 /* Eat up white-space. */
218 if (isspace(c)) {
219 while (isspace(*bp))
220 bp++;
221 continue;
222 }
223
224 if (c != '%')
225 goto literal;
226
227
228again: switch (c = *fmt++) {
229 case '%': /* "%%" is converted to "%". */
230literal:
231 if (c != *bp++)
232 return NULL;
233 LEGAL_ALT(0);
234 continue;
235
236 /*
237 * "Alternative" modifiers. Just set the appropriate flag
238 * and start over again.
239 */
240 case 'E': /* "%E?" alternative conversion modifier. */
241 LEGAL_ALT(0);
242 alt_format |= ALT_E;
243 goto again;
244
245 case 'O': /* "%O?" alternative conversion modifier. */
246 LEGAL_ALT(0);
247 alt_format |= ALT_O;
248 goto again;
249
250 /*
251 * "Complex" conversion rules, implemented through recursion.
252 */
253 case 'c': /* Date and time, using the locale's format. */
254// new_fmt = _TIME_LOCALE(loc)->d_t_fmt;
255 new_fmt = HERE_D_T_FMT;
256 state |= S_WDAY | S_MON | S_MDAY | S_YEAR;
257 goto recurse;
258
259 case 'F': /* The date as "%Y-%m-%d". */
260 new_fmt = "%Y-%m-%d";
261 LEGAL_ALT(0);
262 state |= S_MON | S_MDAY | S_YEAR;
263 goto recurse;
264
265 case 'R': /* The time as "%H:%M". */
266 new_fmt = "%H:%M";
267 LEGAL_ALT(0);
268 goto recurse;
269
270 case 'r': /* The time in 12-hour clock representation. */
271// new_fmt = _TIME_LOCALE(loc)->t_fmt_ampm;
272 new_fmt = HERE_T_FMT_AMPM;
273 LEGAL_ALT(0);
274 goto recurse;
275
276 case 'X': /* The time, using the locale's format. */
277 /* fall through */
278
279 case 'T': /* The time as "%H:%M:%S". */
280 new_fmt = HERE_T_FMT;
281 LEGAL_ALT(0);
282
283recurse:
284 bp = (const unsigned char *)strptime((const char *)bp,
285 new_fmt, tm);
286 LEGAL_ALT(ALT_E);
287 continue;
288
289 case 'x': /* The date, using the locale's format. */
290 /* fall throug */
291
292 case 'D': /* The date as "%y/%m/%d". */
293 {
294 new_fmt = HERE_D_FMT;
295 LEGAL_ALT(0);
296 state |= S_MON | S_MDAY | S_YEAR;
297 const int year = split_year ? tm->tm_year : 0;
298
299 bp = (const unsigned char *)strptime((const char *)bp,
300 new_fmt, tm);
301 LEGAL_ALT(ALT_E);
302 tm->tm_year += year;
303 if (split_year && tm->tm_year % (2000 - TM_YEAR_BASE) <= 68)
304 tm->tm_year -= 2000 - TM_YEAR_BASE;
305 split_year = 1;
306 continue;
307 }
308 /*
309 * "Elementary" conversion rules.
310 */
311 case 'A': /* The day of week, using the locale's form. */
312 case 'a':
313 bp = find_string(bp, &tm->tm_wday, weekday_name, ab_weekday_name, 7);
314 LEGAL_ALT(0);
315 state |= S_WDAY;
316 continue;
317
318 case 'B': /* The month, using the locale's form. */
319 case 'b':
320 case 'h':
321 bp = find_string(bp, &tm->tm_mon, month_name, ab_month_name, 12);
322 LEGAL_ALT(0);
323 state |= S_MON;
324 continue;
325
326 case 'C': /* The century number. */
327 i = 20;
328 bp = conv_num(bp, &i, 0, 99);
329
330 i = i * 100 - TM_YEAR_BASE;
331 if (split_year)
332 i += tm->tm_year % 100;
333 split_year = 1;
334 tm->tm_year = i;
335 LEGAL_ALT(ALT_E);
336 state |= S_YEAR;
337 continue;
338
339 case 'd': /* The day of month. */
340 case 'e':
341 bp = conv_num(bp, &tm->tm_mday, 1, 31);
342 LEGAL_ALT(ALT_O);
343 state |= S_MDAY;
344 continue;
345
346 case 'k': /* The hour (24-hour clock representation). */
347 LEGAL_ALT(0);
348 /* FALLTHROUGH */
349 case 'H':
350 bp = conv_num(bp, &tm->tm_hour, 0, 23);
351 LEGAL_ALT(ALT_O);
352 state |= S_HOUR;
353 continue;
354
355 case 'l': /* The hour (12-hour clock representation). */
356 LEGAL_ALT(0);
357 /* FALLTHROUGH */
358 case 'I':
359 bp = conv_num(bp, &tm->tm_hour, 1, 12);
360 if (tm->tm_hour == 12)
361 tm->tm_hour = 0;
362 LEGAL_ALT(ALT_O);
363 state |= S_HOUR;
364 continue;
365
366 case 'j': /* The day of year. */
367 i = 1;
368 bp = conv_num(bp, &i, 1, 366);
369 tm->tm_yday = i - 1;
370 LEGAL_ALT(0);
371 state |= S_YDAY;
372 continue;
373
374 case 'M': /* The minute. */
375 bp = conv_num(bp, &tm->tm_min, 0, 59);
376 LEGAL_ALT(ALT_O);
377 continue;
378
379 case 'm': /* The month. */
380 i = 1;
381 bp = conv_num(bp, &i, 1, 12);
382 tm->tm_mon = i - 1;
383 LEGAL_ALT(ALT_O);
384 state |= S_MON;
385 continue;
386
387 case 'p': /* The locale's equivalent of AM/PM. */
388 bp = find_string(bp, &i, am_pm, NULL, 2);
389 if (HAVE_HOUR(state) && tm->tm_hour > 11)
390 return NULL;
391 tm->tm_hour += i * 12;
392 LEGAL_ALT(0);
393 continue;
394
395 case 'S': /* The seconds. */
396 bp = conv_num(bp, &tm->tm_sec, 0, 61);
397 LEGAL_ALT(ALT_O);
398 continue;
399
400#ifndef TIME_MAX
401#define TIME_MAX INT64_MAX
402#endif
403 case 's': /* seconds since the epoch */
404 {
405 time_t sse = 0;
406 uint64_t rulim = TIME_MAX;
407
408 if (*bp < '0' || *bp > '9') {
409 bp = NULL;
410 continue;
411 }
412
413 do {
414 sse *= 10;
415 sse += *bp++ - '0';
416 rulim /= 10;
417 } while ((sse * 10 <= TIME_MAX) &&
418 rulim && *bp >= '0' && *bp <= '9');
419
420 if (sse < 0 || (uint64_t)sse > TIME_MAX) {
421 bp = NULL;
422 continue;
423 }
424#ifdef _WIN32
425 if (localtime_s(tm, &sse) == 0)
426#else
427 if (localtime_r(&sse, tm))
428#endif
429 state |= S_YDAY | S_WDAY | S_MON | S_MDAY | S_YEAR;
430 else
431 bp = NULL;
432 }
433 continue;
434
435 case 'U': /* The week of year, beginning on sunday. */
436 case 'W': /* The week of year, beginning on monday. */
437 /*
438 * This is bogus, as we can not assume any valid
439 * information present in the tm structure at this
440 * point to calculate a real value, so save the
441 * week for now in case it can be used later.
442 */
443 bp = conv_num(bp, &i, 0, 53);
444 LEGAL_ALT(ALT_O);
445 if (c == 'U')
446 day_offset = TM_SUNDAY;
447 else
448 day_offset = TM_MONDAY;
449 week_offset = i;
450 continue;
451
452 case 'w': /* The day of week, beginning on sunday. */
453 bp = conv_num(bp, &tm->tm_wday, 0, 6);
454 LEGAL_ALT(ALT_O);
455 state |= S_WDAY;
456 continue;
457
458 case 'u': /* The day of week, monday = 1. */
459 bp = conv_num(bp, &i, 1, 7);
460 tm->tm_wday = i % 7;
461 LEGAL_ALT(ALT_O);
462 state |= S_WDAY;
463 continue;
464
465 case 'g': /* The year corresponding to the ISO week
466 * number but without the century.
467 */
468 bp = conv_num(bp, &i, 0, 99);
469 continue;
470
471 case 'G': /* The year corresponding to the ISO week
472 * number with century.
473 */
474 do
475 bp++;
476 while (isdigit(*bp));
477 continue;
478
479 case 'V': /* The ISO 8601:1988 week number as decimal */
480 bp = conv_num(bp, &i, 0, 53);
481 continue;
482
483 case 'Y': /* The year. */
484 i = TM_YEAR_BASE; /* just for data sanity... */
485 bp = conv_num(bp, &i, 0, 9999);
486 tm->tm_year = i - TM_YEAR_BASE;
487 LEGAL_ALT(ALT_E);
488 state |= S_YEAR;
489 continue;
490
491 case 'y': /* The year within 100 years of the epoch. */
492 /* LEGAL_ALT(ALT_E | ALT_O); */
493 bp = conv_num(bp, &i, 0, 99);
494
495 if (split_year)
496 /* preserve century */
497 i += (tm->tm_year / 100) * 100;
498 else {
499 split_year = 1;
500 if (i <= 68)
501 i = i + 2000 - TM_YEAR_BASE;
502 }
503 tm->tm_year = i;
504 state |= S_YEAR;
505 continue;
506
507 case 'Z': // time zone name
508 case 'z': //
509#ifdef _WIN32
510 _tzset();
511#else
512 tzset();
513#endif
514 mandatory = c == 'z';
515 /*
516 * We recognize all ISO 8601 formats:
517 * Z = Zulu time/UTC
518 * [+-]hhmm
519 * [+-]hh:mm
520 * [+-]hh
521 * We recognize all RFC-822/RFC-2822 formats:
522 * UT|GMT
523 * North American : UTC offsets
524 * E[DS]T = Eastern : -4 | -5
525 * C[DS]T = Central : -5 | -6
526 * M[DS]T = Mountain: -6 | -7
527 * P[DS]T = Pacific : -7 | -8
528 * Nautical/Military
529 * [A-IL-M] = -1 ... -9 (J not used)
530 * [N-Y] = +1 ... +12
531 * Note: J maybe used to denote non-nautical
532 * local time
533 */
534 if (mandatory)
535 while (isspace(*bp))
536 bp++;
537
538 zname = bp;
539 switch (*bp++) {
540 case 'G':
541 if (*bp++ != 'M')
542 goto namedzone;
543 /*FALLTHROUGH*/
544 case 'U':
545 if (*bp++ != 'T')
546 goto namedzone;
547 else if (!delim(*bp) && *bp++ != 'C')
548 goto namedzone;
549 /*FALLTHROUGH*/
550 case 'Z':
551 if (!delim(*bp))
552 goto namedzone;
553 tm->tm_isdst = 0;
554#ifdef TM_GMTOFF
555 tm->TM_GMTOFF = 0;
556#endif
557#ifdef TM_ZONE
558 tm->TM_ZONE = utc;
559#endif
560 continue;
561 case '+':
562 neg = 0;
563 break;
564 case '-':
565 neg = 1;
566 break;
567 default:
568namedzone:
569 bp = zname;
570
571 /* Nautical / Military style */
572 if (delim(bp[1]) &&
573 ((*bp >= 'A' && *bp <= 'I') ||
574 (*bp >= 'L' && *bp <= 'Y'))) {
575#ifdef TM_GMTOFF
576 /* Argh! No 'J'! */
577 if (*bp >= 'A' && *bp <= 'I')
578 tm->TM_GMTOFF =
579 (int)*bp - ('A' - 1);
580 else if (*bp >= 'L' && *bp <= 'M')
581 tm->TM_GMTOFF = (int)*bp - 'A';
582 else if (*bp >= 'N' && *bp <= 'Y')
583 tm->TM_GMTOFF = 'M' - (int)*bp;
584 tm->TM_GMTOFF *= SECSPERHOUR;
585#endif
586#ifdef TM_ZONE
587 tm->TM_ZONE = NULL; /* XXX */
588#endif
589 bp++;
590 continue;
591 }
592 /* 'J' is local time */
593 if (delim(bp[1]) && *bp == 'J') {
594#ifdef TM_GMTOFF
595 tm->TM_GMTOFF = -timezone;
596#endif
597#ifdef TM_ZONE
598 tm->TM_ZONE = NULL; /* XXX */
599#endif
600 bp++;
601 continue;
602 }
603
604 /*
605 * From our 3 letter hard-coded table
606 * XXX: Can be removed, handled by tzload()
607 */
608 if (delim(bp[0]) || delim(bp[1]) ||
609 delim(bp[2]) || !delim(bp[3]))
610 goto loadzone;
611 ep = find_string(bp, &i, nast, NULL, 4);
612 if (ep != NULL) {
613#ifdef TM_GMTOFF
614 tm->TM_GMTOFF = (-5 - i) * SECSPERHOUR;
615#endif
616#ifdef TM_ZONE
617 tm->TM_ZONE = __UNCONST(nast[i]);
618#endif
619 bp = ep;
620 continue;
621 }
622 ep = find_string(bp, &i, nadt, NULL, 4);
623 if (ep != NULL) {
624 tm->tm_isdst = 1;
625#ifdef TM_GMTOFF
626 tm->TM_GMTOFF = (-4 - i) * SECSPERHOUR;
627#endif
628#ifdef TM_ZONE
629 tm->TM_ZONE = __UNCONST(nadt[i]);
630#endif
631 bp = ep;
632 continue;
633 }
634 /*
635 * Our current timezone
636 */
637 ep = find_string(bp, &i,
[44]638 (const char * const *)NULL,
[32]639 NULL, 2);
640 if (ep != NULL) {
641 tm->tm_isdst = i;
642#ifdef TM_GMTOFF
643 tm->TM_GMTOFF = -timezone;
644#endif
645#ifdef TM_ZONE
646 tm->TM_ZONE = tzname[i];
647#endif
648 bp = ep;
649 continue;
650 }
651loadzone:
652 /*
653 * The hard way, load the zone!
654 */
655 if (fromzone(&bp, tm, mandatory))
656 continue;
657 goto out;
658 }
659 offs = 0;
660 for (i = 0; i < 4; ) {
661 if (isdigit(*bp)) {
662 offs = offs * 10 + (*bp++ - '0');
663 i++;
664 continue;
665 }
666 if (i == 2 && *bp == ':') {
667 bp++;
668 continue;
669 }
670 break;
671 }
672 if (isdigit(*bp))
673 goto out;
674 switch (i) {
675 case 2:
676 offs *= SECSPERHOUR;
677 break;
678 case 4:
679 i = offs % 100;
680 offs /= 100;
681 if (i >= SECSPERMIN)
682 goto out;
683 /* Convert minutes into decimal */
684 offs = offs * SECSPERHOUR + i * SECSPERMIN;
685 break;
686 default:
687out:
688 if (mandatory)
689 return NULL;
690 bp = zname;
691 continue;
692 }
693 /* ISO 8601 & RFC 3339 limit to 23:59 max */
694 if (offs >= (HOURSPERDAY * SECSPERHOUR))
695 goto out;
696 if (neg)
697 offs = -offs;
698 tm->tm_isdst = 0; /* XXX */
699#ifdef TM_GMTOFF
700 tm->TM_GMTOFF = offs;
701#endif
702#ifdef TM_ZONE
703 tm->TM_ZONE = NULL; /* XXX */
704#endif
705 continue;
706
707 /*
708 * Miscellaneous conversions.
709 */
710 case 'n': /* Any kind of white-space. */
711 case 't':
712 while (isspace(*bp))
713 bp++;
714 LEGAL_ALT(0);
715 continue;
716
717
718 default: /* Unknown/unsupported conversion. */
719 return NULL;
720 }
721 }
722
723 if (!HAVE_YDAY(state) && HAVE_YEAR(state)) {
724 if (HAVE_MON(state) && HAVE_MDAY(state)) {
725 /* calculate day of year (ordinal date) */
726 tm->tm_yday = start_of_month[isleap_sum(tm->tm_year,
727 TM_YEAR_BASE)][tm->tm_mon] + (tm->tm_mday - 1);
728 state |= S_YDAY;
729 } else if (day_offset != -1) {
730 /*
731 * Set the date to the first Sunday (or Monday)
732 * of the specified week of the year.
733 */
734 if (!HAVE_WDAY(state)) {
735 tm->tm_wday = day_offset;
736 state |= S_WDAY;
737 }
738 tm->tm_yday = (7 -
739 first_wday_of(tm->tm_year + TM_YEAR_BASE) +
740 day_offset) % 7 + (week_offset - 1) * 7 +
741 tm->tm_wday - day_offset;
742 state |= S_YDAY;
743 }
744 }
745
746 if (HAVE_YDAY(state) && HAVE_YEAR(state)) {
747 int isleap;
748
749 if (!HAVE_MON(state)) {
750 /* calculate month of day of year */
751 i = 0;
752 isleap = isleap_sum(tm->tm_year, TM_YEAR_BASE);
753 while (tm->tm_yday >= start_of_month[isleap][i])
754 i++;
755 if (i > 12) {
756 i = 1;
757 tm->tm_yday -= start_of_month[isleap][12];
758 tm->tm_year++;
759 }
760 tm->tm_mon = i - 1;
761 state |= S_MON;
762 }
763
764 if (!HAVE_MDAY(state)) {
765 /* calculate day of month */
766 isleap = isleap_sum(tm->tm_year, TM_YEAR_BASE);
767 tm->tm_mday = tm->tm_yday -
768 start_of_month[isleap][tm->tm_mon] + 1;
769 state |= S_MDAY;
770 }
771
772 if (!HAVE_WDAY(state)) {
773 /* calculate day of week */
774 i = 0;
775 week_offset = first_wday_of(tm->tm_year);
776 while (i++ <= tm->tm_yday) {
777 if (week_offset++ >= 6)
778 week_offset = 0;
779 }
780 tm->tm_wday = week_offset;
781 state |= S_WDAY;
782 }
783 }
784
785 return (char*)bp;
786}
787
788
789static const unsigned char *
790conv_num(const unsigned char *buf, int *dest, unsigned int llim, unsigned int ulim)
791{
792 unsigned int result = 0;
793 unsigned char ch;
794
795 /* The limit also determines the number of valid digits. */
796 unsigned int rulim = ulim;
797
798 ch = *buf;
799 if (ch < '0' || ch > '9')
800 return NULL;
801
802 do {
803 result *= 10;
804 result += ch - '0';
805 rulim /= 10;
806 ch = *++buf;
807 } while ((result <= ulim) && rulim && ch >= '0' && ch <= '9');
808
809 if (result < llim || result > ulim)
810 return NULL;
811
812 *dest = result;
813 return buf;
814}
815
816static const unsigned char *
817find_string(const unsigned char *bp, int *tgt, const char * const *n1,
818 const char * const *n2, int c)
819{
820 int i;
821 size_t len;
822
823 /* check full name - then abbreviated ones */
824 for (; n1 != NULL; n1 = n2, n2 = NULL) {
825 for (i = 0; i < c; i++, n1++) {
826 len = strlen(*n1);
827 if (strncasecmp(*n1, (const char *)bp, len) == 0) {
828 *tgt = i;
829 return bp + len;
830 }
831 }
832 }
833
834 /* Nothing matched */
835 return NULL;
836}
[34]837
838#endif
Note: See TracBrowser for help on using the repository browser.