4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 1997 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
30 #pragma ident "%Z%%M% %I% %E% SMI"
32 #if !defined(lint) && defined(SCCSIDS)
33 static char *sccsid
= "%Z%%M% %I% %E% SMI";
40 static char *strmatch(/*char *cp, char *string*/);
41 static char *yearmatch(/*char *cp, char *format, struct tm *tm,
43 static char *cvtnum(/*char *cp, int *nump*/);
44 static char *skipnws(/*char *format*/);
46 extern char *getlocale_time();
50 strptime(buf
, format
, tm
)
55 register char *cp
, *p
;
58 register struct dtconv
*dtcp
;
61 (void) getlocale_time();
62 dtcp
= localdtconv(); /* get locale's strings */
65 while ((c
= *format
++) != '\0') {
69 case '%': /* Percent sign */
74 case 'a': /* Abbreviated weekday name */
75 case 'A': /* Weekday name */
76 for (i
= 0; i
< 7; i
++) {
78 dtcp
->weekday_names
[i
],
81 dtcp
->abbrev_weekday_names
[i
],
85 return (NULL
); /* no match */
93 case 'b': /* Abbreviated month name */
94 case 'B': /* Month name */
95 for (i
= 0; i
< 12; i
++) {
100 dtcp
->abbrev_month_names
[i
],
104 return (NULL
); /* no match */
111 case 'c': /* date and time representation */
112 cp
= strptime(cp
, "%x %X", tm
);
117 case 'C': /* long date and time representation */
118 cp
= strptime(cp
, dtcp
->ldate_format
, tm
);
123 case 'd': /* Day of month, with leading zero */
124 case 'e': /* Day of month without leading zero */
125 cp
= cvtnum(cp
, &tm
->tm_mday
);
127 return (NULL
); /* no digits */
128 if (tm
->tm_mday
> 31)
130 if ((c
= *cp
) == '\0'
131 || isspace((unsigned char)c
))
132 format
= skipnws(format
);
135 case 'D': /* Shorthand for %m/%d/%y */
136 cp
= strptime(cp
, "%m/%d/%y", tm
);
141 case 'H': /* Hour (24 hour version) */
142 case 'k': /* Hour (24 hour version) */
143 cp
= cvtnum(cp
, &tm
->tm_hour
);
145 return (NULL
); /* no digits */
146 if (tm
->tm_hour
> 23)
148 if ((c
= *cp
) == '\0'
149 || isspace((unsigned char)c
))
150 format
= skipnws(format
);
153 case 'I': /* Hour (12 hour version) */
154 case 'l': /* Hour (12 hour version) */
155 cp
= cvtnum(cp
, &tm
->tm_hour
);
157 return (NULL
); /* no digits */
158 if (tm
->tm_hour
== 12)
160 else if (tm
->tm_hour
> 11)
162 if ((c
= *cp
) == '\0'
163 || isspace((unsigned char)c
))
164 format
= skipnws(format
);
167 case 'j': /* Julian date */
168 cp
= cvtnum(cp
, &tm
->tm_yday
);
170 return (NULL
); /* no digits */
171 if (tm
->tm_yday
> 365)
175 case 'm': /* Month number */
176 cp
= cvtnum(cp
, &tm
->tm_mon
);
178 return (NULL
); /* no digits */
180 if (tm
->tm_mon
< 0 || tm
->tm_mon
> 11)
182 if ((c
= *cp
) == '\0'
183 || isspace((unsigned char)c
))
184 format
= skipnws(format
);
187 case 'M': /* Minute */
189 * This is optional; if we're at the end of the
190 * string, or the next character is white
191 * space, don't try to match it.
193 if ((c
= *cp
) != '\0'
194 && !isspace((unsigned char)c
)) {
195 cp
= cvtnum(cp
, &tm
->tm_min
);
197 return (NULL
); /* no digits */
201 if ((c
= *cp
) == '\0'
202 || isspace((unsigned char)c
))
203 format
= skipnws(format
);
206 case 'p': /* AM or PM */
207 if ((p
= strmatch(cp
, dtcp
->am_string
,
212 if (tm
->tm_hour
== 12)
215 } else if ((p
= strmatch(cp
, dtcp
->pm_string
,
220 if (tm
->tm_hour
> 12)
221 return (NULL
); /* error */
222 else if (tm
->tm_hour
!= 12)
228 case 'r': /* Shorthand for %I:%M:%S AM or PM */
229 cp
= strptime(cp
, "%I:%M:%S %p", tm
);
234 case 'R': /* Time as %H:%M */
235 cp
= strptime(cp
, "%H:%M", tm
);
240 case 'S': /* Seconds */
242 * This is optional; if we're at the end of the
243 * string, or the next character is white
244 * space, don't try to match it.
246 if ((c
= *cp
) != '\0'
247 && !isspace((unsigned char)c
)) {
248 cp
= cvtnum(cp
, &tm
->tm_sec
);
250 return (NULL
); /* no digits */
254 if ((c
= *cp
) == '\0'
255 || isspace((unsigned char)c
))
256 format
= skipnws(format
);
259 case 'T': /* Shorthand for %H:%M:%S */
260 cp
= strptime(cp
, "%H:%M:%S", tm
);
265 case 'x': /* Localized date format */
266 cp
= strptime(cp
, dtcp
->sdate_format
, tm
);
271 case 'X': /* Localized time format */
272 cp
= strptime(cp
, dtcp
->time_format
, tm
);
277 case 'y': /* Year in the form yy */
278 cp
= yearmatch(cp
, format
, tm
, &hadyear
);
282 if (tm
->tm_year
< 69)
285 return (cp
); /* match is complete */
287 case 'Y': /* Year in the form ccyy */
288 cp
= yearmatch(cp
, format
, tm
, &hadyear
);
296 return (cp
); /* match is complete */
299 return (NULL
); /* unknown conversion */
302 if (isspace((unsigned char)c
)) {
303 while ((ch
= *cp
++) != '\0'
304 && isspace((unsigned char)ch
))
317 * Try to match the beginning of the string pointed to by "cp" with the string
318 * pointed to by "string". The match is independent of the case of either
321 * "termc" is the next character in the format string following the one for
322 * which this match is being done. If the match succeeds, make sure the next
323 * character after the match is either '\0', or that it would match "termc".
325 * If both matches succeed, return a pointer to the next character after the
326 * first match. Otherwise, return NULL.
329 strmatch(cp
, string
, termc
)
331 register char *string
;
334 register unsigned char c
, strc
;
337 * Match the beginning portion of "cp" with "string".
339 while ((strc
= *string
++) != '\0') {
344 strc
= tolower(strc
);
349 if ((c
= *cp
) != '\0') {
350 if (isspace((unsigned char)termc
)) {
354 if (c
!= (unsigned char)termc
)
362 * Try to match a %y or %Y specification.
363 * If it matches, try matching the rest of the format. If it succeeds, just
364 * return. Otherwise, try backing the scan up, ignoring the %y/%Y and any
365 * following non-white-space string. If that succeeds, just return. (This
366 * permits a missing year to be detected if it's at the beginning of a date, as
367 * well as if it's at the end of a date, so that formats such as "%Y/%m/%d" can
368 * match "3/14" and default the year.)
370 * Set "*hadyearp" to indicate whether a year was specified or not.
373 yearmatch(cp
, format
, tm
, hadyearp
)
384 * This is optional; if we're at the end of the
385 * string, or the next character is white
386 * space, don't try to match it.
388 if ((c
= *cp
) != '\0' && !isspace((unsigned char)c
)) {
390 saveyear
= tm
->tm_year
;
391 cp
= cvtnum(cp
, &tm
->tm_year
);
393 return (NULL
); /* no digits */
394 if ((c
= *cp
) == '\0'
395 || isspace((unsigned char)c
))
396 format
= skipnws(format
);
399 * Year can also be optional if it's at
400 * the *beginning* of a date. We check
401 * this by trying to parse the rest of
402 * the date here. If we succeed, OK;
403 * otherwise, we skip over the %y and
406 cp
= strptime(cp
, format
, tm
);
412 format
= skipnws(format
);
413 tm
->tm_year
= saveyear
;
414 cp
= strptime(cp
, format
, tm
);
418 if ((c
= *cp
) == '\0'
419 || isspace((unsigned char)c
))
420 format
= skipnws(format
);
421 cp
= strptime(cp
, format
, tm
);
428 * Try to match a (decimal) number in the string pointed to by "cp".
429 * If the match succeeds, store the result in the "int" pointed to by "nump"
430 * and return a pointer to the character following the number in the string.
431 * If it fails, return NULL.
441 c
= (unsigned char)*cp
++;
443 return (NULL
); /* no digits */
447 c
= (unsigned char)*cp
++;
448 } while (isdigit(c
));
454 * If a format item (such as %H, hours) is followed by a non-white-space
455 * character other than "%", and the part of the string that matched the format
456 * item is followed by white space, the string of non-white-space,
457 * non-format-item characters following that format item may be omitted.
461 register char *format
;
466 * Skip over non-white-space, non-digit characters. "%" is special.
468 while ((c
= *format
) != '\0' && !isspace((unsigned char)c
)) {
471 * This is a format item. If it's %%, skip it as
472 * that's a non-white space, non-digit character.
474 if (*(format
+ 1) == '%')
475 format
++; /* skip % */