Page MenuHomeClusterLabs Projects

cmdline.c
No OneTemporary

cmdline.c

/*
* Copyright 2019-2021 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 <crm_internal.h>
#include <ctype.h>
#include <glib.h>
#include <crm/crm.h>
#include <crm/common/cmdline_internal.h>
#include <crm/common/strings_internal.h>
#include <crm/common/util.h>
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;
}
pcmk__common_args_t *
pcmk__new_common_args(const char *summary)
{
pcmk__common_args_t *args = NULL;
args = calloc(1, sizeof(pcmk__common_args_t));
if (args == NULL) {
crm_exit(CRM_EX_OSERR);
}
args->summary = strdup(summary);
if (args->summary == NULL) {
free(args);
args = NULL;
crm_exit(CRM_EX_OSERR);
}
return args;
}
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_dest);
if (common_args->output_as_descr != NULL) {
free(common_args->output_as_descr);
}
free(common_args);
}
GOptionContext *
pcmk__build_arg_context(pcmk__common_args_t *common_args, const char *fmts,
GOptionGroup **output_group, const char *param_string) {
char *desc = crm_strdup_printf("Report bugs to %s\n", PACKAGE_BUGREPORT);
GOptionContext *context;
GOptionGroup *main_group;
GOptionEntry main_entries[3] = {
{ "version", '$', 0, G_OPTION_ARG_NONE, &(common_args->version),
"Display software version 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 },
{ NULL }
};
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(param_string);
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_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 file name for output (or \"-\" for stdout)", "DEST" },
{ NULL }
};
if (*output_group == NULL) {
*output_group = g_option_group_new("output", "Output Options:", "Show output help", NULL, NULL);
}
common_args->output_as_descr = crm_strdup_printf("Specify output format as one of: %s", fmts);
output_entries[0].description = common_args->output_as_descr;
g_option_group_add_entries(*output_group, output_entries);
g_option_context_add_group(context, *output_group);
}
free(desc);
// main_group is now owned by context, we don't free it here
// cppcheck-suppress memleak
return context;
}
void
pcmk__free_arg_context(GOptionContext *context) {
if (context == NULL) {
return;
}
g_option_context_free(context);
}
void
pcmk__add_main_args(GOptionContext *context, GOptionEntry entries[])
{
GOptionGroup *main_group = g_option_context_get_main_group(context);
g_option_group_add_entries(main_group, entries);
}
void
pcmk__add_arg_group(GOptionContext *context, const char *name,
const char *header, const char *desc,
GOptionEntry entries[])
{
GOptionGroup *group = NULL;
group = g_option_group_new(name, header, desc, NULL, NULL);
g_option_group_add_entries(group, entries);
g_option_context_add_group(context, group);
// group is now owned by context, we don't free it here
// cppcheck-suppress memleak
}
static gchar *
string_replace(gchar *str, const gchar *sub, const gchar *repl)
{
/* This function just replaces all occurrences of a substring
* with some other string. It doesn't handle cases like overlapping,
* so don't get clever with it.
*
* FIXME: When glib >= 2.68 is supported, we can get rid of this
* function and use g_string_replace instead.
*/
gchar **split = g_strsplit(str, sub, 0);
gchar *retval = g_strjoinv(repl, split);
g_strfreev(split);
return retval;
}
gchar *
pcmk__quote_cmdline(gchar **argv)
{
GString *gs = NULL;
gchar *retval = NULL;
if (argv == NULL || argv[0] == NULL) {
return NULL;
}
gs = g_string_sized_new(100);
for (int i = 0; argv[i] != NULL; i++) {
if (i > 0) {
g_string_append(gs, " ");
}
if (strchr(argv[i], ' ') == NULL) {
/* The arg does not contain a space. */
g_string_append_printf(gs, "%s", argv[i]);
} else if (strchr(argv[i], '\'') == NULL) {
/* The arg contains a space, but not a single quote. */
g_string_append_printf(gs, "'%s'", argv[i]);
} else {
/* The arg contains both a space and a single quote, which needs to
* be replaced with an escaped version. We do this instead of counting
* on libxml to handle the escaping for various reasons:
*
* (1) This keeps the string as valid shell.
* (2) We don't want to use XML entities in formats besides XML and HTML.
* (3) The string we are feeding to libxml is something like: "a b 'c d' e".
* It won't escape the single quotes around 'c d' here because there is
* no need to escape quotes inside a different form of quote. If we
* change the string to "a b 'c'd' e", we haven't changed anything - it's
* still single quotes inside double quotes.
*
* On the other hand, if we replace the single quote with "&apos;", then
* we have introduced an ampersand which libxml will escape. This leaves
* us with "&amp;apos;" which is not what we want.
*
* It's simplest to just escape with a backslash.
*/
gchar *repl = string_replace(argv[i], "'", "\\\'");
g_string_append_printf(gs, "'%s'", repl);
g_free(repl);
}
}
retval = gs->str;
g_string_free(gs, FALSE);
return retval;
}
gchar **
pcmk__cmdline_preproc(char **argv, const char *special) {
GPtrArray *arr = NULL;
bool saw_dash_dash = false;
bool copy_option = false;
if (argv == NULL) {
return NULL;
}
if (g_get_prgname() == NULL && argv && *argv) {
gchar *basename = g_path_get_basename(*argv);
g_set_prgname(basename);
g_free(basename);
}
arr = g_ptr_array_new();
for (int i = 0; argv[i] != NULL; 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, g_strdup(argv[i]));
continue;
}
if (copy_option == true) {
g_ptr_array_add(arr, g_strdup(argv[i]));
copy_option = false;
continue;
}
/* This is just a dash by itself. That could indicate stdin/stdout, or
* it could be user error. Copy it over and let glib figure it out.
*/
if (pcmk__str_eq(argv[i], "-", pcmk__str_casei)) {
g_ptr_array_add(arr, g_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;
/* This looks like the start of a number, which means it is a negative
* number. It's probably the argument to the preceeding option, but
* we can't know that here. Copy it over and let whatever handles
* arguments next figure it out.
*/
if (*ch != '\0' && *ch >= '1' && *ch <= '9') {
bool is_numeric = true;
while (*ch != '\0') {
if (!isdigit(*ch)) {
is_numeric = false;
break;
}
ch++;
}
if (is_numeric) {
g_ptr_array_add(arr, g_strdup_printf("%s", argv[i]));
continue;
} else {
/* This argument wasn't entirely numeric. Reset ch to the
* beginning so we can process it one character at a time.
*/
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 (special != NULL && 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, g_strdup_printf("-%c", *ch));
g_ptr_array_add(arr, g_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, g_strdup_printf("-%c", *ch));
copy_option = true;
ch++;
}
/* This is a regular short argument. Just copy it over. */
} else {
g_ptr_array_add(arr, g_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, g_strdup(argv[i]));
}
}
g_ptr_array_add(arr, NULL);
return (char **) g_ptr_array_free(arr, FALSE);
}
G_GNUC_PRINTF(3, 4)
gboolean
pcmk__force_args(GOptionContext *context, GError **error, const char *format, ...) {
int len = 0;
char *buf = NULL;
gchar **extra_args = NULL;
va_list ap;
gboolean retval = TRUE;
va_start(ap, format);
len = vasprintf(&buf, format, ap);
CRM_ASSERT(len > 0);
va_end(ap);
if (!g_shell_parse_argv(buf, NULL, &extra_args, error)) {
g_strfreev(extra_args);
free(buf);
return FALSE;
}
retval = g_option_context_parse_strv(context, &extra_args, error);
g_strfreev(extra_args);
free(buf);
return retval;
}

File Metadata

Mime Type
text/x-c
Expires
Thu, Jun 26, 6:06 PM (1 d, 3 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1959339
Default Alt Text
cmdline.c (11 KB)

Event Timeline