-- public domain 20080404 lua@ztact.com require 'pozix' require 'ztact' local _G = _G local assert = assert local debug = debug local error = error local getfenv = getfenv local ipairs = ipairs local pairs = pairs local pozix = pozix local print = print local select = select local setenv = pozix.setenv local setfenv_ = setfenv local setmetatable = setmetatable local string = string local table = table local type = type local unpack = unpack local values = ztact.values module ((...) or 'luashell') ------------------------------- module luashell local expand_, expand_sub -- mutually recursive local functions function expand_sub (level, name) ------------------------- local expand_sub --[[ for i=1,9 do local info = debug.getinfo (i, 'n') print ('expand_sub', i, info and info.name) end local info = debug.getinfo (level, 'n') print ('expand_sub', level, info and info.name) --]] local i = 1 repeat local k,v = debug.getlocal (level, i) -- print ('getlocal', k, v) -- warning: Should be redone without table.concat! 20080401 if k == name then return table.concat ({expand_ (level+1, v)}, ' ') end i = i + 1 until not k if (string.match (name, '^[%w_]')) then return pozix.getenv (name) or '' end return name end function expand_ (level, ...) -------------------------------- local expand_ -- print ('expand_', level, s) if level > 100 then error ('expand: too many levels') end local argv = {} local function sub (name) return expand_sub (level+3, name) end for arg in values {...} do if type (arg) ~= 'string' then error ('expand: expected string') end -- variable substitution local gsub = (string.gsub (arg, '$(.[%w_]*)', sub)) -- split on whitespace for arg in string.gmatch (gsub, '%S+') do table.insert (argv, arg) end end -- print_r (argv) return unpack (argv) end local function execute_check (command, ...) ------------ local execute_check local argv = select ('#', ...) > 1 and { command , ... } or { command, expand_ (4, ...) } local childpid, status = execute_argv (argv) if status ~= 0 then error (table.concat ({...}, ' '), 3) end end local function wrap (command) ----------------------------------- local wrap return function (...) execute_check (command, ...) end end -- luashell ---------------------------------------------------------- luashell local rules = {} function cd (...) ------------------------------------------------------- cd local dir = expand_ (3, ...) print ('cd '..dir) local rv, err = pozix.chdir (dir) if not rv then error ('cd: '..err..': '..dir, 2) end end function echo (...) --------------------------------------------------- echo print (table.concat ( {expand_ (3, ...)} , ' ')) end function execute (...) --------------------------------------------- execute return execute_argv {expand_ (3, ...)} end function execute_argv (argv) ---------------------------------- execute_argv local fork = assert (pozix.fork ()) if fork == 0 then -- print_r (argv) print (table.concat (argv, ' ')) assert (pozix.execv (which (argv[1]), argv)) end local childpid, status = pozix.wait () -- print ('wait', childpid, status) return childpid, status end function expand (...) ----------------------------------------------- expand return expand_ (3, ...) end function print_r (mixed) ------------------------------------------- print_r if type (mixed) == 'string' then print (mixed) end if type (mixed) == 'table' then for k,v in pairs (mixed) do print (k, v) end end end function setfenv () ------------------------------------------------ setfenv local level = 2 local _G = getfenv (level) local cache = { cd = cd, echo = echo, execute = execute } local function __index (t, k) if _G[k] ~= nil then return _G[k] end if cache[k] then return cache[k] end if which (k) then cache[k] = wrap (k) return cache[k] end end local mt = { __index = __index } setfenv_ (level, setmetatable ({}, mt)) end function rule (...) --------------------------------------------------- rule end function which (filename) -------------------------------------------- which -- print ('which', filename) -- warning: Assumes $PATH will not contain escaped colons. 20080401 local paths = {} for path in string.gmatch (pozix.getenv ('PATH'), '[^:]+') do table.insert (paths, path) end for i,path in ipairs (paths) do path = path..'/'..filename if pozix.access (path, pozix.X_OK) then return path end end if string.find (filename, '_') then return which (string.gsub (filename, '_', '-')) end end