-- ffi/readline.lua — GNU readline binding. -- Phase 0: readline + add_history + EOF handling. Phase 1: custom key bindings. -- See docs/PHASE0.md §9. local ffi = require("ffi") ffi.cdef[[ char *readline(const char *prompt); 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 = {} -- 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