Add support for embedded arguments with equal characters in them.

e.g. "--var=debug=1" --> {var, "debug=1"}
This commit is contained in:
Juan Jose Comellas 2009-12-30 18:11:25 -03:00
parent d26cb9df09
commit 0329f560c4
2 changed files with 28 additions and 11 deletions

View File

@ -11,6 +11,9 @@
-module(getopt). -module(getopt).
-author('juanjo@comellas.org'). -author('juanjo@comellas.org').
-export([parse/2, usage/2]).
-define(TAB_LENGTH, 8). -define(TAB_LENGTH, 8).
%% Indentation of the help messages in number of tabs. %% Indentation of the help messages in number of tabs.
-define(INDENTATION, 3). -define(INDENTATION, 3).
@ -44,8 +47,6 @@
Help :: string() | undefined Help :: string() | undefined
}. }.
-export([parse/2, usage/2]).
-spec parse([option_spec()], string() | [string()]) -> {ok, {[option()], [string()]}} | {error, {Reason :: atom(), Data :: any()}}. -spec parse([option_spec()], string() | [string()]) -> {ok, {[option()], [string()]}} | {error, {Reason :: atom(), Data :: any()}}.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
@ -73,13 +74,13 @@ parse(OptSpecList, CmdLine) ->
%% Process long options. %% Process long options.
parse(OptSpecList, OptAcc, ArgAcc, ArgPos, [[$-, $- | LongName] = OptStr | Tail]) -> parse(OptSpecList, OptAcc, ArgAcc, ArgPos, [[$-, $- | LongName] = OptStr | Tail]) ->
{Option, Tail1} = {Option, Tail1} =
case string:tokens(LongName, "=") of case split_embedded_arg(LongName) of
[_LongName1] -> {LongName1, Arg} ->
get_option(OptSpecList, OptStr, LongName, ?OPT_LONG, Tail); %% Get option that has its argument within the same string
[LongName1, Arg] -> %% separated by an equal ('=') character.
{get_option_embedded_arg(OptSpecList, OptStr, LongName1, Arg), Tail}; {get_option_embedded_arg(OptSpecList, OptStr, LongName1, Arg), Tail};
_ -> _LongName1 ->
throw({error, {invalid_option_arg, OptStr}}) get_option(OptSpecList, OptStr, LongName, ?OPT_LONG, Tail)
end, end,
parse(OptSpecList, [Option | OptAcc], ArgAcc, ArgPos, Tail1); parse(OptSpecList, [Option | OptAcc], ArgAcc, ArgPos, Tail1);
%% Process short options. %% Process short options.
@ -130,6 +131,7 @@ get_option(OptSpecList, OptStr, OptName, FieldPos, Tail) ->
throw({error, {invalid_option, OptStr}}) throw({error, {invalid_option, OptStr}})
end. end.
-spec get_option_embedded_arg([option_spec()], string(), string(), string()) -> option(). -spec get_option_embedded_arg([option_spec()], string(), string(), string()) -> option().
%% @doc Retrieve the specification corresponding to an option matching a string %% @doc Retrieve the specification corresponding to an option matching a string
%% received on the command line that had its argument assigned within the %% received on the command line that had its argument assigned within the
@ -147,6 +149,21 @@ get_option_embedded_arg(OptSpecList, OptStr, OptName, Arg) ->
throw({error, {invalid_option, OptStr}}) throw({error, {invalid_option, OptStr}})
end. end.
-spec split_embedded_arg(string()) -> {Name :: string(), Arg :: string()} | string().
%% @doc Split an option string that may contain and option with its argument
%% separated by an equal ('=') character (e.g. "port=1000").
split_embedded_arg(OptStr) ->
split_embedded_arg(OptStr, OptStr, []).
split_embedded_arg(_OptStr, [$= | Tail], Acc) ->
{lists:reverse(Acc), Tail};
split_embedded_arg(OptStr, [Char | Tail], Acc) ->
split_embedded_arg(OptStr, Tail, [Char | Acc]);
split_embedded_arg(OptStr, [], _Acc) ->
OptStr.
-spec get_option_no_arg([option_spec()], string(), string() | char(), integer()) -> option(). -spec get_option_no_arg([option_spec()], string(), string() | char(), integer()) -> option().
%% @doc Retrieve the specification corresponding to an option that has no %% @doc Retrieve the specification corresponding to an option that has no
%% argument and matches a string received on the command line. %% argument and matches a string received on the command line.
@ -160,6 +177,7 @@ get_option_no_arg(OptSpecList, OptStr, OptName, FieldPos) ->
throw({error, {invalid_option, OptStr}}) throw({error, {invalid_option, OptStr}})
end. end.
-spec find_non_option_arg([option_spec()], integer()) -> {value, option_spec()} | false. -spec find_non_option_arg([option_spec()], integer()) -> {value, option_spec()} | false.
%% @doc Find the option for the discrete argument in position specified in the %% @doc Find the option for the discrete argument in position specified in the
%% Pos argument. %% Pos argument.
@ -207,6 +225,7 @@ convert_option_arg({Name, _Short, _Long, ArgSpec, _Help}, Arg) ->
throw({error, {invalid_option_arg, {Name, Arg}}}) throw({error, {invalid_option_arg, {Name, Arg}}})
end. end.
-spec to_type(atom(), string()) -> arg_value(). -spec to_type(atom(), string()) -> arg_value().
to_type(binary, Arg) -> to_type(binary, Arg) ->
list_to_binary(Arg); list_to_binary(Arg);

