Before, isatty() was an alias for WinAPI _isatty(). This resulted in wrong result for mintty.
Implement a pipe name check in a static isatty(). This makes io.h include NT and Windows APIs. The change isn't strictly standard, as it adds 'static' to the isatty() signature. Signed-off-by: Mihail Konev <k....@ya.ru> Moved-from: https://github.com/Alexpux/mingw-w64/pull/3 Reference: https://cygwin.com/ml/cygwin-developers/2016-11/msg00002.html --- v4: - do not check for msys-* pipe name - fix typos This would only work for MSYS once it is patched to revert pipe names. mingw-w64-headers/crt/io.h | 105 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 104 insertions(+), 1 deletion(-) diff --git a/mingw-w64-headers/crt/io.h b/mingw-w64-headers/crt/io.h index c61e94ab8743..9fa776c8c9b1 100644 --- a/mingw-w64-headers/crt/io.h +++ b/mingw-w64-headers/crt/io.h @@ -9,6 +9,12 @@ #include <crtdefs.h> #include <string.h> +/* for cygwin-compatible isatty */ +#include <wchar.h> +#include <errno.h> +#include <winternl.h> +#include <windows.h> + #pragma pack(push,_CRT_PACKING) #ifdef __cplusplus @@ -322,7 +328,6 @@ _CRTIMP char* __cdecl _getcwd (char*, int); int __cdecl dup2(int _FileHandleSrc,int _FileHandleDst) __MINGW_ATTRIB_DEPRECATED_MSVC2005; int __cdecl eof(int _FileHandle) __MINGW_ATTRIB_DEPRECATED_MSVC2005; long __cdecl filelength(int _FileHandle) __MINGW_ATTRIB_DEPRECATED_MSVC2005; - int __cdecl isatty(int _FileHandle) __MINGW_ATTRIB_DEPRECATED_MSVC2005; int __cdecl locking(int _FileHandle,int _LockMode,long _NumOfBytes) __MINGW_ATTRIB_DEPRECATED_MSVC2005; long __cdecl lseek(int _FileHandle,long _Offset,int _Origin) __MINGW_ATTRIB_DEPRECATED_MSVC2005; char *__cdecl mktemp(char *_TemplateName) __MINGW_ATTRIB_DEPRECATED_MSVC2005; @@ -335,6 +340,104 @@ _CRTIMP char* __cdecl _getcwd (char*, int); int __cdecl write(int _Filehandle,const void *_Buf,unsigned int _MaxCharCount) __MINGW_ATTRIB_DEPRECATED_MSVC2005; #endif +/* Cygwin-compatible isatty. + * + * Cygwin pty is a specially-named named pipe. + * Fetch [absolute] fd's NT object path (if any), + * and check it for the following pattern: + * + * \Device\NamedPipe\cygwin-[a-fA-F0-9]{16}-pty[0-9]{1,4}-(from-master|to-master|to-master-cyg) + * + * [a-fA-F0-9] is the cygwin installation key, 16 characters long. + * pty[0-9] is the pty name. Its index is of type int, but is safe to be limited to 4 characters. + * + * */ + +static int __cdecl isatty(int fd) { + wchar_t const expect_dev[] = L"\\Device\\NamedPipe\\cygwin-"; + wchar_t const expect_pty[] = L"-pty"; + + typedef NTSTATUS (NTAPI proc_NtQueryObject) (HANDLE, OBJECT_INFORMATION_CLASS, PVOID, ULONG, PULONG); + proc_NtQueryObject *pNtQueryObject; + + HANDLE h_fd; + + /* NtQueryObject needs space for OBJECT_NAME_INFORMATION.Name->Buffer also. */ + char ntfn_bytes[sizeof(OBJECT_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR)]; + + OBJECT_NAME_INFORMATION *ntfn = (OBJECT_NAME_INFORMATION*) ntfn_bytes; + NTSTATUS status; + ULONG ntfn_size = sizeof(ntfn_bytes); + + wchar_t c, *s; + int l; + + h_fd = (HANDLE) _get_osfhandle(fd); + if (!h_fd || h_fd == INVALID_HANDLE_VALUE) { + errno = EBADF; + return 0; + } + + pNtQueryObject = (proc_NtQueryObject*) GetProcAddress(GetModuleHandle("ntdll.dll"), "NtQueryObject"); + if (!pNtQueryObject) { + goto no_tty; + } + + memset(ntfn, 0, ntfn_size); + status = pNtQueryObject((HANDLE)h_fd, ObjectNameInformation, + ntfn, ntfn_size, &ntfn_size); + + if (!NT_SUCCESS(status)) { + /* If it is not NUL (i.e. \Device\Null, which would succeed), + * then normal isatty() could be consulted. + * */ + if (_isatty(fd)) { + return 1; + } + goto no_tty; + } + + s = ntfn->Name.Buffer; + s[ntfn->Name.Length / sizeof(WCHAR)] = 0; + + l = wcslen(expect_dev); + if (wcsncmp(s, expect_dev, l) != 0) { + goto no_tty; + } + s += l; + + l = wcsspn(s, L"0123456789abcdefABCDEF"); + if (l != 16) { + goto no_tty; + } + s += l; + + l = wcslen(expect_pty); + if (wcsncmp(s, expect_pty, l) != 0) { + goto no_tty; + } + s += l; + + l = wcsspn(s, L"0123456789"); + if (l < 1 || l > 4) { + goto no_tty; + } + s += l; + + if (wcscmp(s, L"-from-master") != 0 && + wcscmp(s, L"-to-master") != 0 && + wcscmp(s, L"-to-master-cyg") != 0) + { + goto no_tty; + } + + return 1; + +no_tty: + errno = EINVAL; + return 0; +} + #ifndef _FILE_OFFSET_BITS_SET_LSEEK #define _FILE_OFFSET_BITS_SET_LSEEK #if (defined(_FILE_OFFSET_BITS) && (_FILE_OFFSET_BITS == 64)) -- 2.9.2 ------------------------------------------------------------------------------ _______________________________________________ Mingw-w64-public mailing list Mingw-w64-public@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/mingw-w64-public