Complete Citrus import. Import message catalog implement from
[dragonfly.git] / lib / libc / stdtime / strptime.c
1 /*      $NetBSD: src/lib/libc/time/strptime.c,v 1.22 2000/12/20 20:56:34 christos Exp $ */
2 /*      $DragonFly: src/lib/libc/stdtime/strptime.c,v 1.4 2005/04/21 16:36:35 joerg 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 <locale.h>
42 #include <string.h>
43 #include <time.h>
44 #include "private.h"
45 #include "tzfile.h"
46
47 #define _ctloc(x)               (_CurrentTimeLocale->x)
48
49 /*
50  * We do not implement alternate representations. However, we always
51  * check whether a given modifier is allowed for a certain conversion.
52  */
53 #define ALT_E                   0x01
54 #define ALT_O                   0x02
55 #define LEGAL_ALT(x)            { if (alt_format & ~(x)) return (0); }
56
57 static  int conv_num __P((const unsigned char **, int *, int, int));
58
59 char *
60 strptime(const char *buf, const char *fmt, struct tm *tm)
61 {
62         unsigned char c;
63         const unsigned char *bp;
64         size_t len = 0;
65         int alt_format, i, split_year = 0;
66
67         bp = (const u_char *)buf;
68
69         while ((c = *fmt) != '\0') {
70                 /* Clear `alternate' modifier prior to new conversion. */
71                 alt_format = 0;
72
73                 /* Eat up white-space. */
74                 if (isspace(c)) {
75                         while (isspace(*bp))
76                                 bp++;
77
78                         fmt++;
79                         continue;
80                 }
81                                 
82                 if ((c = *fmt++) != '%')
83                         goto literal;
84
85
86 again:          switch (c = *fmt++) {
87                 case '%':       /* "%%" is converted to "%". */
88 literal:
89                         if (c != *bp++)
90                                 return (0);
91                         break;
92
93                 /*
94                  * "Alternative" modifiers. Just set the appropriate flag
95                  * and start over again.
96                  */
97                 case 'E':       /* "%E?" alternative conversion modifier. */
98                         LEGAL_ALT(0);
99                         alt_format |= ALT_E;
100                         goto again;
101
102                 case 'O':       /* "%O?" alternative conversion modifier. */
103                         LEGAL_ALT(0);
104                         alt_format |= ALT_O;
105                         goto again;
106                         
107                 /*
108                  * "Complex" conversion rules, implemented through recursion.
109                  */
110                 case 'c':       /* Date and time, using the locale's format. */
111                         LEGAL_ALT(ALT_E);
112                         if (!(bp = (const u_char *)strptime((const char *)bp,
113                             _ctloc(d_t_fmt), tm)))
114                                 return (0);
115                         break;
116
117                 case 'D':       /* The date as "%m/%d/%y". */
118                         LEGAL_ALT(0);
119                         if (!(bp = (const u_char *) strptime((const char *)bp,
120                             "%m/%d/%y", tm)))
121                                 return (0);
122                         break;
123
124                 case 'R':       /* The time as "%H:%M". */
125                         LEGAL_ALT(0);
126                         if (!(bp = (const u_char *)strptime((const char *)bp,
127                             "%H:%M", tm)))
128                                 return (0);
129                         break;
130
131                 case 'r':       /* The time in 12-hour clock representation. */
132                         LEGAL_ALT(0);
133                         if (!(bp = (const u_char *)strptime((const char *)bp,
134                             _ctloc(t_fmt_ampm), tm)))
135                                 return (0);
136                         break;
137
138                 case 'T':       /* The time as "%H:%M:%S". */
139                         LEGAL_ALT(0);
140                         if (!(bp = (const u_char *)strptime((const char *)bp,
141                             "%H:%M:%S", tm)))
142                                 return (0);
143                         break;
144
145                 case 'X':       /* The time, using the locale's format. */
146                         LEGAL_ALT(ALT_E);
147                         if (!(bp = (const u_char *)strptime((const char *)bp,
148                             _ctloc(t_fmt), tm)))
149                                 return (0);
150                         break;
151
152                 case 'x':       /* The date, using the locale's format. */
153                         LEGAL_ALT(ALT_E);
154                         if (!(bp = (const u_char *)strptime((const char *)bp,
155                             _ctloc(d_fmt), tm)))
156                                 return (0);
157                         break;
158
159                 /*
160                  * "Elementary" conversion rules.
161                  */
162                 case 'A':       /* The day of week, using the locale's form. */
163                 case 'a':
164                         LEGAL_ALT(0);
165                         for (i = 0; i < 7; i++) {
166                                 /* Full name. */
167                                 len = strlen(_ctloc(day[i]));
168                                 if (strncasecmp(_ctloc(day[i]),
169                                     (const char *)bp, len) == 0)
170                                         break;
171
172                                 /* Abbreviated name. */
173                                 len = strlen(_ctloc(abday[i]));
174                                 if (strncasecmp(_ctloc(abday[i]),
175                                     (const char *)bp, len) == 0)
176                                         break;
177                         }
178
179                         /* Nothing matched. */
180                         if (i == 7)
181                                 return (0);
182
183                         tm->tm_wday = i;
184                         bp += len;
185                         break;
186
187                 case 'B':       /* The month, using the locale's form. */
188                 case 'b':
189                 case 'h':
190                         LEGAL_ALT(0);
191                         for (i = 0; i < 12; i++) {
192                                 /* Full name. */
193                                 len = strlen(_ctloc(mon[i]));
194                                 if (strncasecmp(_ctloc(mon[i]),
195                                     (const char *)bp, len) == 0)
196                                         break;
197
198                                 /* Abbreviated name. */
199                                 len = strlen(_ctloc(abmon[i]));
200                                 if (strncasecmp(_ctloc(abmon[i]),
201                                     (const char *)bp, len) == 0)
202                                         break;
203                         }
204
205                         /* Nothing matched. */
206                         if (i == 12)
207                                 return (0);
208
209                         tm->tm_mon = i;
210                         bp += len;
211                         break;
212
213                 case 'C':       /* The century number. */
214                         LEGAL_ALT(ALT_E);
215                         if (!(conv_num(&bp, &i, 0, 99)))
216                                 return (0);
217
218                         if (split_year) {
219                                 tm->tm_year = (tm->tm_year % 100) + (i * 100);
220                         } else {
221                                 tm->tm_year = i * 100;
222                                 split_year = 1;
223                         }
224                         break;
225
226                 case 'd':       /* The day of month. */
227                 case 'e':
228                         LEGAL_ALT(ALT_O);
229                         if (!(conv_num(&bp, &tm->tm_mday, 1, 31)))
230                                 return (0);
231                         break;
232
233                 case 'k':       /* The hour (24-hour clock representation). */
234                         LEGAL_ALT(0);
235                         /* FALLTHROUGH */
236                 case 'H':
237                         LEGAL_ALT(ALT_O);
238                         if (!(conv_num(&bp, &tm->tm_hour, 0, 23)))
239                                 return (0);
240                         break;
241
242                 case 'l':       /* The hour (12-hour clock representation). */
243                         LEGAL_ALT(0);
244                         /* FALLTHROUGH */
245                 case 'I':
246                         LEGAL_ALT(ALT_O);
247                         if (!(conv_num(&bp, &tm->tm_hour, 1, 12)))
248                                 return (0);
249                         if (tm->tm_hour == 12)
250                                 tm->tm_hour = 0;
251                         break;
252
253                 case 'j':       /* The day of year. */
254                         LEGAL_ALT(0);
255                         if (!(conv_num(&bp, &i, 1, 366)))
256                                 return (0);
257                         tm->tm_yday = i - 1;
258                         break;
259
260                 case 'M':       /* The minute. */
261                         LEGAL_ALT(ALT_O);
262                         if (!(conv_num(&bp, &tm->tm_min, 0, 59)))
263                                 return (0);
264                         break;
265
266                 case 'm':       /* The month. */
267                         LEGAL_ALT(ALT_O);
268                         if (!(conv_num(&bp, &i, 1, 12)))
269                                 return (0);
270                         tm->tm_mon = i - 1;
271                         break;
272
273                 case 'p':       /* The locale's equivalent of AM/PM. */
274                         LEGAL_ALT(0);
275                         /* AM? */
276                         if (strcasecmp(_ctloc(am_pm[0]),
277                             (const char *)bp) == 0) {
278                                 if (tm->tm_hour > 11)
279                                         return (0);
280
281                                 bp += strlen(_ctloc(am_pm[0]));
282                                 break;
283                         }
284                         /* PM? */
285                         else if (strcasecmp(_ctloc(am_pm[1]),
286                             (const char *)bp) == 0) {
287                                 if (tm->tm_hour > 11)
288                                         return (0);
289
290                                 tm->tm_hour += 12;
291                                 bp += strlen(_ctloc(am_pm[1]));
292                                 break;
293                         }
294
295                         /* Nothing matched. */
296                         return (0);
297
298                 case 'S':       /* The seconds. */
299                         LEGAL_ALT(ALT_O);
300                         if (!(conv_num(&bp, &tm->tm_sec, 0, 61)))
301                                 return (0);
302                         break;
303
304                 case 'U':       /* The week of year, beginning on sunday. */
305                 case 'W':       /* The week of year, beginning on monday. */
306                         LEGAL_ALT(ALT_O);
307                         /*
308                          * XXX This is bogus, as we can not assume any valid
309                          * information present in the tm structure at this
310                          * point to calculate a real value, so just check the
311                          * range for now.
312                          */
313                          if (!(conv_num(&bp, &i, 0, 53)))
314                                 return (0);
315                          break;
316
317                 case 'w':       /* The day of week, beginning on sunday. */
318                         LEGAL_ALT(ALT_O);
319                         if (!(conv_num(&bp, &tm->tm_wday, 0, 6)))
320                                 return (0);
321                         break;
322
323                 case 'Y':       /* The year. */
324                         LEGAL_ALT(ALT_E);
325                         if (!(conv_num(&bp, &i, 0, 9999)))
326                                 return (0);
327
328                         tm->tm_year = i - TM_YEAR_BASE;
329                         break;
330
331                 case 'y':       /* The year within 100 years of the epoch. */
332                         LEGAL_ALT(ALT_E | ALT_O);
333                         if (!(conv_num(&bp, &i, 0, 99)))
334                                 return (0);
335
336                         if (split_year) {
337                                 tm->tm_year = ((tm->tm_year / 100) * 100) + i;
338                                 break;
339                         }
340                         split_year = 1;
341                         if (i <= 68)
342                                 tm->tm_year = i + 2000 - TM_YEAR_BASE;
343                         else
344                                 tm->tm_year = i + 1900 - TM_YEAR_BASE;
345                         break;
346
347                 /*
348                  * Miscellaneous conversions.
349                  */
350                 case 'n':       /* Any kind of white-space. */
351                 case 't':
352                         LEGAL_ALT(0);
353                         while (isspace(*bp))
354                                 bp++;
355                         break;
356
357
358                 default:        /* Unknown/unsupported conversion. */
359                         return (0);
360                 }
361
362
363         }
364
365         /* LINTED functional specification */
366         return ((char *)bp);
367 }
368
369
370 static int
371 conv_num(const unsigned char **buf, int *dest, int llim, int ulim)
372 {
373         int result = 0;
374
375         /* The limit also determines the number of valid digits. */
376         int rulim = ulim;
377
378         if (**buf < '0' || **buf > '9')
379                 return (0);
380
381         do {
382                 result *= 10;
383                 result += *(*buf)++ - '0';
384                 rulim /= 10;
385         } while ((result * 10 <= ulim) && rulim && **buf >= '0' && **buf <= '9');
386
387         if (result < llim || result > ulim)
388                 return (0);
389
390         *dest = result;
391         return (1);
392 }