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