--[[
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 .
]]
if os.type == "unix" then
-- Try LuaJIT-like FFI
local succ, M = pcall(function()
local ffi = require "ffi"
assert(ffi.os ~= "" and ffi.arch ~= "", "ffi library is stub")
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
-- Fallback using system command
return {
isatty = function(file)
local fd
if file == io.stdin then
fd = 0
elseif file == io.stdout then
fd = 1
elseif file == io.stderr then
fd = 2
else
return false
end
local result = os.execute(string.format("test -t %d", fd))
return result == true or result == 0
end,
}
else
-- Try LuaJIT
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)--pty-(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,
}