From d26cb9df09ffad3828ace9bb0ea7a4dbe4e2901e Mon Sep 17 00:00:00 2001 From: Juan Jose Comellas Date: Wed, 30 Dec 2009 17:20:35 -0300 Subject: [PATCH] Add support for arguments assigned to options with an equal ('=') character within the option string. e.g. "--port=1000". --- README.markdown | 8 ++++---- src/getopt.erl | 32 +++++++++++++++++++++++++++++--- src/test/getopt_test.erl | 7 +++++++ 3 files changed, 40 insertions(+), 7 deletions(-) diff --git a/README.markdown b/README.markdown index 57b1261..412e3bf 100644 --- a/README.markdown +++ b/README.markdown @@ -56,7 +56,7 @@ The fields of the record are: - ``Short``: character for the short option (e.g. $i for -i). - ``Long``: string for the long option (e.g. "info" for --info). - ``ArgSpec``: data type and optional default value the argument will be converted to. - - ``Help``: help message that is shown for the option when usage/2 is called. + - ``Help``: help message that is shown for the option when ``usage/2`` is called. The second parameter holds the list of arguments as passed to the ``main/1`` function in escripts. e.g. @@ -85,11 +85,11 @@ e.g. For a program named ``ex.escript`` with the following option specifications And this command line: - Args = "-h myhost --port 1000 -x myfile.txt dummy1 dummy2" + Args = "-h myhost --port=1000 -x myfile.txt dummy1 dummy2" Which could also be passed in the format the ``main/1`` function receives the arguments in escripts: - Args = ["-h", "myhost", "--port", "1000", "-x", "myfile.txt", "dummy1", "dummy2"]. + Args = ["-h", "myhost", "--port=1000", "-x", "myfile.txt", "dummy1", "dummy2"]. The call to ``getopt:parse/2``: @@ -110,7 +110,7 @@ Also, the call to ``getopt:usage/2``: Will show (on *stdout*): - Usage: ex.escript [-h ] [-p ] [--dbname ] [-x] + Usage: ex1 [-h ] [-p ] [--dbname ] [-x] -h, --host Database server host -p, --port Database server port diff --git a/src/getopt.erl b/src/getopt.erl index b335f0c..9809eea 100644 --- a/src/getopt.erl +++ b/src/getopt.erl @@ -72,7 +72,15 @@ parse(OptSpecList, CmdLine) -> {ok, {[option()], [string()]}} | {error, {Reason :: atom(), Data:: any()}}. %% Process long options. parse(OptSpecList, OptAcc, ArgAcc, ArgPos, [[$-, $- | LongName] = OptStr | Tail]) -> - {Option, Tail1} = get_option(OptSpecList, OptStr, LongName, ?OPT_LONG, Tail), + {Option, Tail1} = + case string:tokens(LongName, "=") of + [_LongName1] -> + get_option(OptSpecList, OptStr, LongName, ?OPT_LONG, Tail); + [LongName1, Arg] -> + {get_option_embedded_arg(OptSpecList, OptStr, LongName1, Arg), Tail}; + _ -> + throw({error, {invalid_option_arg, OptStr}}) + end, parse(OptSpecList, [Option | OptAcc], ArgAcc, ArgPos, Tail1); %% Process short options. parse(OptSpecList, OptAcc, ArgAcc, ArgPos, [[$-, ShortName] = OptStr | Tail]) -> @@ -114,7 +122,7 @@ get_option(OptSpecList, OptStr, OptName, FieldPos, Tail) -> case Tail of [Arg | Tail1] -> {convert_option_arg(OptSpec, Arg), Tail1}; - [] -> + [] -> throw({error, {missing_option_arg, Name}}) end end; @@ -122,6 +130,23 @@ get_option(OptSpecList, OptStr, OptName, FieldPos, Tail) -> throw({error, {invalid_option, OptStr}}) end. +-spec get_option_embedded_arg([option_spec()], string(), string(), string()) -> option(). +%% @doc Retrieve the specification corresponding to an option matching a string +%% received on the command line that had its argument assigned within the +%% same string (e.g. "--verbose=true"). +get_option_embedded_arg(OptSpecList, OptStr, OptName, Arg) -> + case lists:keysearch(OptName, ?OPT_LONG, OptSpecList) of + {value, {_Name, _Short, _Long, ArgSpec, _Help} = OptSpec} -> + case ArgSpec of + undefined -> + throw({error, {invalid_option_arg, OptStr}}); + _ -> + convert_option_arg(OptSpec, Arg) + end; + false -> + throw({error, {invalid_option, OptStr}}) + end. + -spec get_option_no_arg([option_spec()], string(), string() | char(), integer()) -> option(). %% @doc Retrieve the specification corresponding to an option that has no %% argument and matches a string received on the command line. @@ -195,7 +220,8 @@ to_type(boolean, Arg) -> LowerArg = string:to_lower(Arg), (LowerArg =:= "true") orelse (LowerArg =:= "t") orelse (LowerArg =:= "yes") orelse (LowerArg =:= "y") orelse - (LowerArg =:= "on") orelse (LowerArg =:= "enabled"); + (LowerArg =:= "on") orelse (LowerArg =:= "enabled") orelse + (LowerArg =:= "1"); to_type(_Type, Arg) -> Arg. diff --git a/src/test/getopt_test.erl b/src/test/getopt_test.erl index 793d6d7..708ed3f 100644 --- a/src/test/getopt_test.erl +++ b/src/test/getopt_test.erl @@ -126,8 +126,13 @@ parse_1_test_() -> %% Options with only the long form {?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(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"]))}, + %% 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", ?_assertMatch({ok, {[long], ["arg1", "arg2"]}}, parse([Long], ["--long", "arg1", "arg2"]))}, {"Long form option and arguments (unsorted)", @@ -137,9 +142,11 @@ parse_1_test_() -> {?HELP(ShortLong), ?_assertMatch({ok, {[short_long], []}}, parse([ShortLong], ["--short-long"]))}, {?HELP(ShortLongArg), ?_assertEqual({ok, {[{short_long_arg, "value"}], []}}, parse([ShortLongArg], [[$-, ?SHORT(ShortLongArg)], "value"]))}, {?HELP(ShortLongArg), ?_assertMatch({ok, {[{short_long_arg, "value"}], []}}, parse([ShortLongArg], ["--short-long-arg", "value"]))}, + {?HELP(ShortLongArg), ?_assertMatch({ok, {[{short_long_arg, "value"}], []}}, parse([ShortLongArg], ["--short-long-arg=value"]))}, {?HELP(ShortLongDefArg), ?_assertMatch({ok, {[{short_long_def_arg, "default-short-long"}], []}}, parse([ShortLongDefArg], []))}, {?HELP(ShortLongInt), ?_assertEqual({ok, {[{short_long_int, 1234}], []}}, parse([ShortLongInt], [[$-, ?SHORT(ShortLongInt)], "1234"]))}, {?HELP(ShortLongInt), ?_assertMatch({ok, {[{short_long_int, 1234}], []}}, parse([ShortLongInt], ["--short-long-int", "1234"]))}, + {?HELP(ShortLongInt), ?_assertMatch({ok, {[{short_long_int, 1234}], []}}, parse([ShortLongInt], ["--short-long-int=1234"]))}, %% Non-option arguments {?HELP(NonOptArg), ?_assertMatch({ok, {[{non_opt_arg, "value"}], []}}, parse([NonOptArg], ["value"]))}, {?HELP(NonOptInt), ?_assertMatch({ok, {[{non_opt_int, 1234}], []}}, parse([NonOptInt], ["1234"]))},