treat negative numbers as non-options in getopt

develop
myk002 2021-05-04 11:55:21 -07:00
parent 27433b7388
commit fd735d4b42
No known key found for this signature in database
GPG Key ID: 8A39CA0FA0C16E78
4 changed files with 83 additions and 6 deletions

@ -44,6 +44,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences:
## Lua ## Lua
- ``gui.Painter``: fixed error when calling ``viewport()`` method - ``gui.Painter``: fixed error when calling ``viewport()`` method
- `xlsxreader`: Added Lua class wrappers for the xlsxreader plugin API - `xlsxreader`: Added Lua class wrappers for the xlsxreader plugin API
- ``utils.processArgsGetopt()``: now returns negative numbers (e.g. ``-10``) in the list of positional parameters instead of treating it as an option string equivalent to ``-1 -0``.
## Documentation ## Documentation
- Added more client library implementations to the `remote interface docs <remote-client-libs>` - Added more client library implementations to the `remote interface docs <remote-client-libs>`

@ -21,10 +21,10 @@
-- based on https://github.com/LuaDist/alt-getopt/blob/master/alt_getopt.lua -- based on https://github.com/LuaDist/alt-getopt/blob/master/alt_getopt.lua
-- MIT licence -- MIT licence
-- modified to support aggregation of non-options and to call qerror instead of -- modified to support negative numbers as non-options, to aggregate non-options
-- os.exit() on error. can be used directly or via the utils.processArgs2() -- for return, and to call error/qerror instead of os.exit on error. can be used
-- wrapper. -- directly or via the utils.processArgsGetopt() wrapper.
--
-- sh_opts should be in standard getopt format: a string of letters that -- 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. -- 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' -- e.g.: 'ak:hv' has three flags (options with no arguments): 'a', 'h', and 'v'
@ -35,6 +35,13 @@
-- -abcd is equivalent to -a -b -c -d if none of them accept arguments. -- -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 -- -abckVALUE and -abck VALUE are also acceptable (where k is the only option
-- in the string that takes a value). -- in the string that takes a value).
--
-- Note that arguments that have a number as the second character are
-- interpreted as positional parameters and not options. For example, the
-- following strings are never interpreted as options:
-- -10
-- -0
-- -1a
local _ENV = mkmodule('3rdparty.alt_getopt') local _ENV = mkmodule('3rdparty.alt_getopt')
@ -69,7 +76,7 @@ local function canonicalize(options, opt)
end end
if type(options[opt]) ~= 'number' then if type(options[opt]) ~= 'number' then
qerror(string.format( error(string.format(
'Option "%s" resolves to non-number for has_arg flag', opt)) 'Option "%s" resolves to non-number for has_arg flag', opt))
end end
@ -115,7 +122,7 @@ function get_ordered_opts(args, sh_opts, long_opts)
end end
end end
count = count + 1 count = count + 1
elseif a:sub(1, 1) == '-' then elseif a:sub(1, 1) == '-' and not tonumber(a:sub(2, 2)) then
local j local j
for j=2,#a do for j=2,#a do
local opt = canonicalize(options, a:sub(j, j)) local opt = canonicalize(options, a:sub(j, j))

@ -620,6 +620,9 @@ end
-- can be combined into a single option string (e.g. '-abc' is the same as -- 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. -- '-a -b -c' if options 'a' and 'b' do not take arguments.
-- --
-- Numbers cannot be options and negative numbers (e.g. -10) will be interpreted
-- as positional parameters and returned in the nonoptions list.
--
-- optionActions is a vector with elements in the following format: -- optionActions is a vector with elements in the following format:
-- {shortOptionName, longOptionAlias, hasArg=boolean, handler=fn} -- {shortOptionName, longOptionAlias, hasArg=boolean, handler=fn}
-- shortOptionName and handler are required. If the option takes an argument, -- shortOptionName and handler are required. If the option takes an argument,

@ -40,3 +40,69 @@ function test.invert_overwrite()
expect.eq(i.b, 2) expect.eq(i.b, 2)
expect.eq(i.a, 3) expect.eq(i.a, 3)
end end
function test.processArgsGetopt_happy_path()
local quiet, verbose, name
local function process(args, expected_q, expected_v, expected_n)
quiet, verbose, name = false, false, nil
local nonoptions = utils.processArgsGetopt(args, {
{'q', handler=function() quiet = true end},
{'v', 'verbose', handler=function() verbose = true end},
{'n', 'name', hasArg=true,
handler=function(optarg) name = optarg end},
})
expect.eq(expected_q, quiet)
expect.eq(expected_v, verbose)
expect.eq(expected_n, name)
return nonoptions
end
local args = {}
expect.table_eq({}, process(args, false, false, nil))
args = {'-q'}
expect.table_eq({}, process(args, true, false, nil))
args = {'-v'}
expect.table_eq({}, process(args, false, true, nil))
args = {'--verbose'}
expect.table_eq({}, process(args, false, true, nil))
args = {'-n', 'foo'}
expect.table_eq({}, process(args, false, false, 'foo'))
args = {'-n', 'foo'}
expect.table_eq({}, process(args, false, false, 'foo'))
args = {'-nfoo'}
expect.table_eq({}, process(args, false, false, 'foo'))
args = {'--name', 'foo'}
expect.table_eq({}, process(args, false, false, 'foo'))
args = {'--name=foo'}
expect.table_eq({}, process(args, false, false, 'foo'))
args = {'-vqnfoo'}
expect.table_eq({}, process(args, true, true, 'foo'))
args = {'nonopt1', '-nfoo', 'nonopt2', '-1', '-10', '-0v'}
expect.table_eq({'nonopt1', 'nonopt2', '-1', '-10', '-0v'},
process(args, false, false, 'foo'))
end
function test.processArgsGetopt_errors()
expect.error_match('missing option letter',
function() utils.processArgsGetopt({}, {{handler=function() end}}) end)
expect.error_match('missing option letter',
function() utils.processArgsGetopt({}, {{'notoneletter'}}) end)
expect.error_match('missing option letter',
function() utils.processArgsGetopt({}, {{function() end}}) end)
expect.error_match('handler missing',
function() utils.processArgsGetopt({}, {{'r'}}) end)
end