Merge remote-tracking branch 'myk002/myk_getopt' into develop

develop
lethosor 2021-01-28 23:58:45 -05:00
commit d4aef78b26
No known key found for this signature in database
GPG Key ID: 76A269552F4F58C1
4 changed files with 229 additions and 1 deletions

@ -34,6 +34,7 @@ tinythread_ Zlib \(c\) 2010, Marcus Geelnard
tinyxml_ Zlib \(c\) 2000-2006, Lee Thomason
UTF-8-decoder_ MIT \(c\) 2008-2010, Bjoern Hoehrmann
xlsxio_ MIT \(c\) 2016-2020, Brecht Sanders
alt-getopt_ MIT \(c\) 2009 Aleksey Cheusov
=============== ============= =================================================
.. _DFHack: https://github.com/DFHack/dfhack
@ -52,6 +53,7 @@ xlsxio_ MIT \(c\) 2016-2020, Brecht Sanders
.. _tinyxml: http://www.sourceforge.net/projects/tinyxml
.. _UTF-8-decoder: http://bjoern.hoehrmann.de/utf-8/decoder/dfa
.. _xlsxio: https://github.com/brechtsanders/xlsxio
.. _alt-getopt: https://github.com/LuaDist/alt-getopt
.. _CC-BY-SA: http://creativecommons.org/licenses/by/3.0/deed.en_US

@ -44,6 +44,9 @@ changelog.txt uses a syntax similar to RST, with a few special sequences:
## Misc Improvements
- `quickfort`: Dreamfort blueprint set improvements: add a streamlined checklist for all required dreamfort commands and give names to stockpiles, levers, bridges, and zones
## Lua
- ``processArgs2()`` added to utils.lua, providing a callback interface for parameter parsing and getopt-like flexibility for parameter ordering and combination (see docs in ``library/lua/utils.lua`` and ``library/lua/3rdparty/alt_getopt.lua`` for details).
# 0.47.04-r4
## Fixes
@ -1176,4 +1179,3 @@ changelog.txt uses a syntax similar to RST, with a few special sequences:
- The ``ui_menu_width`` global is now a 2-byte array; the second item is the former ``ui_area_map_width`` global, which is now removed
- The former ``announcements`` global is now a field in ``d_init``
- ``world`` fields formerly beginning with ``job_`` are now fields of ``world.jobs``, e.g. ``world.job_list`` is now ``world.jobs.list``

