timeptr->tm_yday = 0; } } /* strptime: roken */ //extern "C" char * //strptime (const char *buf, const char *format, struct tm *timeptr) _DEFUN (strptime, (buf, format, timeptr), _CONST char *buf _AND _CONST char *format _AND struct tm *timeptr) { char c; struct lc_time_T *_CurrentTimeLocale = __get_current_time_locale (); for (; (c = *format) != '\0'; ++format) { char *s; int ret; if (isspace (c)) { while (isspace (*buf)) ++buf; } else if (c == '%' && format[1] != '\0') { c = *++format; if (c == 'E' || c == 'O') c = *++format; switch (c) { case 'A' : ret = match_string (&buf, _ctloc (weekday)); if (ret < 0)
char * nl_langinfo_l(nl_item item, locale_t loc) { char *ret, *cs; const char *s; FIX_LOCALE(loc); switch (item) { case CODESET: s = XLOCALE_CTYPE(loc)->runes->__encoding; if (strcmp(s, "EUC-CN") == 0) ret = "eucCN"; else if (strcmp(s, "EUC-JP") == 0) ret = "eucJP"; else if (strcmp(s, "EUC-KR") == 0) ret = "eucKR"; else if (strcmp(s, "EUC-TW") == 0) ret = "eucTW"; else if (strcmp(s, "BIG5") == 0) ret = "Big5"; else if (strcmp(s, "MSKanji") == 0) ret = "SJIS"; else if (strcmp(s, "NONE") == 0) ret = "POSIX"; else if (strncmp(s, "NONE:", 5) == 0) ret = (char *)(s + 5); else ret = (char *)s; break; case D_T_FMT: ret = (char *) __get_current_time_locale(loc)->c_fmt; break; case D_FMT: ret = (char *) __get_current_time_locale(loc)->x_fmt; break; case T_FMT: ret = (char *) __get_current_time_locale(loc)->X_fmt; break; case T_FMT_AMPM: ret = (char *) __get_current_time_locale(loc)->ampm_fmt; break; case AM_STR: ret = (char *) __get_current_time_locale(loc)->am; break; case PM_STR: ret = (char *) __get_current_time_locale(loc)->pm; break; case DAY_1: case DAY_2: case DAY_3: case DAY_4: case DAY_5: case DAY_6: case DAY_7: ret = (char*) __get_current_time_locale(loc)->weekday[_REL(DAY_1)]; break; case ABDAY_1: case ABDAY_2: case ABDAY_3: case ABDAY_4: case ABDAY_5: case ABDAY_6: case ABDAY_7: ret = (char*) __get_current_time_locale(loc)->wday[_REL(ABDAY_1)]; break; case MON_1: case MON_2: case MON_3: case MON_4: case MON_5: case MON_6: case MON_7: case MON_8: case MON_9: case MON_10: case MON_11: case MON_12: ret = (char*) __get_current_time_locale(loc)->month[_REL(MON_1)]; break; case ABMON_1: case ABMON_2: case ABMON_3: case ABMON_4: case ABMON_5: case ABMON_6: case ABMON_7: case ABMON_8: case ABMON_9: case ABMON_10: case ABMON_11: case ABMON_12: ret = (char*) __get_current_time_locale(loc)->mon[_REL(ABMON_1)]; break; case ALTMON_1: case ALTMON_2: case ALTMON_3: case ALTMON_4: case ALTMON_5: case ALTMON_6: case ALTMON_7: case ALTMON_8: case ALTMON_9: case ALTMON_10: case ALTMON_11: case ALTMON_12: ret = (char*) __get_current_time_locale(loc)->alt_month[_REL(ALTMON_1)]; break; case ERA: /* XXX: need to be implemented */ ret = ""; break; case ERA_D_FMT: /* XXX: need to be implemented */ ret = ""; break; case ERA_D_T_FMT: /* XXX: need to be implemented */ ret = ""; break; case ERA_T_FMT: /* XXX: need to be implemented */ ret = ""; break; case ALT_DIGITS: /* XXX: need to be implemented */ ret = ""; break; case RADIXCHAR: ret = (char*) __get_current_numeric_locale(loc)->decimal_point; break; case THOUSEP: ret = (char*) __get_current_numeric_locale(loc)->thousands_sep; break; case YESEXPR: ret = (char*) __get_current_messages_locale(loc)->yesexpr; break; case NOEXPR: ret = (char*) __get_current_messages_locale(loc)->noexpr; break; /* * YESSTR and NOSTR items marked with LEGACY are available, but not * recomended by SUSv2 to be used in portable applications since * they're subject to remove in future specification editions. */ case YESSTR: /* LEGACY */ ret = (char*) __get_current_messages_locale(loc)->yesstr; break; case NOSTR: /* LEGACY */ ret = (char*) __get_current_messages_locale(loc)->nostr; break; /* * SUSv2 special formatted currency string */ case CRNCYSTR: ret = ""; cs = (char*) __get_current_monetary_locale(loc)->currency_symbol; if (*cs != '\0') { char pos = localeconv_l(loc)->p_cs_precedes; if (pos == localeconv_l(loc)->n_cs_precedes) { char psn = '\0'; if (pos == CHAR_MAX) { if (strcmp(cs, __get_current_monetary_locale(loc)->mon_decimal_point) == 0) psn = '.'; } else psn = pos ? '-' : '+'; if (psn != '\0') { int clen = strlen(cs); if ((loc->csym = reallocf(loc->csym, clen + 2)) != NULL) { *loc->csym = psn; strcpy(loc->csym + 1, cs); ret = loc->csym; } } } } break; case D_MD_ORDER: /* FreeBSD local extension */ ret = (char *) __get_current_time_locale(loc)->md_order; break; default: ret = ""; } return (ret); }
static char * _fmt(const char *format, const struct tm *t, char *pt, const char *ptlim) { int Ealternative, Oalternative, PadIndex; #ifdef __ORCAC__ #define tptr (&_C_time_locale) #else struct lc_time_T *tptr = __get_current_time_locale(__get_locale()); #endif for ( ; *format; ++format) { if (*format == '%') { Ealternative = 0; Oalternative = 0; PadIndex = PAD_DEFAULT; label: switch (*++format) { case '\0': --format; break; case 'A': pt = _add((t->tm_wday < 0 || t->tm_wday >= DAYSPERWEEK) ? "?" : tptr->weekday[t->tm_wday], pt, ptlim); continue; case 'a': pt = _add((t->tm_wday < 0 || t->tm_wday >= DAYSPERWEEK) ? "?" : tptr->wday[t->tm_wday], pt, ptlim); continue; case 'B': pt = _add((t->tm_mon < 0 || t->tm_mon >= MONSPERYEAR) ? "?" : (Oalternative ? tptr->alt_month : tptr->month)[t->tm_mon], pt, ptlim); continue; continue; case 'b': case 'h': pt = _add((t->tm_mon < 0 || t->tm_mon >= MONSPERYEAR) ? "?" : tptr->mon[t->tm_mon], pt, ptlim); continue; case 'C': /* * %C used to do a... * _fmt("%a %b %e %X %Y", t); * ...whereas now POSIX 1003.2 calls for * something completely different. * (ado, 1993-05-24) */ pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 0, pt, ptlim); continue; case 'c': pt = _fmt(tptr->c_fmt, t, pt, ptlim); continue; case 'D': pt = _fmt("%m/%d/%y", t, pt, ptlim); continue; case 'd': pt = _conv(t->tm_mday, fmt_padding[PAD_FMT_DAYOFMONTH][PadIndex], pt, ptlim); continue; case 'E': if (Ealternative || Oalternative) break; Ealternative++; goto label; case 'O': /* * C99 locale modifiers. * The sequences * %Ec %EC %Ex %EX %Ey %EY * %Od %oe %OH %OI %Om %OM * %OS %Ou %OU %OV %Ow %OW %Oy * are supposed to provide alternate * representations. * * FreeBSD extension * %OB */ if (Ealternative || Oalternative) break; Oalternative++; goto label; case 'e': pt = _conv(t->tm_mday, fmt_padding[PAD_FMT_SDAYOFMONTH][PadIndex], pt, ptlim); continue; case 'F': pt = _fmt("%Y-%m-%d", t, pt, ptlim); continue; case 'H': pt = _conv(t->tm_hour, fmt_padding[PAD_FMT_HMS][PadIndex], pt, ptlim); continue; case 'I': pt = _conv((t->tm_hour % 12) ? (t->tm_hour % 12) : 12, fmt_padding[PAD_FMT_HMS][PadIndex], pt, ptlim); continue; case 'j': pt = _conv(t->tm_yday + 1, fmt_padding[PAD_FMT_DAYOFYEAR][PadIndex], pt, ptlim); continue; case 'k': /* * This used to be... * _conv(t->tm_hour % 12 ? * t->tm_hour % 12 : 12, 2, ' '); * ...and has been changed to the below to * match SunOS 4.1.1 and Arnold Robbins' * strftime version 3.0. That is, "%k" and * "%l" have been swapped. * (ado, 1993-05-24) */ pt = _conv(t->tm_hour, fmt_padding[PAD_FMT_SHMS][PadIndex], pt, ptlim); continue; #ifdef KITCHEN_SINK case 'K': /* ** After all this time, still unclaimed! */ pt = _add("kitchen sink", pt, ptlim); continue; #endif /* defined KITCHEN_SINK */ case 'l': /* * This used to be... * _conv(t->tm_hour, 2, ' '); * ...and has been changed to the below to * match SunOS 4.1.1 and Arnold Robbin's * strftime version 3.0. That is, "%k" and * "%l" have been swapped. * (ado, 1993-05-24) */ pt = _conv((t->tm_hour % 12) ? (t->tm_hour % 12) : 12, fmt_padding[PAD_FMT_SHMS][PadIndex], pt, ptlim); continue; case 'M': pt = _conv(t->tm_min, fmt_padding[PAD_FMT_HMS][PadIndex], pt, ptlim); continue; case 'm': pt = _conv(t->tm_mon + 1, fmt_padding[PAD_FMT_MONTH][PadIndex], pt, ptlim); continue; case 'n': pt = _add("\n", pt, ptlim); continue; case 'p': pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ? tptr->pm : tptr->am, pt, ptlim); continue; case 'R': pt = _fmt("%H:%M", t, pt, ptlim); continue; case 'r': pt = _fmt(tptr->ampm_fmt, t, pt, ptlim); continue; case 'S': pt = _conv(t->tm_sec, fmt_padding[PAD_FMT_HMS][PadIndex], pt, ptlim); continue; case 's': pt = _secs(t, pt, ptlim); continue; case 'T': pt = _fmt("%H:%M:%S", t, pt, ptlim); continue; case 't': pt = _add("\t", pt, ptlim); continue; case 'U': pt = _conv((t->tm_yday + DAYSPERWEEK - t->tm_wday) / DAYSPERWEEK, fmt_padding[PAD_FMT_WEEKOFYEAR][PadIndex], pt, ptlim); continue; case 'u': /* * From Arnold Robbins' strftime version 3.0: * "ISO 8601: Weekday as a decimal number * [1 (Monday) - 7]" * (ado, 1993-05-24) */ pt = _conv((t->tm_wday == 0) ? DAYSPERWEEK : t->tm_wday, "%d", pt, ptlim); continue; case 'V': /* ISO 8601 week number */ case 'G': /* ISO 8601 year (four digits) */ case 'g': /* ISO 8601 year (two digits) */ /* * From Arnold Robbins' strftime version 3.0: "the week number of the * year (the first Monday as the first day of week 1) as a decimal number * (01-53)." * (ado, 1993-05-24) * * From "http://www.ft.uni-erlangen.de/~mskuhn/iso-time.html" by Markus Kuhn: * "Week 01 of a year is per definition the first week which has the * Thursday in this year, which is equivalent to the week which contains * the fourth day of January. In other words, the first week of a new year * is the week which has the majority of its days in the new year. Week 01 * might also contain days from the previous year and the week before week * 01 of a year is the last week (52 or 53) of the previous year even if * it contains days from the new year. A week starts with Monday (day 1) * and ends with Sunday (day 7). For example, the first week of the year * 1997 lasts from 1996-12-30 to 1997-01-05..." * (ado, 1996-01-02) */ { int year; int base; int yday; int wday; int w; year = t->tm_year; base = TM_YEAR_BASE; yday = t->tm_yday; wday = t->tm_wday; for ( ; ; ) { int len; int bot; int top; len = isleap_sum(year, base) ? DAYSPERLYEAR : DAYSPERNYEAR; /* * What yday (-3 ... 3) does * the ISO year begin on? */ bot = ((yday + 11 - wday) % DAYSPERWEEK) - 3; /* * What yday does the NEXT * ISO year begin on? */ top = bot - (len % DAYSPERWEEK); if (top < -3) top += DAYSPERWEEK; top += len; if (yday >= top) { ++base; w = 1; break; } if (yday >= bot) { w = 1 + ((yday - bot) / DAYSPERWEEK); break; } --base; yday += isleap_sum(year, base) ? DAYSPERLYEAR : DAYSPERNYEAR; } #ifdef XPG4_1994_04_09 if ((w == 52 && t->tm_mon == TM_JANUARY) || (w == 1 && t->tm_mon == TM_DECEMBER)) w = 53; #endif /* defined XPG4_1994_04_09 */ if (*format == 'V') pt = _conv(w, fmt_padding[PAD_FMT_WEEKOFYEAR][PadIndex], pt, ptlim); else if (*format == 'g') { pt = _yconv(year, base, 0, 1, pt, ptlim); } else pt = _yconv(year, base, 1, 1, pt, ptlim); } continue; case 'v': /* * From Arnold Robbins' strftime version 3.0: * "date as dd-bbb-YYYY" * (ado, 1993-05-24) */ pt = _fmt("%e-%b-%Y", t, pt, ptlim); continue; case 'W': pt = _conv((t->tm_yday + DAYSPERWEEK - (t->tm_wday ? (t->tm_wday - 1) : (DAYSPERWEEK - 1))) / DAYSPERWEEK, fmt_padding[PAD_FMT_WEEKOFYEAR][PadIndex], pt, ptlim); continue; case 'w': pt = _conv(t->tm_wday, "%d", pt, ptlim); continue; case 'X': pt = _fmt(tptr->X_fmt, t, pt, ptlim); continue; case 'x': pt = _fmt(tptr->x_fmt, t, pt, ptlim); continue; case 'y': pt = _yconv(t->tm_year, TM_YEAR_BASE, 0, 1, pt, ptlim); continue; case 'Y': pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 1, pt, ptlim); continue; case 'Z': #ifdef TM_ZONE if (t->TM_ZONE != NULL) pt = _add(t->TM_ZONE, pt, ptlim); else #endif /* defined TM_ZONE */ if (t->tm_isdst >= 0) pt = _add(tzname[t->tm_isdst != 0], pt, ptlim); /* * C99 says that %Z must be replaced by the * empty string if the time zone is not * determinable. */ continue; case 'z': { int diff; char const * sign; if (t->tm_isdst < 0) continue; #ifdef TM_GMTOFF diff = t->TM_GMTOFF; #else /* !defined TM_GMTOFF */ /* * C99 says that the UTC offset must * be computed by looking only at * tm_isdst. This requirement is * incorrect, since it means the code * must rely on magic (in this case * altzone and timezone), and the * magic might not have the correct * offset. Doing things correctly is * tricky and requires disobeying C99; * see GNU C strftime for details. * For now, punt and conform to the * standard, even though it's incorrect. * * C99 says that %z must be replaced by the * empty string if the time zone is not * determinable, so output nothing if the * appropriate variables are not available. */ if (t->tm_isdst == 0) #ifdef USG_COMPAT diff = -timezone; #else /* !defined USG_COMPAT */ continue; #endif /* !defined USG_COMPAT */ else #ifdef ALTZONE diff = -altzone; #else /* !defined ALTZONE */ continue; #endif /* !defined ALTZONE */ #endif /* !defined TM_GMTOFF */ if (diff < 0) { sign = "-"; diff = -diff; } else sign = "+"; pt = _add(sign, pt, ptlim); diff /= SECSPERMIN; diff = (diff / MINSPERHOUR) * 100 + (diff % MINSPERHOUR); pt = _conv(diff, fmt_padding[PAD_FMT_YEAR][PadIndex], pt, ptlim); } continue; case '+': pt = _fmt(tptr->date_fmt, t, pt, ptlim); continue; case '-': if (PadIndex != PAD_DEFAULT) break; PadIndex = PAD_LESS; goto label; case '_': if (PadIndex != PAD_DEFAULT) break; PadIndex = PAD_SPACE; goto label; case '0': if (PadIndex != PAD_DEFAULT) break; PadIndex = PAD_ZERO; goto label; case '%': /* * X311J/88-090 (4.12.3.5): if conversion char is * undefined, behavior is undefined. Print out the * character itself as printf(3) also does. */ default: break; } }
static char* _strptime(const char* buf, const char* fmt, struct tm* tm, int* GMTp) { char c; const char* ptr; int i, len; int Ealternative, Oalternative; struct lc_time_T* tptr = __get_current_time_locale(); ptr = fmt; while (*ptr != 0) { if (*buf == 0) { break; } c = *ptr++; if (c != '%') { if (isspace((unsigned char)c)) while (*buf != 0 && isspace((unsigned char)*buf)) { buf++; } else if (c != *buf++) { return 0; } continue; } Ealternative = 0; Oalternative = 0; label: c = *ptr++; switch (c) { case 0: case '%': if (*buf++ != '%') { return 0; } break; case '+': buf = _strptime(buf, tptr->date_fmt, tm, GMTp); if (buf == 0) { return 0; } break; case 'C': if (!isdigit((unsigned char)*buf)) { return 0; } /* XXX This will break for 3-digit centuries. */ len = 2; for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { i *= 10; i += *buf - '0'; len--; } if (i < 19) { return 0; } tm->tm_year = i * 100 - 1900; break; case 'c': buf = _strptime(buf, tptr->c_fmt, tm, GMTp); if (buf == 0) { return 0; } break; case 'D': buf = _strptime(buf, "%m/%d/%y", tm, GMTp); if (buf == 0) { return 0; } break; case 'E': if (Ealternative || Oalternative) { break; } Ealternative++; goto label; case 'O': if (Ealternative || Oalternative) { break; } Oalternative++; goto label; case 'F': buf = _strptime(buf, "%Y-%m-%d", tm, GMTp); if (buf == 0) { return 0; } break; case 'R': buf = _strptime(buf, "%H:%M", tm, GMTp); if (buf == 0) { return 0; } break; case 'r': buf = _strptime(buf, tptr->ampm_fmt, tm, GMTp); if (buf == 0) { return 0; } break; case 'T': buf = _strptime(buf, "%H:%M:%S", tm, GMTp); if (buf == 0) { return 0; } break; case 'X': buf = _strptime(buf, tptr->X_fmt, tm, GMTp); if (buf == 0) { return 0; } break; case 'x': buf = _strptime(buf, tptr->x_fmt, tm, GMTp); if (buf == 0) { return 0; } break; case 'j': if (!isdigit((unsigned char)*buf)) { return 0; } len = 3; for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { i *= 10; i += *buf - '0'; len--; } if (i < 1 || i > 366) { return 0; } tm->tm_yday = i - 1; break; case 'M': case 'S': if (*buf == 0 || isspace((unsigned char)*buf)) { break; } if (!isdigit((unsigned char)*buf)) { return 0; } len = 2; for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { i *= 10; i += *buf - '0'; len--; } if (c == 'M') { if (i > 59) { return 0; } tm->tm_min = i; } else { if (i > 60) { return 0; } tm->tm_sec = i; } if (*buf != 0 && isspace((unsigned char)*buf)) while (*ptr != 0 && !isspace((unsigned char)*ptr)) { ptr++; } break; case 'H': case 'I': case 'k': case 'l': /* * Of these, %l is the only specifier explicitly * documented as not being zero-padded. However, * there is no harm in allowing zero-padding. * * XXX The %l specifier may gobble one too many * digits if used incorrectly. */ if (!isdigit((unsigned char)*buf)) { return 0; } len = 2; for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { i *= 10; i += *buf - '0'; len--; } if (c == 'H' || c == 'k') { if (i > 23) { return 0; } } else if (i > 12) { return 0; } tm->tm_hour = i; if (*buf != 0 && isspace((unsigned char)*buf)) while (*ptr != 0 && !isspace((unsigned char)*ptr)) { ptr++; } break; case 'p': /* * XXX This is bogus if parsed before hour-related * specifiers. */ len = strlen(tptr->am); if (strncasecmp(buf, tptr->am, len) == 0) { if (tm->tm_hour > 12) { return 0; } if (tm->tm_hour == 12) { tm->tm_hour = 0; } buf += len; break; } len = strlen(tptr->pm); if (strncasecmp(buf, tptr->pm, len) == 0) { if (tm->tm_hour > 12) { return 0; } if (tm->tm_hour != 12) { tm->tm_hour += 12; } buf += len; break; } return 0; case 'A': case 'a': for (i = 0; i < asizeof(tptr->weekday); i++) { len = strlen(tptr->weekday[i]); if (strncasecmp(buf, tptr->weekday[i], len) == 0) { break; } len = strlen(tptr->wday[i]); if (strncasecmp(buf, tptr->wday[i], len) == 0) { break; } } if (i == asizeof(tptr->weekday)) { return 0; } tm->tm_wday = i; buf += len; break; case 'U': case 'W': /* * 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 (!isdigit((unsigned char)*buf)) { return 0; } len = 2; for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { i *= 10; i += *buf - '0'; len--; } if (i > 53) { return 0; } if (*buf != 0 && isspace((unsigned char)*buf)) while (*ptr != 0 && !isspace((unsigned char)*ptr)) { ptr++; } break; case 'w': if (!isdigit((unsigned char)*buf)) { return 0; } i = *buf - '0'; if (i > 6) { return 0; } tm->tm_wday = i; if (*buf != 0 && isspace((unsigned char)*buf)) while (*ptr != 0 && !isspace((unsigned char)*ptr)) { ptr++; } break; case 'd': case 'e': /* * The %e specifier is explicitly documented as not * being zero-padded but there is no harm in allowing * such padding. * * XXX The %e specifier may gobble one too many * digits if used incorrectly. */ if (!isdigit((unsigned char)*buf)) { return 0; } len = 2; for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { i *= 10; i += *buf - '0'; len--; } if (i > 31) { return 0; } tm->tm_mday = i; if (*buf != 0 && isspace((unsigned char)*buf)) while (*ptr != 0 && !isspace((unsigned char)*ptr)) { ptr++; } break; case 'B': case 'b': case 'h': for (i = 0; i < asizeof(tptr->month); i++) { if (Oalternative) { if (c == 'B') { len = strlen(tptr->alt_month[i]); if (strncasecmp(buf, tptr->alt_month[i], len) == 0) { break; } } } else { len = strlen(tptr->month[i]); if (strncasecmp(buf, tptr->month[i], len) == 0) { break; } len = strlen(tptr->mon[i]); if (strncasecmp(buf, tptr->mon[i], len) == 0) { break; } } } if (i == asizeof(tptr->month)) { return 0; } tm->tm_mon = i; buf += len; break; case 'm': if (!isdigit((unsigned char)*buf)) { return 0; } len = 2; for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { i *= 10; i += *buf - '0'; len--; } if (i < 1 || i > 12) { return 0; } tm->tm_mon = i - 1; if (*buf != 0 && isspace((unsigned char)*buf)) while (*ptr != 0 && !isspace((unsigned char)*ptr)) { ptr++; } break; case 's': { char* cp; int sverrno; long n; time_t t; sverrno = errno; errno = 0; n = strtol(buf, &cp, 10); if (errno == ERANGE || (long)(t = n) != n) { errno = sverrno; return 0; } errno = sverrno; buf = cp; gmtime_r(&t, tm); *GMTp = 1; } break; case 'Y': case 'y': if (*buf == 0 || isspace((unsigned char)*buf)) { break; } if (!isdigit((unsigned char)*buf)) { return 0; } len = (c == 'Y') ? 4 : 2; for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { i *= 10; i += *buf - '0'; len--; } if (c == 'Y') { i -= 1900; } if (c == 'y' && i < 69) { i += 100; } if (i < 0) { return 0; } tm->tm_year = i; if (*buf != 0 && isspace((unsigned char)*buf)) while (*ptr != 0 && !isspace((unsigned char)*ptr)) { ptr++; } break; case 'Z': { const char* cp; char* zonestr; for (cp = buf; *cp && isupper((unsigned char)*cp); ++cp) { /*empty*/ } if (cp - buf) { zonestr = alloca(cp - buf + 1); strncpy(zonestr, buf, cp - buf); zonestr[cp - buf] = '\0'; tzset(); if (0 == strcmp(zonestr, "GMT")) { *GMTp = 1; } else if (0 == strcmp(zonestr, tzname[0])) { tm->tm_isdst = 0; } else if (0 == strcmp(zonestr, tzname[1])) { tm->tm_isdst = 1; } else { return 0; } buf += cp - buf; } } break; } } return (char*)buf; }
static char * _strptime(const char *buf, const char *fmt, struct tm *tm, int *GMTp, locale_t locale) { char c; const char *ptr; int day_offset = -1, wday_offset; int week_offset; int i, len; int flags; int Ealternative, Oalternative; const struct lc_time_T *tptr = __get_current_time_locale(locale); static int start_of_month[2][13] = { {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}, {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366} }; flags = FLAG_NONE; ptr = fmt; while (*ptr != 0) { c = *ptr++; if (c != '%') { if (isspace_l((unsigned char)c, locale)) while (*buf != 0 && isspace_l((unsigned char)*buf, locale)) buf++; else if (c != *buf++) return (NULL); continue; } Ealternative = 0; Oalternative = 0; label: c = *ptr++; switch (c) { case '%': if (*buf++ != '%') return (NULL); break; case '+': buf = _strptime(buf, tptr->date_fmt, tm, GMTp, locale); if (buf == NULL) return (NULL); flags |= FLAG_WDAY | FLAG_MONTH | FLAG_MDAY | FLAG_YEAR; break; case 'C': if (!isdigit_l((unsigned char)*buf, locale)) return (NULL); /* XXX This will break for 3-digit centuries. */ len = 2; for (i = 0; len && *buf != 0 && isdigit_l((unsigned char)*buf, locale); buf++) { i *= 10; i += *buf - '0'; len--; } if (i < 19) return (NULL); tm->tm_year = i * 100 - TM_YEAR_BASE; flags |= FLAG_YEAR; break; case 'c': buf = _strptime(buf, tptr->c_fmt, tm, GMTp, locale); if (buf == NULL) return (NULL); flags |= FLAG_WDAY | FLAG_MONTH | FLAG_MDAY | FLAG_YEAR; break; case 'D': buf = _strptime(buf, "%m/%d/%y", tm, GMTp, locale); if (buf == NULL) return (NULL); flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR; break; case 'E': if (Ealternative || Oalternative) break; Ealternative++; goto label; case 'O': if (Ealternative || Oalternative) break; Oalternative++; goto label; case 'F': buf = _strptime(buf, "%Y-%m-%d", tm, GMTp, locale); if (buf == NULL) return (NULL); flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR; break; case 'R': buf = _strptime(buf, "%H:%M", tm, GMTp, locale); if (buf == NULL) return (NULL); break; case 'r': buf = _strptime(buf, tptr->ampm_fmt, tm, GMTp, locale); if (buf == NULL) return (NULL); break; case 'T': buf = _strptime(buf, "%H:%M:%S", tm, GMTp, locale); if (buf == NULL) return (NULL); break; case 'X': buf = _strptime(buf, tptr->X_fmt, tm, GMTp, locale); if (buf == NULL) return (NULL); break; case 'x': buf = _strptime(buf, tptr->x_fmt, tm, GMTp, locale); if (buf == NULL) return (NULL); flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR; break; case 'j': if (!isdigit_l((unsigned char)*buf, locale)) return (NULL); len = 3; for (i = 0; len && *buf != 0 && isdigit_l((unsigned char)*buf, locale); buf++) { i *= 10; i += *buf - '0'; len--; } if (i < 1 || i > 366) return (NULL); tm->tm_yday = i - 1; flags |= FLAG_YDAY; break; case 'M': case 'S': if (*buf == 0 || isspace_l((unsigned char)*buf, locale)) break; if (!isdigit_l((unsigned char)*buf, locale)) return (NULL); len = 2; for (i = 0; len && *buf != 0 && isdigit_l((unsigned char)*buf, locale); buf++) { i *= 10; i += *buf - '0'; len--; } if (c == 'M') { if (i > 59) return (NULL); tm->tm_min = i; } else { if (i > 60) return (NULL); tm->tm_sec = i; } break; case 'H': case 'I': case 'k': case 'l': /* * Of these, %l is the only specifier explicitly * documented as not being zero-padded. However, * there is no harm in allowing zero-padding. * * XXX The %l specifier may gobble one too many * digits if used incorrectly. */ if (!isdigit_l((unsigned char)*buf, locale)) return (NULL); len = 2; for (i = 0; len && *buf != 0 && isdigit_l((unsigned char)*buf, locale); buf++) { i *= 10; i += *buf - '0'; len--; } if (c == 'H' || c == 'k') { if (i > 23) return (NULL); } else if (i > 12) return (NULL); tm->tm_hour = i; break; case 'p': /* * XXX This is bogus if parsed before hour-related * specifiers. */ len = strlen(tptr->am); if (strncasecmp_l(buf, tptr->am, len, locale) == 0) { if (tm->tm_hour > 12) return (NULL); if (tm->tm_hour == 12) tm->tm_hour = 0; buf += len; break; } len = strlen(tptr->pm); if (strncasecmp_l(buf, tptr->pm, len, locale) == 0) { if (tm->tm_hour > 12) return (NULL); if (tm->tm_hour != 12) tm->tm_hour += 12; buf += len; break; } return (NULL); case 'A': case 'a': for (i = 0; i < asizeof(tptr->weekday); i++) { len = strlen(tptr->weekday[i]); if (strncasecmp_l(buf, tptr->weekday[i], len, locale) == 0) break; len = strlen(tptr->wday[i]); if (strncasecmp_l(buf, tptr->wday[i], len, locale) == 0) break; } if (i == asizeof(tptr->weekday)) return (NULL); buf += len; tm->tm_wday = i; flags |= FLAG_WDAY; break; case 'U': case 'W': /* * 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 (!isdigit_l((unsigned char)*buf, locale)) return (NULL); len = 2; for (i = 0; len && *buf != 0 && isdigit_l((unsigned char)*buf, locale); buf++) { i *= 10; i += *buf - '0'; len--; } if (i > 53) return (NULL); if (c == 'U') day_offset = TM_SUNDAY; else day_offset = TM_MONDAY; week_offset = i; break; case 'w': if (!isdigit_l((unsigned char)*buf, locale)) return (NULL); i = *buf - '0'; buf++; if (i > 6) return (NULL); tm->tm_wday = i; flags |= FLAG_WDAY; break; case 'e': /* * With %e format, our strftime(3) adds a blank space * before single digits. */ if (*buf != 0 && isspace_l((unsigned char)*buf, locale)) buf++; /* FALLTHROUGH */ case 'd': /* * The %e specifier was once explicitly documented as * not being zero-padded but was later changed to * equivalent to %d. There is no harm in allowing * such padding. * * XXX The %e specifier may gobble one too many * digits if used incorrectly. */ if (!isdigit_l((unsigned char)*buf, locale)) return (NULL); len = 2; for (i = 0; len && *buf != 0 && isdigit_l((unsigned char)*buf, locale); buf++) { i *= 10; i += *buf - '0'; len--; } if (i > 31) return (NULL); tm->tm_mday = i; flags |= FLAG_MDAY; break; case 'B': case 'b': case 'h': for (i = 0; i < asizeof(tptr->month); i++) { if (Oalternative) { if (c == 'B') { len = strlen(tptr->alt_month[i]); if (strncasecmp_l(buf, tptr->alt_month[i], len, locale) == 0) break; } } else { len = strlen(tptr->month[i]); if (strncasecmp_l(buf, tptr->month[i], len, locale) == 0) break; } } /* * Try the abbreviated month name if the full name * wasn't found and Oalternative was not requested. */ if (i == asizeof(tptr->month) && !Oalternative) { for (i = 0; i < asizeof(tptr->month); i++) { len = strlen(tptr->mon[i]); if (strncasecmp_l(buf, tptr->mon[i], len, locale) == 0) break; } } if (i == asizeof(tptr->month)) return (NULL); tm->tm_mon = i; buf += len; flags |= FLAG_MONTH; break; case 'm': if (!isdigit_l((unsigned char)*buf, locale)) return (NULL); len = 2; for (i = 0; len && *buf != 0 && isdigit_l((unsigned char)*buf, locale); buf++) { i *= 10; i += *buf - '0'; len--; } if (i < 1 || i > 12) return (NULL); tm->tm_mon = i - 1; flags |= FLAG_MONTH; break; case 's': { char *cp; int sverrno; long n; time_t t; sverrno = errno; errno = 0; n = strtol_l(buf, &cp, 10, locale); if (errno == ERANGE || (long)(t = n) != n) { errno = sverrno; return (NULL); } errno = sverrno; buf = cp; if (gmtime_r(&t, tm) == NULL) return (NULL); *GMTp = 1; flags |= FLAG_YDAY | FLAG_WDAY | FLAG_MONTH | FLAG_MDAY | FLAG_YEAR; } break; case 'Y': case 'y': if (*buf == 0 || isspace_l((unsigned char)*buf, locale)) break; if (!isdigit_l((unsigned char)*buf, locale)) return (NULL); len = (c == 'Y') ? 4 : 2; for (i = 0; len && *buf != 0 && isdigit_l((unsigned char)*buf, locale); buf++) { i *= 10; i += *buf - '0'; len--; } if (c == 'Y') i -= TM_YEAR_BASE; if (c == 'y' && i < 69) i += 100; if (i < 0) return (NULL); tm->tm_year = i; flags |= FLAG_YEAR; break; case 'Z': { const char *cp; char *zonestr; for (cp = buf; *cp && isupper_l((unsigned char)*cp, locale); ++cp) { /*empty*/ } if (cp - buf) { zonestr = alloca(cp - buf + 1); strncpy(zonestr, buf, cp - buf); zonestr[cp - buf] = '\0'; tzset(); if (0 == strcmp(zonestr, "GMT") || 0 == strcmp(zonestr, "UTC")) { *GMTp = 1; } else if (0 == strcmp(zonestr, tzname[0])) { tm->tm_isdst = 0; } else if (0 == strcmp(zonestr, tzname[1])) { tm->tm_isdst = 1; } else { return (NULL); } buf += cp - buf; } } break; case 'z': { int sign = 1; len = 4; /* RFC 822/ISO 8601 */ if (*buf != '+') { if (*buf == '-') sign = -1; else if (*buf == 'Z') /* ISO 8601 Z (UTC) */ len = 0; else return (NULL); } buf++; i = 0; for (; len > 0; len--) { if (isdigit_l((unsigned char)*buf, locale)) { i *= 10; i += *buf - '0'; buf++; } else if (*buf == ':' && len == 2) { buf++; /* ISO 8601 +hh:mm */ if (isdigit_l((unsigned char)*buf, locale)) { i *= 10; i += *buf - '0'; buf++; } else { return (NULL); } } else if (len == 2) { i *= 100; /* ISO 8601 +hh */ break; } else { return (NULL); } } tm->tm_hour -= sign * (i / 100); tm->tm_min -= sign * (i % 100); *GMTp = 1; } break; case 'n': case 't': while (isspace_l((unsigned char)*buf, locale)) buf++; break; default: return (NULL); } } if (!(flags & FLAG_YDAY) && (flags & FLAG_YEAR)) { if ((flags & (FLAG_MONTH | FLAG_MDAY)) == (FLAG_MONTH | FLAG_MDAY)) { tm->tm_yday = start_of_month[isleap(tm->tm_year + TM_YEAR_BASE)][tm->tm_mon] + (tm->tm_mday - 1); flags |= FLAG_YDAY; } else if (day_offset != -1) { /* Set the date to the first Sunday (or Monday) * of the specified week of the year. */ if (!(flags & FLAG_WDAY)) { tm->tm_wday = day_offset; flags |= FLAG_WDAY; } tm->tm_yday = (7 - first_wday_of(tm->tm_year + TM_YEAR_BASE) + day_offset) % 7 + (week_offset - 1 + (tm->tm_wday == 0 ? day_offset : 0)) * 7 + tm->tm_wday - day_offset; flags |= FLAG_YDAY; } } if ((flags & (FLAG_YEAR | FLAG_YDAY)) == (FLAG_YEAR | FLAG_YDAY)) { if (!(flags & FLAG_MONTH)) { i = 0; while (tm->tm_yday >= start_of_month[isleap(tm->tm_year + TM_YEAR_BASE)][i]) i++; if (i > 12) { i = 1; tm->tm_yday -= start_of_month[isleap(tm->tm_year + TM_YEAR_BASE)][12]; tm->tm_year++; } tm->tm_mon = i - 1; flags |= FLAG_MONTH; } if (!(flags & FLAG_MDAY)) { tm->tm_mday = tm->tm_yday - start_of_month[isleap(tm->tm_year + TM_YEAR_BASE)] [tm->tm_mon] + 1; flags |= FLAG_MDAY; } if (!(flags & FLAG_WDAY)) { i = 0; wday_offset = first_wday_of(tm->tm_year); while (i++ <= tm->tm_yday) { if (wday_offset++ >= 6) wday_offset = 0; } tm->tm_wday = wday_offset; flags |= FLAG_WDAY; } } return ((char *)buf); }