locales, libconv: Sync with FreeBSD (extensive reach)
[dragonfly.git] / lib / libc / stdtime / strptime.c
1 /*
2  * Powerdog Industries kindly requests feedback from anyone modifying
3  * this function:
4  *
5  * Date: Thu, 05 Jun 1997 23:17:17 -0400
6  * From: Kevin Ruddy <kevin.ruddy@powerdog.com>
7  * To: James FitzGibbon <james@nexis.net>
8  * Subject: Re: Use of your strptime(3) code (fwd)
9  *
10  * The reason for the "no mod" clause was so that modifications would
11  * come back and we could integrate them and reissue so that a wider
12  * audience could use it (thereby spreading the wealth).  This has
13  * made it possible to get strptime to work on many operating systems.
14  * I'm not sure why that's "plain unacceptable" to the FreeBSD team.
15  *
16  * Anyway, you can change it to "with or without modification" as
17  * you see fit.  Enjoy.
18  *
19  * Kevin Ruddy
20  * Powerdog Industries, Inc.
21  */
22 /*
23  * Copyright (c) 1994 Powerdog Industries.  All rights reserved.
24  *
25  * Copyright (c) 2011 The FreeBSD Foundation
26  * All rights reserved.
27  * Portions of this software were developed by David Chisnall
28  * under sponsorship from the FreeBSD Foundation.
29  *
30  * Redistribution and use in source and binary forms, with or without
31  * modification, are permitted provided that the following conditions
32  * are met:
33  * 1. Redistributions of source code must retain the above copyright
34  *    notice, this list of conditions and the following disclaimer.
35  * 2. Redistributions in binary form must reproduce the above copyright
36  *    notice, this list of conditions and the following disclaimer
37  *    in the documentation and/or other materials provided with the
38  *    distribution.
39  * 3. All advertising materials mentioning features or use of this
40  *    software must display the following acknowledgement:
41  *      This product includes software developed by Powerdog Industries.
42  * 4. The name of Powerdog Industries may not be used to endorse or
43  *    promote products derived from this software without specific prior
44  *    written permission.
45  *
46  * THIS SOFTWARE IS PROVIDED BY POWERDOG INDUSTRIES ``AS IS'' AND ANY
47  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
48  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
49  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE POWERDOG INDUSTRIES BE
50  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
51  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
52  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
53  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
54  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
55  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
56  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
57  *
58  * @(#)strptime.c       0.1 (Powerdog) 94/03/27
59  * @(#) Copyright (c) 1994 Powerdog Industries.  All rights reserved.
60  * $FreeBSD: head/lib/libc/stdtime/strptime.c 227753 2011-11-20 14:45:42Z theraven $
61  */
62
63 #include "namespace.h"
64 #include <time.h>
65 #include <ctype.h>
66 #include <errno.h>
67 #include <stdlib.h>
68 #include <string.h>
69 #include <pthread.h>
70 #include "un-namespace.h"
71 #include "libc_private.h"
72 #include "timelocal.h"
73
74 static char * _strptime(const char *, const char *, struct tm *, int *, locale_t);
75
76 #define asizeof(a)      (sizeof (a) / sizeof ((a)[0]))
77
78 static char *
79 _strptime(const char *buf, const char *fmt, struct tm *tm, int *GMTp,
80                 locale_t locale)
81 {
82         char    c;
83         const char *ptr;
84         int     i,
85                 len;
86         int Ealternative, Oalternative;
87         struct lc_time_T *tptr = __get_current_time_locale(locale);
88
89         ptr = fmt;
90         while (*ptr != 0) {
91                 if (*buf == 0)
92                         break;
93
94                 c = *ptr++;
95
96                 if (c != '%') {
97                         if (isspace_l((unsigned char)c, locale))
98                                 while (*buf != 0 && 
99                                        isspace_l((unsigned char)*buf, locale))
100                                         buf++;
101                         else if (c != *buf++)
102                                 return 0;
103                         continue;
104                 }
105
106                 Ealternative = 0;
107                 Oalternative = 0;
108 label:
109                 c = *ptr++;
110                 switch (c) {
111                 case 0:
112                 case '%':
113                         if (*buf++ != '%')
114                                 return 0;
115                         break;
116
117                 case '+':
118                         buf = _strptime(buf, tptr->date_fmt, tm, GMTp, locale);
119                         if (buf == 0)
120                                 return 0;
121                         break;
122
123                 case 'C':
124                         if (!isdigit_l((unsigned char)*buf, locale))
125                                 return 0;
126
127                         /* XXX This will break for 3-digit centuries. */
128                         len = 2;
129                         for (i = 0; len && *buf != 0 &&
130                              isdigit_l((unsigned char)*buf, locale); buf++) {
131                                 i *= 10;
132                                 i += *buf - '0';
133                                 len--;
134                         }
135                         if (i < 19)
136                                 return 0;
137
138                         tm->tm_year = i * 100 - 1900;
139                         break;
140
141                 case 'c':
142                         buf = _strptime(buf, tptr->c_fmt, tm, GMTp, locale);
143                         if (buf == 0)
144                                 return 0;
145                         break;
146
147                 case 'D':
148                         buf = _strptime(buf, "%m/%d/%y", tm, GMTp, locale);
149                         if (buf == 0)
150                                 return 0;
151                         break;
152
153                 case 'E':
154                         if (Ealternative || Oalternative)
155                                 break;
156                         Ealternative++;
157                         goto label;
158
159                 case 'O':
160                         if (Ealternative || Oalternative)
161                                 break;
162                         Oalternative++;
163                         goto label;
164
165                 case 'F':
166                         buf = _strptime(buf, "%Y-%m-%d", tm, GMTp, locale);
167                         if (buf == 0)
168                                 return 0;
169                         break;
170
171                 case 'R':
172                         buf = _strptime(buf, "%H:%M", tm, GMTp, locale);
173                         if (buf == 0)
174                                 return 0;
175                         break;
176
177                 case 'r':
178                         buf = _strptime(buf, tptr->ampm_fmt, tm, GMTp, locale);
179                         if (buf == 0)
180                                 return 0;
181                         break;
182
183                 case 'T':
184                         buf = _strptime(buf, "%H:%M:%S", tm, GMTp, locale);
185                         if (buf == 0)
186                                 return 0;
187                         break;
188
189                 case 'X':
190                         buf = _strptime(buf, tptr->X_fmt, tm, GMTp, locale);
191                         if (buf == 0)
192                                 return 0;
193                         break;
194
195                 case 'x':
196                         buf = _strptime(buf, tptr->x_fmt, tm, GMTp, locale);
197                         if (buf == 0)
198                                 return 0;
199                         break;
200
201                 case 'j':
202                         if (!isdigit_l((unsigned char)*buf, locale))
203                                 return 0;
204
205                         len = 3;
206                         for (i = 0; len && *buf != 0 &&
207                              isdigit_l((unsigned char)*buf, locale); buf++){
208                                 i *= 10;
209                                 i += *buf - '0';
210                                 len--;
211                         }
212                         if (i < 1 || i > 366)
213                                 return 0;
214
215                         tm->tm_yday = i - 1;
216                         break;
217
218                 case 'M':
219                 case 'S':
220                         if (*buf == 0 ||
221                                 isspace_l((unsigned char)*buf, locale))
222                                 break;
223
224                         if (!isdigit_l((unsigned char)*buf, locale))
225                                 return 0;
226
227                         len = 2;
228                         for (i = 0; len && *buf != 0 &&
229                                 isdigit_l((unsigned char)*buf, locale); buf++){
230                                 i *= 10;
231                                 i += *buf - '0';
232                                 len--;
233                         }
234
235                         if (c == 'M') {
236                                 if (i > 59)
237                                         return 0;
238                                 tm->tm_min = i;
239                         } else {
240                                 if (i > 60)
241                                         return 0;
242                                 tm->tm_sec = i;
243                         }
244
245                         if (*buf != 0 &&
246                                 isspace_l((unsigned char)*buf, locale))
247                                 while (*ptr != 0 &&
248                                        !isspace_l((unsigned char)*ptr, locale))
249                                         ptr++;
250                         break;
251
252                 case 'H':
253                 case 'I':
254                 case 'k':
255                 case 'l':
256                         /*
257                          * Of these, %l is the only specifier explicitly
258                          * documented as not being zero-padded.  However,
259                          * there is no harm in allowing zero-padding.
260                          *
261                          * XXX The %l specifier may gobble one too many
262                          * digits if used incorrectly.
263                          */
264                         if (!isdigit_l((unsigned char)*buf, locale))
265                                 return 0;
266
267                         len = 2;
268                         for (i = 0; len && *buf != 0 &&
269                              isdigit_l((unsigned char)*buf, locale); buf++) {
270                                 i *= 10;
271                                 i += *buf - '0';
272                                 len--;
273                         }
274                         if (c == 'H' || c == 'k') {
275                                 if (i > 23)
276                                         return 0;
277                         } else if (i > 12)
278                                 return 0;
279
280                         tm->tm_hour = i;
281
282                         if (*buf != 0 &&
283                             isspace_l((unsigned char)*buf, locale))
284                                 while (*ptr != 0 &&
285                                        !isspace_l((unsigned char)*ptr, locale))
286                                         ptr++;
287                         break;
288
289                 case 'p':
290                         /*
291                          * XXX This is bogus if parsed before hour-related
292                          * specifiers.
293                          */
294                         len = strlen(tptr->am);
295                         if (strncasecmp_l(buf, tptr->am, len, locale) == 0) {
296                                 if (tm->tm_hour > 12)
297                                         return 0;
298                                 if (tm->tm_hour == 12)
299                                         tm->tm_hour = 0;
300                                 buf += len;
301                                 break;
302                         }
303
304                         len = strlen(tptr->pm);
305                         if (strncasecmp_l(buf, tptr->pm, len, locale) == 0) {
306                                 if (tm->tm_hour > 12)
307                                         return 0;
308                                 if (tm->tm_hour != 12)
309                                         tm->tm_hour += 12;
310                                 buf += len;
311                                 break;
312                         }
313
314                         return 0;
315
316                 case 'A':
317                 case 'a':
318                         for (i = 0; i < asizeof(tptr->weekday); i++) {
319                                 len = strlen(tptr->weekday[i]);
320                                 if (strncasecmp_l(buf, tptr->weekday[i],
321                                                 len, locale) == 0)
322                                         break;
323                                 len = strlen(tptr->wday[i]);
324                                 if (strncasecmp_l(buf, tptr->wday[i],
325                                                 len, locale) == 0)
326                                         break;
327                         }
328                         if (i == asizeof(tptr->weekday))
329                                 return 0;
330
331                         tm->tm_wday = i;
332                         buf += len;
333                         break;
334
335                 case 'U':
336                 case 'W':
337                         /*
338                          * XXX This is bogus, as we can not assume any valid
339                          * information present in the tm structure at this
340                          * point to calculate a real value, so just check the
341                          * range for now.
342                          */
343                         if (!isdigit_l((unsigned char)*buf, locale))
344                                 return 0;
345
346                         len = 2;
347                         for (i = 0; len && *buf != 0 &&
348                              isdigit_l((unsigned char)*buf, locale); buf++) {
349                                 i *= 10;
350                                 i += *buf - '0';
351                                 len--;
352                         }
353                         if (i > 53)
354                                 return 0;
355
356                         if (*buf != 0 &&
357                             isspace_l((unsigned char)*buf, locale))
358                                 while (*ptr != 0 &&
359                                        !isspace_l((unsigned char)*ptr, locale))
360                                         ptr++;
361                         break;
362
363                 case 'w':
364                         if (!isdigit_l((unsigned char)*buf, locale))
365                                 return 0;
366
367                         i = *buf - '0';
368                         if (i > 6)
369                                 return 0;
370
371                         tm->tm_wday = i;
372
373                         if (*buf != 0 &&
374                             isspace_l((unsigned char)*buf, locale))
375                                 while (*ptr != 0 &&
376                                        !isspace_l((unsigned char)*ptr, locale))
377                                         ptr++;
378                         break;
379
380                 case 'd':
381                 case 'e':
382                         /*
383                          * The %e specifier is explicitly documented as not
384                          * being zero-padded but there is no harm in allowing
385                          * such padding.
386                          *
387                          * XXX The %e specifier may gobble one too many
388                          * digits if used incorrectly.
389                          */
390                         if (!isdigit_l((unsigned char)*buf, locale))
391                                 return 0;
392
393                         len = 2;
394                         for (i = 0; len && *buf != 0 &&
395                              isdigit_l((unsigned char)*buf, locale); buf++) {
396                                 i *= 10;
397                                 i += *buf - '0';
398                                 len--;
399                         }
400                         if (i > 31)
401                                 return 0;
402
403                         tm->tm_mday = i;
404
405                         if (*buf != 0 &&
406                             isspace_l((unsigned char)*buf, locale))
407                                 while (*ptr != 0 &&
408                                        !isspace_l((unsigned char)*ptr, locale))
409                                         ptr++;
410                         break;
411
412                 case 'B':
413                 case 'b':
414                 case 'h':
415                         for (i = 0; i < asizeof(tptr->month); i++) {
416                                 if (Oalternative) {
417                                         if (c == 'B') {
418                                                 len = strlen(tptr->alt_month[i]);
419                                                 if (strncasecmp_l(buf,
420                                                                 tptr->alt_month[i],
421                                                                 len, locale) == 0)
422                                                         break;
423                                         }
424                                 } else {
425                                         len = strlen(tptr->month[i]);
426                                         if (strncasecmp_l(buf, tptr->month[i],
427                                                         len, locale) == 0)
428                                                 break;
429                                 }
430                         }
431                         /*
432                          * Try the abbreviated month name if the full name
433                          * wasn't found and Oalternative was not requested.
434                          */
435                         if (i == asizeof(tptr->month) && !Oalternative) {
436                                 for (i = 0; i < asizeof(tptr->month); i++) {
437                                         len = strlen(tptr->mon[i]);
438                                         if (strncasecmp_l(buf, tptr->mon[i],
439                                                         len, locale) == 0)
440                                                 break;
441                                 }
442                         }
443                         if (i == asizeof(tptr->month))
444                                 return 0;
445
446                         tm->tm_mon = i;
447                         buf += len;
448                         break;
449
450                 case 'm':
451                         if (!isdigit_l((unsigned char)*buf, locale))
452                                 return 0;
453
454                         len = 2;
455                         for (i = 0; len && *buf != 0 &&
456                              isdigit_l((unsigned char)*buf, locale); buf++) {
457                                 i *= 10;
458                                 i += *buf - '0';
459                                 len--;
460                         }
461                         if (i < 1 || i > 12)
462                                 return 0;
463
464                         tm->tm_mon = i - 1;
465
466                         if (*buf != 0 &&
467                             isspace_l((unsigned char)*buf, locale))
468                                 while (*ptr != 0 &&
469                                        !isspace_l((unsigned char)*ptr, locale))
470                                         ptr++;
471                         break;
472
473                 case 's':
474                         {
475                         char *cp;
476                         int sverrno;
477                         long n;
478                         time_t t;
479
480                         sverrno = errno;
481                         errno = 0;
482                         n = strtol_l(buf, &cp, 10, locale);
483                         if (errno == ERANGE || (long)(t = n) != n) {
484                                 errno = sverrno;
485                                 return 0;
486                         }
487                         errno = sverrno;
488                         buf = cp;
489                         gmtime_r(&t, tm);
490                         *GMTp = 1;
491                         }
492                         break;
493
494                 case 'Y':
495                 case 'y':
496                         if (*buf == 0 ||
497                             isspace_l((unsigned char)*buf, locale))
498                                 break;
499
500                         if (!isdigit_l((unsigned char)*buf, locale))
501                                 return 0;
502
503                         len = (c == 'Y') ? 4 : 2;
504                         for (i = 0; len && *buf != 0 &&
505                              isdigit_l((unsigned char)*buf, locale); buf++) {
506                                 i *= 10;
507                                 i += *buf - '0';
508                                 len--;
509                         }
510                         if (c == 'Y')
511                                 i -= 1900;
512                         if (c == 'y' && i < 69)
513                                 i += 100;
514                         if (i < 0)
515                                 return 0;
516
517                         tm->tm_year = i;
518
519                         if (*buf != 0 &&
520                             isspace_l((unsigned char)*buf, locale))
521                                 while (*ptr != 0 &&
522                                        !isspace_l((unsigned char)*ptr, locale))
523                                         ptr++;
524                         break;
525
526                 case 'Z':
527                         {
528                         const char *cp;
529                         char *zonestr;
530
531                         for (cp = buf; *cp &&
532                              isupper_l((unsigned char)*cp, locale); ++cp) {
533                                 /*empty*/}
534                         if (cp - buf) {
535                                 zonestr = alloca(cp - buf + 1);
536                                 strncpy(zonestr, buf, cp - buf);
537                                 zonestr[cp - buf] = '\0';
538                                 tzset();
539                                 if (0 == strcmp(zonestr, "GMT")) {
540                                     *GMTp = 1;
541                                 } else if (0 == strcmp(zonestr, tzname[0])) {
542                                     tm->tm_isdst = 0;
543                                 } else if (0 == strcmp(zonestr, tzname[1])) {
544                                     tm->tm_isdst = 1;
545                                 } else {
546                                     return 0;
547                                 }
548                                 buf += cp - buf;
549                         }
550                         }
551                         break;
552
553                 case 'z':
554                         {
555                         int sign = 1;
556
557                         if (*buf != '+') {
558                                 if (*buf == '-')
559                                         sign = -1;
560                                 else
561                                         return 0;
562                         }
563
564                         buf++;
565                         i = 0;
566                         for (len = 4; len > 0; len--) {
567                                 if (isdigit_l((unsigned char)*buf, locale)) {
568                                         i *= 10;
569                                         i += *buf - '0';
570                                         buf++;
571                                 } else
572                                         return 0;
573                         }
574
575                         tm->tm_hour -= sign * (i / 100);
576                         tm->tm_min  -= sign * (i % 100);
577                         *GMTp = 1;
578                         }
579                         break;
580                 }
581         }
582         return (char *)buf;
583 }
584
585
586 char *
587 strptime_l(const char * __restrict buf, const char * __restrict fmt,
588     struct tm * __restrict tm, locale_t loc)
589 {
590         char *ret;
591         int gmt;
592         FIX_LOCALE(loc);
593
594         gmt = 0;
595         ret = _strptime(buf, fmt, tm, &gmt, loc);
596         if (ret && gmt) {
597                 time_t t = timegm(tm);
598                 localtime_r(&t, tm);
599         }
600
601         return (ret);
602 }
603 char *
604 strptime(const char * __restrict buf, const char * __restrict fmt,
605     struct tm * __restrict tm)
606 {
607         return strptime_l(buf, fmt, tm, __get_locale());
608 }