From e74a51a09fdd4e21886dcc3e6df8f8f38d8d5c81 Mon Sep 17 00:00:00 2001 From: Jay Howard Date: Sun, 6 Sep 2026 20:36:43 -0500 Subject: [PATCH] programs: strip trailing '\r' from --filelist entries on Windows `--filelist` stopped accepting Windows CRLF line endings in 165e52ce ("first implementation supporting Process Substitution", #4349). That rewrite reads the list into a buffer opened in binary mode and splits it on '\n' alone. Previously the list was opened in text mode, where the Windows CRT translated CRLF to LF before the parser saw it, so stripping '\n' was sufficient. In binary mode the CR survives into every path: zstd: can't stat a.txt : No such file or directory -- ignored The '\r' is invisible in that message, which makes it awkward to diagnose. Strip a trailing '\r' when the line pointers are built, but only on Windows, where CRLF is the native line separator and a '\r' cannot be used in a filename. Elsewhere '\r' is a legal filename byte and is left alone, so behaviour on those platforms is unchanged. This restores the pre-regression behaviour exactly, on every platform, without adding a new one. Adds a playTests case, guarded to Windows, that fails without the fix. --- programs/util.c | 7 +++++++ tests/playTests.sh | 17 +++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/programs/util.c b/programs/util.c index 652530b1223..10f39af0566 100644 --- a/programs/util.c +++ b/programs/util.c @@ -730,6 +730,13 @@ static const char** UTIL_createLinePointers(char* buffer, size_t numLines, size_ len++; } +#if defined(_WIN32) + /* note: read in binary mode, so the CRT no longer folds CRLF; CR is legal in a filename elsewhere */ + if (len > 0 && buffer[pos + len - 1] == '\r') { + buffer[pos + len - 1] = '\0'; + } +#endif + /* Move past this string and its null terminator */ pos += len; if (pos < bufferSize) pos++; /* Skip the null terminator if we're not at buffer end */ diff --git a/tests/playTests.sh b/tests/playTests.sh index d8ea0c3dad3..6eb029a9b49 100755 --- a/tests/playTests.sh +++ b/tests/playTests.sh @@ -800,6 +800,23 @@ zstd -f --filelist=tmp_symLink test -f tmp2.zst test -f tmp1.zst +# CR is only stripped when _WIN32 is defined: MSVC and MinGW, but not Cygwin +stripsCR=false +if [ "$isWindows" = true ] ; then + case "$UNAME" in + MINGW*|MSYS*) stripsCR=true ;; + esac +fi + +if [ "$stripsCR" = true ] ; then + println "test : file list with Windows CRLF line endings, --filelist=FILE" # (#4349) + rm -f *.zst + printf 'tmp1\r\ntmp2\r\n' > tmp_crlfList + zstd -f --filelist=tmp_crlfList + test -f tmp1.zst + test -f tmp2.zst +fi + println "test : compress multiple files reading them from multiple files, --filelist=FILE" rm -f *.zst println "Hello world!, file3" > tmp3