Ejemplo n.º 1
0
static char *
_strptime(const char *buf, const char *fmt, struct tm *tm, int initialize)
{
	unsigned char c;
	const unsigned char *bp, *ep;
	size_t len;
	int alt_format, i, offs;
	int neg = 0;
	static int century, relyear;

	if (initialize) {
		century = TM_YEAR_BASE;
		relyear = -1;
	}

	bp = (unsigned char *)buf;
	while ((c = *fmt) != '\0') {
		/* Clear `alternate' modifier prior to new conversion. */
		alt_format = 0;

		/* Eat up white-space. */
		if (isspace(c)) {
			while (isspace(*bp))
				bp++;

			fmt++;
			continue;
		}
				
		if ((c = *fmt++) != '%')
			goto literal;


again:		switch (c = *fmt++) {
		case '%':	/* "%%" is converted to "%". */
literal:
		if (c != *bp++)
			return (NULL);

		break;

		/*
		 * "Alternative" modifiers. Just set the appropriate flag
		 * and start over again.
		 */
		case 'E':	/* "%E?" alternative conversion modifier. */
			_LEGAL_ALT(0);
			alt_format |= _ALT_E;
			goto again;

		case 'O':	/* "%O?" alternative conversion modifier. */
			_LEGAL_ALT(0);
			alt_format |= _ALT_O;
			goto again;
			
		/*
		 * "Complex" conversion rules, implemented through recursion.
		 */
		case 'c':	/* Date and time, using the locale's format. */
			_LEGAL_ALT(_ALT_E);
			if (!(bp = _strptime(bp, _ctloc(d_t_fmt), tm, 0)))
				return (NULL);
			break;

		case 'D':	/* The date as "%m/%d/%y". */
			_LEGAL_ALT(0);
			if (!(bp = _strptime(bp, "%m/%d/%y", tm, 0)))
				return (NULL);
			break;

		case 'F':	/* The date as "%Y-%m-%d". */
			_LEGAL_ALT(0);
			if (!(bp = _strptime(bp, "%Y-%m-%d", tm, 0)))
				return (NULL);
			continue;

		case 'R':	/* The time as "%H:%M". */
			_LEGAL_ALT(0);
			if (!(bp = _strptime(bp, "%H:%M", tm, 0)))
				return (NULL);
			break;

		case 'r':	/* The time as "%I:%M:%S %p". */
			_LEGAL_ALT(0);
			if (!(bp = _strptime(bp, "%I:%M:%S %p", tm, 0)))
				return (NULL);
			break;

		case 'T':	/* The time as "%H:%M:%S". */
			_LEGAL_ALT(0);
			if (!(bp = _strptime(bp, "%H:%M:%S", tm, 0)))
				return (NULL);
			break;

		case 'X':	/* The time, using the locale's format. */
			_LEGAL_ALT(_ALT_E);
			if (!(bp = _strptime(bp, _ctloc(t_fmt), tm, 0)))
				return (NULL);
			break;

		case 'x':	/* The date, using the locale's format. */
			_LEGAL_ALT(_ALT_E);
			if (!(bp = _strptime(bp, _ctloc(d_fmt), tm, 0)))
				return (NULL);
			break;

		/*
		 * "Elementary" conversion rules.
		 */
		case 'A':	/* The day of week, using the locale's form. */
		case 'a':
			_LEGAL_ALT(0);
			for (i = 0; i < 7; i++) {
				/* Full name. */
				len = strlen(_ctloc(day[i]));
				if (strncasecmp(_ctloc(day[i]), bp, len) == 0)
					break;

				/* Abbreviated name. */
				len = strlen(_ctloc(abday[i]));
				if (strncasecmp(_ctloc(abday[i]), bp, len) == 0)
					break;
			}

			/* Nothing matched. */
			if (i == 7)
				return (NULL);

			tm->tm_wday = i;
			bp += len;
			break;

		case 'B':	/* The month, using the locale's form. */
		case 'b':
		case 'h':
			_LEGAL_ALT(0);
			for (i = 0; i < 12; i++) {
				/* Full name. */
				len = strlen(_ctloc(mon[i]));
				if (strncasecmp(_ctloc(mon[i]), bp, len) == 0)
					break;

				/* Abbreviated name. */
				len = strlen(_ctloc(abmon[i]));
				if (strncasecmp(_ctloc(abmon[i]), bp, len) == 0)
					break;
			}

			/* Nothing matched. */
			if (i == 12)
				return (NULL);

			tm->tm_mon = i;
			bp += len;
			break;

		case 'C':	/* The century number. */
			_LEGAL_ALT(_ALT_E);
			if (!(_conv_num(&bp, &i, 0, 99)))
				return (NULL);

			century = i * 100;
			break;

		case 'd':	/* The day of month. */
		case 'e':
			_LEGAL_ALT(_ALT_O);
			if (!(_conv_num(&bp, &tm->tm_mday, 1, 31)))
				return (NULL);
			break;

		case 'k':	/* The hour (24-hour clock representation). */
			_LEGAL_ALT(0);
			/* FALLTHROUGH */
		case 'H':
			_LEGAL_ALT(_ALT_O);
			if (!(_conv_num(&bp, &tm->tm_hour, 0, 23)))
				return (NULL);
			break;

		case 'l':	/* The hour (12-hour clock representation). */
			_LEGAL_ALT(0);
			/* FALLTHROUGH */
		case 'I':
			_LEGAL_ALT(_ALT_O);
			if (!(_conv_num(&bp, &tm->tm_hour, 1, 12)))
				return (NULL);
			break;

		case 'j':	/* The day of year. */
			_LEGAL_ALT(0);
			if (!(_conv_num(&bp, &tm->tm_yday, 1, 366)))
				return (NULL);
			tm->tm_yday--;
			break;

		case 'M':	/* The minute. */
			_LEGAL_ALT(_ALT_O);
			if (!(_conv_num(&bp, &tm->tm_min, 0, 59)))
				return (NULL);
			break;

		case 'm':	/* The month. */
			_LEGAL_ALT(_ALT_O);
			if (!(_conv_num(&bp, &tm->tm_mon, 1, 12)))
				return (NULL);
			tm->tm_mon--;
			break;

		case 'p':	/* The locale's equivalent of AM/PM. */
			_LEGAL_ALT(0);
			/* AM? */
			len = strlen(_ctloc(am_pm[0]));
			if (strncasecmp(_ctloc(am_pm[0]), bp, len) == 0) {
				if (tm->tm_hour > 12)	/* i.e., 13:00 AM ?! */
					return (NULL);
				else if (tm->tm_hour == 12)
					tm->tm_hour = 0;

				bp += len;
				break;
			}
			/* PM? */
			len = strlen(_ctloc(am_pm[1]));
			if (strncasecmp(_ctloc(am_pm[1]), bp, len) == 0) {
				if (tm->tm_hour > 12)	/* i.e., 13:00 PM ?! */
					return (NULL);
				else if (tm->tm_hour < 12)
					tm->tm_hour += 12;

				bp += len;
				break;
			}

			/* Nothing matched. */
			return (NULL);

		case 'S':	/* The seconds. */
			_LEGAL_ALT(_ALT_O);
			if (!(_conv_num(&bp, &tm->tm_sec, 0, 61)))
				return (NULL);
			break;

		case 'U':	/* The week of year, beginning on sunday. */
		case 'W':	/* The week of year, beginning on monday. */
			_LEGAL_ALT(_ALT_O);
			/*
			 * XXX This is bogus, as we can not assume any valid
			 * information present in the tm structure at this
			 * point to calculate a real value, so just check the
			 * range for now.
			 */
			 if (!(_conv_num(&bp, &i, 0, 53)))
				return (NULL);
			 break;

		case 'w':	/* The day of week, beginning on sunday. */
			_LEGAL_ALT(_ALT_O);
			if (!(_conv_num(&bp, &tm->tm_wday, 0, 6)))
				return (NULL);
			break;

		case 'u':	/* The day of week, monday = 1. */
			_LEGAL_ALT(_ALT_O);
			if (!(_conv_num(&bp, &i, 1, 7)))
				return (NULL);
			tm->tm_wday = i % 7;
			continue;

		case 'g':	/* The year corresponding to the ISO week
				 * number but without the century.
				 */
			if (!(_conv_num(&bp, &i, 0, 99)))
				return (NULL);				
			continue;

		case 'G':	/* The year corresponding to the ISO week
				 * number with century.
				 */
			do
				bp++;
			while (isdigit(*bp));
			continue;

		case 'V':	/* The ISO 8601:1988 week number as decimal */
			if (!(_conv_num(&bp, &i, 0, 53)))
				return (NULL);
			continue;

		case 'Y':	/* The year. */
			_LEGAL_ALT(_ALT_E);
			if (!(_conv_num(&bp, &i, 0, 9999)))
				return (NULL);

			relyear = -1;
			tm->tm_year = i - TM_YEAR_BASE;
			break;

		case 'y':	/* The year within the century (2 digits). */
			_LEGAL_ALT(_ALT_E | _ALT_O);
			if (!(_conv_num(&bp, &relyear, 0, 99)))
				return (NULL);
			break;

		case 'Z':
			tzset();
			if (strncmp((const char *)bp, gmt, 3) == 0) {
				tm->tm_isdst = 0;
#ifdef TM_GMTOFF
				tm->TM_GMTOFF = 0;
#endif
#ifdef TM_ZONE
				tm->TM_ZONE = gmt;
#endif
				bp += 3;
			} else {
				ep = _find_string(bp, &i,
					       	 (const char * const *)tzname,
					       	  NULL, 2);
				if (ep != NULL) {
					tm->tm_isdst = i;
#ifdef TM_GMTOFF
					tm->TM_GMTOFF = -(timezone);
#endif
#ifdef TM_ZONE
					tm->TM_ZONE = tzname[i];
#endif
				}
				bp = ep;
			}
			continue;

		case 'z':
			/*
			 * We recognize all ISO 8601 formats:
			 * Z	= Zulu time/UTC
			 * [+-]hhmm
			 * [+-]hh:mm
			 * [+-]hh
			 * We recognize all RFC-822/RFC-2822 formats:
			 * UT|GMT
			 *          North American : UTC offsets
			 * E[DS]T = Eastern : -4 | -5
			 * C[DS]T = Central : -5 | -6
			 * M[DS]T = Mountain: -6 | -7
			 * P[DS]T = Pacific : -7 | -8
			 *          Military
			 * [A-IL-M] = -1 ... -9 (J not used)
			 * [N-Y]  = +1 ... +12
			 */
			while (isspace(*bp))
				bp++;

			switch (*bp++) {
			case 'G':
				if (*bp++ != 'M')
					return NULL;
				/*FALLTHROUGH*/
			case 'U':
				if (*bp++ != 'T')
					return NULL;
				/*FALLTHROUGH*/
			case 'Z':
				tm->tm_isdst = 0;
#ifdef TM_GMTOFF
				tm->TM_GMTOFF = 0;
#endif
#ifdef TM_ZONE
				tm->TM_ZONE = utc;
#endif
				continue;
			case '+':
				neg = 0;
				break;
			case '-':
				neg = 1;
				break;
			default:
				--bp;
				ep = _find_string(bp, &i, nast, NULL, 4);
				if (ep != NULL) {
#ifdef TM_GMTOFF
					tm->TM_GMTOFF = -5 - i;
#endif
#ifdef TM_ZONE
					tm->TM_ZONE = __UNCONST(nast[i]);
#endif
					bp = ep;
					continue;
				}
				ep = _find_string(bp, &i, nadt, NULL, 4);
				if (ep != NULL) {
					tm->tm_isdst = 1;
#ifdef TM_GMTOFF
					tm->TM_GMTOFF = -4 - i;
#endif
#ifdef TM_ZONE
					tm->TM_ZONE = __UNCONST(nadt[i]);
#endif
					bp = ep;
					continue;
				}

				if ((*bp >= 'A' && *bp <= 'I') ||
				    (*bp >= 'L' && *bp <= 'Y')) {
#ifdef TM_GMTOFF
					/* Argh! No 'J'! */
					if (*bp >= 'A' && *bp <= 'I')
						tm->TM_GMTOFF =
						    ('A' - 1) - (int)*bp;
					else if (*bp >= 'L' && *bp <= 'M')
						tm->TM_GMTOFF = 'A' - (int)*bp;
					else if (*bp >= 'N' && *bp <= 'Y')
						tm->TM_GMTOFF = (int)*bp - 'M';
#endif
#ifdef TM_ZONE
					tm->TM_ZONE = NULL; /* XXX */
#endif
					bp++;
					continue;
				}
				return NULL;
			}
			offs = 0;
			for (i = 0; i < 4; ) {
				if (isdigit(*bp)) {
					offs = offs * 10 + (*bp++ - '0');
					i++;
					continue;
				}
				if (i == 2 && *bp == ':') {
					bp++;
					continue;
				}
				break;
			}
			switch (i) {
			case 2:
				offs *= 100;
				break;
			case 4:
				i = offs % 100;
				if (i >= 60)
					return NULL;
				/* Convert minutes into decimal */
				offs = (offs / 100) * 100 + (i * 50) / 30;
				break;
			default:
				return NULL;
			}
			if (neg)
				offs = -offs;
			tm->tm_isdst = 0;	/* XXX */
#ifdef TM_GMTOFF
			tm->TM_GMTOFF = offs;
#endif
#ifdef TM_ZONE
			tm->TM_ZONE = NULL;	/* XXX */
#endif
			continue;

		/*
		 * Miscellaneous conversions.
		 */
		case 'n':	/* Any kind of white-space. */
		case 't':
			_LEGAL_ALT(0);
			while (isspace(*bp))
				bp++;
			break;


		default:	/* Unknown/unsupported conversion. */
			return (NULL);
		}


	}

	/*
	 * We need to evaluate the two digit year spec (%y)
	 * last as we can get a century spec (%C) at any time.
	 */
	if (relyear != -1) {
		if (century == TM_YEAR_BASE) {
			if (relyear <= 68)
				tm->tm_year = relyear + 2000 - TM_YEAR_BASE;
			else
				tm->tm_year = relyear + 1900 - TM_YEAR_BASE;
		} else {
			tm->tm_year = relyear + century - TM_YEAR_BASE;
		}
	}

	return ((char *)bp);
}
Ejemplo n.º 2
0
static unsigned char *
_strptime(const unsigned char *buf, const char *fmt, struct tm *tm, struct century_relyear *cr)
{
    unsigned char c;
    const unsigned char *bp;
    size_t len = 0;
    int alt_format, i;

    bp = (unsigned char *)buf;
    while ((c = *fmt) != '\0') {
        /* Clear `alternate' modifier prior to new conversion. */
        alt_format = 0;

        /* Eat up white-space. */
        if (isspace(c)) {
            while (isspace(*bp))
                bp++;

            fmt++;
            continue;
        }
                
        if ((c = *fmt++) != '%')
            goto literal;


again:      switch (c = *fmt++) {
        case '%':   /* "%%" is converted to "%". */
literal:
        if (c != *bp++)
            return (NULL);

        break;

        /*
         * "Alternative" modifiers. Just set the appropriate flag
         * and start over again.
         */
        case 'E':   /* "%E?" alternative conversion modifier. */
            _LEGAL_ALT(0);
            alt_format |= _ALT_E;
            goto again;

        case 'O':   /* "%O?" alternative conversion modifier. */
            _LEGAL_ALT(0);
            alt_format |= _ALT_O;
            goto again;
            
        /*
         * "Complex" conversion rules, implemented through recursion.
         */
        case 'c':   /* Date and time, using the locale's format. */
            _LEGAL_ALT(_ALT_E);
            if (!(bp = _strptime(bp, _ctloc(d_t_fmt), tm, cr)))
                return (NULL);
            break;

        case 'D':   /* The date as "%m/%d/%y". */
            _LEGAL_ALT(0);
            if (!(bp = _strptime(bp, "%m/%d/%y", tm, cr)))
                return (NULL);
            break;
    
        case 'R':   /* The time as "%H:%M". */
            _LEGAL_ALT(0);
            if (!(bp = _strptime(bp, "%H:%M", tm, cr)))
                return (NULL);
            break;

        case 'r':   /* The time as "%I:%M:%S %p". */
            _LEGAL_ALT(0);
            if (!(bp = _strptime(bp, "%I:%M:%S %p", tm, cr)))
                return (NULL);
            break;

        case 'T':   /* The time as "%H:%M:%S". */
            _LEGAL_ALT(0);
            if (!(bp = _strptime(bp, "%H:%M:%S", tm, cr)))
                return (NULL);
            break;

        case 'X':   /* The time, using the locale's format. */
            _LEGAL_ALT(_ALT_E);
            if (!(bp = _strptime(bp, _ctloc(t_fmt), tm, cr)))
                return (NULL);
            break;

        case 'x':   /* The date, using the locale's format. */
            _LEGAL_ALT(_ALT_E);
            if (!(bp = _strptime(bp, _ctloc(d_fmt), tm, cr)))
                return (NULL);
            break;

        /*
         * "Elementary" conversion rules.
         */
        case 'A':   /* The day of week, using the locale's form. */
        case 'a':
            _LEGAL_ALT(0);
            for (i = 0; i < 7; i++) {
                /* Full name. */
                len = strlen(_ctloc(day[i]));
                if (strncasecmp(_ctloc(day[i]), (const char*)bp, len) == 0)
                    break;

                /* Abbreviated name. */
                len = strlen(_ctloc(abday[i]));
                if (strncasecmp(_ctloc(abday[i]), (const char*)bp, len) == 0)
                    break;
            }

            /* Nothing matched. */
            if (i == 7)
                return (NULL);

            tm->tm_wday = i;
            bp += len;
            break;

        case 'B':   /* The month, using the locale's form. */
        case 'b':
        case 'h':
            _LEGAL_ALT(0);
            for (i = 0; i < 12; i++) {
                /* Full name. */
                len = strlen(_ctloc(mon[i]));
                if (strncasecmp(_ctloc(mon[i]), (const char*)bp, len) == 0)
                    break;

                /* Abbreviated name. */
                len = strlen(_ctloc(abmon[i]));
                if (strncasecmp(_ctloc(abmon[i]), (const char*)bp, len) == 0)
                    break;
            }

            /* Nothing matched. */
            if (i == 12)
                return (NULL);

            tm->tm_mon = i;
            bp += len;
            break;

        case 'C':   /* The century number. */
            _LEGAL_ALT(_ALT_E);
            if (!(_conv_num(&bp, &i, 0, 99)))
                return (NULL);

            cr->century = i * 100;
            break;

        case 'd':   /* The day of month. */
        case 'e':
            _LEGAL_ALT(_ALT_O);
            if (!(_conv_num(&bp, &tm->tm_mday, 1, 31)))
                return (NULL);
            break;

        case 'k':   /* The hour (24-hour clock representation). */
            _LEGAL_ALT(0);
            /* FALLTHROUGH */
        case 'H':
            _LEGAL_ALT(_ALT_O);
            if (!(_conv_num(&bp, &tm->tm_hour, 0, 23)))
                return (NULL);
            break;

        case 'l':   /* The hour (12-hour clock representation). */
            _LEGAL_ALT(0);
            /* FALLTHROUGH */
        case 'I':
            _LEGAL_ALT(_ALT_O);
            if (!(_conv_num(&bp, &tm->tm_hour, 1, 12)))
                return (NULL);
            break;

        case 'j':   /* The day of year. */
            _LEGAL_ALT(0);
            if (!(_conv_num(&bp, &tm->tm_yday, 1, 366)))
                return (NULL);
            tm->tm_yday--;
            break;

        case 'M':   /* The minute. */
            _LEGAL_ALT(_ALT_O);
            if (!(_conv_num(&bp, &tm->tm_min, 0, 59)))
                return (NULL);
            break;

        case 'm':   /* The month. */
            _LEGAL_ALT(_ALT_O);
            if (!(_conv_num(&bp, &tm->tm_mon, 1, 12)))
                return (NULL);
            tm->tm_mon--;
            break;

        case 'p':   /* The locale's equivalent of AM/PM. */
            _LEGAL_ALT(0);
            /* AM? */
            len = strlen(_ctloc(am_pm[0]));
            if (strncasecmp(_ctloc(am_pm[0]), (const char*)bp, len) == 0) {
                if (tm->tm_hour > 12)   /* i.e., 13:00 AM ?! */
                    return (NULL);
                else if (tm->tm_hour == 12)
                    tm->tm_hour = 0;

                bp += len;
                break;
            }
            /* PM? */
            len = strlen(_ctloc(am_pm[1]));
            if (strncasecmp(_ctloc(am_pm[1]), (const char*)bp, len) == 0) {
                if (tm->tm_hour > 12)   /* i.e., 13:00 PM ?! */
                    return (NULL);
                else if (tm->tm_hour < 12)
                    tm->tm_hour += 12;

                bp += len;
                break;
            }

            /* Nothing matched. */
            return (NULL);

        case 'S':   /* The seconds. */
            _LEGAL_ALT(_ALT_O);
            if (!(_conv_num(&bp, &tm->tm_sec, 0, 61)))
                return (NULL);
            break;

        case 'U':   /* The week of year, beginning on sunday. */
        case 'W':   /* The week of year, beginning on monday. */
            _LEGAL_ALT(_ALT_O);
            /*
             * XXX This is bogus, as we can not assume any valid
             * information present in the tm structure at this
             * point to calculate a real value, so just check the
             * range for now.
             */
             if (!(_conv_num(&bp, &i, 0, 53)))
                return (NULL);
             break;

        case 'w':   /* The day of week, beginning on sunday. */
            _LEGAL_ALT(_ALT_O);
            if (!(_conv_num(&bp, &tm->tm_wday, 0, 6)))
                return (NULL);
            break;

        case 'Y':   /* The year. */
            _LEGAL_ALT(_ALT_E);
            if (!(_conv_num(&bp, &i, 0, 9999)))
                return (NULL);

            cr->relyear = -1;
            tm->tm_year = i - TM_YEAR_BASE;
            break;

        case 'y':   /* The year within the century (2 digits). */
            _LEGAL_ALT(_ALT_E | _ALT_O);
            if (!(_conv_num(&bp, &cr->relyear, 0, 99)))
                return (NULL);
            break;

        /*
         * Miscellaneous conversions.
         */
        case 'n':   /* Any kind of white-space. */
        case 't':
            _LEGAL_ALT(0);
            while (isspace(*bp))
                bp++;
            break;


        default:    /* Unknown/unsupported conversion. */
            return (NULL);
        }


    }

    /*
     * We need to evaluate the two digit year spec (%y)
     * last as we can get a century spec (%C) at any time.
     */
    if (cr->relyear != -1) {
        if (cr->century == TM_YEAR_BASE) {
            if (cr->relyear <= 68)
                tm->tm_year = cr->relyear + 2000 - TM_YEAR_BASE;
            else
                tm->tm_year = cr->relyear + 1900 - TM_YEAR_BASE;
        } else {
            tm->tm_year = cr->relyear + cr->century - TM_YEAR_BASE;
        }
    }

    return (unsigned char*)bp;
}