Page Menu
Home
ClusterLabs Projects
Search
Configure Global Search
Log In
Files
F4525196
cmdline.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
11 KB
Referenced Files
None
Subscribers
None
cmdline.c
View Options
/*
* 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 "'", then
* we have introduced an ampersand which libxml will escape. This leaves
* us with "&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
Details
Attached
Mime Type
text/x-c
Expires
Thu, Jun 26, 6:06 PM (22 h, 40 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1959339
Default Alt Text
cmdline.c (11 KB)
Attached To
Mode
rP Pacemaker
Attached
Detach File
Event Timeline
Log In to Comment