xz: Create separate is_tty() function.

The new is_tty() will report if a file descriptor is a terminal or not.
On POSIX systems, it is a wrapper around isatty(). However, the native
Windows implementation of isatty() will return true for all character
devices, not just terminals. So is_tty() has a special case for Windows
so it can use alternative Windows API functions to determine if a file
descriptor is a terminal.

This fixes a bug with MSVC and MinGW-w64 builds that refused to read from
or write to non-terminal character devices because xz thought it was a
terminal. For instance:

    xz foo -c > /dev/null

would fail because /dev/null was assumed to be a terminal.

(cherry picked from commit 0ecfaa6df9)
This commit is contained in:
Jia Tan 2023-11-23 22:04:35 +08:00 committed by Lasse Collin
parent aa036419c2
commit 9f00ad72f0
2 changed files with 37 additions and 2 deletions

View File

@ -272,10 +272,31 @@ is_empty_filename(const char *filename)
} }
extern bool
is_tty(int fd)
{
#if defined(_WIN32) && !defined(__CYGWIN__)
// There is no need to check if handle == INVALID_HANDLE_VALUE
// because it will return false anyway when used in GetConsoleMode().
// The resulting HANDLE is owned by the file descriptor.
// The HANDLE must not be closed here.
intptr_t handle = _get_osfhandle(fd);
DWORD mode;
// GetConsoleMode() is an easy way to tell if the HANDLE is a
// console or not. We do not care about the value of mode since we
// do not plan to use any further Windows console functions.
return GetConsoleMode((HANDLE)handle, &mode);
#else
return isatty(fd);
#endif
}
extern bool extern bool
is_tty_stdin(void) is_tty_stdin(void)
{ {
const bool ret = isatty(STDIN_FILENO); const bool ret = is_tty(STDIN_FILENO);
if (ret) if (ret)
message_error(_("Compressed data cannot be read from " message_error(_("Compressed data cannot be read from "
@ -288,7 +309,7 @@ is_tty_stdin(void)
extern bool extern bool
is_tty_stdout(void) is_tty_stdout(void)
{ {
const bool ret = isatty(STDOUT_FILENO); const bool ret = is_tty(STDOUT_FILENO);
if (ret) if (ret)
message_error(_("Compressed data cannot be written to " message_error(_("Compressed data cannot be written to "

View File

@ -109,6 +109,20 @@ extern void my_snprintf(char **pos, size_t *left, const char *fmt, ...);
extern bool is_empty_filename(const char *filename); extern bool is_empty_filename(const char *filename);
/// \brief Test if file descriptor is a terminal
///
/// For POSIX systems, this is a simple wrapper around isatty(). However on
/// Windows, isatty() returns true for all character devices, not just
/// terminals.
///
/// \param fd File descriptor to test
///
/// \return bool:
/// - true if file descriptor is a terminal
/// - false otherwise
extern bool is_tty(int fd);
/// \brief Test if stdin is a terminal /// \brief Test if stdin is a terminal
/// ///
/// If stdin is a terminal, an error message is printed and exit status set /// If stdin is a terminal, an error message is printed and exit status set