OSDN Git Service

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