1 %%%-------------------------------------------------------------------
2 %%% @author Juan Jose Comellas <juanjo@comellas.org>
3 %%% @copyright (C) 2009 Juan Jose Comellas
4 %%% @doc Parses command line options with a format similar to that of GNU getopt.
7 %%% This source file is subject to the New BSD License. You should have received
8 %%% a copy of the New BSD license with this software. If not, it can be
9 %%% retrieved from: http://www.opensource.org/licenses/bsd-license.php
10 %%%-------------------------------------------------------------------
12 -author('juanjo@comellas.org').
14 -export([parse
/2, usage
/2, usage
/3, usage
/4]).
17 -define(TAB_LENGTH
, 8).
18 %% Indentation of the help messages in number of tabs.
19 -define(INDENTATION
, 3).
21 %% Position of each field in the option specification tuple.
23 -define(OPT_SHORT
, 2).
28 -define(IS_OPT_SPEC(Opt
), (tuple_size(Opt
) =:= ?OPT_HELP
)).
31 %% Atom indicating the data type that an argument can be converted to.
32 -type
arg_type() :: 'atom' | 'binary' | 'boolean' | 'float' | 'integer' | 'string'.
33 %% Data type that an argument can be converted to.
34 -type
arg_value() :: atom() | binary() | boolean() | float() | integer() | string().
35 %% Argument specification.
36 -type
arg_spec() :: arg_type() | {arg_type(), arg_value()} | undefined
.
37 %% Option type and optional default argument.
38 -type
simple_option() :: atom().
39 -type
compound_option() :: {atom(), arg_value()}.
40 -type
option() :: simple_option() | compound_option().
41 %% Command line option specification.
42 -type
option_spec() :: {
44 Short
:: char() | undefined
,
45 Long
:: string() | undefined
,
46 ArgSpec
:: arg_spec(),
47 Help
:: string() | undefined
51 %% @doc Parse the command line options and arguments returning a list of tuples
52 %% and/or atoms using the Erlang convention for sending options to a
54 -spec
parse([option_spec()], string() | [string()]) ->
55 {ok
, {[option()], [string()]}} | {error
, {Reason
:: atom(), Data
:: any()}}.
56 parse(OptSpecList
, CmdLine
) ->
59 is_integer(hd(CmdLine
)) ->
60 string:tokens(CmdLine
, " \t\n");
64 parse(OptSpecList
, [], [], 0, Args
)
66 throw: {error
, {_Reason
, _Data
}} = Error
->
71 -spec
parse([option_spec()], [option()], [string()], integer(), [string()]) ->
72 {ok
, {[option()], [string()]}}.
73 %% Process the option terminator.
74 parse(OptSpecList
, OptAcc
, ArgAcc
, _ArgPos
, ["--" | Tail
]) ->
75 % Any argument present after the terminator is not considered an option.
76 {ok
, {lists:reverse(append_default_options(OptSpecList
, OptAcc
)), lists:reverse(ArgAcc
, Tail
)}};
77 %% Process long options.
78 parse(OptSpecList
, OptAcc
, ArgAcc
, ArgPos
, [[$
-, $
- | OptArg
] = OptStr
| Tail
]) ->
79 parse_option_long(OptSpecList
, OptAcc
, ArgAcc
, ArgPos
, Tail
, OptStr
, OptArg
);
80 %% Process short options.
81 parse(OptSpecList
, OptAcc
, ArgAcc
, ArgPos
, [[$
- | [_Char
| _
] = OptArg
] = OptStr
| Tail
]) ->
82 parse_option_short(OptSpecList
, OptAcc
, ArgAcc
, ArgPos
, Tail
, OptStr
, OptArg
);
83 %% Process non-option arguments.
84 parse(OptSpecList
, OptAcc
, ArgAcc
, ArgPos
, [Arg
| Tail
]) ->
85 case find_non_option_arg(OptSpecList
, ArgPos
) of
86 {value
, OptSpec
} when ?
IS_OPT_SPEC(OptSpec
) ->
87 parse(OptSpecList
, [convert_option_arg(OptSpec
, Arg
) | OptAcc
],
88 ArgAcc
, ArgPos
+ 1, Tail
);
90 parse(OptSpecList
, OptAcc
, [Arg
| ArgAcc
], ArgPos
, Tail
)
92 parse(OptSpecList
, OptAcc
, ArgAcc
, _ArgPos
, []) ->
93 % Once we have completed gathering the options we add the ones that were
94 % not present but had default arguments in the specification.
95 {ok
, {lists:reverse(append_default_options(OptSpecList
, OptAcc
)), lists:reverse(ArgAcc
)}}.
98 %% @doc Parse a long option, add it to the option accumulator and continue
99 %% parsing the rest of the arguments recursively.
100 %% A long option can have the following syntax:
101 %% --foo Single option 'foo', no argument
102 %% --foo=bar Single option 'foo', argument "bar"
103 %% --foo bar Single option 'foo', argument "bar"
104 -spec
parse_option_long([option_spec()], [option()], [string()], integer(), [string()], string(), string()) ->
105 {ok
, {[option()], [string()]}}.
106 parse_option_long(OptSpecList
, OptAcc
, ArgAcc
, ArgPos
, Args
, OptStr
, OptArg
) ->
107 case split_assigned_arg(OptArg
) of
109 % Get option that has its argument within the same string
110 % separated by an equal ('=') character (e.g. "--port=1000").
111 parse_option_assigned_arg(OptSpecList
, OptAcc
, ArgAcc
, ArgPos
, Args
, OptStr
, Long
, Arg
);
114 case lists:keysearch(Long
, ?OPT_LONG
, OptSpecList
) of
115 {value
, {Name
, _Short
, Long
, undefined
, _Help
}} ->
116 parse(OptSpecList
, [Name
| OptAcc
], ArgAcc
, ArgPos
, Args
);
118 {value
, {_Name
, _Short
, Long
, _ArgSpec
, _Help
} = OptSpec
} ->
119 % The option argument string is empty, but the option requires
120 % an argument, so we look into the next string in the list.
121 parse_option_next_arg(OptSpecList
, OptAcc
, ArgAcc
, ArgPos
, Args
, OptSpec
);
123 throw({error
, {invalid_option
, OptStr
}})
128 %% @doc Parse an option where the argument is 'assigned' in the same string using
129 %% the '=' character, add it to the option accumulator and continue parsing the
130 %% rest of the arguments recursively. This syntax is only valid for long options.
131 -spec
parse_option_assigned_arg([option_spec()], [option()], [string()], integer(),
132 [string()], string(), string(), string()) ->
133 {ok
, {[option()], [string()]}}.
134 parse_option_assigned_arg(OptSpecList
, OptAcc
, ArgAcc
, ArgPos
, Args
, OptStr
, Long
, Arg
) ->
135 case lists:keysearch(Long
, ?OPT_LONG
, OptSpecList
) of
136 {value
, {_Name
, _Short
, Long
, ArgSpec
, _Help
} = OptSpec
} ->
139 throw({error
, {invalid_option_arg
, OptStr
}});
141 parse(OptSpecList
, [convert_option_arg(OptSpec
, Arg
) | OptAcc
], ArgAcc
, ArgPos
, Args
)
144 throw({error
, {invalid_option
, OptStr
}})
148 %% @doc Split an option string that may contain an option with its argument
149 %% separated by an equal ('=') character (e.g. "port=1000").
150 -spec
split_assigned_arg(string()) -> {Name
:: string(), Arg
:: string()} | string().
151 split_assigned_arg(OptStr
) ->
152 split_assigned_arg(OptStr
, OptStr
, []).
154 split_assigned_arg(_OptStr
, [$
= | Tail
], Acc
) ->
155 {lists:reverse(Acc
), Tail
};
156 split_assigned_arg(OptStr
, [Char
| Tail
], Acc
) ->
157 split_assigned_arg(OptStr
, Tail
, [Char
| Acc
]);
158 split_assigned_arg(OptStr
, [], _Acc
) ->
162 %% @doc Parse a short option, add it to the option accumulator and continue
163 %% parsing the rest of the arguments recursively.
164 %% A short option can have the following syntax:
165 %% -a Single option 'a', no argument or implicit boolean argument
166 %% -a foo Single option 'a', argument "foo"
167 %% -afoo Single option 'a', argument "foo"
168 %% -abc Multiple options: 'a'; 'b'; 'c'
169 %% -bcafoo Multiple options: 'b'; 'c'; 'a' with argument "foo"
170 -spec
parse_option_short([option_spec()], [option()], [string()], integer(), [string()], string(), string()) ->
171 {ok
, {[option()], [string()]}}.
172 parse_option_short(OptSpecList
, OptAcc
, ArgAcc
, ArgPos
, Args
, OptStr
, [Short
| Arg
]) ->
173 case lists:keysearch(Short
, ?OPT_SHORT
, OptSpecList
) of
174 {value
, {Name
, Short
, _Long
, undefined
, _Help
}} ->
175 parse_option_short(OptSpecList
, [Name
| OptAcc
], ArgAcc
, ArgPos
, Args
, OptStr
, Arg
);
177 {value
, {_Name
, Short
, _Long
, ArgSpec
, _Help
} = OptSpec
} ->
180 % The option argument string is empty, but the option requires
181 % an argument, so we look into the next string in the list.
182 parse_option_next_arg(OptSpecList
, OptAcc
, ArgAcc
, ArgPos
, Args
, OptSpec
);
185 case is_valid_arg(ArgSpec
, Arg
) of
187 parse(OptSpecList
, [convert_option_arg(OptSpec
, Arg
) | OptAcc
], ArgAcc
, ArgPos
, Args
);
189 parse_option_short(OptSpecList
, [convert_option_no_arg(OptSpec
) | OptAcc
], ArgAcc
, ArgPos
, Args
, OptStr
, Arg
)
194 throw({error
, {invalid_option
, OptStr
}})
196 parse_option_short(OptSpecList
, OptAcc
, ArgAcc
, ArgPos
, Args
, _OptStr
, []) ->
197 parse(OptSpecList
, OptAcc
, ArgAcc
, ArgPos
, Args
).
200 %% @doc Retrieve the argument for an option from the next string in the list of
201 %% command-line parameters.
202 parse_option_next_arg(OptSpecList
, OptAcc
, ArgAcc
, ArgPos
, [Arg
| Tail
] = Args
, {Name
, _Short
, _Long
, ArgSpec
, _Help
} = OptSpec
) ->
203 % Special case for booleans: when the next string is an option we assume
204 % the value is 'true'.
205 case (arg_spec_type(ArgSpec
) =:= boolean
) andalso not
is_boolean_arg(Arg
) of
207 parse(OptSpecList
, [{Name
, true
} | OptAcc
], ArgAcc
, ArgPos
, Args
);
209 parse(OptSpecList
, [convert_option_arg(OptSpec
, Arg
) | OptAcc
], ArgAcc
, ArgPos
, Tail
)
211 parse_option_next_arg(OptSpecList
, OptAcc
, ArgAcc
, ArgPos
, [] = Args
, {Name
, _Short
, _Long
, ArgSpec
, _Help
}) ->
212 % Special case for booleans: when the next string is missing we assume the
214 case arg_spec_type(ArgSpec
) of
216 parse(OptSpecList
, [{Name
, true
} | OptAcc
], ArgAcc
, ArgPos
, Args
);
218 throw({error
, {missing_option_arg
, Name
}})
222 %% @doc Find the option for the discrete argument in position specified in the
224 -spec
find_non_option_arg([option_spec()], integer()) -> {value
, option_spec()} | false
.
225 find_non_option_arg([{_Name
, undefined
, undefined
, _ArgSpec
, _Help
} = OptSpec
| _Tail
], 0) ->
227 find_non_option_arg([{_Name
, undefined
, undefined
, _ArgSpec
, _Help
} | Tail
], Pos
) ->
228 find_non_option_arg(Tail
, Pos
- 1);
229 find_non_option_arg([_Head
| Tail
], Pos
) ->
230 find_non_option_arg(Tail
, Pos
);
231 find_non_option_arg([], _Pos
) ->
235 %% @doc Append options that were not present in the command line arguments with
236 %% their default arguments.
237 -spec
append_default_options([option_spec()], [option()]) -> [option()].
238 append_default_options([{Name
, _Short
, _Long
, {_Type
, DefaultArg
}, _Help
} | Tail
], OptAcc
) ->
239 append_default_options(Tail
,
240 case lists:keymember(Name
, 1, OptAcc
) of
242 [{Name
, DefaultArg
} | OptAcc
];
246 %% For options with no default argument.
247 append_default_options([_Head
| Tail
], OptAcc
) ->
248 append_default_options(Tail
, OptAcc
);
249 append_default_options([], OptAcc
) ->
253 -spec
convert_option_no_arg(option_spec()) -> compound_option().
254 convert_option_no_arg({Name
, _Short
, _Long
, ArgSpec
, _Help
}) ->
256 % Special case for booleans: if there is no argument we assume
257 % the value is 'true'.
258 {boolean
, _DefaultValue
} ->
263 throw({error
, {missing_option_arg
, Name
}})
267 %% @doc Convert the argument passed in the command line to the data type
268 %% indicated by the argument specification.
269 -spec
convert_option_arg(option_spec(), string()) -> compound_option().
270 convert_option_arg({Name
, _Short
, _Long
, ArgSpec
, _Help
}, Arg
) ->
272 {Name
, to_type(arg_spec_type(ArgSpec
), Arg
)}
275 throw({error
, {invalid_option_arg
, {Name
, Arg
}}})
279 %% @doc Retrieve the data type form an argument specification.
280 -spec
arg_spec_type(arg_spec()) -> arg_type() | undefined
.
281 arg_spec_type({Type
, _DefaultArg
}) ->
283 arg_spec_type(Type
) when is_atom(Type
) ->
287 %% @doc Convert an argument string to its corresponding data type.
288 -spec
to_type(arg_type(), string()) -> arg_value().
289 to_type(binary, Arg
) ->
291 to_type(atom, Arg
) ->
293 to_type(integer, Arg
) ->
294 list_to_integer(Arg
);
295 to_type(float, Arg
) ->
297 to_type(boolean
, Arg
) ->
298 LowerArg
= string:to_lower(Arg
),
299 case is_arg_true(LowerArg
) of
303 case is_arg_false(LowerArg
) of
310 to_type(_Type
, Arg
) ->
314 -spec
is_arg_true(string()) -> boolean().
316 (Arg
=:= "true") orelse (Arg
=:= "t") orelse
317 (Arg
=:= "yes") orelse (Arg
=:= "y") orelse
318 (Arg
=:= "on") orelse (Arg
=:= "enabled") orelse
322 -spec
is_arg_false(string()) -> boolean().
324 (Arg
=:= "false") orelse (Arg
=:= "f") orelse
325 (Arg
=:= "no") orelse (Arg
=:= "n") orelse
326 (Arg
=:= "off") orelse (Arg
=:= "disabled") orelse
330 -spec
is_valid_arg(arg_spec(), nonempty_string()) -> boolean().
331 is_valid_arg({Type
, _DefaultArg
}, Arg
) ->
332 is_valid_arg(Type
, Arg
);
333 is_valid_arg(boolean
, Arg
) ->
335 is_valid_arg(integer, Arg
) ->
337 is_valid_arg(float, Arg
) ->
339 is_valid_arg(_Type
, _Arg
) ->
343 -spec
is_boolean_arg(string()) -> boolean().
344 is_boolean_arg(Arg
) ->
345 LowerArg
= string:to_lower(Arg
),
346 is_arg_true(LowerArg
) orelse
is_arg_false(LowerArg
).
349 -spec
is_integer_arg(string()) -> boolean().
350 is_integer_arg([Head
| Tail
]) when Head
>= $
0, Head
=< $
9 ->
351 is_integer_arg(Tail
);
352 is_integer_arg([_Head
| _Tail
]) ->
354 is_integer_arg([]) ->
358 -spec
is_float_arg(string()) -> boolean().
359 is_float_arg([Head
| Tail
]) when (Head
>= $
0 andalso Head
=< $
9) orelse Head
=:= $
. ->
361 is_float_arg([_Head
| _Tail
]) ->
367 %% @doc Show a message on stdout indicating the command line options and
368 %% arguments that are supported by the program.
369 -spec
usage([option_spec()], string()) -> ok
.
370 usage(OptSpecList
, ProgramName
) ->
371 io:format("Usage: ~s~s~n~n~s~n",
372 [ProgramName
, usage_cmd_line(OptSpecList
), usage_options(OptSpecList
)]).
375 %% @doc Show a message on stdout indicating the command line options and
376 %% arguments that are supported by the program. The CmdLineTail argument
377 %% is a string that is added to the end of the usage command line.
378 -spec
usage([option_spec()], string(), string()) -> ok
.
379 usage(OptSpecList
, ProgramName
, CmdLineTail
) ->
380 io:format("Usage: ~s~s ~s~n~n~s~n",
381 [ProgramName
, usage_cmd_line(OptSpecList
), CmdLineTail
, usage_options(OptSpecList
)]).
384 %% @doc Show a message on stdout indicating the command line options and
385 %% arguments that are supported by the program. The CmdLineTail and OptionsTail
386 %% arguments are a string that is added to the end of the usage command line
387 %% and a list of tuples that are added to the end of the options' help lines.
388 -spec
usage([option_spec()], string(), string(), [{string(), string()}]) -> ok
.
389 usage(OptSpecList
, ProgramName
, CmdLineTail
, OptionsTail
) ->
390 UsageOptions
= lists:foldl(
391 fun ({Prefix
, Help
}, Acc
) ->
392 add_option_help(Prefix
, Help
, Acc
)
393 end, usage_options_reverse(OptSpecList
, []), OptionsTail
),
394 io:format("Usage: ~s~s ~s~n~n~s~n",
395 [ProgramName
, usage_cmd_line(OptSpecList
), CmdLineTail
,
396 lists:flatten(lists:reverse(UsageOptions
))]).
399 %% @doc Return a string with the syntax for the command line options and
401 -spec
usage_cmd_line([option_spec()]) -> string().
402 usage_cmd_line(OptSpecList
) ->
403 usage_cmd_line(OptSpecList
, []).
405 usage_cmd_line([{Name
, Short
, Long
, ArgSpec
, _Help
} | Tail
], Acc
) ->
410 % For options with short form and no argument.
411 Short
=/= undefined
->
412 [$\s
, $
[, $
-, Short
, $
]];
413 % For options with only long form and no argument.
414 Long
=/= undefined
->
415 [$\s
, $
[, $
-, $
-, Long
, $
]];
421 % For options with short form and argument.
422 Short
=/= undefined
->
423 [$\s
, $
[, $
-, Short
, $\s
, $
<, atom_to_list(Name
), $
>, $
]];
424 % For options with only long form and argument.
425 Long
=/= undefined
->
426 [$\s
, $
[, $
-, $
-, Long
, $\s
, $
<, atom_to_list(Name
), $
>, $
]];
427 % For options with neither short nor long form and argument.
429 [$\s
, $
<, atom_to_list(Name
), $
>]
432 usage_cmd_line(Tail
, [CmdLine
| Acc
]);
433 usage_cmd_line([], Acc
) ->
434 lists:flatten(lists:reverse(Acc
)).
437 %% @doc Return a string with the help message for each of the options and
439 -spec
usage_options([option_spec()]) -> string().
440 usage_options(OptSpecList
) ->
441 lists:flatten(lists:reverse(usage_options_reverse(OptSpecList
, []))).
443 usage_options_reverse([{Name
, Short
, Long
, _ArgSpec
, Help
} | Tail
], Acc
) ->
448 % Neither short nor long form (non-option argument).
450 [$
<, atom_to_list(Name
), $
>];
460 % Both short and long form.
462 [$
-, Short
, $
,, $\s
, $
-, $
- | Long
]
465 usage_options_reverse(Tail
, add_option_help(Prefix
, Help
, Acc
));
466 usage_options_reverse([], Acc
) ->
470 %% @doc Add the help message corresponding to an option specification to a list
471 %% with the correct indentation.
472 -spec
add_option_help(Prefix
:: string(), Help
:: string(), Acc
:: string()) -> string().
473 add_option_help(Prefix
, Help
, Acc
) when is_list(Help
), Help
=/= [] ->
474 FlatPrefix
= lists:flatten(Prefix
),
475 case ((?INDENTATION
* ?TAB_LENGTH
) - 2 - length(FlatPrefix
)) of
476 TabSize
when TabSize
> 0 ->
477 Tab
= lists:duplicate(ceiling(TabSize
/ ?TAB_LENGTH
), $
\t),
478 [[$\s
, $\s
, FlatPrefix
, Tab
, Help
, $
\n] | Acc
];
480 % The indentation for the option description is 3 tabs (i.e. 24 characters)
481 % IMPORTANT: Change the number of tabs below if you change the
482 % value of the INDENTATION macro.
483 [[$
\t, $
\t, $
\t, Help
, $
\n], [$\s
, $\s
, FlatPrefix
, $
\n] | Acc
]
485 add_option_help(_Opt
, _Prefix
, Acc
) ->
490 %% @doc Return the smallest integral value not less than the argument.
491 -spec
ceiling(float()) -> integer().
495 % Neg when Neg < 0 ->