source: trunk/libs/newlib/src/newlib/libc/time/strftime.c @ 450

Last change on this file since 450 was 444, checked in by satin@…, 6 years ago

add newlib,libalmos-mkh, restructure shared_syscalls.h and mini-libc

File size: 57.5 KB
Line 
1/* NOTE:  This file defines both strftime() and wcsftime().  Take care when
2 * making changes.  See also wcsftime.c, and note the (small) overlap in the
3 * manual description, taking care to edit both as needed.  */
4/*
5 * strftime.c
6 * Original Author:     G. Haley
7 * Additions from:      Eric Blake, Corinna Vinschen
8 * Changes to allow dual use as wcstime, also:  Craig Howland
9 *
10 * Places characters into the array pointed to by s as controlled by the string
11 * pointed to by format. If the total number of resulting characters including
12 * the terminating null character is not more than maxsize, returns the number
13 * of characters placed into the array pointed to by s (not including the
14 * terminating null character); otherwise zero is returned and the contents of
15 * the array indeterminate.
16 */
17
18/*
19FUNCTION
20<<strftime>>, <<strftime_l>>---convert date and time to a formatted string
21
22INDEX
23        strftime
24
25INDEX
26        strftime_l
27
28SYNOPSIS
29        #include <time.h>
30        size_t strftime(char *restrict <[s]>, size_t <[maxsize]>,
31                        const char *restrict <[format]>,
32                        const struct tm *restrict <[timp]>);
33        size_t strftime_l(char *restrict <[s]>, size_t <[maxsize]>,
34                          const char *restrict <[format]>,
35                          const struct tm *restrict <[timp]>,
36                          locale_t <[locale]>);
37
38DESCRIPTION
39<<strftime>> converts a <<struct tm>> representation of the time (at
40<[timp]>) into a null-terminated string, starting at <[s]> and occupying
41no more than <[maxsize]> characters.
42
43<<strftime_l>> is like <<strftime>> but creates a string in a format
44as expected in locale <[locale]>.  If <[locale]> is LC_GLOBAL_LOCALE or
45not a valid locale object, the behaviour is undefined.
46
47You control the format of the output using the string at <[format]>.
48<<*<[format]>>> can contain two kinds of specifications: text to be
49copied literally into the formatted string, and time conversion
50specifications.  Time conversion specifications are two- and
51three-character sequences beginning with `<<%>>' (use `<<%%>>' to
52include a percent sign in the output).  Each defined conversion
53specification selects only the specified field(s) of calendar time
54data from <<*<[timp]>>>, and converts it to a string in one of the
55following ways:
56
57o+
58o %a
59The abbreviated weekday name according to the current locale. [tm_wday]
60
61o %A
62The full weekday name according to the current locale.
63In the default "C" locale, one of `<<Sunday>>', `<<Monday>>', `<<Tuesday>>',
64`<<Wednesday>>', `<<Thursday>>', `<<Friday>>', `<<Saturday>>'. [tm_wday]
65
66o %b
67The abbreviated month name according to the current locale. [tm_mon]
68
69o %B
70The full month name according to the current locale.
71In the default "C" locale, one of `<<January>>', `<<February>>',
72`<<March>>', `<<April>>', `<<May>>', `<<June>>', `<<July>>',
73`<<August>>', `<<September>>', `<<October>>', `<<November>>',
74`<<December>>'. [tm_mon]
75
76o %c
77The preferred date and time representation for the current locale.
78[tm_sec, tm_min, tm_hour, tm_mday, tm_mon, tm_year, tm_wday]
79
80o %C
81The century, that is, the year divided by 100 then truncated.  For
824-digit years, the result is zero-padded and exactly two characters;
83but for other years, there may a negative sign or more digits.  In
84this way, `<<%C%y>>' is equivalent to `<<%Y>>'. [tm_year]
85
86o %d
87The day of the month, formatted with two digits (from `<<01>>' to
88`<<31>>'). [tm_mday]
89
90o %D
91A string representing the date, in the form `<<"%m/%d/%y">>'.
92[tm_mday, tm_mon, tm_year]
93
94o %e
95The day of the month, formatted with leading space if single digit
96(from `<<1>>' to `<<31>>'). [tm_mday]
97
98o %E<<x>>
99In some locales, the E modifier selects alternative representations of
100certain modifiers <<x>>.  In newlib, it is ignored, and treated as %<<x>>.
101
102o %F
103A string representing the ISO 8601:2000 date format, in the form
104`<<"%Y-%m-%d">>'. [tm_mday, tm_mon, tm_year]
105
106o %g
107The last two digits of the week-based year, see specifier %G (from
108`<<00>>' to `<<99>>'). [tm_year, tm_wday, tm_yday]
109
110o %G
111The week-based year. In the ISO 8601:2000 calendar, week 1 of the year
112includes January 4th, and begin on Mondays. Therefore, if January 1st,
1132nd, or 3rd falls on a Sunday, that day and earlier belong to the last
114week of the previous year; and if December 29th, 30th, or 31st falls
115on Monday, that day and later belong to week 1 of the next year.  For
116consistency with %Y, it always has at least four characters.
117Example: "%G" for Saturday 2nd January 1999 gives "1998", and for
118Tuesday 30th December 1997 gives "1998". [tm_year, tm_wday, tm_yday]
119
120o %h
121Synonym for "%b". [tm_mon]
122
123o %H
124The hour (on a 24-hour clock), formatted with two digits (from
125`<<00>>' to `<<23>>'). [tm_hour]
126
127o %I
128The hour (on a 12-hour clock), formatted with two digits (from
129`<<01>>' to `<<12>>'). [tm_hour]
130
131o %j
132The count of days in the year, formatted with three digits
133(from `<<001>>' to `<<366>>'). [tm_yday]
134
135o %k
136The hour (on a 24-hour clock), formatted with leading space if single
137digit (from `<<0>>' to `<<23>>'). Non-POSIX extension (c.p. %I). [tm_hour]
138
139o %l
140The hour (on a 12-hour clock), formatted with leading space if single
141digit (from `<<1>>' to `<<12>>'). Non-POSIX extension (c.p. %H). [tm_hour]
142
143o %m
144The month number, formatted with two digits (from `<<01>>' to `<<12>>').
145[tm_mon]
146
147o %M
148The minute, formatted with two digits (from `<<00>>' to `<<59>>'). [tm_min]
149
150o %n
151A newline character (`<<\n>>').
152
153o %O<<x>>
154In some locales, the O modifier selects alternative digit characters
155for certain modifiers <<x>>.  In newlib, it is ignored, and treated as %<<x>>.
156
157o %p
158Either `<<AM>>' or `<<PM>>' as appropriate, or the corresponding strings for
159the current locale. [tm_hour]
160
161o %P
162Same as '<<%p>>', but in lowercase.  This is a GNU extension. [tm_hour]
163
164o %r
165Replaced by the time in a.m. and p.m. notation.  In the "C" locale this
166is equivalent to "%I:%M:%S %p".  In locales which don't define a.m./p.m.
167notations, the result is an empty string. [tm_sec, tm_min, tm_hour]
168
169o %R
170The 24-hour time, to the minute.  Equivalent to "%H:%M". [tm_min, tm_hour]
171
172o %s
173The time elapsed, in seconds, since the start of the Unix epoch at
1741970-01-01 00:00:00 UTC.
175
176o %S
177The second, formatted with two digits (from `<<00>>' to `<<60>>').  The
178value 60 accounts for the occasional leap second. [tm_sec]
179
180o %t
181A tab character (`<<\t>>').
182
183o %T
184The 24-hour time, to the second.  Equivalent to "%H:%M:%S". [tm_sec,
185tm_min, tm_hour]
186
187o %u
188The weekday as a number, 1-based from Monday (from `<<1>>' to
189`<<7>>'). [tm_wday]
190
191o %U
192The week number, where weeks start on Sunday, week 1 contains the first
193Sunday in a year, and earlier days are in week 0.  Formatted with two
194digits (from `<<00>>' to `<<53>>').  See also <<%W>>. [tm_wday, tm_yday]
195
196o %V
197The week number, where weeks start on Monday, week 1 contains January 4th,
198and earlier days are in the previous year.  Formatted with two digits
199(from `<<01>>' to `<<53>>').  See also <<%G>>. [tm_year, tm_wday, tm_yday]
200
201o %w
202The weekday as a number, 0-based from Sunday (from `<<0>>' to `<<6>>').
203[tm_wday]
204
205o %W
206The week number, where weeks start on Monday, week 1 contains the first
207Monday in a year, and earlier days are in week 0.  Formatted with two
208digits (from `<<00>>' to `<<53>>'). [tm_wday, tm_yday]
209
210o %x
211Replaced by the preferred date representation in the current locale.
212In the "C" locale this is equivalent to "%m/%d/%y".
213[tm_mon, tm_mday, tm_year]
214
215o %X
216Replaced by the preferred time representation in the current locale.
217In the "C" locale this is equivalent to "%H:%M:%S". [tm_sec, tm_min, tm_hour]
218
219o %y
220The last two digits of the year (from `<<00>>' to `<<99>>'). [tm_year]
221(Implementation interpretation:  always positive, even for negative years.)
222
223o %Y
224The full year, equivalent to <<%C%y>>.  It will always have at least four
225characters, but may have more.  The year is accurate even when tm_year
226added to the offset of 1900 overflows an int. [tm_year]
227
228o %z
229The offset from UTC.  The format consists of a sign (negative is west of
230Greewich), two characters for hour, then two characters for minutes
231(-hhmm or +hhmm).  If tm_isdst is negative, the offset is unknown and no
232output is generated; if it is zero, the offset is the standard offset for
233the current time zone; and if it is positive, the offset is the daylight
234savings offset for the current timezone. The offset is determined from
235the TZ environment variable, as if by calling tzset(). [tm_isdst]
236
237o %Z
238The time zone name.  If tm_isdst is negative, no output is generated.
239Otherwise, the time zone name is based on the TZ environment variable,
240as if by calling tzset(). [tm_isdst]
241
242o %%
243A single character, `<<%>>'.
244o-
245
246RETURNS
247When the formatted time takes up no more than <[maxsize]> characters,
248the result is the length of the formatted string.  Otherwise, if the
249formatting operation was abandoned due to lack of room, the result is
250<<0>>, and the string starting at <[s]> corresponds to just those
251parts of <<*<[format]>>> that could be completely filled in within the
252<[maxsize]> limit.
253
254PORTABILITY
255ANSI C requires <<strftime>>, but does not specify the contents of
256<<*<[s]>>> when the formatted string would require more than
257<[maxsize]> characters.  Unrecognized specifiers and fields of
258<<timp>> that are out of range cause undefined results.  Since some
259formats expand to 0 bytes, it is wise to set <<*<[s]>>> to a nonzero
260value beforehand to distinguish between failure and an empty string.
261This implementation does not support <<s>> being NULL, nor overlapping
262<<s>> and <<format>>.
263
264<<strftime_l>> is POSIX-1.2008.
265
266<<strftime>> and <<strftime_l>> require no supporting OS subroutines.
267
268BUGS
269(NOT Cygwin:) <<strftime>> ignores the LC_TIME category of the current
270locale, hard-coding the "C" locale settings.
271*/
272
273#include <newlib.h>
274#include <sys/config.h>
275#include <stddef.h>
276#include <stdio.h>
277#include <time.h>
278#include <string.h>
279#include <stdlib.h>
280#include <limits.h>
281#include <ctype.h>
282#include <wctype.h>
283#include "local.h"
284#include "../locale/setlocale.h"
285
286/* Defines to make the file dual use for either strftime() or wcsftime().
287 * To get wcsftime, define MAKE_WCSFTIME.
288 * To get strftime, do not define MAKE_WCSFTIME.
289 * Names are kept friendly to strftime() usage.  The biggest ugliness is the
290 * use of the CQ() macro to make either regular character constants and
291 * string literals or wide-character constants and wide-character-string
292 * literals, as appropriate.  */
293#if !defined(MAKE_WCSFTIME)
294#  define CHAR          char            /* string type basis */
295#  define CQ(a)         a               /* character constant qualifier */
296#  define SFLG                          /* %s flag (null for normal char) */
297#  define _ctloc(x) (ctloclen = strlen (ctloc = _CurrentTimeLocale->x), ctloc)
298#  define snprintf      sniprintf       /* avoid to pull in FP functions. */
299#  define TOLOWER(c)    tolower((int)(unsigned char)(c))
300#  define STRTOUL(c,p,b) strtoul((c),(p),(b))
301#  define STRCPY(a,b)   strcpy((a),(b))
302#  define STRCHR(a,b)   strchr((a),(b))
303#  define STRLEN(a)     strlen(a)
304# else
305#  define strftime      wcsftime        /* Alternate function name */
306#  define strftime_l    wcsftime_l      /* Alternate function name */
307#  define CHAR          wchar_t         /* string type basis */
308#  define CQ(a)         L##a            /* character constant qualifier */
309#  define snprintf      swprintf        /* wide-char equivalent function name */
310#  define strncmp       wcsncmp         /* wide-char equivalent function name */
311#  define TOLOWER(c)    towlower((wint_t)(c))
312#  define STRTOUL(c,p,b) wcstoul((c),(p),(b))
313#  define STRCPY(a,b)   wcscpy((a),(b))
314#  define STRCHR(a,b)   wcschr((a),(b))
315#  define STRLEN(a)     wcslen(a)
316#  define SFLG          "l"             /* %s flag (l for wide char) */
317#  ifdef __HAVE_LOCALE_INFO_EXTENDED__
318#   define _ctloc(x) (ctloclen = wcslen (ctloc = _CurrentTimeLocale->w##x), \
319                      ctloc)
320#  else
321#   define CTLOCBUFLEN   256            /* Arbitrary big buffer size */
322    const wchar_t *
323    __ctloc (wchar_t *buf, const char *elem, size_t *len_ret)
324    {
325      buf[CTLOCBUFLEN - 1] = L'\0';
326      *len_ret = mbstowcs (buf, elem, CTLOCBUFLEN - 1);
327      if (*len_ret == (size_t) -1 )
328        *len_ret = 0;
329      return buf;
330    }
331#   define _ctloc(x) (ctloc = __ctloc (ctlocbuf, _CurrentTimeLocale->x, \
332                      &ctloclen))
333#  endif
334#endif  /* MAKE_WCSFTIME */
335
336#define CHECK_LENGTH()  if (len < 0 || (count += len) >= maxsize) \
337                          return 0
338
339/* Enforce the coding assumptions that YEAR_BASE is positive.  (%C, %Y, etc.) */
340#if YEAR_BASE < 0
341#  error "YEAR_BASE < 0"
342#endif
343
344static const int dname_len[7] =
345{6, 6, 7, 9, 8, 6, 8};
346
347/* Using the tm_year, tm_wday, and tm_yday components of TIM_P, return
348   -1, 0, or 1 as the adjustment to add to the year for the ISO week
349   numbering used in "%g%G%V", avoiding overflow.  */
350static int
351iso_year_adjust (const struct tm *tim_p)
352{
353  /* Account for fact that tm_year==0 is year 1900.  */
354  int leap = isleap (tim_p->tm_year + (YEAR_BASE
355                                       - (tim_p->tm_year < 0 ? 0 : 2000)));
356
357  /* Pack the yday, wday, and leap year into a single int since there are so
358     many disparate cases.  */
359#define PACK(yd, wd, lp) (((yd) << 4) + (wd << 1) + (lp))
360  switch (PACK (tim_p->tm_yday, tim_p->tm_wday, leap))
361    {
362    case PACK (0, 5, 0): /* Jan 1 is Fri, not leap.  */
363    case PACK (0, 6, 0): /* Jan 1 is Sat, not leap.  */
364    case PACK (0, 0, 0): /* Jan 1 is Sun, not leap.  */
365    case PACK (0, 5, 1): /* Jan 1 is Fri, leap year.  */
366    case PACK (0, 6, 1): /* Jan 1 is Sat, leap year.  */
367    case PACK (0, 0, 1): /* Jan 1 is Sun, leap year.  */
368    case PACK (1, 6, 0): /* Jan 2 is Sat, not leap.  */
369    case PACK (1, 0, 0): /* Jan 2 is Sun, not leap.  */
370    case PACK (1, 6, 1): /* Jan 2 is Sat, leap year.  */
371    case PACK (1, 0, 1): /* Jan 2 is Sun, leap year.  */
372    case PACK (2, 0, 0): /* Jan 3 is Sun, not leap.  */
373    case PACK (2, 0, 1): /* Jan 3 is Sun, leap year.  */
374      return -1; /* Belongs to last week of previous year.  */
375    case PACK (362, 1, 0): /* Dec 29 is Mon, not leap.  */
376    case PACK (363, 1, 1): /* Dec 29 is Mon, leap year.  */
377    case PACK (363, 1, 0): /* Dec 30 is Mon, not leap.  */
378    case PACK (363, 2, 0): /* Dec 30 is Tue, not leap.  */
379    case PACK (364, 1, 1): /* Dec 30 is Mon, leap year.  */
380    case PACK (364, 2, 1): /* Dec 30 is Tue, leap year.  */
381    case PACK (364, 1, 0): /* Dec 31 is Mon, not leap.  */
382    case PACK (364, 2, 0): /* Dec 31 is Tue, not leap.  */
383    case PACK (364, 3, 0): /* Dec 31 is Wed, not leap.  */
384    case PACK (365, 1, 1): /* Dec 31 is Mon, leap year.  */
385    case PACK (365, 2, 1): /* Dec 31 is Tue, leap year.  */
386    case PACK (365, 3, 1): /* Dec 31 is Wed, leap year.  */
387      return 1; /* Belongs to first week of next year.  */
388    }
389  return 0; /* Belongs to specified year.  */
390#undef PACK
391}
392
393#ifdef _WANT_C99_TIME_FORMATS
394typedef struct {
395  int   year;
396  CHAR *era_C;
397  CHAR *era_Y;
398} era_info_t;
399
400static era_info_t *
401#if defined (MAKE_WCSFTIME) && defined (__HAVE_LOCALE_INFO_EXTENDED__)
402get_era_info (const struct tm *tim_p, const wchar_t *era)
403#else
404get_era_info (const struct tm *tim_p, const char *era)
405#endif
406{
407#if defined (MAKE_WCSFTIME) && defined (__HAVE_LOCALE_INFO_EXTENDED__)
408  wchar_t *c;
409  const wchar_t *dir;
410# define ERA_STRCHR(a,b)        wcschr((a),(b))
411# define ERA_STRNCPY(a,b,c)     wcsncpy((a),(b),(c))
412# define ERA_STRTOL(a,b,c)      wcstol((a),(b),(c))
413#else
414  char *c;
415  const char *dir;
416# define ERA_STRCHR(a,b)        strchr((a),(b))
417# define ERA_STRNCPY(a,b,c)     strncpy((a),(b),(c))
418# define ERA_STRTOL(a,b,c)      strtol((a),(b),(c))
419#endif
420  long offset;
421  struct tm stm, etm;
422  era_info_t *ei;
423
424  ei = (era_info_t *) calloc (1, sizeof (era_info_t));
425  if (!ei)
426    return NULL;
427
428  stm.tm_isdst = etm.tm_isdst = 0;
429  while (era)
430    {
431      dir = era;
432      era += 2;
433      offset = ERA_STRTOL (era, &c, 10);
434      era = c + 1;
435      stm.tm_year = ERA_STRTOL (era, &c, 10) - YEAR_BASE;
436      /* Adjust offset for negative gregorian dates. */
437      if (stm.tm_year <= -YEAR_BASE)
438        ++stm.tm_year;
439      stm.tm_mon = ERA_STRTOL (c + 1, &c, 10) - 1;
440      stm.tm_mday = ERA_STRTOL (c + 1, &c, 10);
441      stm.tm_hour = stm.tm_min = stm.tm_sec = 0;
442      era = c + 1;
443      if (era[0] == '-' && era[1] == '*')
444        {
445          etm = stm;
446          stm.tm_year = INT_MIN;
447          stm.tm_mon = stm.tm_mday = stm.tm_hour = stm.tm_min = stm.tm_sec = 0;
448          era += 3;
449        }
450      else if (era[0] == '+' && era[1] == '*')
451        {
452          etm.tm_year = INT_MAX;
453          etm.tm_mon = 11;
454          etm.tm_mday = 31;
455          etm.tm_hour = 23;
456          etm.tm_min = etm.tm_sec = 59;
457          era += 3;
458        }
459      else
460        {
461          etm.tm_year = ERA_STRTOL (era, &c, 10) - YEAR_BASE;
462          /* Adjust offset for negative gregorian dates. */
463          if (etm.tm_year <= -YEAR_BASE)
464            ++etm.tm_year;
465          etm.tm_mon = ERA_STRTOL (c + 1, &c, 10) - 1;
466          etm.tm_mday = ERA_STRTOL (c + 1, &c, 10);
467          etm.tm_mday = 31;
468          etm.tm_hour = 23;
469          etm.tm_min = etm.tm_sec = 59;
470          era = c + 1;
471        }
472      if ((tim_p->tm_year > stm.tm_year
473           || (tim_p->tm_year == stm.tm_year
474               && (tim_p->tm_mon > stm.tm_mon
475                   || (tim_p->tm_mon == stm.tm_mon
476                       && tim_p->tm_mday >= stm.tm_mday))))
477          && (tim_p->tm_year < etm.tm_year
478              || (tim_p->tm_year == etm.tm_year
479                  && (tim_p->tm_mon < etm.tm_mon
480                      || (tim_p->tm_mon == etm.tm_mon
481                          && tim_p->tm_mday <= etm.tm_mday)))))
482        {
483          /* Gotcha */
484          size_t len;
485
486          /* year */
487          if (*dir == '+' && stm.tm_year != INT_MIN)
488            ei->year = tim_p->tm_year - stm.tm_year + offset;
489          else
490            ei->year = etm.tm_year - tim_p->tm_year + offset;
491          /* era_C */
492          c = ERA_STRCHR (era, ':');
493#if defined (MAKE_WCSFTIME) && !defined (__HAVE_LOCALE_INFO_EXTENDED__)
494          len = mbsnrtowcs (NULL, &era, c - era, 0, NULL);
495          if (len == (size_t) -1)
496            {
497              free (ei);
498              return NULL;
499            }
500#else
501          len = c - era;
502#endif
503          ei->era_C = (CHAR *) malloc ((len + 1) * sizeof (CHAR));
504          if (!ei->era_C)
505            {
506              free (ei);
507              return NULL;
508            }
509#if defined (MAKE_WCSFTIME) && !defined (__HAVE_LOCALE_INFO_EXTENDED__)
510          len = mbsnrtowcs (ei->era_C, &era, c - era, len + 1, NULL);
511#else
512          ERA_STRNCPY (ei->era_C, era, len);
513          era += len;
514#endif
515          ei->era_C[len] = CQ('\0');
516          /* era_Y */
517          ++era;
518          c = ERA_STRCHR (era, ';');
519          if (!c)
520            c = ERA_STRCHR (era, '\0');
521#if defined (MAKE_WCSFTIME) && !defined (__HAVE_LOCALE_INFO_EXTENDED__)
522          len = mbsnrtowcs (NULL, &era, c - era, 0, NULL);
523          if (len == (size_t) -1)
524            {
525              free (ei->era_C);
526              free (ei);
527              return NULL;
528            }
529#else
530          len = c - era;
531#endif
532          ei->era_Y = (CHAR *) malloc ((len + 1) * sizeof (CHAR));
533          if (!ei->era_Y)
534            {
535              free (ei->era_C);
536              free (ei);
537              return NULL;
538            }
539#if defined (MAKE_WCSFTIME) && !defined (__HAVE_LOCALE_INFO_EXTENDED__)
540          len = mbsnrtowcs (ei->era_Y, &era, c - era, len + 1, NULL);
541#else
542          ERA_STRNCPY (ei->era_Y, era, len);
543          era += len;
544#endif
545          ei->era_Y[len] = CQ('\0');
546          return ei;
547        }
548      else
549        era = ERA_STRCHR (era, ';');
550      if (era)
551        ++era;
552    }
553  return NULL;
554}
555
556static void
557free_era_info (era_info_t *ei)
558{
559  free (ei->era_C);
560  free (ei->era_Y);
561  free (ei);
562}
563
564typedef struct {
565  size_t num;
566  CHAR **digit;
567  CHAR *buffer;
568} alt_digits_t;
569
570static alt_digits_t *
571#if defined (MAKE_WCSFTIME) && defined (__HAVE_LOCALE_INFO_EXTENDED__)
572get_alt_digits (const wchar_t *alt_digits)
573#else
574get_alt_digits (const char *alt_digits)
575#endif
576{
577  alt_digits_t *adi;
578#if defined (MAKE_WCSFTIME) && defined (__HAVE_LOCALE_INFO_EXTENDED__)
579  const wchar_t *a, *e;
580# define ALT_STRCHR(a,b)        wcschr((a),(b))
581# define ALT_STRCPY(a,b)        wcscpy((a),(b))
582# define ALT_STRLEN(a)          wcslen(a)
583#else
584  const char *a, *e;
585# define ALT_STRCHR(a,b)        strchr((a),(b))
586# define ALT_STRCPY(a,b)        strcpy((a),(b))
587# define ALT_STRLEN(a)          strlen(a)
588#endif
589  CHAR *aa, *ae;
590  size_t len;
591
592  adi = (alt_digits_t *) calloc (1, sizeof (alt_digits_t));
593  if (!adi)
594    return NULL;
595
596  /* Compute number of alt_digits. */
597  adi->num = 1;
598  for (a = alt_digits; (e = ALT_STRCHR (a, ';')) != NULL; a = e + 1)
599      ++adi->num;
600  /* Allocate the `digit' array, which is an array of `num' pointers into
601     `buffer'. */
602  adi->digit = (CHAR **) calloc (adi->num, sizeof (CHAR *));
603  if (!adi->digit)
604    {
605      free (adi);
606      return NULL;
607    }
608  /* Compute memory required for `buffer'. */
609#if defined (MAKE_WCSFTIME) && !defined (__HAVE_LOCALE_INFO_EXTENDED__)
610  len = mbstowcs (NULL, alt_digits, 0);
611  if (len == (size_t) -1)
612    {
613      free (adi->digit);
614      free (adi);
615      return NULL;
616    }
617#else
618  len = ALT_STRLEN (alt_digits);
619#endif
620  /* Allocate it. */
621  adi->buffer = (CHAR *) malloc ((len + 1) * sizeof (CHAR));
622  if (!adi->buffer)
623    {
624      free (adi->digit);
625      free (adi);
626      return NULL;
627    }
628  /* Store digits in it. */
629#if defined (MAKE_WCSFTIME) && !defined (__HAVE_LOCALE_INFO_EXTENDED__)
630  mbstowcs (adi->buffer, alt_digits, len + 1);
631#else
632  ALT_STRCPY (adi->buffer, alt_digits);
633#endif
634  /* Store the pointers into `buffer' into the appropriate `digit' slot. */
635  for (len = 0, aa = adi->buffer; (ae = STRCHR (aa, CQ(';'))) != NULL;
636       ++len, aa = ae + 1)
637    {
638      *ae = '\0';
639      adi->digit[len] = aa;
640    }
641  adi->digit[len] = aa;
642  return adi;
643}
644
645static void
646free_alt_digits (alt_digits_t *adi)
647{
648  free (adi->digit);
649  free (adi->buffer);
650  free (adi);
651}
652
653/* Return 0 if no alt_digit is available for a number.
654   Return -1 if buffer size isn't sufficient to hold alternative digit.
655   Return length of new digit otherwise. */
656static int
657conv_to_alt_digits (CHAR *buf, size_t bufsiz, unsigned num, alt_digits_t *adi)
658{
659  if (num < adi->num)
660    {
661      size_t len = STRLEN (adi->digit[num]);
662      if (bufsiz < len)
663        return -1;
664      STRCPY (buf, adi->digit[num]);
665      return (int) len;
666    }
667  return 0;
668}
669
670static size_t
671__strftime (CHAR *s, size_t maxsize, const CHAR *format,
672            const struct tm *tim_p, struct __locale_t *locale,
673            era_info_t **era_info, alt_digits_t **alt_digits)
674#else /* !_WANT_C99_TIME_FORMATS */
675static size_t
676__strftime (CHAR *s, size_t maxsize, const CHAR *format,
677            const struct tm *tim_p, struct __locale_t *locale)
678
679#define __strftime(s,m,f,t,l,e,a)       __strftime((s),(m),(f),(t),(l))
680#endif /* !_WANT_C99_TIME_FORMATS */
681{
682  size_t count = 0;
683  int len = 0;
684  const CHAR *ctloc;
685#if defined (MAKE_WCSFTIME) && !defined (__HAVE_LOCALE_INFO_EXTENDED__)
686  CHAR ctlocbuf[CTLOCBUFLEN];
687#endif
688  size_t i, ctloclen;
689  CHAR alt;
690  CHAR pad;
691  unsigned long width;
692  int tzset_called = 0;
693
694  const struct lc_time_T *_CurrentTimeLocale = __get_time_locale (locale);
695  for (;;)
696    {
697      while (*format && *format != CQ('%'))
698        {
699          if (count < maxsize - 1)
700            s[count++] = *format++;
701          else
702            return 0;
703        }
704      if (*format == CQ('\0'))
705        break;
706      format++;
707      pad = '\0';
708      width = 0;
709
710      /* POSIX-1.2008 feature: '0' and '+' modifiers require 0-padding with
711         slightly different semantics. */
712      if (*format == CQ('0') || *format == CQ('+'))
713        pad = *format++;
714
715      /* POSIX-1.2008 feature: A minimum field width can be specified. */
716      if (*format >= CQ('1') && *format <= CQ('9'))
717        {
718          CHAR *fp;
719          width = STRTOUL (format, &fp, 10);
720          format = fp;
721        }
722
723      alt = CQ('\0');
724      if (*format == CQ('E'))
725        {
726          alt = *format++;
727#ifdef _WANT_C99_TIME_FORMATS
728#if defined (MAKE_WCSFTIME) && defined (__HAVE_LOCALE_INFO_EXTENDED__)
729          if (!*era_info && *_CurrentTimeLocale->wera)
730            *era_info = get_era_info (tim_p, _CurrentTimeLocale->wera);
731#else
732          if (!*era_info && *_CurrentTimeLocale->era)
733            *era_info = get_era_info (tim_p, _CurrentTimeLocale->era);
734#endif
735#endif /* _WANT_C99_TIME_FORMATS */
736        }
737      else if (*format == CQ('O'))
738        {
739          alt = *format++;
740#ifdef _WANT_C99_TIME_FORMATS
741#if defined (MAKE_WCSFTIME) && defined (__HAVE_LOCALE_INFO_EXTENDED__)
742          if (!*alt_digits && *_CurrentTimeLocale->walt_digits)
743            *alt_digits = get_alt_digits (_CurrentTimeLocale->walt_digits);
744#else
745          if (!*alt_digits && *_CurrentTimeLocale->alt_digits)
746            *alt_digits = get_alt_digits (_CurrentTimeLocale->alt_digits);
747#endif
748#endif /* _WANT_C99_TIME_FORMATS */
749        }
750
751      switch (*format)
752        {
753        case CQ('a'):
754          _ctloc (wday[tim_p->tm_wday]);
755          for (i = 0; i < ctloclen; i++)
756            {
757              if (count < maxsize - 1)
758                s[count++] = ctloc[i];
759              else
760                return 0;
761            }
762          break;
763        case CQ('A'):
764          _ctloc (weekday[tim_p->tm_wday]);
765          for (i = 0; i < ctloclen; i++)
766            {
767              if (count < maxsize - 1)
768                s[count++] = ctloc[i];
769              else
770                return 0;
771            }
772          break;
773        case CQ('b'):
774        case CQ('h'):
775          _ctloc (mon[tim_p->tm_mon]);
776          for (i = 0; i < ctloclen; i++)
777            {
778              if (count < maxsize - 1)
779                s[count++] = ctloc[i];
780              else
781                return 0;
782            }
783          break;
784        case CQ('B'):
785          _ctloc (month[tim_p->tm_mon]);
786          for (i = 0; i < ctloclen; i++)
787            {
788              if (count < maxsize - 1)
789                s[count++] = ctloc[i];
790              else
791                return 0;
792            }
793          break;
794        case CQ('c'):
795#ifdef _WANT_C99_TIME_FORMATS
796          if (alt == 'E' && *era_info && *_CurrentTimeLocale->era_d_t_fmt)
797            _ctloc (era_d_t_fmt);
798          else
799#endif /* _WANT_C99_TIME_FORMATS */
800            _ctloc (c_fmt);
801          goto recurse;
802        case CQ('r'):
803          _ctloc (ampm_fmt);
804          goto recurse;
805        case CQ('x'):
806#ifdef _WANT_C99_TIME_FORMATS
807          if (alt == 'E' && *era_info && *_CurrentTimeLocale->era_d_fmt)
808            _ctloc (era_d_fmt);
809          else
810#endif /* _WANT_C99_TIME_FORMATS */
811            _ctloc (x_fmt);
812          goto recurse;
813        case CQ('X'):
814#ifdef _WANT_C99_TIME_FORMATS
815          if (alt == 'E' && *era_info && *_CurrentTimeLocale->era_t_fmt)
816            _ctloc (era_t_fmt);
817          else
818#endif /* _WANT_C99_TIME_FORMATS */
819            _ctloc (X_fmt);
820recurse:
821          if (*ctloc)
822            {
823              /* Recurse to avoid need to replicate %Y formation. */
824              len = __strftime (&s[count], maxsize - count, ctloc, tim_p,
825                                locale, era_info, alt_digits);
826              if (len > 0)
827                count += len;
828              else
829                return 0;
830            }
831          break;
832        case CQ('C'):
833          {
834            /* Examples of (tm_year + YEAR_BASE) that show how %Y == %C%y
835               with 32-bit int.
836               %Y               %C              %y
837               2147485547       21474855        47
838               10000            100             00
839               9999             99              99
840               0999             09              99
841               0099             00              99
842               0001             00              01
843               0000             00              00
844               -001             -0              01
845               -099             -0              99
846               -999             -9              99
847               -1000            -10             00
848               -10000           -100            00
849               -2147481748      -21474817       48
850
851               Be careful of both overflow and sign adjustment due to the
852               asymmetric range of years.
853            */
854#ifdef _WANT_C99_TIME_FORMATS
855            if (alt == 'E' && *era_info)
856              len = snprintf (&s[count], maxsize - count, CQ("%" SFLG "s"),
857                              (*era_info)->era_C);
858            else
859#endif /* _WANT_C99_TIME_FORMATS */
860              {
861                CHAR *fmt = CQ("%s%.*d");
862                char *pos = "";
863                int neg = tim_p->tm_year < -YEAR_BASE;
864                int century = tim_p->tm_year >= 0
865                  ? tim_p->tm_year / 100 + YEAR_BASE / 100
866                  : abs (tim_p->tm_year + YEAR_BASE) / 100;
867                if (pad) /* '0' or '+' */
868                  {
869                    fmt = CQ("%s%0.*d");
870                    if (century >= 100 && pad == CQ('+'))
871                      pos = "+";
872                  }
873                if (width < 2)
874                  width = 2;
875                len = snprintf (&s[count], maxsize - count, fmt,
876                                neg ? "-" : pos, width - neg, century);
877              }
878            CHECK_LENGTH ();
879          }
880          break;
881        case CQ('d'):
882        case CQ('e'):
883#ifdef _WANT_C99_TIME_FORMATS
884          if (alt == CQ('O') && *alt_digits)
885            {
886              if (tim_p->tm_mday < 10)
887                {
888                  if (*format == CQ('d'))
889                    {
890                      if (maxsize - count < 2) return 0;
891                      len = conv_to_alt_digits (&s[count], maxsize - count,
892                                                0, *alt_digits);
893                      CHECK_LENGTH ();
894                    }
895                  if (*format == CQ('e') || len == 0)
896                    s[count++] = CQ(' ');
897                }
898              len = conv_to_alt_digits (&s[count], maxsize - count,
899                                        tim_p->tm_mday, *alt_digits);
900              CHECK_LENGTH ();
901              if (len > 0)
902                break;
903            }
904#endif /* _WANT_C99_TIME_FORMATS */
905          len = snprintf (&s[count], maxsize - count,
906                          *format == CQ('d') ? CQ("%.2d") : CQ("%2d"),
907                          tim_p->tm_mday);
908          CHECK_LENGTH ();
909          break;
910        case CQ('D'):
911          /* %m/%d/%y */
912          len = snprintf (&s[count], maxsize - count,
913                          CQ("%.2d/%.2d/%.2d"),
914                          tim_p->tm_mon + 1, tim_p->tm_mday,
915                          tim_p->tm_year >= 0 ? tim_p->tm_year % 100
916                          : abs (tim_p->tm_year + YEAR_BASE) % 100);
917          CHECK_LENGTH ();
918          break;
919        case CQ('F'):
920          { /* %F is equivalent to "%+4Y-%m-%d", flags and width can change
921               that.  Recurse to avoid need to replicate %Y formation. */
922            CHAR fmtbuf[32], *fmt = fmtbuf;
923
924            *fmt++ = CQ('%');
925            if (pad) /* '0' or '+' */
926              *fmt++ = pad;
927            else
928              *fmt++ = '+';
929            if (!pad)
930              width = 10;
931            if (width < 6)
932              width = 6;
933            width -= 6;
934            if (width)
935              {
936                len = snprintf (fmt, fmtbuf + 32 - fmt, CQ("%lu"), width);
937                if (len > 0)
938                  fmt += len;
939              }
940            STRCPY (fmt, CQ("Y-%m-%d"));
941            len = __strftime (&s[count], maxsize - count, fmtbuf, tim_p,
942                              locale, era_info, alt_digits);
943            if (len > 0)
944              count += len;
945            else
946              return 0;
947          }
948          break;
949        case CQ('g'):
950          /* Be careful of both overflow and negative years, thanks to
951                 the asymmetric range of years.  */
952          {
953            int adjust = iso_year_adjust (tim_p);
954            int year = tim_p->tm_year >= 0 ? tim_p->tm_year % 100
955                : abs (tim_p->tm_year + YEAR_BASE) % 100;
956            if (adjust < 0 && tim_p->tm_year <= -YEAR_BASE)
957                adjust = 1;
958            else if (adjust > 0 && tim_p->tm_year < -YEAR_BASE)
959                adjust = -1;
960            len = snprintf (&s[count], maxsize - count, CQ("%.2d"),
961                            ((year + adjust) % 100 + 100) % 100);
962            CHECK_LENGTH ();
963          }
964          break;
965        case CQ('G'):
966          {
967            /* See the comments for 'C' and 'Y'; this is a variable length
968               field.  Although there is no requirement for a minimum number
969               of digits, we use 4 for consistency with 'Y'.  */
970            int sign = tim_p->tm_year < -YEAR_BASE;
971            int adjust = iso_year_adjust (tim_p);
972            int century = tim_p->tm_year >= 0
973              ? tim_p->tm_year / 100 + YEAR_BASE / 100
974              : abs (tim_p->tm_year + YEAR_BASE) / 100;
975            int year = tim_p->tm_year >= 0 ? tim_p->tm_year % 100
976              : abs (tim_p->tm_year + YEAR_BASE) % 100;
977            if (adjust < 0 && tim_p->tm_year <= -YEAR_BASE)
978              sign = adjust = 1;
979            else if (adjust > 0 && sign)
980              adjust = -1;
981            year += adjust;
982            if (year == -1)
983              {
984                year = 99;
985                --century;
986              }
987            else if (year == 100)
988              {
989                year = 0;
990                ++century;
991              }
992            CHAR fmtbuf[10], *fmt = fmtbuf;
993            /* int potentially overflows, so use unsigned instead.  */
994            unsigned p_year = century * 100 + year;
995            if (sign)
996              *fmt++ = CQ('-');
997            else if (pad == CQ('+') && p_year >= 10000)
998              {
999                *fmt++ = CQ('+');
1000                sign = 1;
1001              }
1002            if (width && sign)
1003              --width;
1004            *fmt++ = CQ('%');
1005            if (pad)
1006              *fmt++ = CQ('0');
1007            STRCPY (fmt, CQ(".*u"));
1008            len = snprintf (&s[count], maxsize - count, fmtbuf, width, p_year);
1009            if (len < 0  ||  (count+=len) >= maxsize)
1010              return 0;
1011          }
1012          break;
1013        case CQ('H'):
1014#ifdef _WANT_C99_TIME_FORMATS
1015          if (alt == CQ('O') && *alt_digits)
1016            {
1017              len = conv_to_alt_digits (&s[count], maxsize - count,
1018                                        tim_p->tm_hour, *alt_digits);
1019              CHECK_LENGTH ();
1020              if (len > 0)
1021                break;
1022            }
1023#endif /* _WANT_C99_TIME_FORMATS */
1024          /*FALLTHRU*/
1025        case CQ('k'):   /* newlib extension */
1026          len = snprintf (&s[count], maxsize - count,
1027                          *format == CQ('k') ? CQ("%2d") : CQ("%.2d"),
1028                          tim_p->tm_hour);
1029          CHECK_LENGTH ();
1030          break;
1031        case CQ('l'):   /* newlib extension */
1032          if (alt == CQ('O'))
1033            alt = CQ('\0');
1034          /*FALLTHRU*/
1035        case CQ('I'):
1036          {
1037            register int  h12;
1038            h12 = (tim_p->tm_hour == 0 || tim_p->tm_hour == 12)  ?
1039                                                12  :  tim_p->tm_hour % 12;
1040#ifdef _WANT_C99_TIME_FORMATS
1041            if (alt != CQ('O') || !*alt_digits
1042                || !(len = conv_to_alt_digits (&s[count], maxsize - count,
1043                                               h12, *alt_digits)))
1044#endif /* _WANT_C99_TIME_FORMATS */
1045              len = snprintf (&s[count], maxsize - count,
1046                              *format == CQ('I') ? CQ("%.2d") : CQ("%2d"), h12);
1047            CHECK_LENGTH ();
1048          }
1049          break;
1050        case CQ('j'):
1051          len = snprintf (&s[count], maxsize - count, CQ("%.3d"),
1052                          tim_p->tm_yday + 1);
1053          CHECK_LENGTH ();
1054          break;
1055        case CQ('m'):
1056#ifdef _WANT_C99_TIME_FORMATS
1057          if (alt != CQ('O') || !*alt_digits
1058              || !(len = conv_to_alt_digits (&s[count], maxsize - count,
1059                                             tim_p->tm_mon + 1, *alt_digits)))
1060#endif /* _WANT_C99_TIME_FORMATS */
1061            len = snprintf (&s[count], maxsize - count, CQ("%.2d"),
1062                            tim_p->tm_mon + 1);
1063          CHECK_LENGTH ();
1064          break;
1065        case CQ('M'):
1066#ifdef _WANT_C99_TIME_FORMATS
1067          if (alt != CQ('O') || !*alt_digits
1068              || !(len = conv_to_alt_digits (&s[count], maxsize - count,
1069                                             tim_p->tm_min, *alt_digits)))
1070#endif /* _WANT_C99_TIME_FORMATS */
1071            len = snprintf (&s[count], maxsize - count, CQ("%.2d"),
1072                            tim_p->tm_min);
1073          CHECK_LENGTH ();
1074          break;
1075        case CQ('n'):
1076          if (count < maxsize - 1)
1077            s[count++] = CQ('\n');
1078          else
1079            return 0;
1080          break;
1081        case CQ('p'):
1082        case CQ('P'):
1083          _ctloc (am_pm[tim_p->tm_hour < 12 ? 0 : 1]);
1084          for (i = 0; i < ctloclen; i++)
1085            {
1086              if (count < maxsize - 1)
1087                s[count++] = (*format == CQ('P') ? TOLOWER (ctloc[i])
1088                                                 : ctloc[i]);
1089              else
1090                return 0;
1091            }
1092          break;
1093        case CQ('R'):
1094          len = snprintf (&s[count], maxsize - count, CQ("%.2d:%.2d"),
1095                          tim_p->tm_hour, tim_p->tm_min);
1096          CHECK_LENGTH ();
1097          break;
1098        case CQ('s'):
1099/*
1100 * From:
1101 * The Open Group Base Specifications Issue 7
1102 * IEEE Std 1003.1, 2013 Edition
1103 * Copyright (c) 2001-2013 The IEEE and The Open Group
1104 * XBD Base Definitions
1105 * 4. General Concepts
1106 * 4.15 Seconds Since the Epoch
1107 * A value that approximates the number of seconds that have elapsed since the
1108 * Epoch. A Coordinated Universal Time name (specified in terms of seconds
1109 * (tm_sec), minutes (tm_min), hours (tm_hour), days since January 1 of the year
1110 * (tm_yday), and calendar year minus 1900 (tm_year)) is related to a time
1111 * represented as seconds since the Epoch, according to the expression below.
1112 * If the year is <1970 or the value is negative, the relationship is undefined.
1113 * If the year is >=1970 and the value is non-negative, the value is related to a
1114 * Coordinated Universal Time name according to the C-language expression, where
1115 * tm_sec, tm_min, tm_hour, tm_yday, and tm_year are all integer types:
1116 * tm_sec + tm_min*60 + tm_hour*3600 + tm_yday*86400 +
1117 *     (tm_year-70)*31536000 + ((tm_year-69)/4)*86400 -
1118 *     ((tm_year-1)/100)*86400 + ((tm_year+299)/400)*86400
1119 * OR
1120 * ((((tm_year-69)/4 - (tm_year-1)/100 + (tm_year+299)/400 +
1121 *         (tm_year-70)*365 + tm_yday)*24 + tm_hour)*60 + tm_min)*60 + tm_sec
1122 */
1123/* modified from %z case by hoisting offset outside if block and initializing */
1124          {
1125            long offset = 0;    /* offset < 0 => W of GMT, > 0 => E of GMT:
1126                                   subtract to get UTC */
1127
1128            if (tim_p->tm_isdst >= 0)
1129              {
1130                TZ_LOCK;
1131                if (!tzset_called)
1132                  {
1133                    _tzset_unlocked ();
1134                    tzset_called = 1;
1135                  }
1136
1137#if defined (__CYGWIN__)
1138                /* Cygwin must check if the application has been built with or
1139                   without the extra tm members for backward compatibility, and
1140                   then use either that or the old method fetching from tzinfo.
1141                   Rather than pulling in the version check infrastructure, we
1142                   just call a Cygwin function. */
1143                extern long __cygwin_gettzoffset (const struct tm *tmp);
1144                offset = __cygwin_gettzoffset (tim_p);
1145#elif defined (__TM_GMTOFF)
1146                offset = tim_p->__TM_GMTOFF;
1147#else
1148                __tzinfo_type *tz = __gettzinfo ();
1149                /* The sign of this is exactly opposite the envvar TZ.  We
1150                   could directly use the global _timezone for tm_isdst==0,
1151                   but have to use __tzrule for daylight savings.  */
1152                offset = -tz->__tzrule[tim_p->tm_isdst > 0].offset;
1153#endif
1154                TZ_UNLOCK;
1155              }
1156            len = snprintf (&s[count], maxsize - count, CQ("%lld"),
1157                            (((((long long)tim_p->tm_year - 69)/4
1158                                - (tim_p->tm_year - 1)/100
1159                                + (tim_p->tm_year + 299)/400
1160                                + (tim_p->tm_year - 70)*365 + tim_p->tm_yday)*24
1161                              + tim_p->tm_hour)*60 + tim_p->tm_min)*60
1162                            + tim_p->tm_sec - offset);
1163            CHECK_LENGTH ();
1164          }
1165          break;
1166        case CQ('S'):
1167#ifdef _WANT_C99_TIME_FORMATS
1168          if (alt != CQ('O') || !*alt_digits
1169              || !(len = conv_to_alt_digits (&s[count], maxsize - count,
1170                                             tim_p->tm_sec, *alt_digits)))
1171#endif /* _WANT_C99_TIME_FORMATS */
1172            len = snprintf (&s[count], maxsize - count, CQ("%.2d"),
1173                            tim_p->tm_sec);
1174          CHECK_LENGTH ();
1175          break;
1176        case CQ('t'):
1177          if (count < maxsize - 1)
1178            s[count++] = CQ('\t');
1179          else
1180            return 0;
1181          break;
1182        case CQ('T'):
1183          len = snprintf (&s[count], maxsize - count, CQ("%.2d:%.2d:%.2d"),
1184                          tim_p->tm_hour, tim_p->tm_min, tim_p->tm_sec);
1185          CHECK_LENGTH ();
1186          break;
1187        case CQ('u'):
1188#ifdef _WANT_C99_TIME_FORMATS
1189          if (alt == CQ('O') && *alt_digits)
1190            {
1191              len = conv_to_alt_digits (&s[count], maxsize - count,
1192                                        tim_p->tm_wday == 0 ? 7
1193                                                            : tim_p->tm_wday,
1194                                        *alt_digits);
1195              CHECK_LENGTH ();
1196              if (len > 0)
1197                break;
1198            }
1199#endif /* _WANT_C99_TIME_FORMATS */
1200          if (count < maxsize - 1)
1201            {
1202              if (tim_p->tm_wday == 0)
1203                s[count++] = CQ('7');
1204              else
1205                s[count++] = CQ('0') + tim_p->tm_wday;
1206            }
1207          else
1208            return 0;
1209          break;
1210        case CQ('U'):
1211#ifdef _WANT_C99_TIME_FORMATS
1212          if (alt != CQ('O') || !*alt_digits
1213              || !(len = conv_to_alt_digits (&s[count], maxsize - count,
1214                                             (tim_p->tm_yday + 7 -
1215                                              tim_p->tm_wday) / 7,
1216                                             *alt_digits)))
1217#endif /* _WANT_C99_TIME_FORMATS */
1218            len = snprintf (&s[count], maxsize - count, CQ("%.2d"),
1219                         (tim_p->tm_yday + 7 -
1220                          tim_p->tm_wday) / 7);
1221          CHECK_LENGTH ();
1222          break;
1223        case CQ('V'):
1224          {
1225            int adjust = iso_year_adjust (tim_p);
1226            int wday = (tim_p->tm_wday) ? tim_p->tm_wday - 1 : 6;
1227            int week = (tim_p->tm_yday + 10 - wday) / 7;
1228            if (adjust > 0)
1229                week = 1;
1230            else if (adjust < 0)
1231                /* Previous year has 53 weeks if current year starts on
1232                   Fri, and also if current year starts on Sat and
1233                   previous year was leap year.  */
1234                week = 52 + (4 >= (wday - tim_p->tm_yday
1235                                   - isleap (tim_p->tm_year
1236                                             + (YEAR_BASE - 1
1237                                                - (tim_p->tm_year < 0
1238                                                   ? 0 : 2000)))));
1239#ifdef _WANT_C99_TIME_FORMATS
1240            if (alt != CQ('O') || !*alt_digits
1241                || !(len = conv_to_alt_digits (&s[count], maxsize - count,
1242                                               week, *alt_digits)))
1243#endif /* _WANT_C99_TIME_FORMATS */
1244              len = snprintf (&s[count], maxsize - count, CQ("%.2d"), week);
1245            CHECK_LENGTH ();
1246          }
1247          break;
1248        case CQ('w'):
1249#ifdef _WANT_C99_TIME_FORMATS
1250          if (alt == CQ('O') && *alt_digits)
1251            {
1252              len = conv_to_alt_digits (&s[count], maxsize - count,
1253                                        tim_p->tm_wday, *alt_digits);
1254              CHECK_LENGTH ();
1255              if (len > 0)
1256                break;
1257            }
1258#endif /* _WANT_C99_TIME_FORMATS */
1259          if (count < maxsize - 1)
1260            s[count++] = CQ('0') + tim_p->tm_wday;
1261          else
1262            return 0;
1263          break;
1264        case CQ('W'):
1265          {
1266            int wday = (tim_p->tm_wday) ? tim_p->tm_wday - 1 : 6;
1267            wday = (tim_p->tm_yday + 7 - wday) / 7;
1268#ifdef _WANT_C99_TIME_FORMATS
1269            if (alt != CQ('O') || !*alt_digits
1270                || !(len = conv_to_alt_digits (&s[count], maxsize - count,
1271                                               wday, *alt_digits)))
1272#endif /* _WANT_C99_TIME_FORMATS */
1273              len = snprintf (&s[count], maxsize - count, CQ("%.2d"), wday);
1274            CHECK_LENGTH ();
1275          }
1276          break;
1277        case CQ('y'):
1278            {
1279#ifdef _WANT_C99_TIME_FORMATS
1280              if (alt == 'E' && *era_info)
1281                len = snprintf (&s[count], maxsize - count, CQ("%d"),
1282                                (*era_info)->year);
1283              else
1284#endif /* _WANT_C99_TIME_FORMATS */
1285                {
1286                  /* Be careful of both overflow and negative years, thanks to
1287                     the asymmetric range of years.  */
1288                  int year = tim_p->tm_year >= 0 ? tim_p->tm_year % 100
1289                             : abs (tim_p->tm_year + YEAR_BASE) % 100;
1290#ifdef _WANT_C99_TIME_FORMATS
1291                  if (alt != CQ('O') || !*alt_digits
1292                      || !(len = conv_to_alt_digits (&s[count], maxsize - count,
1293                                                     year, *alt_digits)))
1294#endif /* _WANT_C99_TIME_FORMATS */
1295                    len = snprintf (&s[count], maxsize - count, CQ("%.2d"),
1296                                    year);
1297                }
1298              CHECK_LENGTH ();
1299            }
1300          break;
1301        case CQ('Y'):
1302#ifdef _WANT_C99_TIME_FORMATS
1303          if (alt == 'E' && *era_info)
1304            {
1305              ctloc = (*era_info)->era_Y;
1306              goto recurse;
1307            }
1308          else
1309#endif /* _WANT_C99_TIME_FORMATS */
1310            {
1311              CHAR fmtbuf[10], *fmt = fmtbuf;
1312              int sign = tim_p->tm_year < -YEAR_BASE;
1313              /* int potentially overflows, so use unsigned instead.  */
1314              register unsigned year = (unsigned) tim_p->tm_year
1315                                       + (unsigned) YEAR_BASE;
1316              if (sign)
1317                {
1318                  *fmt++ = CQ('-');
1319                  year = UINT_MAX - year + 1;
1320                }
1321              else if (pad == CQ('+') && year >= 10000)
1322                {
1323                  *fmt++ = CQ('+');
1324                  sign = 1;
1325                }
1326              if (width && sign)
1327                --width;
1328              *fmt++ = CQ('%');
1329              if (pad)
1330                *fmt++ = CQ('0');
1331              STRCPY (fmt, CQ(".*u"));
1332              len = snprintf (&s[count], maxsize - count, fmtbuf, width,
1333                              year);
1334              CHECK_LENGTH ();
1335            }
1336          break;
1337        case CQ('z'):
1338          if (tim_p->tm_isdst >= 0)
1339            {
1340              long offset;
1341
1342              TZ_LOCK;
1343              if (!tzset_called)
1344                {
1345                  _tzset_unlocked ();
1346                  tzset_called = 1;
1347                }
1348
1349#if defined (__CYGWIN__)
1350              /* Cygwin must check if the application has been built with or
1351                 without the extra tm members for backward compatibility, and
1352                 then use either that or the old method fetching from tzinfo.
1353                 Rather than pulling in the version check infrastructure, we
1354                 just call a Cygwin function. */
1355              extern long __cygwin_gettzoffset (const struct tm *tmp);
1356              offset = __cygwin_gettzoffset (tim_p);
1357#elif defined (__TM_GMTOFF)
1358              offset = tim_p->__TM_GMTOFF;
1359#else
1360              __tzinfo_type *tz = __gettzinfo ();
1361              /* The sign of this is exactly opposite the envvar TZ.  We
1362                 could directly use the global _timezone for tm_isdst==0,
1363                 but have to use __tzrule for daylight savings.  */
1364              offset = -tz->__tzrule[tim_p->tm_isdst > 0].offset;
1365#endif
1366              TZ_UNLOCK;
1367              len = snprintf (&s[count], maxsize - count, CQ("%+03ld%.2ld"),
1368                              offset / SECSPERHOUR,
1369                              labs (offset / SECSPERMIN) % 60L);
1370              CHECK_LENGTH ();
1371            }
1372          break;
1373        case CQ('Z'):
1374          if (tim_p->tm_isdst >= 0)
1375            {
1376              size_t size;
1377              const char *tznam = NULL;
1378
1379              TZ_LOCK;
1380              if (!tzset_called)
1381                {
1382                  _tzset_unlocked ();
1383                  tzset_called = 1;
1384                }
1385#if defined (__CYGWIN__)
1386              /* See above. */
1387              extern const char *__cygwin_gettzname (const struct tm *tmp);
1388              tznam = __cygwin_gettzname (tim_p);
1389#elif defined (__TM_ZONE)
1390              tznam = tim_p->__TM_ZONE;
1391#endif
1392              if (!tznam)
1393                tznam = _tzname[tim_p->tm_isdst > 0];
1394              /* Note that in case of wcsftime this loop only works for
1395                 timezone abbreviations using the portable codeset (aka ASCII).
1396                 This seems to be the case, but if that ever changes, this
1397                 loop needs revisiting. */
1398              size = strlen (tznam);
1399              for (i = 0; i < size; i++)
1400                {
1401                  if (count < maxsize - 1)
1402                    s[count++] = tznam[i];
1403                  else
1404                    {
1405                      TZ_UNLOCK;
1406                      return 0;
1407                    }
1408                }
1409              TZ_UNLOCK;
1410            }
1411          break;
1412        case CQ('%'):
1413          if (count < maxsize - 1)
1414            s[count++] = CQ('%');
1415          else
1416            return 0;
1417          break;
1418        default:
1419          return 0;
1420        }
1421      if (*format)
1422        format++;
1423      else
1424        break;
1425    }
1426  if (maxsize)
1427    s[count] = CQ('\0');
1428
1429  return count;
1430}
1431
1432size_t
1433strftime (CHAR *__restrict s,
1434        size_t maxsize,
1435        const CHAR *__restrict format,
1436        const struct tm *__restrict tim_p)
1437{
1438#ifdef _WANT_C99_TIME_FORMATS
1439  era_info_t *era_info = NULL;
1440  alt_digits_t *alt_digits = NULL;
1441  size_t ret = __strftime (s, maxsize, format, tim_p, __get_current_locale (),
1442                           &era_info, &alt_digits);
1443  if (era_info)
1444    free_era_info (era_info);
1445  if (alt_digits)
1446    free_alt_digits (alt_digits);
1447  return ret;
1448#else /* !_WANT_C99_TIME_FORMATS */
1449  return __strftime (s, maxsize, format, tim_p, __get_current_locale (),
1450                     NULL, NULL);
1451#endif /* !_WANT_C99_TIME_FORMATS */
1452}
1453
1454size_t
1455strftime_l (CHAR *__restrict s, size_t maxsize, const CHAR *__restrict format,
1456            const struct tm *__restrict tim_p, struct __locale_t *locale)
1457{
1458#ifdef _WANT_C99_TIME_FORMATS
1459  era_info_t *era_info = NULL;
1460  alt_digits_t *alt_digits = NULL;
1461  size_t ret = __strftime (s, maxsize, format, tim_p, locale,
1462                           &era_info, &alt_digits);
1463  if (era_info)
1464    free_era_info (era_info);
1465  if (alt_digits)
1466    free_alt_digits (alt_digits);
1467  return ret;
1468#else /* !_WANT_C99_TIME_FORMATS */
1469  return __strftime (s, maxsize, format, tim_p, locale, NULL, NULL);
1470#endif /* !_WANT_C99_TIME_FORMATS */
1471}
1472
1473/* The remainder of this file can serve as a regression test.  Compile
1474 *  with -D_REGRESSION_TEST.  */
1475#if defined(_REGRESSION_TEST)   /* [Test code:  */
1476
1477/* This test code relies on ANSI C features, in particular on the ability
1478 * of adjacent strings to be pasted together into one string.  */
1479
1480/* Test output buffer size (should be larger than all expected results) */
1481#define OUTSIZE 256
1482
1483struct test {
1484        CHAR  *fmt;     /* Testing format */
1485        size_t  max;    /* Testing maxsize */
1486        size_t  ret;    /* Expected return value */
1487        CHAR  *out;     /* Expected output string */
1488        };
1489struct list {
1490        const struct tm  *tms;  /* Time used for these vectors */
1491        const struct test *vec; /* Test vectors */
1492        int  cnt;               /* Number of vectors */
1493        };
1494
1495const char  TZ[]="TZ=EST5EDT";
1496
1497/* Define list of test inputs and expected outputs, for the given time zone
1498 * and time.  */
1499const struct tm  tm0 = {
1500        /* Tue Dec 30 10:53:47 EST 2008 (time_t=1230648827) */
1501        .tm_sec         = 47,
1502        .tm_min         = 53,
1503        .tm_hour        = 9,
1504        .tm_mday        = 30,
1505        .tm_mon         = 11,
1506        .tm_year        = 108,
1507        .tm_wday        = 2,
1508        .tm_yday        = 364,
1509        .tm_isdst       = 0
1510        };
1511const struct test  Vec0[] = {
1512        /* Testing fields one at a time, expecting to pass, using exact
1513         * allowed length as what is needed.  */
1514        /* Using tm0 for time: */
1515        #define EXP(s)  sizeof(s)/sizeof(CHAR)-1, s
1516        { CQ("%a"), 3+1, EXP(CQ("Tue")) },
1517        { CQ("%A"), 7+1, EXP(CQ("Tuesday")) },
1518        { CQ("%b"), 3+1, EXP(CQ("Dec")) },
1519        { CQ("%B"), 8+1, EXP(CQ("December")) },
1520        { CQ("%c"), 24+1, EXP(CQ("Tue Dec 30 09:53:47 2008")) },
1521        { CQ("%C"), 2+1, EXP(CQ("20")) },
1522        { CQ("%d"), 2+1, EXP(CQ("30")) },
1523        { CQ("%D"), 8+1, EXP(CQ("12/30/08")) },
1524        { CQ("%e"), 2+1, EXP(CQ("30")) },
1525        { CQ("%F"), 10+1, EXP(CQ("2008-12-30")) },
1526        { CQ("%g"), 2+1, EXP(CQ("09")) },
1527        { CQ("%G"), 4+1, EXP(CQ("2009")) },
1528        { CQ("%h"), 3+1, EXP(CQ("Dec")) },
1529        { CQ("%H"), 2+1, EXP(CQ("09")) },
1530        { CQ("%I"), 2+1, EXP(CQ("09")) },
1531        { CQ("%j"), 3+1, EXP(CQ("365")) },
1532        { CQ("%k"), 2+1, EXP(CQ(" 9")) },
1533        { CQ("%l"), 2+1, EXP(CQ(" 9")) },
1534        { CQ("%m"), 2+1, EXP(CQ("12")) },
1535        { CQ("%M"), 2+1, EXP(CQ("53")) },
1536        { CQ("%n"), 1+1, EXP(CQ("\n")) },
1537        { CQ("%p"), 2+1, EXP(CQ("AM")) },
1538        { CQ("%r"), 11+1, EXP(CQ("09:53:47 AM")) },
1539        { CQ("%R"), 5+1, EXP(CQ("09:53")) },
1540        { CQ("%s"), 2+1, EXP(CQ("1230648827")) },
1541        { CQ("%S"), 2+1, EXP(CQ("47")) },
1542        { CQ("%t"), 1+1, EXP(CQ("\t")) },
1543        { CQ("%T"), 8+1, EXP(CQ("09:53:47")) },
1544        { CQ("%u"), 1+1, EXP(CQ("2")) },
1545        { CQ("%U"), 2+1, EXP(CQ("52")) },
1546        { CQ("%V"), 2+1, EXP(CQ("01")) },
1547        { CQ("%w"), 1+1, EXP(CQ("2")) },
1548        { CQ("%W"), 2+1, EXP(CQ("52")) },
1549        { CQ("%x"), 8+1, EXP(CQ("12/30/08")) },
1550        { CQ("%X"), 8+1, EXP(CQ("09:53:47")) },
1551        { CQ("%y"), 2+1, EXP(CQ("08")) },
1552        { CQ("%Y"), 4+1, EXP(CQ("2008")) },
1553        { CQ("%z"), 5+1, EXP(CQ("-0500")) },
1554        { CQ("%Z"), 3+1, EXP(CQ("EST")) },
1555        { CQ("%%"), 1+1, EXP(CQ("%")) },
1556        #undef EXP
1557        };
1558/* Define list of test inputs and expected outputs, for the given time zone
1559 * and time.  */
1560const struct tm  tm1 = {
1561        /* Wed Jul  2 23:01:13 EDT 2008 (time_t=1215054073) */
1562        .tm_sec         = 13,
1563        .tm_min         = 1,
1564        .tm_hour        = 23,
1565        .tm_mday        = 2,
1566        .tm_mon         = 6,
1567        .tm_year        = 108,
1568        .tm_wday        = 3,
1569        .tm_yday        = 183,
1570        .tm_isdst       = 1
1571        };
1572const struct test  Vec1[] = {
1573        /* Testing fields one at a time, expecting to pass, using exact
1574         * allowed length as what is needed.  */
1575        /* Using tm1 for time: */
1576        #define EXP(s)  sizeof(s)/sizeof(CHAR)-1, s
1577        { CQ("%a"), 3+1, EXP(CQ("Wed")) },
1578        { CQ("%A"), 9+1, EXP(CQ("Wednesday")) },
1579        { CQ("%b"), 3+1, EXP(CQ("Jul")) },
1580        { CQ("%B"), 4+1, EXP(CQ("July")) },
1581        { CQ("%c"), 24+1, EXP(CQ("Wed Jul  2 23:01:13 2008")) },
1582        { CQ("%C"), 2+1, EXP(CQ("20")) },
1583        { CQ("%d"), 2+1, EXP(CQ("02")) },
1584        { CQ("%D"), 8+1, EXP(CQ("07/02/08")) },
1585        { CQ("%e"), 2+1, EXP(CQ(" 2")) },
1586        { CQ("%F"), 10+1, EXP(CQ("2008-07-02")) },
1587        { CQ("%g"), 2+1, EXP(CQ("08")) },
1588        { CQ("%G"), 4+1, EXP(CQ("2008")) },
1589        { CQ("%h"), 3+1, EXP(CQ("Jul")) },
1590        { CQ("%H"), 2+1, EXP(CQ("23")) },
1591        { CQ("%I"), 2+1, EXP(CQ("11")) },
1592        { CQ("%j"), 3+1, EXP(CQ("184")) },
1593        { CQ("%k"), 2+1, EXP(CQ("23")) },
1594        { CQ("%l"), 2+1, EXP(CQ("11")) },
1595        { CQ("%m"), 2+1, EXP(CQ("07")) },
1596        { CQ("%M"), 2+1, EXP(CQ("01")) },
1597        { CQ("%n"), 1+1, EXP(CQ("\n")) },
1598        { CQ("%p"), 2+1, EXP(CQ("PM")) },
1599        { CQ("%r"), 11+1, EXP(CQ("11:01:13 PM")) },
1600        { CQ("%R"), 5+1, EXP(CQ("23:01")) },
1601        { CQ("%s"), 2+1, EXP(CQ("1215054073")) },
1602        { CQ("%S"), 2+1, EXP(CQ("13")) },
1603        { CQ("%t"), 1+1, EXP(CQ("\t")) },
1604        { CQ("%T"), 8+1, EXP(CQ("23:01:13")) },
1605        { CQ("%u"), 1+1, EXP(CQ("3")) },
1606        { CQ("%U"), 2+1, EXP(CQ("26")) },
1607        { CQ("%V"), 2+1, EXP(CQ("27")) },
1608        { CQ("%w"), 1+1, EXP(CQ("3")) },
1609        { CQ("%W"), 2+1, EXP(CQ("26")) },
1610        { CQ("%x"), 8+1, EXP(CQ("07/02/08")) },
1611        { CQ("%X"), 8+1, EXP(CQ("23:01:13")) },
1612        { CQ("%y"), 2+1, EXP(CQ("08")) },
1613        { CQ("%Y"), 4+1, EXP(CQ("2008")) },
1614        { CQ("%z"), 5+1, EXP(CQ("-0400")) },
1615        { CQ("%Z"), 3+1, EXP(CQ("EDT")) },
1616        { CQ("%%"), 1+1, EXP(CQ("%")) },
1617        #undef EXP
1618        #define VEC(s)  s, sizeof(s)/sizeof(CHAR), sizeof(s)/sizeof(CHAR)-1, s
1619        #define EXP(s)  sizeof(s)/sizeof(CHAR), sizeof(s)/sizeof(CHAR)-1, s
1620        { VEC(CQ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")) },
1621        { CQ("0123456789%%%h:`~"), EXP(CQ("0123456789%Jul:`~")) },
1622        { CQ("%R%h:`~ %x %w"), EXP(CQ("23:01Jul:`~ 07/02/08 3")) },
1623        #undef VEC
1624        #undef EXP
1625        };
1626
1627#if YEAR_BASE == 1900  /* ( */
1628/* Checks for very large years.  YEAR_BASE value relied upon so that the
1629 * answer strings can be predetermined.
1630 * Years more than 4 digits are not mentioned in the standard for %C, so the
1631 * test for those cases are based on the design intent (which is to print the
1632 * whole number, being the century).  */
1633const struct tm  tmyr0 = {
1634        /* Wed Jul  2 23:01:13 EDT [HUGE#] */
1635        .tm_sec         = 13,
1636        .tm_min         = 1,
1637        .tm_hour        = 23,
1638        .tm_mday        = 2,
1639        .tm_mon         = 6,
1640        .tm_year        = INT_MAX - YEAR_BASE/2,
1641        .tm_wday        = 3,
1642        .tm_yday        = 183,
1643        .tm_isdst       = 1
1644        };
1645#if INT_MAX == 32767
1646#  define YEAR  CQ("33717")             /* INT_MAX + YEAR_BASE/2 */
1647#  define CENT  CQ("337")
1648#  define Year     CQ("17")
1649# elif INT_MAX == 2147483647
1650#  define YEAR  CQ("2147484597")
1651#  define CENT  CQ("21474845")
1652#  define Year          CQ("97")
1653# elif INT_MAX == 9223372036854775807
1654#  define YEAR  CQ("9223372036854776757")
1655#  define CENT  CQ("92233720368547777")
1656#  define Year                   CQ("57")
1657# else
1658#  error "Unrecognized INT_MAX value:  enhance me to recognize what you have"
1659#endif
1660const struct test  Vecyr0[] = {
1661        /* Testing fields one at a time, expecting to pass, using a larger
1662         * allowed length than what is needed.  */
1663        /* Using tmyr0 for time: */
1664        #define EXP(s)  sizeof(s)/sizeof(CHAR)-1, s
1665        { CQ("%C"), OUTSIZE, EXP(CENT) },
1666        { CQ("%c"), OUTSIZE, EXP(CQ("Wed Jul  2 23:01:13 ")YEAR) },
1667        { CQ("%D"), OUTSIZE, EXP(CQ("07/02/")Year) },
1668        { CQ("%F"), OUTSIZE, EXP(YEAR CQ("-07-02")) },
1669        { CQ("%x"), OUTSIZE, EXP(CQ("07/02/")Year) },
1670        { CQ("%y"), OUTSIZE, EXP(Year) },
1671        { CQ("%Y"), OUTSIZE, EXP(YEAR) },
1672        #undef EXP
1673        };
1674#undef YEAR
1675#undef CENT
1676#undef Year
1677/* Checks for very large negative years.  YEAR_BASE value relied upon so that
1678 * the answer strings can be predetermined.  */
1679const struct tm  tmyr1 = {
1680        /* Wed Jul  2 23:01:13 EDT [HUGE#] */
1681        .tm_sec         = 13,
1682        .tm_min         = 1,
1683        .tm_hour        = 23,
1684        .tm_mday        = 2,
1685        .tm_mon         = 6,
1686        .tm_year        = INT_MIN,
1687        .tm_wday        = 3,
1688        .tm_yday        = 183,
1689        .tm_isdst       = 1
1690        };
1691#if INT_MAX == 32767
1692#  define YEAR  CQ("-30868")            /* INT_MIN + YEAR_BASE */
1693#  define CENT  CQ("-308")
1694#  define Year      CQ("68")
1695# elif INT_MAX == 2147483647
1696#  define YEAR  CQ("-2147481748")
1697#  define CENT  CQ("-21474817")
1698#  define Year           CQ("48")
1699# elif INT_MAX == 9223372036854775807
1700#  define YEAR  CQ("-9223372036854773908")
1701#  define CENT  CQ("-92233720368547739")
1702#  define Year                    CQ("08")
1703# else
1704#  error "Unrecognized INT_MAX value:  enhance me to recognize what you have"
1705#endif
1706const struct test  Vecyr1[] = {
1707        /* Testing fields one at a time, expecting to pass, using a larger
1708         * allowed length than what is needed.  */
1709        /* Using tmyr1 for time: */
1710        #define EXP(s)  sizeof(s)/sizeof(CHAR)-1, s
1711        { CQ("%C"), OUTSIZE, EXP(CENT) },
1712        { CQ("%c"), OUTSIZE, EXP(CQ("Wed Jul  2 23:01:13 ")YEAR) },
1713        { CQ("%D"), OUTSIZE, EXP(CQ("07/02/")Year) },
1714        { CQ("%F"), OUTSIZE, EXP(YEAR CQ("-07-02")) },
1715        { CQ("%x"), OUTSIZE, EXP(CQ("07/02/")Year) },
1716        { CQ("%y"), OUTSIZE, EXP(Year) },
1717        { CQ("%Y"), OUTSIZE, EXP(YEAR) },
1718        #undef EXP
1719        };
1720#undef YEAR
1721#undef CENT
1722#undef Year
1723#endif /* YEAR_BASE ) */
1724
1725/* Checks for years just over zero (also test for s=60).
1726 * Years less than 4 digits are not mentioned for %Y in the standard, so the
1727 * test for that case is based on the design intent.  */
1728const struct tm  tmyrzp = {
1729        /* Wed Jul  2 23:01:60 EDT 0007 */
1730        .tm_sec         = 60,
1731        .tm_min         = 1,
1732        .tm_hour        = 23,
1733        .tm_mday        = 2,
1734        .tm_mon         = 6,
1735        .tm_year        = 7-YEAR_BASE,
1736        .tm_wday        = 3,
1737        .tm_yday        = 183,
1738        .tm_isdst       = 1
1739        };
1740#define YEAR    CQ("0007")      /* Design intent:  %Y=%C%y */
1741#define CENT    CQ("00")
1742#define Year      CQ("07")
1743const struct test  Vecyrzp[] = {
1744        /* Testing fields one at a time, expecting to pass, using a larger
1745         * allowed length than what is needed.  */
1746        /* Using tmyrzp for time: */
1747        #define EXP(s)  sizeof(s)/sizeof(CHAR)-1, s
1748        { CQ("%C"), OUTSIZE, EXP(CENT) },
1749        { CQ("%c"), OUTSIZE, EXP(CQ("Wed Jul  2 23:01:60 ")YEAR) },
1750        { CQ("%D"), OUTSIZE, EXP(CQ("07/02/")Year) },
1751        { CQ("%F"), OUTSIZE, EXP(YEAR CQ("-07-02")) },
1752        { CQ("%x"), OUTSIZE, EXP(CQ("07/02/")Year) },
1753        { CQ("%y"), OUTSIZE, EXP(Year) },
1754        { CQ("%Y"), OUTSIZE, EXP(YEAR) },
1755        #undef EXP
1756        };
1757#undef YEAR
1758#undef CENT
1759#undef Year
1760/* Checks for years just under zero.
1761 * Negative years are not handled by the standard, so the vectors here are
1762 * verifying the chosen implemtation.  */
1763const struct tm  tmyrzn = {
1764        /* Wed Jul  2 23:01:00 EDT -004 */
1765        .tm_sec         = 00,
1766        .tm_min         = 1,
1767        .tm_hour        = 23,
1768        .tm_mday        = 2,
1769        .tm_mon         = 6,
1770        .tm_year        = -4-YEAR_BASE,
1771        .tm_wday        = 3,
1772        .tm_yday        = 183,
1773        .tm_isdst       = 1
1774        };
1775#define YEAR    CQ("-004")
1776#define CENT    CQ("-0")
1777#define Year      CQ("04")
1778const struct test  Vecyrzn[] = {
1779        /* Testing fields one at a time, expecting to pass, using a larger
1780         * allowed length than what is needed.  */
1781        /* Using tmyrzn for time: */
1782        #define EXP(s)  sizeof(s)/sizeof(CHAR)-1, s
1783        { CQ("%C"), OUTSIZE, EXP(CENT) },
1784        { CQ("%c"), OUTSIZE, EXP(CQ("Wed Jul  2 23:01:00 ")YEAR) },
1785        { CQ("%D"), OUTSIZE, EXP(CQ("07/02/")Year) },
1786        { CQ("%F"), OUTSIZE, EXP(YEAR CQ("-07-02")) },
1787        { CQ("%x"), OUTSIZE, EXP(CQ("07/02/")Year) },
1788        { CQ("%y"), OUTSIZE, EXP(Year) },
1789        { CQ("%Y"), OUTSIZE, EXP(YEAR) },
1790        #undef EXP
1791        };
1792#undef YEAR
1793#undef CENT
1794#undef Year
1795
1796const struct list  ListYr[] = {
1797        { &tmyrzp, Vecyrzp, sizeof(Vecyrzp)/sizeof(Vecyrzp[0]) },
1798        { &tmyrzn, Vecyrzn, sizeof(Vecyrzn)/sizeof(Vecyrzn[0]) },
1799        #if YEAR_BASE == 1900
1800        { &tmyr0, Vecyr0, sizeof(Vecyr0)/sizeof(Vecyr0[0]) },
1801        { &tmyr1, Vecyr1, sizeof(Vecyr1)/sizeof(Vecyr1[0]) },
1802        #endif
1803        };
1804
1805
1806/* List of tests to be run */
1807const struct list  List[] = {
1808        { &tm0, Vec0, sizeof(Vec0)/sizeof(Vec0[0]) },
1809        { &tm1, Vec1, sizeof(Vec1)/sizeof(Vec1[0]) },
1810        };
1811
1812#if defined(STUB_getenv_r)
1813char *
1814_getenv_r(struct _reent *p, const char *cp) { return getenv(cp); }
1815#endif
1816
1817int
1818main(void)
1819{
1820int  i, l, errr=0, erro=0, tot=0;
1821const char  *cp;
1822CHAR  out[OUTSIZE];
1823size_t  ret;
1824
1825/* Set timezone so that %z and %Z tests come out right */
1826cp = TZ;
1827if((i=putenv(cp)))  {
1828    printf( "putenv(%s) FAILED, ret %d\n", cp, i);
1829    return(-1);
1830    }
1831if(strcmp(getenv("TZ"),strchr(TZ,'=')+1))  {
1832    printf( "TZ not set properly in environment\n");
1833    return(-2);
1834    }
1835tzset();
1836
1837#if defined(VERBOSE)
1838printf("_timezone=%d, _daylight=%d, _tzname[0]=%s, _tzname[1]=%s\n", _timezone, _daylight, _tzname[0], _tzname[1]);
1839{
1840long offset;
1841__tzinfo_type *tz = __gettzinfo ();
1842/* The sign of this is exactly opposite the envvar TZ.  We
1843   could directly use the global _timezone for tm_isdst==0,
1844   but have to use __tzrule for daylight savings.  */
1845printf("tz->__tzrule[0].offset=%d, tz->__tzrule[1].offset=%d\n", tz->__tzrule[0].offset, tz->__tzrule[1].offset);
1846}
1847#endif
1848
1849/* Run all of the exact-length tests as-given--results should match */
1850for(l=0; l<sizeof(List)/sizeof(List[0]); l++)  {
1851    const struct list  *test = &List[l];
1852    for(i=0; i<test->cnt; i++)  {
1853        tot++;  /* Keep track of number of tests */
1854        ret = strftime(out, test->vec[i].max, test->vec[i].fmt, test->tms);
1855        if(ret != test->vec[i].ret)  {
1856            errr++;
1857            fprintf(stderr,
1858                "ERROR:  return %d != %d expected for List[%d].vec[%d]\n",
1859                                                ret, test->vec[i].ret, l, i);
1860            }
1861        if(strncmp(out, test->vec[i].out, test->vec[i].max-1))  {
1862            erro++;
1863            fprintf(stderr,
1864                "ERROR:  \"%"SFLG"s\" != \"%"SFLG"s\" expected for List[%d].vec[%d]\n",
1865                                                out, test->vec[i].out, l, i);
1866            }
1867        }
1868    }
1869
1870/* Run all of the exact-length tests with the length made too short--expect to
1871 * fail.  */
1872for(l=0; l<sizeof(List)/sizeof(List[0]); l++)  {
1873    const struct list  *test = &List[l];
1874    for(i=0; i<test->cnt; i++)  {
1875        tot++;  /* Keep track of number of tests */
1876        ret = strftime(out, test->vec[i].max-1, test->vec[i].fmt, test->tms);
1877        if(ret != 0)  {
1878            errr++;
1879            fprintf(stderr,
1880                "ERROR:  return %d != %d expected for List[%d].vec[%d]\n",
1881                                                ret, 0, l, i);
1882            }
1883        /* Almost every conversion puts out as many characters as possible, so
1884         * go ahead and test the output even though have failed.  (The test
1885         * times chosen happen to not hit any of the cases that fail this, so it
1886         * works.)  */
1887        if(strncmp(out, test->vec[i].out, test->vec[i].max-1-1))  {
1888            erro++;
1889            fprintf(stderr,
1890                "ERROR:  \"%"SFLG"s\" != \"%"SFLG"s\" expected for List[%d].vec[%d]\n",
1891                                                out, test->vec[i].out, l, i);
1892            }
1893        }
1894    }
1895
1896/* Run all of the special year test cases */
1897for(l=0; l<sizeof(ListYr)/sizeof(ListYr[0]); l++)  {
1898    const struct list  *test = &ListYr[l];
1899    for(i=0; i<test->cnt; i++)  {
1900        tot++;  /* Keep track of number of tests */
1901        ret = strftime(out, test->vec[i].max, test->vec[i].fmt, test->tms);
1902        if(ret != test->vec[i].ret)  {
1903            errr++;
1904            fprintf(stderr,
1905                "ERROR:  return %d != %d expected for ListYr[%d].vec[%d]\n",
1906                                                ret, test->vec[i].ret, l, i);
1907            }
1908        if(strncmp(out, test->vec[i].out, test->vec[i].max-1))  {
1909            erro++;
1910            fprintf(stderr,
1911                "ERROR:  \"%"SFLG"s\" != \"%"SFLG"s\" expected for ListYr[%d].vec[%d]\n",
1912                                                out, test->vec[i].out, l, i);
1913            }
1914        }
1915    }
1916
1917#define STRIZE(f)       #f
1918#define NAME(f) STRIZE(f)
1919printf(NAME(strftime) "() test ");
1920if(errr || erro)  printf("FAILED %d/%d of", errr, erro);
1921  else    printf("passed");
1922printf(" %d test cases.\n", tot);
1923
1924return(errr || erro);
1925}
1926#endif /* defined(_REGRESSION_TEST) ] */
Note: See TracBrowser for help on using the repository browser.