mirror of
https://github.com/correl/rebar.git
synced 2024-11-15 03:00:18 +00:00
Import new upstream getopt.erl
This commit is contained in:
parent
ee44d8554a
commit
263b49f970
1 changed files with 125 additions and 49 deletions
174
src/getopt.erl
174
src/getopt.erl
|
@ -53,6 +53,8 @@
|
|||
ArgSpec :: arg_spec(),
|
||||
Help :: string() | undefined
|
||||
}.
|
||||
%% Output streams
|
||||
-type output_stream() :: 'standard_io' | 'standard_error'.
|
||||
|
||||
|
||||
%% @doc Parse the command line options and arguments returning a list of tuples
|
||||
|
@ -91,7 +93,7 @@ parse(OptSpecList, OptAcc, ArgAcc, ArgPos, ["-" ++ ([_Char | _] = OptArg) = OptS
|
|||
parse(OptSpecList, OptAcc, ArgAcc, ArgPos, [Arg | Tail]) ->
|
||||
case find_non_option_arg(OptSpecList, ArgPos) of
|
||||
{value, OptSpec} when ?IS_OPT_SPEC(OptSpec) ->
|
||||
parse(OptSpecList, [convert_option_arg(OptSpec, Arg) | OptAcc],
|
||||
parse(OptSpecList, add_option_arg(OptSpec, Arg, OptAcc),
|
||||
ArgAcc, ArgPos + 1, Tail);
|
||||
false ->
|
||||
parse(OptSpecList, OptAcc, [Arg | ArgAcc], ArgPos, Tail)
|
||||
|
@ -145,7 +147,7 @@ parse_option_assigned_arg(OptSpecList, OptAcc, ArgAcc, ArgPos, Args, OptStr, Lon
|
|||
undefined ->
|
||||
throw({error, {invalid_option_arg, OptStr}});
|
||||
_ ->
|
||||
parse(OptSpecList, [convert_option_arg(OptSpec, Arg) | OptAcc], ArgAcc, ArgPos, Args)
|
||||
parse(OptSpecList, add_option_arg(OptSpec, Arg, OptAcc), ArgAcc, ArgPos, Args)
|
||||
end;
|
||||
false ->
|
||||
throw({error, {invalid_option, OptStr}})
|
||||
|
@ -174,6 +176,7 @@ split_assigned_arg(OptStr, [], _Acc) ->
|
|||
%% -afoo Single option 'a', argument "foo"
|
||||
%% -abc Multiple options: 'a'; 'b'; 'c'
|
||||
%% -bcafoo Multiple options: 'b'; 'c'; 'a' with argument "foo"
|
||||
%% -aaa Multiple repetitions of option 'a' (only valid for options with integer arguments)
|
||||
-spec parse_option_short([option_spec()], [option()], [string()], integer(), [string()], string(), string()) ->
|
||||
{ok, {[option()], [string()]}}.
|
||||
parse_option_short(OptSpecList, OptAcc, ArgAcc, ArgPos, Args, OptStr, [Short | Arg]) ->
|
||||
|
@ -184,16 +187,22 @@ parse_option_short(OptSpecList, OptAcc, ArgAcc, ArgPos, Args, OptStr, [Short | A
|
|||
{_Name, Short, _Long, ArgSpec, _Help} = OptSpec ->
|
||||
case Arg of
|
||||
[] ->
|
||||
% The option argument string is empty, but the option requires
|
||||
% an argument, so we look into the next string in the list.
|
||||
%% The option argument string is empty, but the option requires
|
||||
%% an argument, so we look into the next string in the list.
|
||||
parse_option_next_arg(OptSpecList, OptAcc, ArgAcc, ArgPos, Args, OptSpec);
|
||||
|
||||
_ ->
|
||||
case is_valid_arg(ArgSpec, Arg) of
|
||||
true ->
|
||||
parse(OptSpecList, [convert_option_arg(OptSpec, Arg) | OptAcc], ArgAcc, ArgPos, Args);
|
||||
parse(OptSpecList, add_option_arg(OptSpec, Arg, OptAcc), ArgAcc, ArgPos, Args);
|
||||
_ ->
|
||||
parse_option_short(OptSpecList, [convert_option_no_arg(OptSpec) | OptAcc], ArgAcc, ArgPos, Args, OptStr, Arg)
|
||||
%% There are 2 valid cases in which we may not receive the expected argument:
|
||||
%% 1) When the expected argument is a boolean: in this case the presence
|
||||
%% of the option makes the argument true.
|
||||
%% 2) When the expected argument is an integer: in this case the presence
|
||||
%% of the option sets the value to 1 and any additional appearances of
|
||||
%% the option increment it by 1 (e.g. "-vvv" would return {verbose, 3}).
|
||||
parse_option_short(OptSpecList, add_option_no_arg(OptSpec, OptAcc), ArgAcc, ArgPos, Args, OptStr, Arg)
|
||||
end
|
||||
end;
|
||||
|
||||
|
@ -205,25 +214,34 @@ parse_option_short(OptSpecList, OptAcc, ArgAcc, ArgPos, Args, _OptStr, []) ->
|
|||
|
||||
|
||||
%% @doc Retrieve the argument for an option from the next string in the list of
|
||||
%% command-line parameters.
|
||||
%% command-line parameters or set the value of the argument from the argument
|
||||
%% specification (for boolean and integer arguments), if possible.
|
||||
parse_option_next_arg(OptSpecList, OptAcc, ArgAcc, ArgPos, [Arg | Tail] = Args, {Name, _Short, _Long, ArgSpec, _Help} = OptSpec) ->
|
||||
% Special case for booleans: when the next string is an option we assume
|
||||
% the value is 'true'.
|
||||
case (arg_spec_type(ArgSpec) =:= boolean) andalso not is_boolean_arg(Arg) of
|
||||
ArgSpecType = arg_spec_type(ArgSpec),
|
||||
case (ArgSpecType =:= boolean) andalso not is_boolean_arg(Arg) of
|
||||
true ->
|
||||
parse(OptSpecList, [{Name, true} | OptAcc], ArgAcc, ArgPos, Args);
|
||||
_ ->
|
||||
parse(OptSpecList, [convert_option_arg(OptSpec, Arg) | OptAcc], ArgAcc, ArgPos, Tail)
|
||||
%% Special case for booleans: when the next string is not a boolean
|
||||
%% argument we assume the value is 'true'.
|
||||
parse(OptSpecList, add_arg(OptSpec, true, OptAcc), ArgAcc, ArgPos, Args);
|
||||
false ->
|
||||
case (ArgSpecType =:= integer) andalso not is_integer_arg(Arg) of
|
||||
true ->
|
||||
%% Special case for integer arguments: if the option had not been set
|
||||
%% before we set the value to 1; if not we increment the previous value
|
||||
%% the option had. This is needed to support options like "-vvv" to
|
||||
%% return something like {verbose, 3}.
|
||||
parse(OptSpecList, add_implicit_integer_arg(OptSpec, OptAcc), ArgAcc, ArgPos, Args);
|
||||
false ->
|
||||
try
|
||||
parse(OptSpecList, add_arg(OptSpec, to_type(ArgSpecType, Arg), OptAcc), ArgAcc, ArgPos, Tail)
|
||||
catch
|
||||
error:_ ->
|
||||
throw({error, {invalid_option_arg, {Name, Arg}}})
|
||||
end
|
||||
end
|
||||
end;
|
||||
parse_option_next_arg(OptSpecList, OptAcc, ArgAcc, ArgPos, [] = Args, {Name, _Short, _Long, ArgSpec, _Help}) ->
|
||||
% Special case for booleans: when the next string is missing we assume the
|
||||
% value is 'true'.
|
||||
case arg_spec_type(ArgSpec) of
|
||||
boolean ->
|
||||
parse(OptSpecList, [{Name, true} | OptAcc], ArgAcc, ArgPos, Args);
|
||||
_ ->
|
||||
throw({error, {missing_option_arg, Name}})
|
||||
end.
|
||||
parse_option_next_arg(OptSpecList, OptAcc, ArgAcc, ArgPos, [] = Args, OptSpec) ->
|
||||
parse(OptSpecList, add_option_no_arg(OptSpec, OptAcc), ArgAcc, ArgPos, Args).
|
||||
|
||||
|
||||
%% @doc Find the option for the discrete argument in position specified in the
|
||||
|
@ -257,32 +275,72 @@ append_default_options([], OptAcc) ->
|
|||
OptAcc.
|
||||
|
||||
|
||||
-spec convert_option_no_arg(option_spec()) -> compound_option().
|
||||
convert_option_no_arg({Name, _Short, _Long, ArgSpec, _Help}) ->
|
||||
case ArgSpec of
|
||||
% Special case for booleans: if there is no argument we assume
|
||||
% the value is 'true'.
|
||||
{boolean, _DefaultValue} ->
|
||||
{Name, true};
|
||||
%% @doc Add an option with no argument.
|
||||
-spec add_option_no_arg(option_spec(), [option()]) -> [option()].
|
||||
add_option_no_arg({Name, _Short, _Long, ArgSpec, _Help} = OptSpec, OptAcc) ->
|
||||
case arg_spec_type(ArgSpec) of
|
||||
boolean ->
|
||||
{Name, true};
|
||||
%% Special case for boolean arguments: if there is no argument we
|
||||
%% set the value to 'true'.
|
||||
add_arg(OptSpec, true, OptAcc);
|
||||
integer ->
|
||||
%% Special case for integer arguments: if the option had not been set
|
||||
%% before we set the value to 1; if not we increment the previous value
|
||||
%% the option had. This is needed to support options like "-vvv" to
|
||||
%% return something like {verbose, 3}.
|
||||
add_implicit_integer_arg(OptSpec, OptAcc);
|
||||
_ ->
|
||||
throw({error, {missing_option_arg, Name}})
|
||||
end.
|
||||
|
||||
|
||||
%% @doc Convert the argument passed in the command line to the data type
|
||||
%% indicated by the argument specification.
|
||||
-spec convert_option_arg(option_spec(), string()) -> compound_option().
|
||||
convert_option_arg({Name, _Short, _Long, ArgSpec, _Help}, Arg) ->
|
||||
try
|
||||
{Name, to_type(arg_spec_type(ArgSpec), Arg)}
|
||||
catch
|
||||
error:_ ->
|
||||
throw({error, {invalid_option_arg, {Name, Arg}}})
|
||||
%% @doc Add an option with argument converting it to the data type indicated by the
|
||||
%% argument specification.
|
||||
-spec add_option_arg(option_spec(), string(), [option()]) -> [option()].
|
||||
add_option_arg({Name, _Short, _Long, ArgSpec, _Help} = OptSpec, Arg, OptAcc) ->
|
||||
ArgSpecType = arg_spec_type(ArgSpec),
|
||||
case (ArgSpecType =:= boolean) andalso not is_boolean_arg(Arg) of
|
||||
true ->
|
||||
%% Special case for booleans: when the next string is not a boolean
|
||||
%% argument we assume the value is 'true'.
|
||||
add_arg(OptSpec, true, OptAcc);
|
||||
false ->
|
||||
case (ArgSpecType =:= integer) andalso not is_integer_arg(Arg) of
|
||||
true ->
|
||||
%% Special case for integer arguments: if the option had not been set
|
||||
%% before we set the value to 1; if not we increment the previous value
|
||||
%% the option had. This is needed to support options like "-vvv" to
|
||||
%% return something like {verbose, 3}.
|
||||
add_implicit_integer_arg(OptSpec, OptAcc);
|
||||
false ->
|
||||
try
|
||||
add_arg(OptSpec, to_type(ArgSpecType, Arg), OptAcc)
|
||||
catch
|
||||
error:_ ->
|
||||
throw({error, {invalid_option_arg, {Name, Arg}}})
|
||||
end
|
||||
end
|
||||
end.
|
||||
|
||||
|
||||
%% Add an option with an integer argument.
|
||||
-spec add_implicit_integer_arg(option_spec(), [option()]) -> [option()].
|
||||
add_implicit_integer_arg({Name, _Short, _Long, _ArgSpec, _Help}, OptAcc) ->
|
||||
case lists:keyfind(Name, 1, OptAcc) of
|
||||
{Name, Count} ->
|
||||
lists:keyreplace(Name, 1, OptAcc, {Name, Count + 1});
|
||||
false ->
|
||||
[{Name, 1} | OptAcc]
|
||||
end.
|
||||
|
||||
|
||||
%% @doc Add an option with an argument and convert it to the data type corresponding
|
||||
%% to the argument specification.
|
||||
-spec add_arg(option_spec(), arg_value(), [option()]) -> [option()].
|
||||
add_arg({Name, _Short, _Long, _ArgSpec, _Help}, Arg, OptAcc) ->
|
||||
[{Name, Arg} | lists:keydelete(Name, 1, OptAcc)].
|
||||
|
||||
|
||||
%% @doc Retrieve the data type form an argument specification.
|
||||
-spec arg_spec_type(arg_spec()) -> arg_type() | undefined.
|
||||
arg_spec_type({Type, _DefaultArg}) ->
|
||||
|
@ -371,34 +429,52 @@ is_float_arg([]) ->
|
|||
true.
|
||||
|
||||
|
||||
%% @doc Show a message on stdout indicating the command line options and
|
||||
%% @doc Show a message on stderr indicating the command line options and
|
||||
%% arguments that are supported by the program.
|
||||
-spec usage([option_spec()], string()) -> ok.
|
||||
usage(OptSpecList, ProgramName) ->
|
||||
io:format("Usage: ~s~s~n~n~s~n",
|
||||
[ProgramName, usage_cmd_line(OptSpecList), usage_options(OptSpecList)]).
|
||||
usage(OptSpecList, ProgramName, standard_error).
|
||||
|
||||
|
||||
%% @doc Show a message on stdout indicating the command line options and
|
||||
%% @doc Show a message on stderr or stdout indicating the command line options and
|
||||
%% arguments that are supported by the program.
|
||||
-spec usage([option_spec()], string(), output_stream() | string()) -> ok.
|
||||
usage(OptSpecList, ProgramName, OutputStream) when is_atom(OutputStream) ->
|
||||
io:format(OutputStream, "Usage: ~s~s~n~n~s~n",
|
||||
[ProgramName, usage_cmd_line(OptSpecList), usage_options(OptSpecList)]);
|
||||
%% @doc Show a message on stderr indicating the command line options and
|
||||
%% arguments that are supported by the program. The CmdLineTail argument
|
||||
%% is a string that is added to the end of the usage command line.
|
||||
-spec usage([option_spec()], string(), string()) -> ok.
|
||||
usage(OptSpecList, ProgramName, CmdLineTail) ->
|
||||
io:format("Usage: ~s~s ~s~n~n~s~n",
|
||||
[ProgramName, usage_cmd_line(OptSpecList), CmdLineTail, usage_options(OptSpecList)]).
|
||||
usage(OptSpecList, ProgramName, CmdLineTail, standard_error).
|
||||
|
||||
|
||||
%% @doc Show a message on stdout indicating the command line options and
|
||||
%% @doc Show a message on stderr or stdout indicating the command line options and
|
||||
%% arguments that are supported by the program. The CmdLineTail argument
|
||||
%% is a string that is added to the end of the usage command line.
|
||||
-spec usage([option_spec()], string(), string(), output_stream() | [{string(), string()}]) -> ok.
|
||||
usage(OptSpecList, ProgramName, CmdLineTail, OutputStream) when is_atom(OutputStream) ->
|
||||
io:format(OutputStream, "Usage: ~s~s ~s~n~n~s~n",
|
||||
[ProgramName, usage_cmd_line(OptSpecList), CmdLineTail, usage_options(OptSpecList)]);
|
||||
%% @doc Show a message on stderr indicating the command line options and
|
||||
%% arguments that are supported by the program. The CmdLineTail and OptionsTail
|
||||
%% arguments are a string that is added to the end of the usage command line
|
||||
%% and a list of tuples that are added to the end of the options' help lines.
|
||||
-spec usage([option_spec()], string(), string(), [{string(), string()}]) -> ok.
|
||||
usage(OptSpecList, ProgramName, CmdLineTail, OptionsTail) ->
|
||||
usage(OptSpecList, ProgramName, CmdLineTail, OptionsTail, standard_error).
|
||||
|
||||
|
||||
%% @doc Show a message on stderr or stdout indicating the command line options and
|
||||
%% arguments that are supported by the program. The CmdLineTail and OptionsTail
|
||||
%% arguments are a string that is added to the end of the usage command line
|
||||
%% and a list of tuples that are added to the end of the options' help lines.
|
||||
-spec usage([option_spec()], string(), string(), [{string(), string()}], output_stream()) -> ok.
|
||||
usage(OptSpecList, ProgramName, CmdLineTail, OptionsTail, OutputStream) ->
|
||||
UsageOptions = lists:foldl(
|
||||
fun ({Prefix, Help}, Acc) ->
|
||||
add_option_help(Prefix, Help, Acc)
|
||||
end, usage_options_reverse(OptSpecList, []), OptionsTail),
|
||||
io:format("Usage: ~s~s ~s~n~n~s~n",
|
||||
io:format(OutputStream, "Usage: ~s~s ~s~n~n~s~n",
|
||||
[ProgramName, usage_cmd_line(OptSpecList), CmdLineTail,
|
||||
lists:flatten(lists:reverse(UsageOptions))]).
|
||||
|
||||
|
|
Loading…
Reference in a new issue