[!] VersionNotice: The below code pertains to an older Lua version, Lua 4. It does not run as is under Lua 5. There is a Lua 5 version of getopt in [stdlib]. Here's an implementation of GetOpt in Lua. It's quite flexible. It needs ReubensUtilLib. function getOpt(argIn, options, optsFirst, inOrder) local noProcess local argOut, optOut, errors = {[0] = argIn[0]}, {}, {} function getArg(optTy, opt, arg) if optTy.arg == "None" then if arg then tinsert(%errors, errNoArg(opt)) end else if not arg and %argIn[1] and strsub(%argIn[1], 1, 1) ~= "-" then arg = %argIn[1] tremove(%argIn, 1) end if not arg and optTy.arg == "Req" then tinsert(%errors, errReqArg(opt, optTy.var)) return nil end end if optTy.func then return optTy.func(arg) end return arg or 1 end function addOpt(key, val) if not %inOrder then if %optOut[key] then tinsert(%optOut[key], val) else %optOut[key] = {val} end else tinsert(%argOut, {[key] = val}) end end function shortOpt(opt, arg) if %options.short[opt] then local o = %options.short[opt] %addOpt(o.long[1], %getArg(o.type, opt, arg)) else tinsert(%errors, errUnrec(opt)) end end function longOpt(opt, arg) local o = findLongOpt(%options, opt) if o.type then %addOpt(o.long[1], %getArg(o.type, opt, arg)) elseif getn(o) > 0 then tinsert(%errors, errAmbig(opt, o)) else tinsert(%errors, errUnrec(opt)) end end while argIn[1] do local v = argIn[1] tremove(argIn, 1) local _, _, dash, opt = strfind(v, "^(%-%-?)([^=-][^=]*)") local _, _, arg = strfind(v, "=(.*)$") if v == "--" then noProcess = 1 elseif not dash or noProcess then tinsert(argOut, v) if optsFirst then noProcess = 1 end elseif dash == "-" and options.short[strsub(opt, 1, 1)] then for i = 1, strlen(opt) - 1 do shortOpt(strsub(opt, i, i)) end shortOpt(strsub(opt, -1), arg) elseif dash then longOpt(opt, arg) end end argOut.n = getn(argOut) if inOrder then return argOut, nil, errors end return argOut, optOut, errors end Option = constructor{ "short", "long", "type", "desc", } OptType = constructor{ "arg", "var", "func" } function Options(t) local short, long = {}, {} for i = 1, getn(t) do for j, s in t[i].short do if short[s] then warn("duplicate short option '" .. s .. "'") end short[s] = t[i] end for j, l in t[i].long do long[l] = t[i] end end t.short = short t.long = long return t end function findLongOpt(options, opt) local len, match = strlen(opt), {} for i, v in options.long do if strsub(i, 1, len) == opt then tinsert(match, v) end end if getn(match) == 1 then return match[1] end return match end function errAmbig(optStr, optTypes) local header = "option `" .. optStr .. "' is ambiguous; could be one of:" return usageInfo(header, optTypes) end function errNoArg(optStr) return "option `" .. optStr .. "' doesn't take an argument" end function errReqArg(optStr, desc) return "option `" .. optStr .. "' requires an argument `" .. desc .. "'" end function errUnrec(optStr) return "unrecognized option `" .. optStr .. "'" end function usageInfo(header, optDesc, pageWidth) pageWidth = pageWidth or 78 function fmtOpt(opt) function fmtShort(c) return "-" .. c end function fmtLong(o) local arg = %opt.type.arg if arg == "None" then return "--" .. o end if arg == "Req" then return "--" .. o .. "=" .. %opt.type.var end return "--" .. o .. "[=" .. %opt.type.var .. "]" end return { join(", " , { join(", ", map(fmtShort, opt.short)), join(", ", map(fmtLong, opt.long )) } ) , opt.desc } end function sameLen(xs) local n = call(max, map(strlen, xs)) for i, v in xs do xs[i] = strsub(v .. strrep(" ", n), 1, n) end return xs, n end function paste(x, y) return " " .. x .. " " .. y end function wrapper(w, i) return function (s) return wrap(s, %w, 0, %i) end end local cols = unzip(map(fmtOpt, optDesc)) local width cols[1], width = sameLen(cols[1]) cols[2] = map(wrapper(pageWidth - width - 4, width + 4), cols[2]) return header .. "\n" .. join("\n", mapCall(paste, unzip{sameLen(cols[1]), cols[2]}) ) end function processArgs(options) local totArgs = getn(arg) options = Options(concat(options, { Option({"v"}, {"version"}, OptType "None", "show program version"), Option({"h", "?"}, {"help"}, OptType "None", "show this help") } )) local errors arg, opt, errors = getOpt(arg, options) if (opt.version or totArgs == 0) and prog.banner then write(_STDERR, prog.banner .. "\n") end if getn(errors) > 0 or totArgs == 0 or opt.help then local name name, prog.name = prog.name, nil local header = "" if getn(errors) > 0 then header = join("\n", errors) .. "\n" end die(header .. usageInfo("Usage: " .. name .. " " .. (prog.usage or "[OPTION...] FILE...") .. "\n" .. (prog.purpose or "") .. "\n" , options) .. ((prog.notes and "\n\n" .. prog.notes) or "")) end end if _DEBUG then options = Options{ Option({"v"}, {"verbose"}, OptType("None"), "verbosely list files"), Option({"V", "?"}, {"version", "release"}, OptType("None"), "show version info"), Option({"o"}, {"output"}, OptType("Opt", "FILE", out), "use FILE for dump"), Option({"n"}, {"name"}, OptType("Req", "USER"), "only dump USER's files") } function out(o) return o or _STDOUT end function test(cmdLine, optsFirst, inOrder) local opts, nonOpts, errors = getOpt(cmdLine, options, optsFirst, inOrder) if getn(errors) == 0 then print("options=" .. tostring(opts) .. " args=" .. tostring(nonOpts) .. "\n") else print(join("\n", errors) .. "\n" .. usageInfo("Usage: foobar [OPTION...] FILE...", options)) end end test({"foo", "-v"}, 1) test{"foo", "-v"} test({"foo", "-v"}, nil, 1) test{"foo", "--", "-v"} test{"-?o", "--name", "bar", "--na=baz"} test{"--ver", "foo"} end
|