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