Command Line Module

lua-users home
wiki

This module is intended for processing command line parameters of Lua scripts. It doesn't try to be POSIX (or any other standard) compliant, since it is very Lua-oriented and allows to specify parameters of any Lua data type.

The module exports just one function: cmdline.getparam (its detailed description is given in the comment preceding the function body).

See also: GetOpt, AlternativeGetOpt and PosixGetOpt

cmdline.lua

-- started: 2008-04-12 by Shmuel Zeigerman

-- license: public domain



local ipairs,pairs,setfenv,tonumber,loadstring,type =

  ipairs,pairs,setfenv,tonumber,loadstring,type

local tinsert, tconcat = table.insert, table.concat

module(...)



local function commonerror (msg)

  return nil, ("[cmdline]: " .. msg)

end



local function argerror (msg, numarg)

  msg = msg and (": " .. msg) or ""

  return nil, ("[cmdline]: bad argument #" .. numarg .. msg)

end



local function iderror (numarg)

  return argerror("ID not valid", numarg)

end



local function idcheck (id)

  return id:match("^[%a_][%w_]*$") and true

end



--[[------------------------------------------------------------------------

Syntax:

  t_out = getparam(t_in [,options] [,params])



Parameters:

  t_in:   table - list of string arguments to be processed in order

          (usually it is the `arg' table created by the Lua interpreter).



     * if an argument begins with $, the $ is skipped and the rest is inserted

       into the array part of the output table.



     * if an argument begins with -, the rest is a sequence of variables

       (separated by commas or semicolons) that are all set to true;

            example: -var1,var2  --> var1,var2 = true,true



     * if an argument begins with !, the rest is a Lua chunk;

            example: !a=(40+3)*5;b=20;name="John";window={w=600,h=480}



     * if an argument contains =, then it is an assignment in the form

       var1,...=value (no space is allowed around the =)

         * if value begins with $, the $ is skipped, the rest is a string

                 example: var1,var2=$         --> var1,var2 = "",""

                 example: var1,var2=$125      --> var1,var2 = "125","125"

                 example: var1,var2=$$125     --> var1,var2 = "$125","$125"

         * if value is convertible to number, it is a number

                 example: var1,var2=125       --> var1,var2 = 125,125

         * otherwise it is a string

                 example: name=John           --> name = "John"



     * if an argument neither begins with one of the special characters (-,!,$),

       nor contains =, it is inserted as is into the array part of the output

       table.



  options (optional): a list of names of all command-line options and parameters

     permitted in the application; used to check that each found option

     is valid; no checks are done if not supplied.



  params (optional): a list of names of all command-line parameters required

     by the application; used to check that each required parameter is present;

     no checks are done if not supplied.



Returns:

  On success: the output table, e.g. { [1]="./myfile.txt", name="John", age=40 }

  On error: nil followed by error message string.



--]]------------------------------------------------------------------------

function getparam (t_in, options, params)

  local t_out = {}

  for i,v in ipairs(t_in) do

    local prefix, command = v:sub(1,1), v:sub(2)

    if prefix == "$" then

      tinsert(t_out, command)

    elseif prefix == "-" then

      for id in command:gmatch"[^,;]+" do

        if not idcheck(id) then return iderror(i) end

        t_out[id] = true

      end

    elseif prefix == "!" then

      local f, err = loadstring(command)

      if not f then return argerror(err, i) end

      setfenv(f, t_out)()

    elseif v:find("=") then

      local ids, val = v:match("^([^=]+)%=(.*)") -- no space around =

      if not ids then return argerror("invalid assignment syntax", i) end

      val = val:sub(1,1)=="$" and val:sub(2) or tonumber(val) or val

      for id in ids:gmatch"[^,;]+" do

        if not idcheck(id) then return iderror(i) end

        t_out[id] = val

      end

    else

      tinsert(t_out, v)

    end

  end

  if options then

    local lookup, unknown = {}, {}

    for _,v in ipairs(options) do lookup[v] = true end

    for k,_ in pairs(t_out) do

      if lookup[k]==nil and type(k)=="string" then tinsert(unknown, k) end

    end

    if #unknown > 0 then

      return commonerror("unknown options: " .. tconcat(unknown, ", "))

    end

  end

  if params then

    local missing = {}

    for _,v in ipairs(params) do

      if t_out[v]==nil then tinsert(missing, v) end

    end

    if #missing > 0 then

      return commonerror("missing parameters: " .. tconcat(missing, ", "))

    end

  end

  return t_out

end

test_cmdline.lua

require "cmdline"

local getparam = cmdline.getparam

local function assertgood(...) return assert(getparam(...)) end

local function assertbad(...) return assert(not getparam(...)) end



local t_in = {

  "var1,var1b=John",

  "var2,var2b=$Peter",

  "var3,var3b=$005",

  "var4,var4b=005",

  "!var5=8/2;var6=var5*40;var7=\"Ann\"",

  "!var8=nil",

  "!var9={} and 5 and 6",

  "-a,b2,c",

  "abcd",

  "/bin",

  "$-abc"

}



local function test1(t_out)

  assert (t_out.var1 == "John" and t_out.var1b == "John")

  assert (t_out.var2 == "Peter" and t_out.var2b == "Peter")

  assert (t_out.var3 == "005" and t_out.var3b == "005")

  assert (t_out.var4 == 5 and t_out.var4b == 5)

  assert (t_out.var5 == 4)

  assert (t_out.var6 == 160)

  assert (t_out.var7 == "Ann")

  assert (t_out.var8 == nil)

  assert (t_out.var9 == 6)

  assert (t_out.a==true and t_out.b2==true and t_out.c==true)

  assert (t_out[1] == "abcd")

  assert (t_out[2] == "/bin")

  assert (t_out[3] == "-abc")

end



local options = {

  "var1", "var1b", "var2", "var2b", "var3", "var3b", "var4", "var4b", "var5",

  "var6", "var7", "var8", "var9", "a", "b2", "c"

}



local t_out = assertgood(t_in) -- no options checking

test1(t_out)



local t_out = assertgood(t_in, options) -- options checking

test1(t_out)

assertbad(t_in, {}) -- no options permitted



local t_out = assertgood(t_in, nil, {3, "var1"}) -- parameters checking

test1(t_out)

assertbad(t_in, nil, {"var0"}) -- parameter missing



local t_bad = { -- bad syntax tests

  {"!abc"}, {"--88"}, {"-a,8,b"}, {"=8a=1"}

}

for _,v in ipairs(t_bad) do assertbad(v) end



print ("OK: test_cmdline")


RecentChanges · preferences
edit · history
Last edited December 11, 2009 11:40 pm GMT (diff)