]> git.proxmox.com Git - mirror_edk2.git/blame - StdLib/LibC/Time/strptime.c
EmbeddedPkg: Extend NvVarStoreFormattedLib LIBRARY_CLASS
[mirror_edk2.git] / StdLib / LibC / Time / strptime.c
CommitLineData
53e1e5c6 1/** @file \r
2 strptime implementation\r
3\r
4 Copyright (c) 2011, Intel Corporation. All rights reserved.<BR>\r
5 This program and the accompanying materials are licensed and made available under\r
6 the terms and conditions of the BSD License that accompanies this distribution.\r
7 The full text of the license may be found at\r
8 http://opensource.org/licenses/bsd-license.php.\r
9\r
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
12\r
13 * Copyright (c) 1997, 1998, 2005, 2008 The NetBSD Foundation, Inc.\r
14 * All rights reserved.\r
15 *\r
16 * This code was contributed to The NetBSD Foundation by Klaus Klein.\r
17 * Heavily optimised by David Laight\r
18 *\r
19 * Redistribution and use in source and binary forms, with or without\r
20 * modification, are permitted provided that the following conditions\r
21 * are met:\r
22 * 1. Redistributions of source code must retain the above copyright\r
23 * notice, this list of conditions and the following disclaimer.\r
24 * 2. Redistributions in binary form must reproduce the above copyright\r
25 * notice, this list of conditions and the following disclaimer in the\r
26 * documentation and/or other materials provided with the distribution.\r
27 *\r
28 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS\r
29 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED\r
30 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\r
31 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS\r
32 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\r
33 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\r
34 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r
35 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\r
36 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\r
37 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\r
38 * POSSIBILITY OF SUCH DAMAGE.\r
39\r
40 $NetBSD: strptime.c,v 1.28 2008/04/28 20:23:01 martin Exp $\r
41\r
42**/\r
43\r
44#if defined(_MSC_VER) /* Handle Microsoft VC++ compiler specifics. */\r
45 #pragma warning ( disable : 4244 )\r
46 #pragma warning ( disable : 4018 )\r
47#endif\r
48\r
49#include <LibConfig.h>\r
50\r
51#include <sys/cdefs.h>\r
52\r
53#include "namespace.h"\r
54#include <time.h>\r
55#include "tzfile.h"\r
56#include <TimeVals.h>\r
57#include <fcntl.h>\r
58\r
59#include <sys/localedef.h>\r
60\r
61#include <ctype.h>\r
62#include <locale.h>\r
63#include <string.h>\r
64\r
65#ifdef __weak_alias\r
66__weak_alias(strptime,_strptime)\r
67#endif\r
68\r
69#define _ctloc(x) (_CurrentTimeLocale->x)\r
70\r
71/*\r
72 * We do not implement alternate representations. However, we always\r
73 * check whether a given modifier is allowed for a certain conversion.\r
74 */\r
75#define ALT_E 0x01\r
76#define ALT_O 0x02\r
77#define LEGAL_ALT(x) { if (alt_format & ~(x)) return NULL; }\r
78\r
79static const unsigned char *conv_num(const unsigned char *, int *, unsigned int, unsigned int);\r
80static const unsigned char *find_string(const unsigned char *, int *, const char * const *,\r
81 const char * const *, int);\r
82\r
83\r
84char *\r
85strptime(const char *buf, const char *fmt, struct tm *tm)\r
86{\r
87 unsigned char c;\r
88 const unsigned char *bp;\r
89 int alt_format, i, split_year = 0;\r
90 const char *new_fmt;\r
91\r
92 bp = (const unsigned char *)buf;\r
93\r
94 while (bp != NULL && (c = *fmt++) != '\0') {\r
95 /* Clear `alternate' modifier prior to new conversion. */\r
96 alt_format = 0;\r
97 i = 0;\r
98\r
99 /* Eat up white-space. */\r
100 if (isspace(c)) {\r
101 while (isspace(*bp))\r
102 bp++;\r
103 continue;\r
104 }\r
105\r
106 if (c != '%')\r
107 goto literal;\r
108\r
109\r
110again: switch (c = *fmt++) {\r
111 case '%': /* "%%" is converted to "%". */\r
112literal:\r
113 if (c != *bp++)\r
114 return NULL;\r
115 LEGAL_ALT(0);\r
116 continue;\r
117\r
118 /*\r
119 * "Alternative" modifiers. Just set the appropriate flag\r
120 * and start over again.\r
121 */\r
122 case 'E': /* "%E?" alternative conversion modifier. */\r
123 LEGAL_ALT(0);\r
124 alt_format |= ALT_E;\r
125 goto again;\r
126\r
127 case 'O': /* "%O?" alternative conversion modifier. */\r
128 LEGAL_ALT(0);\r
129 alt_format |= ALT_O;\r
130 goto again;\r
131\r
132 /*\r
133 * "Complex" conversion rules, implemented through recursion.\r
134 */\r
135 case 'c': /* Date and time, using the locale's format. */\r
136 new_fmt = _ctloc(d_t_fmt);\r
137 goto recurse;\r
138\r
139 case 'D': /* The date as "%m/%d/%y". */\r
140 new_fmt = "%m/%d/%y";\r
141 LEGAL_ALT(0);\r
142 goto recurse;\r
143\r
144 case 'F': /* The date as "%Y-%m-%d". */\r
145 new_fmt = "%Y-%m-%d";\r
146 LEGAL_ALT(0);\r
147 goto recurse;\r
148\r
149 case 'R': /* The time as "%H:%M". */\r
150 new_fmt = "%H:%M";\r
151 LEGAL_ALT(0);\r
152 goto recurse;\r
153\r
154 case 'r': /* The time in 12-hour clock representation. */\r
155 new_fmt =_ctloc(t_fmt_ampm);\r
156 LEGAL_ALT(0);\r
157 goto recurse;\r
158\r
159 case 'T': /* The time as "%H:%M:%S". */\r
160 new_fmt = "%H:%M:%S";\r
161 LEGAL_ALT(0);\r
162 goto recurse;\r
163\r
164 case 'X': /* The time, using the locale's format. */\r
165 new_fmt =_ctloc(t_fmt);\r
166 goto recurse;\r
167\r
168 case 'x': /* The date, using the locale's format. */\r
169 new_fmt =_ctloc(d_fmt);\r
170 recurse:\r
171 bp = (const unsigned char *)strptime((const char *)bp,\r
172 new_fmt, tm);\r
173 LEGAL_ALT(ALT_E);\r
174 continue;\r
175\r
176 /*\r
177 * "Elementary" conversion rules.\r
178 */\r
179 case 'A': /* The day of week, using the locale's form. */\r
180 case 'a':\r
181 bp = find_string(bp, &tm->tm_wday, _ctloc(day),\r
182 _ctloc(abday), 7);\r
183 LEGAL_ALT(0);\r
184 continue;\r
185\r
186 case 'B': /* The month, using the locale's form. */\r
187 case 'b':\r
188 case 'h':\r
189 bp = find_string(bp, &tm->tm_mon, _ctloc(mon),\r
190 _ctloc(abmon), 12);\r
191 LEGAL_ALT(0);\r
192 continue;\r
193\r
194 case 'C': /* The century number. */\r
195 i = 20;\r
196 bp = conv_num(bp, &i, 0, 99);\r
197\r
198 i = i * 100 - TM_YEAR_BASE;\r
199 if (split_year)\r
200 i += tm->tm_year % 100;\r
201 split_year = 1;\r
202 tm->tm_year = i;\r
203 LEGAL_ALT(ALT_E);\r
204 continue;\r
205\r
206 case 'd': /* The day of month. */\r
207 case 'e':\r
208 bp = conv_num(bp, &tm->tm_mday, 1, 31);\r
209 LEGAL_ALT(ALT_O);\r
210 continue;\r
211\r
212 case 'k': /* The hour (24-hour clock representation). */\r
213 LEGAL_ALT(0);\r
214 /* FALLTHROUGH */\r
215 case 'H':\r
216 bp = conv_num(bp, &tm->tm_hour, 0, 23);\r
217 LEGAL_ALT(ALT_O);\r
218 continue;\r
219\r
220 case 'l': /* The hour (12-hour clock representation). */\r
221 LEGAL_ALT(0);\r
222 /* FALLTHROUGH */\r
223 case 'I':\r
224 bp = conv_num(bp, &tm->tm_hour, 1, 12);\r
225 if (tm->tm_hour == 12)\r
226 tm->tm_hour = 0;\r
227 LEGAL_ALT(ALT_O);\r
228 continue;\r
229\r
230 case 'j': /* The day of year. */\r
231 i = 1;\r
232 bp = conv_num(bp, &i, 1, 366);\r
233 tm->tm_yday = i - 1;\r
234 LEGAL_ALT(0);\r
235 continue;\r
236\r
237 case 'M': /* The minute. */\r
238 bp = conv_num(bp, &tm->tm_min, 0, 59);\r
239 LEGAL_ALT(ALT_O);\r
240 continue;\r
241\r
242 case 'm': /* The month. */\r
243 i = 1;\r
244 bp = conv_num(bp, &i, 1, 12);\r
245 tm->tm_mon = i - 1;\r
246 LEGAL_ALT(ALT_O);\r
247 continue;\r
248\r
249 case 'p': /* The locale's equivalent of AM/PM. */\r
250 bp = find_string(bp, &i, _ctloc(am_pm), NULL, 2);\r
251 if (tm->tm_hour > 11)\r
252 return NULL;\r
253 tm->tm_hour += i * 12;\r
254 LEGAL_ALT(0);\r
255 continue;\r
256\r
257 case 'S': /* The seconds. */\r
258 bp = conv_num(bp, &tm->tm_sec, 0, 61);\r
259 LEGAL_ALT(ALT_O);\r
260 continue;\r
261\r
262 case 'U': /* The week of year, beginning on sunday. */\r
263 case 'W': /* The week of year, beginning on monday. */\r
264 /*\r
265 * XXX This is bogus, as we can not assume any valid\r
266 * information present in the tm structure at this\r
267 * point to calculate a real value, so just check the\r
268 * range for now.\r
269 */\r
270 bp = conv_num(bp, &i, 0, 53);\r
271 LEGAL_ALT(ALT_O);\r
272 continue;\r
273\r
274 case 'w': /* The day of week, beginning on sunday. */\r
275 bp = conv_num(bp, &tm->tm_wday, 0, 6);\r
276 LEGAL_ALT(ALT_O);\r
277 continue;\r
278\r
279 case 'Y': /* The year. */\r
280 i = TM_YEAR_BASE; /* just for data sanity... */\r
281 bp = conv_num(bp, &i, 0, 9999);\r
282 tm->tm_year = i - TM_YEAR_BASE;\r
283 LEGAL_ALT(ALT_E);\r
284 continue;\r
285\r
286 case 'y': /* The year within 100 years of the epoch. */\r
287 /* LEGAL_ALT(ALT_E | ALT_O); */\r
288 bp = conv_num(bp, &i, 0, 99);\r
289\r
290 if (split_year)\r
291 /* preserve century */\r
292 i += (tm->tm_year / 100) * 100;\r
293 else {\r
294 split_year = 1;\r
295 if (i <= 68)\r
296 i = i + 2000 - TM_YEAR_BASE;\r
297 else\r
298 i = i + 1900 - TM_YEAR_BASE;\r
299 }\r
300 tm->tm_year = i;\r
301 continue;\r
302\r
303 case 'Z':\r
304 tzset();\r
305 if (strncmp((const char *)bp, gmt, 3) == 0) {\r
306 tm->tm_isdst = 0;\r
307#ifdef TM_GMTOFF\r
308 tm->TM_GMTOFF = 0;\r
309#endif\r
310#ifdef TM_ZONE\r
311 tm->TM_ZONE = gmt;\r
312#endif\r
313 bp += 3;\r
314 } else {\r
315 const unsigned char *ep;\r
316\r
317 ep = find_string(bp, &i,\r
318 (const char * const *)tzname,\r
319 NULL, 2);\r
320 if (ep != NULL) {\r
321 tm->tm_isdst = i;\r
322#ifdef TM_GMTOFF\r
323 tm->TM_GMTOFF = -(timezone);\r
324#endif\r
325#ifdef TM_ZONE\r
326 tm->TM_ZONE = tzname[i];\r
327#endif\r
328 }\r
329 bp = ep;\r
330 }\r
331 continue;\r
332\r
333 /*\r
334 * Miscellaneous conversions.\r
335 */\r
336 case 'n': /* Any kind of white-space. */\r
337 case 't':\r
338 while (isspace(*bp))\r
339 bp++;\r
340 LEGAL_ALT(0);\r
341 continue;\r
342\r
343\r
344 default: /* Unknown/unsupported conversion. */\r
345 return NULL;\r
346 }\r
347 }\r
348\r
349 return __UNCONST(bp);\r
350}\r
351\r
352\r
353static const unsigned char *\r
354conv_num(const unsigned char *buf, int *dest, unsigned int llim, unsigned int ulim)\r
355{\r
356 unsigned int result = 0;\r
357 unsigned char ch;\r
358\r
359 /* The limit also determines the number of valid digits. */\r
360 unsigned int rulim = ulim;\r
361\r
362 ch = *buf;\r
363 if (ch < '0' || ch > '9')\r
364 return NULL;\r
365\r
366 do {\r
367 result *= 10;\r
368 result += ch - '0';\r
369 rulim /= 10;\r
370 ch = *++buf;\r
371 } while ((result * 10 <= ulim) && rulim && ch >= '0' && ch <= '9');\r
372\r
373 if (result < llim || result > ulim)\r
374 return NULL;\r
375\r
376 *dest = result;\r
377 return buf;\r
378}\r
379\r
380static const unsigned char *\r
381find_string(const unsigned char *bp, int *tgt, const char * const *n1,\r
382 const char * const *n2, int c)\r
383{\r
384 int i;\r
385 size_t len;\r
386\r
387 /* check full name - then abbreviated ones */\r
388 for (; n1 != NULL; n1 = n2, n2 = NULL) {\r
389 for (i = 0; i < c; i++, n1++) {\r
390 len = strlen(*n1);\r
391 if (strncasecmp(*n1, (const char *)bp, len) == 0) {\r
392 *tgt = i;\r
393 return bp + len;\r
394 }\r
395 }\r
396 }\r
397\r
398 /* Nothing matched */\r
399 return NULL;\r
400}\r