#define _GNU_SOURCE
#include <stddef.h>
#include <stdio.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <ctype.h>
#include <wctype.h>
#include <wchar.h>
#include "local.h"
#include "locale_private.h"
#include "stdio_private.h"
#undef TOLOWER
#if !defined(MAKE_WCSFTIME)
#define CHAR char
#define CQ(a) a
#define t_snprintf __d_snprintf
#define t_strncmp strncmp
#define SFLG
#define _ctloc(x) (ctloclen = strlen(ctloc = x))
#define TOLOWER(c) tolower((int)(unsigned char)(c))
#define STRTOUL(c, p, b) strtoul((c), (p), (b))
#define STRCPY(a, b) strcpy((a), (b))
#define STRCHR(a, b) strchr((a), (b))
#define STRLEN(a) strlen(a)
#else
#define strftime wcsftime
#define strftime_l wcsftime_l
#define CHAR wchar_t
#define CQ(a) L##a
#define t_snprintf __d_swprintf
#define t_strncmp wcsncmp
#define TOLOWER(c) towlower((wint_t)(c))
#define STRTOUL(c, p, b) wcstoul((c), (p), (b))
#define STRCPY(a, b) wcscpy((a), (b))
#define STRCHR(a, b) wcschr((a), (b))
#define STRLEN(a) wcslen(a)
#define SFLG "l"
#ifdef WTIME_WDAY
#define _ctloc(x) (ctloclen = wcslen(ctloc = W##x))
#else
#define CTLOCBUFLEN 256
static const wchar_t *
__ctloc(wchar_t *buf, const char *elem, size_t *len_ret)
{
buf[CTLOCBUFLEN - 1] = L'\0';
*len_ret = mbstowcs(buf, elem, CTLOCBUFLEN - 1);
if (*len_ret == (size_t)-1)
*len_ret = 0;
return buf;
}
#define _ctloc(x) (ctloc = __ctloc(ctlocbuf, x, &ctloclen))
#endif
#endif
#define CHECK_LENGTH() \
if (len < 0 || (count += len) >= maxsize) \
return 0
#if YEAR_BASE < 0
#error "YEAR_BASE < 0"
#endif
static int
iso_year_adjust(const struct tm *tim_p)
{
int leap = isleap(tim_p->tm_year + (YEAR_BASE - (tim_p->tm_year < 0 ? 0 : 2000)));
#define PACK(yd, wd, lp) (((yd) << 4) + (wd << 1) + (lp))
switch (PACK(tim_p->tm_yday, tim_p->tm_wday, leap)) {
case PACK(0, 5, 0):
case PACK(0, 6, 0):
case PACK(0, 0, 0):
case PACK(0, 5, 1):
case PACK(0, 6, 1):
case PACK(0, 0, 1):
case PACK(1, 6, 0):
case PACK(1, 0, 0):
case PACK(1, 6, 1):
case PACK(1, 0, 1):
case PACK(2, 0, 0):
case PACK(2, 0, 1):
return -1;
case PACK(362, 1, 0):
case PACK(363, 1, 1):
case PACK(363, 1, 0):
case PACK(363, 2, 0):
case PACK(364, 1, 1):
case PACK(364, 2, 1):
case PACK(364, 1, 0):
case PACK(364, 2, 0):
case PACK(364, 3, 0):
case PACK(365, 1, 1):
case PACK(365, 2, 1):
case PACK(365, 3, 1):
return 1;
}
return 0;
#undef PACK
}
#ifdef _WANT_C99_TIME_FORMATS
typedef struct {
int year;
CHAR *era_C;
CHAR *era_Y;
} era_info_t;
static era_info_t *
#if defined(MAKE_WCSFTIME) && defined(WTIME_ERA)
get_era_info(const struct tm *tim_p, const wchar_t *era)
#else
get_era_info(const struct tm *tim_p, const char *era)
#endif
{
#if defined(MAKE_WCSFTIME) && defined(WTIME_ERA)
wchar_t *c;
const wchar_t *dir;
#define ERA_STRCHR(a, b) wcschr((a), (b))
#define ERA_STRNCPY(a, b, c) wcsncpy((a), (b), (c))
#define ERA_STRTOL(a, b, c) wcstol((a), (b), (c))
#else
char *c;
const char *dir;
#define ERA_STRCHR(a, b) strchr((a), (b))
#define ERA_STRNCPY(a, b, c) strncpy((a), (b), (c))
#define ERA_STRTOL(a, b, c) strtol((a), (b), (c))
#endif
long offset;
struct tm stm, etm;
era_info_t *ei;
ei = (era_info_t *)calloc(1, sizeof(era_info_t));
if (!ei)
return NULL;
stm.tm_isdst = etm.tm_isdst = 0;
while (era) {
dir = era;
era += 2;
offset = ERA_STRTOL(era, &c, 10);
era = c + 1;
stm.tm_year = ERA_STRTOL(era, &c, 10) - YEAR_BASE;
if (stm.tm_year <= -YEAR_BASE)
++stm.tm_year;
stm.tm_mon = ERA_STRTOL(c + 1, &c, 10) - 1;
stm.tm_mday = ERA_STRTOL(c + 1, &c, 10);
stm.tm_hour = stm.tm_min = stm.tm_sec = 0;
era = c + 1;
if (era[0] == '-' && era[1] == '*') {
etm = stm;
stm.tm_year = INT_MIN;
stm.tm_mon = stm.tm_mday = stm.tm_hour = stm.tm_min = stm.tm_sec = 0;
era += 3;
} else if (era[0] == '+' && era[1] == '*') {
etm.tm_year = INT_MAX;
etm.tm_mon = 11;
etm.tm_mday = 31;
etm.tm_hour = 23;
etm.tm_min = etm.tm_sec = 59;
era += 3;
} else {
etm.tm_year = ERA_STRTOL(era, &c, 10) - YEAR_BASE;
if (etm.tm_year <= -YEAR_BASE)
++etm.tm_year;
etm.tm_mon = ERA_STRTOL(c + 1, &c, 10) - 1;
etm.tm_mday = ERA_STRTOL(c + 1, &c, 10);
etm.tm_mday = 31;
etm.tm_hour = 23;
etm.tm_min = etm.tm_sec = 59;
era = c + 1;
}
if ((tim_p->tm_year > stm.tm_year
|| (tim_p->tm_year == stm.tm_year
&& (tim_p->tm_mon > stm.tm_mon
|| (tim_p->tm_mon == stm.tm_mon && tim_p->tm_mday >= stm.tm_mday))))
&& (tim_p->tm_year < etm.tm_year
|| (tim_p->tm_year == etm.tm_year
&& (tim_p->tm_mon < etm.tm_mon
|| (tim_p->tm_mon == etm.tm_mon && tim_p->tm_mday <= etm.tm_mday))))) {
size_t len;
if (*dir == '+' && stm.tm_year != INT_MIN)
ei->year = tim_p->tm_year - stm.tm_year + offset;
else
ei->year = etm.tm_year - tim_p->tm_year + offset;
c = ERA_STRCHR(era, ':');
#if defined(MAKE_WCSFTIME) && !defined(WTIME_ERA)
len = mbsnrtowcs(NULL, &era, c - era, 0, NULL);
if (len == (size_t)-1) {
free(ei);
return NULL;
}
#else
len = c - era;
#endif
ei->era_C = (CHAR *)malloc((len + 1) * sizeof(CHAR));
if (!ei->era_C) {
free(ei);
return NULL;
}
#if defined(MAKE_WCSFTIME) && !defined(WTIME_ERA)
len = mbsnrtowcs(ei->era_C, &era, c - era, len + 1, NULL);
#else
ERA_STRNCPY(ei->era_C, era, len);
era += len;
#endif
ei->era_C[len] = CQ('\0');
++era;
c = ERA_STRCHR(era, ';');
if (!c)
c = ERA_STRCHR(era, '\0');
#if defined(MAKE_WCSFTIME) && !defined(WTIME_ERA)
len = mbsnrtowcs(NULL, &era, c - era, 0, NULL);
if (len == (size_t)-1) {
free(ei->era_C);
free(ei);
return NULL;
}
#else
len = c - era;
#endif
ei->era_Y = (CHAR *)malloc((len + 1) * sizeof(CHAR));
if (!ei->era_Y) {
free(ei->era_C);
free(ei);
return NULL;
}
#if defined(MAKE_WCSFTIME) && !defined(WTIME_ERA)
len = mbsnrtowcs(ei->era_Y, &era, c - era, len + 1, NULL);
#else
ERA_STRNCPY(ei->era_Y, era, len);
era += len;
#endif
ei->era_Y[len] = CQ('\0');
return ei;
} else
era = ERA_STRCHR(era, ';');
if (era)
++era;
}
return NULL;
}
static void
free_era_info(era_info_t *ei)
{
free(ei->era_C);
free(ei->era_Y);
free(ei);
}
typedef struct {
size_t num;
CHAR **digit;
CHAR *buffer;
} alt_digits_t;
static alt_digits_t *
#if defined(MAKE_WCSFTIME) && defined(WTIME_ALT_DIGITS)
get_alt_digits(const wchar_t *alt_digits)
#else
get_alt_digits(const char *alt_digits)
#endif
{
alt_digits_t *adi;
#if defined(MAKE_WCSFTIME) && defined(WTIME_ALT_DIGITS)
const wchar_t *a, *e;
#define ALT_STRCHR(a, b) wcschr((a), (b))
#define ALT_STRCPY(a, b) wcscpy((a), (b))
#define ALT_STRLEN(a) wcslen(a)
#else
const char *a, *e;
#define ALT_STRCHR(a, b) strchr((a), (b))
#define ALT_STRCPY(a, b) strcpy((a), (b))
#define ALT_STRLEN(a) strlen(a)
#endif
CHAR *aa, *ae;
size_t len;
adi = (alt_digits_t *)calloc(1, sizeof(alt_digits_t));
if (!adi)
return NULL;
adi->num = 1;
for (a = alt_digits; (e = ALT_STRCHR(a, ';')) != NULL; a = e + 1)
++adi->num;
adi->digit = (CHAR **)calloc(adi->num, sizeof(CHAR *));
if (!adi->digit) {
free(adi);
return NULL;
}
#if defined(MAKE_WCSFTIME) && !defined(WTIME_ALT_DIGITS)
len = mbstowcs(NULL, alt_digits, 0);
if (len == (size_t)-1) {
free(adi->digit);
free(adi);
return NULL;
}
#else
len = ALT_STRLEN(alt_digits);
#endif
adi->buffer = (CHAR *)malloc((len + 1) * sizeof(CHAR));
if (!adi->buffer) {
free(adi->digit);
free(adi);
return NULL;
}
#if defined(MAKE_WCSFTIME) && !defined(WTIME_ALT_DIGITS)
mbstowcs(adi->buffer, alt_digits, len + 1);
#else
ALT_STRCPY(adi->buffer, alt_digits);
#endif
for (len = 0, aa = adi->buffer; (ae = STRCHR(aa, CQ(';'))) != NULL; ++len, aa = ae + 1) {
*ae = '\0';
adi->digit[len] = aa;
}
adi->digit[len] = aa;
return adi;
}
static void
free_alt_digits(alt_digits_t *adi)
{
free(adi->digit);
free(adi->buffer);
free(adi);
}
static int
conv_to_alt_digits(CHAR *buf, size_t bufsiz, unsigned num, alt_digits_t *adi)
{
if (num < adi->num) {
size_t len = STRLEN(adi->digit[num]);
if (bufsiz < len)
return -1;
STRCPY(buf, adi->digit[num]);
return (int)len;
}
return 0;
}
static size_t
__strftime(CHAR *s, size_t maxsize, const CHAR *format, const struct tm *tim_p, locale_t locale,
era_info_t **era_info, alt_digits_t **alt_digits)
#else
static size_t
__strftime(CHAR *s, size_t maxsize, const CHAR *format, const struct tm *tim_p, locale_t locale)
#define __strftime(s, m, f, t, l, e, a) __strftime((s), (m), (f), (t), (l))
#endif
{
size_t count = 0;
int len = 0;
const CHAR *ctloc;
#if defined(MAKE_WCSFTIME) && !defined(WTIME_WDAY)
CHAR ctlocbuf[CTLOCBUFLEN];
#endif
size_t i, ctloclen;
CHAR alt;
CHAR pad;
unsigned long width;
int tzset_called = 0;
for (;;) {
while (*format && *format != CQ('%')) {
if (count < maxsize - 1)
s[count++] = *format++;
else
return 0;
}
if (*format == CQ('\0'))
break;
format++;
pad = '\0';
width = 0;
if (*format == CQ('0') || *format == CQ('+'))
pad = *format++;
if (*format >= CQ('1') && *format <= CQ('9')) {
CHAR *fp;
width = STRTOUL(format, &fp, 10);
format = fp;
}
alt = CQ('\0');
if (*format == CQ('E')) {
alt = *format++;
#ifdef _WANT_C99_TIME_FORMATS
#if defined(MAKE_WCSFTIME) && defined(TIME_WERA)
if (!*era_info && *TIME_WERA)
*era_info = get_era_info(tim_p, TIME_WERA);
#else
if (!*era_info && *TIME_ERA)
*era_info = get_era_info(tim_p, TIME_ERA);
#endif
#endif
} else if (*format == CQ('O')) {
alt = *format++;
#ifdef _WANT_C99_TIME_FORMATS
#if defined(MAKE_WCSFTIME) && defined(TIME_WALT_DIGITS)
if (!*alt_digits && *TIME_WALT_DIGITS)
*alt_digits = get_alt_digits(TIME_WALT_DIGITS);
#else
if (!*alt_digits && *TIME_ALT_DIGITS)
*alt_digits = get_alt_digits(TIME_ALT_DIGITS);
#endif
#endif
}
switch (*format) {
case CQ('a'):
_ctloc(TIME_WDAY[tim_p->tm_wday]);
for (i = 0; i < ctloclen; i++) {
if (count < maxsize - 1)
s[count++] = ctloc[i];
else
return 0;
}
break;
case CQ('A'):
_ctloc(TIME_WEEKDAY[tim_p->tm_wday]);
for (i = 0; i < ctloclen; i++) {
if (count < maxsize - 1)
s[count++] = ctloc[i];
else
return 0;
}
break;
case CQ('b'):
case CQ('h'):
_ctloc(TIME_MON[tim_p->tm_mon]);
for (i = 0; i < ctloclen; i++) {
if (count < maxsize - 1)
s[count++] = ctloc[i];
else
return 0;
}
break;
case CQ('B'):
_ctloc(TIME_MONTH[tim_p->tm_mon]);
for (i = 0; i < ctloclen; i++) {
if (count < maxsize - 1)
s[count++] = ctloc[i];
else
return 0;
}
break;
case CQ('c'):
#ifdef _WANT_C99_TIME_FORMATS
if (alt == 'E' && *era_info && *TIME_ERA_D_T_FMT)
_ctloc(TIME_ERA_E_T_FMT);
else
#endif
_ctloc(TIME_C_FMT);
goto recurse;
case CQ('r'):
_ctloc(TIME_AMPM_FMT);
goto recurse;
case CQ('x'):
#ifdef _WANT_C99_TIME_FORMATS
if (alt == 'E' && *era_info && *TIME_ERA_D_FMT)
_ctloc(TIME_ERA_D_FMT);
else
#endif
_ctloc(TIME_X_FMT);
goto recurse;
case CQ('X'):
#ifdef _WANT_C99_TIME_FORMATS
if (alt == 'E' && *era_info && *TIME_ERA_T_FMT)
_ctloc(TIME_ERA_T_FMT) else
#endif
_ctloc(TIME_UX_FMT);
recurse:
if (*ctloc) {
len = __strftime(&s[count], maxsize - count, ctloc, tim_p, locale, era_info,
alt_digits);
if (len > 0)
count += len;
else
return 0;
}
break;
case CQ('C'): {
#ifdef _WANT_C99_TIME_FORMATS
if (alt == 'E' && *era_info)
len = t_snprintf(&s[count], maxsize - count, CQ("%" SFLG "s"), (*era_info)->era_C);
else
#endif
{
char *pos = "";
int year = tim_p->tm_year + YEAR_BASE;
int century = year / 100 - (year % 100 < 0);
if (pad)
{
if (century >= 100 && pad == CQ('+'))
pos = "+";
}
if (width < 1)
width = 1;
len = t_snprintf(&s[count], maxsize - count, CQ("%s%.*d"), pos, (int)width,
century);
}
CHECK_LENGTH();
} break;
case CQ('d'):
case CQ('e'):
#ifdef _WANT_C99_TIME_FORMATS
if (alt == CQ('O') && *alt_digits) {
if (tim_p->tm_mday < 10) {
if (*format == CQ('d')) {
if (maxsize - count < 2)
return 0;
len = conv_to_alt_digits(&s[count], maxsize - count, 0, *alt_digits);
CHECK_LENGTH();
}
if (*format == CQ('e') || len == 0)
s[count++] = CQ(' ');
}
len = conv_to_alt_digits(&s[count], maxsize - count, tim_p->tm_mday, *alt_digits);
CHECK_LENGTH();
if (len > 0)
break;
}
#endif
len = t_snprintf(&s[count], maxsize - count,
*format == CQ('d') ? CQ("%.2d") : CQ("%2d"), tim_p->tm_mday);
CHECK_LENGTH();
break;
case CQ('D'):
{
len = __strftime(&s[count], maxsize - count, CQ("%m/%d/%y"), tim_p, locale,
era_info, alt_digits);
if (len > 0)
count += len;
else
return 0;
}
break;
case CQ('F'): {
len = __strftime(&s[count], maxsize - count, CQ("%Y-%m-%d"), tim_p, locale, era_info,
alt_digits);
if (len > 0)
count += len;
else
return 0;
} break;
case CQ('g'):
{
int adjust = iso_year_adjust(tim_p);
int year = tim_p->tm_year >= 0 ? tim_p->tm_year % 100
: abs(tim_p->tm_year + YEAR_BASE) % 100;
if (adjust < 0 && tim_p->tm_year <= -YEAR_BASE)
adjust = 1;
else if (adjust > 0 && tim_p->tm_year < -YEAR_BASE)
adjust = -1;
len = t_snprintf(&s[count], maxsize - count, CQ("%.2d"),
((year + adjust) % 100 + 100) % 100);
CHECK_LENGTH();
}
break;
case CQ('G'): {
int sign = tim_p->tm_year < -YEAR_BASE;
int adjust = iso_year_adjust(tim_p);
int century = tim_p->tm_year >= 0 ? tim_p->tm_year / 100 + YEAR_BASE / 100
: abs(tim_p->tm_year + YEAR_BASE) / 100;
int year = tim_p->tm_year >= 0 ? tim_p->tm_year % 100
: abs(tim_p->tm_year + YEAR_BASE) % 100;
if (adjust < 0 && tim_p->tm_year <= -YEAR_BASE)
sign = adjust = 1;
else if (adjust > 0 && sign)
adjust = -1;
year += adjust;
if (year == -1) {
year = 99;
--century;
} else if (year == 100) {
year = 0;
++century;
}
CHAR fmtbuf[10], *fmt = fmtbuf;
unsigned p_year = century * 100 + year;
if (sign)
*fmt++ = CQ('-');
else if (pad == CQ('+') && p_year >= 10000) {
*fmt++ = CQ('+');
sign = 1;
}
if (width && sign)
--width;
*fmt++ = CQ('%');
if (pad)
*fmt++ = CQ('0');
STRCPY(fmt, CQ(".*u"));
len = t_snprintf(&s[count], maxsize - count, fmtbuf, (int)width, p_year);
if (len < 0 || (count += len) >= maxsize)
return 0;
} break;
case CQ('H'):
#ifdef _WANT_C99_TIME_FORMATS
if (alt == CQ('O') && *alt_digits) {
len = conv_to_alt_digits(&s[count], maxsize - count, tim_p->tm_hour, *alt_digits);
CHECK_LENGTH();
if (len > 0)
break;
}
#endif
__fallthrough;
case CQ('k'):
len = t_snprintf(&s[count], maxsize - count,
*format == CQ('k') ? CQ("%2d") : CQ("%.2d"), tim_p->tm_hour);
CHECK_LENGTH();
break;
case CQ('l'):
if (alt == CQ('O'))
alt = CQ('\0');
__fallthrough;
case CQ('I'): {
register int h12;
h12 = (tim_p->tm_hour == 0 || tim_p->tm_hour == 12) ? 12 : tim_p->tm_hour % 12;
#ifdef _WANT_C99_TIME_FORMATS
if (alt != CQ('O') || !*alt_digits
|| !(len = conv_to_alt_digits(&s[count], maxsize - count, h12, *alt_digits)))
#endif
len = t_snprintf(&s[count], maxsize - count,
*format == CQ('I') ? CQ("%.2d") : CQ("%2d"), h12);
CHECK_LENGTH();
} break;
case CQ('j'):
len = t_snprintf(&s[count], maxsize - count, CQ("%.3d"), tim_p->tm_yday + 1);
CHECK_LENGTH();
break;
case CQ('m'):
#ifdef _WANT_C99_TIME_FORMATS
if (alt != CQ('O') || !*alt_digits
|| !(len = conv_to_alt_digits(&s[count], maxsize - count, tim_p->tm_mon + 1,
*alt_digits)))
#endif
len = t_snprintf(&s[count], maxsize - count, CQ("%.2d"), tim_p->tm_mon + 1);
CHECK_LENGTH();
break;
case CQ('M'):
#ifdef _WANT_C99_TIME_FORMATS
if (alt != CQ('O') || !*alt_digits
|| !(len
= conv_to_alt_digits(&s[count], maxsize - count, tim_p->tm_min, *alt_digits)))
#endif
len = t_snprintf(&s[count], maxsize - count, CQ("%.2d"), tim_p->tm_min);
CHECK_LENGTH();
break;
case CQ('n'):
if (count < maxsize - 1)
s[count++] = CQ('\n');
else
return 0;
break;
case CQ('p'):
case CQ('P'):
_ctloc(TIME_AM_PM[tim_p->tm_hour < 12 ? 0 : 1]);
for (i = 0; i < ctloclen; i++) {
if (count < maxsize - 1)
s[count++] = (*format == CQ('P') ? (CHAR)TOLOWER(ctloc[i]) : ctloc[i]);
else
return 0;
}
break;
case CQ('q'):
len = t_snprintf(&s[count], maxsize - count, CQ("%.1d"), tim_p->tm_mon / 3 + 1);
CHECK_LENGTH();
break;
case CQ('R'):
len = t_snprintf(&s[count], maxsize - count, CQ("%.2d:%.2d"), tim_p->tm_hour,
tim_p->tm_min);
CHECK_LENGTH();
break;
case CQ('s'):
{
long offset = 0;
if (tim_p->tm_isdst >= 0) {
TZ_LOCK;
if (!tzset_called) {
_tzset_unlocked();
tzset_called = 1;
}
#if defined(__TM_GMTOFF)
offset = tim_p->__TM_GMTOFF;
#else
__tzinfo_type *tz = __gettzinfo();
offset = -tz->__tzrule[tim_p->tm_isdst > 0].offset;
#endif
TZ_UNLOCK;
}
len = t_snprintf(&s[count], maxsize - count, CQ("%lld"),
(((((long long)tim_p->tm_year - 69) / 4
- (tim_p->tm_year - 1) / 100 + (tim_p->tm_year + 299) / 400
+ (tim_p->tm_year - 70) * 365 + tim_p->tm_yday)
* 24
+ tim_p->tm_hour)
* 60
+ tim_p->tm_min)
* 60
+ tim_p->tm_sec - offset);
CHECK_LENGTH();
}
break;
case CQ('S'):
#ifdef _WANT_C99_TIME_FORMATS
if (alt != CQ('O') || !*alt_digits
|| !(len
= conv_to_alt_digits(&s[count], maxsize - count, tim_p->tm_sec, *alt_digits)))
#endif
len = t_snprintf(&s[count], maxsize - count, CQ("%.2d"), tim_p->tm_sec);
CHECK_LENGTH();
break;
case CQ('t'):
if (count < maxsize - 1)
s[count++] = CQ('\t');
else
return 0;
break;
case CQ('T'):
len = t_snprintf(&s[count], maxsize - count, CQ("%.2d:%.2d:%.2d"), tim_p->tm_hour,
tim_p->tm_min, tim_p->tm_sec);
CHECK_LENGTH();
break;
case CQ('u'):
#ifdef _WANT_C99_TIME_FORMATS
if (alt == CQ('O') && *alt_digits) {
len = conv_to_alt_digits(&s[count], maxsize - count,
tim_p->tm_wday == 0 ? 7 : tim_p->tm_wday, *alt_digits);
CHECK_LENGTH();
if (len > 0)
break;
}
#endif
if (count < maxsize - 1) {
if (tim_p->tm_wday == 0)
s[count++] = CQ('7');
else
s[count++] = CQ('0') + tim_p->tm_wday;
} else
return 0;
break;
case CQ('U'):
#ifdef _WANT_C99_TIME_FORMATS
if (alt != CQ('O') || !*alt_digits
|| !(len
= conv_to_alt_digits(&s[count], maxsize - count,
(tim_p->tm_yday + 7 - tim_p->tm_wday) / 7, *alt_digits)))
#endif
len = t_snprintf(&s[count], maxsize - count, CQ("%.2d"),
(tim_p->tm_yday + 7 - tim_p->tm_wday) / 7);
CHECK_LENGTH();
break;
case CQ('V'): {
int adjust = iso_year_adjust(tim_p);
int wday = (tim_p->tm_wday) ? tim_p->tm_wday - 1 : 6;
int week = (tim_p->tm_yday + 10 - wday) / 7;
if (adjust > 0)
week = 1;
else if (adjust < 0)
week = 52
+ (4 >= (wday - tim_p->tm_yday
- isleap(tim_p->tm_year
+ (YEAR_BASE - 1 - (tim_p->tm_year < 0 ? 0 : 2000)))));
#ifdef _WANT_C99_TIME_FORMATS
if (alt != CQ('O') || !*alt_digits
|| !(len = conv_to_alt_digits(&s[count], maxsize - count, week, *alt_digits)))
#endif
len = t_snprintf(&s[count], maxsize - count, CQ("%.2d"), week);
CHECK_LENGTH();
} break;
case CQ('v'):
{
CHAR fmtbuf[32], *fmt = fmtbuf;
STRCPY(fmt, CQ("%e-%b-%"));
fmt += STRLEN(fmt);
if (pad)
*fmt++ = pad;
else
*fmt++ = '+';
if (!pad)
width = 10;
if (width < 6)
width = 6;
width -= 6;
if (width) {
len = t_snprintf(fmt, fmtbuf + 32 - fmt, CQ("%lu"), width);
if (len > 0)
fmt += len;
}
STRCPY(fmt, CQ("Y"));
len = __strftime(&s[count], maxsize - count, fmtbuf, tim_p, locale, era_info,
alt_digits);
if (len > 0)
count += len;
else
return 0;
} break;
case CQ('w'):
#ifdef _WANT_C99_TIME_FORMATS
if (alt == CQ('O') && *alt_digits) {
len = conv_to_alt_digits(&s[count], maxsize - count, tim_p->tm_wday, *alt_digits);
CHECK_LENGTH();
if (len > 0)
break;
}
#endif
if (count < maxsize - 1)
s[count++] = CQ('0') + tim_p->tm_wday;
else
return 0;
break;
case CQ('W'): {
int wday = (tim_p->tm_wday) ? tim_p->tm_wday - 1 : 6;
wday = (tim_p->tm_yday + 7 - wday) / 7;
#ifdef _WANT_C99_TIME_FORMATS
if (alt != CQ('O') || !*alt_digits
|| !(len = conv_to_alt_digits(&s[count], maxsize - count, wday, *alt_digits)))
#endif
len = t_snprintf(&s[count], maxsize - count, CQ("%.2d"), wday);
CHECK_LENGTH();
} break;
case CQ('y'): {
#ifdef _WANT_C99_TIME_FORMATS
if (alt == 'E' && *era_info)
len = t_snprintf(&s[count], maxsize - count, CQ("%d"), (*era_info)->year);
else
#endif
{
int year = tim_p->tm_year % 100;
if (year < 0)
year += 100;
#ifdef _WANT_C99_TIME_FORMATS
if (alt != CQ('O') || !*alt_digits
|| !(len = conv_to_alt_digits(&s[count], maxsize - count, year, *alt_digits)))
#endif
len = t_snprintf(&s[count], maxsize - count, CQ("%.2d"), year);
}
CHECK_LENGTH();
} break;
case CQ('Y'):
#ifdef _WANT_C99_TIME_FORMATS
if (alt == 'E' && *era_info) {
ctloc = (*era_info)->era_Y;
goto recurse;
} else
#endif
{
CHAR fmtbuf[10], *fmt = fmtbuf;
int sign = tim_p->tm_year < -YEAR_BASE;
register unsigned year = (unsigned)tim_p->tm_year + (unsigned)YEAR_BASE;
if (sign) {
*fmt++ = CQ('-');
year = UINT_MAX - year + 1;
} else if (pad == CQ('+') && year >= 10000) {
*fmt++ = CQ('+');
sign = 1;
}
if (width && sign)
--width;
*fmt++ = CQ('%');
if (pad)
*fmt++ = CQ('0');
STRCPY(fmt, CQ(".*u"));
len = t_snprintf(&s[count], maxsize - count, fmtbuf, (int)width, year);
CHECK_LENGTH();
}
break;
case CQ('z'):
if (tim_p->tm_isdst >= 0) {
long offset;
TZ_LOCK;
if (!tzset_called) {
_tzset_unlocked();
tzset_called = 1;
}
#if defined(__TM_GMTOFF)
offset = tim_p->__TM_GMTOFF;
#else
__tzinfo_type *tz = __gettzinfo();
offset = -tz->__tzrule[tim_p->tm_isdst > 0].offset;
#endif
TZ_UNLOCK;
len = t_snprintf(&s[count], maxsize - count, CQ("%+03ld%.2ld"),
offset / SECSPERHOUR, labs(offset / SECSPERMIN) % 60L);
CHECK_LENGTH();
}
break;
case CQ('Z'):
if (tim_p->tm_isdst >= 0) {
size_t size;
const char *tznam = NULL;
TZ_LOCK;
if (!tzset_called) {
_tzset_unlocked();
tzset_called = 1;
}
#if defined(__TM_ZONE)
tznam = tim_p->__TM_ZONE;
#endif
if (!tznam)
tznam = tzname[tim_p->tm_isdst > 0];
size = strlen(tznam);
for (i = 0; i < size; i++) {
if (count < maxsize - 1)
s[count++] = tznam[i];
else {
TZ_UNLOCK;
return 0;
}
}
TZ_UNLOCK;
}
break;
case CQ('%'):
if (count < maxsize - 1)
s[count++] = CQ('%');
else
return 0;
break;
default:
return 0;
}
if (*format)
format++;
else
break;
}
if (maxsize)
s[count] = CQ('\0');
return count;
}
size_t
strftime(CHAR * __restrict s, size_t maxsize, const CHAR * __restrict format,
const struct tm * __restrict tim_p)
{
#ifdef _WANT_C99_TIME_FORMATS
era_info_t *era_info = NULL;
alt_digits_t *alt_digits = NULL;
size_t ret
= __strftime(s, maxsize, format, tim_p, __get_current_locale(), &era_info, &alt_digits);
if (era_info)
free_era_info(era_info);
if (alt_digits)
free_alt_digits(alt_digits);
return ret;
#else
return __strftime(s, maxsize, format, tim_p, __get_current_locale(), NULL, NULL);
#endif
}
size_t
strftime_l(CHAR * __restrict s, size_t maxsize, const CHAR *__restrict format,
const struct tm * __restrict tim_p, locale_t locale)
{
#ifdef _WANT_C99_TIME_FORMATS
era_info_t *era_info = NULL;
alt_digits_t *alt_digits = NULL;
size_t ret = __strftime(s, maxsize, format, tim_p, locale, &era_info, &alt_digits);
if (era_info)
free_era_info(era_info);
if (alt_digits)
free_alt_digits(alt_digits);
return ret;
#else
return __strftime(s, maxsize, format, tim_p, locale, NULL, NULL);
#endif
}