[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Command Line handling in Lua scripts
- From: Asko Kauppi <askok@...>
- Date: Fri, 6 Jan 2006 23:24:14 +0200
I have brought up this before, but now I have some sample code to
play with.
The issue is (uniform) command line handling in Lua scripts, so users
would be able to more or less trust Lua scripts to work with similar
logic as to their command parameters, and programmers would be able
to get some help on parsing that 'arg' table their main chunk received.
Less words, more code. Comments are welcome!
-asko :)
....clipclip....
---=== COMMAND LINE ===---
--
-- [tbl][,err_str]= Loc_RunArgs( arg_tbl, params_tbl )
--
-- Handle flags in the 'arg' table (1..N), calling functions in 'params'
-- (keyed by their flag names).
--
-- Callback functions are given the name of the flag causing their
call, and
-- any non-flag parameters following them (potential parameters for
the flag).
-- They return the number of additional parameters used (nil=0,1..).
In case of
-- error, the function shall return 'nil', and an error message.
--
-- In other words:
-- [int][,err_str]= function( flag_str [, ...] )
--
-- The whole function returns the table of non-flag parameters (s.a.
file names),
-- 1..N indexed, or 'nil' and error string on command line error.
--
local function Loc_RunArgs( arg, params )
--
local rest= {} -- normal, non-flag parameters
-- [...]= Loc_NonflagParams( arg_tbl, start_int )
--
-- Return params from 'arg[start]' onwards, until first flag.
--
local function Loc_NonflagParams( arg, start, ... )
--
local v= arg[start]
if not v or string.sub(v,1,1)=="-" then
return unpack(arg) -- end of recursion
else
return Loc_NonflagParams( arg, start+1, unpack(arg),
v ) -- tail recursion :)
end
end
local i=1
while arg[i] do
local v= arg[i]
local flag= skip2( string.find( v, "%-+(.+)" ) )
if not flag then
table.insert( rest, v )
else
local vv= params[flag]
if not vv then
return nil, "Unexpected flag: '"..v.."'"
--
elseif type(vv)=="string" then
-- Note: we also change the 'flag' var for aliases,
so the handler
-- will get the same name, no matter which
string was leading
-- to it. This should help differentiation.
flag= vv
vv= params[vv] -- redirect: str->str->func
end
-- errors in 'params' table come from programmer, not
user, so we may
-- expect a bit more decency from it. (and error,
instead of returning)
--
local func= vv
if type(func)~="function" then
error( "Not a proper handler for: '"..v.."'" )
end
-- Now, find out how many non-flag parameters we can
provide the call
--
local n,err= func( flag, Loc_NonflagParams( arg, i+1 ) )
if err then
return nil,err
end
if n then
ASSUME( tonumber(n), "Handler returned bad (non-
numeric) value: '"..flag.."'" )
i= i+n -- skip n
end
end
i= i+1
end
return rest
end
-- [int][,err_str]= function( flag_str [, ...] )
--
-- Returns the number of extra arguments it used (1..N, nil for none)
local recursive
local check_links
local check_xml
local strip_comments
local exclude
local upload_ftp
local upload_cache
local params= {
["r"]= function() recursive= true end,
["cl"]= function() check_links= true end,
["cx"]= function() check_xml= true end,
["sc"]= function() strip_comments= true end,
["x"]= function(_,text)
if not text then
return nil,"no exclusion text" -- syntax error
end
exclude= text
return 1 -- one parameter used
end,
["uf"]= function(_,path)
if not text then
return nil,"no ftp upload path"
end
upload_ftp= path
return 1
end,
["uc"]= function(_,path)
if not text then
return nil,"no upload cache name"
end
upload_cache= path
return 1
end,
["v"]= function()
io.stderr:write( "Lumikki v."..VERSION.."\n" )
end,
["l"]= function(_,fn)
if not fn then
return nil,"no script name"
end
if not string.find( fn, "%.[lL][uU][aA]$" ) then
fn= fn..".lua"
end
if not FileExists(fn) then
return nil,"no such script: '"..fn.."'"
end
-- We could (should?) build up a sandbox here...
--
dofile( fn )
end,
["h"]= function()
io.stderr:write( [[
Lumikki is a static website generation and/or XML preprocessing tool.
Copyright (c) 2005-06 Asko Kauppi.
lumikki [-r | --recursive] [-cl | --check-links] [-cx | --check-xml]
[-sc | --strip-comments] [-x | --exclude text] [-l | --
load myscript[.lua] ] [index.lxml]
lumikki --recursive ..other flags.. [-uf | --upload-ftp
user[:passwd]@ftp-host[:port]/path[/] ] [-uc | --upload-
cache filename]
lumikki [-v | --version]
lumikki [-h | --help]
]] )
end,
}
-- Long name aliases
--
params["recursive"]= assert( params["r"] )
params["check-links"]= assert( params["cl"] )
params["check-xml"]= assert( params["cx"] )
params["strip-comments"]= assert( params["sc"] )
params["exclude"]= assert( params["x"] )
params["upload-ftp"]= assert( params["uf"] )
params["upload-cache"]= assert( params["uc"] )
params["version"]= assert( params["v"] )
params["load"]= assert( params["l"] )
params["help"]= assert( params["h"] )
--
local rest,err= Loc_RunArgs( arg, params )
ASSUME( rest,err )
-- Unimplemented features:
--
if recursive or check_links or check_xml or strip_comments or exclude or
upload_ftp or upload_cache then
error "SOME FEATURE NOT IMPLEMENTED YET. Sorry! :o"
end
-- 'rest' is the filenames that were left after processing params.
local xmls_handled= 0
for i,v in ipairs(rest) do
if string.find( v, "%.[lL][uU][aA]$" ) then
--
if xmls_handled>0 then
error "Give LUA files prior to LXML; so the filters get
loaded."
end
-- Use 'params["l"]' function here, to get uniformity with
"--load" flag
--
params["l"]( "", v )
else
xmls_handled= xmls_handled+1
if xmls_handled>1 then
error "Multiple source files not supported for now (if
they were, LXML->HTML renaming would need to be done, is that okay?)"
end
local str= Loc_Filter( rest[i] )
print(str)
end
end
if xmls_handled==0 then
params["h"]( "" ) -- Help :)
end