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