strptime.c revision 1.3 1 /* $NetBSD: strptime.c,v 1.3 1997/05/25 19:26:43 kleink Exp $ */
2
3 /*-
4 * Copyright (c) 1997 The NetBSD Foundation, Inc.
5 * 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.
17 * 3. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software developed by the NetBSD
20 * Foundation, Inc. and its contributors.
21 * 4. Neither the name of The NetBSD Foundation nor the names of its
22 * contributors may be used to endorse or promote products derived
23 * from this software without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
26 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
29 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 * POSSIBILITY OF SUCH DAMAGE.
36 */
37
38 #if defined(LIBC_SCCS) && !defined(lint)
39 static char rcsid[] = "$NetBSD: strptime.c,v 1.3 1997/05/25 19:26:43 kleink Exp $";
40 #endif
41
42 #include <sys/localedef.h>
43 #include <ctype.h>
44 #include <locale.h>
45 #include <string.h>
46 #include <time.h>
47
48 #if defined (__STDC__) && (__STDC__)
49 #define _ctloc(x) (_CurrentTimeLocale-> ## x)
50 #else
51 #define _ctloc(x) (_CurrentTimeLocale->/**/x)
52 #endif
53
54 /*
55 * We do not implement alternate representations. However, we always
56 * check whether a given modifier is allowed for a certain conversion.
57 */
58 #define _ALT_E 0x01
59 #define _ALT_O 0x02
60 #define _LEGAL_ALT(x) { if (alt_format & ~(x)) return (0); }
61
62
63 static int _conv_num __P((const char **, int *, int, int));
64
65
66 char *
67 strptime(buf, fmt, tm)
68 const char *buf, *fmt;
69 struct tm *tm;
70 {
71 char c;
72 const char *bp;
73 int alt_format, i, len;
74
75 bp = buf;
76
77 while ((c = *fmt) != '\0') {
78 /* Clear `alternate' modifier prior to new conversion. */
79 alt_format = 0;
80
81 /* Eat up white-space. */
82 if (isspace(c)) {
83 while (isspace(*bp))
84 bp++;
85
86 fmt++;
87 continue;
88 }
89
90 if ((c = *fmt++) != '%')
91 goto literal;
92
93
94 again: switch (c = *fmt++) {
95 case '%': /* "%%" is converted to "%". */
96 literal:
97 if (c != *bp++)
98 return (0);
99
100 break;
101
102 /*
103 * "Alternative" modifiers. Just set the appropriate flag
104 * and start over again.
105 */
106 case 'E': /* "%E?" alternative conversion modifier. */
107 _LEGAL_ALT(0);
108 alt_format |= _ALT_E;
109 goto again;
110
111 case 'O': /* "%O?" alternative conversion modifier. */
112 _LEGAL_ALT(0);
113 alt_format |= _ALT_O;
114 goto again;
115
116 /*
117 * "Complex" conversion rules, implemented through recursion.
118 */
119 case 'c': /* Date and time, using the locale's format. */
120 _LEGAL_ALT(_ALT_E);
121 if (!(bp = strptime(bp, _ctloc(d_t_fmt), tm)))
122 return (0);
123 break;
124
125 case 'D': /* The date as "%m/%d/%y". */
126 _LEGAL_ALT(0);
127 if (!(bp = strptime(bp, "%m/%d/%y", tm)))
128 return (0);
129 break;
130
131 case 'R': /* The time as "%H:%M". */
132 _LEGAL_ALT(0);
133 if (!(bp = strptime(bp, "%H:%M", tm)))
134 return (0);
135 break;
136
137 case 'r': /* The time in 12-hour clock representation. */
138 _LEGAL_ALT(0);
139 if (!(bp = strptime(bp, _ctloc(t_fmt_ampm), tm)))
140 return (0);
141 break;
142
143 case 'T': /* The time as "%H:%M:%S". */
144 _LEGAL_ALT(0);
145 if (!(bp = strptime(bp, "%H:%M:%S", tm)))
146 return (0);
147 break;
148
149 case 'X': /* The time, using the locale's format. */
150 _LEGAL_ALT(_ALT_E);
151 if (!(bp = strptime(bp, _ctloc(t_fmt), tm)))
152 return (0);
153 break;
154
155 case 'x': /* The date, using the locale's format. */
156 _LEGAL_ALT(_ALT_E);
157 if (!(bp = strptime(bp, _ctloc(d_fmt), tm)))
158 return (0);
159 break;
160
161 /*
162 * "Elementary" conversion rules.
163 */
164 case 'A': /* The day of week, using the locale's form. */
165 case 'a':
166 _LEGAL_ALT(0);
167 for (i = 0; i < 7; i++) {
168 /* Full name. */
169 len = strlen(_ctloc(day[i]));
170 if (strncmp(_ctloc(day[i]), bp, len) == 0)
171 break;
172
173 /* Abbreviated name. */
174 len = strlen(_ctloc(abday[i]));
175 if (strncmp(_ctloc(abday[i]), 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 (strncmp(_ctloc(mon[i]), bp, len) == 0)
195 break;
196
197 /* Abbreviated name. */
198 len = strlen(_ctloc(abmon[i]));
199 if (strncmp(_ctloc(abmon[i]), bp, len) == 0)
200 break;
201 }
202
203 /* Nothing matched. */
204 if (i == 12)
205 return (0);
206
207 tm->tm_mon = i;
208 bp += len;
209 break;
210
211 case 'C': /* The century number. */
212 _LEGAL_ALT(_ALT_E);
213 if (!(_conv_num(&bp, &i, 0, 99)))
214 return (0);
215
216 tm->tm_year = i * 100;
217 break;
218
219 case 'd': /* The day of month. */
220 case 'e':
221 _LEGAL_ALT(_ALT_O);
222 if (!(_conv_num(&bp, &tm->tm_mday, 1, 31)))
223 return (0);
224 break;
225
226 case 'k': /* The hour (24-hour clock representation). */
227 _LEGAL_ALT(0);
228 /* FALLTHROUGH */
229 case 'H':
230 _LEGAL_ALT(_ALT_O);
231 if (!(_conv_num(&bp, &tm->tm_hour, 0, 23)))
232 return (0);
233 break;
234
235 case 'l': /* The hour (12-hour clock representation). */
236 _LEGAL_ALT(0);
237 /* FALLTHROUGH */
238 case 'I':
239 _LEGAL_ALT(_ALT_O);
240 if (!(_conv_num(&bp, &tm->tm_hour, 0, 11)))
241 return (0);
242 break;
243
244 case 'j': /* The day of year. */
245 _LEGAL_ALT(0);
246 if (!(_conv_num(&bp, &tm->tm_yday, 1, 366)))
247 return (0);
248 break;
249
250 case 'M': /* The minute. */
251 _LEGAL_ALT(_ALT_O);
252 if (!(_conv_num(&bp, &tm->tm_min, 0, 59)))
253 return (0);
254 break;
255
256 case 'm': /* The month. */
257 _LEGAL_ALT(_ALT_O);
258 if (!(_conv_num(&bp, &tm->tm_mon, 1, 12)))
259 return (0);
260 break;
261
262 case 'p': /* The locale's equivalent of AM/PM. */
263 _LEGAL_ALT(0);
264 /* AM? */
265 if (strcmp(_ctloc(am_pm[0]), bp) == 0) {
266 if (tm->tm_hour > 12) /* i.e., 13:00 AM ?! */
267 return (0);
268 else if (tm->tm_hour == 12)
269 tm->tm_hour = 0;
270
271 bp += strlen(_ctloc(am_pm[0]));
272 break;
273 }
274 /* PM? */
275 else if (strcmp(_ctloc(am_pm[1]), bp) == 0) {
276 if (tm->tm_hour > 12) /* i.e., 13:00 PM ?! */
277 return (0);
278 else if (tm->tm_hour < 12)
279 tm->tm_hour += 12;
280
281 bp += strlen(_ctloc(am_pm[1]));
282 break;
283 }
284
285 /* Nothing matched. */
286 return (0);
287
288 case 'S': /* The seconds. */
289 _LEGAL_ALT(_ALT_O);
290 if (!(_conv_num(&bp, &tm->tm_sec, 1, 61)))
291 return (0);
292 break;
293
294 case 'U': /* The week of year, beginning on sunday. */
295 case 'W': /* The week of year, beginning on monday. */
296 _LEGAL_ALT(_ALT_O);
297 /*
298 * XXX This is bogus, as we can not assume any valid
299 * information present in the tm structure at this
300 * point to calculate a real value, so just check the
301 * range for now.
302 */
303 if (!(_conv_num(&bp, &i, 0, 53)))
304 return (0);
305 break;
306
307 case 'w': /* The day of week, beginning on sunday. */
308 _LEGAL_ALT(_ALT_O);
309 if (!(_conv_num(&bp, &tm->tm_wday, 0, 6)))
310 return (0);
311 break;
312
313 case 'Y': /* The year. */
314 _LEGAL_ALT(_ALT_E);
315 if (!(_conv_num(&bp, &i, 0, INT_MAX)))
316 return (0);
317
318 tm->tm_year = i - 1900;
319 break;
320
321 case 'y': /* The year within the 20th century. */
322 _LEGAL_ALT(_ALT_E | _ALT_O);
323 if (!(_conv_num(&bp, &tm->tm_year, 0, 99)))
324 return (0);
325 break;
326
327 /*
328 * Miscellaneous conversions.
329 */
330 case 'n': /* Any kind of white-space. */
331 case 't':
332 _LEGAL_ALT(0);
333 while (isspace(*bp))
334 bp++;
335 break;
336
337
338 default: /* Unknown/unsupported conversion. */
339 return (0);
340 }
341
342
343 }
344
345 return ((char *)bp);
346 }
347
348
349 static int
350 _conv_num(buf, dest, llim, ulim)
351 const char **buf;
352 int *dest;
353 int llim, ulim;
354 {
355 *dest = 0;
356
357 if (**buf < '0' || **buf > '9')
358 return (0);
359
360 do {
361 *dest *= 10;
362 *dest += *(*buf)++ - '0';
363 } while ((*dest * 10 <= ulim) && **buf >= '0' && **buf <= '9');
364
365 if (*dest < llim || *dest > ulim)
366 return (0);
367
368 return (1);
369 }
370