root/lib/common/cmdline.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. bump_verbosity
  2. pcmk__new_common_args
  3. free_common_args
  4. pcmk__build_arg_context
  5. pcmk__free_arg_context
  6. pcmk__add_main_args
  7. pcmk__add_arg_group
  8. pcmk__cmdline_preproc
  9. G_GNUC_PRINTF

   1 /*
   2  * Copyright 2019-2021 the Pacemaker project contributors
   3  *
   4  * The version control history for this file may have further details.
   5  *
   6  * This source code is licensed under the GNU Lesser General Public License
   7  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #include <crm_internal.h>
  11 
  12 #include <ctype.h>
  13 #include <glib.h>
  14 
  15 #include <crm/crm.h>
  16 #include <crm/common/cmdline_internal.h>
  17 #include <crm/common/strings_internal.h>
  18 #include <crm/common/util.h>
  19 
  20 static gboolean
  21 bump_verbosity(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
  22     pcmk__common_args_t *common_args = (pcmk__common_args_t *) data;
  23     common_args->verbosity++;
  24     return TRUE;
  25 }
  26 
  27 pcmk__common_args_t *
  28 pcmk__new_common_args(const char *summary)
     /* [previous][next][first][last][top][bottom][index][help] */
  29 {
  30     pcmk__common_args_t *args = NULL;
  31 
  32     args = calloc(1, sizeof(pcmk__common_args_t));
  33     if (args == NULL) {
  34         crm_exit(crm_errno2exit(-ENOMEM));
  35     }
  36 
  37     args->summary = strdup(summary);
  38     if (args->summary == NULL) {
  39         crm_exit(crm_errno2exit(-ENOMEM));
  40     }
  41 
  42     return args;
  43 }
  44 
  45 static void
  46 free_common_args(gpointer data) {
     /* [previous][next][first][last][top][bottom][index][help] */
  47     pcmk__common_args_t *common_args = (pcmk__common_args_t *) data;
  48 
  49     free(common_args->summary);
  50     free(common_args->output_ty);
  51     free(common_args->output_dest);
  52 
  53     if (common_args->output_as_descr != NULL) {
  54         free(common_args->output_as_descr);
  55     }
  56 
  57     free(common_args);
  58 }
  59 
  60 GOptionContext *
  61 pcmk__build_arg_context(pcmk__common_args_t *common_args, const char *fmts,
     /* [previous][next][first][last][top][bottom][index][help] */
  62                         GOptionGroup **output_group, const char *param_string) {
  63     char *desc = crm_strdup_printf("Report bugs to %s\n", PACKAGE_BUGREPORT);
  64     GOptionContext *context;
  65     GOptionGroup *main_group;
  66 
  67     GOptionEntry main_entries[3] = {
  68         { "version", '$', 0, G_OPTION_ARG_NONE, &(common_args->version),
  69           "Display software version and exit",
  70           NULL },
  71         { "verbose", 'V', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, bump_verbosity,
  72           "Increase debug output (may be specified multiple times)",
  73           NULL },
  74 
  75         { NULL }
  76     };
  77 
  78     main_group = g_option_group_new(NULL, "Application Options:", NULL, common_args, free_common_args);
  79     g_option_group_add_entries(main_group, main_entries);
  80 
  81     context = g_option_context_new(param_string);
  82     g_option_context_set_summary(context, common_args->summary);
  83     g_option_context_set_description(context, desc);
  84     g_option_context_set_main_group(context, main_group);
  85 
  86     if (fmts != NULL) {
  87         GOptionEntry output_entries[3] = {
  88             { "output-as", 0, 0, G_OPTION_ARG_STRING, &(common_args->output_ty),
  89               NULL,
  90               "FORMAT" },
  91             { "output-to", 0, 0, G_OPTION_ARG_STRING, &(common_args->output_dest),
  92               "Specify file name for output (or \"-\" for stdout)", "DEST" },
  93 
  94             { NULL }
  95         };
  96 
  97         if (*output_group == NULL) {
  98             *output_group = g_option_group_new("output", "Output Options:", "Show output help", NULL, NULL);
  99         }
 100 
 101         common_args->output_as_descr = crm_strdup_printf("Specify output format as one of: %s", fmts);
 102         output_entries[0].description = common_args->output_as_descr;
 103         g_option_group_add_entries(*output_group, output_entries);
 104         g_option_context_add_group(context, *output_group);
 105     }
 106 
 107     free(desc);
 108 
 109     // main_group is now owned by context, we don't free it here
 110     // cppcheck-suppress memleak
 111     return context;
 112 }
 113 
 114 void
 115 pcmk__free_arg_context(GOptionContext *context) {
     /* [previous][next][first][last][top][bottom][index][help] */
 116     if (context == NULL) {
 117         return;
 118     }
 119 
 120     g_option_context_free(context);
 121 }
 122 
 123 void
 124 pcmk__add_main_args(GOptionContext *context, GOptionEntry entries[])
     /* [previous][next][first][last][top][bottom][index][help] */
 125 {
 126     GOptionGroup *main_group = g_option_context_get_main_group(context);
 127 
 128     g_option_group_add_entries(main_group, entries);
 129 }
 130 
 131 void
 132 pcmk__add_arg_group(GOptionContext *context, const char *name,
     /* [previous][next][first][last][top][bottom][index][help] */
 133                     const char *header, const char *desc,
 134                     GOptionEntry entries[])
 135 {
 136     GOptionGroup *group = NULL;
 137 
 138     group = g_option_group_new(name, header, desc, NULL, NULL);
 139     g_option_group_add_entries(group, entries);
 140     g_option_context_add_group(context, group);
 141     // group is now owned by context, we don't free it here
 142     // cppcheck-suppress memleak
 143 }
 144 
 145 gchar **
 146 pcmk__cmdline_preproc(char **argv, const char *special) {
     /* [previous][next][first][last][top][bottom][index][help] */
 147     GPtrArray *arr = NULL;
 148     bool saw_dash_dash = false;
 149     bool copy_option = false;
 150 
 151     if (argv == NULL) {
 152         return NULL;
 153     }
 154 
 155     if (g_get_prgname() == NULL && argv && *argv) {
 156         gchar *basename = g_path_get_basename(*argv);
 157 
 158         g_set_prgname(basename);
 159         g_free(basename);
 160     }
 161 
 162     arr = g_ptr_array_new();
 163 
 164     for (int i = 0; argv[i] != NULL; i++) {
 165         /* If this is the first time we saw "--" in the command line, set
 166          * a flag so we know to just copy everything after it over.  We also
 167          * want to copy the "--" over so whatever actually parses the command
 168          * line when we're done knows where arguments end.
 169          */
 170         if (saw_dash_dash == false && strcmp(argv[i], "--") == 0) {
 171             saw_dash_dash = true;
 172         }
 173 
 174         if (saw_dash_dash == true) {
 175             g_ptr_array_add(arr, g_strdup(argv[i]));
 176             continue;
 177         }
 178 
 179         if (copy_option == true) {
 180             g_ptr_array_add(arr, g_strdup(argv[i]));
 181             copy_option = false;
 182             continue;
 183         }
 184 
 185         /* This is just a dash by itself.  That could indicate stdin/stdout, or
 186          * it could be user error.  Copy it over and let glib figure it out.
 187          */
 188         if (pcmk__str_eq(argv[i], "-", pcmk__str_casei)) {
 189             g_ptr_array_add(arr, g_strdup(argv[i]));
 190             continue;
 191         }
 192 
 193         /* This is a short argument, or perhaps several.  Iterate over it
 194          * and explode them out into individual arguments.
 195          */
 196         if (g_str_has_prefix(argv[i], "-") && !g_str_has_prefix(argv[i], "--")) {
 197             /* Skip over leading dash */
 198             char *ch = argv[i]+1;
 199 
 200             /* This looks like the start of a number, which means it is a negative
 201              * number.  It's probably the argument to the preceeding option, but
 202              * we can't know that here.  Copy it over and let whatever handles
 203              * arguments next figure it out.
 204              */
 205             if (*ch != '\0' && *ch >= '1' && *ch <= '9') {
 206                 bool is_numeric = true;
 207 
 208                 while (*ch != '\0') {
 209                     if (!isdigit(*ch)) {
 210                         is_numeric = false;
 211                         break;
 212                     }
 213 
 214                     ch++;
 215                 }
 216 
 217                 if (is_numeric) {
 218                     g_ptr_array_add(arr, g_strdup_printf("%s", argv[i]));
 219                     continue;
 220                 } else {
 221                     /* This argument wasn't entirely numeric.  Reset ch to the
 222                      * beginning so we can process it one character at a time.
 223                      */
 224                     ch = argv[i]+1;
 225                 }
 226             }
 227 
 228             while (*ch != '\0') {
 229                 /* This is a special short argument that takes an option.  getopt
 230                  * allows values to be interspersed with a list of arguments, but
 231                  * glib does not.  Grab both the argument and its value and
 232                  * separate them into a new argument.
 233                  */
 234                 if (special != NULL && strchr(special, *ch) != NULL) {
 235                     /* The argument does not occur at the end of this string of
 236                      * arguments.  Take everything through the end as its value.
 237                      */
 238                     if (*(ch+1) != '\0') {
 239                         g_ptr_array_add(arr, g_strdup_printf("-%c", *ch));
 240                         g_ptr_array_add(arr, g_strdup(ch+1));
 241                         break;
 242 
 243                     /* The argument occurs at the end of this string.  Hopefully
 244                      * whatever comes next in argv is its value.  It may not be,
 245                      * but that is not for us to decide.
 246                      */
 247                     } else {
 248                         g_ptr_array_add(arr, g_strdup_printf("-%c", *ch));
 249                         copy_option = true;
 250                         ch++;
 251                     }
 252 
 253                 /* This is a regular short argument.  Just copy it over. */
 254                 } else {
 255                     g_ptr_array_add(arr, g_strdup_printf("-%c", *ch));
 256                     ch++;
 257                 }
 258             }
 259 
 260         /* This is a long argument, or an option, or something else.
 261          * Copy it over - everything else is copied, so this keeps it easy for
 262          * the caller to know what to do with the memory when it's done.
 263          */
 264         } else {
 265             g_ptr_array_add(arr, g_strdup(argv[i]));
 266         }
 267     }
 268 
 269     g_ptr_array_add(arr, NULL);
 270 
 271     return (char **) g_ptr_array_free(arr, FALSE);
 272 }
 273 
 274 G_GNUC_PRINTF(3, 4)
     /* [previous][next][first][last][top][bottom][index][help] */
 275 gboolean
 276 pcmk__force_args(GOptionContext *context, GError **error, const char *format, ...) {
 277     int len = 0;
 278     char *buf = NULL;
 279     gchar **extra_args = NULL;
 280     va_list ap;
 281     gboolean retval = TRUE;
 282 
 283     va_start(ap, format);
 284     len = vasprintf(&buf, format, ap);
 285     CRM_ASSERT(len > 0);
 286     va_end(ap);
 287 
 288     if (!g_shell_parse_argv(buf, NULL, &extra_args, error)) {
 289         g_strfreev(extra_args);
 290         free(buf);
 291         return FALSE;
 292     }
 293 
 294     retval = g_option_context_parse_strv(context, &extra_args, error);
 295 
 296     g_strfreev(extra_args);
 297     free(buf);
 298     return retval;
 299 }

/* [previous][next][first][last][top][bottom][index][help] */