#include #include #include #include #include #include #ifndef __MINGW64_VERSION_MAJOR /* MS winternl.h defines FILE_INFORMATION_CLASS, but with only a different single member. */ enum FILE_INFORMATION_CLASSX { FileNameInformation = 9 }; typedef struct _FILE_NAME_INFORMATION { ULONG FileNameLength; WCHAR FileName[1]; } FILE_NAME_INFORMATION, *PFILE_NAME_INFORMATION; NTSTATUS (NTAPI *pNtQueryInformationFile) (HANDLE, PIO_STATUS_BLOCK, PVOID, ULONG, FILE_INFORMATION_CLASSX); #else NTSTATUS (NTAPI *pNtQueryInformationFile) (HANDLE, PIO_STATUS_BLOCK, PVOID, ULONG, FILE_INFORMATION_CLASS); #endif int isatty (int fd) { HANDLE fh; NTSTATUS status; IO_STATUS_BLOCK io; long buf[66]; /* NAME_MAX + 1 + sizeof ULONG */ PFILE_NAME_INFORMATION pfni = (PFILE_NAME_INFORMATION) buf; PWCHAR cp; /* First check using _isatty. Note that this returns the wrong result for NUL, for instance! Workaround is not to use _isatty at all, but rather GetFileType plus object name checking. */ if (_isatty (fd)) return 1; /* Now fetch the underlying HANDLE. */ fh = (HANDLE) _get_osfhandle (fd); if (!fh || fh == INVALID_HANDLE_VALUE) { errno = EBADF; return 0; } /* Must be a pipe. */ if (GetFileType (fh) != FILE_TYPE_PIPE) goto no_tty; /* Calling the native NT function NtQueryInformationFile is required to support pre-Vista systems. If that's of no concern, Vista introduced the GetFileInformationByHandleEx call with the FileNameInfo info class, which can be used instead. */ if (!pNtQueryInformationFile) { pNtQueryInformationFile = (NTSTATUS (NTAPI *)(HANDLE, PIO_STATUS_BLOCK, PVOID, ULONG, FILE_INFORMATION_CLASS)) GetProcAddress (GetModuleHandle ("ntdll.dll"), "NtQueryInformationFile"); if (!pNtQueryInformationFile) goto no_tty; } if (!NT_SUCCESS (pNtQueryInformationFile (fh, &io, pfni, sizeof buf, FileNameInformation))) goto no_tty; /* The filename is not guaranteed to be NUL-terminated. */ pfni->FileName[pfni->FileNameLength / sizeof (WCHAR)] = L'\0'; /* Now check the name pattern. The filename of a Cygwin pseudo tty pipe looks like this: \cygwin-%16llx-pty%d-{to,from}-master %16llx is the hash of the Cygwin installation, (to support multiple parallel installations), %d is the pseudo tty number, "to" or "from" differs the pipe direction. "from" is a stdin, "to" a stdout-like pipe. */ cp = pfni->FileName; if (!wcsncmp (cp, L"\\cygwin-", 8) && !wcsncmp (cp + 24, L"-pty", 4)) { cp = wcschr (cp + 28, '-'); if (!cp) goto no_tty; if (!wcscmp (cp, L"-from-master") || !wcscmp (cp, L"-to-master")) return 1; } no_tty: errno = EINVAL; return 0; } int main () { if (isatty(0)) printf("tty\n"); else printf("not a tty\n"); return 0; }