diff options
Diffstat (limited to 'support/cluttex/src/texrunner/isatty.lua')
-rw-r--r-- | support/cluttex/src/texrunner/isatty.lua | 206 |
1 files changed, 206 insertions, 0 deletions
diff --git a/support/cluttex/src/texrunner/isatty.lua b/support/cluttex/src/texrunner/isatty.lua new file mode 100644 index 0000000000..000e2510b7 --- /dev/null +++ b/support/cluttex/src/texrunner/isatty.lua @@ -0,0 +1,206 @@ +--[[ + Copyright 2018 ARATA Mizuki + + This file is part of ClutTeX. + + ClutTeX is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + ClutTeX is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with ClutTeX. If not, see <http://www.gnu.org/licenses/>. +]] + +if os.type == "unix" then + -- Try LuaJIT-like FFI + local succ, M = pcall(function() + local ffi = require "ffi" + ffi.cdef[[ +int isatty(int fd); +int fileno(void *stream); +]] + local isatty = assert(ffi.C.isatty, "isatty not found") + local fileno = assert(ffi.C.fileno, "fileno not found") + return { + isatty = function(file) + -- LuaJIT converts Lua's file handles into FILE* (void*) + return isatty(fileno(file)) ~= 0 + end + } + end) + if succ then + if CLUTTEX_VERBOSITY >= 3 then + io.stderr:write("ClutTeX: isatty found via FFI (Unix)\n") + end + return M + else + if CLUTTEX_VERBOSITY >= 3 then + io.stderr:write("ClutTeX: FFI (Unix) not found: ", M, "\n") + end + end + + -- Try luaposix + local succ, M = pcall(function() + local isatty = require "posix.unistd".isatty + local fileno = require "posix.stdio".fileno + return { + isatty = function(file) + return isatty(fileno(file)) == 1 + end, + } + end) + if succ then + if CLUTTEX_VERBOSITY >= 3 then + io.stderr:write("ClutTeX: isatty found via luaposix\n") + end + return M + else + if CLUTTEX_VERBOSITY >= 3 then + io.stderr:write("ClutTeX: luaposix not found: ", M, "\n") + end + end + +else + -- Try LuaJIT + -- TODO: Try to detect MinTTY using GetFileInformationByHandleEx + local succ, M = pcall(function() + local ffi = require "ffi" + local bitlib = assert(bit32 or bit, "Neither bit32 (Lua 5.2) nor bit (LuaJIT) found") -- Lua 5.2 or LuaJIT + ffi.cdef[[ +int _isatty(int fd); +int _fileno(void *stream); +void *_get_osfhandle(int fd); // should return intptr_t +typedef int BOOL; +typedef uint32_t DWORD; +typedef int FILE_INFO_BY_HANDLE_CLASS; // ??? +typedef struct _FILE_NAME_INFO { +DWORD FileNameLength; +uint16_t FileName[?]; +} FILE_NAME_INFO; +DWORD GetFileType(void *hFile); +BOOL GetFileInformationByHandleEx(void *hFile, FILE_INFO_BY_HANDLE_CLASS fic, void *fileinfo, DWORD dwBufferSize); +BOOL GetConsoleMode(void *hConsoleHandle, DWORD* lpMode); +BOOL SetConsoleMode(void *hConsoleHandle, DWORD dwMode); +DWORD GetLastError(); +]] + local isatty = assert(ffi.C._isatty, "_isatty not found") + local fileno = assert(ffi.C._fileno, "_fileno not found") + local get_osfhandle = assert(ffi.C._get_osfhandle, "_get_osfhandle not found") + local GetFileType = assert(ffi.C.GetFileType, "GetFileType not found") + local GetFileInformationByHandleEx = assert(ffi.C.GetFileInformationByHandleEx, "GetFileInformationByHandleEx not found") + local GetConsoleMode = assert(ffi.C.GetConsoleMode, "GetConsoleMode not found") + local SetConsoleMode = assert(ffi.C.SetConsoleMode, "SetConsoleMode not found") + local GetLastError = assert(ffi.C.GetLastError, "GetLastError not found") + local function wide_to_narrow(array, length) + local t = {} + for i = 0, length - 1 do + table.insert(t, string.char(math.min(array[i], 0xff))) + end + return table.concat(t, "") + end + local function is_mintty(fd) + local handle = get_osfhandle(fd) + local filetype = GetFileType(handle) + if filetype ~= 0x0003 then -- not FILE_TYPE_PIPE (0x0003) + -- mintty must be a pipe + if CLUTTEX_VERBOSITY >= 4 then + io.stderr:write("ClutTeX: is_mintty: not a pipe\n") + end + return false + end + local nameinfo = ffi.new("FILE_NAME_INFO", 32768) + local FileNameInfo = 2 -- : FILE_INFO_BY_HANDLE_CLASS + if GetFileInformationByHandleEx(handle, FileNameInfo, nameinfo, ffi.sizeof("FILE_NAME_INFO", 32768)) ~= 0 then + local filename = wide_to_narrow(nameinfo.FileName, math.floor(nameinfo.FileNameLength / 2)) + -- \(cygwin|msys)-<hex digits>-pty<N>-(from|to)-master + if CLUTTEX_VERBOSITY >= 4 then + io.stderr:write("ClutTeX: is_mintty: GetFileInformationByHandleEx returned ", filename, "\n") + end + local a, b = string.match(filename, "^\\(%w+)%-%x+%-pty%d+%-(%w+)%-master$") + return (a == "cygwin" or a == "msys") and (b == "from" or b == "to") + else + if CLUTTEX_VERBOSITY >= 4 then + io.stderr:write("ClutTeX: is_mintty: GetFileInformationByHandleEx failed\n") + end + return false + end + end + return { + isatty = function(file) + -- LuaJIT converts Lua's file handles into FILE* (void*) + local fd = fileno(file) + return isatty(fd) ~= 0 or is_mintty(fd) + end, + enable_virtual_terminal = function(file) + local fd = fileno(file) + if is_mintty(fd) then + -- MinTTY + if CLUTTEX_VERBOSITY >= 4 then + io.stderr:write("ClutTeX: Detected MinTTY\n") + end + return true + elseif isatty(fd) ~= 0 then + -- Check for ConEmu or ansicon + if os.getenv("ConEmuANSI") == "ON" or os.getenv("ANSICON") then + if CLUTTEX_VERBOSITY >= 4 then + io.stderr:write("ClutTeX: Detected ConEmu or ansicon\n") + end + return true + else + -- Try native VT support on recent Windows + local handle = get_osfhandle(fd) + local modePtr = ffi.new("DWORD[1]") + local result = GetConsoleMode(handle, modePtr) + if result == 0 then + if CLUTTEX_VERBOSITY >= 3 then + local err = GetLastError() + io.stderr:write(string.format("ClutTeX: GetConsoleMode failed (0x%08X)\n", err)) + end + return false + end + local ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004 + result = SetConsoleMode(handle, bitlib.bor(modePtr[0], ENABLE_VIRTUAL_TERMINAL_PROCESSING)) + if result == 0 then + -- SetConsoleMode failed: Command Prompt on older Windows + if CLUTTEX_VERBOSITY >= 3 then + local err = GetLastError() + -- Typical error code: ERROR_INVALID_PARAMETER (0x57) + io.stderr:write(string.format("ClutTeX: SetConsoleMode failed (0x%08X)\n", err)) + end + return false + end + if CLUTTEX_VERBOSITY >= 4 then + io.stderr:write("ClutTeX: Detected recent Command Prompt\n") + end + return true + end + else + -- Not a TTY + return false + end + end, + } + end) + if succ then + if CLUTTEX_VERBOSITY >= 3 then + io.stderr:write("ClutTeX: isatty found via FFI (Windows)\n") + end + return M + else + if CLUTTEX_VERBOSITY >= 3 then + io.stderr:write("ClutTeX: FFI (Windows) not found: ", M, "\n") + end + end +end + +return { + isatty = function(file) + return false + end, +} |