]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - lib/libc/stdtime/strptime.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.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
59 #include <sys/cdefs.h>
60 #ifndef lint
61 #ifndef NOID
62 static char copyright[] __unused =
63 "@(#) Copyright (c) 1994 Powerdog Industries.  All rights reserved.";
64 static char sccsid[] __unused = "@(#)strptime.c 0.1 (Powerdog) 94/03/27";
65 #endif /* !defined NOID */
66 #endif /* not lint */
67 __FBSDID("$FreeBSD$");
68
69 #include "namespace.h"
70 #include <time.h>
71 #include <ctype.h>
72 #include <errno.h>
73 #include <stdlib.h>
74 #include <string.h>
75 #include <pthread.h>
76 #include "un-namespace.h"
77 #include "libc_private.h"
78 #include "timelocal.h"
79
80 static char * _strptime(const char *, const char *, struct tm *, int *, locale_t);
81
82 #define asizeof(a)      (sizeof (a) / sizeof ((a)[0]))
83
84 static char *
85 _strptime(const char *buf, const char *fmt, struct tm *tm, int *GMTp,
86                 locale_t locale)
87 {
88         char    c;
89         const char *ptr;
90         int     i,
91                 len;
92         int Ealternative, Oalternative;
93         struct lc_time_T *tptr = __get_current_time_locale(locale);
94
95         ptr = fmt;
96         while (*ptr != 0) {
97                 if (*buf == 0)
98                         break;
99
100                 c = *ptr++;
101
102                 if (c != '%') {
103                         if (isspace_l((unsigned char)c, locale))
104                                 while (*buf != 0 && 
105                                        isspace_l((unsigned char)*buf, locale))
106                                         buf++;
107                         else if (c != *buf++)
108                                 return 0;
109                         continue;
110                 }
111
112                 Ealternative = 0;
113                 Oalternative = 0;
114 label:
115                 c = *ptr++;
116                 switch (c) {
117                 case 0:
118                 case '%':
119                         if (*buf++ != '%')
120                                 return 0;
121                         break;
122
123                 case '+':
124                         buf = _strptime(buf, tptr->date_fmt, tm, GMTp, locale);
125                         if (buf == 0)
126                                 return 0;
127                         break;
128
129                 case 'C':
130                         if (!isdigit_l((unsigned char)*buf, locale))
131                                 return 0;
132
133                         /* XXX This will break for 3-digit centuries. */
134                         len = 2;
135                         for (i = 0; len && *buf != 0 &&
136                              isdigit_l((unsigned char)*buf, locale); buf++) {
137                                 i *= 10;
138                                 i += *buf - '0';
139                                 len--;
140                         }
141                         if (i < 19)
142                                 return 0;
143
144                         tm->tm_year = i * 100 - 1900;
145                         break;
146
147                 case 'c':
148                         buf = _strptime(buf, tptr->c_fmt, tm, GMTp, locale);
149                         if (buf == 0)
150                                 return 0;
151                         break;
152
153                 case 'D':
154                         buf = _strptime(buf, "%m/%d/%y", tm, GMTp, locale);
155                         if (buf == 0)
156                                 return 0;
157                         break;
158
159                 case 'E':
160                         if (Ealternative || Oalternative)
161                                 break;
162                         Ealternative++;
163                         goto label;
164
165                 case 'O':
166                         if (Ealternative || Oalternative)
167                                 break;
168                         Oalternative++;
169                         goto label;
170
171                 case 'F':
172                         buf = _strptime(buf, "%Y-%m-%d", tm, GMTp, locale);
173                         if (buf == 0)
174                                 return 0;
175                         break;
176
177                 case 'R':
178                         buf = _strptime(buf, "%H:%M", tm, GMTp, locale);
179                         if (buf == 0)
180                                 return 0;
181                         break;
182
183                 case 'r':
184                         buf = _strptime(buf, tptr->ampm_fmt, tm, GMTp, locale);
185                         if (buf == 0)
186                                 return 0;
187                         break;
188
189                 case 'T':
190                         buf = _strptime(buf, "%H:%M:%S", 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 'x':
202                         buf = _strptime(buf, tptr->x_fmt, tm, GMTp, locale);
203                         if (buf == 0)
204                                 return 0;
205                         break;
206
207                 case 'j':
208                         if (!isdigit_l((unsigned char)*buf, locale))
209                                 return 0;
210
211                         len = 3;
212                         for (i = 0; len && *buf != 0 &&
213                              isdigit_l((unsigned char)*buf, locale); buf++){
214                                 i *= 10;
215                                 i += *buf - '0';
216                                 len--;
217                         }
218                         if (i < 1 || i > 366)
219                                 return 0;
220
221                         tm->tm_yday = i - 1;
222                         break;
223
224                 case 'M':
225                 case 'S':
226                         if (*buf == 0 ||
227                                 isspace_l((unsigned char)*buf, locale))
228                                 break;
229
230                         if (!isdigit_l((unsigned char)*buf, locale))
231                                 return 0;
232
233                         len = 2;
234                         for (i = 0; len && *buf != 0 &&
235                                 isdigit_l((unsigned char)*buf, locale); buf++){
236                                 i *= 10;
237                                 i += *buf - '0';
238                                 len--;
239                         }
240
241                         if (c == 'M') {
242                                 if (i > 59)
243                                         return 0;
244                                 tm->tm_min = i;
245                         } else {
246                                 if (i > 60)
247                                         return 0;
248                                 tm->tm_sec = i;
249                         }
250
251                         if (*buf != 0 &&
252                                 isspace_l((unsigned char)*buf, locale))
253                                 while (*ptr != 0 &&
254                                        !isspace_l((unsigned char)*ptr, locale))
255                                         ptr++;
256                         break;
257
258                 case 'H':
259                 case 'I':
260                 case 'k':
261                 case 'l':
262                         /*
263                          * Of these, %l is the only specifier explicitly
264                          * documented as not being zero-padded.  However,
265                          * there is no harm in allowing zero-padding.
266                          *
267                          * XXX The %l specifier may gobble one too many
268                          * digits if used incorrectly.
269                          */
270                         if (!isdigit_l((unsigned char)*buf, locale))
271                                 return 0;
272
273                         len = 2;
274                         for (i = 0; len && *buf != 0 &&
275                              isdigit_l((unsigned char)*buf, locale); buf++) {
276                                 i *= 10;
277                                 i += *buf - '0';
278                                 len--;
279                         }
280                         if (c == 'H' || c == 'k') {
281                                 if (i > 23)
282                                         return 0;
283                         } else if (i > 12)
284                                 return 0;
285
286                         tm->tm_hour = i;
287
288                         if (*buf != 0 &&
289                             isspace_l((unsigned char)*buf, locale))
290                                 while (*ptr != 0 &&
291                                        !isspace_l((unsigned char)*ptr, locale))
292                                         ptr++;
293                         break;
294
295                 case 'p':
296                         /*
297                          * XXX This is bogus if parsed before hour-related
298                          * specifiers.
299                          */
300                         len = strlen(tptr->am);
301                         if (strncasecmp_l(buf, tptr->am, len, locale) == 0) {
302                                 if (tm->tm_hour > 12)
303                                         return 0;
304                                 if (tm->tm_hour == 12)
305                                         tm->tm_hour = 0;
306                                 buf += len;
307                                 break;
308                         }
309
310                         len = strlen(tptr->pm);
311                         if (strncasecmp_l(buf, tptr->pm, len, locale) == 0) {
312                                 if (tm->tm_hour > 12)
313                                         return 0;
314                                 if (tm->tm_hour != 12)
315                                         tm->tm_hour += 12;
316                                 buf += len;
317                                 break;
318                         }
319
320                         return 0;
321
322                 case 'A':
323                 case 'a':
324                         for (i = 0; i < asizeof(tptr->weekday); i++) {
325                                 len = strlen(tptr->weekday[i]);
326                                 if (strncasecmp_l(buf, tptr->weekday[i],
327                                                 len, locale) == 0)
328                                         break;
329                                 len = strlen(tptr->wday[i]);
330                                 if (strncasecmp_l(buf, tptr->wday[i],
331                                                 len, locale) == 0)
332                                         break;
333                         }
334                         if (i == asizeof(tptr->weekday))
335                                 return 0;
336
337                         tm->tm_wday = i;
338                         buf += len;
339                         break;
340
341                 case 'U':
342                 case 'W':
343                         /*
344                          * XXX This is bogus, as we can not assume any valid
345                          * information present in the tm structure at this
346                          * point to calculate a real value, so just check the
347                          * range for now.
348                          */
349                         if (!isdigit_l((unsigned char)*buf, locale))
350                                 return 0;
351
352                         len = 2;
353                         for (i = 0; len && *buf != 0 &&
354                              isdigit_l((unsigned char)*buf, locale); buf++) {
355                                 i *= 10;
356                                 i += *buf - '0';
357                                 len--;
358                         }
359                         if (i > 53)
360                                 return 0;
361
362                         if (*buf != 0 &&
363                             isspace_l((unsigned char)*buf, locale))
364                                 while (*ptr != 0 &&
365                                        !isspace_l((unsigned char)*ptr, locale))
366                                         ptr++;
367                         break;
368
369                 case 'w':
370                         if (!isdigit_l((unsigned char)*buf, locale))
371                                 return 0;
372
373                         i = *buf - '0';
374                         if (i > 6)
375                                 return 0;
376
377                         tm->tm_wday = i;
378
379                         if (*buf != 0 &&
380                             isspace_l((unsigned char)*buf, locale))
381                                 while (*ptr != 0 &&
382                                        !isspace_l((unsigned char)*ptr, locale))
383                                         ptr++;
384                         break;
385
386                 case 'd':
387                 case 'e':
388                         /*
389                          * The %e specifier is explicitly documented as not
390                          * being zero-padded but there is no harm in allowing
391                          * such padding.
392                          *
393                          * XXX The %e specifier may gobble one too many
394                          * digits if used incorrectly.
395                          */
396                         if (!isdigit_l((unsigned char)*buf, locale))
397                                 return 0;
398
399                         len = 2;
400                         for (i = 0; len && *buf != 0 &&
401                              isdigit_l((unsigned char)*buf, locale); buf++) {
402                                 i *= 10;
403                                 i += *buf - '0';
404                                 len--;
405                         }
406                         if (i > 31)
407                                 return 0;
408
409                         tm->tm_mday = i;
410
411                         if (*buf != 0 &&
412                             isspace_l((unsigned char)*buf, locale))
413                                 while (*ptr != 0 &&
414                                        !isspace_l((unsigned char)*ptr, locale))
415                                         ptr++;
416                         break;
417
418                 case 'B':
419                 case 'b':
420                 case 'h':
421                         for (i = 0; i < asizeof(tptr->month); i++) {
422                                 if (Oalternative) {
423                                         if (c == 'B') {
424                                                 len = strlen(tptr->alt_month[i]);
425                                                 if (strncasecmp_l(buf,
426                                                                 tptr->alt_month[i],
427                                                                 len, locale) == 0)
428                                                         break;
429                                         }
430                                 } else {
431                                         len = strlen(tptr->month[i]);
432                                         if (strncasecmp_l(buf, tptr->month[i],
433                                                         len, locale) == 0)
434                                                 break;
435                                 }
436                         }
437                         /*
438                          * Try the abbreviated month name if the full name
439                          * wasn't found and Oalternative was not requested.
440                          */
441                         if (i == asizeof(tptr->month) && !Oalternative) {
442                                 for (i = 0; i < asizeof(tptr->month); i++) {
443                                         len = strlen(tptr->mon[i]);
444                                         if (strncasecmp_l(buf, tptr->mon[i],
445                                                         len, locale) == 0)
446                                                 break;
447                                 }
448                         }
449                         if (i == asizeof(tptr->month))
450                                 return 0;
451
452                         tm->tm_mon = i;
453                         buf += len;
454                         break;
455
456                 case 'm':
457                         if (!isdigit_l((unsigned char)*buf, locale))
458                                 return 0;
459
460                         len = 2;
461                         for (i = 0; len && *buf != 0 &&
462                              isdigit_l((unsigned char)*buf, locale); buf++) {
463                                 i *= 10;
464                                 i += *buf - '0';
465                                 len--;
466                         }
467                         if (i < 1 || i > 12)
468                                 return 0;
469
470                         tm->tm_mon = i - 1;
471
472                         if (*buf != 0 &&
473                             isspace_l((unsigned char)*buf, locale))
474                                 while (*ptr != 0 &&
475                                        !isspace_l((unsigned char)*ptr, locale))
476                                         ptr++;
477                         break;
478
479                 case 's':
480                         {
481                         char *cp;
482                         int sverrno;
483                         long n;
484                         time_t t;
485
486                         sverrno = errno;
487                         errno = 0;
488                         n = strtol_l(buf, &cp, 10, locale);
489                         if (errno == ERANGE || (long)(t = n) != n) {
490                                 errno = sverrno;
491                                 return 0;
492                         }
493                         errno = sverrno;
494                         buf = cp;
495                         gmtime_r(&t, tm);
496                         *GMTp = 1;
497                         }
498                         break;
499
500                 case 'Y':
501                 case 'y':
502                         if (*buf == 0 ||
503                             isspace_l((unsigned char)*buf, locale))
504                                 break;
505
506                         if (!isdigit_l((unsigned char)*buf, locale))
507                                 return 0;
508
509                         len = (c == 'Y') ? 4 : 2;
510                         for (i = 0; len && *buf != 0 &&
511                              isdigit_l((unsigned char)*buf, locale); buf++) {
512                                 i *= 10;
513                                 i += *buf - '0';
514                                 len--;
515                         }
516                         if (c == 'Y')
517                                 i -= 1900;
518                         if (c == 'y' && i < 69)
519                                 i += 100;
520                         if (i < 0)
521                                 return 0;
522
523                         tm->tm_year = i;
524
525                         if (*buf != 0 &&
526                             isspace_l((unsigned char)*buf, locale))
527                                 while (*ptr != 0 &&
528                                        !isspace_l((unsigned char)*ptr, locale))
529                                         ptr++;
530                         break;
531
532                 case 'Z':
533                         {
534                         const char *cp;
535                         char *zonestr;
536
537                         for (cp = buf; *cp &&
538                              isupper_l((unsigned char)*cp, locale); ++cp) {
539                                 /*empty*/}
540                         if (cp - buf) {
541                                 zonestr = alloca(cp - buf + 1);
542                                 strncpy(zonestr, buf, cp - buf);
543                                 zonestr[cp - buf] = '\0';
544                                 tzset();
545                                 if (0 == strcmp(zonestr, "GMT")) {
546                                     *GMTp = 1;
547                                 } else if (0 == strcmp(zonestr, tzname[0])) {
548                                     tm->tm_isdst = 0;
549                                 } else if (0 == strcmp(zonestr, tzname[1])) {
550                                     tm->tm_isdst = 1;
551                                 } else {
552                                     return 0;
553                                 }
554                                 buf += cp - buf;
555                         }
556                         }
557                         break;
558
559                 case 'z':
560                         {
561                         int sign = 1;
562
563                         if (*buf != '+') {
564                                 if (*buf == '-')
565                                         sign = -1;
566                                 else
567                                         return 0;
568                         }
569
570                         buf++;
571                         i = 0;
572                         for (len = 4; len > 0; len--) {
573                                 if (isdigit_l((unsigned char)*buf, locale)) {
574                                         i *= 10;
575                                         i += *buf - '0';
576                                         buf++;
577                                 } else
578                                         return 0;
579                         }
580
581                         tm->tm_hour -= sign * (i / 100);
582                         tm->tm_min  -= sign * (i % 100);
583                         *GMTp = 1;
584                         }
585                         break;
586                 }
587         }
588         return (char *)buf;
589 }
590
591
592 char *
593 strptime_l(const char * __restrict buf, const char * __restrict fmt,
594     struct tm * __restrict tm, locale_t loc)
595 {
596         char *ret;
597         int gmt;
598         FIX_LOCALE(loc);
599
600         gmt = 0;
601         ret = _strptime(buf, fmt, tm, &gmt, loc);
602         if (ret && gmt) {
603                 time_t t = timegm(tm);
604                 localtime_r(&t, tm);
605         }
606
607         return (ret);
608 }
609 char *
610 strptime(const char * __restrict buf, const char * __restrict fmt,
611     struct tm * __restrict tm)
612 {
613         return strptime_l(buf, fmt, tm, __get_locale());
614 }