1
0
mirror of git://sourceware.org/git/lvm2.git synced 2025-01-05 13:18:20 +03:00
lvm2/tools/create-commands.c
David Teigland 45e23131b8 commands: new method for defining commands
. Define a prototype for every lvm command.
. Match every user command with one definition.
. Generate help text and man pages from them.

The new file command-lines.in defines a prototype for every
unique lvm command.  A unique lvm command is a unique
combination of: command name + required option args +
required positional args.  Each of these prototypes also
includes the optional option args and optional positional
args that the command will accept, a description, and a
unique string ID for the definition.  Any valid command
will match one of the prototypes.

Here's an example of the lvresize command definitions from
command-lines.in, there are three unique lvresize commands:

lvresize --size SizeMB LV
OO: --alloc Alloc, --autobackup Bool, --force,
--nofsck, --nosync, --noudevsync, --reportformat String, --resizefs,
--stripes Number, --stripesize SizeKB, --poolmetadatasize SizeMB
OP: PV ...
ID: lvresize_by_size
DESC: Resize an LV by a specified size.

lvresize LV PV ...
OO: --alloc Alloc, --autobackup Bool, --force,
--nofsck, --nosync, --noudevsync,
--reportformat String, --resizefs, --stripes Number, --stripesize SizeKB
ID: lvresize_by_pv
DESC: Resize an LV by specified PV extents.
FLAGS: SECONDARY_SYNTAX

lvresize --poolmetadatasize SizeMB LV_thinpool
OO: --alloc Alloc, --autobackup Bool, --force,
--nofsck, --nosync, --noudevsync,
--reportformat String, --stripes Number, --stripesize SizeKB
OP: PV ...
ID: lvresize_pool_metadata_by_size
DESC: Resize a pool metadata SubLV by a specified size.

The three commands have separate definitions because they have
different required parameters.  Required parameters are specified
on the first line of the definition.  Optional options are
listed after OO, and optional positional args are listed after OP.

This data is used to generate corresponding command definition
structures for lvm in command-lines.h.  usage/help output is also
auto generated, so it is always in sync with the definitions.

Example of the corresponding generated structure in
command-lines.h for the first lvresize prototype
(these structures are never edited directly):

commands[83].name = "lvresize";
commands[83].command_line_id = "lvresize_by_size";
commands[83].command_line_enum = lvresize_by_size_CMD;
commands[83].fn = lvresize;
commands[83].ro_count = 1;
commands[83].rp_count = 1;
commands[83].oo_count = 22;
commands[83].op_count = 1;
commands[83].cmd_flags = 0;
commands[83].desc = "DESC: Resize an LV by a specified size.";
commands[83].usage = "lvresize --size Number[m|unit] LV"
" [ --resizefs, --poolmetadatasize Number[m|unit], COMMON_OPTIONS ]"
" [ PV ... ]";
commands[83].usage_common =
" [ --alloc contiguous|cling|cling_by_tags|normal|anywhere|inherit, --nosync, --reportformat String, --autobackup y|n, --stripes Number, --stripesize Number[k|unit], --nofsck, --commandprofile String, --config String, --debug, --driverloaded y|n, --help, --profile String, --quiet, --verbose, --version, --yes, --test, --force, --noudevsync ]";
commands[83].required_opt_args[0].opt = size_ARG;
commands[83].required_opt_args[0].def.val_bits = val_enum_to_bit(sizemb_VAL);
commands[83].required_pos_args[0].pos = 1;
commands[83].required_pos_args[0].def.val_bits = val_enum_to_bit(lv_VAL);
commands[83].optional_opt_args[0].opt = commandprofile_ARG;
commands[83].optional_opt_args[0].def.val_bits = val_enum_to_bit(string_VAL);
commands[83].optional_opt_args[1].opt = config_ARG;
commands[83].optional_opt_args[1].def.val_bits = val_enum_to_bit(string_VAL);
commands[83].optional_opt_args[2].opt = debug_ARG;
commands[83].optional_opt_args[3].opt = driverloaded_ARG;
commands[83].optional_opt_args[3].def.val_bits = val_enum_to_bit(bool_VAL);
commands[83].optional_opt_args[4].opt = help_ARG;
commands[83].optional_opt_args[5].opt = profile_ARG;
commands[83].optional_opt_args[5].def.val_bits = val_enum_to_bit(string_VAL);
commands[83].optional_opt_args[6].opt = quiet_ARG;
commands[83].optional_opt_args[7].opt = verbose_ARG;
commands[83].optional_opt_args[8].opt = version_ARG;
commands[83].optional_opt_args[9].opt = yes_ARG;
commands[83].optional_opt_args[10].opt = test_ARG;
commands[83].optional_opt_args[11].opt = alloc_ARG;
commands[83].optional_opt_args[11].def.val_bits = val_enum_to_bit(alloc_VAL);
commands[83].optional_opt_args[12].opt = autobackup_ARG;
commands[83].optional_opt_args[12].def.val_bits = val_enum_to_bit(bool_VAL);
commands[83].optional_opt_args[13].opt = force_ARG;
commands[83].optional_opt_args[14].opt = nofsck_ARG;
commands[83].optional_opt_args[15].opt = nosync_ARG;
commands[83].optional_opt_args[16].opt = noudevsync_ARG;
commands[83].optional_opt_args[17].opt = reportformat_ARG;
commands[83].optional_opt_args[17].def.val_bits = val_enum_to_bit(string_VAL);
commands[83].optional_opt_args[18].opt = resizefs_ARG;
commands[83].optional_opt_args[19].opt = stripes_ARG;
commands[83].optional_opt_args[19].def.val_bits = val_enum_to_bit(number_VAL);
commands[83].optional_opt_args[20].opt = stripesize_ARG;
commands[83].optional_opt_args[20].def.val_bits = val_enum_to_bit(sizekb_VAL);
commands[83].optional_opt_args[21].opt = poolmetadatasize_ARG;
commands[83].optional_opt_args[21].def.val_bits = val_enum_to_bit(sizemb_VAL);
commands[83].optional_pos_args[0].pos = 2;
commands[83].optional_pos_args[0].def.val_bits = val_enum_to_bit(pv_VAL);
commands[83].optional_pos_args[0].def.flags = ARG_DEF_FLAG_MAY_REPEAT;

Every user-entered command is compared against the set of
command structures, and matched with one.  An error is
reported if an entered command does not have the required
parameters for any definition.  The closest match is printed
as a suggestion, and running lvresize --help will display
the usage for each possible lvresize command.

The prototype syntax used for help/man output includes
required --option and positional args on the first line,
and optional --option and positional args enclosed in [ ]
on subsequent lines.

  command_name <required_opt_args> <required_pos_args>
          [ <optional_opt_args> ]
          [ <optional_pos_args> ]

$ lvresize --help
  lvresize - Resize a logical volume

  Resize an LV by a specified size.
  lvresize --size Number[m|unit] LV
        [ --resizefs,
          --poolmetadatasize Number[m|unit],
          COMMON_OPTIONS ]
        [ PV ... ]

  Resize a pool metadata SubLV by a specified size.
  lvresize --poolmetadatasize Number[m|unit] LV_thinpool
        [ COMMON_OPTIONS ]
        [ PV ... ]

  Common options:
        [ --alloc contiguous|cling|cling_by_tags|normal|anywhere|inherit,
          --nosync,
          --reportformat String,
          --autobackup y|n,
          --stripes Number,
          --stripesize Number[k|unit],
          --nofsck,
          --commandprofile String,
          --config String,
          --debug,
          --driverloaded y|n,
          --help,
          --profile String,
          --quiet,
          --verbose,
          --version,
          --yes,
          --test,
          --force,
          --noudevsync ]

  (Use --help --help for usage notes.)

$ lvresize --poolmetadatasize 4
  Failed to find a matching command definition.
  Closest command usage is:
  lvresize --poolmetadatasize Number[m|unit] LV_thinpool

Command definitions that are not to be advertised/suggested
have the flag SECONDARY_SYNTAX.  These commands will not be
printed in the normal help output.

Man page prototypes are also generated from the same original
command definitions, and are always in sync with the code
and help text.

Very early in command execution, a matching command definition
is found.  lvm then knows the operation being done, and that
the provided args conform to the definition.  This will allow
lots of ad hoc checking/validation to be removed throughout
the code.

Each command definition can also be routed to a specific
function to implement it.  The function is associated with
an enum value for the command definition (generated from
the ID string.)  These per-command-definition implementation
functions have not yet been created, so all commands
currently fall back to the existing per-command-name
implementation functions.

Using per-command-definition functions will allow lots of
code to be removed which tries to figure out what the
command is meant to do.  This is currently based on ad hoc
and complicated option analysis.  When using the new
functions, what the command is doing is already known
from the associated command definition.

So, this first phase validates every user-entered command
against the set of command prototypes, then calls the existing
implementation.  The second phase can associate an implementation
function with each definition, and take further advantage of the
known operation to avoid the complicated option analysis.
2016-11-30 16:37:20 -06:00

2899 lines
64 KiB
C