@ -0,0 +1,169 @@
-- Copyright (c) 2009 Aleksey Cheusov <vle@gmx.net>
--
-- Permission is hereby granted, free of charge, to any person obtaining
-- a copy of this software and associated documentation files (the
-- "Software"), to deal in the Software without restriction, including
-- without limitation the rights to use, copy, modify, merge, publish,
-- distribute, sublicense, and/or sell copies of the Software, and to
-- permit persons to whom the Software is furnished to do so, subject to
-- the following conditions:
--
-- The above copyright notice and this permission notice shall be
-- included in all copies or substantial portions of the Software.
--
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-- based on https://github.com/LuaDist/alt-getopt/blob/master/alt_getopt.lua
-- MIT licence
-- modified to support aggregation of non-options and to call qerror instead of
-- os.exit() on error. can be used directly or via the utils.processArgs2()
-- wrapper.
-- sh_opts should be in standard getopt format: a string of letters that
-- represent options, each followed by a colon if that option takes an argument.
-- e.g.: 'ak:hv' has three flags (options with no arguments): 'a', 'h', and 'v'
-- and one option that takes an argument: 'k'.
--
-- Options passed to the module to parse can be in any of the following formats:
-- -kVALUE, -k VALUE, --key=VALUE, --key VALUE
-- -abcd is equivalent to -a -b -c -d if none of them accept arguments.
-- -abckVALUE and -abck VALUE are also acceptable (where k is the only option
-- in the string that takes a value).
local _ENV = mkmodule('3rdparty.alt_getopt')
local function get_opt_map(opts)
local i = 1
local len = #opts
local options = {}
for short_opt, accept_arg in opts:gmatch('(%w)(:?)') do
options[short_opt] = #accept_arg
end
return options
end
local function err_unknown_opt(opt)
qerror(string.format('Unknown option "-%s%s"', #opt > 1 and '-' or '', opt))
end
-- resolve aliases into their canonical forms
local function canonicalize(options, opt)
if not options[opt] then
err_unknown_opt(opt)
end
while type(options[opt]) == 'string' do
opt = options[opt]
if not options[opt] then
err_unknown_opt(opt)
end
end
if type(options[opt]) ~= 'number' then
qerror(string.format(
'Option "%s" resolves to non-number for has_arg flag', opt))
end
return opt
end
local function has_arg(options, opt)
return options[canonicalize(options, opt)] == 1
end
-- returns vectors for opts, optargs, and nonoptions
function get_ordered_opts(args, sh_opts, long_opts)
local optind, count, opts, optargs, nonoptions = 1, 1, {}, {}, {}
local options = get_opt_map(sh_opts)
for k,v in pairs(long_opts) do
options[k] = v
end
while optind <= #args do
local a = args[optind]
if a == '--' then
optind = optind + 1
elseif a:sub(1, 2) == '--' then
local pos = a:find('=', 1, true)
if pos then
local opt = a:sub(3, pos-1)
if not has_arg(options, opt) then
qerror(string.format('Bad usage of option "%s"', a))
end
opts[count] = opt
optargs[count] = a:sub(pos+1)
else
local opt = a:sub(3)
opts[count] = opt
if has_arg(options, opt) then
if i == #args then
qerror(string.format(
'Missing value for option "%s"', a))
end
optargs[count] = args[optind+1]
optind = optind + 1
end
end
count = count + 1
elseif a:sub(1, 1) == '-' then
local j
for j=2,#a do
local opt = canonicalize(options, a:sub(j, j))
if not has_arg(options, opt) then
opts[count] = opt
count = count + 1
elseif j == #a then
if optind == #args then
qerror(string.format(
'Missing value for option "-%s"', opt))
end
opts[count] = opt
optargs[count] = args[optind+1]
optind = optind + 1
count = count + 1
else
opts[count] = opt
optargs[count] = a:sub(j+1)
count = count + 1
break
end
end
else
table.insert(nonoptions, args[optind])
end
optind = optind + 1
end
for i=optind,#args do
table.insert(nonoptions, args[i])
end
return opts, optargs, nonoptions
end
-- returns a map of options to their optargs (or 1 if the option doesn't take an
-- argument), and a vector for nonoptions
function get_opts(args, sh_opts, long_opts)
local ret = {}
local opts,optargs,nonoptions = get_ordered_opts(args, sh_opts, long_opts)
for i,v in ipairs(opts) do
if optarg[i] then
ret[v] = optarg[i]
else
ret[v] = 1
end
end
return ret, nonoptions
end
return _ENV

@ -1,6 +1,7 @@
local _ENV = mkmodule('utils')
local df = df
local getopt = require('3rdparty.alt_getopt')
-- Comparator function
function compare(a,b)
@ -613,6 +614,60 @@ function processArgs(args, validArgs)
return result
end
-- processes commandline options according to optionActions and returns all
-- argument strings that are not options. Options and non-option strings can
-- appear in any order, and single-letter options that do not take arguments
-- can be combined into a single option string (e.g. '-abc' is the same as
-- '-a -b -c' if options 'a' and 'b' do not take arguments.
--
-- optionActions is a vector with elements in the following format:
-- {shortOptionName, longOptionAlias, hasArg=boolean, handler=fn}
-- shortOptionName and handler are required. If the option takes an argument,
-- it will be passed to the handler function.
-- longOptionAlias is optional.
-- hasArgument defaults to false.
--
-- example usage:
--
-- local filename = nil
-- local open_readonly = false
-- local nonoptions = processArgsGetopt(args, {
-- {'r', handler=function() open_readonly = true end},
-- {'f', 'filename', hasArg=true,
-- handler=function(optarg) filename = optarg end}
-- })
--
-- when args is {'first', '-f', 'fname', 'second'} or, equivalently,
-- {'first', '--filename', 'fname', 'second'} (note the double dash in front of
-- the long option alias), then filename will be fname and nonoptions will
-- contain {'first', 'second'}.
function processArgsGetopt(args, optionActions)
local sh_opts, long_opts = '', {}
local handlers = {}
for _,optionAction in ipairs(optionActions) do
local sh_opt,long_opt = optionAction[1], optionAction[2]
if not sh_opt or type(sh_opt) ~= 'string' or #sh_opt ~= 1 then
qerror('optionAction missing option letter at index 1')
end
if not optionAction.handler then
qerror(string.format('handler missing for option "%s"', sh_opt))
end
sh_opts = sh_opts .. sh_opt
if optionAction.hasArg then sh_opts = sh_opts .. ':' end
handlers[sh_opt] = optionAction.handler
if long_opt then
long_opts[long_opt] = sh_opt
handlers[long_opt] = optionAction.handler
end
end
local opts, optargs, nonoptions =
getopt.get_ordered_opts(args, sh_opts, long_opts)
for i,v in ipairs(opts) do
handlers[v](optargs[i])
end
return nonoptions
end
function fillTable(table1,table2)
for k,v in pairs(table2) do
table1[k] = v