cli: allow the GNU option format

$ nmcli --fields=all c
  Error: Option '-fields=all' is unknown, try 'nmcli -help'.

What a shame. Let's fix this.
This commit is contained in:
Lubomir Rintel 2017-05-15 19:19:49 +02:00
parent 24c079e4b2
commit 7c24a2cb39

View file

@ -93,7 +93,9 @@ complete_field (GHashTable *h, const NmcMetaGenericInfo *const*field)
static void static void
complete_one (gpointer key, gpointer value, gpointer user_data) complete_one (gpointer key, gpointer value, gpointer user_data)
{ {
const char *prefix = user_data; const char **option_with_value = user_data;
const char *option = option_with_value[0];
const char *prefix = option_with_value[1];
const char *name = key; const char *name = key;
const char *last; const char *last;
@ -104,16 +106,23 @@ complete_one (gpointer key, gpointer value, gpointer user_data)
last = prefix; last = prefix;
if ((!*last && !strchr (name, '.')) || matches (last, name)) { if ((!*last && !strchr (name, '.')) || matches (last, name)) {
if (option != prefix) {
/* value prefix was not a standalone argument,
* it was part of --option=<value> argument.
* Repeat the part leading to "=". */
g_print ("%s=", option);
}
g_print ("%.*s%s%s\n", (int)(last-prefix), prefix, name, g_print ("%.*s%s%s\n", (int)(last-prefix), prefix, name,
strcmp (last, name) == 0 ? "," : ""); strcmp (last, name) == 0 ? "," : "");
} }
} }
static void static void
complete_fields (const char *prefix) complete_fields (const char *option, const char *prefix)
{ {
guint i; guint i;
GHashTable *h; GHashTable *h;
const char *option_with_value[2] = { option, prefix };
h = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); h = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
@ -144,10 +153,31 @@ complete_fields (const char *prefix)
for (i = 0; i < _NM_META_SETTING_TYPE_NUM; i++) for (i = 0; i < _NM_META_SETTING_TYPE_NUM; i++)
complete_field_setting (h, i); complete_field_setting (h, i);
g_hash_table_foreach (h, complete_one, (gpointer) prefix); g_hash_table_foreach (h, complete_one, (gpointer) &option_with_value[0]);
g_hash_table_destroy (h); g_hash_table_destroy (h);
} }
static void
complete_option_with_value (const char *option, const char *prefix, ...)
{
va_list args;
const char *candidate;
va_start (args, prefix);
while ((candidate = va_arg (args, const char *))) {
if (!*prefix || matches (prefix, candidate)) {
if (option != prefix) {
/* value prefix was not a standalone argument,
* it was part of --option=<value> argument.
* Repeat the part leading to "=". */
g_print ("%s=", option);
}
g_print ("%s\n", candidate);
}
}
va_end (args);
}
static void static void
usage (void) usage (void)
{ {
@ -189,6 +219,57 @@ static const NMCCommand nmcli_cmds[] = {
{ NULL, do_overview, usage, TRUE, TRUE }, { NULL, do_overview, usage, TRUE, TRUE },
}; };
static gboolean
matches_arg (NmCli *nmc, int *argc, char ***argv, const char *pattern, char **arg)
{
char *opt = *argv[0];
if (nmc->return_value != NMC_RESULT_SUCCESS) {
/* Don't process further matches if there has been an error. */
return FALSE;
}
if (opt[1] == '-') {
/* We know one '-' was already seen by the caller.
* Skip it if there's a second one*/
opt++;
}
if (arg) {
/* If there's a "=" separator, replace it with NUL so that matches()
* works and consider the part after it to be the arguemnt's value. */
*arg = strchr (opt, '=');
if (*arg) {
**arg = '\0';
(*arg)++;
}
}
if (!matches (opt, pattern)) {
if (arg && *arg) {
/* Back off the replacement of "=". */
(*arg)--;
**arg = '=';
}
return FALSE;
}
if (arg && !*arg) {
/* We need a value, but the option didn't contain a "=<value>" part.
* Proceed to the next argument. */
(*argc)--;
(*argv)++;
if (!*argc) {
g_string_printf (nmc->return_text, _("Error: missing argument for '%s' option."), opt);
nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
return FALSE;
}
*arg = *argv[0];
}
return TRUE;
}
static gboolean static gboolean
process_command_line (NmCli *nmc, int argc, char **argv) process_command_line (NmCli *nmc, int argc, char **argv)
{ {
@ -208,26 +289,24 @@ process_command_line (NmCli *nmc, int argc, char **argv)
/* parse options */ /* parse options */
while (argc) { while (argc) {
char *opt = argv[0]; char *value;
if (opt[0] != '-')
if (argv[0][0] != '-')
break; break;
if (argc == 1 && nmc->complete) { if (argc == 1 && nmc->complete) {
nmc_complete_strings (opt, "--terse", "--pretty", "--mode", "--colors", "--escape", nmc_complete_strings (argv[0], "--terse", "--pretty", "--mode", "--colors", "--escape",
"--fields", "--nocheck", "--get-values", "--fields", "--nocheck", "--get-values",
"--wait", "--version", "--help", NULL); "--wait", "--version", "--help", NULL);
} }
if (opt[1] == '-') { if (argv[0][1] == '-' && argv[0][2] == '\0') {
opt++;
/* '--' ends options */ /* '--' ends options */
if (opt[1] == '\0') { next_arg (nmc, &argc, &argv, NULL);
next_arg (nmc, &argc, &argv, NULL); break;
break;
}
} }
if (matches (opt, "-terse")) { if (matches_arg (nmc, &argc, &argv, "-terse", NULL)) {
if (nmc->nmc_config.print_output == NMC_PRINT_TERSE) { if (nmc->nmc_config.print_output == NMC_PRINT_TERSE) {
g_string_printf (nmc->return_text, _("Error: Option '--terse' is specified the second time.")); g_string_printf (nmc->return_text, _("Error: Option '--terse' is specified the second time."));
nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
@ -240,7 +319,7 @@ process_command_line (NmCli *nmc, int argc, char **argv)
} }
else else
nmc->nmc_config_mutable.print_output = NMC_PRINT_TERSE; nmc->nmc_config_mutable.print_output = NMC_PRINT_TERSE;
} else if (matches (opt, "-pretty")) { } else if (matches_arg (nmc, &argc, &argv, "-pretty", NULL)) {
if (nmc->nmc_config.print_output == NMC_PRINT_PRETTY) { if (nmc->nmc_config.print_output == NMC_PRINT_PRETTY) {
g_string_printf (nmc->return_text, _("Error: Option '--pretty' is specified the second time.")); g_string_printf (nmc->return_text, _("Error: Option '--pretty' is specified the second time."));
nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
@ -253,126 +332,86 @@ process_command_line (NmCli *nmc, int argc, char **argv)
} }
else else
nmc->nmc_config_mutable.print_output = NMC_PRINT_PRETTY; nmc->nmc_config_mutable.print_output = NMC_PRINT_PRETTY;
} else if (matches (opt, "-mode")) { } else if (matches_arg (nmc, &argc, &argv, "-mode", &value)) {
nmc->mode_specified = TRUE; nmc->mode_specified = TRUE;
argc--;
argv++;
if (!argc) {
g_string_printf (nmc->return_text, _("Error: missing argument for '%s' option."), opt);
nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
return FALSE;
}
if (argc == 1 && nmc->complete) if (argc == 1 && nmc->complete)
nmc_complete_strings (argv[0], "tabular", "multiline", NULL); complete_option_with_value (argv[0], value, "tabular", "multiline", NULL);
if (matches (argv[0], "tabular")) if (matches (value, "tabular"))
nmc->nmc_config_mutable.multiline_output = FALSE; nmc->nmc_config_mutable.multiline_output = FALSE;
else if (matches (argv[0], "multiline")) else if (matches (value, "multiline"))
nmc->nmc_config_mutable.multiline_output = TRUE; nmc->nmc_config_mutable.multiline_output = TRUE;
else { else {
g_string_printf (nmc->return_text, _("Error: '%s' is not valid argument for '%s' option."), argv[0], opt); g_string_printf (nmc->return_text, _("Error: '%s' is not a valid argument for '%s' option."), value, argv[0]);
nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
return FALSE;
}
} else if (matches (opt, "-colors")) {
argc--;
argv++;
if (!argc) {
g_string_printf (nmc->return_text, _("Error: missing argument for '%s' option."), opt);
nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
return FALSE; return FALSE;
} }
} else if (matches_arg (nmc, &argc, &argv, "-colors", &value)) {
if (argc == 1 && nmc->complete) if (argc == 1 && nmc->complete)
nmc_complete_strings (argv[0], "yes", "no", "auto", NULL); complete_option_with_value (argv[0], value, "yes", "no", "auto", NULL);
if (matches (argv[0], "auto")) if (matches (value, "auto"))
nmc->nmc_config_mutable.use_colors = NMC_USE_COLOR_AUTO; nmc->nmc_config_mutable.use_colors = NMC_USE_COLOR_AUTO;
else if (matches (argv[0], "yes")) else if (matches (value, "yes"))
nmc->nmc_config_mutable.use_colors = NMC_USE_COLOR_YES; nmc->nmc_config_mutable.use_colors = NMC_USE_COLOR_YES;
else if (matches (argv[0], "no")) else if (matches (value, "no"))
nmc->nmc_config_mutable.use_colors = NMC_USE_COLOR_NO; nmc->nmc_config_mutable.use_colors = NMC_USE_COLOR_NO;
else { else {
g_string_printf (nmc->return_text, _("Error: '%s' is not valid argument for '%s' option."), argv[0], opt); g_string_printf (nmc->return_text, _("Error: '%s' is not valid argument for '%s' option."), value, argv[0]);
nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
return FALSE;
}
} else if (matches (opt, "-escape")) {
argc--;
argv++;
if (!argc) {
g_string_printf (nmc->return_text, _("Error: missing argument for '%s' option."), opt);
nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
return FALSE; return FALSE;
} }
} else if (matches_arg (nmc, &argc, &argv, "-escape", &value)) {
if (argc == 1 && nmc->complete) if (argc == 1 && nmc->complete)
nmc_complete_strings (argv[0], "yes", "no", NULL); complete_option_with_value (argv[0], value, "yes", "no", NULL);
if (matches (argv[0], "yes")) if (matches (value, "yes"))
nmc->nmc_config_mutable.escape_values = TRUE; nmc->nmc_config_mutable.escape_values = TRUE;
else if (matches (argv[0], "no")) else if (matches (value, "no"))
nmc->nmc_config_mutable.escape_values = FALSE; nmc->nmc_config_mutable.escape_values = FALSE;
else { else {
g_string_printf (nmc->return_text, _("Error: '%s' is not valid argument for '%s' option."), argv[0], opt); g_string_printf (nmc->return_text, _("Error: '%s' is not valid argument for '%s' option."), value, argv[0]);
nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
return FALSE;
}
} else if (matches (opt, "-fields")) {
argc--;
argv++;
if (!argc) {
g_string_printf (nmc->return_text, _("Error: fields for '%s' options are missing."), opt);
nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
return FALSE; return FALSE;
} }
} else if (matches_arg (nmc, &argc, &argv, "-fields", &value)) {
if (argc == 1 && nmc->complete) if (argc == 1 && nmc->complete)
complete_fields (argv[0]); complete_fields (argv[0], value);
nmc->required_fields = g_strdup (argv[0]); nmc->required_fields = g_strdup (value);
} else if (matches (opt, "-get-values")) { } else if (matches_arg (nmc, &argc, &argv, "-get-values", &value)) {
argc--;
argv++;
if (!argc) {
g_string_printf (nmc->return_text, _("Error: fields for '%s' options are missing."), opt);
nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
return FALSE;
}
if (argc == 1 && nmc->complete) if (argc == 1 && nmc->complete)
complete_fields (argv[0]); complete_fields (argv[0], value);
nmc->required_fields = g_strdup (argv[0]); nmc->required_fields = g_strdup (value);
nmc->nmc_config_mutable.print_output = NMC_PRINT_TERSE; nmc->nmc_config_mutable.print_output = NMC_PRINT_TERSE;
/* We want fixed tabular mode here, but just set the mode specified and rely on the initialization /* We want fixed tabular mode here, but just set the mode specified and rely on the initialization
* in nmc_init: in this way we allow use of "-m multiline" to swap the output mode also if placed * in nmc_init: in this way we allow use of "-m multiline" to swap the output mode also if placed
* before the "-g <field>" option (-g may be still more practical and easy to remember than -t -f). * before the "-g <field>" option (-g may be still more practical and easy to remember than -t -f).
*/ */
nmc->mode_specified = TRUE; nmc->mode_specified = TRUE;
} else if (matches (opt, "-nocheck")) { } else if (matches_arg (nmc, &argc, &argv, "-nocheck", NULL)) {
/* ignore for backward compatibility */ /* ignore for backward compatibility */
} else if (matches (opt, "-wait")) { } else if (matches_arg (nmc, &argc, &argv, "-wait", &value)) {
unsigned long timeout; unsigned long timeout;
argc--; if (!nmc_string_to_uint (value, TRUE, 0, G_MAXINT, &timeout)) {
argv++; g_string_printf (nmc->return_text, _("Error: '%s' is not a valid timeout."), value);
if (!argc) {
g_string_printf (nmc->return_text, _("Error: missing argument for '%s' option."), opt);
nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
return FALSE;
}
if (!nmc_string_to_uint (argv[0], TRUE, 0, G_MAXINT, &timeout)) {
g_string_printf (nmc->return_text, _("Error: '%s' is not a valid timeout for '%s' option."),
argv[0], opt);
nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
return FALSE; return FALSE;
} }
nmc->timeout = (int) timeout; nmc->timeout = (int) timeout;
} else if (matches (opt, "-version")) { } else if (matches_arg (nmc, &argc, &argv, "-version", NULL)) {
if (!nmc->complete) if (!nmc->complete)
g_print (_("nmcli tool, version %s\n"), NMCLI_VERSION); g_print (_("nmcli tool, version %s\n"), NMCLI_VERSION);
return NMC_RESULT_SUCCESS; return NMC_RESULT_SUCCESS;
} else if (matches (opt, "-help")) { } else if (matches_arg (nmc, &argc, &argv, "-help", NULL)) {
if (!nmc->complete) if (!nmc->complete)
usage (); usage ();
return NMC_RESULT_SUCCESS; return NMC_RESULT_SUCCESS;
} else { } else {
g_string_printf (nmc->return_text, _("Error: Option '%s' is unknown, try 'nmcli -help'."), opt); if (nmc->return_value == NMC_RESULT_SUCCESS) {
nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; g_string_printf (nmc->return_text, _("Error: Option '%s' is unknown, try 'nmcli -help'."), argv[0]);
nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
}
return FALSE; return FALSE;
} }
next_arg (nmc, &argc, &argv, NULL); next_arg (nmc, &argc, &argv, NULL);
} }