local array = require " util.array " ;
local function getchar ( n )
local stty_ret = os.execute ( " stty raw -echo 2>/dev/null " ) ;
local ok , char ;
if stty_ret == true or stty_ret == 0 then
ok , char = pcall ( io.read , n or 1 ) ;
os.execute ( " stty sane " ) ;
else
ok , char = pcall ( io.read , " *l " ) ;
if ok then
char = char : sub ( 1 , n or 1 ) ;
end
end
if ok then
return char ;
end
end
local function getline ( )
local ok , line = pcall ( io.read , " *l " ) ;
if ok then
return line ;
end
end
local function getpass ( )
local stty_ret , _ , status_code = os.execute ( " stty -echo 2>/dev/null " ) ;
if status_code then -- COMPAT w/ Lua 5.1
stty_ret = status_code ;
end
if stty_ret ~= 0 then
io.write ( " \027 [08m " ) ; -- ANSI 'hidden' text attribute
end
local ok , pass = pcall ( io.read , " *l " ) ;
if stty_ret == 0 then
os.execute ( " stty sane " ) ;
else
io.write ( " \027 [00m " ) ;
end
io.write ( " \n " ) ;
if ok then
return pass ;
end
end
local function show_yesno ( prompt )
io.write ( prompt , " " ) ;
local choice = getchar ( ) : lower ( ) ;
io.write ( " \n " ) ;
if not choice : match ( " %a " ) then
choice = prompt : match ( " %[.-(%U).-%]$ " ) ;
if not choice then return nil ; end
end
return ( choice == " y " ) ;
end
local function read_password ( )
local password ;
while true do
io.write ( " Enter new password: " ) ;
password = getpass ( ) ;
if not password then
print ( " No password - cancelled " ) ;
return ;
end
io.write ( " Retype new password: " ) ;
if getpass ( ) ~= password then
if not show_yesno [=[Passwords did not match, try again? [Y/n]]=] then
return ;
end
else
break ;
end
end
return password ;
end
local function show_prompt ( prompt )
io.write ( prompt , " " ) ;
local line = getline ( ) ;
line = line and line : gsub ( " \n $ " , " " ) ;
return ( line and # line > 0 ) and line or nil ;
end
local function printf ( fmt , ... )
print ( fmt : format ( ... ) ) ;
end
local function padright ( s , width )
return s .. string.rep ( " " , width -# s ) ;
end
local function padleft ( s , width )
return string.rep ( " " , width -# s ) .. s ;
end
local function new_table ( col_specs , max_width )
max_width = max_width or tonumber ( os.getenv ( " COLUMNS " ) ) or 80 ;
local separator = " | " ;
local widths = { } ;
local total_width = max_width - # separator * ( # col_specs - 1 ) ;
local free_width = total_width ;
-- Calculate width of fixed-size columns
for i = 1 , # col_specs do
local width = col_specs [ i ] . width or " 0 " ;
if not ( type ( width ) == " string " and width : sub ( - 1 ) == " % " ) then
local title = col_specs [ i ] . title ;
width = math.max ( tonumber ( width ) , title and ( # title + 1 ) or 0 ) ;
widths [ i ] = width ;
free_width = free_width - width ;
if i > 1 then
free_width = free_width - # separator ;
end
end
end
-- Calculate width of %-based columns
for i = 1 , # col_specs do
if not widths [ i ] then
local pc_width = tonumber ( ( col_specs [ i ] . width : gsub ( " %%$ " , " " ) ) ) ;
widths [ i ] = math.floor ( free_width * ( pc_width / 100 ) ) ;
end
end
return function ( row )
local titles ;
if not row then
titles , row = true , array.pluck ( col_specs , " title " , " " ) ;
end
local output = { } ;
for i , column in ipairs ( col_specs ) do
local width = widths [ i ] ;
local v = ( not titles and column.mapper or tostring ) ( row [ not titles and column.key or i ] or " " , row ) ;
if # v < width then
if column.align == " right " then
v = padleft ( v , width ) ;
else
v = padright ( v , width ) ;
end
elseif # v > width then
v = v : sub ( 1 , width - 1 ) .. " … " ;
end
table.insert ( output , v ) ;
end
return table.concat ( output , separator ) ;
end ;
end
return {
getchar = getchar ;
getline = getline ;
getpass = getpass ;
show_yesno = show_yesno ;
read_password = read_password ;
show_prompt = show_prompt ;
printf = printf ;
padleft = padleft ;
padright = padright ;
table = new_table ;
} ;