xref: /titanic_44/usr/src/lib/libbc/libc/gen/common/strptime.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * CDDL HEADER START
3*7c478bd9Sstevel@tonic-gate  *
4*7c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*7c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*7c478bd9Sstevel@tonic-gate  * with the License.
8*7c478bd9Sstevel@tonic-gate  *
9*7c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*7c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*7c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*7c478bd9Sstevel@tonic-gate  * and limitations under the License.
13*7c478bd9Sstevel@tonic-gate  *
14*7c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*7c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*7c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*7c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*7c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*7c478bd9Sstevel@tonic-gate  *
20*7c478bd9Sstevel@tonic-gate  * CDDL HEADER END
21*7c478bd9Sstevel@tonic-gate  */
22*7c478bd9Sstevel@tonic-gate /*
23*7c478bd9Sstevel@tonic-gate  * Copyright 1997 Sun Microsystems, Inc.  All rights reserved.
24*7c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
25*7c478bd9Sstevel@tonic-gate  */
26*7c478bd9Sstevel@tonic-gate 
27*7c478bd9Sstevel@tonic-gate /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28*7c478bd9Sstevel@tonic-gate /*	  All Rights Reserved	*/
29*7c478bd9Sstevel@tonic-gate 
30*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
31*7c478bd9Sstevel@tonic-gate 
32*7c478bd9Sstevel@tonic-gate #if !defined(lint) && defined(SCCSIDS)
33*7c478bd9Sstevel@tonic-gate static  char *sccsid = "%Z%%M% %I%	%E% SMI";
34*7c478bd9Sstevel@tonic-gate #endif
35*7c478bd9Sstevel@tonic-gate 
36*7c478bd9Sstevel@tonic-gate #include <ctype.h>
37*7c478bd9Sstevel@tonic-gate #include <locale.h>
38*7c478bd9Sstevel@tonic-gate #include <time.h>
39*7c478bd9Sstevel@tonic-gate 
40*7c478bd9Sstevel@tonic-gate static char	*strmatch(/*char *cp, char *string*/);
41*7c478bd9Sstevel@tonic-gate static char	*yearmatch(/*char *cp, char *format, struct tm *tm,
42*7c478bd9Sstevel@tonic-gate     int *hadyearp*/);
43*7c478bd9Sstevel@tonic-gate static char	*cvtnum(/*char *cp, int *nump*/);
44*7c478bd9Sstevel@tonic-gate static char	*skipnws(/*char *format*/);
45*7c478bd9Sstevel@tonic-gate 
46*7c478bd9Sstevel@tonic-gate extern char *getlocale_time();
47*7c478bd9Sstevel@tonic-gate #define NULL	0
48*7c478bd9Sstevel@tonic-gate 
49*7c478bd9Sstevel@tonic-gate char *
strptime(buf,format,tm)50*7c478bd9Sstevel@tonic-gate strptime(buf, format, tm)
51*7c478bd9Sstevel@tonic-gate 	char *buf;
52*7c478bd9Sstevel@tonic-gate 	char *format;
53*7c478bd9Sstevel@tonic-gate 	struct tm *tm;
54*7c478bd9Sstevel@tonic-gate {
55*7c478bd9Sstevel@tonic-gate 	register char *cp, *p;
56*7c478bd9Sstevel@tonic-gate 	register int c, ch;
57*7c478bd9Sstevel@tonic-gate 	register int i;
58*7c478bd9Sstevel@tonic-gate 	register struct dtconv *dtcp;
59*7c478bd9Sstevel@tonic-gate 	int hadyear;
60*7c478bd9Sstevel@tonic-gate 
61*7c478bd9Sstevel@tonic-gate 	(void) getlocale_time();
62*7c478bd9Sstevel@tonic-gate 	dtcp = localdtconv();	/* get locale's strings */
63*7c478bd9Sstevel@tonic-gate 
64*7c478bd9Sstevel@tonic-gate 	cp = buf;
65*7c478bd9Sstevel@tonic-gate 	while ((c = *format++) != '\0') {
66*7c478bd9Sstevel@tonic-gate 		if (c == '%') {
67*7c478bd9Sstevel@tonic-gate 			switch (*format++) {
68*7c478bd9Sstevel@tonic-gate 
69*7c478bd9Sstevel@tonic-gate 			case '%':	/* Percent sign */
70*7c478bd9Sstevel@tonic-gate 				if (*cp++ != '%')
71*7c478bd9Sstevel@tonic-gate 					return (NULL);
72*7c478bd9Sstevel@tonic-gate 				break;
73*7c478bd9Sstevel@tonic-gate 
74*7c478bd9Sstevel@tonic-gate 			case 'a':	/* Abbreviated weekday name */
75*7c478bd9Sstevel@tonic-gate 			case 'A':	/* Weekday name */
76*7c478bd9Sstevel@tonic-gate 				for (i = 0; i < 7; i++) {
77*7c478bd9Sstevel@tonic-gate 					if ((p = strmatch(cp,
78*7c478bd9Sstevel@tonic-gate 					      dtcp->weekday_names[i],
79*7c478bd9Sstevel@tonic-gate 					      *format)) != NULL
80*7c478bd9Sstevel@tonic-gate 					    || (p = strmatch(cp,
81*7c478bd9Sstevel@tonic-gate 					      dtcp->abbrev_weekday_names[i],
82*7c478bd9Sstevel@tonic-gate 					      *format)) != NULL)
83*7c478bd9Sstevel@tonic-gate 						goto match_wday;
84*7c478bd9Sstevel@tonic-gate 				}
85*7c478bd9Sstevel@tonic-gate 				return (NULL);	/* no match */
86*7c478bd9Sstevel@tonic-gate 
87*7c478bd9Sstevel@tonic-gate 			match_wday:
88*7c478bd9Sstevel@tonic-gate 				tm->tm_wday = i;
89*7c478bd9Sstevel@tonic-gate 				cp = p;
90*7c478bd9Sstevel@tonic-gate 				break;
91*7c478bd9Sstevel@tonic-gate 
92*7c478bd9Sstevel@tonic-gate 			case 'h':
93*7c478bd9Sstevel@tonic-gate 			case 'b':	/* Abbreviated month name */
94*7c478bd9Sstevel@tonic-gate 			case 'B':	/* Month name */
95*7c478bd9Sstevel@tonic-gate 				for (i = 0; i < 12; i++) {
96*7c478bd9Sstevel@tonic-gate 					if ((p = strmatch(cp,
97*7c478bd9Sstevel@tonic-gate 					      dtcp->month_names[i],
98*7c478bd9Sstevel@tonic-gate 					      *format)) != NULL
99*7c478bd9Sstevel@tonic-gate 					    || (p = strmatch(cp,
100*7c478bd9Sstevel@tonic-gate 					      dtcp->abbrev_month_names[i],
101*7c478bd9Sstevel@tonic-gate 					      *format)) != NULL)
102*7c478bd9Sstevel@tonic-gate 						goto match_month;
103*7c478bd9Sstevel@tonic-gate 				}
104*7c478bd9Sstevel@tonic-gate 				return (NULL);	/* no match */
105*7c478bd9Sstevel@tonic-gate 
106*7c478bd9Sstevel@tonic-gate 			match_month:
107*7c478bd9Sstevel@tonic-gate 				tm->tm_mon = i;
108*7c478bd9Sstevel@tonic-gate 				cp = p;
109*7c478bd9Sstevel@tonic-gate 				break;
110*7c478bd9Sstevel@tonic-gate 
111*7c478bd9Sstevel@tonic-gate 			case 'c':	/* date and time representation */
112*7c478bd9Sstevel@tonic-gate 				cp = strptime(cp, "%x %X", tm);
113*7c478bd9Sstevel@tonic-gate 				if (cp == NULL)
114*7c478bd9Sstevel@tonic-gate 					return (NULL);
115*7c478bd9Sstevel@tonic-gate 				break;
116*7c478bd9Sstevel@tonic-gate 
117*7c478bd9Sstevel@tonic-gate 			case 'C':	/* long date and time representation */
118*7c478bd9Sstevel@tonic-gate 				cp = strptime(cp, dtcp->ldate_format, tm);
119*7c478bd9Sstevel@tonic-gate 				if (cp == NULL)
120*7c478bd9Sstevel@tonic-gate 					return (NULL);
121*7c478bd9Sstevel@tonic-gate 				break;
122*7c478bd9Sstevel@tonic-gate 
123*7c478bd9Sstevel@tonic-gate 			case 'd':	/* Day of month, with leading zero */
124*7c478bd9Sstevel@tonic-gate 			case 'e':       /* Day of month without leading zero */
125*7c478bd9Sstevel@tonic-gate 				cp = cvtnum(cp, &tm->tm_mday);
126*7c478bd9Sstevel@tonic-gate 				if (cp == NULL)
127*7c478bd9Sstevel@tonic-gate 					return (NULL);	/* no digits */
128*7c478bd9Sstevel@tonic-gate 				if (tm->tm_mday > 31)
129*7c478bd9Sstevel@tonic-gate 					return (NULL);
130*7c478bd9Sstevel@tonic-gate 				if ((c = *cp) == '\0'
131*7c478bd9Sstevel@tonic-gate 				    || isspace((unsigned char)c))
132*7c478bd9Sstevel@tonic-gate 					format = skipnws(format);
133*7c478bd9Sstevel@tonic-gate 				break;
134*7c478bd9Sstevel@tonic-gate 
135*7c478bd9Sstevel@tonic-gate 			case 'D':	/* Shorthand for %m/%d/%y */
136*7c478bd9Sstevel@tonic-gate 				cp = strptime(cp, "%m/%d/%y", tm);
137*7c478bd9Sstevel@tonic-gate 				if (cp == NULL)
138*7c478bd9Sstevel@tonic-gate 					return (NULL);
139*7c478bd9Sstevel@tonic-gate 				break;
140*7c478bd9Sstevel@tonic-gate 
141*7c478bd9Sstevel@tonic-gate 			case 'H':	/* Hour (24 hour version) */
142*7c478bd9Sstevel@tonic-gate 			case 'k':	/* Hour (24 hour version) */
143*7c478bd9Sstevel@tonic-gate 				cp = cvtnum(cp, &tm->tm_hour);
144*7c478bd9Sstevel@tonic-gate 				if (cp == NULL)
145*7c478bd9Sstevel@tonic-gate 					return (NULL);	/* no digits */
146*7c478bd9Sstevel@tonic-gate 				if (tm->tm_hour > 23)
147*7c478bd9Sstevel@tonic-gate 					return (NULL);
148*7c478bd9Sstevel@tonic-gate 				if ((c = *cp) == '\0'
149*7c478bd9Sstevel@tonic-gate 				    || isspace((unsigned char)c))
150*7c478bd9Sstevel@tonic-gate 					format = skipnws(format);
151*7c478bd9Sstevel@tonic-gate 				break;
152*7c478bd9Sstevel@tonic-gate 
153*7c478bd9Sstevel@tonic-gate 			case 'I':	/* Hour (12 hour version) */
154*7c478bd9Sstevel@tonic-gate 			case 'l':	/* Hour (12 hour version) */
155*7c478bd9Sstevel@tonic-gate 				cp = cvtnum(cp, &tm->tm_hour);
156*7c478bd9Sstevel@tonic-gate 				if (cp == NULL)
157*7c478bd9Sstevel@tonic-gate 					return (NULL);	/* no digits */
158*7c478bd9Sstevel@tonic-gate 				if (tm->tm_hour == 12)
159*7c478bd9Sstevel@tonic-gate 					tm->tm_hour = 0;
160*7c478bd9Sstevel@tonic-gate 				else if (tm->tm_hour > 11)
161*7c478bd9Sstevel@tonic-gate 					return (NULL);
162*7c478bd9Sstevel@tonic-gate 				if ((c = *cp) == '\0'
163*7c478bd9Sstevel@tonic-gate 				    || isspace((unsigned char)c))
164*7c478bd9Sstevel@tonic-gate 					format = skipnws(format);
165*7c478bd9Sstevel@tonic-gate 				break;
166*7c478bd9Sstevel@tonic-gate 
167*7c478bd9Sstevel@tonic-gate 			case 'j':	/* Julian date */
168*7c478bd9Sstevel@tonic-gate 				cp = cvtnum(cp, &tm->tm_yday);
169*7c478bd9Sstevel@tonic-gate 				if (cp == NULL)
170*7c478bd9Sstevel@tonic-gate 					return (NULL);	/* no digits */
171*7c478bd9Sstevel@tonic-gate 				if (tm->tm_yday > 365)
172*7c478bd9Sstevel@tonic-gate 					return (NULL);
173*7c478bd9Sstevel@tonic-gate 				break;
174*7c478bd9Sstevel@tonic-gate 
175*7c478bd9Sstevel@tonic-gate 			case 'm':	/* Month number */
176*7c478bd9Sstevel@tonic-gate 				cp = cvtnum(cp, &tm->tm_mon);
177*7c478bd9Sstevel@tonic-gate 				if (cp == NULL)
178*7c478bd9Sstevel@tonic-gate 					return (NULL);	/* no digits */
179*7c478bd9Sstevel@tonic-gate 				tm->tm_mon--;
180*7c478bd9Sstevel@tonic-gate 				if (tm->tm_mon < 0 || tm->tm_mon > 11)
181*7c478bd9Sstevel@tonic-gate 					return (NULL);
182*7c478bd9Sstevel@tonic-gate 				if ((c = *cp) == '\0'
183*7c478bd9Sstevel@tonic-gate 				    || isspace((unsigned char)c))
184*7c478bd9Sstevel@tonic-gate 					format = skipnws(format);
185*7c478bd9Sstevel@tonic-gate 				break;
186*7c478bd9Sstevel@tonic-gate 
187*7c478bd9Sstevel@tonic-gate 			case 'M':	/* Minute */
188*7c478bd9Sstevel@tonic-gate 				/*
189*7c478bd9Sstevel@tonic-gate 				 * This is optional; if we're at the end of the
190*7c478bd9Sstevel@tonic-gate 				 * string, or the next character is white
191*7c478bd9Sstevel@tonic-gate 				 * space, don't try to match it.
192*7c478bd9Sstevel@tonic-gate 				 */
193*7c478bd9Sstevel@tonic-gate 				if ((c = *cp) != '\0'
194*7c478bd9Sstevel@tonic-gate 				    && !isspace((unsigned char)c)) {
195*7c478bd9Sstevel@tonic-gate 					cp = cvtnum(cp, &tm->tm_min);
196*7c478bd9Sstevel@tonic-gate 					if (cp == NULL)
197*7c478bd9Sstevel@tonic-gate 						return (NULL);	/* no digits */
198*7c478bd9Sstevel@tonic-gate 					if (tm->tm_min > 59)
199*7c478bd9Sstevel@tonic-gate 						return (NULL);
200*7c478bd9Sstevel@tonic-gate 				}
201*7c478bd9Sstevel@tonic-gate 				if ((c = *cp) == '\0'
202*7c478bd9Sstevel@tonic-gate 				    || isspace((unsigned char)c))
203*7c478bd9Sstevel@tonic-gate 					format = skipnws(format);
204*7c478bd9Sstevel@tonic-gate 				break;
205*7c478bd9Sstevel@tonic-gate 
206*7c478bd9Sstevel@tonic-gate 			case 'p':	/* AM or PM */
207*7c478bd9Sstevel@tonic-gate 				if ((p = strmatch(cp, dtcp->am_string,
208*7c478bd9Sstevel@tonic-gate 				    *format)) != NULL) {
209*7c478bd9Sstevel@tonic-gate 					/*
210*7c478bd9Sstevel@tonic-gate 					 * AM.
211*7c478bd9Sstevel@tonic-gate 					 */
212*7c478bd9Sstevel@tonic-gate 					if (tm->tm_hour == 12)
213*7c478bd9Sstevel@tonic-gate 						tm->tm_hour = 0;
214*7c478bd9Sstevel@tonic-gate 					cp = p;
215*7c478bd9Sstevel@tonic-gate 				} else if ((p = strmatch(cp, dtcp->pm_string,
216*7c478bd9Sstevel@tonic-gate 				    *format)) != NULL) {
217*7c478bd9Sstevel@tonic-gate 					/*
218*7c478bd9Sstevel@tonic-gate 					 * PM.
219*7c478bd9Sstevel@tonic-gate 					 */
220*7c478bd9Sstevel@tonic-gate 					if (tm->tm_hour > 12)
221*7c478bd9Sstevel@tonic-gate 						return (NULL); /* error */
222*7c478bd9Sstevel@tonic-gate 					else if (tm->tm_hour != 12)
223*7c478bd9Sstevel@tonic-gate 						tm->tm_hour += 12;
224*7c478bd9Sstevel@tonic-gate 					cp = p;
225*7c478bd9Sstevel@tonic-gate 				}
226*7c478bd9Sstevel@tonic-gate 				break;
227*7c478bd9Sstevel@tonic-gate 
228*7c478bd9Sstevel@tonic-gate 			case 'r':	/* Shorthand for %I:%M:%S AM or PM */
229*7c478bd9Sstevel@tonic-gate 				cp = strptime(cp, "%I:%M:%S %p", tm);
230*7c478bd9Sstevel@tonic-gate 				if (cp == NULL)
231*7c478bd9Sstevel@tonic-gate 					return (NULL);
232*7c478bd9Sstevel@tonic-gate 				break;
233*7c478bd9Sstevel@tonic-gate 
234*7c478bd9Sstevel@tonic-gate 			case 'R':	/* Time as %H:%M */
235*7c478bd9Sstevel@tonic-gate 				cp = strptime(cp, "%H:%M", tm);
236*7c478bd9Sstevel@tonic-gate 				if (cp == NULL)
237*7c478bd9Sstevel@tonic-gate 					return (NULL);
238*7c478bd9Sstevel@tonic-gate 				break;
239*7c478bd9Sstevel@tonic-gate 
240*7c478bd9Sstevel@tonic-gate 			case 'S':	/* Seconds */
241*7c478bd9Sstevel@tonic-gate 				/*
242*7c478bd9Sstevel@tonic-gate 				 * This is optional; if we're at the end of the
243*7c478bd9Sstevel@tonic-gate 				 * string, or the next character is white
244*7c478bd9Sstevel@tonic-gate 				 * space, don't try to match it.
245*7c478bd9Sstevel@tonic-gate 				 */
246*7c478bd9Sstevel@tonic-gate 				if ((c = *cp) != '\0'
247*7c478bd9Sstevel@tonic-gate 				    && !isspace((unsigned char)c)) {
248*7c478bd9Sstevel@tonic-gate 					cp = cvtnum(cp, &tm->tm_sec);
249*7c478bd9Sstevel@tonic-gate 					if (cp == NULL)
250*7c478bd9Sstevel@tonic-gate 						return (NULL);	/* no digits */
251*7c478bd9Sstevel@tonic-gate 					if (tm->tm_sec > 59)
252*7c478bd9Sstevel@tonic-gate 						return (NULL);
253*7c478bd9Sstevel@tonic-gate 				}
254*7c478bd9Sstevel@tonic-gate 				if ((c = *cp) == '\0'
255*7c478bd9Sstevel@tonic-gate 				    || isspace((unsigned char)c))
256*7c478bd9Sstevel@tonic-gate 					format = skipnws(format);
257*7c478bd9Sstevel@tonic-gate 				break;
258*7c478bd9Sstevel@tonic-gate 
259*7c478bd9Sstevel@tonic-gate 			case 'T':	/* Shorthand for %H:%M:%S */
260*7c478bd9Sstevel@tonic-gate 				cp = strptime(cp, "%H:%M:%S", tm);
261*7c478bd9Sstevel@tonic-gate 				if (cp == NULL)
262*7c478bd9Sstevel@tonic-gate 					return (NULL);
263*7c478bd9Sstevel@tonic-gate 				break;
264*7c478bd9Sstevel@tonic-gate 
265*7c478bd9Sstevel@tonic-gate 			case 'x':	/* Localized date format */
266*7c478bd9Sstevel@tonic-gate 				cp = strptime(cp, dtcp->sdate_format, tm);
267*7c478bd9Sstevel@tonic-gate 				if (cp == NULL)
268*7c478bd9Sstevel@tonic-gate 					return (NULL);
269*7c478bd9Sstevel@tonic-gate 				break;
270*7c478bd9Sstevel@tonic-gate 
271*7c478bd9Sstevel@tonic-gate 			case 'X':	/* Localized time format */
272*7c478bd9Sstevel@tonic-gate 				cp = strptime(cp, dtcp->time_format, tm);
273*7c478bd9Sstevel@tonic-gate 				if (cp == NULL)
274*7c478bd9Sstevel@tonic-gate 					return (NULL);
275*7c478bd9Sstevel@tonic-gate 				break;
276*7c478bd9Sstevel@tonic-gate 
277*7c478bd9Sstevel@tonic-gate 			case 'y':	/* Year in the form yy */
278*7c478bd9Sstevel@tonic-gate 				cp = yearmatch(cp, format, tm, &hadyear);
279*7c478bd9Sstevel@tonic-gate 				if (cp == NULL)
280*7c478bd9Sstevel@tonic-gate 					return (NULL);
281*7c478bd9Sstevel@tonic-gate 				if (hadyear) {
282*7c478bd9Sstevel@tonic-gate 					if (tm->tm_year < 69)
283*7c478bd9Sstevel@tonic-gate 						tm->tm_year += 100;
284*7c478bd9Sstevel@tonic-gate 				}
285*7c478bd9Sstevel@tonic-gate 				return (cp);	/* match is complete */
286*7c478bd9Sstevel@tonic-gate 
287*7c478bd9Sstevel@tonic-gate 			case 'Y':	/* Year in the form ccyy */
288*7c478bd9Sstevel@tonic-gate 				cp = yearmatch(cp, format, tm, &hadyear);
289*7c478bd9Sstevel@tonic-gate 				if (cp == NULL)
290*7c478bd9Sstevel@tonic-gate 					return (NULL);
291*7c478bd9Sstevel@tonic-gate 				if (hadyear) {
292*7c478bd9Sstevel@tonic-gate 					tm->tm_year -= 1900;
293*7c478bd9Sstevel@tonic-gate 					if (tm->tm_year < 0)
294*7c478bd9Sstevel@tonic-gate 						return (NULL);
295*7c478bd9Sstevel@tonic-gate 				}
296*7c478bd9Sstevel@tonic-gate 				return (cp);	/* match is complete */
297*7c478bd9Sstevel@tonic-gate 
298*7c478bd9Sstevel@tonic-gate 			default:
299*7c478bd9Sstevel@tonic-gate 				return (NULL);	/* unknown conversion */
300*7c478bd9Sstevel@tonic-gate 			}
301*7c478bd9Sstevel@tonic-gate 		} else {
302*7c478bd9Sstevel@tonic-gate 			if (isspace((unsigned char)c)) {
303*7c478bd9Sstevel@tonic-gate 				while ((ch = *cp++) != '\0'
304*7c478bd9Sstevel@tonic-gate 				    && isspace((unsigned char)ch))
305*7c478bd9Sstevel@tonic-gate 					;
306*7c478bd9Sstevel@tonic-gate 				cp--;
307*7c478bd9Sstevel@tonic-gate 			} else {
308*7c478bd9Sstevel@tonic-gate 				if (*cp++ != c)
309*7c478bd9Sstevel@tonic-gate 					return (NULL);
310*7c478bd9Sstevel@tonic-gate 			}
311*7c478bd9Sstevel@tonic-gate 		}
312*7c478bd9Sstevel@tonic-gate 	}
313*7c478bd9Sstevel@tonic-gate 	return (cp);
314*7c478bd9Sstevel@tonic-gate }
315*7c478bd9Sstevel@tonic-gate 
316*7c478bd9Sstevel@tonic-gate /*
317*7c478bd9Sstevel@tonic-gate  * Try to match the beginning of the string pointed to by "cp" with the string
318*7c478bd9Sstevel@tonic-gate  * pointed to by "string".  The match is independent of the case of either
319*7c478bd9Sstevel@tonic-gate  * string.
320*7c478bd9Sstevel@tonic-gate  *
321*7c478bd9Sstevel@tonic-gate  * "termc" is the next character in the format string following the one for
322*7c478bd9Sstevel@tonic-gate  * which this match is being done.  If the match succeeds, make sure the next
323*7c478bd9Sstevel@tonic-gate  * character after the match is either '\0', or that it would match "termc".
324*7c478bd9Sstevel@tonic-gate  *
325*7c478bd9Sstevel@tonic-gate  * If both matches succeed, return a pointer to the next character after the
326*7c478bd9Sstevel@tonic-gate  * first match.  Otherwise, return NULL.
327*7c478bd9Sstevel@tonic-gate  */
328*7c478bd9Sstevel@tonic-gate static char *
strmatch(cp,string,termc)329*7c478bd9Sstevel@tonic-gate strmatch(cp, string, termc)
330*7c478bd9Sstevel@tonic-gate 	register char *cp;
331*7c478bd9Sstevel@tonic-gate 	register char *string;
332*7c478bd9Sstevel@tonic-gate 	char termc;
333*7c478bd9Sstevel@tonic-gate {
334*7c478bd9Sstevel@tonic-gate 	register unsigned char c, strc;
335*7c478bd9Sstevel@tonic-gate 
336*7c478bd9Sstevel@tonic-gate 	/*
337*7c478bd9Sstevel@tonic-gate 	 * Match the beginning portion of "cp" with "string".
338*7c478bd9Sstevel@tonic-gate 	 */
339*7c478bd9Sstevel@tonic-gate 	while ((strc = *string++) != '\0') {
340*7c478bd9Sstevel@tonic-gate 		c = *cp++;
341*7c478bd9Sstevel@tonic-gate 		if (isupper(c))
342*7c478bd9Sstevel@tonic-gate 			c = tolower(c);
343*7c478bd9Sstevel@tonic-gate 		if (isupper(strc))
344*7c478bd9Sstevel@tonic-gate 			strc = tolower(strc);
345*7c478bd9Sstevel@tonic-gate 		if (c != strc)
346*7c478bd9Sstevel@tonic-gate 			return (NULL);
347*7c478bd9Sstevel@tonic-gate 	}
348*7c478bd9Sstevel@tonic-gate 
349*7c478bd9Sstevel@tonic-gate 	if ((c = *cp) != '\0') {
350*7c478bd9Sstevel@tonic-gate 		if (isspace((unsigned char)termc)) {
351*7c478bd9Sstevel@tonic-gate 			if (!isspace(c))
352*7c478bd9Sstevel@tonic-gate 				return (NULL);
353*7c478bd9Sstevel@tonic-gate 		} else {
354*7c478bd9Sstevel@tonic-gate 			if (c != (unsigned char)termc)
355*7c478bd9Sstevel@tonic-gate 				return (NULL);
356*7c478bd9Sstevel@tonic-gate 		}
357*7c478bd9Sstevel@tonic-gate 	}
358*7c478bd9Sstevel@tonic-gate 	return (cp);
359*7c478bd9Sstevel@tonic-gate }
360*7c478bd9Sstevel@tonic-gate 
361*7c478bd9Sstevel@tonic-gate /*
362*7c478bd9Sstevel@tonic-gate  * Try to match a %y or %Y specification.
363*7c478bd9Sstevel@tonic-gate  * If it matches, try matching the rest of the format.  If it succeeds, just
364*7c478bd9Sstevel@tonic-gate  * return.  Otherwise, try backing the scan up, ignoring the %y/%Y and any
365*7c478bd9Sstevel@tonic-gate  * following non-white-space string.  If that succeeds, just return.  (This
366*7c478bd9Sstevel@tonic-gate  * permits a missing year to be detected if it's at the beginning of a date, as
367*7c478bd9Sstevel@tonic-gate  * well as if it's at the end of a date, so that formats such as "%Y/%m/%d" can
368*7c478bd9Sstevel@tonic-gate  * match "3/14" and default the year.)
369*7c478bd9Sstevel@tonic-gate  *
370*7c478bd9Sstevel@tonic-gate  * Set "*hadyearp" to indicate whether a year was specified or not.
371*7c478bd9Sstevel@tonic-gate  */
372*7c478bd9Sstevel@tonic-gate static char *
yearmatch(cp,format,tm,hadyearp)373*7c478bd9Sstevel@tonic-gate yearmatch(cp, format, tm, hadyearp)
374*7c478bd9Sstevel@tonic-gate 	register char *cp;
375*7c478bd9Sstevel@tonic-gate 	char *format;
376*7c478bd9Sstevel@tonic-gate 	struct tm *tm;
377*7c478bd9Sstevel@tonic-gate 	int *hadyearp;
378*7c478bd9Sstevel@tonic-gate {
379*7c478bd9Sstevel@tonic-gate 	register int c;
380*7c478bd9Sstevel@tonic-gate 	char *savecp;
381*7c478bd9Sstevel@tonic-gate 	int saveyear;
382*7c478bd9Sstevel@tonic-gate 
383*7c478bd9Sstevel@tonic-gate 	/*
384*7c478bd9Sstevel@tonic-gate 	 * This is optional; if we're at the end of the
385*7c478bd9Sstevel@tonic-gate 	 * string, or the next character is white
386*7c478bd9Sstevel@tonic-gate 	 * space, don't try to match it.
387*7c478bd9Sstevel@tonic-gate 	 */
388*7c478bd9Sstevel@tonic-gate 	if ((c = *cp) != '\0' && !isspace((unsigned char)c)) {
389*7c478bd9Sstevel@tonic-gate 		savecp = cp;
390*7c478bd9Sstevel@tonic-gate 		saveyear = tm->tm_year;
391*7c478bd9Sstevel@tonic-gate 		cp = cvtnum(cp, &tm->tm_year);
392*7c478bd9Sstevel@tonic-gate 		if (cp == NULL)
393*7c478bd9Sstevel@tonic-gate 			return (NULL);	/* no digits */
394*7c478bd9Sstevel@tonic-gate 		if ((c = *cp) == '\0'
395*7c478bd9Sstevel@tonic-gate 		    || isspace((unsigned char)c))
396*7c478bd9Sstevel@tonic-gate 			format = skipnws(format);
397*7c478bd9Sstevel@tonic-gate 
398*7c478bd9Sstevel@tonic-gate 		/*
399*7c478bd9Sstevel@tonic-gate 		 * Year can also be optional if it's at
400*7c478bd9Sstevel@tonic-gate 		 * the *beginning* of a date.  We check
401*7c478bd9Sstevel@tonic-gate 		 * this by trying to parse the rest of
402*7c478bd9Sstevel@tonic-gate 		 * the date here.  If we succeed, OK;
403*7c478bd9Sstevel@tonic-gate 		 * otherwise, we skip over the %y and
404*7c478bd9Sstevel@tonic-gate 		 * try again.
405*7c478bd9Sstevel@tonic-gate 		 */
406*7c478bd9Sstevel@tonic-gate 		cp = strptime(cp, format, tm);
407*7c478bd9Sstevel@tonic-gate 		if (cp != NULL)
408*7c478bd9Sstevel@tonic-gate 			*hadyearp = 1;
409*7c478bd9Sstevel@tonic-gate 		else {
410*7c478bd9Sstevel@tonic-gate 			*hadyearp = 0;
411*7c478bd9Sstevel@tonic-gate 			cp = savecp;
412*7c478bd9Sstevel@tonic-gate 			format = skipnws(format);
413*7c478bd9Sstevel@tonic-gate 			tm->tm_year = saveyear;
414*7c478bd9Sstevel@tonic-gate 			cp = strptime(cp, format, tm);
415*7c478bd9Sstevel@tonic-gate 		}
416*7c478bd9Sstevel@tonic-gate 	} else {
417*7c478bd9Sstevel@tonic-gate 		*hadyearp = 0;
418*7c478bd9Sstevel@tonic-gate 		if ((c = *cp) == '\0'
419*7c478bd9Sstevel@tonic-gate 		    || isspace((unsigned char)c))
420*7c478bd9Sstevel@tonic-gate 			format = skipnws(format);
421*7c478bd9Sstevel@tonic-gate 		cp = strptime(cp, format, tm);
422*7c478bd9Sstevel@tonic-gate 	}
423*7c478bd9Sstevel@tonic-gate 
424*7c478bd9Sstevel@tonic-gate 	return (cp);
425*7c478bd9Sstevel@tonic-gate }
426*7c478bd9Sstevel@tonic-gate 
427*7c478bd9Sstevel@tonic-gate /*
428*7c478bd9Sstevel@tonic-gate  * Try to match a (decimal) number in the string pointed to by "cp".
429*7c478bd9Sstevel@tonic-gate  * If the match succeeds, store the result in the "int" pointed to by "nump"
430*7c478bd9Sstevel@tonic-gate  * and return a pointer to the character following the number in the string.
431*7c478bd9Sstevel@tonic-gate  * If it fails, return NULL.
432*7c478bd9Sstevel@tonic-gate  */
433*7c478bd9Sstevel@tonic-gate static char *
cvtnum(cp,nump)434*7c478bd9Sstevel@tonic-gate cvtnum(cp, nump)
435*7c478bd9Sstevel@tonic-gate 	register char *cp;
436*7c478bd9Sstevel@tonic-gate 	int *nump;
437*7c478bd9Sstevel@tonic-gate {
438*7c478bd9Sstevel@tonic-gate 	register int c;
439*7c478bd9Sstevel@tonic-gate 	register int i;
440*7c478bd9Sstevel@tonic-gate 
441*7c478bd9Sstevel@tonic-gate 	c = (unsigned char)*cp++;
442*7c478bd9Sstevel@tonic-gate 	if (!isdigit(c))
443*7c478bd9Sstevel@tonic-gate 		return (NULL);	/* no digits */
444*7c478bd9Sstevel@tonic-gate 	i = 0;
445*7c478bd9Sstevel@tonic-gate 	do {
446*7c478bd9Sstevel@tonic-gate 		i = i*10 + c - '0';
447*7c478bd9Sstevel@tonic-gate 		c = (unsigned char)*cp++;
448*7c478bd9Sstevel@tonic-gate 	} while (isdigit(c));
449*7c478bd9Sstevel@tonic-gate 	*nump = i;
450*7c478bd9Sstevel@tonic-gate 	return (cp - 1);
451*7c478bd9Sstevel@tonic-gate }
452*7c478bd9Sstevel@tonic-gate 
453*7c478bd9Sstevel@tonic-gate /*
454*7c478bd9Sstevel@tonic-gate  * If a format item (such as %H, hours) is followed by a non-white-space
455*7c478bd9Sstevel@tonic-gate  * character other than "%", and the part of the string that matched the format
456*7c478bd9Sstevel@tonic-gate  * item is followed by white space, the string of non-white-space,
457*7c478bd9Sstevel@tonic-gate  * non-format-item characters following that format item may be omitted.
458*7c478bd9Sstevel@tonic-gate  */
459*7c478bd9Sstevel@tonic-gate static char *
skipnws(format)460*7c478bd9Sstevel@tonic-gate skipnws(format)
461*7c478bd9Sstevel@tonic-gate 	register char *format;
462*7c478bd9Sstevel@tonic-gate {
463*7c478bd9Sstevel@tonic-gate 	register char c;
464*7c478bd9Sstevel@tonic-gate 
465*7c478bd9Sstevel@tonic-gate 	/*
466*7c478bd9Sstevel@tonic-gate 	 * Skip over non-white-space, non-digit characters.  "%" is special.
467*7c478bd9Sstevel@tonic-gate 	 */
468*7c478bd9Sstevel@tonic-gate 	while ((c = *format) != '\0' && !isspace((unsigned char)c)) {
469*7c478bd9Sstevel@tonic-gate 		if (c == '%') {
470*7c478bd9Sstevel@tonic-gate 			/*
471*7c478bd9Sstevel@tonic-gate 			 * This is a format item.  If it's %%, skip it as
472*7c478bd9Sstevel@tonic-gate 			 * that's a non-white space, non-digit character.
473*7c478bd9Sstevel@tonic-gate 			 */
474*7c478bd9Sstevel@tonic-gate 			if (*(format + 1) == '%')
475*7c478bd9Sstevel@tonic-gate 				format++;	/* skip % */
476*7c478bd9Sstevel@tonic-gate 			else
477*7c478bd9Sstevel@tonic-gate 				break;
478*7c478bd9Sstevel@tonic-gate 		}
479*7c478bd9Sstevel@tonic-gate 		format++;
480*7c478bd9Sstevel@tonic-gate 	}
481*7c478bd9Sstevel@tonic-gate 
482*7c478bd9Sstevel@tonic-gate 	return (format);
483*7c478bd9Sstevel@tonic-gate }
484