diff --git a/spec/04-term_spec.lua b/spec/04-term_spec.lua index ba58fde..eb975b5 100644 --- a/spec/04-term_spec.lua +++ b/spec/04-term_spec.lua @@ -392,13 +392,45 @@ describe("Terminal:", function() - pending("termbackup()", function() + describe("termbackup() & termrestore()", function() - end) + -- this is all Lua code, so testing one platform should be good enough + win_it("creates and restores a backup", function() + local backup = system.termbackup() + + local old_cp = assert(system.getconsoleoutputcp()) + finally(function() + system.setconsoleoutputcp(old_cp) -- ensure we restore the original one + end) + + -- get the console page... + local new_cp + if old_cp ~= 65001 then + new_cp = 65001 -- set to UTF8 + else + new_cp = 850 -- another common one + end + -- change the console page... + local success, err = system.setconsoleoutputcp(new_cp) + assert.is_nil(err) + assert.is_true(success) + -- ... and check it + local updated_cp = assert(system.getconsoleoutputcp()) + assert.equals(new_cp, updated_cp) + + -- restore the console page + system.termrestore(backup) + local restored_cp = assert(system.getconsoleoutputcp()) + assert.equals(old_cp, restored_cp) + end) - pending("termrestore()", function() + it("termrestore() fails on bad input", function() + assert.has.error(function() + system.termrestore("invalid") + end, "arg #1 to termrestore, expected backup table, got string") + end) end) diff --git a/system/init.lua b/system/init.lua index c232cd2..b9a4f6f 100644 --- a/system/init.lua +++ b/system/init.lua @@ -4,61 +4,67 @@ local sys = require 'system.core' +do + local backup_mt = {} + + --- Returns a backup of terminal setting for stdin/out/err. + -- Handles terminal/console flags, Windows codepage, 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 = setmetatable({}, backup_mt) + + 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 ---- Returns a backup of terminal setting for stdin/out/err. --- Handles terminal/console flags, Windows codepage, 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) - backup.block_in = sys.getnonblock(io.stdin) - backup.block_out = sys.getnonblock(io.stdout) - backup.block_err = sys.getnonblock(io.stderr) + backup.consoleoutcodepage = sys.getconsoleoutputcp() + backup.consolecp = sys.getconsolecp() - backup.consoleoutcodepage = sys.getconsoleoutputcp() - backup.consolecp = sys.getconsolecp() + return backup + end - 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 getmetatable(backup) ~= backup_mt then + error("arg #1 to termrestore, expected backup table, got " .. type(backup), 2) + 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.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 + 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 - if backup.consoleoutcodepage then sys.setconsoleoutputcp(backup.consoleoutcodepage) end - if backup.consolecp then sys.setconsolecp(backup.consolecp) end - return true + if backup.consoleoutcodepage then sys.setconsoleoutputcp(backup.consoleoutcodepage) end + if backup.consolecp then sys.setconsolecp(backup.consolecp) end + return true + end end - do -- autotermrestore local global_backup -- global backup for terminal settings