diff --git a/examples/rebar_test.escript b/examples/rebar_test.escript new file mode 100755 index 0000000..56770f1 --- /dev/null +++ b/examples/rebar_test.escript @@ -0,0 +1,40 @@ +#!/usr/bin/env escript +%% -*- erlang -*- +%%! -sname ex1 -pz ebin + +%%%------------------------------------------------------------------- +%%% @author Juan Jose Comellas +%%% @copyright (C) 2009 Juan Jose Comellas +%%% @doc Example file for the getopt module. +%%% @end +%%% +%%% This source file is subject to the New BSD License. You should have received +%%% a copy of the New BSD license with this software. If not, it can be +%%% retrieved from: http://www.opensource.org/licenses/bsd-license.php +%%%------------------------------------------------------------------- +-module(rebar_test). +-author('juanjo@comellas.org'). + +main([]) -> + getopt:usage(option_spec_list(), escript:script_name()); +main(Args) -> + OptSpecList = option_spec_list(), + io:format("For command line: ~p~n" + "getopt:parse/2 returns:~n~n", [Args]), + case getopt:parse(OptSpecList, Args) of + {ok, {Options, NonOptArgs}} -> + io:format("Options:~n ~p~n~nNon-option arguments:~n ~p~n", [Options, NonOptArgs]); + {error, {Reason, Data}} -> + io:format("Error: ~s ~p~n~n", [Reason, Data]), + getopt:usage(OptSpecList, "ex1.escript") + end. + + +option_spec_list() -> + [ + %% {Name, ShortOpt, LongOpt, ArgSpec, HelpMsg} + {help, $h, "help", undefined, "Show the program options"}, + {verbose, $v, "verbose", {boolean, false}, "Be verbose about what gets done"}, + {quiet, $q, "quiet", {boolean, false}, "Be quiet about what gets done"}, + {force, $f, "force", {boolean, false}, "Force"} + ]. diff --git a/src/examples/ex1.erl b/src/examples/ex1.erl index e84d1a5..2ac5906 100644 --- a/src/examples/ex1.erl +++ b/src/examples/ex1.erl @@ -18,7 +18,7 @@ test() -> test(CmdLine) -> - OptSpecList = option_spec(), + OptSpecList = option_spec_list(), io:format("For command line: ~p~n" "getopt:parse/2 returns:~n~n", [CmdLine]), @@ -31,7 +31,7 @@ test(CmdLine) -> end. -option_spec() -> +option_spec_list() -> CurrentUser = os:getenv("USER"), [ %% {Name, ShortOpt, LongOpt, ArgSpec, HelpMsg} diff --git a/src/examples/rebar_test.erl b/src/examples/rebar_test.erl new file mode 100644 index 0000000..1ea3400 --- /dev/null +++ b/src/examples/rebar_test.erl @@ -0,0 +1,41 @@ +%%%------------------------------------------------------------------- +%%% @author Juan Jose Comellas +%%% @copyright (C) 2009 Juan Jose Comellas +%%% @doc Example file for the getopt module. +%%% @end +%%% +%%% This source file is subject to the New BSD License. You should have received +%%% a copy of the New BSD license with this software. If not, it can be +%%% retrieved from: http://www.opensource.org/licenses/bsd-license.php +%%%------------------------------------------------------------------- +-module(rebar_test). +-author('juanjo@comellas.org'). + +-export([test/0, test/1]). + +test() -> + test("-f verbose=1 --quiet=on dummy1 dummy2"). + + +test(CmdLine) -> + OptSpecList = option_spec_list(), + + io:format("For command line: ~p~n" + "getopt:parse/2 returns:~n~n", [CmdLine]), + case getopt:parse(OptSpecList, CmdLine) of + {ok, {Options, NonOptArgs}} -> + io:format("Options:~n ~p~n~nNon-option arguments:~n ~p~n", [Options, NonOptArgs]); + {error, {Reason, Data}} -> + io:format("Error: ~s ~p~n~n", [Reason, Data]), + getopt:usage(OptSpecList, "ex1") + end. + + +option_spec_list() -> + [ + %% {Name, ShortOpt, LongOpt, ArgSpec, HelpMsg} + {help, $h, "help", undefined, "Show the program options"}, + {verbose, $v, "verbose", {boolean, false}, "Be verbose about what gets done"}, + {quiet, $q, "quiet", {boolean, false}, "Be quiet about what gets done"}, + {force, $f, "force", {boolean, false}, "Force"} + ]. diff --git a/src/getopt.erl b/src/getopt.erl index 2b0ad2e..052200d 100644 --- a/src/getopt.erl +++ b/src/getopt.erl @@ -11,7 +11,7 @@ -module(getopt). -author('juanjo@comellas.org'). --export([parse/2, usage/2]). +-export([parse/2, usage/2, is_option/1]). -define(TAB_LENGTH, 8). @@ -129,11 +129,24 @@ get_option(OptSpecList, OptStr, FieldPos, OptName, Tail) -> undefined -> {Name, Tail}; _ -> + ArgSpecType = arg_spec_type(ArgSpec), case Tail of [Arg | Tail1] -> - {convert_option_arg(OptSpec, Arg), Tail1}; + case (ArgSpecType =:= boolean) andalso is_option(Arg) of + %% Special case for booleans: when the next string + %% is an option we assume the value is 'true'. + true -> + {{Name, true}, Tail}; + _ -> + {convert_option_arg(OptSpec, Arg), Tail1} + end; [] -> - throw({error, {missing_option_arg, Name}}) + case ArgSpecType of + boolean -> + {{Name, true}, Tail}; + _ -> + throw({error, {missing_option_arg, Name}}) + end end end; false -> @@ -181,8 +194,15 @@ get_option_no_arg(OptSpecList, OptStr, OptName, FieldPos) -> case lists:keysearch(OptName, FieldPos, OptSpecList) of {value, {Name, _Short, _Long, undefined, _Help}} -> Name; - {value, {Name, _Short, _Long, _ArgSpec, _Help}} -> - throw({error, {missing_option_arg, Name}}); + {value, {Name, _Short, _Long, ArgSpec, _Help}} -> + case arg_spec_type(ArgSpec) of + %% Special case for booleans: if there is no argument we assume + %% the value is 'true'. + boolean -> + {Name, true}; + _ -> + throw({error, {missing_option_arg, Name}}) + end; false -> throw({error, {invalid_option, OptStr}}) end. @@ -218,24 +238,30 @@ append_default_args([], OptAcc) -> OptAcc. +-spec is_option(string()) -> boolean(). +is_option([Char | _Tail] = OptStr) -> + (Char =:= $-) orelse lists:member($=, OptStr). + + -spec convert_option_arg(option_spec(), string()) -> [option()]. %% @doc Convert the argument passed in the command line to the data type -%% indicated byt the argument specification. +%% indicated by the argument specification. convert_option_arg({Name, _Short, _Long, ArgSpec, _Help}, Arg) -> try - Converted = case ArgSpec of - {Type, _DefaultArg} -> - to_type(Type, Arg); - Type when is_atom(Type) -> - to_type(Type, Arg) - end, - {Name, Converted} + {Name, to_type(arg_spec_type(ArgSpec), Arg)} catch error:_ -> throw({error, {invalid_option_arg, {Name, Arg}}}) end. +-spec arg_spec_type(arg_spec()) -> arg_type() | undefined. +arg_spec_type({Type, _DefaultArg}) -> + Type; +arg_spec_type(Type) when is_atom(Type) -> + Type. + + -spec to_type(atom(), string()) -> arg_value(). to_type(binary, Arg) -> list_to_binary(Arg);