diff options
Diffstat (limited to 'system/init.lua')
-rw-r--r-- | system/init.lua | 212 |
1 files changed, 210 insertions, 2 deletions
diff --git a/system/init.lua b/system/init.lua index 77e0c3b..c3ea94d 100644 --- a/system/init.lua +++ b/system/init.lua @@ -1,2 +1,210 @@ -local system = require 'system.core' -return system +--- Lua System Library. +-- @module init + +local sys = require 'system.core' +local global_backup -- global backup for terminal settings + + + +local add_gc_method do + -- feature detection; __GC meta-method, not available in all Lua versions + local has_gc = false + local tt = setmetatable({}, { -- luacheck: ignore + __gc = function() has_gc = true end + }) + + -- clear table and run GC to trigger + tt = nil + collectgarbage() + collectgarbage() + + + if has_gc then + -- use default GC mechanism since it is available + function add_gc_method(t, f) + setmetatable(t, { __gc = f }) + end + else + -- create workaround using a proxy userdata, typical for Lua 5.1 + function add_gc_method(t, f) + local proxy = newproxy(true) + getmetatable(proxy).__gc = function() + t["__gc_proxy"] = nil + f(t) + end + t["__gc_proxy"] = proxy + end + end +end + + + +--- Returns a backup of terminal setting for stdin/out/err. +-- Handles terminal/console flags and non-block flags on the streams. +-- Backs up terminal/console flags only if a stream is a tty. +-- @return table with backup of terminal settings +function sys.termbackup() + local backup = {} + + if sys.isatty(io.stdin) then + backup.console_in = sys.getconsoleflags(io.stdin) + backup.term_in = sys.tcgetattr(io.stdin) + end + if sys.isatty(io.stdout) then + backup.console_out = sys.getconsoleflags(io.stdout) + backup.term_out = sys.tcgetattr(io.stdout) + end + if sys.isatty(io.stderr) then + backup.console_err = sys.getconsoleflags(io.stderr) + backup.term_err = sys.tcgetattr(io.stderr) + end + + backup.block_in = sys.getnonblock(io.stdin) + backup.block_out = sys.getnonblock(io.stdout) + backup.block_err = sys.getnonblock(io.stderr) + + return backup +end + + + +--- Restores terminal settings from a backup +-- @tparam table backup the backup of terminal settings, see `termbackup`. +-- @treturn boolean true +function sys.termrestore(backup) + if backup.console_in then sys.setconsoleflags(io.stdin, backup.console_in) end + if backup.term_in then sys.tcsetattr(io.stdin, sys.TCSANOW, backup.term_in) end + if backup.console_out then sys.setconsoleflags(io.stdout, backup.console_out) end + if backup.term_out then sys.tcsetattr(io.stdout, sys.TCSANOW, backup.term_out) end + if backup.console_err then sys.setconsoleflags(io.stderr, backup.console_err) end + if backup.term_err then sys.tcsetattr(io.stderr, sys.TCSANOW, backup.term_err) end + + if backup.block_in ~= nil then sys.setnonblock(io.stdin, backup.block_in) end + if backup.block_out ~= nil then sys.setnonblock(io.stdout, backup.block_out) end + if backup.block_err ~= nil then sys.setnonblock(io.stderr, backup.block_err) end + return true +end + + + +--- Backs up terminal settings and restores them on application exit. +-- Calls `termbackup` to back up terminal settings and sets up a GC method to +-- automatically restore them on application exit (also works on Lua 5.1). +-- @treturn[1] boolean true +-- @treturn[2] nil if the backup was already created +-- @treturn[2] string error message +function sys.autotermrestore() + if global_backup then + return nil, "global terminal backup was already set up" + end + global_backup = sys.termbackup() + add_gc_method(global_backup, function(self) + sys.termrestore(self) end) + return true +end + + + +do + local oldunpack = unpack or table.unpack + local pack = function(...) return { n = select("#", ...), ... } end + local unpack = function(t) return oldunpack(t, 1, t.n) end + + --- Wraps a function to automatically restore terminal settings upon returning. + -- Calls `termbackup` before calling the function and `termrestore` after. + -- @tparam function f function to wrap + -- @treturn function wrapped function + function sys.termwrap(f) + if type(f) ~= "function" then + error("arg #1 to wrap, expected function, got " .. type(f), 2) + end + + return function(...) + local bu = sys.termbackup() + local results = pack(f(...)) + sys.termrestore(bu) + return unpack(results) + end + end +end + + + +--- Debug function for console flags (Windows). +-- Pretty prints the current flags set for the handle. +-- @param fh file handle (`io.stdin`, `io.stdout`, `io.stderr`) +-- @usage -- Print the flags for stdin/out/err +-- system.listconsoleflags(io.stdin) +-- system.listconsoleflags(io.stdout) +-- system.listconsoleflags(io.stderr) +function sys.listconsoleflags(fh) + local flagtype + if fh == io.stdin then + print "------ STDIN FLAGS WINDOWS ------" + flagtype = "CIF_" + elseif fh == io.stdout then + print "------ STDOUT FLAGS WINDOWS ------" + flagtype = "COF_" + elseif fh == io.stderr then + print "------ STDERR FLAGS WINDOWS ------" + flagtype = "COF_" + end + + local flags = assert(sys.getconsoleflags(fh)) + local out = {} + for k,v in pairs(sys) do + if type(k) == "string" and k:sub(1,4) == flagtype then + if flags:has(v) then + out[#out+1] = string.format("%10d [x] %s",v:value(),k) + else + out[#out+1] = string.format("%10d [ ] %s",v:value(),k) + end + end + end + table.sort(out) + for k,v in pairs(out) do + print(v) + end +end + + + +--- Debug function for terminal flags (Posix). +-- Pretty prints the current flags set for the handle. +-- @param fh file handle (`io.stdin`, `io.stdout`, `io.stderr`) +-- @usage -- Print the flags for stdin/out/err +-- system.listconsoleflags(io.stdin) +-- system.listconsoleflags(io.stdout) +-- system.listconsoleflags(io.stderr) +function sys.listtermflags(fh) + if fh == io.stdin then + print "------ STDIN FLAGS POSIX ------" + elseif fh == io.stdout then + print "------ STDOUT FLAGS POSIX ------" + elseif fh == io.stderr then + print "------ STDERR FLAGS POSIX ------" + end + + local flags = assert(sys.tcgetattr(fh)) + for _, flagtype in ipairs { "iflag", "oflag", "lflag" } do + local prefix = flagtype:sub(1,1):upper() .. "_" -- I_, O_, or L_, the constant prefixes + local out = {} + for k,v in pairs(sys) do + if type(k) == "string" and k:sub(1,2) == prefix then + if flags[flagtype]:has(v) then + out[#out+1] = string.format("%10d [x] %s",v:value(),k) + else + out[#out+1] = string.format("%10d [ ] %s",v:value(),k) + end + end + end + table.sort(out) + for k,v in pairs(out) do + print(v) + end + end +end + + + +return sys |