Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 37 additions & 1 deletion configure.ac
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
dnl
dnl autoconf for Pacemaker
dnl
dnl Copyright 2009-2025 the Pacemaker project contributors
dnl Copyright 2009-2026 the Pacemaker project contributors
dnl
dnl The version control history for this file may have further details.
dnl
Expand Down Expand Up @@ -1234,6 +1234,42 @@ AS_IF([test x"$have_sscanf_m" = x"yes"],
[AC_DEFINE([HAVE_SSCANF_M], [1],
[Define to 1 if sscanf %m modifier is available])])

AC_RUN_IFELSE([AC_LANG_PROGRAM([[#include <time.h>]], [[
char s[200];
time_t t;
struct tm *tmp;
size_t rc;

t = time(NULL);
tmp = localtime(&t);
rc = strftime(s, sizeof(s), "%3S", tmp);
return rc != 3;
]])],
[have_strftime_width="yes"],
[have_strftime_width="no"],
[have_strftime_width="no"])
AS_IF([test x"$have_strftime_width" = x"yes"],
[AC_DEFINE([HAVE_STRFTIME_WIDTH], [1],
[Define to 1 if strftime supports field width])])

AC_RUN_IFELSE([AC_LANG_PROGRAM([[#include <time.h>]], [[
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good lord this is ugly. I looked around a bit for a cleaner way, but this seems like about the best we can do, other than potentially dropping bare declarations:

char s[200] = { '\0', };
time_t t = time(NULL);

return strftime(s, sizeof(s), "% abcd", localtime(&t)) != 0;

With that said, we use HAVE_STRFTIME_EMPTY_SPEC_FAILS only in one unit test file. So alternatively, we could add a setup function there that tests whether strftime() returns nonzero when there's an empty spec, and sets a file-scope variable if so.

I don't have strong feelings on it and it's not worth spending a ton of time on. Hopefully this all goes away in the future.

char s[200];
time_t t;
struct tm *tmp;
size_t rc;

t = time(NULL);
tmp = localtime(&t);
rc = strftime(s, sizeof(s), "% abcd", tmp);
return rc != 0;
]])],
[have_strftime_empty_spec_fails="yes"],
[have_strftime_empty_spec_fails="no"],
[have_strftime_empty_spec_fails="no"])
AS_IF([test x"$have_strftime_empty_spec_fails" = x"yes"],
[AC_DEFINE([HAVE_STRFTIME_EMPTY_SPEC_FAILS], [1],
[Define to 1 if a percent with no specifier fails])])

dnl ========================================================================
dnl bzip2
dnl ========================================================================
Expand Down
37 changes: 34 additions & 3 deletions lib/common/tests/iso8601/pcmk__time_format_hr_test.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2024-2025 the Pacemaker project contributors
* Copyright 2024-2026 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
Expand Down Expand Up @@ -85,24 +85,43 @@ no_specifiers(void **state)
* as "%%".
*/
assert_hr_format("this has a literal % in it",
#ifdef HAVE_STRFTIME_EMPTY_SPEC_FAILS
NULL, NULL, 0);
#else
"this has a literal % in it",
// *BSD strftime() strips single %
"this has a literal in it", 0);
#endif

assert_hr_format("this has a literal %01 in it",
#ifdef HAVE_STRFTIME_EMPTY_SPEC_FAILS
NULL, NULL, 0);
#else
"this has a literal %01 in it",
// *BSD strftime() strips %0
"this has a literal 1 in it", 0);
#endif

assert_hr_format("%2 this starts and ends with %",
#ifdef HAVE_STRFTIME_EMPTY_SPEC_FAILS
NULL, NULL, 0);
#else
"%2 this starts and ends with %",
// *BSD strftime() strips % in front of nonzero number
"2 this starts and ends with %", 0);
#endif

/* strftime() treats % with a number (and no specifier) as a literal string
* to be formatted with a field width (undocumented and probably a bug ...)
*/
assert_hr_format("this ends with %10", "this ends with %10",
assert_hr_format("this ends with %10",
#ifdef HAVE_STRFTIME_EMPTY_SPEC_FAILS
NULL, NULL, 0);
#else
"this ends with %10",
// *BSD strftime() strips % in front of nonzero number
"this ends with 10", 0);
#endif
}

static void
Expand All @@ -120,8 +139,20 @@ without_frac(void **state)
* g_date_time_format() doesn't support field widths.
*/
assert_hr_format("%3S seconds", "0" SECOND_S " seconds",
// *BSD strftime() doesn't support field widths
#if defined(HAVE_STRFTIME_WIDTH)
NULL, 0);
#elif defined(__FreeBSD__)
/* FreeBSD doesn't support field width in strftime(). It
* strips the leading % and copies the rest of the specifier
* into the string.
*/
"3S seconds", 0);
#else
/* musl (and maybe other c libraries) ignores the field width
* and handle the rest of the specifier normally.
*/
SECOND_S " seconds", 0);
#endif

// strftime() treats %% as a literal %
assert_hr_format("%%H %%N", "%H %N", NULL, 0);
Expand Down