lua-users home
lua-l archive

Command Line handling in Lua scripts

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


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

AltStyle によって変換されたページ (->オリジナル) /