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