1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
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,
}
|