diff --git a/Help/manual/cmake.1.rst b/Help/manual/cmake.1.rst
index 5223acb5f1..15ae6ea467 100644
--- a/Help/manual/cmake.1.rst
+++ b/Help/manual/cmake.1.rst
@@ -891,6 +891,10 @@ Available commands are:
``-`` will result in an error. Use ``--`` to indicate the end of options, in
case a file starts with ``-``.
+ .. versionadded:: 3.29
+
+ ``cat`` can now print the standard input by passing the ``-`` argument.
+
.. program:: cmake-E
.. option:: chdir
[...]
diff --git a/Help/release/dev/cmake-E-cat-stdin.rst b/Help/release/dev/cmake-E-cat-stdin.rst
new file mode 100644
index 0000000000..43a8aedea9
--- /dev/null
+++ b/Help/release/dev/cmake-E-cat-stdin.rst
@@ -0,0 +1,5 @@
+cmake-E-cat-stdin
+-----------------
+
+* :manual:`cmake(1)` :option:`-E cat ` can now print the standard
+ input by passing the ``-`` argument.
diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx
index 43a945f2ed..93b008694e 100644
--- a/Source/cmcmd.cxx
+++ b/Source/cmcmd.cxx
@@ -203,11 +203,16 @@ bool cmTarFilesFrom(std::string const& file, std::vector& files)
void cmCatFile(const std::string& fileToAppend)
{
#ifdef _WIN32
+ _setmode(fileno(stdin), _O_BINARY);
_setmode(fileno(stdout), _O_BINARY);
#endif
- cmsys::ifstream source(fileToAppend.c_str(),
- (std::ios::binary | std::ios::in));
- std::cout << source.rdbuf();
+ std::streambuf* buf = std::cin.rdbuf();
+ cmsys::ifstream source;
+ if (fileToAppend != "-") {
+ source.open(fileToAppend.c_str(), (std::ios::binary | std::ios::in));
+ buf = source.rdbuf();
+ }
+ std::cout << buf;
}
bool cmRemoveDirectory(const std::string& dir, bool recursive = true)
@@ -1147,7 +1152,12 @@ int cmcmd::ExecuteCMakeCommand(std::vector const& args,
int return_value = 0;
bool doing_options = true;
for (auto const& arg : cmMakeRange(args).advance(2)) {
- if (doing_options && cmHasLiteralPrefix(arg, "-")) {
+ if (arg == "-") {
+ doing_options = false;
+ // Destroy console buffers to drop cout/cerr encoding transform.
+ consoleBuf.reset();
+ cmCatFile(arg);
+ } else if (doing_options && cmHasLiteralPrefix(arg, "-")) {
if (arg == "--") {
doing_options = false;
} else {
diff --git a/Tests/RunCMake/CommandLine/E_cat-stdin-stdout.txt b/Tests/RunCMake/CommandLine/E_cat-stdin-stdout.txt
new file mode 100644
index 0000000000..8210d59ac0
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/E_cat-stdin-stdout.txt
@@ -0,0 +1 @@
+^Hello world$
diff --git a/Tests/RunCMake/CommandLine/E_cat-stdin.cmake b/Tests/RunCMake/CommandLine/E_cat-stdin.cmake
new file mode 100644
index 0000000000..e83e6194d3
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/E_cat-stdin.cmake
@@ -0,0 +1,10 @@
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/ell.txt" "ell")
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/rld.txt" "rld")
+execute_process(
+ COMMAND ${CMAKE_COMMAND} -E echo_append "H"
+ COMMAND ${CMAKE_COMMAND} -E cat -
+ )
+execute_process(
+ COMMAND ${CMAKE_COMMAND} -E echo_append "o wo"
+ COMMAND ${CMAKE_COMMAND} -E cat "${CMAKE_CURRENT_BINARY_DIR}/ell.txt" - "${CMAKE_CURRENT_BINARY_DIR}/rld.txt"
+ )
diff --git a/Tests/RunCMake/CommandLine/RunCMakeTest.cmake b/Tests/RunCMake/CommandLine/RunCMakeTest.cmake
index 52be1bbd8a..6d4e0e2087 100644
--- a/Tests/RunCMake/CommandLine/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CommandLine/RunCMakeTest.cmake
@@ -782,6 +782,8 @@ run_cmake_command(E_cat-without-double-dash ${CMAKE_COMMAND} -E cat "-file-start
unset(RunCMake_TEST_COMMAND_WORKING_DIRECTORY)
unset(out)
+run_cmake_command(E_cat-stdin ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/E_cat-stdin.cmake)
+
# Unset environment variables that are used for testing cmake -E
unset(ENV{TEST_ENV})
unset(ENV{TEST_ENV_EXPECTED})