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