getopt: add positional argument handling.

positional arguments must appear at the end of the description array
(after the last option) and should have a metavar set and not have
shortname or longname. Simple, elegant and fit :-)

There is a new function to parse the positional arguments,
ecore_getopt_parse_positional() because we may want to not try to
parse them in the case of a quit-option such as --help, --license,
--copyright, --version or some user-defined action. This avoids us
producing errors of missing positional arguments when printing help
and adds some flexibility as well.

This should make Tasn happy :-)
This commit is contained in:
Gustavo Sverzut Barbieri 2013-12-17 21:06:53 -02:00
parent d32e6e0500
commit 3bc8f09733
1 changed files with 80 additions and 4 deletions

View File

@ -5,6 +5,17 @@
#include <Ecore_Getopt.h> #include <Ecore_Getopt.h>
#include <assert.h> #include <assert.h>
/* if defined will end the positional arguments with the special
* action ECORE_GETOPT_ACTION_APPEND that will require at least one
* trailing argument and will also consume the remaining arguments
* until the end.
*
* if not defined unhandled positional arguments start at the index
* returned by ecore_getopt_parse_positional(), that will be less or
* equal to argc.
*/
#define END_WITH_POS_APPEND 1
static const char * available_choices[] = { static const char * available_choices[] = {
"banana", "banana",
"apple", "apple",
@ -15,8 +26,8 @@ static const char * available_choices[] = {
static const Ecore_Getopt options = { static const Ecore_Getopt options = {
/* program name, usually a macro PACKAGE_NAME */ /* program name, usually a macro PACKAGE_NAME */
"ecore_getopt_example", "ecore_getopt_example",
/* usage line */ /* usage line, leave empty to generate one with positional arguments */
"%prog [options]", NULL,
/* program version, usually a macro PACKAGE_VERSION */ /* program version, usually a macro PACKAGE_VERSION */
"0.1", "0.1",
/* copyright string */ /* copyright string */
@ -136,6 +147,25 @@ static const Ecore_Getopt options = {
ECORE_GETOPT_LICENSE('L', "license"), ECORE_GETOPT_LICENSE('L', "license"),
ECORE_GETOPT_HELP('h', "help"), ECORE_GETOPT_HELP('h', "help"),
/* positional arguments can be handled as well, add their
* description after the last option was specified. They should
* have empty short and long options, but have the metavar
* defined.
*/
ECORE_GETOPT_STORE_METAVAR_STR(0, NULL, "Positional string.", "STRING"),
ECORE_GETOPT_STORE_METAVAR_INT(0, NULL, "Positional integer.", "INT"),
ECORE_GETOPT_CHOICE_METAVAR(0, NULL, "Positional choice.", "CHOICE",
available_choices),
#ifdef END_WITH_POS_APPEND
/* this will consume until the end of the command line, forcing
* ecore_getopt_parse() to return args == argc on succes.
* It will require at least one argument in the end of the command line.
*/
ECORE_GETOPT_APPEND_METAVAR(0, NULL, "Extra options.", "ARG",
ECORE_GETOPT_TYPE_STR),
#endif
/* the sentinel is required to notify end of descriptions */ /* the sentinel is required to notify end of descriptions */
ECORE_GETOPT_SENTINEL ECORE_GETOPT_SENTINEL
} }
@ -172,6 +202,12 @@ main(int argc, char **argv)
Eina_List *lst_ints = NULL; Eina_List *lst_ints = NULL;
Eina_Bool break_given = EINA_FALSE; Eina_Bool break_given = EINA_FALSE;
Eina_Bool quit_option = EINA_FALSE; Eina_Bool quit_option = EINA_FALSE;
char *pos_str = NULL;
int pos_int = 0;
char *pos_choice = NULL;
#ifdef END_WITH_POS_APPEND
Eina_List *pos_args = NULL;
#endif
Ecore_Getopt_Value values[] = { Ecore_Getopt_Value values[] = {
/* block of options that store a single value in a variable of type */ /* block of options that store a single value in a variable of type */
ECORE_GETOPT_VALUE_STR(str_value), ECORE_GETOPT_VALUE_STR(str_value),
@ -218,6 +254,15 @@ main(int argc, char **argv)
ECORE_GETOPT_VALUE_BOOL(quit_option), /* -L/--license quits */ ECORE_GETOPT_VALUE_BOOL(quit_option), /* -L/--license quits */
ECORE_GETOPT_VALUE_BOOL(quit_option), /* -h/--help quits */ ECORE_GETOPT_VALUE_BOOL(quit_option), /* -h/--help quits */
/* example of positiona argument */
ECORE_GETOPT_VALUE_STR(pos_str),
ECORE_GETOPT_VALUE_INT(pos_int),
ECORE_GETOPT_VALUE_STR(pos_choice),
#ifdef END_WITH_POS_APPEND
ECORE_GETOPT_VALUE_LIST(pos_args),
#endif
ECORE_GETOPT_VALUE_NONE /* sentinel */ ECORE_GETOPT_VALUE_NONE /* sentinel */
}; };
int args, retval = EXIT_SUCCESS; int args, retval = EXIT_SUCCESS;
@ -240,6 +285,14 @@ main(int argc, char **argv)
/* options that set 'quit_option' to true requires us to exit. */ /* options that set 'quit_option' to true requires us to exit. */
if (quit_option) goto end; if (quit_option) goto end;
args = ecore_getopt_parse_positional(&options, values, argc, argv, args);
if (args < 0)
{
fputs("ERROR: Could not parse positional arguments.\n", stderr);
retval = EXIT_FAILURE;
goto end;
}
printf("given values:\n" printf("given values:\n"
"string = %s\n" "string = %s\n"
"bool = %s\n" "bool = %s\n"
@ -269,6 +322,10 @@ main(int argc, char **argv)
"choice = %s\n" "choice = %s\n"
"\n" "\n"
"--break = %s\n" "--break = %s\n"
"\nDeclared Positional:\n"
"STRING = %s\n"
"INT = %d\n"
"CHOICE = %s\n"
"\n", "\n",
str_value, str_value,
bool_value ? "true" : "false", bool_value ? "true" : "false",
@ -294,7 +351,10 @@ main(int argc, char **argv)
use_z, use_z,
count, count,
choice, choice,
break_given ? "given" : "omitted"); break_given ? "given" : "omitted",
pos_str,
pos_int,
pos_choice);
if (!lst_strs) if (!lst_strs)
puts("no --append-string=VALUE was given."); puts("no --append-string=VALUE was given.");
@ -324,14 +384,30 @@ main(int argc, char **argv)
} }
} }
#ifdef END_WITH_POS_APPEND
assert(pos_args != NULL);
assert(args == argc);
if (1)
{
char *str;
printf("%u extra arguments:\n",
eina_list_count(pos_args));
EINA_LIST_FREE(pos_args, str)
{
printf("\t%s\n", str);
free(str);
}
}
#else
if (args == argc) if (args == argc)
puts("no positional arguments were given."); puts("no extra positional arguments were given.");
else else
{ {
printf("%d positional arguments were given:\n", argc - args); printf("%d positional arguments were given:\n", argc - args);
for (; args < argc; args++) for (; args < argc; args++)
printf("\t%s\n", argv[args]); printf("\t%s\n", argv[args]);
} }
#endif
end: end:
ecore_shutdown(); ecore_shutdown();