Rev 4874 | Go to most recent revision | Show entire file | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 4874 | Rev 4921 | ||
---|---|---|---|
Line 22... | Line 22... | ||
22 | INDEX |
22 | INDEX |
23 | strftime |
23 | strftime |
Line 24... | Line 24... | ||
24 | 24 | ||
25 | ANSI_SYNOPSIS |
25 | ANSI_SYNOPSIS |
26 | #include |
26 | #include |
- | 27 | size_t strftime(char *restrict <[s]>, size_t <[maxsize]>, |
|
27 | size_t strftime(char *<[s]>, size_t <[maxsize]>, |
28 | const char *restrict <[format]>, |
Line 28... | Line 29... | ||
28 | const char *<[format]>, const struct tm *<[timp]>); |
29 | const struct tm *restrict <[timp]>); |
29 | 30 | ||
30 | TRAD_SYNOPSIS |
31 | TRAD_SYNOPSIS |
31 | #include |
32 | #include |
Line 268... | Line 269... | ||
268 | #include |
269 | #include |
269 | #include |
270 | #include |
270 | #include |
271 | #include |
271 | #include |
272 | #include |
272 | #include |
273 | #include |
- | 274 | #include "local.h" |
|
- | 275 | #include "../locale/timelocal.h" |
|
Line 273... | Line 276... | ||
273 | 276 | ||
274 | /* Defines to make the file dual use for either strftime() or wcsftime(). |
277 | /* Defines to make the file dual use for either strftime() or wcsftime(). |
275 | * To get wcsftime, define MAKE_WCSFTIME. |
278 | * To get wcsftime, define MAKE_WCSFTIME. |
276 | * To get strftime, do not define MAKE_WCSFTIME. |
279 | * To get strftime, do not define MAKE_WCSFTIME. |
Line 281... | Line 284... | ||
281 | #if !defined(MAKE_WCSFTIME) |
284 | #if !defined(MAKE_WCSFTIME) |
282 | # define CHAR char /* string type basis */ |
285 | # define CHAR char /* string type basis */ |
283 | # define CQ(a) a /* character constant qualifier */ |
286 | # define CQ(a) a /* character constant qualifier */ |
284 | # define SFLG /* %s flag (null for normal char) */ |
287 | # define SFLG /* %s flag (null for normal char) */ |
285 | # define _ctloc(x) (ctloclen = strlen (ctloc = _CurrentTimeLocale->x), ctloc) |
288 | # define _ctloc(x) (ctloclen = strlen (ctloc = _CurrentTimeLocale->x), ctloc) |
- | 289 | # define snprintf sniprintf /* avoid to pull in FP functions. */ |
|
286 | # define TOLOWER(c) tolower((int)(unsigned char)(c)) |
290 | # define TOLOWER(c) tolower((int)(unsigned char)(c)) |
287 | # define STRTOUL(c,p,b) strtoul((c),(p),(b)) |
291 | # define STRTOUL(c,p,b) strtoul((c),(p),(b)) |
288 | # define STRCPY(a,b) strcpy((a),(b)) |
292 | # define STRCPY(a,b) strcpy((a),(b)) |
289 | # define STRCHR(a,b) strchr((a),(b)) |
293 | # define STRCHR(a,b) strchr((a),(b)) |
290 | # define STRLEN(a) strlen(a) |
294 | # define STRLEN(a) strlen(a) |
Line 317... | Line 321... | ||
317 | # define _ctloc(x) (ctloc = __ctloc (ctlocbuf, _CurrentTimeLocale->x, \ |
321 | # define _ctloc(x) (ctloc = __ctloc (ctlocbuf, _CurrentTimeLocale->x, \ |
318 | &ctloclen)) |
322 | &ctloclen)) |
319 | # endif |
323 | # endif |
320 | #endif /* MAKE_WCSFTIME */ |
324 | #endif /* MAKE_WCSFTIME */ |
Line -... | Line 325... | ||
- | 325 | ||
- | 326 | #define CHECK_LENGTH() if (len < 0 || (count += len) >= maxsize) \ |
|
Line 321... | Line 327... | ||
321 | 327 | return 0 |
|
322 | 328 | ||
323 | size_t _DEFUN (strftime, (s, maxsize, format, tim_p), |
329 | /* Enforce the coding assumptions that YEAR_BASE is positive. (%C, %Y, etc.) */ |
- | 330 | #if YEAR_BASE < 0 |
|
- | 331 | # error "YEAR_BASE < 0" |
|
324 | CHAR *s _AND |
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. */ |
|
325 | size_t maxsize _AND |
340 | static int |
326 | _CONST CHAR *format _AND |
341 | _DEFUN (iso_year_adjust, (tim_p), |
- | 342 | _CONST struct tm *tim_p) |
|
- | 343 | { |
|
- | 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))); |
|
- | 347 | ||
- | 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 | } |
|
- | 658 | return 0; |
|
- | 659 | } |
|
- | 660 | ||
- | 661 | static size_t __strftime (CHAR *, size_t, const CHAR *, const struct tm *, |
|
- | 662 | era_info_t **, alt_digits_t **); |
|
- | 663 | ||
- | 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 |
|
Line -... | Line 861... | ||
- | 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]; |
|
327 | _CONST struct tm *tim_p) |
1309 | else |
328 | { |
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; 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; 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); |
|
Line -... | Line 1755... | ||
- | 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; 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) |
|
Line -... | Line 1782... | ||
- | 1782 | printf(NAME(strftime) "() test "); |
|
- | 1783 | if(errr || erro) printf("FAILED %d/%d of", errr, erro); |
|
- | 1784 | else printf("passed"); |