diff options
Diffstat (limited to 'usr/src/lib/libbc/libc/gen/common/strptime.c')
-rw-r--r-- | usr/src/lib/libbc/libc/gen/common/strptime.c | 483 |
1 files changed, 483 insertions, 0 deletions
diff --git a/usr/src/lib/libbc/libc/gen/common/strptime.c b/usr/src/lib/libbc/libc/gen/common/strptime.c new file mode 100644 index 0000000000..bfd7aef821 --- /dev/null +++ b/usr/src/lib/libbc/libc/gen/common/strptime.c @@ -0,0 +1,483 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 1997 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#if !defined(lint) && defined(SCCSIDS) +static char *sccsid = "%Z%%M% %I% %E% SMI"; +#endif + +#include <ctype.h> +#include <locale.h> +#include <time.h> + +static char *strmatch(/*char *cp, char *string*/); +static char *yearmatch(/*char *cp, char *format, struct tm *tm, + int *hadyearp*/); +static char *cvtnum(/*char *cp, int *nump*/); +static char *skipnws(/*char *format*/); + +extern char *getlocale_time(); +#define NULL 0 + +char * +strptime(buf, format, tm) + char *buf; + char *format; + struct tm *tm; +{ + register char *cp, *p; + register int c, ch; + register int i; + register struct dtconv *dtcp; + int hadyear; + + (void) getlocale_time(); + dtcp = localdtconv(); /* get locale's strings */ + + cp = buf; + while ((c = *format++) != '\0') { + if (c == '%') { + switch (*format++) { + + case '%': /* Percent sign */ + if (*cp++ != '%') + return (NULL); + break; + + case 'a': /* Abbreviated weekday name */ + case 'A': /* Weekday name */ + for (i = 0; i < 7; i++) { + if ((p = strmatch(cp, + dtcp->weekday_names[i], + *format)) != NULL + || (p = strmatch(cp, + dtcp->abbrev_weekday_names[i], + *format)) != NULL) + goto match_wday; + } + return (NULL); /* no match */ + + match_wday: + tm->tm_wday = i; + cp = p; + break; + + case 'h': + case 'b': /* Abbreviated month name */ + case 'B': /* Month name */ + for (i = 0; i < 12; i++) { + if ((p = strmatch(cp, + dtcp->month_names[i], + *format)) != NULL + || (p = strmatch(cp, + dtcp->abbrev_month_names[i], + *format)) != NULL) + goto match_month; + } + return (NULL); /* no match */ + + match_month: + tm->tm_mon = i; + cp = p; + break; + + case 'c': /* date and time representation */ + cp = strptime(cp, "%x %X", tm); + if (cp == NULL) + return (NULL); + break; + + case 'C': /* long date and time representation */ + cp = strptime(cp, dtcp->ldate_format, tm); + if (cp == NULL) + return (NULL); + break; + + case 'd': /* Day of month, with leading zero */ + case 'e': /* Day of month without leading zero */ + cp = cvtnum(cp, &tm->tm_mday); + if (cp == NULL) + return (NULL); /* no digits */ + if (tm->tm_mday > 31) + return (NULL); + if ((c = *cp) == '\0' + || isspace((unsigned char)c)) + format = skipnws(format); + break; + + case 'D': /* Shorthand for %m/%d/%y */ + cp = strptime(cp, "%m/%d/%y", tm); + if (cp == NULL) + return (NULL); + break; + + case 'H': /* Hour (24 hour version) */ + case 'k': /* Hour (24 hour version) */ + cp = cvtnum(cp, &tm->tm_hour); + if (cp == NULL) + return (NULL); /* no digits */ + if (tm->tm_hour > 23) + return (NULL); + if ((c = *cp) == '\0' + || isspace((unsigned char)c)) + format = skipnws(format); + break; + + case 'I': /* Hour (12 hour version) */ + case 'l': /* Hour (12 hour version) */ + cp = cvtnum(cp, &tm->tm_hour); + if (cp == NULL) + return (NULL); /* no digits */ + if (tm->tm_hour == 12) + tm->tm_hour = 0; + else if (tm->tm_hour > 11) + return (NULL); + if ((c = *cp) == '\0' + || isspace((unsigned char)c)) + format = skipnws(format); + break; + + case 'j': /* Julian date */ + cp = cvtnum(cp, &tm->tm_yday); + if (cp == NULL) + return (NULL); /* no digits */ + if (tm->tm_yday > 365) + return (NULL); + break; + + case 'm': /* Month number */ + cp = cvtnum(cp, &tm->tm_mon); + if (cp == NULL) + return (NULL); /* no digits */ + tm->tm_mon--; + if (tm->tm_mon < 0 || tm->tm_mon > 11) + return (NULL); + if ((c = *cp) == '\0' + || isspace((unsigned char)c)) + format = skipnws(format); + break; + + case 'M': /* Minute */ + /* + * This is optional; if we're at the end of the + * string, or the next character is white + * space, don't try to match it. + */ + if ((c = *cp) != '\0' + && !isspace((unsigned char)c)) { + cp = cvtnum(cp, &tm->tm_min); + if (cp == NULL) + return (NULL); /* no digits */ + if (tm->tm_min > 59) + return (NULL); + } + if ((c = *cp) == '\0' + || isspace((unsigned char)c)) + format = skipnws(format); + break; + + case 'p': /* AM or PM */ + if ((p = strmatch(cp, dtcp->am_string, + *format)) != NULL) { + /* + * AM. + */ + if (tm->tm_hour == 12) + tm->tm_hour = 0; + cp = p; + } else if ((p = strmatch(cp, dtcp->pm_string, + *format)) != NULL) { + /* + * PM. + */ + if (tm->tm_hour > 12) + return (NULL); /* error */ + else if (tm->tm_hour != 12) + tm->tm_hour += 12; + cp = p; + } + break; + + case 'r': /* Shorthand for %I:%M:%S AM or PM */ + cp = strptime(cp, "%I:%M:%S %p", tm); + if (cp == NULL) + return (NULL); + break; + + case 'R': /* Time as %H:%M */ + cp = strptime(cp, "%H:%M", tm); + if (cp == NULL) + return (NULL); + break; + + case 'S': /* Seconds */ + /* + * This is optional; if we're at the end of the + * string, or the next character is white + * space, don't try to match it. + */ + if ((c = *cp) != '\0' + && !isspace((unsigned char)c)) { + cp = cvtnum(cp, &tm->tm_sec); + if (cp == NULL) + return (NULL); /* no digits */ + if (tm->tm_sec > 59) + return (NULL); + } + if ((c = *cp) == '\0' + || isspace((unsigned char)c)) + format = skipnws(format); + break; + + case 'T': /* Shorthand for %H:%M:%S */ + cp = strptime(cp, "%H:%M:%S", tm); + if (cp == NULL) + return (NULL); + break; + + case 'x': /* Localized date format */ + cp = strptime(cp, dtcp->sdate_format, tm); + if (cp == NULL) + return (NULL); + break; + + case 'X': /* Localized time format */ + cp = strptime(cp, dtcp->time_format, tm); + if (cp == NULL) + return (NULL); + break; + + case 'y': /* Year in the form yy */ + cp = yearmatch(cp, format, tm, &hadyear); + if (cp == NULL) + return (NULL); + if (hadyear) { + if (tm->tm_year < 69) + tm->tm_year += 100; + } + return (cp); /* match is complete */ + + case 'Y': /* Year in the form ccyy */ + cp = yearmatch(cp, format, tm, &hadyear); + if (cp == NULL) + return (NULL); + if (hadyear) { + tm->tm_year -= 1900; + if (tm->tm_year < 0) + return (NULL); + } + return (cp); /* match is complete */ + + default: + return (NULL); /* unknown conversion */ + } + } else { + if (isspace((unsigned char)c)) { + while ((ch = *cp++) != '\0' + && isspace((unsigned char)ch)) + ; + cp--; + } else { + if (*cp++ != c) + return (NULL); + } + } + } + return (cp); +} + +/* + * Try to match the beginning of the string pointed to by "cp" with the string + * pointed to by "string". The match is independent of the case of either + * string. + * + * "termc" is the next character in the format string following the one for + * which this match is being done. If the match succeeds, make sure the next + * character after the match is either '\0', or that it would match "termc". + * + * If both matches succeed, return a pointer to the next character after the + * first match. Otherwise, return NULL. + */ +static char * +strmatch(cp, string, termc) + register char *cp; + register char *string; + char termc; +{ + register unsigned char c, strc; + + /* + * Match the beginning portion of "cp" with "string". + */ + while ((strc = *string++) != '\0') { + c = *cp++; + if (isupper(c)) + c = tolower(c); + if (isupper(strc)) + strc = tolower(strc); + if (c != strc) + return (NULL); + } + + if ((c = *cp) != '\0') { + if (isspace((unsigned char)termc)) { + if (!isspace(c)) + return (NULL); + } else { + if (c != (unsigned char)termc) + return (NULL); + } + } + return (cp); +} + +/* + * Try to match a %y or %Y specification. + * If it matches, try matching the rest of the format. If it succeeds, just + * return. Otherwise, try backing the scan up, ignoring the %y/%Y and any + * following non-white-space string. If that succeeds, just return. (This + * permits a missing year to be detected if it's at the beginning of a date, as + * well as if it's at the end of a date, so that formats such as "%Y/%m/%d" can + * match "3/14" and default the year.) + * + * Set "*hadyearp" to indicate whether a year was specified or not. + */ +static char * +yearmatch(cp, format, tm, hadyearp) + register char *cp; + char *format; + struct tm *tm; + int *hadyearp; +{ + register int c; + char *savecp; + int saveyear; + + /* + * This is optional; if we're at the end of the + * string, or the next character is white + * space, don't try to match it. + */ + if ((c = *cp) != '\0' && !isspace((unsigned char)c)) { + savecp = cp; + saveyear = tm->tm_year; + cp = cvtnum(cp, &tm->tm_year); + if (cp == NULL) + return (NULL); /* no digits */ + if ((c = *cp) == '\0' + || isspace((unsigned char)c)) + format = skipnws(format); + + /* + * Year can also be optional if it's at + * the *beginning* of a date. We check + * this by trying to parse the rest of + * the date here. If we succeed, OK; + * otherwise, we skip over the %y and + * try again. + */ + cp = strptime(cp, format, tm); + if (cp != NULL) + *hadyearp = 1; + else { + *hadyearp = 0; + cp = savecp; + format = skipnws(format); + tm->tm_year = saveyear; + cp = strptime(cp, format, tm); + } + } else { + *hadyearp = 0; + if ((c = *cp) == '\0' + || isspace((unsigned char)c)) + format = skipnws(format); + cp = strptime(cp, format, tm); + } + + return (cp); +} + +/* + * Try to match a (decimal) number in the string pointed to by "cp". + * If the match succeeds, store the result in the "int" pointed to by "nump" + * and return a pointer to the character following the number in the string. + * If it fails, return NULL. + */ +static char * +cvtnum(cp, nump) + register char *cp; + int *nump; +{ + register int c; + register int i; + + c = (unsigned char)*cp++; + if (!isdigit(c)) + return (NULL); /* no digits */ + i = 0; + do { + i = i*10 + c - '0'; + c = (unsigned char)*cp++; + } while (isdigit(c)); + *nump = i; + return (cp - 1); +} + +/* + * If a format item (such as %H, hours) is followed by a non-white-space + * character other than "%", and the part of the string that matched the format + * item is followed by white space, the string of non-white-space, + * non-format-item characters following that format item may be omitted. + */ +static char * +skipnws(format) + register char *format; +{ + register char c; + + /* + * Skip over non-white-space, non-digit characters. "%" is special. + */ + while ((c = *format) != '\0' && !isspace((unsigned char)c)) { + if (c == '%') { + /* + * This is a format item. If it's %%, skip it as + * that's a non-white space, non-digit character. + */ + if (*(format + 1) == '%') + format++; /* skip % */ + else + break; + } + format++; + } + + return (format); +} |