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

Last change on this file since 32 was 32, checked in by Nishi, on Sep 16, 2024 at 9:42:19 PM

can handle cache headers.

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