summaryrefslogtreecommitdiff
path: root/usr/src/lib/libbc/libc/gen/common/strptime.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/libbc/libc/gen/common/strptime.c')
-rw-r--r--usr/src/lib/libbc/libc/gen/common/strptime.c483
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);
+}