blob: fe9e10f9679841cbe1f7594c387e87ce5a369f01 [file] [log] [blame]
Elliott Hughesd3627a42022-12-06 22:24:27 +00001/* $OpenBSD: strptime.c,v 1.30 2019/05/12 12:49:52 schwarze Exp $ */
2/* $NetBSD: strptime.c,v 1.12 1998/01/20 21:39:40 mycroft Exp $ */
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08003/*-
Elliott Hughesd3627a42022-12-06 22:24:27 +00004 * Copyright (c) 1997, 1998, 2005, 2008 The NetBSD Foundation, Inc.
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08005 * All rights reserved.
6 *
7 * This code was contributed to The NetBSD Foundation by Klaus Klein.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080017 *
18 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
19 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
20 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
22 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 */
30
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080031#include <ctype.h>
32#include <locale.h>
Elliott Hughesd3627a42022-12-06 22:24:27 +000033#include <stdint.h>
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080034#include <string.h>
35#include <time.h>
Elliott Hughesd3627a42022-12-06 22:24:27 +000036
37#include "localedef.h"
38#include "private.h"
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080039#include "tzfile.h"
40
Elliott Hughesd3627a42022-12-06 22:24:27 +000041// Android: ignore OpenBSD's DEF_WEAK() stuff.
42#define DEF_WEAK(sym) /* */
43// Android: this code is not pointer-sign clean.
44#pragma clang diagnostic ignored "-Wpointer-sign"
45#pragma clang diagnostic ignored "-Wunused-function"
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080046
Elliott Hughesd3627a42022-12-06 22:24:27 +000047#define _ctloc(x) (_CurrentTimeLocale->x)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080048
49/*
50 * We do not implement alternate representations. However, we always
51 * check whether a given modifier is allowed for a certain conversion.
52 */
Elliott Hughesd3627a42022-12-06 22:24:27 +000053#define _ALT_E 0x01
54#define _ALT_O 0x02
55#define _LEGAL_ALT(x) { if (alt_format & ~(x)) return (0); }
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080056
Elliott Hughesd3627a42022-12-06 22:24:27 +000057/*
58 * We keep track of some of the fields we set in order to compute missing ones.
59 */
60#define FIELD_TM_MON (1 << 0)
61#define FIELD_TM_MDAY (1 << 1)
62#define FIELD_TM_WDAY (1 << 2)
63#define FIELD_TM_YDAY (1 << 3)
64#define FIELD_TM_YEAR (1 << 4)
Elliott Hughesd065c042020-09-01 19:02:44 -070065
66static char gmt[] = { "GMT" };
67static char utc[] = { "UTC" };
68/* RFC-822/RFC-2822 */
69static const char * const nast[5] = {
70 "EST", "CST", "MST", "PST", "\0\0\0"
71};
72static const char * const nadt[5] = {
73 "EDT", "CDT", "MDT", "PDT", "\0\0\0"
74};
75
Elliott Hughesd3627a42022-12-06 22:24:27 +000076static const int mon_lengths[2][MONSPERYEAR] = {
77 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
78 { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
79};
80
81static int _conv_num64(const unsigned char **, int64_t *, int64_t, int64_t);
82static int _conv_num(const unsigned char **, int *, int, int);
83static int leaps_thru_end_of(const int y);
84static char *_strptime(const char *, const char *, struct tm *, int);
Elliott Hughesd065c042020-09-01 19:02:44 -070085static const u_char *_find_string(const u_char *, int *, const char * const *,
86 const char * const *, int);
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080087
88
89char *
90strptime(const char *buf, const char *fmt, struct tm *tm)
91{
Elliott Hughesd3627a42022-12-06 22:24:27 +000092 return(_strptime(buf, fmt, tm, 1));
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080093}
Elliott Hughesd3627a42022-12-06 22:24:27 +000094DEF_WEAK(strptime);
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080095
Elliott Hughesd3627a42022-12-06 22:24:27 +000096static char *
97_strptime(const char *buf, const char *fmt, struct tm *tm, int initialize)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080098{
Elliott Hughesd3627a42022-12-06 22:24:27 +000099 unsigned char c;
100 const unsigned char *bp, *ep;
101 size_t len;
102 int alt_format, i, offs;
103 int neg = 0;
104 static int century, relyear, fields;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800105
Elliott Hughesd3627a42022-12-06 22:24:27 +0000106 if (initialize) {
107 century = TM_YEAR_BASE;
108 relyear = -1;
109 fields = 0;
110 }
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800111
Elliott Hughesd3627a42022-12-06 22:24:27 +0000112 bp = (const unsigned char *)buf;
113 while ((c = *fmt) != '\0') {
114 /* Clear `alternate' modifier prior to new conversion. */
115 alt_format = 0;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800116
Elliott Hughesd3627a42022-12-06 22:24:27 +0000117 /* Eat up white-space. */
118 if (isspace(c)) {
119 while (isspace(*bp))
120 bp++;
Elliott Hughes81baaf22018-02-28 15:22:48 -0800121
Elliott Hughesd3627a42022-12-06 22:24:27 +0000122 fmt++;
123 continue;
124 }
125
126 if ((c = *fmt++) != '%')
127 goto literal;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800128
129
Elliott Hughesd3627a42022-12-06 22:24:27 +0000130again: switch (c = *fmt++) {
131 case '%': /* "%%" is converted to "%". */
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800132literal:
Elliott Hughesd3627a42022-12-06 22:24:27 +0000133 if (c != *bp++)
134 return (NULL);
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800135
Elliott Hughesd3627a42022-12-06 22:24:27 +0000136 break;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800137
Elliott Hughesd3627a42022-12-06 22:24:27 +0000138 /*
139 * "Alternative" modifiers. Just set the appropriate flag
140 * and start over again.
141 */
142 case 'E': /* "%E?" alternative conversion modifier. */
143 _LEGAL_ALT(0);
144 alt_format |= _ALT_E;
145 goto again;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800146
Elliott Hughesd3627a42022-12-06 22:24:27 +0000147 case 'O': /* "%O?" alternative conversion modifier. */
148 _LEGAL_ALT(0);
149 alt_format |= _ALT_O;
150 goto again;
Elliott Hughes81baaf22018-02-28 15:22:48 -0800151
Elliott Hughesd3627a42022-12-06 22:24:27 +0000152 /*
153 * "Complex" conversion rules, implemented through recursion.
154 */
155 case 'c': /* Date and time, using the locale's format. */
156 _LEGAL_ALT(_ALT_E);
157 if (!(bp = _strptime(bp, _ctloc(d_t_fmt), tm, 0)))
158 return (NULL);
159 break;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800160
Elliott Hughesd3627a42022-12-06 22:24:27 +0000161 case 'D': /* The date as "%m/%d/%y". */
162 _LEGAL_ALT(0);
163 if (!(bp = _strptime(bp, "%m/%d/%y", tm, 0)))
164 return (NULL);
165 break;
Elliott Hughes81baaf22018-02-28 15:22:48 -0800166
Elliott Hughesd3627a42022-12-06 22:24:27 +0000167 case 'F': /* The date as "%Y-%m-%d". */
168 _LEGAL_ALT(0);
169 if (!(bp = _strptime(bp, "%Y-%m-%d", tm, 0)))
170 return (NULL);
171 continue;
Elliott Hughesa1fb15b2019-03-26 19:07:40 -0700172
Elliott Hughesd3627a42022-12-06 22:24:27 +0000173 case 'R': /* The time as "%H:%M". */
174 _LEGAL_ALT(0);
175 if (!(bp = _strptime(bp, "%H:%M", tm, 0)))
176 return (NULL);
177 break;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800178
Elliott Hughesd3627a42022-12-06 22:24:27 +0000179 case 'r': /* The time as "%I:%M:%S %p". */
180 _LEGAL_ALT(0);
181 if (!(bp = _strptime(bp, "%I:%M:%S %p", tm, 0)))
182 return (NULL);
183 break;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800184
Elliott Hughesd3627a42022-12-06 22:24:27 +0000185 case 'T': /* The time as "%H:%M:%S". */
186 _LEGAL_ALT(0);
187 if (!(bp = _strptime(bp, "%H:%M:%S", tm, 0)))
188 return (NULL);
189 break;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800190
Elliott Hughesd3627a42022-12-06 22:24:27 +0000191 case 'v': /* Android: the date as "%e-%b-%Y" for strftime() compat; glibc does this too. */
192 _LEGAL_ALT(0);
193 if (!(bp = _strptime(bp, "%e-%b-%Y", tm, 0)))
194 return (NULL);
195 break;
Elliott Hughesa1fb15b2019-03-26 19:07:40 -0700196
Elliott Hughesd3627a42022-12-06 22:24:27 +0000197 case 'X': /* The time, using the locale's format. */
198 _LEGAL_ALT(_ALT_E);
199 if (!(bp = _strptime(bp, _ctloc(t_fmt), tm, 0)))
200 return (NULL);
201 break;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800202
Elliott Hughesd3627a42022-12-06 22:24:27 +0000203 case 'x': /* The date, using the locale's format. */
204 _LEGAL_ALT(_ALT_E);
205 if (!(bp = _strptime(bp, _ctloc(d_fmt), tm, 0)))
206 return (NULL);
207 break;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800208
Elliott Hughesd3627a42022-12-06 22:24:27 +0000209 /*
210 * "Elementary" conversion rules.
211 */
212 case 'A': /* The day of week, using the locale's form. */
213 case 'a':
214 _LEGAL_ALT(0);
215 for (i = 0; i < 7; i++) {
216 /* Full name. */
217 len = strlen(_ctloc(day[i]));
218 if (strncasecmp(_ctloc(day[i]), bp, len) == 0)
219 break;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800220
Elliott Hughesd3627a42022-12-06 22:24:27 +0000221 /* Abbreviated name. */
222 len = strlen(_ctloc(abday[i]));
223 if (strncasecmp(_ctloc(abday[i]), bp, len) == 0)
224 break;
225 }
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800226
Elliott Hughesd3627a42022-12-06 22:24:27 +0000227 /* Nothing matched. */
228 if (i == 7)
229 return (NULL);
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800230
Elliott Hughesd3627a42022-12-06 22:24:27 +0000231 tm->tm_wday = i;
232 bp += len;
233 fields |= FIELD_TM_WDAY;
234 break;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800235
Elliott Hughesd3627a42022-12-06 22:24:27 +0000236 case 'B': /* The month, using the locale's form. */
237 case 'b':
238 case 'h':
239 _LEGAL_ALT(0);
240 for (i = 0; i < 12; i++) {
241 /* Full name. */
242 len = strlen(_ctloc(mon[i]));
243 if (strncasecmp(_ctloc(mon[i]), bp, len) == 0)
244 break;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800245
Elliott Hughesd3627a42022-12-06 22:24:27 +0000246 /* Abbreviated name. */
247 len = strlen(_ctloc(abmon[i]));
248 if (strncasecmp(_ctloc(abmon[i]), bp, len) == 0)
249 break;
250 }
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800251
Elliott Hughesd3627a42022-12-06 22:24:27 +0000252 /* Nothing matched. */
253 if (i == 12)
254 return (NULL);
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800255
Elliott Hughesd3627a42022-12-06 22:24:27 +0000256 tm->tm_mon = i;
257 bp += len;
258 fields |= FIELD_TM_MON;
259 break;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800260
Elliott Hughesd3627a42022-12-06 22:24:27 +0000261 case 'C': /* The century number. */
262 _LEGAL_ALT(_ALT_E);
263 if (!(_conv_num(&bp, &i, 0, 99)))
264 return (NULL);
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800265
Elliott Hughesd3627a42022-12-06 22:24:27 +0000266 century = i * 100;
267 break;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800268
Elliott Hughesd3627a42022-12-06 22:24:27 +0000269 case 'e': /* The day of month. */
270 if (isspace(*bp))
271 bp++;
272 /* FALLTHROUGH */
273 case 'd':
274 _LEGAL_ALT(_ALT_O);
275 if (!(_conv_num(&bp, &tm->tm_mday, 1, 31)))
276 return (NULL);
277 fields |= FIELD_TM_MDAY;
278 break;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800279
Elliott Hughesd3627a42022-12-06 22:24:27 +0000280 case 'k': /* The hour (24-hour clock representation). */
281 _LEGAL_ALT(0);
282 /* FALLTHROUGH */
283 case 'H':
284 _LEGAL_ALT(_ALT_O);
285 if (!(_conv_num(&bp, &tm->tm_hour, 0, 23)))
286 return (NULL);
287 break;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800288
Elliott Hughesd3627a42022-12-06 22:24:27 +0000289 case 'l': /* The hour (12-hour clock representation). */
290 _LEGAL_ALT(0);
291 /* FALLTHROUGH */
292 case 'I':
293 _LEGAL_ALT(_ALT_O);
294 if (!(_conv_num(&bp, &tm->tm_hour, 1, 12)))
295 return (NULL);
296 break;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800297
Elliott Hughesd3627a42022-12-06 22:24:27 +0000298 case 'j': /* The day of year. */
299 _LEGAL_ALT(0);
300 if (!(_conv_num(&bp, &tm->tm_yday, 1, 366)))
301 return (NULL);
302 tm->tm_yday--;
303 fields |= FIELD_TM_YDAY;
304 break;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800305
Elliott Hughesd3627a42022-12-06 22:24:27 +0000306 case 'M': /* The minute. */
307 _LEGAL_ALT(_ALT_O);
308 if (!(_conv_num(&bp, &tm->tm_min, 0, 59)))
309 return (NULL);
310 break;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800311
Elliott Hughesd3627a42022-12-06 22:24:27 +0000312 case 'm': /* The month. */
313 _LEGAL_ALT(_ALT_O);
314 if (!(_conv_num(&bp, &tm->tm_mon, 1, 12)))
315 return (NULL);
316 tm->tm_mon--;
317 fields |= FIELD_TM_MON;
318 break;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800319
Elliott Hughesd3627a42022-12-06 22:24:27 +0000320 case 'P': /* Android addition for strftime() compat; glibc does this too. */
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]), 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;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800330
Elliott Hughesd3627a42022-12-06 22:24:27 +0000331 bp += len;
332 break;
333 }
334 /* PM? */
335 len = strlen(_ctloc(am_pm[1]));
336 if (strncasecmp(_ctloc(am_pm[1]), 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;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800341
Elliott Hughesd3627a42022-12-06 22:24:27 +0000342 bp += len;
343 break;
344 }
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800345
Elliott Hughesd3627a42022-12-06 22:24:27 +0000346 /* Nothing matched. */
347 return (NULL);
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800348
Elliott Hughesd3627a42022-12-06 22:24:27 +0000349 case 'S': /* The seconds. */
350 _LEGAL_ALT(_ALT_O);
351 if (!(_conv_num(&bp, &tm->tm_sec, 0, 60)))
352 return (NULL);
353 break;
354 case 's': /* Seconds since epoch */
355 {
356 // Android change based on FreeBSD's implementation.
357 int saved_errno = errno;
358 errno = 0;
359 const unsigned char* old_bp = bp;
360 long n = strtol((const char*) bp, (char**) &bp, 10);
361 errno = saved_errno;
362 time_t t = n;
363 if (bp == old_bp || errno == ERANGE ||
364 ((long) t) != n) return NULL;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800365
Elliott Hughesd3627a42022-12-06 22:24:27 +0000366 if (localtime_r(&t, tm) == NULL) return NULL;
Elliott Hughes81baaf22018-02-28 15:22:48 -0800367
Elliott Hughesd3627a42022-12-06 22:24:27 +0000368 //int64_t i64;
369 //if (!(_conv_num64(&bp, &i64, 0, INT64_MAX)))
370 // return (NULL);
371 //if (!gmtime_r(&i64, tm))
372 // return (NULL);
373 fields = 0xffff; /* everything */
374 }
375 break;
376 case 'U': /* The week of year, beginning on sunday. */
377 case 'W': /* The week of year, beginning on monday. */
378 _LEGAL_ALT(_ALT_O);
379 /*
380 * XXX This is bogus, as we can not assume any valid
381 * information present in the tm structure at this
382 * point to calculate a real value, so just check the
383 * range for now.
384 */
385 if (!(_conv_num(&bp, &i, 0, 53)))
386 return (NULL);
387 break;
Elliott Hughes81baaf22018-02-28 15:22:48 -0800388
Elliott Hughesd3627a42022-12-06 22:24:27 +0000389 case 'w': /* The day of week, beginning on sunday. */
390 _LEGAL_ALT(_ALT_O);
391 if (!(_conv_num(&bp, &tm->tm_wday, 0, 6)))
392 return (NULL);
393 fields |= FIELD_TM_WDAY;
394 break;
Elliott Hughes81baaf22018-02-28 15:22:48 -0800395
Elliott Hughesd3627a42022-12-06 22:24:27 +0000396 case 'u': /* The day of week, monday = 1. */
397 _LEGAL_ALT(_ALT_O);
398 if (!(_conv_num(&bp, &i, 1, 7)))
399 return (NULL);
400 tm->tm_wday = i % 7;
401 fields |= FIELD_TM_WDAY;
402 continue;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800403
Elliott Hughesd3627a42022-12-06 22:24:27 +0000404 case 'g': /* The year corresponding to the ISO week
405 * number but without the century.
406 */
407 if (!(_conv_num(&bp, &i, 0, 99)))
408 return (NULL);
409 continue;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800410
Elliott Hughesd3627a42022-12-06 22:24:27 +0000411 case 'G': /* The year corresponding to the ISO week
412 * number with century.
413 */
414 do
415 bp++;
416 while (isdigit(*bp));
417 continue;
Elliott Hughesa1fb15b2019-03-26 19:07:40 -0700418
Elliott Hughesd3627a42022-12-06 22:24:27 +0000419 case 'V': /* The ISO 8601:1988 week number as decimal */
420 if (!(_conv_num(&bp, &i, 0, 53)))
421 return (NULL);
422 continue;
Elliott Hughesa1fb15b2019-03-26 19:07:40 -0700423
Elliott Hughesd3627a42022-12-06 22:24:27 +0000424 case 'Y': /* The year. */
425 _LEGAL_ALT(_ALT_E);
426 if (!(_conv_num(&bp, &i, 0, 9999)))
427 return (NULL);
Elliott Hughesa1fb15b2019-03-26 19:07:40 -0700428
Elliott Hughesd3627a42022-12-06 22:24:27 +0000429 relyear = -1;
430 tm->tm_year = i - TM_YEAR_BASE;
431 fields |= FIELD_TM_YEAR;
432 break;
Elliott Hughesa1fb15b2019-03-26 19:07:40 -0700433
Elliott Hughesd3627a42022-12-06 22:24:27 +0000434 case 'y': /* The year within the century (2 digits). */
435 _LEGAL_ALT(_ALT_E | _ALT_O);
436 if (!(_conv_num(&bp, &relyear, 0, 99)))
437 return (NULL);
438 break;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800439
Elliott Hughesd065c042020-09-01 19:02:44 -0700440 case 'Z':
441 tzset();
442 if (strncmp((const char *)bp, gmt, 3) == 0) {
443 tm->tm_isdst = 0;
444 tm->tm_gmtoff = 0;
445 tm->tm_zone = gmt;
446 bp += 3;
447 } else if (strncmp((const char *)bp, utc, 3) == 0) {
448 tm->tm_isdst = 0;
449 tm->tm_gmtoff = 0;
450 tm->tm_zone = utc;
451 bp += 3;
452 } else {
453 ep = _find_string(bp, &i,
454 (const char * const *)tzname,
455 NULL, 2);
456 if (ep == NULL)
457 return (NULL);
458
459 tm->tm_isdst = i;
460 tm->tm_gmtoff = -(timezone);
461 tm->tm_zone = tzname[i];
462 bp = ep;
463 }
464 continue;
465
466 case 'z':
467 /*
468 * We recognize all ISO 8601 formats:
469 * Z = Zulu time/UTC
470 * [+-]hhmm
471 * [+-]hh:mm
472 * [+-]hh
473 * We recognize all RFC-822/RFC-2822 formats:
474 * UT|GMT
475 * North American : UTC offsets
476 * E[DS]T = Eastern : -4 | -5
477 * C[DS]T = Central : -5 | -6
478 * M[DS]T = Mountain: -6 | -7
479 * P[DS]T = Pacific : -7 | -8
480 */
481 while (isspace(*bp))
482 bp++;
483
484 switch (*bp++) {
485 case 'G':
486 if (*bp++ != 'M')
487 return NULL;
488 /*FALLTHROUGH*/
489 case 'U':
490 if (*bp++ != 'T')
491 return NULL;
492 /*FALLTHROUGH*/
493 case 'Z':
494 tm->tm_isdst = 0;
495 tm->tm_gmtoff = 0;
496 tm->tm_zone = utc;
497 continue;
498 case '+':
499 neg = 0;
500 break;
501 case '-':
502 neg = 1;
503 break;
504 default:
505 --bp;
506 ep = _find_string(bp, &i, nast, NULL, 4);
507 if (ep != NULL) {
508 tm->tm_gmtoff = (-5 - i) * SECSPERHOUR;
509 tm->tm_zone = (char *)nast[i];
510 bp = ep;
511 continue;
512 }
513 ep = _find_string(bp, &i, nadt, NULL, 4);
514 if (ep != NULL) {
515 tm->tm_isdst = 1;
516 tm->tm_gmtoff = (-4 - i) * SECSPERHOUR;
517 tm->tm_zone = (char *)nadt[i];
518 bp = ep;
519 continue;
520 }
521 return NULL;
522 }
523 if (!isdigit(bp[0]) || !isdigit(bp[1]))
524 return NULL;
525 offs = ((bp[0]-'0') * 10 + (bp[1]-'0')) * SECSPERHOUR;
526 bp += 2;
527 if (*bp == ':')
528 bp++;
529 if (isdigit(*bp)) {
530 offs += (*bp++ - '0') * 10 * SECSPERMIN;
531 if (!isdigit(*bp))
532 return NULL;
533 offs += (*bp++ - '0') * SECSPERMIN;
534 }
535 if (neg)
536 offs = -offs;
537 tm->tm_isdst = 0; /* XXX */
538 tm->tm_gmtoff = offs;
539 tm->tm_zone = NULL; /* XXX */
540 continue;
541
Elliott Hughesd3627a42022-12-06 22:24:27 +0000542 /*
543 * Miscellaneous conversions.
544 */
545 case 'n': /* Any kind of white-space. */
546 case 't':
547 _LEGAL_ALT(0);
548 while (isspace(*bp))
549 bp++;
550 break;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800551
552
Elliott Hughesd3627a42022-12-06 22:24:27 +0000553 default: /* Unknown/unsupported conversion. */
554 return (NULL);
555 }
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800556
557
Elliott Hughesd3627a42022-12-06 22:24:27 +0000558 }
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800559
Elliott Hughesd3627a42022-12-06 22:24:27 +0000560 /*
561 * We need to evaluate the two digit year spec (%y)
562 * last as we can get a century spec (%C) at any time.
563 */
564 if (relyear != -1) {
565 if (century == TM_YEAR_BASE) {
566 if (relyear <= 68)
567 tm->tm_year = relyear + 2000 - TM_YEAR_BASE;
568 else
569 tm->tm_year = relyear + 1900 - TM_YEAR_BASE;
570 } else {
571 tm->tm_year = relyear + century - TM_YEAR_BASE;
572 }
573 fields |= FIELD_TM_YEAR;
574 }
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800575
Elliott Hughesd3627a42022-12-06 22:24:27 +0000576 /* Compute some missing values when possible. */
577 if (fields & FIELD_TM_YEAR) {
578 const int year = tm->tm_year + TM_YEAR_BASE;
579 const int *mon_lens = mon_lengths[isleap(year)];
580 if (!(fields & FIELD_TM_YDAY) &&
581 (fields & FIELD_TM_MON) && (fields & FIELD_TM_MDAY)) {
582 tm->tm_yday = tm->tm_mday - 1;
583 for (i = 0; i < tm->tm_mon; i++)
584 tm->tm_yday += mon_lens[i];
585 fields |= FIELD_TM_YDAY;
586 }
587 if (fields & FIELD_TM_YDAY) {
588 int days = tm->tm_yday;
589 if (!(fields & FIELD_TM_WDAY)) {
590 tm->tm_wday = EPOCH_WDAY +
591 ((year - EPOCH_YEAR) % DAYSPERWEEK) *
592 (DAYSPERNYEAR % DAYSPERWEEK) +
593 leaps_thru_end_of(year - 1) -
594 leaps_thru_end_of(EPOCH_YEAR - 1) +
595 tm->tm_yday;
596 tm->tm_wday %= DAYSPERWEEK;
597 if (tm->tm_wday < 0)
598 tm->tm_wday += DAYSPERWEEK;
599 }
600 if (!(fields & FIELD_TM_MON)) {
601 tm->tm_mon = 0;
602 while (tm->tm_mon < MONSPERYEAR && days >= mon_lens[tm->tm_mon])
603 days -= mon_lens[tm->tm_mon++];
604 }
605 if (!(fields & FIELD_TM_MDAY))
606 tm->tm_mday = days + 1;
607 }
608 }
609
610 return ((char *)bp);
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800611}
612
Elliott Hughesd3627a42022-12-06 22:24:27 +0000613
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800614static int
615_conv_num(const unsigned char **buf, int *dest, int llim, int ulim)
616{
Elliott Hughesd065c042020-09-01 19:02:44 -0700617 int result = 0;
618 int rulim = ulim;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800619
Elliott Hughesd065c042020-09-01 19:02:44 -0700620 if (**buf < '0' || **buf > '9')
621 return (0);
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800622
Elliott Hughesd065c042020-09-01 19:02:44 -0700623 /* we use rulim to break out of the loop when we run out of digits */
624 do {
625 result *= 10;
626 result += *(*buf)++ - '0';
627 rulim /= 10;
628 } while ((result * 10 <= ulim) && rulim && **buf >= '0' && **buf <= '9');
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800629
Elliott Hughesd065c042020-09-01 19:02:44 -0700630 if (result < llim || result > ulim)
631 return (0);
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800632
Elliott Hughesd065c042020-09-01 19:02:44 -0700633 *dest = result;
634 return (1);
635}
636
Elliott Hughesd3627a42022-12-06 22:24:27 +0000637static int
638_conv_num64(const unsigned char **buf, int64_t *dest, int64_t llim, int64_t ulim)
639{
640 int result = 0;
641 int64_t rulim = ulim;
642
643 if (**buf < '0' || **buf > '9')
644 return (0);
645
646 /* we use rulim to break out of the loop when we run out of digits */
647 do {
648 result *= 10;
649 result += *(*buf)++ - '0';
650 rulim /= 10;
651 } while ((result * 10 <= ulim) && rulim && **buf >= '0' && **buf <= '9');
652
653 if (result < llim || result > ulim)
654 return (0);
655
656 *dest = result;
657 return (1);
658}
659
Elliott Hughesd065c042020-09-01 19:02:44 -0700660static const u_char *
661_find_string(const u_char *bp, int *tgt, const char * const *n1,
662 const char * const *n2, int c)
663{
664 int i;
665 unsigned int len;
666
667 /* check full name - then abbreviated ones */
668 for (; n1 != NULL; n1 = n2, n2 = NULL) {
669 for (i = 0; i < c; i++, n1++) {
670 len = strlen(*n1);
671 if (strncasecmp(*n1, (const char *)bp, len) == 0) {
672 *tgt = i;
673 return bp + len;
674 }
675 }
676 }
677
678 /* Nothing matched */
679 return NULL;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800680}
Elliott Hughes3376c232018-02-13 23:14:12 -0800681
Elliott Hughesd3627a42022-12-06 22:24:27 +0000682static int
683leaps_thru_end_of(const int y)
684{
685 return (y >= 0) ? (y / 4 - y / 100 + y / 400) :
686 -(leaps_thru_end_of(-(y + 1)) + 1);
Elliott Hughes3376c232018-02-13 23:14:12 -0800687}