• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*  $OpenBSD: strptime.c,v 1.11 2005/08/08 08:05:38 espie Exp $ */
2 /*  $NetBSD: strptime.c,v 1.12 1998/01/20 21:39:40 mycroft Exp $    */
3 
4 /*-
5  * Copyright (c) 1997, 1998 The NetBSD Foundation, Inc.
6  * All rights reserved.
7  *
8  * This code was contributed to The NetBSD Foundation by Klaus Klein.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *        This product includes software developed by the NetBSD
21  *        Foundation, Inc. and its contributors.
22  * 4. Neither the name of The NetBSD Foundation nor the names of its
23  *    contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 
39 //#include <sys/localedef.h>
40 #include <ctype.h>
41 #include <errno.h>
42 #include <locale.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <time.h>
46 #include "tzfile.h"
47 
48 static const struct {
49     const char *abday[7];
50     const char *day[7];
51     const char *abmon[12];
52     const char *mon[12];
53     const char *am_pm[2];
54     const char *d_t_fmt;
55     const char *d_fmt;
56     const char *t_fmt;
57     const char *t_fmt_ampm;
58 } _DefaultTimeLocale = {
59     {
60         "Sun","Mon","Tue","Wed","Thu","Fri","Sat",
61     },
62     {
63         "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
64         "Friday", "Saturday"
65     },
66     {
67         "Jan", "Feb", "Mar", "Apr", "May", "Jun",
68         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
69     },
70     {
71         "January", "February", "March", "April", "May", "June", "July",
72         "August", "September", "October", "November", "December"
73     },
74     {
75         "AM", "PM"
76     },
77     "%a %b %d %H:%M:%S %Y",
78     "%m/%d/%y",
79     "%H:%M:%S",
80     "%I:%M:%S %p"
81 };
82 
83 #define _ctloc(x) (_DefaultTimeLocale.x)
84 
85 /*
86  * We do not implement alternate representations. However, we always
87  * check whether a given modifier is allowed for a certain conversion.
88  */
89 #define _ALT_E          0x01
90 #define _ALT_O          0x02
91 #define _LEGAL_ALT(x)       { if (alt_format & ~(x)) return (0); }
92 
93 
94 struct century_relyear {
95     int century;
96     int relyear;
97 };
98 static  int _conv_num(const unsigned char **, int *, int, int);
99 static  unsigned char *_strptime(const unsigned char *, const char *, struct tm *,
100         struct century_relyear *);
101 
102 
103 char *
strptime(const char * buf,const char * fmt,struct tm * tm)104 strptime(const char *buf, const char *fmt, struct tm *tm)
105 {
106     struct century_relyear cr;
107     cr.century = TM_YEAR_BASE;
108     cr.relyear = -1;
109     return (char*)(_strptime((const unsigned char*)buf, fmt, tm, &cr));
110 }
111 
112 static unsigned char *
_strptime(const unsigned char * buf,const char * fmt,struct tm * tm,struct century_relyear * cr)113 _strptime(const unsigned char *buf, const char *fmt, struct tm *tm, struct century_relyear *cr)
114 {
115     unsigned char c;
116     const unsigned char *bp;
117     size_t len = 0;
118     int alt_format, i;
119 
120     bp = (unsigned char *)buf;
121     while ((c = *fmt) != '\0') {
122         /* Clear `alternate' modifier prior to new conversion. */
123         alt_format = 0;
124 
125         /* Eat up white-space. */
126         if (isspace(c)) {
127             while (isspace(*bp))
128                 bp++;
129 
130             fmt++;
131             continue;
132         }
133 
134         if ((c = *fmt++) != '%')
135             goto literal;
136 
137 
138 again:      switch (c = *fmt++) {
139         case '%':   /* "%%" is converted to "%". */
140 literal:
141         if (c != *bp++)
142             return (NULL);
143 
144         break;
145 
146         /*
147          * "Alternative" modifiers. Just set the appropriate flag
148          * and start over again.
149          */
150         case 'E':   /* "%E?" alternative conversion modifier. */
151             _LEGAL_ALT(0);
152             alt_format |= _ALT_E;
153             goto again;
154 
155         case 'O':   /* "%O?" alternative conversion modifier. */
156             _LEGAL_ALT(0);
157             alt_format |= _ALT_O;
158             goto again;
159 
160         /*
161          * "Complex" conversion rules, implemented through recursion.
162          */
163         case 'c':   /* Date and time, using the locale's format. */
164             _LEGAL_ALT(_ALT_E);
165             if (!(bp = _strptime(bp, _ctloc(d_t_fmt), tm, cr)))
166                 return (NULL);
167             break;
168 
169         case 'D':   /* The date as "%m/%d/%y". */
170             _LEGAL_ALT(0);
171             if (!(bp = _strptime(bp, "%m/%d/%y", tm, cr)))
172                 return (NULL);
173             break;
174 
175         case 'F':  /* The date as "%Y-%m-%d". */
176             _LEGAL_ALT(0);
177             if (!(bp = _strptime(bp, "%Y-%m-%d", tm, cr)))
178                 return (NULL);
179             continue;
180 
181         case 'R':   /* The time as "%H:%M". */
182             _LEGAL_ALT(0);
183             if (!(bp = _strptime(bp, "%H:%M", tm, cr)))
184                 return (NULL);
185             break;
186 
187         case 'r':   /* The time as "%I:%M:%S %p". */
188             _LEGAL_ALT(0);
189             if (!(bp = _strptime(bp, "%I:%M:%S %p", tm, cr)))
190                 return (NULL);
191             break;
192 
193         case 'T':   /* The time as "%H:%M:%S". */
194             _LEGAL_ALT(0);
195             if (!(bp = _strptime(bp, "%H:%M:%S", tm, cr)))
196                 return (NULL);
197             break;
198 
199         case 'v':  /* The date as "%e-%b-%Y". */
200             _LEGAL_ALT(0);
201             if (!(bp = _strptime(bp, "%e-%b-%Y", tm, cr)))
202                 return (NULL);
203             break;
204 
205         case 'X':   /* The time, using the locale's format. */
206             _LEGAL_ALT(_ALT_E);
207             if (!(bp = _strptime(bp, _ctloc(t_fmt), tm, cr)))
208                 return (NULL);
209             break;
210 
211         case 'x':   /* The date, using the locale's format. */
212             _LEGAL_ALT(_ALT_E);
213             if (!(bp = _strptime(bp, _ctloc(d_fmt), tm, cr)))
214                 return (NULL);
215             break;
216 
217         /*
218          * "Elementary" conversion rules.
219          */
220         case 'A':   /* The day of week, using the locale's form. */
221         case 'a':
222             _LEGAL_ALT(0);
223             for (i = 0; i < 7; i++) {
224                 /* Full name. */
225                 len = strlen(_ctloc(day[i]));
226                 if (strncasecmp(_ctloc(day[i]), (const char*)bp, len) == 0)
227                     break;
228 
229                 /* Abbreviated name. */
230                 len = strlen(_ctloc(abday[i]));
231                 if (strncasecmp(_ctloc(abday[i]), (const char*)bp, len) == 0)
232                     break;
233             }
234 
235             /* Nothing matched. */
236             if (i == 7)
237                 return (NULL);
238 
239             tm->tm_wday = i;
240             bp += len;
241             break;
242 
243         case 'B':   /* The month, using the locale's form. */
244         case 'b':
245         case 'h':
246             _LEGAL_ALT(0);
247             for (i = 0; i < 12; i++) {
248                 /* Full name. */
249                 len = strlen(_ctloc(mon[i]));
250                 if (strncasecmp(_ctloc(mon[i]), (const char*)bp, len) == 0)
251                     break;
252 
253                 /* Abbreviated name. */
254                 len = strlen(_ctloc(abmon[i]));
255                 if (strncasecmp(_ctloc(abmon[i]), (const char*)bp, len) == 0)
256                     break;
257             }
258 
259             /* Nothing matched. */
260             if (i == 12)
261                 return (NULL);
262 
263             tm->tm_mon = i;
264             bp += len;
265             break;
266 
267         case 'C':   /* The century number. */
268             _LEGAL_ALT(_ALT_E);
269             if (!(_conv_num(&bp, &i, 0, 99)))
270                 return (NULL);
271 
272             cr->century = i * 100;
273             break;
274 
275         case 'd':   /* The day of month. */
276         case 'e':
277             _LEGAL_ALT(_ALT_O);
278             if (!(_conv_num(&bp, &tm->tm_mday, 1, 31)))
279                 return (NULL);
280             break;
281 
282         case 'k':   /* The hour (24-hour clock representation). */
283             _LEGAL_ALT(0);
284             /* FALLTHROUGH */
285         case 'H':
286             _LEGAL_ALT(_ALT_O);
287             if (!(_conv_num(&bp, &tm->tm_hour, 0, 23)))
288                 return (NULL);
289             break;
290 
291         case 'l':   /* The hour (12-hour clock representation). */
292             _LEGAL_ALT(0);
293             /* FALLTHROUGH */
294         case 'I':
295             _LEGAL_ALT(_ALT_O);
296             if (!(_conv_num(&bp, &tm->tm_hour, 1, 12)))
297                 return (NULL);
298             break;
299 
300         case 'j':   /* The day of year. */
301             _LEGAL_ALT(0);
302             if (!(_conv_num(&bp, &tm->tm_yday, 1, 366)))
303                 return (NULL);
304             tm->tm_yday--;
305             break;
306 
307         case 'M':   /* The minute. */
308             _LEGAL_ALT(_ALT_O);
309             if (!(_conv_num(&bp, &tm->tm_min, 0, 59)))
310                 return (NULL);
311             break;
312 
313         case 'm':   /* The month. */
314             _LEGAL_ALT(_ALT_O);
315             if (!(_conv_num(&bp, &tm->tm_mon, 1, 12)))
316                 return (NULL);
317             tm->tm_mon--;
318             break;
319 
320         case 'P':
321         case 'p':   /* The locale's equivalent of AM/PM. */
322             _LEGAL_ALT(0);
323             /* AM? */
324             len = strlen(_ctloc(am_pm[0]));
325             if (strncasecmp(_ctloc(am_pm[0]), (const char*)bp, len) == 0) {
326                 if (tm->tm_hour > 12)   /* i.e., 13:00 AM ?! */
327                     return (NULL);
328                 else if (tm->tm_hour == 12)
329                     tm->tm_hour = 0;
330 
331                 bp += len;
332                 break;
333             }
334             /* PM? */
335             len = strlen(_ctloc(am_pm[1]));
336             if (strncasecmp(_ctloc(am_pm[1]), (const char*)bp, len) == 0) {
337                 if (tm->tm_hour > 12)   /* i.e., 13:00 PM ?! */
338                     return (NULL);
339                 else if (tm->tm_hour < 12)
340                     tm->tm_hour += 12;
341 
342                 bp += len;
343                 break;
344             }
345 
346             /* Nothing matched. */
347             return (NULL);
348 
349         case 'S':   /* The seconds. */
350             _LEGAL_ALT(_ALT_O);
351             if (!(_conv_num(&bp, &tm->tm_sec, 0, 61)))
352                 return (NULL);
353             break;
354 
355         case 's':
356             {
357                 // Android addition, based on FreeBSD's implementation.
358                 int saved_errno = errno;
359                 errno = 0;
360                 const unsigned char* old_bp = bp;
361                 long n = strtol((const char*) bp, (char**) &bp, 10);
362                 time_t t = n;
363                 if (bp == old_bp || errno == ERANGE || ((long) t) != n) {
364                     errno = saved_errno;
365                     return NULL;
366                 }
367                 errno = saved_errno;
368 
369                 if (localtime_r(&t, tm) == NULL) return NULL;
370             }
371             break;
372 
373 
374         case 'U':   /* The week of year, beginning on sunday. */
375         case 'W':   /* The week of year, beginning on monday. */
376             _LEGAL_ALT(_ALT_O);
377             /*
378              * XXX This is bogus, as we can not assume any valid
379              * information present in the tm structure at this
380              * point to calculate a real value, so just check the
381              * range for now.
382              */
383              if (!(_conv_num(&bp, &i, 0, 53)))
384                 return (NULL);
385              break;
386 
387         case 'w':   /* The day of week, beginning on sunday. */
388             _LEGAL_ALT(_ALT_O);
389             if (!(_conv_num(&bp, &tm->tm_wday, 0, 6)))
390                 return (NULL);
391             break;
392 
393         case 'u':  /* The day of week, monday = 1. */
394             _LEGAL_ALT(_ALT_O);
395             if (!(_conv_num(&bp, &i, 1, 7)))
396                 return (NULL);
397             tm->tm_wday = i % 7;
398             continue;
399 
400         case 'g':  /* The year corresponding to the ISO week
401                     * number but without the century.
402                     */
403             if (!(_conv_num(&bp, &i, 0, 99)))
404                 return (NULL);
405             continue;
406 
407         case 'G':  /* The year corresponding to the ISO week
408                     * number with century.
409                     */
410             do
411                 bp++;
412             while (isdigit(*bp));
413             continue;
414 
415         case 'V':  /* The ISO 8601:1988 week number as decimal */
416             if (!(_conv_num(&bp, &i, 0, 53)))
417                 return (NULL);
418             continue;
419 
420         case 'Y':   /* The year. */
421             _LEGAL_ALT(_ALT_E);
422             if (!(_conv_num(&bp, &i, 0, 9999)))
423                 return (NULL);
424 
425             cr->relyear = -1;
426             tm->tm_year = i - TM_YEAR_BASE;
427             break;
428 
429         case 'y':   /* The year within the century (2 digits). */
430             _LEGAL_ALT(_ALT_E | _ALT_O);
431             if (!(_conv_num(&bp, &cr->relyear, 0, 99)))
432                 return (NULL);
433             break;
434 
435         /*
436          * Miscellaneous conversions.
437          */
438         case 'n':   /* Any kind of white-space. */
439         case 't':
440             _LEGAL_ALT(0);
441             while (isspace(*bp))
442                 bp++;
443             break;
444 
445 
446         default:    /* Unknown/unsupported conversion. */
447             return (NULL);
448         }
449 
450 
451     }
452 
453     /*
454      * We need to evaluate the two digit year spec (%y)
455      * last as we can get a century spec (%C) at any time.
456      */
457     if (cr->relyear != -1) {
458         if (cr->century == TM_YEAR_BASE) {
459             if (cr->relyear <= 68)
460                 tm->tm_year = cr->relyear + 2000 - TM_YEAR_BASE;
461             else
462                 tm->tm_year = cr->relyear + 1900 - TM_YEAR_BASE;
463         } else {
464             tm->tm_year = cr->relyear + cr->century - TM_YEAR_BASE;
465         }
466     }
467 
468     return (unsigned char*)bp;
469 }
470 
471 
472 static int
_conv_num(const unsigned char ** buf,int * dest,int llim,int ulim)473 _conv_num(const unsigned char **buf, int *dest, int llim, int ulim)
474 {
475     int result = 0;
476     int rulim = ulim;
477 
478     if (**buf < '0' || **buf > '9')
479         return (0);
480 
481     /* we use rulim to break out of the loop when we run out of digits */
482     do {
483         result *= 10;
484         result += *(*buf)++ - '0';
485         rulim /= 10;
486     } while ((result * 10 <= ulim) && rulim && **buf >= '0' && **buf <= '9');
487 
488     if (result < llim || result > ulim)
489         return (0);
490 
491     *dest = result;
492     return (1);
493 }
494 
strptime_l(const char * buf,const char * fmt,struct tm * tm,locale_t l)495 char* strptime_l(const char* buf, const char* fmt, struct tm* tm, locale_t l) {
496   return strptime(buf, fmt, tm);
497 }
498