]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libc/stdtime/strptime.c
Import lib9p 9d5aee77bcc1bf0e79b0a3bfefff5fdf2146283c.
[FreeBSD/FreeBSD.git] / lib / libc / stdtime / strptime.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2014 Gary Mills
5  * Copyright 2011, Nexenta Systems, Inc.  All rights reserved.
6  * Copyright (c) 1994 Powerdog Industries.  All rights reserved.
7  *
8  * Copyright (c) 2011 The FreeBSD Foundation
9  * All rights reserved.
10  * Portions of this software were developed by David Chisnall
11  * under sponsorship from the FreeBSD Foundation.
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
20  *    in the documentation and/or other materials provided with the
21  *    distribution.
22  *
23  * THIS SOFTWARE IS PROVIDED BY POWERDOG INDUSTRIES ``AS IS'' AND ANY
24  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE POWERDOG INDUSTRIES BE
27  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
30  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
31  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
32  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
33  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34  *
35  * The views and conclusions contained in the software and documentation
36  * are those of the authors and should not be interpreted as representing
37  * official policies, either expressed or implied, of Powerdog Industries.
38  */
39
40 #include <sys/cdefs.h>
41 #ifndef lint
42 #ifndef NOID
43 static char copyright[] __unused =
44 "@(#) Copyright (c) 1994 Powerdog Industries.  All rights reserved.";
45 static char sccsid[] __unused = "@(#)strptime.c 0.1 (Powerdog) 94/03/27";
46 #endif /* !defined NOID */
47 #endif /* not lint */
48 __FBSDID("$FreeBSD$");
49
50 #include "namespace.h"
51 #include <time.h>
52 #include <ctype.h>
53 #include <errno.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #include <pthread.h>
57 #include "un-namespace.h"
58 #include "libc_private.h"
59 #include "timelocal.h"
60 #include "tzfile.h"
61
62 static char * _strptime(const char *, const char *, struct tm *, int *, locale_t);
63
64 #define asizeof(a)      (sizeof(a) / sizeof((a)[0]))
65
66 #define FLAG_NONE       (1 << 0)
67 #define FLAG_YEAR       (1 << 1)
68 #define FLAG_MONTH      (1 << 2)
69 #define FLAG_YDAY       (1 << 3)
70 #define FLAG_MDAY       (1 << 4)
71 #define FLAG_WDAY       (1 << 5)
72
73 /*
74  * Calculate the week day of the first day of a year. Valid for
75  * the Gregorian calendar, which began Sept 14, 1752 in the UK
76  * and its colonies. Ref:
77  * http://en.wikipedia.org/wiki/Determination_of_the_day_of_the_week
78  */
79
80 static int
81 first_wday_of(int year)
82 {
83         return (((2 * (3 - (year / 100) % 4)) + (year % 100) +
84                 ((year % 100) / 4) + (isleap(year) ? 6 : 0) + 1) % 7);
85 }
86
87 static char *
88 _strptime(const char *buf, const char *fmt, struct tm *tm, int *GMTp,
89                 locale_t locale)
90 {
91         char    c;
92         const char *ptr;
93         int     day_offset = -1, wday_offset;
94         int week_offset;
95         int     i, len;
96         int flags;
97         int Ealternative, Oalternative;
98         int century, year;
99         const struct lc_time_T *tptr = __get_current_time_locale(locale);
100         static int start_of_month[2][13] = {
101                 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
102                 {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}
103         };
104
105         flags = FLAG_NONE;
106         century = -1;
107         year = -1;
108
109         ptr = fmt;
110         while (*ptr != 0) {
111                 c = *ptr++;
112
113                 if (c != '%') {
114                         if (isspace_l((unsigned char)c, locale))
115                                 while (*buf != 0 && 
116                                        isspace_l((unsigned char)*buf, locale))
117                                         buf++;
118                         else if (c != *buf++)
119                                 return (NULL);
120                         continue;
121                 }
122
123                 Ealternative = 0;
124                 Oalternative = 0;
125 label:
126                 c = *ptr++;
127                 switch (c) {
128                 case '%':
129                         if (*buf++ != '%')
130                                 return (NULL);
131                         break;
132
133                 case '+':
134                         buf = _strptime(buf, tptr->date_fmt, tm, GMTp, locale);
135                         if (buf == NULL)
136                                 return (NULL);
137                         flags |= FLAG_WDAY | FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;
138                         break;
139
140                 case 'C':
141                         if (!isdigit_l((unsigned char)*buf, locale))
142                                 return (NULL);
143
144                         /* XXX This will break for 3-digit centuries. */
145                         len = 2;
146                         for (i = 0; len && *buf != 0 &&
147                              isdigit_l((unsigned char)*buf, locale); buf++) {
148                                 i *= 10;
149                                 i += *buf - '0';
150                                 len--;
151                         }
152
153                         century = i;
154                         flags |= FLAG_YEAR;
155
156                         break;
157
158                 case 'c':
159                         buf = _strptime(buf, tptr->c_fmt, tm, GMTp, locale);
160                         if (buf == NULL)
161                                 return (NULL);
162                         flags |= FLAG_WDAY | FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;
163                         break;
164
165                 case 'D':
166                         buf = _strptime(buf, "%m/%d/%y", tm, GMTp, locale);
167                         if (buf == NULL)
168                                 return (NULL);
169                         flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;
170                         break;
171
172                 case 'E':
173                         if (Ealternative || Oalternative)
174                                 break;
175                         Ealternative++;
176                         goto label;
177
178                 case 'O':
179                         if (Ealternative || Oalternative)
180                                 break;
181                         Oalternative++;
182                         goto label;
183
184                 case 'F':
185                         buf = _strptime(buf, "%Y-%m-%d", tm, GMTp, locale);
186                         if (buf == NULL)
187                                 return (NULL);
188                         flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;
189                         break;
190
191                 case 'R':
192                         buf = _strptime(buf, "%H:%M", tm, GMTp, locale);
193                         if (buf == NULL)
194                                 return (NULL);
195                         break;
196
197                 case 'r':
198                         buf = _strptime(buf, tptr->ampm_fmt, tm, GMTp, locale);
199                         if (buf == NULL)
200                                 return (NULL);
201                         break;
202
203                 case 'T':
204                         buf = _strptime(buf, "%H:%M:%S", tm, GMTp, locale);
205                         if (buf == NULL)
206                                 return (NULL);
207                         break;
208
209                 case 'X':
210                         buf = _strptime(buf, tptr->X_fmt, tm, GMTp, locale);
211                         if (buf == NULL)
212                                 return (NULL);
213                         break;
214
215                 case 'x':
216                         buf = _strptime(buf, tptr->x_fmt, tm, GMTp, locale);
217                         if (buf == NULL)
218                                 return (NULL);
219                         flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;
220                         break;
221
222                 case 'j':
223                         if (!isdigit_l((unsigned char)*buf, locale))
224                                 return (NULL);
225
226                         len = 3;
227                         for (i = 0; len && *buf != 0 &&
228                              isdigit_l((unsigned char)*buf, locale); buf++){
229                                 i *= 10;
230                                 i += *buf - '0';
231                                 len--;
232                         }
233                         if (i < 1 || i > 366)
234                                 return (NULL);
235
236                         tm->tm_yday = i - 1;
237                         flags |= FLAG_YDAY;
238
239                         break;
240
241                 case 'M':
242                 case 'S':
243                         if (*buf == 0 ||
244                                 isspace_l((unsigned char)*buf, locale))
245                                 break;
246
247                         if (!isdigit_l((unsigned char)*buf, locale))
248                                 return (NULL);
249
250                         len = 2;
251                         for (i = 0; len && *buf != 0 &&
252                                 isdigit_l((unsigned char)*buf, locale); buf++){
253                                 i *= 10;
254                                 i += *buf - '0';
255                                 len--;
256                         }
257
258                         if (c == 'M') {
259                                 if (i > 59)
260                                         return (NULL);
261                                 tm->tm_min = i;
262                         } else {
263                                 if (i > 60)
264                                         return (NULL);
265                                 tm->tm_sec = i;
266                         }
267
268                         break;
269
270                 case 'H':
271                 case 'I':
272                 case 'k':
273                 case 'l':
274                         /*
275                          * %k and %l specifiers are documented as being
276                          * blank-padded.  However, there is no harm in
277                          * allowing zero-padding.
278                          *
279                          * XXX %k and %l specifiers may gobble one too many
280                          * digits if used incorrectly.
281                          */
282
283                         len = 2;
284                         if ((c == 'k' || c == 'l') &&
285                             isblank_l((unsigned char)*buf, locale)) {
286                                 buf++;
287                                 len = 1;
288                         }
289
290                         if (!isdigit_l((unsigned char)*buf, locale))
291                                 return (NULL);
292
293                         for (i = 0; len && *buf != 0 &&
294                              isdigit_l((unsigned char)*buf, locale); buf++) {
295                                 i *= 10;
296                                 i += *buf - '0';
297                                 len--;
298                         }
299                         if (c == 'H' || c == 'k') {
300                                 if (i > 23)
301                                         return (NULL);
302                         } else if (i == 0 || i > 12)
303                                 return (NULL);
304
305                         tm->tm_hour = i;
306
307                         break;
308
309                 case 'p':
310                         /*
311                          * XXX This is bogus if parsed before hour-related
312                          * specifiers.
313                          */
314                         if (tm->tm_hour > 12)
315                                 return (NULL);
316
317                         len = strlen(tptr->am);
318                         if (strncasecmp_l(buf, tptr->am, len, locale) == 0) {
319                                 if (tm->tm_hour == 12)
320                                         tm->tm_hour = 0;
321                                 buf += len;
322                                 break;
323                         }
324
325                         len = strlen(tptr->pm);
326                         if (strncasecmp_l(buf, tptr->pm, len, locale) == 0) {
327                                 if (tm->tm_hour != 12)
328                                         tm->tm_hour += 12;
329                                 buf += len;
330                                 break;
331                         }
332
333                         return (NULL);
334
335                 case 'A':
336                 case 'a':
337                         for (i = 0; i < asizeof(tptr->weekday); i++) {
338                                 len = strlen(tptr->weekday[i]);
339                                 if (strncasecmp_l(buf, tptr->weekday[i],
340                                                 len, locale) == 0)
341                                         break;
342                                 len = strlen(tptr->wday[i]);
343                                 if (strncasecmp_l(buf, tptr->wday[i],
344                                                 len, locale) == 0)
345                                         break;
346                         }
347                         if (i == asizeof(tptr->weekday))
348                                 return (NULL);
349
350                         buf += len;
351                         tm->tm_wday = i;
352                         flags |= FLAG_WDAY;
353                         break;
354
355                 case 'U':
356                 case 'W':
357                         /*
358                          * XXX This is bogus, as we can not assume any valid
359                          * information present in the tm structure at this
360                          * point to calculate a real value, so just check the
361                          * range for now.
362                          */
363                         if (!isdigit_l((unsigned char)*buf, locale))
364                                 return (NULL);
365
366                         len = 2;
367                         for (i = 0; len && *buf != 0 &&
368                              isdigit_l((unsigned char)*buf, locale); buf++) {
369                                 i *= 10;
370                                 i += *buf - '0';
371                                 len--;
372                         }
373                         if (i > 53)
374                                 return (NULL);
375
376                         if (c == 'U')
377                                 day_offset = TM_SUNDAY;
378                         else
379                                 day_offset = TM_MONDAY;
380
381
382                         week_offset = i;
383
384                         break;
385
386                 case 'u':
387                 case 'w':
388                         if (!isdigit_l((unsigned char)*buf, locale))
389                                 return (NULL);
390
391                         i = *buf++ - '0';
392                         if (i < 0 || i > 7 || (c == 'u' && i < 1) ||
393                             (c == 'w' && i > 6))
394                                 return (NULL);
395
396                         tm->tm_wday = i % 7;
397                         flags |= FLAG_WDAY;
398
399                         break;
400
401                 case 'e':
402                         /*
403                          * With %e format, our strftime(3) adds a blank space
404                          * before single digits.
405                          */
406                         if (*buf != 0 &&
407                             isspace_l((unsigned char)*buf, locale))
408                                buf++;
409                         /* FALLTHROUGH */
410                 case 'd':
411                         /*
412                          * The %e specifier was once explicitly documented as
413                          * not being zero-padded but was later changed to
414                          * equivalent to %d.  There is no harm in allowing
415                          * such padding.
416                          *
417                          * XXX The %e specifier may gobble one too many
418                          * digits if used incorrectly.
419                          */
420                         if (!isdigit_l((unsigned char)*buf, locale))
421                                 return (NULL);
422
423                         len = 2;
424                         for (i = 0; len && *buf != 0 &&
425                              isdigit_l((unsigned char)*buf, locale); buf++) {
426                                 i *= 10;
427                                 i += *buf - '0';
428                                 len--;
429                         }
430                         if (i == 0 || i > 31)
431                                 return (NULL);
432
433                         tm->tm_mday = i;
434                         flags |= FLAG_MDAY;
435
436                         break;
437
438                 case 'B':
439                 case 'b':
440                 case 'h':
441                         for (i = 0; i < asizeof(tptr->month); i++) {
442                                 if (Oalternative) {
443                                         if (c == 'B') {
444                                                 len = strlen(tptr->alt_month[i]);
445                                                 if (strncasecmp_l(buf,
446                                                                 tptr->alt_month[i],
447                                                                 len, locale) == 0)
448                                                         break;
449                                         }
450                                 } else {
451                                         len = strlen(tptr->month[i]);
452                                         if (strncasecmp_l(buf, tptr->month[i],
453                                                         len, locale) == 0)
454                                                 break;
455                                 }
456                         }
457                         /*
458                          * Try the abbreviated month name if the full name
459                          * wasn't found and Oalternative was not requested.
460                          */
461                         if (i == asizeof(tptr->month) && !Oalternative) {
462                                 for (i = 0; i < asizeof(tptr->month); i++) {
463                                         len = strlen(tptr->mon[i]);
464                                         if (strncasecmp_l(buf, tptr->mon[i],
465                                                         len, locale) == 0)
466                                                 break;
467                                 }
468                         }
469                         if (i == asizeof(tptr->month))
470                                 return (NULL);
471
472                         tm->tm_mon = i;
473                         buf += len;
474                         flags |= FLAG_MONTH;
475
476                         break;
477
478                 case 'm':
479                         if (!isdigit_l((unsigned char)*buf, locale))
480                                 return (NULL);
481
482                         len = 2;
483                         for (i = 0; len && *buf != 0 &&
484                              isdigit_l((unsigned char)*buf, locale); buf++) {
485                                 i *= 10;
486                                 i += *buf - '0';
487                                 len--;
488                         }
489                         if (i < 1 || i > 12)
490                                 return (NULL);
491
492                         tm->tm_mon = i - 1;
493                         flags |= FLAG_MONTH;
494
495                         break;
496
497                 case 's':
498                         {
499                         char *cp;
500                         int sverrno;
501                         long n;
502                         time_t t;
503
504                         sverrno = errno;
505                         errno = 0;
506                         n = strtol_l(buf, &cp, 10, locale);
507                         if (errno == ERANGE || (long)(t = n) != n) {
508                                 errno = sverrno;
509                                 return (NULL);
510                         }
511                         errno = sverrno;
512                         buf = cp;
513                         if (gmtime_r(&t, tm) == NULL)
514                                 return (NULL);
515                         *GMTp = 1;
516                         flags |= FLAG_YDAY | FLAG_WDAY | FLAG_MONTH |
517                             FLAG_MDAY | FLAG_YEAR;
518                         }
519                         break;
520
521                 case 'Y':
522                 case 'y':
523                         if (*buf == 0 ||
524                             isspace_l((unsigned char)*buf, locale))
525                                 break;
526
527                         if (!isdigit_l((unsigned char)*buf, locale))
528                                 return (NULL);
529
530                         len = (c == 'Y') ? 4 : 2;
531                         for (i = 0; len && *buf != 0 &&
532                              isdigit_l((unsigned char)*buf, locale); buf++) {
533                                 i *= 10;
534                                 i += *buf - '0';
535                                 len--;
536                         }
537                         if (c == 'Y')
538                                 century = i / 100;
539                         year = i % 100;
540
541                         flags |= FLAG_YEAR;
542
543                         break;
544
545                 case 'Z':
546                         {
547                         const char *cp;
548                         char *zonestr;
549
550                         for (cp = buf; *cp &&
551                              isupper_l((unsigned char)*cp, locale); ++cp) {
552                                 /*empty*/}
553                         if (cp - buf) {
554                                 zonestr = alloca(cp - buf + 1);
555                                 strncpy(zonestr, buf, cp - buf);
556                                 zonestr[cp - buf] = '\0';
557                                 tzset();
558                                 if (0 == strcmp(zonestr, "GMT") ||
559                                     0 == strcmp(zonestr, "UTC")) {
560                                     *GMTp = 1;
561                                 } else if (0 == strcmp(zonestr, tzname[0])) {
562                                     tm->tm_isdst = 0;
563                                 } else if (0 == strcmp(zonestr, tzname[1])) {
564                                     tm->tm_isdst = 1;
565                                 } else {
566                                     return (NULL);
567                                 }
568                                 buf += cp - buf;
569                         }
570                         }
571                         break;
572
573                 case 'z':
574                         {
575                         int sign = 1;
576
577                         if (*buf != '+') {
578                                 if (*buf == '-')
579                                         sign = -1;
580                                 else
581                                         return (NULL);
582                         }
583
584                         buf++;
585                         i = 0;
586                         for (len = 4; len > 0; len--) {
587                                 if (isdigit_l((unsigned char)*buf, locale)) {
588                                         i *= 10;
589                                         i += *buf - '0';
590                                         buf++;
591                                 } else if (len == 2) {
592                                         i *= 100;
593                                         break;
594                                 } else
595                                         return (NULL);
596                         }
597
598                         if (i > 1400 || (sign == -1 && i > 1200) ||
599                             (i % 100) >= 60)
600                                 return (NULL);
601                         tm->tm_hour -= sign * (i / 100);
602                         tm->tm_min  -= sign * (i % 100);
603                         *GMTp = 1;
604                         }
605                         break;
606
607                 case 'n':
608                 case 't':
609                         while (isspace_l((unsigned char)*buf, locale))
610                                 buf++;
611                         break;
612
613                 default:
614                         return (NULL);
615                 }
616         }
617
618         if (century != -1 || year != -1) {
619                 if (year == -1)
620                         year = 0;
621                 if (century == -1) {
622                         if (year < 69)
623                                 year += 100;
624                 } else
625                         year += century * 100 - TM_YEAR_BASE;
626                 tm->tm_year = year;
627         }
628
629         if (!(flags & FLAG_YDAY) && (flags & FLAG_YEAR)) {
630                 if ((flags & (FLAG_MONTH | FLAG_MDAY)) ==
631                     (FLAG_MONTH | FLAG_MDAY)) {
632                         tm->tm_yday = start_of_month[isleap(tm->tm_year +
633                             TM_YEAR_BASE)][tm->tm_mon] + (tm->tm_mday - 1);
634                         flags |= FLAG_YDAY;
635                 } else if (day_offset != -1) {
636                         int tmpwday, tmpyday, fwo;
637
638                         fwo = first_wday_of(tm->tm_year + TM_YEAR_BASE);
639                         /* No incomplete week (week 0). */
640                         if (week_offset == 0 && fwo == day_offset)
641                                 return (NULL);
642
643                         /* Set the date to the first Sunday (or Monday)
644                          * of the specified week of the year.
645                          */
646                         tmpwday = (flags & FLAG_WDAY) ? tm->tm_wday :
647                             day_offset;
648                         tmpyday = (7 - fwo + day_offset) % 7 +
649                             (week_offset - 1) * 7 +
650                             (tmpwday - day_offset + 7) % 7;
651                         /* Impossible yday for incomplete week (week 0). */
652                         if (tmpyday < 0) {
653                                 if (flags & FLAG_WDAY)
654                                         return (NULL);
655                                 tmpyday = 0;
656                         }
657                         tm->tm_yday = tmpyday;
658                         flags |= FLAG_YDAY;
659                 }
660         }
661
662         if ((flags & (FLAG_YEAR | FLAG_YDAY)) == (FLAG_YEAR | FLAG_YDAY)) {
663                 if (!(flags & FLAG_MONTH)) {
664                         i = 0;
665                         while (tm->tm_yday >=
666                             start_of_month[isleap(tm->tm_year +
667                             TM_YEAR_BASE)][i])
668                                 i++;
669                         if (i > 12) {
670                                 i = 1;
671                                 tm->tm_yday -=
672                                     start_of_month[isleap(tm->tm_year +
673                                     TM_YEAR_BASE)][12];
674                                 tm->tm_year++;
675                         }
676                         tm->tm_mon = i - 1;
677                         flags |= FLAG_MONTH;
678                 }
679                 if (!(flags & FLAG_MDAY)) {
680                         tm->tm_mday = tm->tm_yday -
681                             start_of_month[isleap(tm->tm_year + TM_YEAR_BASE)]
682                             [tm->tm_mon] + 1;
683                         flags |= FLAG_MDAY;
684                 }
685                 if (!(flags & FLAG_WDAY)) {
686                         i = 0;
687                         wday_offset = first_wday_of(tm->tm_year);
688                         while (i++ <= tm->tm_yday) {
689                                 if (wday_offset++ >= 6)
690                                         wday_offset = 0;
691                         }
692                         tm->tm_wday = wday_offset;
693                         flags |= FLAG_WDAY;
694                 }
695         }
696
697         return ((char *)buf);
698 }
699
700 char *
701 strptime_l(const char * __restrict buf, const char * __restrict fmt,
702     struct tm * __restrict tm, locale_t loc)
703 {
704         char *ret;
705         int gmt;
706         FIX_LOCALE(loc);
707
708         gmt = 0;
709         ret = _strptime(buf, fmt, tm, &gmt, loc);
710         if (ret && gmt) {
711                 time_t t = timegm(tm);
712
713                 localtime_r(&t, tm);
714         }
715
716         return (ret);
717 }
718
719 char *
720 strptime(const char * __restrict buf, const char * __restrict fmt,
721     struct tm * __restrict tm)
722 {
723         return strptime_l(buf, fmt, tm, __get_locale());
724 }