diff --git a/ffi/readline.lua b/ffi/readline.lua index 9669b4b..fdcad90 100644 --- a/ffi/readline.lua +++ b/ffi/readline.lua @@ -1,5 +1,5 @@ -- ffi/readline.lua — GNU readline binding. --- Phase 0: readline + add_history + free. Phase 1: custom key bindings. +-- Phase 0: readline + add_history + EOF handling. Phase 1: custom key bindings. -- See docs/PHASE0.md §9. local ffi = require("ffi") @@ -10,6 +10,41 @@ void add_history(const char *line); void free(void *ptr); ]] +-- libreadline-dev (which ships the unversioned `libreadline.so` symlink) is +-- not assumed to be installed on the runtime host; fall back to versioned +-- sonames so a base Debian/Arch with just libreadline runtime works. +local function load_readline() + local errs = {} + for _, name in ipairs({"readline", "readline.so.8", "readline.so.7"}) do + local ok, lib = pcall(ffi.load, name) + if ok then return lib end + errs[#errs+1] = name .. ": " .. tostring(lib) + end + error("libreadline not loadable: " .. table.concat(errs, "; ")) +end + +local rl = load_readline() +local C = ffi.C + local M = {} --- Phase 0 stubs; wired with the REPL implementation. + +-- Read one line of input. +-- Returns: +-- string : the line (no trailing newline) +-- nil : EOF (Ctrl-D on empty line) +function M.readline(prompt) + local cstr = rl.readline(prompt) + if cstr == nil then return nil end + local s = ffi.string(cstr) + C.free(cstr) + return s +end + +-- Append a non-empty line to readline's in-memory history. +function M.add_history(line) + if line and #line > 0 then + rl.add_history(line) + end +end + return M