|
|
@ -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))]). |
|
|
|
|
|
|
|