View File

@ -127,12 +127,10 @@ parse_1_test_() ->
{?HELP(Long), ?_assertMatch({ok, {[long], []}}, parse([Long], ["--long"]))}, {?HELP(Long), ?_assertMatch({ok, {[long], []}}, parse([Long], ["--long"]))},
{?HELP(LongArg), ?_assertMatch({ok, {[{long_arg, "value"}], []}}, parse([LongArg], ["--long-arg", "value"]))}, {?HELP(LongArg), ?_assertMatch({ok, {[{long_arg, "value"}], []}}, parse([LongArg], ["--long-arg", "value"]))},
{?HELP(LongArg), ?_assertMatch({ok, {[{long_arg, "value"}], []}}, parse([LongArg], ["--long-arg=value"]))}, {?HELP(LongArg), ?_assertMatch({ok, {[{long_arg, "value"}], []}}, parse([LongArg], ["--long-arg=value"]))},
{?HELP(LongArg), ?_assertMatch({ok, {[{long_arg, "value=1"}], []}}, parse([LongArg], ["--long-arg=value=1"]))},
{?HELP(LongDefArg), ?_assertMatch({ok, {[{long_def_arg, "default-long"}], []}}, parse([LongDefArg], []))}, {?HELP(LongDefArg), ?_assertMatch({ok, {[{long_def_arg, "default-long"}], []}}, parse([LongDefArg], []))},
{?HELP(LongInt), ?_assertMatch({ok, {[{long_int, 100}], []}}, parse([LongInt], ["--long-int", "100"]))}, {?HELP(LongInt), ?_assertMatch({ok, {[{long_int, 100}], []}}, parse([LongInt], ["--long-int", "100"]))},
{?HELP(LongInt), ?_assertMatch({ok, {[{long_int, 100}], []}}, parse([LongInt], ["--long-int=100"]))}, {?HELP(LongInt), ?_assertMatch({ok, {[{long_int, 100}], []}}, parse([LongInt], ["--long-int=100"]))},
%% Options with only the long form (expected failures)
{?HELP(LongArg) ++ " (XFAIL)",
?_assertEqual({error, {invalid_option_arg, "--long-arg=value=1"}}, parse([LongArg], ["--long-arg=value=1"]))},
{"Long form option and arguments", {"Long form option and arguments",
?_assertMatch({ok, {[long], ["arg1", "arg2"]}}, parse([Long], ["--long", "arg1", "arg2"]))}, ?_assertMatch({ok, {[long], ["arg1", "arg2"]}}, parse([Long], ["--long", "arg1", "arg2"]))},
{"Long form option and arguments (unsorted)", {"Long form option and arguments (unsorted)",