diff --git a/include/crm/common/cmdline_internal.h b/include/crm/common/cmdline_internal.h index bbe8ff71f5..cbed6d63f4 100644 --- a/include/crm/common/cmdline_internal.h +++ b/include/crm/common/cmdline_internal.h @@ -1,77 +1,91 @@ /* * Copyright 2019 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU Lesser General Public License * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. */ #ifndef CMDLINE_INTERNAL__H #define CMDLINE_INTERNAL__H #ifdef __cplusplus extern "C" { #endif #include typedef struct { char *summary; gboolean version; gboolean quiet; unsigned int verbosity; char *output_ty; - char *output_ty_desc; char *output_dest; } pcmk__common_args_t; +/*! + * \internal + * \brief Create and return a GOptionContext containing the command line options + * supported by all tools. + * + * \note Formatted output options will be added unless fmts is NULL. This allows + * for using this function in tools that have not yet been converted to + * formatted output. It should not be NULL in any tool that calls + * pcmk__register_formats() as that function adds its own command line + * options. + * + * \param[in,out] common_args A ::pcmk__common_args_t structure where the + * results of handling command options will be written. + * \param[in] fmts The help string for which formats are supported. + */ GOptionContext * pcmk__build_arg_context(pcmk__common_args_t *common_args, const char *fmts); /*! * \internal * \brief Pre-process command line arguments to preserve compatibility with * getopt behavior. * * getopt and glib have slightly different behavior when it comes to processing * single command line arguments. getopt allows this: -x, while glib will * try to handle like it is additional single letter arguments. glib * prefers -x instead. * * This function scans argv, looking for any single letter command line options * (indicated by the 'special' parameter). When one is found, everything after * that argument to the next whitespace is converted into its own value. Single * letter command line options can come in a group after a single dash, but * this function will expand each group into many arguments. * * Long options and anything after "--" is preserved. The result of this function * can then be passed to ::g_option_context_parse_strv for actual processing. * * In pseudocode, this: * * pcmk__cmdline_preproc(4, ["-XbA", "--blah=foo", "-aF", "-Fval", "--", "--extra", "-args"], "aF") * * Would be turned into this: * * ["-X", "-b", "-A", "--blah=foo", "-a", "F", "-F", "val", "--", "--extra", "-args"] * * This function does not modify argv, and the return value is built of copies * of all the command line arguments. It is up to the caller to free this memory * after use. * * \param[in] argc The length of argv. * \param[in] argv The command line arguments. * \param[in] special Single-letter command line arguments that take a value. * These letters will all have pre-processing applied. */ char ** pcmk__cmdline_preproc(int argc, char **argv, const char *special); #ifdef __cplusplus } #endif #endif diff --git a/lib/common/cmdline.c b/lib/common/cmdline.c index c4c5848f47..b2eb707fae 100644 --- a/lib/common/cmdline.c +++ b/lib/common/cmdline.c @@ -1,151 +1,157 @@ /* * Copyright 2019 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU Lesser General Public License * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. */ #include #include #include #include static gboolean bump_verbosity(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) { pcmk__common_args_t *common_args = (pcmk__common_args_t *) data; common_args->verbosity++; return TRUE; } static void free_common_args(gpointer data) { pcmk__common_args_t *common_args = (pcmk__common_args_t *) data; free(common_args->summary); free(common_args->output_ty); - free(common_args->output_ty_desc); free(common_args->output_dest); } GOptionContext * pcmk__build_arg_context(pcmk__common_args_t *common_args, const char *fmts) { char *desc = crm_strdup_printf("Report bugs to %s\n", PACKAGE_BUGREPORT); GOptionContext *context; GOptionGroup *main_group; - GOptionEntry main_entries[5] = { + GOptionEntry main_entries[3] = { { "version", '$', 0, G_OPTION_ARG_NONE, &(common_args->version), "Display version information and exit.", NULL }, { "verbose", 'V', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, bump_verbosity, "Increase debug output (may be specified multiple times).", NULL }, - { "output-as", 0, 0, G_OPTION_ARG_STRING, &(common_args->output_ty), - NULL, - "FORMAT" }, - { "output-to", 0, 0, G_OPTION_ARG_STRING, &(common_args->output_dest), - "Specify the destination for output, \"-\" for stdout or a filename.", "DEST" }, { NULL } }; - common_args->output_ty_desc = crm_strdup_printf("Specify the format for output, one of: %s", fmts); - main_entries[2].description = common_args->output_ty_desc; - main_group = g_option_group_new(NULL, "Application Options:", NULL, common_args, free_common_args); g_option_group_add_entries(main_group, main_entries); context = g_option_context_new(NULL); g_option_context_set_summary(context, common_args->summary); g_option_context_set_description(context, desc); g_option_context_set_main_group(context, main_group); + if (fmts != NULL) { + GOptionEntry output_main_entries[3] = { + { "output-as", 0, 0, G_OPTION_ARG_STRING, &(common_args->output_ty), + NULL, + "FORMAT" }, + { "output-to", 0, 0, G_OPTION_ARG_STRING, &(common_args->output_dest), + "Specify the destination for output, \"-\" for stdout or a filename.", "DEST" }, + + { NULL } + }; + + output_main_entries[0].description = crm_strdup_printf("Specify the format for output, one of: %s", fmts); + g_option_context_add_main_entries(context, output_main_entries, NULL); + } + free(desc); return context; } char ** pcmk__cmdline_preproc(int argc, char **argv, const char *special) { char **retval = NULL; GPtrArray *arr = g_ptr_array_new(); bool saw_dash_dash = false; for (int i = 0; i < argc; i++) { /* If this is the first time we saw "--" in the command line, set * a flag so we know to just copy everything after it over. We also * want to copy the "--" over so whatever actually parses the command * line when we're done knows where arguments end. */ if (saw_dash_dash == false && strcmp(argv[i], "--") == 0) { saw_dash_dash = true; } if (saw_dash_dash == true) { g_ptr_array_add(arr, strdup(argv[i])); continue; } /* This is a short argument, or perhaps several. Iterate over it * and explode them out into individual arguments. */ if (g_str_has_prefix(argv[i], "-") && !g_str_has_prefix(argv[i], "--")) { /* Skip over leading dash */ char *ch = argv[i]+1; while (*ch != '\0') { /* This is a special short argument that takes an option. getopt * allows values to be interspersed with a list of arguments, but * glib does not. Grab both the argument and its value and * separate them into a new argument. */ if (strchr(special, *ch) != NULL) { /* The argument does not occur at the end of this string of * arguments. Take everything through the end as its value. */ if (*(ch+1) != '\0') { g_ptr_array_add(arr, (gpointer) crm_strdup_printf("-%c", *ch)); g_ptr_array_add(arr, strdup(ch+1)); break; /* The argument occurs at the end of this string. Hopefully * whatever comes next in argv is its value. It may not be, * but that is not for us to decide. */ } else { g_ptr_array_add(arr, (gpointer) crm_strdup_printf("-%c", *ch)); ch++; } /* This is a regular short argument. Just copy it over. */ } else { g_ptr_array_add(arr, (gpointer) crm_strdup_printf("-%c", *ch)); ch++; } } /* This is a long argument, or an option, or something else. * Copy it over - everything else is copied, so this keeps it easy for * the caller to know what to do with the memory when it's done. */ } else { g_ptr_array_add(arr, strdup(argv[i])); } } /* Convert the GPtrArray into a char **, which the command line parsing * code knows how to deal with. Then we can free the array (but not its * contents). */ retval = calloc(arr->len+1, sizeof(char *)); for (int i = 0; i < arr->len; i++) { retval [i] = (char *) g_ptr_array_index(arr, i); } g_ptr_array_free(arr, FALSE); return retval; }