diff --git a/.github/scripts/positional_printf_check.c b/.github/scripts/positional_printf_check.c new file mode 100644 index 00000000000..2f2b1cc5c86 --- /dev/null +++ b/.github/scripts/positional_printf_check.c @@ -0,0 +1,129 @@ +/* Throwaway check: do POSIX positional printf arguments work here? + * Results are printed as PASS/FAIL lines; the exit status is always 0. + * + * Optional defines: + * USE_LIBINTL include first and report its redirects + * TEST_LIBINTL_DIRECT also call libintl_snprintf()/libintl_vsnprintf() + */ +#ifdef USE_LIBINTL +#include +#endif +#include +#include +#include +#include +#include + +#define BUFSZ 128 + +#ifdef TEST_LIBINTL_DIRECT +/* Declared here in case libintl.h only declares them conditionally. */ +extern int libintl_snprintf(char *, size_t, const char *, ...); +extern int libintl_vsnprintf(char *, size_t, const char *, va_list); +#endif + +static void report(const char *func, const char *fmt, const char *result, + const char *expected) +{ + printf("%s: func=%s format=\"%s\" result=\"%s\" expected=\"%s\"\n", + strcmp(result, expected) == 0 ? "PASS" : "FAIL", func, fmt, result, + expected); +} + +/* Mimics G_vasprintf(): formats through a va_list function. */ +#define DEFINE_WRAPPER(name, vfunc) \ + static int name(char *buf, size_t n, const char *fmt, ...) \ + { \ + va_list ap; \ + int ret; \ + \ + va_start(ap, fmt); \ + ret = vfunc(buf, n, fmt, ap); \ + va_end(ap); \ + return ret; \ + } + +DEFINE_WRAPPER(wrap_vsnprintf, vsnprintf) +#ifdef TEST_LIBINTL_DIRECT +DEFINE_WRAPPER(wrap_libintl_vsnprintf, libintl_vsnprintf) +#endif +#ifdef _MSC_VER +DEFINE_WRAPPER(wrap_vsprintf_p, _vsprintf_p) +#endif + +/* Runs all formats through fn(buf, size, fmt, args...). */ +#define RUN_CASES(label, fn) \ + do { \ + memset(buf, 0, sizeof buf); \ + fn(buf, sizeof buf, "%2$s %1$s", "a", "b"); \ + report(label, "%2$s %1$s", buf, "b a"); \ + memset(buf, 0, sizeof buf); \ + fn(buf, sizeof buf, "%2$d %1$s", "x", 42); \ + report(label, "%2$d %1$s", buf, "42 x"); \ + memset(buf, 0, sizeof buf); \ + fn(buf, sizeof buf, "%2$lu %1$llu", 5ULL, 7UL); \ + report(label, "%2$lu %1$llu", buf, "7 5"); \ + memset(buf, 0, sizeof buf); \ + fn(buf, sizeof buf, "%2$" PRIu64 " %1$s", "x", (uint64_t)123); \ + report(label, "%2$" PRIu64 " %1$s", buf, "123 x"); \ + } while (0) + +int main(void) +{ + char buf[BUFSZ]; + +#ifdef _MSC_VER + printf("compiler: MSVC _MSC_VER=%d\n", _MSC_VER); +#endif +#ifdef __GNUC__ + printf("compiler: GCC %d.%d\n", __GNUC__, __GNUC_MINOR__); +#endif +#ifdef _UCRT + printf("runtime: _UCRT defined\n"); +#else + printf("runtime: _UCRT not defined\n"); +#endif +#ifdef __USE_MINGW_ANSI_STDIO + printf("__USE_MINGW_ANSI_STDIO=%d\n", __USE_MINGW_ANSI_STDIO); +#else + printf("__USE_MINGW_ANSI_STDIO not defined\n"); +#endif +#ifdef USE_LIBINTL + printf("libintl.h included first\n"); +#ifdef snprintf + printf("snprintf IS a macro after libintl.h\n"); +#else + printf("snprintf is NOT a macro after libintl.h\n"); +#endif +#ifdef vsnprintf + printf("vsnprintf IS a macro after libintl.h\n"); +#else + printf("vsnprintf is NOT a macro after libintl.h\n"); +#endif +#ifdef _INTL_REDIRECT_MACROS + printf("_INTL_REDIRECT_MACROS is defined\n"); +#else + printf("_INTL_REDIRECT_MACROS is not defined\n"); +#endif +#ifdef _INTL_NO_REDIRECT_PRINTF + printf("_INTL_NO_REDIRECT_PRINTF is defined\n"); +#else + printf("_INTL_NO_REDIRECT_PRINTF is not defined\n"); +#endif +#endif + + RUN_CASES("snprintf", snprintf); + RUN_CASES("vsnprintf(wrapper)", wrap_vsnprintf); + +#ifdef TEST_LIBINTL_DIRECT + RUN_CASES("libintl_snprintf", libintl_snprintf); + RUN_CASES("libintl_vsnprintf(wrapper)", wrap_libintl_vsnprintf); +#endif + +#ifdef _MSC_VER + RUN_CASES("_sprintf_p", _sprintf_p); + RUN_CASES("_vsprintf_p(wrapper)", wrap_vsprintf_p); +#endif + + return 0; +} diff --git a/.github/workflows/positional_printf_check.yml b/.github/workflows/positional_printf_check.yml new file mode 100644 index 00000000000..5a251dcde45 --- /dev/null +++ b/.github/workflows/positional_printf_check.yml @@ -0,0 +1,91 @@ +--- +name: Positional printf check + +# Throwaway check of POSIX positional printf arguments on Windows toolchains. +on: + workflow_dispatch: + pull_request: + paths: + - .github/workflows/positional_printf_check.yml + - .github/scripts/positional_printf_check.c + +permissions: {} + +jobs: + msvc: + name: MSVC + runs-on: windows-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - uses: ilammy/msvc-dev-cmd@a102174a2b586eec2ea151a69e6fd14404a8ce7c # v1.13.0 + - name: Compile + shell: cmd + run: cl /nologo /W3 .github\scripts\positional_printf_check.c /Fe:check.exe + - name: Run + shell: cmd + run: check.exe + - name: Install libintl with vcpkg + id: vcpkg + continue-on-error: true + shell: cmd + run: vcpkg install gettext-libintl:x64-windows + - name: Compile with libintl + if: steps.vcpkg.outcome == 'success' + continue-on-error: true + shell: cmd + run: | + set VPREFIX=%VCPKG_INSTALLATION_ROOT%\installed\x64-windows + dir %VPREFIX%\include\libintl.h %VPREFIX%\lib\*intl* %VPREFIX%\bin\*intl* + findstr /n /i "define.*printf _INTL_REDIRECT" %VPREFIX%\include\libintl.h + dumpbin /exports %VPREFIX%\bin\intl-8.dll | findstr /i "printf" + cl /nologo /W3 /DUSE_LIBINTL /I%VPREFIX%\include .github\scripts\positional_printf_check.c /Fe:check_intl.exe /link /LIBPATH:%VPREFIX%\lib intl.lib + cl /nologo /W3 /DUSE_LIBINTL /DTEST_LIBINTL_DIRECT /I%VPREFIX%\include .github\scripts\positional_printf_check.c /Fe:check_intl_direct.exe /link /LIBPATH:%VPREFIX%\lib intl.lib + - name: Run with libintl + if: steps.vcpkg.outcome == 'success' + continue-on-error: true + shell: cmd + run: | + set PATH=%VCPKG_INSTALLATION_ROOT%\installed\x64-windows\bin;%PATH% + check_intl.exe + + mingw: + name: MinGW64 (MSYS2) + runs-on: windows-latest + defaults: + run: + shell: msys2 {0} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - uses: msys2/setup-msys2@ec48f7c5447b3140e2b088413ae3a55687bccb6e # v2.33.0 + with: + msystem: MINGW64 + update: false + install: >- + mingw-w64-x86_64-gcc + mingw-w64-x86_64-gettext-runtime + mingw-w64-x86_64-gettext-tools + - name: Compile + run: | + gcc -Wall -DUSE_LIBINTL -DTEST_LIBINTL_DIRECT -o check_intl.exe .github/scripts/positional_printf_check.c -lintl + gcc -Wall -D__USE_MINGW_ANSI_STDIO=0 -DUSE_LIBINTL -DTEST_LIBINTL_DIRECT -o check_intl_msvcrt.exe .github/scripts/positional_printf_check.c -lintl + gcc -Wall -o check_default.exe .github/scripts/positional_printf_check.c + gcc -Wall -D__USE_MINGW_ANSI_STDIO=1 -o check_ansi.exe .github/scripts/positional_printf_check.c + gcc -Wall -D__USE_MINGW_ANSI_STDIO=0 -o check_msvcrt.exe .github/scripts/positional_printf_check.c + - name: Run with default __USE_MINGW_ANSI_STDIO + run: ./check_default.exe + - name: Run with __USE_MINGW_ANSI_STDIO=1 + run: ./check_ansi.exe + - name: Run with __USE_MINGW_ANSI_STDIO=0 + run: ./check_msvcrt.exe + - name: Show libintl.h printf redirects + run: | + ls -l /mingw64/include/libintl.h + grep -n -i "define.*printf\|_INTL_REDIRECT\|_INTL_NO_REDIRECT" /mingw64/include/libintl.h + - name: Run with libintl.h + run: ./check_intl.exe + - name: Run with libintl.h and __USE_MINGW_ANSI_STDIO=0 + run: ./check_intl_msvcrt.exe