Subversion Repositories Kolibri OS

Rev

Rev 4874 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

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