#include <asm/types.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <stddef.h>
#include <stdint.h>
#include <stdarg.h>
#include <limits.h>
#include <unistd.h>
#include <syslog.h>
#include <sched.h>
#include <dirent.h>
#include <ctype.h>
#include <getopt.h>
/* needed to include args.h */
#define ARG_COUNTABLE 0x00000001
#define ARG_GROUPABLE 0x00000002
struct cmd_context;
struct arg_values;
int yes_no_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; }
int activation_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; }
int cachemode_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; }
int discards_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; }
int mirrorlog_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; }
int size_kb_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; }
int size_mb_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; }
int size_mb_arg_with_percent(struct cmd_context *cmd, struct arg_values *av) { return 0; }
int int_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; }
int int_arg_with_sign(struct cmd_context *cmd, struct arg_values *av) { return 0; }
int int_arg_with_sign_and_percent(struct cmd_context *cmd, struct arg_values *av) { return 0; }
int major_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; }
int minor_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; }
int string_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; }
int tag_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; }
int permission_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; }
int metadatatype_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; }
int units_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; }
int segtype_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; }
int alloc_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; }
int locktype_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; }
int readahead_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; }
int vgmetadatacopies_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av) { return 0; }
int pvmetadatacopies_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av) { return 0; }
int metadatacopies_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av) { return 0; }
int polloperation_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av) { return 0; }
int writemostly_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av) { return 0; }
int syncaction_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av) { return 0; }
int reportformat_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av) { return 0; }
int configreport_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av) { return 0; }
int configtype_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av) { return 0; }
/* also see arg_props in tools.h and args.h */
struct opt_name {
const char *name; /* "foo_ARG" */
int opt_enum; /* foo_ARG */
const char short_opt; /* -f */
char _padding[7];
const char *long_opt; /* --foo */
int val_enum; /* xyz_VAL when --foo takes a val like "--foo xyz" */
uint32_t unused1;
uint32_t unused2;
};
/* also see val_props in tools.h and vals.h */
struct val_name {
const char *enum_name; /* "foo_VAL" */
int val_enum; /* foo_VAL */
int (*fn) (struct cmd_context *cmd, struct arg_values *av); /* foo_arg() */
const char *name; /* FooVal */
const char *usage;
};
/* also see lv_props in tools.h and lv_props.h */
struct lvp_name {
const char *enum_name; /* "is_foo_LVP" */
int lvp_enum; /* is_foo_LVP */
const char *name; /* "lv_is_foo" */
};
/* also see lv_types in tools.h and lv_types.h */
struct lvt_name {
const char *enum_name; /* "foo_LVT" */
int lvt_enum; /* foo_LVT */
const char *name; /* "foo" */
};
/* create foo_VAL enums for option and position values */
enum {
#define val(a, b, c, d) a ,
#include "vals.h"
#undef val
};
/* create foo_ARG enums for --option's */
enum {
#define arg(a, b, c, d, e, f) a ,
#include "args.h"
#undef arg
};
/* create foo_LVP enums for LV properties */
enum {
#define lvp(a, b, c) a,
#include "lv_props.h"
#undef lvp
};
/* create foo_LVT enums for LV types */
enum {
#define lvt(a, b, c) a,
#include "lv_types.h"
#undef lvt
};
/* create table of value names, e.g. String, and corresponding enum from vals.h */
static struct val_name val_names[VAL_COUNT + 1] = {
#define val(a, b, c, d) { # a, a, b, c, d },
#include "vals.h"
#undef val
};
/* create table of option names, e.g. --foo, and corresponding enum from args.h */
static struct opt_name opt_names[ARG_COUNT + 1] = {
#define arg(a, b, c, d, e, f) { # a, a, b, "", "--" c, d, e, f },
#include "args.h"
#undef arg
};
/* create table of lv property names, e.g. lv_is_foo, and corresponding enum from lv_props.h */
static struct lvp_name lvp_names[LVP_COUNT + 1] = {
#define lvp(a, b, c) { # a, a, b },
#include "lv_props.h"
#undef lvp
};
/* create table of lv type names, e.g. linear and corresponding enum from lv_types.h */
static struct lvt_name lvt_names[LVT_COUNT + 1] = {
#define lvt(a, b, c) { # a, a, b },
#include "lv_types.h"
#undef lvt
};
#include "command.h"
#define MAX_CMD_NAMES 128
struct cmd_name {
const char *name;
const char *desc;
int common_options[ARG_COUNT + 1];
int all_options[ARG_COUNT + 1];
int variants;
int variant_has_ro;
int variant_has_rp;
int variant_has_oo;
int variant_has_op;
};
/* create table of command names, e.g. vgcreate */
static struct cmd_name cmd_names[MAX_CMD_NAMES] = {
#define xx(a, b, c) { # a , b } ,
#include "commands.h"
#undef xx
};
#define MAX_LINE 1024
#define MAX_LINE_ARGC 256
#define REQUIRED 1
#define OPTIONAL 0
struct oo_line {
char *name;
char *line;
};
#define MAX_CMDS 256
int cmd_count;
struct command cmd_array[MAX_CMDS];
struct command lvm_all; /* for printing common options for all lvm commands */
#define MAX_OO_LINES 256
int oo_line_count;
struct oo_line oo_lines[MAX_OO_LINES];
static int include_man_secondary = 1;
static int include_man_primary = 1;
static char *man_command_name = NULL;
static void add_optional_opt_line(struct command *cmd, int argc, char *argv[]);
/*
* modifies buf, replacing the sep characters with \0
* argv pointers point to positions in buf
*/
static char *split_line(char *buf, int *argc, char **argv, char sep)
{
char *p = buf, *rp = NULL;
int i;
argv[0] = p;
for (i = 1; i < MAX_LINE_ARGC; i++) {
p = strchr(buf, sep);
if (!p)
break;
*p = '\0';
argv[i] = p + 1;
buf = p + 1;
}
*argc = i;
/* we ended by hitting \0, return the point following that */
if (!rp)
rp = strchr(buf, '\0') + 1;
return rp;
}
/* convert value string, e.g. Number, to foo_VAL enum */
static int val_str_to_num(char *str)
{
char name[32] = { 0 };
char *new;
int i;
/* compare the name before any suffix like _new or _<lvtype> */
strncpy(name, str, 31);
if ((new = strstr(name, "_")))
*new = '\0';
for (i = 0; i < VAL_COUNT; i++) {
if (!val_names[i].name)
break;
if (!strncmp(name, val_names[i].name, strlen(val_names[i].name)))
return val_names[i].val_enum;
}
return 0;
}
/* convert "--option" to foo_ARG enum */
static int opt_str_to_num(char *str)
{
char long_name[32];
char *p;
int i;
/*
* --foo_long means there are two args entries
* for --foo, one with a short option and one
* without, and we want the one without the
* short option.
*/
if (strstr(str, "_long")) {
strcpy(long_name, str);
p = strstr(long_name, "_long");
*p = '\0';
for (i = 0; i < ARG_COUNT; i++) {
if (!opt_names[i].long_opt)
continue;
/* skip anything with a short opt */
if (opt_names[i].short_opt)
continue;
if (!strcmp(opt_names[i].long_opt, long_name))
return opt_names[i].opt_enum;
}
printf("Unknown opt str: %s %s\n", str, long_name);
exit(1);
}
for (i = 0; i < ARG_COUNT; i++) {
if (!opt_names[i].long_opt)
continue;
/* These are only selected using --foo_long */
if (strstr(opt_names[i].name, "_long_ARG"))
continue;
if (!strcmp(opt_names[i].long_opt, str))
return opt_names[i].opt_enum;
}
printf("Unknown opt str: \"%s\"\n", str);
exit(1);
}
static char *val_bits_to_str(uint64_t val_bits)
{
static char buf[1024];
int i;
int or = 0;
memset(buf, 0, sizeof(buf));
for (i = 0; i < VAL_COUNT; i++) {
if (val_bits & val_enum_to_bit(i)) {
if (or) strcat(buf, " | ");
strcat(buf, "val_enum_to_bit(");
strcat(buf, val_names[i].enum_name);
strcat(buf, ")");
or = 1;
}
}
return buf;
}
/*
* When bits for foo_LVP and bar_LVP are both set in bits, print:
* lvp_enum_to_bit(foo_LVP) | lvp_enum_to_bit(bar_LVP)
*/
static char *lvp_bits_to_str(uint64_t bits)
{
static char lvp_buf[1024];
int i;
int or = 0;
memset(lvp_buf, 0, sizeof(lvp_buf));
for (i = 0; i < LVP_COUNT; i++) {
if (bits & lvp_enum_to_bit(i)) {
if (or) strcat(lvp_buf, " | ");
strcat(lvp_buf, "lvp_enum_to_bit(");
strcat(lvp_buf, lvp_names[i].enum_name);
strcat(lvp_buf, ")");
or = 1;
}
}
return lvp_buf;
}
/*
* When bits for foo_LVT and bar_LVT are both set in bits, print:
* lvt_enum_to_bit(foo_LVT) | lvt_enum_to_bit(bar_LVT)
*/
static char *lvt_bits_to_str(uint64_t bits)
{
static char lvt_buf[1024];
int i;
int or = 0;
memset(lvt_buf, 0, sizeof(lvt_buf));
for (i = 1; i < LVT_COUNT; i++) {
if (bits & lvt_enum_to_bit(i)) {
if (or) strcat(lvt_buf, " | ");
strcat(lvt_buf, "lvt_enum_to_bit(");
strcat(lvt_buf, lvt_names[i].enum_name);
strcat(lvt_buf, ")");
or = 1;
}
}
return lvt_buf;
}
/* "lv_is_prop" to is_prop_LVP */
static int lvp_name_to_enum(char *str)
{
int i;
for (i = 1; i < LVP_COUNT; i++) {
if (!strcmp(str, lvp_names[i].name))
return lvp_names[i].lvp_enum;
}
printf("unknown lv property %s\n", str);
exit(1);
}
/* type_LVT to "type" */
static const char *lvt_enum_to_name(int lvt_enum)
{
return lvt_names[lvt_enum].name;
}
/* "type" to type_LVT */
static int lvt_name_to_enum(char *str)
{
int i;
for (i = 1; i < LVT_COUNT; i++) {
if (!strcmp(str, lvt_names[i].name))
return lvt_names[i].lvt_enum;
}
printf("unknown lv type %s\n", str);
exit(1);
}
/* LV_<type> to <type>_LVT */
int lv_to_enum(char *name)
{
return lvt_name_to_enum(name + 3);
}
/*
* LV_<type1>_<type2> to lvt_bits
*
* type1 to lvt_enum
* lvt_bits |= lvt_enum_to_bit(lvt_enum)
* type2 to lvt_enum
* lvt_bits |= lvt_enum_to_bit(lvt_enum)
*/
uint64_t lv_to_bits(char *name)
{
char buf[64];
char *argv[MAX_LINE_ARGC];
uint64_t lvt_bits = 0;
int lvt_enum;
int argc;
int i;
strcpy(buf, name);
split_line(buf, &argc, argv, '_');
/* 0 is "LV" */
for (i = 1; i < argc; i++) {
if (!strcmp(argv[i], "new"))
continue;
lvt_enum = lvt_name_to_enum(argv[i]);
lvt_bits |= lvt_enum_to_bit(lvt_enum);
}
return lvt_bits;
}
static const char *is_command_name(char *str)
{
int i;
for (i = 0; i < MAX_CMD_NAMES; i++) {
if (!cmd_names[i].name)
break;
if (!strcmp(cmd_names[i].name, str))
return cmd_names[i].name;
}
return NULL;
}
static const char *cmd_name_desc(const char *name)
{
int i;
for (i = 0; i < MAX_CMD_NAMES; i++) {
if (!cmd_names[i].name)
break;
if (!strcmp(cmd_names[i].name, name))
return cmd_names[i].desc;
}
return NULL;
}
static struct cmd_name *find_command_name(const char *str)
{
int i;
for (i = 0; i < MAX_CMD_NAMES; i++) {
if (!cmd_names[i].name)
break;
if (!strcmp(cmd_names[i].name, str))
return &cmd_names[i];
}
return NULL;
}
static int is_opt_name(char *str)
{
if (!strncmp(str, "--", 2))
return 1;
if ((str[0] == '-') && (str[1] != '-')) {
printf("Options must be specified in long form: %s\n", str);
exit(1);
}
return 0;
}
/*
* "Select" as a pos name means that the position
* can be empty if the --select option is used.
*/
static int is_pos_name(char *str)
{
if (!strncmp(str, "VG", 2))
return 1;
if (!strncmp(str, "LV", 2))
return 1;
if (!strncmp(str, "PV", 2))
return 1;
if (!strncmp(str, "Tag", 3))
return 1;
if (!strncmp(str, "String", 6))
return 1;
if (!strncmp(str, "Select", 6))
return 1;
return 0;
}
static int is_oo_definition(char *str)
{
if (!strncmp(str, "OO_", 3) && strstr(str, ":"))
return 1;
return 0;
}
static int is_oo_line(char *str)
{
if (!strncmp(str, "OO:", 3))
return 1;
return 0;
}
static int is_op_line(char *str)
{
if (!strncmp(str, "OP:", 3))
return 1;
return 0;
}
static int is_desc_line(char *str)
{
if (!strncmp(str, "DESC:", 5))
return 1;
return 0;
}
static int is_flags_line(char *str)
{
if (!strncmp(str, "FLAGS:", 6))
return 1;
return 0;
}
static int is_rule_line(char *str)
{
if (!strncmp(str, "RULE:", 5))
return 1;
return 0;
}
static int is_id_line(char *str)
{
if (!strncmp(str, "ID:", 3))
return 1;
return 0;
}
/*
* parse str for anything that can appear in a position,
* like VG, VG|LV, VG|LV_linear|LV_striped, etc
*/
static void set_pos_def(struct command *cmd, char *str, struct arg_def *def)
{
char *argv[MAX_LINE_ARGC];
int argc;
char *name;
int val_enum;
int i;
split_line(str, &argc, argv, '|');
for (i = 0; i < argc; i++) {
name = argv[i];
val_enum = val_str_to_num(name);
if (!val_enum) {
printf("Unknown pos arg: %s\n", name);
exit(1);
}
def->val_bits |= val_enum_to_bit(val_enum);
if ((val_enum == lv_VAL) && strstr(name, "_"))
def->lvt_bits = lv_to_bits(name);
if (strstr(name, "_new"))
def->flags |= ARG_DEF_FLAG_NEW;
}
}
/*
* parse str for anything that can follow --option
*/
static void set_opt_def(struct command *cmd, char *str, struct arg_def *def)
{
char *argv[MAX_LINE_ARGC];
int argc;
char *name;
int val_enum;
int i, j;
split_line(str, &argc, argv, '|');
for (i = 0; i < argc; i++) {
name = argv[i];
val_enum = val_str_to_num(name);
if (!val_enum) {
/* a literal number or string */
if (isdigit(name[0]))
val_enum = constnum_VAL;
else if (isalpha(name[0]))
val_enum = conststr_VAL;
else {
printf("Unknown opt arg: %s\n", name);
exit(0);
}
}
def->val_bits |= val_enum_to_bit(val_enum);
if (val_enum == constnum_VAL)
def->num = (uint64_t)atoi(name);
if (val_enum == conststr_VAL)
def->str = strdup(name);
if (val_enum == lv_VAL) {
if (strstr(name, "_"))
def->lvt_bits = lv_to_bits(name);
}
if ((val_enum == vg_VAL) || (val_enum == lv_VAL) || (val_enum == pv_VAL)) {
if (strstr(name, "_new"))
def->flags |= ARG_DEF_FLAG_NEW;
}
}
}
/*
* OO_FOO: --opt1 ...
*
* oo->name = "OO_FOO";
* oo->line = "--opt1 ...";
*/
static void add_oo_definition_line(const char *name, const char *line)
{
struct oo_line *oo;
char *colon;
char *start;
oo = &oo_lines[oo_line_count++];
oo->name = strdup(name);
if ((colon = strstr(oo->name, ":")))
*colon = '\0';
else {
printf("invalid OO definition\n");
exit(1);
}
start = strstr(line, ":") + 2;
oo->line = strdup(start);
}
/* when OO_FOO: continues on multiple lines */
static void append_oo_definition_line(const char *new_line)
{
struct oo_line *oo;
char *old_line;
char *line;
int len;
oo = &oo_lines[oo_line_count-1];
old_line = oo->line;
/* +2 = 1 space between old and new + 1 terminating \0 */
len = strlen(old_line) + strlen(new_line) + 2;
line = malloc(len);
memset(line, 0, len);
strcat(line, old_line);
strcat(line, " ");
strcat(line, new_line);
free(oo->line);
oo->line = line;
}
char *get_oo_line(char *str)
{
char *name;
char *end;
char str2[64];
int i;
strcpy(str2, str);
if ((end = strstr(str2, ":")))
*end = '\0';
if ((end = strstr(str2, ",")))
*end = '\0';
for (i = 0; i < oo_line_count; i++) {
name = oo_lines[i].name;
if (!strcmp(name, str2))
return oo_lines[i].line;
}
return NULL;
}
/* add optional_opt_args entries when OO_FOO appears on OO: line */
static void include_optional_opt_args(struct command *cmd, char *str)
{
char *oo_line;
char *line;
char *line_argv[MAX_LINE_ARGC];
int line_argc;
if (!(oo_line = get_oo_line(str))) {
printf("No OO line found for %s\n", str);
exit(1);
}
if (!(line = strdup(oo_line)))
exit(1);
split_line(line, &line_argc, line_argv, ' ');
add_optional_opt_line(cmd, line_argc, line_argv);
free(line);
}
static void add_opt_arg(struct command *cmd, char *str, int *takes_arg, int required)
{
char *comma;
int opt;
/* opt_arg.opt set here */
/* opt_arg.def will be set in update_prev_opt_arg() if needed */
if ((comma = strstr(str, ",")))
*comma = '\0';
/*
* Work around nasty hack where --uuid is used for both uuid_ARG
* and uuidstr_ARG. The input uses --uuidstr, where an actual
* command uses --uuid string.
*/
if (!strcmp(str, "--uuidstr")) {
opt = uuidstr_ARG;
goto skip;
}
opt = opt_str_to_num(str);
skip:
if (required)
cmd->required_opt_args[cmd->ro_count++].opt = opt;
else
cmd->optional_opt_args[cmd->oo_count++].opt = opt;
*takes_arg = opt_names[opt].val_enum ? 1 : 0;
}
static void update_prev_opt_arg(struct command *cmd, char *str, int required)
{
struct arg_def def = { 0 };
char *comma;
if (str[0] == '-') {
printf("Option %s must be followed by an arg.\n", str);
exit(1);
}
/* opt_arg.def set here */
/* opt_arg.opt was previously set in add_opt_arg() when --foo was read */
if ((comma = strstr(str, ",")))
*comma = '\0';
set_opt_def(cmd, str, &def);
if (required)
cmd->required_opt_args[cmd->ro_count-1].def = def;
else
cmd->optional_opt_args[cmd->oo_count-1].def = def;
}
static void add_pos_arg(struct command *cmd, char *str, int required)
{
struct arg_def def = { 0 };
/* pos_arg.pos and pos_arg.def are set here */
set_pos_def(cmd, str, &def);
if (required) {
cmd->required_pos_args[cmd->rp_count].pos = cmd->pos_count++;
cmd->required_pos_args[cmd->rp_count].def = def;
cmd->rp_count++;
} else {
cmd->optional_pos_args[cmd->op_count].pos = cmd->pos_count++;;
cmd->optional_pos_args[cmd->op_count].def = def;
cmd->op_count++;
}
}
/* process something that follows a pos arg, which is not a new pos arg */
static void update_prev_pos_arg(struct command *cmd, char *str, int required)
{
struct arg_def *def;
/* a previous pos_arg.def is modified here */
if (required)
def = &cmd->required_pos_args[cmd->rp_count-1].def;
else
def = &cmd->optional_pos_args[cmd->op_count-1].def;
if (!strcmp(str, "..."))
def->flags |= ARG_DEF_FLAG_MAY_REPEAT;
else {
printf("Unknown pos arg: %s\n", str);
exit(1);
}
}
/* process what follows OO:, which are optional opt args */
static void add_optional_opt_line(struct command *cmd, int argc, char *argv[])
{
int takes_arg;
int i;
for (i = 0; i < argc; i++) {
if (!i && !strncmp(argv[i], "OO:", 3))
continue;
if (is_opt_name(argv[i]))
add_opt_arg(cmd, argv[i], &takes_arg, OPTIONAL);
else if (!strncmp(argv[i], "OO_", 3))
include_optional_opt_args(cmd, argv[i]);
else if (takes_arg)
update_prev_opt_arg(cmd, argv[i], OPTIONAL);
else
printf("Can't parse argc %d argv %s prev %s\n",
i, argv[i], argv[i-1]);
}
}
/* process what follows OP:, which are optional pos args */
static void add_optional_pos_line(struct command *cmd, int argc, char *argv[])
{
int i;
for (i = 0; i < argc; i++) {
if (!i && !strncmp(argv[i], "OP:", 3))
continue;
if (is_pos_name(argv[i]))
add_pos_arg(cmd, argv[i], OPTIONAL);
else
update_prev_pos_arg(cmd, argv[i], OPTIONAL);
}
}
/* add required opt args from OO_FOO definition */
static void add_required_opt_line(struct command *cmd, int argc, char *argv[])
{
int takes_arg;
int i;
for (i = 0; i < argc; i++) {
if (is_opt_name(argv[i]))
add_opt_arg(cmd, argv[i], &takes_arg, REQUIRED);
else if (takes_arg)
update_prev_opt_arg(cmd, argv[i], REQUIRED);
else
printf("Can't parse argc %d argv %s prev %s\n",
i, argv[i], argv[i-1]);
}
}
/* add to required_opt_args when OO_FOO appears on required line */
static void include_required_opt_args(struct command *cmd, char *str)
{
char *oo_line;
char *line;
char *line_argv[MAX_LINE_ARGC];
int line_argc;
if (!(oo_line = get_oo_line(str))) {
printf("No OO line found for %s\n", str);
exit(1);
}
if (!(line = strdup(oo_line)))
exit(1);
split_line(line, &line_argc, line_argv, ' ');
add_required_opt_line(cmd, line_argc, line_argv);
free(line);
}
/* process what follows command_name, which are required opt/pos args */
static void add_required_line(struct command *cmd, int argc, char *argv[])
{
int i;
int takes_arg;
int prev_was_opt = 0, prev_was_pos = 0;
/* argv[0] is command name */
for (i = 1; i < argc; i++) {
if (is_opt_name(argv[i])) {
add_opt_arg(cmd, argv[i], &takes_arg, REQUIRED);
prev_was_opt = 1;
prev_was_pos = 0;
} else if (prev_was_opt && takes_arg) {
update_prev_opt_arg(cmd, argv[i], REQUIRED);
prev_was_opt = 0;
prev_was_pos = 0;
} else if (is_pos_name(argv[i])) {
add_pos_arg(cmd, argv[i], REQUIRED);
prev_was_opt = 0;
prev_was_pos = 1;
} else if (!strncmp(argv[i], "OO_", 3)) {
cmd->cmd_flags |= CMD_FLAG_ONE_REQUIRED_OPT;
include_required_opt_args(cmd, argv[i]);
} else if (prev_was_pos) {
update_prev_pos_arg(cmd, argv[i], REQUIRED);
} else
printf("Can't parse argc %d argv %s prev %s\n",
i, argv[i], argv[i-1]);
}
}
static void print_def(struct arg_def *def, int usage)
{
int val_enum;
int lvt_enum;
int sep = 0;
int i;
for (val_enum = 0; val_enum < VAL_COUNT; val_enum++) {
if (def->val_bits & val_enum_to_bit(val_enum)) {
if (val_enum == conststr_VAL)
printf("%s", def->str);
else if (val_enum == constnum_VAL)
printf("%llu", (unsigned long long)def->num);
else {
if (sep) printf("|");
if (!usage || !val_names[val_enum].usage)
printf("%s", val_names[val_enum].name);
else
printf("%s", val_names[val_enum].usage);
sep = 1;
}
if (val_enum == lv_VAL && def->lvt_bits) {
for (lvt_enum = 1; lvt_enum < LVT_COUNT; lvt_enum++) {
if (lvt_bit_is_set(def->lvt_bits, lvt_enum))
printf("_%s", lvt_enum_to_name(lvt_enum));
}
}
if ((val_enum == pv_VAL) || (val_enum == vg_VAL) || (val_enum == lv_VAL)) {
if (def->flags & ARG_DEF_FLAG_NEW)
printf("_new");
}
}
}
if (def->flags & ARG_DEF_FLAG_MAY_REPEAT)
printf(" ...");
}
void print_expanded(void)
{
struct command *cmd;
int onereq;
int i, ro, rp, oo, op;
for (i = 0; i < cmd_count; i++) {
cmd = &cmd_array[i];
printf("%s", cmd->name);
onereq = (cmd->cmd_flags & CMD_FLAG_ONE_REQUIRED_OPT) ? 1 : 0;
if (cmd->ro_count) {
if (onereq)
printf(" (");
for (ro = 0; ro < cmd->ro_count; ro++) {
if (ro && onereq)
printf(",");
printf(" %s", opt_names[cmd->required_opt_args[ro].opt].long_opt);
if (cmd->required_opt_args[ro].def.val_bits) {
printf(" ");
print_def(&cmd->required_opt_args[ro].def, 0);
}
}
if (onereq)
printf(" )");
}
if (cmd->rp_count) {
for (rp = 0; rp < cmd->rp_count; rp++) {
if (cmd->required_pos_args[rp].def.val_bits) {
printf(" ");
print_def(&cmd->required_pos_args[rp].def, 0);
}
}
}
if (cmd->oo_count) {
printf("\n");
printf("OO:");
for (oo = 0; oo < cmd->oo_count; oo++) {
if (oo)
printf(",");
printf(" %s", opt_names[cmd->optional_opt_args[oo].opt].long_opt);
if (cmd->optional_opt_args[oo].def.val_bits) {
printf(" ");
print_def(&cmd->optional_opt_args[oo].def, 0);
}
}
}
if (cmd->op_count) {
printf("\n");
printf("OP:");
for (op = 0; op < cmd->op_count; op++) {
if (cmd->optional_pos_args[op].def.val_bits) {
printf(" ");
print_def(&cmd->optional_pos_args[op].def, 0);
}
}
}
printf("\n\n");
}
}
static int opt_arg_matches(struct opt_arg *oa1, struct opt_arg *oa2)
{
if (oa1->opt != oa2->opt)
return 0;
/* FIXME: some cases may need more specific val_bits checks */
if (oa1->def.val_bits != oa2->def.val_bits)
return 0;
if (oa1->def.str && oa2->def.str && strcmp(oa1->def.str, oa2->def.str))
return 0;
if (oa1->def.num != oa2->def.num)
return 0;
/*
* Do NOT compare lv_types because we are checking if two
* command lines are ambiguous before the LV type is known.
*/
return 1;
}
static int pos_arg_matches(struct pos_arg *pa1, struct pos_arg *pa2)
{
if (pa1->pos != pa2->pos)
return 0;
/* FIXME: some cases may need more specific val_bits checks */
if (pa1->def.val_bits != pa2->def.val_bits)
return 0;
if (pa1->def.str && pa2->def.str && strcmp(pa1->def.str, pa2->def.str))
return 0;
if (pa1->def.num != pa2->def.num)
return 0;
/*
* Do NOT compare lv_types because we are checking if two
* command lines are ambiguous before the LV type is known.
*/
return 1;
}
static const char *opt_to_enum_str(int opt)
{
return opt_names[opt].name;
}
static char *flags_to_str(int flags)
{
static char buf_flags[32];
memset(buf_flags, 0, sizeof(buf_flags));
if (flags & ARG_DEF_FLAG_MAY_REPEAT) {
if (buf_flags[0])
strcat(buf_flags, " | ");
strcat(buf_flags, "ARG_DEF_FLAG_MAY_REPEAT");
}
if (flags & ARG_DEF_FLAG_NEW) {
if (buf_flags[0])
strcat(buf_flags, " | ");
strcat(buf_flags, "ARG_DEF_FLAG_NEW");
}
return buf_flags;
}
static void add_flags(struct command *cmd, char *line)
{
if (strstr(line, "SECONDARY_SYNTAX"))
cmd->cmd_flags |= CMD_FLAG_SECONDARY_SYNTAX;
}
#define MAX_RULE_OPTS 64
static void add_rule(struct command *cmd, char *line)
{
struct cmd_rule *rule;
char *line_argv[MAX_LINE_ARGC];
char *arg;
int line_argc;
int i, lvt_enum, lvp_enum;
int check = 0;
if (cmd->rule_count == CMD_MAX_RULES) {
printf("too many rules for cmd\n");
exit(1);
}
rule = &cmd->rules[cmd->rule_count++];
split_line(line, &line_argc, line_argv, ' ');
for (i = 0; i < line_argc; i++) {
arg = line_argv[i];
if (!strcmp(arg, "not")) {
rule->rule = RULE_INVALID;
check = 1;
}
else if (!strcmp(arg, "and")) {
rule->rule = RULE_REQUIRE;
check = 1;
}
else if (!strncmp(arg, "all", 3)) {
/* opt/lvt_bits/lvp_bits all remain 0 to mean all */
continue;
}
else if (!strncmp(arg, "--", 2)) {
if (!rule->opts) {
if (!(rule->opts = malloc(MAX_RULE_OPTS * sizeof(int)))) {
printf("no mem\n");
exit(1);
}
memset(rule->opts, 0, MAX_RULE_OPTS * sizeof(int));
}
if (!rule->check_opts) {
if (!(rule->check_opts = malloc(MAX_RULE_OPTS * sizeof(int)))) {
printf("no mem\n");
exit(1);
}
memset(rule->check_opts, 0, MAX_RULE_OPTS * sizeof(int));
}
if (check)
rule->check_opts[rule->check_opts_count++] = opt_str_to_num(arg);
else
rule->opts[rule->opts_count++] = opt_str_to_num(arg);
}
else if (!strncmp(arg, "LV_", 3)) {
lvt_enum = lv_to_enum(arg);
if (check)
rule->check_lvt_bits |= lvt_enum_to_bit(lvt_enum);
else
rule->lvt_bits |= lvt_enum_to_bit(lvt_enum);
}
else if (!strncmp(arg, "lv_is_", 6)) {
lvp_enum = lvp_name_to_enum(arg);
if (check)
rule->check_lvp_bits |= lvp_enum_to_bit(lvp_enum);
else
rule->lvp_bits |= lvp_enum_to_bit(lvp_enum);
}
}
}
static char *rule_to_define_str(int rule_type)
{
switch (rule_type) {
case RULE_INVALID:
return "RULE_INVALID";
case RULE_REQUIRE:
return "RULE_REQUIRE";
}
}
static char *cmd_flags_to_str(uint32_t flags)
{
static char buf_cmd_flags[32];
memset(buf_cmd_flags, 0, sizeof(buf_cmd_flags));
if (flags & CMD_FLAG_SECONDARY_SYNTAX) {
if (buf_cmd_flags[0])
strcat(buf_cmd_flags, " | ");
strcat(buf_cmd_flags, "CMD_FLAG_SECONDARY_SYNTAX");
}
if (flags & CMD_FLAG_ONE_REQUIRED_OPT) {
if (buf_cmd_flags[0])
strcat(buf_cmd_flags, " | ");
strcat(buf_cmd_flags, "CMD_FLAG_ONE_REQUIRED_OPT");
}
return buf_cmd_flags;
}
void print_command_count(void)
{
struct command *cmd;
int i, j;
printf("/* Do not edit. This file is generated by tools/create-commands */\n");
printf("/* using command definitions from tools/command-lines.in */\n");
printf("#define COMMAND_COUNT %d\n", cmd_count);
printf("enum {\n");
printf("\tno_CMD,\n"); /* enum value 0 is not used */
for (i = 0; i < cmd_count; i++) {
cmd = &cmd_array[i];
if (!cmd->command_line_id) {
printf("Missing ID: at %d\n", i);
exit(1);
}
for (j = 0; j < i; j++) {
if (!strcmp(cmd->command_line_id, cmd_array[j].command_line_id))
goto next;
}
printf("\t%s_CMD,\n", cmd->command_line_id);
next:
;
}
printf("\tCOMMAND_ID_COUNT,\n");
printf("};\n");
}
static int is_lvm_all_opt(int opt)
{
int oo;
for (oo = 0; oo < lvm_all.oo_count; oo++) {
if (lvm_all.optional_opt_args[oo].opt == opt)
return 1;
}
return 0;
}
static void factor_common_options(void)
{
int cn, opt_enum, ci, oo, ro, found;
struct command *cmd;
for (cn = 0; cn < MAX_CMD_NAMES; cn++) {
if (!cmd_names[cn].name)
break;
for (ci = 0; ci < cmd_count; ci++) {
cmd = &cmd_array[ci];
if (strcmp(cmd->name, cmd_names[cn].name))
continue;
cmd_names[cn].variants++;
}
for (opt_enum = 0; opt_enum < ARG_COUNT; opt_enum++) {
for (ci = 0; ci < cmd_count; ci++) {
cmd = &cmd_array[ci];
if (strcmp(cmd->name, cmd_names[cn].name))
continue;
if (cmd->ro_count)
cmd_names[cn].variant_has_ro = 1;
if (cmd->rp_count)
cmd_names[cn].variant_has_rp = 1;
if (cmd->oo_count)
cmd_names[cn].variant_has_oo = 1;
if (cmd->op_count)
cmd_names[cn].variant_has_op = 1;
for (ro = 0; ro < cmd->ro_count; ro++) {
cmd_names[cn].all_options[cmd->required_opt_args[ro].opt] = 1;
if ((cmd->required_opt_args[ro].opt == size_ARG) && !strncmp(cmd->name, "lv", 2))
cmd_names[cn].all_options[extents_ARG] = 1;
}
for (oo = 0; oo < cmd->oo_count; oo++)
cmd_names[cn].all_options[cmd->optional_opt_args[oo].opt] = 1;
found = 0;
for (oo = 0; oo < cmd->oo_count; oo++) {
if (cmd->optional_opt_args[oo].opt == opt_enum) {
found = 1;
break;
}
}
if (!found)
goto next_opt;
}
/* all commands starting with this name use this option */
cmd_names[cn].common_options[opt_enum] = 1;
next_opt:
;
}
}
/*
for (cn = 0; cn < MAX_CMD_NAMES; cn++) {
if (!cmd_names[cn].name)
break;
printf("%s (%d)\n", cmd_names[cn].name, cmd_names[cn].variants);
for (opt_enum = 0; opt_enum < ARG_COUNT; opt_enum++) {
if (cmd_names[cn].common_options[opt_enum])
printf(" %s\n", opt_names[opt_enum].long_opt);
}
}
*/
}
void print_usage_common(struct command *cmd)
{
struct cmd_name *cname;
int i, sep, ro, rp, oo, op, opt_enum;
if (!(cname = find_command_name(cmd->name)))
return;
sep = 0;
/*
* when there's more than one variant, options that
* are common to all commands with a common name.
*/
if (cname->variants < 2)
goto all;
for (opt_enum = 0; opt_enum < ARG_COUNT; opt_enum++) {
if (!cname->common_options[opt_enum])
continue;
if (is_lvm_all_opt(opt_enum))
continue;
if (!sep) {
printf("\n");
printf("\" [");
} else {
printf(",");
}
for (oo = 0; oo < cmd->oo_count; oo++) {
if (cmd->optional_opt_args[oo].opt != opt_enum)
continue;
printf(" %s", opt_names[opt_enum].long_opt);
if (cmd->optional_opt_args[oo].def.val_bits) {
printf(" ");
print_def(&cmd->optional_opt_args[oo].def, 1);
}
sep = 1;
break;
}
}
all:
/* options that are common to all lvm commands */
for (oo = 0; oo < lvm_all.oo_count; oo++) {
opt_enum = lvm_all.optional_opt_args[oo].opt;
if (!sep) {
printf("\n");
printf("\" [");
} else {
printf(",");
}
printf(" %s", opt_names[opt_enum].long_opt);
if (lvm_all.optional_opt_args[oo].def.val_bits) {
printf(" ");
print_def(&lvm_all.optional_opt_args[oo].def, 1);
}
sep = 1;
}
printf(" ]\"");
printf(";\n");
}
void print_usage(struct command *cmd)
{
struct cmd_name *cname;
int onereq = (cmd->cmd_flags & CMD_FLAG_ONE_REQUIRED_OPT) ? 1 : 0;
int i, sep, ro, rp, oo, op, opt_enum;
if (!(cname = find_command_name(cmd->name)))
return;
printf("\"%s", cmd->name);
if (cmd->ro_count) {
if (onereq)
printf(" (");
for (ro = 0; ro < cmd->ro_count; ro++) {
if (ro && onereq)
printf(",");
printf(" %s", opt_names[cmd->required_opt_args[ro].opt].long_opt);
if (cmd->required_opt_args[ro].def.val_bits) {
printf(" ");
print_def(&cmd->required_opt_args[ro].def, 1);
}
}
if (onereq)
printf(" )");
}
if (cmd->rp_count) {
for (rp = 0; rp < cmd->rp_count; rp++) {
if (cmd->required_pos_args[rp].def.val_bits) {
printf(" ");
print_def(&cmd->required_pos_args[rp].def, 1);
}
}
}
printf("\"");
oo_count:
if (!cmd->oo_count)
goto op_count;
sep = 0;
if (cmd->oo_count) {
printf("\n");
printf("\" [");
for (oo = 0; oo < cmd->oo_count; oo++) {
opt_enum = cmd->optional_opt_args[oo].opt;
/*
* Skip common opts which are in the usage_common string.
* The common opts are those in lvm_all and in
* cname->common_options.
*/
if (is_lvm_all_opt(opt_enum))
continue;
if ((cname->variants > 1) && cname->common_options[opt_enum])
continue;
if (sep)
printf(",");
printf(" %s", opt_names[opt_enum].long_opt);
if (cmd->optional_opt_args[oo].def.val_bits) {
printf(" ");
print_def(&cmd->optional_opt_args[oo].def, 1);
}
sep = 1;
}
if (sep)
printf(",");
printf(" COMMON_OPTIONS");
printf(" ]\"");
}
op_count:
if (!cmd->op_count)
goto done;
printf("\n");
printf("\" [");
if (cmd->op_count) {
for (op = 0; op < cmd->op_count; op++) {
if (cmd->optional_pos_args[op].def.val_bits) {
printf(" ");
print_def(&cmd->optional_pos_args[op].def, 1);
}
}
}
printf(" ]\"");
done:
printf(";\n");
}
static void print_val_man(const char *str)
{
char *line;
char *line_argv[MAX_LINE_ARGC];
int line_argc;
int i;
if (!strcmp(str, "Number") ||
!strcmp(str, "String") ||
!strncmp(str, "VG", 2) ||
!strncmp(str, "LV", 2) ||
!strncmp(str, "PV", 2) ||
!strcmp(str, "Tag")) {
printf("\\fI%s\\fP", str);
return;
}
if (strstr(str, "Number[") || strstr(str, "]Number")) {
for (i = 0; i < strlen(str); i++) {
if (str[i] == 'N')
printf("\\fI");
if (str[i] == 'r') {
printf("%c", str[i]);
printf("\\fP");
continue;
}
printf("%c", str[i]);
}
return;
}
if (strstr(str, "|")) {
int len = strlen(str);
line = strdup(str);
split_line(line, &line_argc, line_argv, '|');
for (i = 0; i < line_argc; i++) {
if (i) {
printf("|");
/* this is a hack to add a line break for
a long string of opt values */
if ((len > 40) && (i >= (line_argc / 2) + 1)) {
printf("\n");
printf(" ");
len = 0;
}
}
if (strstr(line_argv[i], "Number"))
printf("\\fI%s\\fP", line_argv[i]);
else
printf("\\fB%s\\fP", line_argv[i]);
}
return;
}
printf("\\fB%s\\fP", str);
}
static void print_def_man(struct arg_def *def, int usage)
{
int val_enum;
int lvt_enum;
int sep = 0;
int i;
for (val_enum = 0; val_enum < VAL_COUNT; val_enum++) {
if (def->val_bits & val_enum_to_bit(val_enum)) {
if (val_enum == conststr_VAL) {
printf("\\fB");
printf("%s", def->str);
printf("\\fP");
}
else if (val_enum == constnum_VAL) {
printf("\\fB");
printf("%llu", (unsigned long long)def->num);
printf("\\fP");
}
else {
if (sep) printf("|");
if (!usage || !val_names[val_enum].usage) {
printf("\\fI");
printf("%s", val_names[val_enum].name);
printf("\\fP");
} else {
print_val_man(val_names[val_enum].usage);
}
sep = 1;
}
if (val_enum == lv_VAL && def->lvt_bits) {
printf("\\fI");
for (lvt_enum = 1; lvt_enum < LVT_COUNT; lvt_enum++) {
if (lvt_bit_is_set(def->lvt_bits, lvt_enum))
printf("_%s", lvt_enum_to_name(lvt_enum));
}
printf("\\fP");
}
if ((val_enum == pv_VAL) || (val_enum == vg_VAL) || (val_enum == lv_VAL)) {
if (def->flags & ARG_DEF_FLAG_NEW) {
printf("\\fI");
printf("_new");
printf("\\fP");
}
}
}
}
if (def->flags & ARG_DEF_FLAG_MAY_REPEAT)
printf(" ...");
}
static char *man_long_opt_name(const char *cmdname, int opt_enum)
{
static char long_opt_name[64];
memset(&long_opt_name, 0, sizeof(long_opt_name));
switch (opt_enum) {
case syncaction_ARG:
strncpy(long_opt_name, "--[raid]syncaction", 63);
break;
case writemostly_ARG:
strncpy(long_opt_name, "--[raid]writemostly", 63);
break;
case minrecoveryrate_ARG:
strncpy(long_opt_name, "--[raid]minrecoveryrate", 63);
break;
case maxrecoveryrate_ARG:
strncpy(long_opt_name, "--[raid]maxrecoveryrate", 63);
break;
case writebehind_ARG:
strncpy(long_opt_name, "--[raid]writebehind", 63);
break;
case vgmetadatacopies_ARG:
if (!strncmp(cmdname, "vg", 2))
strncpy(long_opt_name, "--[vg]metadatacopies", 63);
else
strncpy(long_opt_name, "--vgmetadatacopies", 63);
break;
case pvmetadatacopies_ARG:
if (!strncmp(cmdname, "pv", 2))
strncpy(long_opt_name, "--[pv]metadatacopies", 63);
else
strncpy(long_opt_name, "--pvmetadatacopies", 63);
break;
default:
strncpy(long_opt_name, opt_names[opt_enum].long_opt, 63);
break;
}
return long_opt_name;
}
void print_man_usage(struct command *cmd)
{
struct cmd_name *cname;
int onereq = (cmd->cmd_flags & CMD_FLAG_ONE_REQUIRED_OPT) ? 1 : 0;
int i, sep, ro, rp, oo, op, opt_enum;
if (!(cname = find_command_name(cmd->name)))
return;
printf("\\fB%s\\fP", cmd->name);
if (!onereq)
goto ro_normal;
/*
* one required option in a set, print as:
* ( -a|--a,
* -b|--b,
* --c,
* --d )
*
* First loop through ro prints those with short opts,
* and the second loop prints those without short opts.
*/
if (cmd->ro_count) {
printf("\n");
printf(".RS 4\n");
printf("(");
sep = 0;
/* print required options with a short opt */
for (ro = 0; ro < cmd->ro_count; ro++) {
opt_enum = cmd->required_opt_args[ro].opt;
if (!opt_names[opt_enum].short_opt)
continue;
if (sep) {
printf(",");
printf("\n.br\n");
printf(" ");
}
if (opt_names[opt_enum].short_opt) {
printf(" \\fB-%c\\fP|\\fB%s\\fP",
opt_names[opt_enum].short_opt,
man_long_opt_name(cmd->name, opt_enum));
} else {
printf(" ");
printf(" \\fB%s\\fP", man_long_opt_name(cmd->name, opt_enum));
}
if (cmd->required_opt_args[ro].def.val_bits) {
printf(" ");
print_def_man(&cmd->required_opt_args[ro].def, 1);
}
sep = 1;
}
/* print required options without a short opt */
for (ro = 0; ro < cmd->ro_count; ro++) {
opt_enum = cmd->required_opt_args[ro].opt;
if (opt_names[opt_enum].short_opt)
continue;
if (sep) {
printf(",");
printf("\n.br\n");
printf(" ");
}
printf(" ");
printf(" \\fB%s\\fP", man_long_opt_name(cmd->name, opt_enum));
if (cmd->required_opt_args[ro].def.val_bits) {
printf(" ");
print_def_man(&cmd->required_opt_args[ro].def, 1);
}
sep = 1;
}
printf(" )\n");
printf(".RE\n");
}
/* print required position args on a new line after the onereq set */
if (cmd->rp_count) {
printf(".RS 4\n");
for (rp = 0; rp < cmd->rp_count; rp++) {
if (cmd->required_pos_args[rp].def.val_bits) {
printf(" ");
print_def_man(&cmd->required_pos_args[rp].def, 1);
}
}
printf("\n");
printf(".RE\n");
} else {
/* printf("\n"); */
}
printf(".br\n");
goto oo_count;
ro_normal:
/*
* all are required options, print as:
* -a|--aaa <val> -b|--bbb <val>
*/
if (cmd->ro_count) {
for (ro = 0; ro < cmd->ro_count; ro++) {
opt_enum = cmd->required_opt_args[ro].opt;
if (opt_names[opt_enum].short_opt) {
printf(" \\fB-%c\\fP|\\fB%s\\fP",
opt_names[opt_enum].short_opt,
man_long_opt_name(cmd->name, opt_enum));
} else {
printf(" \\fB%s\\fP", opt_names[cmd->required_opt_args[ro].opt].long_opt);
}
if (cmd->required_opt_args[ro].def.val_bits) {
printf(" ");
print_def_man(&cmd->required_opt_args[ro].def, 1);
}
}
}
/* print required position args on the same line as the required options */
if (cmd->rp_count) {
for (rp = 0; rp < cmd->rp_count; rp++) {
if (cmd->required_pos_args[rp].def.val_bits) {
printf(" ");
print_def_man(&cmd->required_pos_args[rp].def, 1);
}
}
printf("\n");
} else {
printf("\n");
}
printf(".br\n");
oo_count:
if (!cmd->oo_count)
goto op_count;
sep = 0;
if (cmd->oo_count) {
printf(".RS 4\n");
printf("[");
/* print optional options with short opts */
for (oo = 0; oo < cmd->oo_count; oo++) {
opt_enum = cmd->optional_opt_args[oo].opt;
if (!opt_names[opt_enum].short_opt)
continue;
/*
* Skip common opts which are in the usage_common string.
* The common opts are those in lvm_all and in
* cname->common_options.
*/
if (is_lvm_all_opt(opt_enum))
continue;
if ((cname->variants > 1) && cname->common_options[opt_enum])
continue;
if (sep) {
printf(",");
printf("\n.br\n");
printf(" ");
}
printf(" \\fB-%c\\fP|\\fB%s\\fP",
opt_names[opt_enum].short_opt,
man_long_opt_name(cmd->name, opt_enum));
if (cmd->optional_opt_args[oo].def.val_bits) {
printf(" ");
print_def_man(&cmd->optional_opt_args[oo].def, 1);
}
sep = 1;
}
/* print optional options without short opts */
for (oo = 0; oo < cmd->oo_count; oo++) {
opt_enum = cmd->optional_opt_args[oo].opt;
if (opt_names[opt_enum].short_opt)
continue;
/*
* Skip common opts which are in the usage_common string.
* The common opts are those in lvm_all and in
* cname->common_options.
*/
if (is_lvm_all_opt(opt_enum))
continue;
if ((cname->variants > 1) && cname->common_options[opt_enum])
continue;
if (sep) {
printf(",");
printf("\n.br\n");
printf(" ");
}
/* space alignment without short opt */
printf(" ");
printf(" \\fB%s\\fP", man_long_opt_name(cmd->name, opt_enum));
if (cmd->optional_opt_args[oo].def.val_bits) {
printf(" ");
print_def_man(&cmd->optional_opt_args[oo].def, 1);
}
sep = 1;
}
if (sep) {
printf(",");
printf("\n.br\n");
printf(" ");
/* space alignment without short opt */
printf(" ");
}
printf(" COMMON_OPTIONS");
printf(" ]\n");
printf(".RE\n");
printf(".br\n");
}
op_count:
if (!cmd->op_count)
goto done;
printf(".RS 4\n");
printf("[");
if (cmd->op_count) {
for (op = 0; op < cmd->op_count; op++) {
if (cmd->optional_pos_args[op].def.val_bits) {
printf(" ");
print_def_man(&cmd->optional_pos_args[op].def, 1);
}
}
}
printf(" ]\n");
printf(".RE\n");
done:
printf("\n");
}
void print_man_usage_common(struct command *cmd)
{
struct cmd_name *cname;
int i, sep, ro, rp, oo, op, opt_enum;
if (!(cname = find_command_name(cmd->name)))
return;
sep = 0;
printf(".RS 4\n");
printf("[");
/*
* when there's more than one variant, options that
* are common to all commands with a common name.
*/
if (cname->variants < 2)
goto all;
/* print those with short opts */
for (opt_enum = 0; opt_enum < ARG_COUNT; opt_enum++) {
if (!cname->common_options[opt_enum])
continue;
if (!opt_names[opt_enum].short_opt)
continue;
if (is_lvm_all_opt(opt_enum))
continue;
if (sep) {
printf(",");
printf("\n.br\n");
printf(" ");
}
for (oo = 0; oo < cmd->oo_count; oo++) {
if (cmd->optional_opt_args[oo].opt != opt_enum)
continue;
printf(" \\fB-%c\\fP|\\fB%s\\fP",
opt_names[opt_enum].short_opt,
man_long_opt_name(cmd->name, opt_enum));
if (cmd->optional_opt_args[oo].def.val_bits) {
printf(" ");
print_def_man(&cmd->optional_opt_args[oo].def, 1);
}
sep = 1;
break;
}
}
/* print those without short opts */
for (opt_enum = 0; opt_enum < ARG_COUNT; opt_enum++) {
if (!cname->common_options[opt_enum])
continue;
if (opt_names[opt_enum].short_opt)
continue;
if (is_lvm_all_opt(opt_enum))
continue;
if (sep) {
printf(",");
printf("\n.br\n");
printf(" ");
}
for (oo = 0; oo < cmd->oo_count; oo++) {
if (cmd->optional_opt_args[oo].opt != opt_enum)
continue;
/* space alignment without short opt */
printf(" ");
printf(" \\fB%s\\fP", man_long_opt_name(cmd->name, opt_enum));
if (cmd->optional_opt_args[oo].def.val_bits) {
printf(" ");
print_def_man(&cmd->optional_opt_args[oo].def, 1);
}
sep = 1;
break;
}
}
all:
/* options that are common to all lvm commands */
/* those with short opts */
for (oo = 0; oo < lvm_all.oo_count; oo++) {
opt_enum = lvm_all.optional_opt_args[oo].opt;
if (!opt_names[opt_enum].short_opt)
continue;
if (sep) {
printf(",");
printf("\n.br\n");
printf(" ");
}
printf(" \\fB-%c\\fP|\\fB%s\\fP",
opt_names[opt_enum].short_opt,
man_long_opt_name(cmd->name, opt_enum));
if (lvm_all.optional_opt_args[oo].def.val_bits) {
printf(" ");
print_def(&lvm_all.optional_opt_args[oo].def, 1);
}
sep = 1;
}
/* those without short opts */
for (oo = 0; oo < lvm_all.oo_count; oo++) {
opt_enum = lvm_all.optional_opt_args[oo].opt;
if (opt_names[opt_enum].short_opt)
continue;
if (sep) {
printf(",");
printf("\n.br\n");
printf(" ");
}
/* space alignment without short opt */
printf(" ");
printf(" \\fB%s\\fP", man_long_opt_name(cmd->name, opt_enum));
if (lvm_all.optional_opt_args[oo].def.val_bits) {
printf(" ");
print_def(&lvm_all.optional_opt_args[oo].def, 1);
}
sep = 1;
}
printf(" ]\n");
}
void print_man_all_options(struct cmd_name *cname)
{
int opt_enum, val_enum;
int sep = 0;
/* print those with short opts */
for (opt_enum = 0; opt_enum < ARG_COUNT; opt_enum++) {
if (!cname->all_options[opt_enum])
continue;
if (!opt_names[opt_enum].short_opt)
continue;
if (sep)
printf("\n.br\n");
printf(" \\fB-%c\\fP|\\fB%s\\fP",
opt_names[opt_enum].short_opt,
man_long_opt_name(cname->name, opt_enum));
val_enum = opt_names[opt_enum].val_enum;
if (!val_names[val_enum].fn) {
/* takes no arg */
} else if (!val_names[val_enum].usage) {
printf(" ");
printf("\\fI");
printf("%s", val_names[val_enum].name);
printf("\\fP");
} else {
printf(" ");
print_val_man(val_names[val_enum].usage);
}
sep = 1;
}
/* print those without short opts */
for (opt_enum = 0; opt_enum < ARG_COUNT; opt_enum++) {
if (!cname->all_options[opt_enum])
continue;
if (opt_names[opt_enum].short_opt)
continue;
if (sep)
printf("\n.br\n");
/* space alignment without short opt */
printf(" ");
printf(" \\fB%s\\fP", man_long_opt_name(cname->name, opt_enum));
val_enum = opt_names[opt_enum].val_enum;
if (!val_names[val_enum].fn) {
/* takes no arg */
} else if (!val_names[val_enum].usage) {
printf(" ");
printf("\\fI");
printf("%s", val_names[val_enum].name);
printf("\\fP");
} else {
printf(" ");
print_val_man(val_names[val_enum].usage);
}
sep = 1;
}
}
#define DESC_LINE 256
void print_desc_man(const char *desc)
{
char buf[DESC_LINE] = {0};
int di = 0;
int bi = 0;
for (di = 0; di < strlen(desc); di++) {
if (desc[di] == '\0')
break;
if (desc[di] == '\n')
continue;
if (!strncmp(&desc[di], "DESC:", 5)) {
if (bi) {
printf("%s\n", buf);
printf(".br\n");
memset(buf, 0, sizeof(buf));
bi = 0;
}
di += 5;
continue;
}
if (!bi && desc[di] == ' ')
continue;
buf[bi++] = desc[di];
if (bi == (DESC_LINE - 1))
break;
}
if (bi) {
printf("%s\n", buf);
printf(".br\n");
}
}
static char *upper_command_name(char *str)
{
static char str_upper[32];
int i = 0;
while (*str) {
str_upper[i++] = toupper(*str);
str++;
}
str_upper[i] = '\0';
return str_upper;
}
void print_man_command(void)
{
struct cmd_name *cname;
struct command *cmd, *prev_cmd = NULL;
const char *desc;
int i, j, ro, rp, oo, op;
include_optional_opt_args(&lvm_all, "OO_USAGE_COMMON");
printf(".TH %s 8 \"LVM TOOLS #VERSION#\" \"Sistina Software UK\"\n",
man_command_name ? upper_command_name(man_command_name) : "LVM_COMMANDS");
for (i = 0; i < cmd_count; i++) {
cmd = &cmd_array[i];
if (prev_cmd && strcmp(prev_cmd->name, cmd->name)) {
printf("Common options:\n");
printf(".\n");
print_man_usage_common(prev_cmd);
prev_cmd = NULL;
}
if ((cmd->cmd_flags & CMD_FLAG_SECONDARY_SYNTAX) && !include_man_secondary)
continue;
if (!(cmd->cmd_flags & CMD_FLAG_SECONDARY_SYNTAX) && !include_man_primary)
continue;
if (man_command_name && strcmp(man_command_name, cmd->name))
continue;
if (!prev_cmd || strcmp(prev_cmd->name, cmd->name)) {
printf(".SH NAME\n");
printf(".\n");
if ((desc = cmd_name_desc(cmd->name)))
printf("%s \\- %s\n", cmd->name, desc);
else
printf("%s\n", cmd->name);
printf(".br\n");
printf(".P\n");
printf(".\n");
printf(".SH SYNOPSIS\n");
printf(".br\n");
printf(".P\n");
printf(".\n");
prev_cmd = cmd;
if (!(cname = find_command_name(cmd->name)))
return;
if (cname->variant_has_ro && cname->variant_has_rp)
printf("\\fB%s\\fP \\fIrequired_option_args\\fP \\fIrequired_position_args\\fP\n", cmd->name);
else if (cname->variant_has_ro && !cname->variant_has_rp)
printf("\\fB%s\\fP \\fIrequired_option_args\\fP\n", cmd->name);
else if (!cname->variant_has_ro && cname->variant_has_rp)
printf("\\fB%s\\fP \\fIrequired_position_args\\fP\n", cmd->name);
else if (!cname->variant_has_ro && !cname->variant_has_rp)
printf("\\fB%s\\fP\n", cmd->name);
printf(".br\n");
if (cname->variant_has_oo) {
printf(" [ \\fIoptional_option_args\\fP ]\n");
printf(".br\n");
}
if (cname->variant_has_op) {
printf(" [ \\fIoptional_position_args\\fP ]\n");
printf(".br\n");
}
printf(".P\n");
printf("\n");
/* listing them all when there's only 1 or 2 is just repetative */
if (cname->variants > 2) {
printf(".P\n");
print_man_all_options(cname);
printf("\n");
printf(".P\n");
printf("\n");
}
printf(".SH USAGE\n");
printf(".br\n");
printf(".P\n");
printf(".\n");
}
if (cmd->desc) {
print_desc_man(cmd->desc);
printf(".P\n");
}
print_man_usage(cmd);
if (i == (cmd_count - 1)) {
printf("Common options:\n");
printf(".\n");
print_man_usage_common(cmd);
}
printf("\n");
continue;
}
}
void print_command_struct(int only_usage)
{
struct command *cmd;
int i, j, ro, rp, oo, op, ru, ruo;
include_optional_opt_args(&lvm_all, "OO_USAGE_COMMON");
printf("/* Do not edit. This file is generated by tools/create-commands */\n");
printf("/* using command definitions from tools/command-lines.in */\n");
printf("\n");
for (i = 0; i < cmd_count; i++) {
cmd = &cmd_array[i];
if (only_usage) {
print_usage(cmd);
print_usage_common(cmd);
printf("\n");
continue;
}
printf("commands[%d].name = \"%s\";\n", i, cmd->name);
printf("commands[%d].command_line_id = \"%s\";\n", i, cmd->command_line_id);
printf("commands[%d].command_line_enum = %s_CMD;\n", i, cmd->command_line_id);
printf("commands[%d].fn = %s;\n", i, cmd->name);
printf("commands[%d].ro_count = %d;\n", i, cmd->ro_count);
printf("commands[%d].rp_count = %d;\n", i, cmd->rp_count);
printf("commands[%d].oo_count = %d;\n", i, cmd->oo_count);
printf("commands[%d].op_count = %d;\n", i, cmd->op_count);
printf("commands[%d].rule_count = %d;\n", i, cmd->rule_count);
if (cmd->cmd_flags)
printf("commands[%d].cmd_flags = %s;\n", i, cmd_flags_to_str(cmd->cmd_flags));
else
printf("commands[%d].cmd_flags = 0;\n", i, cmd_flags_to_str(cmd->cmd_flags));
printf("commands[%d].desc = \"%s\";\n", i, cmd->desc ?: "");
printf("commands[%d].usage = ", i);
print_usage(cmd);
if (cmd->oo_count) {
printf("commands[%d].usage_common = ", i);
print_usage_common(cmd);
} else {
printf("commands[%d].usage_common = \"NULL\";\n", i);
}
if (cmd->ro_count) {
for (ro = 0; ro < cmd->ro_count; ro++) {
printf("commands[%d].required_opt_args[%d].opt = %s;\n",
i, ro, opt_to_enum_str(cmd->required_opt_args[ro].opt));
if (!cmd->required_opt_args[ro].def.val_bits)
continue;
printf("commands[%d].required_opt_args[%d].def.val_bits = %s;\n",
i, ro, val_bits_to_str(cmd->required_opt_args[ro].def.val_bits));
if (cmd->required_opt_args[ro].def.lvt_bits)
printf("commands[%d].required_opt_args[%d].def.lvt_bits = %s;\n",
i, ro, lvt_bits_to_str(cmd->required_opt_args[ro].def.lvt_bits));
if (cmd->required_opt_args[ro].def.flags)
printf("commands[%d].required_opt_args[%d].def.flags = %s;\n",
i, ro, flags_to_str(cmd->required_opt_args[ro].def.flags));
if (val_bit_is_set(cmd->required_opt_args[ro].def.val_bits, constnum_VAL))
printf("commands[%d].required_opt_args[%d].def.num = %d;\n",
i, ro, cmd->required_opt_args[ro].def.num);
if (val_bit_is_set(cmd->required_opt_args[ro].def.val_bits, conststr_VAL))
printf("commands[%d].required_opt_args[%d].def.str = \"%s\";\n",
i, ro, cmd->required_opt_args[ro].def.str ?: "NULL");
}
}
if (cmd->rp_count) {
for (rp = 0; rp < cmd->rp_count; rp++) {
printf("commands[%d].required_pos_args[%d].pos = %d;\n",
i, rp, cmd->required_pos_args[rp].pos);
if (!cmd->required_pos_args[rp].def.val_bits)
continue;
printf("commands[%d].required_pos_args[%d].def.val_bits = %s;\n",
i, rp, val_bits_to_str(cmd->required_pos_args[rp].def.val_bits));
if (cmd->required_pos_args[rp].def.lvt_bits)
printf("commands[%d].required_pos_args[%d].def.lvt_bits = %s;\n",
i, rp, lvt_bits_to_str(cmd->required_pos_args[rp].def.lvt_bits));
if (cmd->required_pos_args[rp].def.flags)
printf("commands[%d].required_pos_args[%d].def.flags = %s;\n",
i, rp, flags_to_str(cmd->required_pos_args[rp].def.flags));
if (val_bit_is_set(cmd->required_pos_args[rp].def.val_bits, constnum_VAL))
printf("commands[%d].required_pos_args[%d].def.num = %d;\n",
i, rp, cmd->required_pos_args[rp].def.num);
if (val_bit_is_set(cmd->required_pos_args[rp].def.val_bits, conststr_VAL))
printf("commands[%d].required_pos_args[%d].def.str = \"%s\";\n",
i, rp, cmd->required_pos_args[rp].def.str ?: "NULL");
}
}
if (cmd->oo_count) {
for (oo = 0; oo < cmd->oo_count; oo++) {
printf("commands[%d].optional_opt_args[%d].opt = %s;\n",
i, oo, opt_to_enum_str(cmd->optional_opt_args[oo].opt));
if (!cmd->optional_opt_args[oo].def.val_bits)
continue;
printf("commands[%d].optional_opt_args[%d].def.val_bits = %s;\n",
i, oo, val_bits_to_str(cmd->optional_opt_args[oo].def.val_bits));
if (cmd->optional_opt_args[oo].def.lvt_bits)
printf("commands[%d].optional_opt_args[%d].def.lvt_bits = %s;\n",
i, oo, lvt_bits_to_str(cmd->optional_opt_args[oo].def.lvt_bits));
if (cmd->optional_opt_args[oo].def.flags)
printf("commands[%d].optional_opt_args[%d].def.flags = %s;\n",
i, oo, flags_to_str(cmd->optional_opt_args[oo].def.flags));
if (val_bit_is_set(cmd->optional_opt_args[oo].def.val_bits, constnum_VAL))
printf("commands[%d].optional_opt_args[%d].def.num = %d;\n",
i, oo, cmd->optional_opt_args[oo].def.num);
if (val_bit_is_set(cmd->optional_opt_args[oo].def.val_bits, conststr_VAL))
printf("commands[%d].optional_opt_args[%d].def.str = \"%s\";\n",
i, oo, cmd->optional_opt_args[oo].def.str ?: "NULL");
}
}
if (cmd->op_count) {
for (op = 0; op < cmd->op_count; op++) {
printf("commands[%d].optional_pos_args[%d].pos = %d;\n",
i, op, cmd->optional_pos_args[op].pos);
if (!cmd->optional_pos_args[op].def.val_bits)
continue;
printf("commands[%d].optional_pos_args[%d].def.val_bits = %s;\n",
i, op, val_bits_to_str(cmd->optional_pos_args[op].def.val_bits));
if (cmd->optional_pos_args[op].def.lvt_bits)
printf("commands[%d].optional_pos_args[%d].def.lvt_bits = %s;\n",
i, op, lvt_bits_to_str(cmd->optional_pos_args[op].def.lvt_bits));
if (cmd->optional_pos_args[op].def.flags)
printf("commands[%d].optional_pos_args[%d].def.flags = %s;\n",
i, op, flags_to_str(cmd->optional_pos_args[op].def.flags));
if (val_bit_is_set(cmd->optional_pos_args[op].def.val_bits, constnum_VAL))
printf("commands[%d].optional_pos_args[%d].def.num = %d;\n",
i, op, cmd->optional_pos_args[op].def.num);
if (val_bit_is_set(cmd->optional_pos_args[op].def.val_bits, conststr_VAL))
printf("commands[%d].optional_pos_args[%d].def.str = \"%s\";\n",
i, op, cmd->optional_pos_args[op].def.str ?: "NULL");
}
}
if (cmd->rule_count) {
for (ru = 0; ru < cmd->rule_count; ru++) {
printf("commands[%d].rules[%d].opts_count = %d;\n", i, ru, cmd->rules[ru].opts_count);
if (cmd->rules[ru].opts_count) {
printf("static int _command%d_rule%d_opts[] = { ", i, ru);
for (ruo = 0; ruo < cmd->rules[ru].opts_count; ruo++) {
if (ruo)
printf(", ");
printf("%s", opt_to_enum_str(cmd->rules[ru].opts[ruo]));
}
printf(" };\n");
printf("commands[%d].rules[%d].opts = _command%d_rule%d_opts;\n", i, ru, i, ru);
} else {
printf("commands[%d].rules[%d].opts = NULL;\n", i, ru);
}
printf("commands[%d].rules[%d].check_opts_count = %d;\n", i, ru, cmd->rules[ru].check_opts_count);
if (cmd->rules[ru].check_opts_count) {
printf("static int _command%d_rule%d_check_opts[] = { ", i, ru);
for (ruo = 0; ruo < cmd->rules[ru].check_opts_count; ruo++) {
if (ruo)
printf(",");
printf("%s ", opt_to_enum_str(cmd->rules[ru].check_opts[ruo]));
}
printf(" };\n");
printf("commands[%d].rules[%d].check_opts = _command%d_rule%d_check_opts;\n", i, ru, i, ru);
} else {
printf("commands[%d].rules[%d].check_opts = NULL;\n", i, ru);
}
printf("commands[%d].rules[%d].lvt_bits = %s;\n", i, ru,
cmd->rules[ru].lvt_bits ? lvt_bits_to_str(cmd->rules[ru].lvt_bits) : "0");
printf("commands[%d].rules[%d].lvp_bits = %s;\n", i, ru,
cmd->rules[ru].lvp_bits ? lvp_bits_to_str(cmd->rules[ru].lvp_bits) : "0");
printf("commands[%d].rules[%d].rule = %s;\n", i, ru,
rule_to_define_str(cmd->rules[ru].rule));
printf("commands[%d].rules[%d].check_lvt_bits = %s;\n", i, ru,
cmd->rules[ru].check_lvt_bits ? lvt_bits_to_str(cmd->rules[ru].check_lvt_bits) : "0");
printf("commands[%d].rules[%d].check_lvp_bits = %s;\n", i, ru,
cmd->rules[ru].check_lvp_bits ? lvp_bits_to_str(cmd->rules[ru].check_lvp_bits) : "0");
}
}
printf("\n");
}
}
struct cmd_pair {
int i, j;
};
static void print_ambiguous(void)
{
struct command *cmd, *dup;
struct cmd_pair dups[64] = { 0 };
int found = 0;
int i, j, f, ro, rp;
for (i = 0; i < cmd_count; i++) {
cmd = &cmd_array[i];
for (j = 0; j < cmd_count; j++) {
dup = &cmd_array[j];
if (i == j)
continue;
if (strcmp(cmd->name, dup->name))
continue;
if (cmd->ro_count != dup->ro_count)
continue;
if (cmd->rp_count != dup->rp_count)
continue;
for (ro = 0; ro < cmd->ro_count; ro++) {
if (!opt_arg_matches(&cmd->required_opt_args[ro],
&dup->required_opt_args[ro]))
goto next;
}
for (rp = 0; rp < cmd->rp_count; rp++) {
if (!pos_arg_matches(&cmd->required_pos_args[rp],
&dup->required_pos_args[rp]))
goto next;
}
for (f = 0; f < found; f++) {
if ((dups[f].i == j) && (dups[f].j == i))
goto next;
}
printf("Ambiguous commands %d and %d:\n", i, j);
print_usage(cmd);
print_usage(dup);
printf("\n");
dups[found].i = i;
dups[found].j = j;
found++;
next:
;
}
}
}
void print_command_list(void)
{
int i;
for (i = 0; i < MAX_CMD_NAMES; i++) {
if (!cmd_names[i].name) {
printf("found %d command names\n", i);
break;
}
printf("%s\n", cmd_names[i].name);
}
}
void print_option_list(void)
{
int i;
for (i = 0; i < ARG_COUNT; i++)
printf("%d %s %s %c (%d)\n",
opt_names[i].opt_enum, opt_names[i].name,
opt_names[i].long_opt, opt_names[i].short_opt ?: ' ',
opt_names[i].short_opt ? opt_names[i].short_opt : 0);
}
static void print_help(int argc, char *argv[])
{
printf("%s --output struct|count|usage|expanded <filename>\n", argv[0]);
printf("\n");
printf("struct: print C structures for command-lines.h\n");
printf("count: print defines and enums for command-lines-count.h\n");
printf("ambiguous: print commands differing only by LV types\n");
printf("usage: print usage format.\n");
printf("expanded: print expanded input format.\n");
}
int main(int argc, char *argv[])
{
char *outputformat = NULL;
char *inputfile = NULL;
FILE *file;
struct command *cmd;
char line[MAX_LINE];
char line_orig[MAX_LINE];
const char *name;
char *line_argv[MAX_LINE_ARGC];
char *n;
int line_argc;
int prev_was_oo_def = 0;
int prev_was_oo = 0;
int prev_was_op = 0;
if (argc < 2) {
print_help(argc, argv);
exit(EXIT_FAILURE);
}
if (!strcmp(argv[1], "debug")) {
print_command_list();
print_option_list();
return 0;
}
static struct option long_options[] = {
{"help", no_argument, 0, 'h' },
{"output", required_argument, 0, 'o' },
{"man-primary", required_argument, 0, 'p' },
{"man-secondary", required_argument, 0, 's' },
{"man-command", required_argument, 0, 'c' },
{0, 0, 0, 0 }
};
while (1) {
int c;
int option_index = 0;
c = getopt_long(argc, argv, "ho:p:s:c:",
long_options, &option_index);
if (c == -1)
break;
switch (c) {
case '0':
break;
case 'h':
print_help(argc, argv);
exit(EXIT_SUCCESS);
case 'o':
outputformat = strdup(optarg);
break;
case 'p':
include_man_primary = atoi(optarg);
break;
case 's':
include_man_secondary = atoi(optarg);
break;
case 'c':
man_command_name = strdup(optarg);
break;
}
}
if (optind < argc)
inputfile = argv[optind];
else {
printf("Missing filename.\n");
return 0;
}
if (!(file = fopen(inputfile, "r"))) {
printf("Cannot open %s\n", argv[1]);
return -1;
}
while (fgets(line, MAX_LINE, file)) {
if (line[0] == '#')
continue;
if (line[0] == '\n')
continue;
if (line[0] == '-' && line[1] == '-' && line[2] == '-')
continue;
if ((n = strchr(line, '\n')))
*n = '\0';
memcpy(line_orig, line, sizeof(line));
split_line(line, &line_argc, line_argv, ' ');
if (!line_argc)
continue;
/* command ... */
if ((name = is_command_name(line_argv[0]))) {
if (cmd_count >= MAX_CMDS) {
printf("MAX_CMDS too small\n");
return -1;
}
cmd = &cmd_array[cmd_count++];
cmd->name = name;
cmd->pos_count = 1;
add_required_line(cmd, line_argc, line_argv);
/* Every cmd gets the OO_ALL options */
include_optional_opt_args(cmd, "OO_ALL:");
continue;
}
if (is_desc_line(line_argv[0])) {
char *desc = strdup(line_orig);
if (cmd->desc) {
int newlen = strlen(cmd->desc) + strlen(desc) + 2;
char *newdesc = malloc(newlen);
memset(newdesc, 0, newlen);
snprintf(newdesc, newlen, "%s %s", cmd->desc, desc);
cmd->desc = newdesc;
free(desc);
} else
cmd->desc = desc;
continue;
}
if (is_flags_line(line_argv[0])) {
add_flags(cmd, line_orig);
continue;
}
if (is_rule_line(line_argv[0])) {
add_rule(cmd, line_orig);
continue;
}
if (is_id_line(line_argv[0])) {
cmd->command_line_id = strdup(line_argv[1]);
continue;
}
/* OO_FOO: ... */
if (is_oo_definition(line_argv[0])) {
add_oo_definition_line(line_argv[0], line_orig);
prev_was_oo_def = 1;
prev_was_oo = 0;
prev_was_op = 0;
continue;
}
/* OO: ... */
if (is_oo_line(line_argv[0])) {
add_optional_opt_line(cmd, line_argc, line_argv);
prev_was_oo_def = 0;
prev_was_oo = 1;
prev_was_op = 0;
continue;
}
/* OP: ... */
if (is_op_line(line_argv[0])) {
add_optional_pos_line(cmd, line_argc, line_argv);
prev_was_oo_def = 0;
prev_was_oo = 0;
prev_was_op = 1;
continue;
}
/* handle OO_FOO:, OO:, OP: continuing on multiple lines */
if (prev_was_oo_def) {
append_oo_definition_line(line_orig);
continue;
}
if (prev_was_oo) {
add_optional_opt_line(cmd, line_argc, line_argv);
continue;
}
if (prev_was_op) {
add_optional_pos_line(cmd, line_argc, line_argv);
continue;
}
}
fclose(file);
factor_common_options();
if (!outputformat)
print_command_struct(1);
else if (!strcmp(outputformat, "struct")) {
print_command_struct(0);
print_ambiguous();
}
else if (!strcmp(outputformat, "count"))
print_command_count();
else if (!strcmp(outputformat, "usage"))
print_command_struct(1);
else if (!strcmp(outputformat, "expanded"))
print_expanded();
else if (!strcmp(outputformat, "ambiguous"))
print_ambiguous();
else if (!strcmp(outputformat, "man"))
print_man_command();
else
print_help(argc, argv);
return 0;
}