mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
Github-ID: https://github.com/ibm-s390-linux/s390-tools/pull/193 Signed-off-by: Sertonix <sertonix@posteo.net> Reviewed-by: Steffen Eiden <seiden@linux.ibm.com> Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com> Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
325 lines
7.5 KiB
C
325 lines
7.5 KiB
C
/*
|
|
* util - Utility function library
|
|
*
|
|
* Parse the command line options
|
|
*
|
|
* Copyright IBM Corp. 2016, 2017
|
|
*
|
|
* s390-tools is free software; you can redistribute it and/or modify
|
|
* it under the terms of the MIT license. See LICENSE for details.
|
|
*/
|
|
|
|
#include <libgen.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "lib/util_base.h"
|
|
#include "lib/util_libc.h"
|
|
#include "lib/util_opt.h"
|
|
#include "lib/util_panic.h"
|
|
#include "lib/util_prg.h"
|
|
|
|
/*
|
|
* Private data
|
|
*/
|
|
/// @cond
|
|
static struct util_opt_l {
|
|
/* Option character string for getopt_long() */
|
|
char *opt_str;
|
|
/* Option array for getopt_long() */
|
|
struct option *option_vec;
|
|
/* Original util_opt array */
|
|
struct util_opt *opt_vec;
|
|
/* Length of longest option string */
|
|
int opt_max;
|
|
/* Command used for parsing */
|
|
const char *command;
|
|
} l;
|
|
|
|
/// @endcond
|
|
|
|
#define util_opt_iterate(opt) \
|
|
for (opt = &l.opt_vec[0]; opt->desc != NULL; opt++)
|
|
|
|
#define MAX_OPTLEN 256
|
|
|
|
static int opt_max_len(void);
|
|
static bool opt_is_active(struct util_opt *opt);
|
|
|
|
/**
|
|
* Initialize the command line options
|
|
*
|
|
* Build short option string and long option array to be used for getopt_long().
|
|
* The ":" prefix is added to the short option string for handling of "missing
|
|
* required arguments".
|
|
*
|
|
* @param[in] opt_vec Option array
|
|
* @param[in] opt_prefix Optional option string prefix
|
|
*/
|
|
void util_opt_init(struct util_opt *opt_vec, const char *opt_prefix)
|
|
{
|
|
int i, j, count;
|
|
char *str;
|
|
size_t prefix_len = opt_prefix ? strlen(opt_prefix) : 0;
|
|
|
|
opterr = 0;
|
|
/* Get number of options */
|
|
for (i = 0, count = 0; opt_vec[i].desc != NULL; i++)
|
|
if (opt_is_active(&opt_vec[i]))
|
|
count++;
|
|
/*
|
|
* Allocate short option string for worst case when all options have
|
|
* optional parameters e.g "x::" and long option string.
|
|
*/
|
|
l.opt_str = util_malloc(sizeof(char) * count * 3 + 2 + prefix_len);
|
|
l.option_vec = util_malloc(sizeof(struct option) * (count + 1));
|
|
l.opt_vec = opt_vec;
|
|
|
|
str = l.opt_str;
|
|
if (opt_prefix) {
|
|
strcpy(str, opt_prefix);
|
|
str += prefix_len;
|
|
}
|
|
/* Force getopt_long() to return ':' for missing required arguments */
|
|
*str++ = ':';
|
|
/* Construction of input structures for getopt_long() function. */
|
|
for (i = 0, j = 0; opt_vec[i].desc != NULL; i++) {
|
|
if (!opt_is_active(&opt_vec[i]))
|
|
continue;
|
|
if (opt_vec[i].flags & UTIL_OPT_FLAG_SECTION)
|
|
continue;
|
|
if (!(opt_vec[i].flags & UTIL_OPT_FLAG_NOLONG)) {
|
|
memcpy(&l.option_vec[j++], &opt_vec[i].option,
|
|
sizeof(struct option));
|
|
}
|
|
if (opt_vec[i].flags & UTIL_OPT_FLAG_NOSHORT)
|
|
continue;
|
|
*str++ = opt_vec[i].option.val;
|
|
switch (opt_vec[i].option.has_arg) {
|
|
case no_argument:
|
|
break;
|
|
case required_argument:
|
|
*str++ = ':';
|
|
break;
|
|
case optional_argument:
|
|
*str++ = ':';
|
|
*str++ = ':';
|
|
break;
|
|
default:
|
|
util_panic("Unexpected \"has_arg\" parameter: %d\n",
|
|
opt_vec[i].option.has_arg);
|
|
}
|
|
}
|
|
/* Add end marker to option array and short option string */
|
|
memset(&l.option_vec[j], 0, sizeof(struct option));
|
|
*str = '\0';
|
|
}
|
|
|
|
/**
|
|
* Set the current command for command line option processing
|
|
*
|
|
* @param[in] command The current command or NULL for no command
|
|
*/
|
|
void util_opt_set_command(const char *command)
|
|
{
|
|
l.command = command;
|
|
}
|
|
|
|
/*
|
|
* Return true, if option belongs to current command setting
|
|
*/
|
|
static bool opt_is_active(struct util_opt *opt)
|
|
{
|
|
if (!opt->command || !l.command)
|
|
return true;
|
|
return (strcmp(opt->command, l.command) == 0);
|
|
}
|
|
|
|
/**
|
|
* Wrapper for getopt_long
|
|
*
|
|
* @param[in] argc Count of command line parameters
|
|
* @param[in] argv Array of command line parameters
|
|
*/
|
|
int util_opt_getopt_long(int argc, char *argv[])
|
|
{
|
|
struct util_opt *opt;
|
|
int val;
|
|
|
|
val = getopt_long(argc, argv, l.opt_str, l.option_vec, NULL);
|
|
|
|
switch (val) {
|
|
case ':':
|
|
case '?':
|
|
case -1:
|
|
break;
|
|
default:
|
|
if (!l.command)
|
|
break;
|
|
util_opt_iterate(opt) {
|
|
if (!opt_is_active(opt))
|
|
continue;
|
|
if (opt->option.val == val)
|
|
goto out;
|
|
}
|
|
/* No valid option found for command */
|
|
val = '?';
|
|
if (optarg)
|
|
optind--;
|
|
break;
|
|
}
|
|
out:
|
|
return val;
|
|
}
|
|
|
|
/*
|
|
* Format option name: Add short, long option and argument (as applicable)
|
|
*/
|
|
static void format_opt(char *buf, size_t maxlen, const struct util_opt *opt)
|
|
{
|
|
int has_arg, flags, rc;
|
|
char val, *arg_str;
|
|
const char *name;
|
|
|
|
has_arg = opt->option.has_arg;
|
|
name = opt->option.name;
|
|
val = opt->option.val;
|
|
flags = opt->flags;
|
|
|
|
/* Prepare potential option argument string */
|
|
if (has_arg == optional_argument) {
|
|
if (flags & UTIL_OPT_FLAG_NOLONG)
|
|
util_asprintf(&arg_str, "[%s]", opt->argument);
|
|
else
|
|
util_asprintf(&arg_str, "[=%s]", opt->argument);
|
|
} else if (has_arg == required_argument) {
|
|
util_asprintf(&arg_str, " %s", opt->argument);
|
|
} else {
|
|
util_asprintf(&arg_str, "");
|
|
}
|
|
|
|
/* Format the option */
|
|
if (flags & UTIL_OPT_FLAG_NOLONG)
|
|
rc = snprintf(buf, maxlen, "-%c%s", val, arg_str);
|
|
else if (flags & UTIL_OPT_FLAG_NOSHORT)
|
|
rc = snprintf(buf, maxlen, " --%s%s", name, arg_str);
|
|
else
|
|
rc = snprintf(buf, maxlen, "-%c, --%s%s", val, name, arg_str);
|
|
|
|
util_assert(rc < (int)maxlen, "Option too long: %s\n", name);
|
|
free(arg_str);
|
|
}
|
|
|
|
/*
|
|
* Return true, if option is to be printed for the current command setting
|
|
*/
|
|
static bool should_print_opt(const struct util_opt *opt)
|
|
{
|
|
if (l.command) {
|
|
/* Print only options that belong to command */
|
|
return opt->command ? !strcmp(opt->command, l.command) : false;
|
|
} else {
|
|
/* Print only common options (standard for non-command tools) */
|
|
return opt->command ? false : true;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Return size of the longest formatted option
|
|
*/
|
|
static int opt_max_len(void)
|
|
{
|
|
const struct util_opt *opt;
|
|
unsigned int max = 0;
|
|
char opt_str[MAX_OPTLEN];
|
|
|
|
util_opt_iterate(opt) {
|
|
if (opt->flags & UTIL_OPT_FLAG_SECTION)
|
|
continue;
|
|
if (!should_print_opt(opt))
|
|
continue;
|
|
format_opt(opt_str, MAX_OPTLEN, opt);
|
|
max = MAX(max, strlen(opt_str));
|
|
}
|
|
return max;
|
|
}
|
|
|
|
/**
|
|
* Print an option name, followed by a description indented to fit the
|
|
* longest option name
|
|
*/
|
|
void util_opt_print_indented(const char *opt, const char *desc)
|
|
{
|
|
printf(" %-*s ", l.opt_max + 1, opt);
|
|
util_print_indented(desc, 3 + l.opt_max);
|
|
}
|
|
|
|
/**
|
|
* Print the usage of the command line options to the console
|
|
*/
|
|
void util_opt_print_help(void)
|
|
{
|
|
char opt_str[MAX_OPTLEN];
|
|
struct util_opt *opt;
|
|
int first = 1;
|
|
|
|
/*
|
|
* Create format string: " -%c, --%-<long opt size>s %s"
|
|
*
|
|
* Example:
|
|
*
|
|
* -p, --print STRING Print STRING to console
|
|
*/
|
|
l.opt_max = opt_max_len();
|
|
|
|
util_opt_iterate(opt) {
|
|
if (!should_print_opt(opt))
|
|
continue;
|
|
if (opt->flags & UTIL_OPT_FLAG_SECTION) {
|
|
printf("%s%s\n", first ? "" : "\n", opt->desc);
|
|
first = 0;
|
|
continue;
|
|
}
|
|
format_opt(opt_str, MAX_OPTLEN, opt);
|
|
util_opt_print_indented(opt_str, opt->desc);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Print option parsing error message
|
|
*
|
|
* This function should be used when the return code of the
|
|
* util_opt_getopt_long() function returns a character that does
|
|
* not match any of the expected options.
|
|
*
|
|
* @param[in] opt Short option returned by getopt_long()
|
|
* @param[in] argv Option array
|
|
*/
|
|
void util_opt_print_parse_error(char opt, char *argv[])
|
|
{
|
|
char optopt_str[3];
|
|
|
|
switch (opt) {
|
|
case ':':
|
|
/* A required option argument has not been specified */
|
|
util_prg_print_required_arg(argv[optind - 1]);
|
|
break;
|
|
case '?':
|
|
/* An invalid option has been specified */
|
|
if (optopt) {
|
|
/* Short option */
|
|
sprintf(optopt_str, "-%c", optopt);
|
|
util_prg_print_invalid_option(optopt_str);
|
|
} else {
|
|
/* Long option */
|
|
util_prg_print_invalid_option(argv[optind - 1]);
|
|
}
|
|
break;
|
|
default:
|
|
util_panic("Option '%c' should not be handled here\n", opt);
|
|
}
|
|
}
|
|
|