1-- Lua command line option parser.
2-- Interface based on Pythons optparse.
3-- http://docs.python.org/lib/module-optparse.html
4-- (c) 2008 David Manura, Licensed under the same terms as Lua (MIT license)
5--
6-- To be used like this:
7-- t={usage="<some usage message>", version="<version string>"}
8-- op=OptionParser(t)
9-- op=add_option{"<opt>", action=<action>, dest=<dest>, help="<help message for this option>"}
10--
11-- with :
12--   <opt> the option string to be used (can be anything, if one letter opt, then should be -x val, more letters: -xy=val )
13--   <action> one of
14--   - store: store in options as key, val
15--   - store_true: stores key, true
16--   - store_false: stores key, false
17--   <dest> is the key under which the option is saved
18--
19-- options,args = op.parse_args()
20--
21-- now options is the table of options (key, val) and args is the table with non-option arguments.
22-- You can use op.fail(message) for failing and op.print_help() for printing the usage as you like.
23
24function OptionParser(t)
25  local usage = t.usage
26  local version = t.version
27
28  local o = {}
29  local option_descriptions = {}
30  local option_of = {}
31
32  function o.fail(s) -- extension
33    io.stderr:write(s .. '\n')
34    os.exit(1)
35  end
36
37  function o.add_option(optdesc)
38    option_descriptions[#option_descriptions+1] = optdesc
39    for _,v in ipairs(optdesc) do
40      option_of[v] = optdesc
41    end
42  end
43  function o.parse_args()
44    -- expand options (e.g. "--input=file" -> "--input", "file")
45    local arg = {table.unpack(arg)}
46    for i=#arg,1,-1 do local v = arg[i]
47      local flag, val = v:match('^(%-%-%w+)=(.*)')
48      if flag then
49        arg[i] = flag
50        table.insert(arg, i+1, val)
51      end
52    end
53
54    local options = {}
55    local args = {}
56    local i = 1
57    while i <= #arg do local v = arg[i]
58      local optdesc = option_of[v]
59      if optdesc then
60        local action = optdesc.action
61        local val
62        if action == 'store' or action == nil then
63          i = i + 1
64          val = arg[i]
65          if not val then o.fail('option requires an argument ' .. v) end
66        elseif action == 'store_true' then
67          val = true
68        elseif action == 'store_false' then
69          val = false
70        end
71        options[optdesc.dest] = val
72      else
73        if v:match('^%-') then o.fail('invalid option ' .. v) end
74        args[#args+1] = v
75      end
76      i = i + 1
77    end
78    if options.help then
79      o.print_help()
80      os.exit()
81    end
82    if options.version then
83      io.stdout:write(t.version .. "\n")
84      os.exit()
85    end
86    return options, args
87  end
88
89  local function flags_str(optdesc)
90    local sflags = {}
91    local action = optdesc.action
92    for _,flag in ipairs(optdesc) do
93      local sflagend
94      if action == nil or action == 'store' then
95        local metavar = optdesc.metavar or optdesc.dest:upper()
96        sflagend = #flag == 2 and ' ' .. metavar
97                              or  '=' .. metavar
98      else
99        sflagend = ''
100      end
101      sflags[#sflags+1] = flag .. sflagend
102    end
103    return table.concat(sflags, ', ')
104  end
105
106  function o.print_help()
107    io.stdout:write("Usage: " .. usage:gsub('%%prog', arg[0]) .. "\n")
108    io.stdout:write("\n")
109    io.stdout:write("Options:\n")
110    for _,optdesc in ipairs(option_descriptions) do
111      io.stdout:write("  " .. flags_str(optdesc) ..
112                      "  " .. optdesc.help .. "\n")
113    end
114  end
115  o.add_option{"--help", action="store_true", dest="help",
116               help="show this help message and exit"}
117  if t.version then
118    o.add_option{"--version", action="store_true", dest="version",
119                 help="output version info."}
120  end
121  return o
122end
123
124