diff options
Diffstat (limited to 'tools/command.c')
-rw-r--r-- | tools/command.c | 4041 |
1 files changed, 4041 insertions, 0 deletions
diff --git a/tools/command.c b/tools/command.c new file mode 100644 index 0000000..c037370 --- /dev/null +++ b/tools/command.c @@ -0,0 +1,4041 @@ +/* + * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. + * Copyright (C) 2004-2017 Red Hat, Inc. All rights reserved. + * + * This file is part of LVM2. + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions + * of the GNU Lesser General Public License v.2.1. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#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 <fcntl.h> +#include <stdio.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> + +const char *_lvt_enum_to_name(int lvt_enum); +const size_t _LONG_LINE = 42; /* length of line that neededs .nh .. .hy */ +/* + * This file can be compiled by itself as a man page generator. + */ +#ifdef MAN_PAGE_GENERATOR + +#define stack + +struct cmd_context { + void *libmem; +}; + +#define log_error(fmt, args...) \ +do { \ + printf(fmt "\n", ##args); \ +} while (0) + +#define dm_snprintf snprintf + +static int dm_strncpy(char *dest, const char *src, size_t n) +{ + if (memccpy(dest, src, 0, n)) + return 1; + + if (n > 0) + dest[n - 1] = '\0'; + + return 0; +} + +static char *dm_pool_strdup(void *p, const char *str) +{ + return strdup(str); +} + +static void *dm_pool_alloc(void *p, size_t size) +{ + return malloc(size); +} + +/* needed to include args.h */ +#define ARG_COUNTABLE 0x00000001 +#define ARG_GROUPABLE 0x00000002 +#define ARG_NONINTERACTIVE 0x00000004 +struct cmd_context; +struct arg_values; + +/* needed to include vals.h */ +static inline int yes_no_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; } +static inline int activation_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; } +static inline int cachemetadataformat_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; } +static inline int cachemode_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; } +static inline int discards_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; } +static inline int mirrorlog_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; } +static inline int size_kb_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; } +static inline int ssize_kb_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; } +static inline int size_mb_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; } +static inline int ssize_mb_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; } +static inline int psize_mb_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; } +static inline int nsize_mb_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; } +static inline int int_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; } +static inline int uint32_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; } +static inline int int_arg_with_sign(struct cmd_context *cmd, struct arg_values *av) { return 0; } +static inline int int_arg_with_plus(struct cmd_context *cmd, struct arg_values *av) { return 0; } +static inline int extents_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; } +static inline int sextents_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; } +static inline int pextents_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; } +static inline int nextents_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; } +static inline int major_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; } +static inline int minor_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; } +static inline int string_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; } +static inline int tag_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; } +static inline int permission_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; } +static inline int metadatatype_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; } +static inline int units_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; } +static inline int segtype_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; } +static inline int alloc_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; } +static inline int locktype_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; } +static inline int readahead_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; } +static inline int regionsize_mb_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; } +static inline int vgmetadatacopies_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av) { return 0; } +static inline int pvmetadatacopies_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av) { return 0; } +static inline int metadatacopies_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av) { return 0; } +static inline int polloperation_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av) { return 0; } +static inline int writemostly_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av) { return 0; } +static inline int syncaction_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av) { return 0; } +static inline int reportformat_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av) { return 0; } +static inline int configreport_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av) { return 0; } +static inline int configtype_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av) { return 0; } +static inline int repairtype_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av) { return 0; } +static inline int dumptype_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av) { return 0; } + +/* needed to include commands.h when building man page generator */ +#define CACHE_VGMETADATA 0x00000001 +#define PERMITTED_READ_ONLY 0x00000002 +#define ALL_VGS_IS_DEFAULT 0x00000004 +#define ENABLE_ALL_DEVS 0x00000008 +#define ALLOW_UUID_AS_NAME 0x00000010 +#define LOCKD_VG_SH 0x00000020 +#define NO_METADATA_PROCESSING 0x00000040 +#define MUST_USE_ALL_ARGS 0x00000100 +#define ENABLE_DUPLICATE_DEVS 0x00000400 +#define DISALLOW_TAG_ARGS 0x00000800 +#define GET_VGNAME_FROM_OPTIONS 0x00001000 +#define CAN_USE_ONE_SCAN 0x00002000 +#define ALLOW_HINTS 0x00004000 +#define ALLOW_EXPORTED 0x00008000 +#define CHECK_DEVS_USED 0x00010000 +#define DEVICE_ID_NOT_FOUND 0x00020000 + +/* create foo_CMD enums for command def ID's in command-lines.in */ + +enum { +#define cmd(a, b) a , +#include "../include/cmds.h" +#undef cmd +}; + +/* 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, g) 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 +}; + +#else /* MAN_PAGE_GENERATOR */ + +#include "tools.h" + +#endif /* MAN_PAGE_GENERATOR */ + +#include "command.h" /* defines struct command */ +#include "command-count.h" /* defines COMMAND_COUNT */ + +/* see cmd_names[] below, one for each unique "ID" in command-lines.in */ + +struct cmd_name { + const char *enum_name; /* "foo_CMD" */ + int cmd_enum; /* foo_CMD */ + const char *name; /* "foo" from string after ID: */ +}; + +/* create table of value names, e.g. String, and corresponding enum from vals.h */ + +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 */ + +struct opt_name opt_names[ARG_COUNT + 1] = { +#define arg(a, b, c, d, e, f, g) { # a, a, b, "", "--" c, d, e, f, g }, +#include "args.h" +#undef arg +}; + +/* create table of lv property names, e.g. lv_is_foo, and corresponding enum from lv_props.h */ + +struct lv_prop lv_props[LVP_COUNT + 1] = { +#define lvp(a, b, c) { # a, a, b, c }, +#include "lv_props.h" +#undef lvp +}; + +/* create table of lv type names, e.g. linear and corresponding enum from lv_types.h */ + +struct lv_type lv_types[LVT_COUNT + 1] = { +#define lvt(a, b, c) { # a, a, b, c }, +#include "lv_types.h" +#undef lvt +}; + +/* create table of command IDs */ + +struct cmd_name cmd_names[CMD_COUNT + 1] = { +#define cmd(a, b) { # a, a, # b }, +#include "../include/cmds.h" +#undef cmd +}; + +/* + * command_names[] and commands[] are defined in lvmcmdline.c when building lvm, + * but need to be defined here when building the stand-alone man page generator. + */ + +#ifdef MAN_PAGE_GENERATOR + +struct command_name command_names[] = { +#define xx(a, b, c...) { # a, b, c }, +#include "commands.h" +#undef xx + { .name = NULL } +}; +struct command commands[COMMAND_COUNT]; + +#else /* MAN_PAGE_GENERATOR */ + +struct command_name command_names[] = { +#define xx(a, b, c...) { # a, b, c, a}, +#include "commands.h" +#undef xx + { .name = NULL } +}; +extern struct command commands[COMMAND_COUNT]; /* defined in lvmcmdline.c */ + +#endif /* MAN_PAGE_GENERATOR */ + +/* array of pointers into opt_names[] that is sorted alphabetically (by long opt name) */ + +struct opt_name *opt_names_alpha[ARG_COUNT + 1]; + +/* lvm_all is for recording options that are common for all lvm commands */ + +struct command lvm_all; + +/* saves OO_FOO lines (groups of optional options) to include in multiple defs */ + +static int _oo_line_count; +#define MAX_OO_LINES 256 + +struct oo_line { + char *name; + char *line; +}; +static struct oo_line _oo_lines[MAX_OO_LINES]; + +#define REQUIRED 1 /* required option */ +#define OPTIONAL 0 /* optional option */ +#define IGNORE (-1) /* ignore option */ + +#define MAX_LINE 1024 +#define MAX_LINE_ARGC 256 +#define DESC_LINE 1024 + +/* + * Contains _command_input[] which is command-lines.in with comments + * removed and wrapped as a string. The _command_input[] string is + * used to populate commands[]. + */ +#include "command-lines-input.h" + +static void __add_optional_opt_line(struct cmd_context *cmdtool, struct command *cmd, int argc, char *argv[]); + +static unsigned _was_hyphen = 0; +static void printf_hyphen(char c) +{ + /* When .hy 1 was printed, we do not want to emit empty space */ + printf("%c%c\n", _was_hyphen ? '\n' : ' ', c); + _was_hyphen = 0; +} + +/* + * 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[MAX_LINE_ARGC]; + char *new; + int i; + + /* compare the name before any suffix like _new or _<lvtype> */ + + if (!dm_strncpy(name, str, sizeof(name))) + return 0; /* Buffer is too short */ + + if ((new = strchr(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 */ + +#define MAX_LONG_OPT_NAME_LEN 32 + +static int _opt_str_to_num(struct command *cmd, char *str) +{ + char long_name[MAX_LONG_OPT_NAME_LEN]; + char *p = NULL; + int i; + int first = 0, last = ARG_COUNT - 1, middle; + + if (!dm_strncpy(long_name, str, sizeof(long_name))) + goto err; + + if ((p = strstr(long_name, "_long"))) + /* + * --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 (== 0). + */ + *p = '\0'; + + /* Binary search in sorted array of long options (with duplicates) */ + while (first <= last) { + middle = first + (last - first) / 2; + if ((i = strcmp(opt_names_alpha[middle]->long_opt, long_name)) < 0) + first = middle + 1; + else if (i > 0) + last = middle - 1; + else { + /* Matching long option string found. + * As sorted array contains duplicates, we need to also + * check left & right side for possible match + */ + for (i = middle;;) { + if ((!p && !strstr(opt_names_alpha[i]->name, "_long_ARG")) || + (p && !opt_names_alpha[i]->short_opt)) + return opt_names_alpha[i]->opt_enum; /* Found */ + /* Check if there is something on the 'left-side' */ + if ((i <= first) || strcmp(opt_names_alpha[--i]->long_opt, long_name)) + break; + } + + /* Nothing on the left, so look on the 'right-side' */ + for (i = middle + 1; i <= last; ++i) { + if (strcmp(opt_names_alpha[i]->long_opt, long_name)) + break; + if ((!p && !strstr(opt_names_alpha[i]->name, "_long_ARG")) || + (p && !opt_names_alpha[i]->short_opt)) + return opt_names_alpha[i]->opt_enum; /* Found */ + } + + break; /* Nothing... */ + } + } +err: + log_error("Parsing command defs: unknown opt str: \"%s\"%s%s.", + str, p ? " ": "", p ? long_name : ""); + cmd->cmd_flags |= CMD_FLAG_PARSE_ERROR; + + return ARG_UNUSED; +} + +/* "foo" string to foo_CMD int */ + +int command_id_to_enum(const char *str) +{ + int i; + int first = 1, last = CMD_COUNT - 1, middle; + + while (first <= last) { + middle = first + (last - first) / 2; + if ((i = strcmp(cmd_names[middle].name, str)) < 0) + first = middle + 1; + else if (i > 0) + last = middle - 1; + else + return cmd_names[middle].cmd_enum; + } + + log_error("Cannot find command %s.", str); + return CMD_NONE; +} + +/* "lv_is_prop" to is_prop_LVP */ + +static int _lvp_name_to_enum(struct command *cmd, char *str) +{ + int i; + + for (i = 1; i < LVP_COUNT; i++) { + if (!strcmp(str, lv_props[i].name)) + return lv_props[i].lvp_enum; + } + + log_error("Parsing command defs: unknown lv property %s.", str); + cmd->cmd_flags |= CMD_FLAG_PARSE_ERROR; + return LVP_NONE; +} + +/* "type" to type_LVT */ + +static int _lvt_name_to_enum(struct command *cmd, char *str) +{ + int i; + + for (i = 1; i < LVT_COUNT; i++) { + if (!strcmp(str, lv_types[i].name)) + return lv_types[i].lvt_enum; + } + + log_error("Parsing command defs: unknown lv type %s.", str); + cmd->cmd_flags |= CMD_FLAG_PARSE_ERROR; + return LVT_NONE; +} + +/* LV_<type> to <type>_LVT */ + +static int _lv_to_enum(struct command *cmd, char *name) +{ + return _lvt_name_to_enum(cmd, 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) + */ + +#define LVTYPE_LEN 128 + +static uint64_t _lv_to_bits(struct command *cmd, char *name) +{ + char buf[LVTYPE_LEN]; + char *argv[MAX_LINE_ARGC]; + uint64_t lvt_bits = 0; + int lvt_enum; + int argc; + int i; + + (void) dm_strncpy(buf, name, LVTYPE_LEN); + + _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(cmd, argv[i]); + lvt_bits |= lvt_enum_to_bit(lvt_enum); + } + + return lvt_bits; +} + +struct command_name *find_command_name(const char *name) +{ + static int _command_names_count = -1; + int first = 0, last, middle; + int i; + + if (_command_names_count == -1) { + /* Validate cmd_names & command_names arrays are properly sorted */ + for (i = 1; i < CMD_COUNT - 2; i++) + if (strcmp(cmd_names[i].name, cmd_names[i + 1].name) > 0) { + log_error("File cmds.h has unsorted name entry %s.", + cmd_names[i].name); + return 0; + } + for (i = 1; command_names[i].name; i++) /* assume > 1 */ + if (strcmp(command_names[i - 1].name, command_names[i].name) > 0) { + log_error("File commands.h has unsorted name entry %s.", + command_names[i].name); + return 0; + } + _command_names_count = i - 1; + } + last = _command_names_count; + + while (first <= last) { + middle = first + (last - first) / 2; + if ((i = strcmp(command_names[middle].name, name)) < 0) + first = middle + 1; + else if (i > 0) + last = middle - 1; + else + return &command_names[middle]; + } + + return NULL; +} + +static struct command_name *_find_command_name(const char *name) +{ + if (!islower(name[0])) + return NULL; /* Commands starts with lower-case */ + + return find_command_name(name); +} + +static const char *_is_command_name(char *str) +{ + const struct command_name *c; + + if ((c = _find_command_name(str))) + return c->name; + + return NULL; +} + +static int _is_opt_name(char *str) +{ + if ((str[0] == '-') && (str[1] == '-')) + return 1; + + if ((str[0] == '-') && (str[1] != '-')) + log_error("Parsing command defs: options must be specified in long form: %s.", str); + + 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) +{ + switch (str[0]) { + case 'V': return (str[1] == 'G'); /* VG */ + case 'L': return (str[1] == 'V'); /* LV */ + case 'P': return (str[1] == 'V'); /* PV */ + case 'T': return (strncmp(str, "Tag", 3) == 0); + case 'S': return ((strncmp(str, "String", 6) == 0) || + (strncmp(str, "Select", 6) == 0)); + } + + return 0; +} + +static int _is_oo_definition(char *str) +{ + if (!strncmp(str, "OO_", 3) && strchr(str, ':')) + return 1; + return 0; +} + +static int _is_oo_line(char *str) +{ + if (!strncmp(str, "OO:", 3)) + return 1; + return 0; +} + +static int _is_io_line(char *str) +{ + if (!strncmp(str, "IO:", 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_autotype_line(char *str) +{ + if (!strncmp(str, "AUTOTYPE:", 6)) + 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; +} + +/* + * Save a positional arg in a struct arg_def. + * 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) { + log_error("Parsing command defs: unknown pos arg: %s.", name); + cmd->cmd_flags |= CMD_FLAG_PARSE_ERROR; + return; + } + + def->val_bits |= val_enum_to_bit(val_enum); + + if ((val_enum == lv_VAL) && strchr(name, '_')) + def->lvt_bits = _lv_to_bits(cmd, name); + + if (strstr(name, "_new")) { + if (val_enum == lv_VAL) + def->flags |= ARG_DEF_FLAG_NEW_LV; + else if (val_enum == vg_VAL) + def->flags |= ARG_DEF_FLAG_NEW_VG; + } + } +} + +/* + * Save an option arg in a struct arg_def. + * Parse str for anything that can follow --option. + */ +static void _set_opt_def(struct cmd_context *cmdtool, 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) { + /* a literal number or string */ + + if (isdigit(name[0])) + val_enum = constnum_VAL; + + else if (isalpha(name[0])) + val_enum = conststr_VAL; + + else { + log_error("Parsing command defs: unknown opt arg: %s.", name); + cmd->cmd_flags |= CMD_FLAG_PARSE_ERROR; + return; + } + } + + + 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) { +#ifdef MAN_PAGE_GENERATOR + free((void*)def->str); +#endif + def->str = dm_pool_strdup(cmdtool->libmem, name); + + if (!def->str) { + /* FIXME */ + stack; + return; + } + } + + if (val_enum == lv_VAL) { + if (strchr(name, '_')) + def->lvt_bits = _lv_to_bits(cmd, name); + } + + if (strstr(name, "_new")) { + if (val_enum == lv_VAL) + def->flags |= ARG_DEF_FLAG_NEW_LV; + else if (val_enum == vg_VAL) + def->flags |= ARG_DEF_FLAG_NEW_VG; + } + } +} + +/* + * Save a set of common options so they can be included in + * multiple command defs. + * + * 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++]; + + if (!(oo->name = strdup(name))) { + log_error("Failer to duplicate name %s.", name); + return; /* FIXME: return code */ + } + + if ((colon = strchr(oo->name, ':'))) + *colon = '\0'; + else { + log_error("Parsing command defs: invalid OO definition."); + return; + } + + start = strchr(line, ':') + 2; + if (!(oo->line = strdup(start))) { + log_error("Failer to duplicate line %s.", start); + return; + } +} + +/* Support OO_FOO: continuing 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); + if (!line) { + log_error("Parsing command defs: no memory."); + return; + } + + (void) dm_snprintf(line, len, "%s %s", old_line, new_line); + free(oo->line); + oo->line = line; +} + +/* Find a saved OO_FOO definition. */ + +#define OO_NAME_LEN 128 + +static char *_get_oo_line(const char *str) +{ + char *name; + char *end; + char str2[OO_NAME_LEN]; + int i; + + (void) dm_strncpy(str2, str, sizeof(str2)); + if ((end = strchr(str2, ':'))) + *end = '\0'; + if ((end = strchr(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, + * i.e. include common options from an OO_FOO definition. + */ + +static void _include_optional_opt_args(struct cmd_context *cmdtool, struct command *cmd, const char *str) +{ + char *oo_line; + char *line; + char *line_argv[MAX_LINE_ARGC]; + int line_argc; + + if (!(oo_line = _get_oo_line(str))) { + log_error("Parsing command defs: no OO line found for %s.", str); + cmd->cmd_flags |= CMD_FLAG_PARSE_ERROR; + return; + } + + if (!(line = strdup(oo_line))) { + cmd->cmd_flags |= CMD_FLAG_PARSE_ERROR; + return; + } + + _split_line(line, &line_argc, line_argv, ' '); + __add_optional_opt_line(cmdtool, cmd, line_argc, line_argv); + free(line); +} + +/* + * When an --option is seen, add a new opt_args entry for it. + * This function sets the opt_args.opt value for it. + */ + +static void _add_opt_arg(struct command *cmd, char *str, + int *takes_arg, int *already, int required) +{ + char *comma; + int opt; + int i; + + /* opt_arg.opt set here */ + /* opt_arg.def will be set in _update_prev_opt_arg() if needed */ + + if ((comma = strchr(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(cmd, str); + + /* If the binary-search finds uuidstr_ARG switch to uuid_ARG */ + if (opt == uuidstr_ARG) + opt = uuid_ARG; + + /* Skip adding an optional opt if it is already included. */ + if (already && !required) { + for (i = 0; i < cmd->oo_count; i++) { + if (cmd->optional_opt_args[i].opt == opt) { + *already = 1; + *takes_arg = opt_names[opt].val_enum ? 1 : 0; + return; + } + } + } + +skip: + if (required > 0) + cmd->required_opt_args[cmd->ro_count++].opt = opt; + else if (!required) + cmd->optional_opt_args[cmd->oo_count++].opt = opt; + else if (required < 0) + cmd->ignore_opt_args[cmd->io_count++].opt = opt; + + *takes_arg = opt_names[opt].val_enum ? 1 : 0; +} + +/* + * After --option has been seen, this function sets opt_args.def value + * for the value that appears after --option. + */ + +static void _update_prev_opt_arg(struct cmd_context *cmdtool, struct command *cmd, char *str, int required) +{ + struct arg_def def = { 0 }; + char *comma; + + if (str[0] == '-') { + log_error("Parsing command defs: option %s must be followed by an arg.", str); + cmd->cmd_flags |= CMD_FLAG_PARSE_ERROR; + return; + } + + /* opt_arg.def set here */ + /* opt_arg.opt was previously set in _add_opt_arg() when --foo was read */ + + if ((comma = strchr(str, ','))) + *comma = '\0'; + + _set_opt_def(cmdtool, cmd, str, &def); + + if (required > 0) + cmd->required_opt_args[cmd->ro_count-1].def = def; + else if (!required) + cmd->optional_opt_args[cmd->oo_count-1].def = def; + else if (required < 0) + cmd->ignore_opt_args[cmd->io_count-1].def = def; +} + +/* + * When an position arg is seen, add a new pos_args entry for it. + * This function sets the pos_args.pos and pos_args.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 { + log_error("Parsing command defs: unknown pos arg: %s.", str); + cmd->cmd_flags |= CMD_FLAG_PARSE_ERROR; + return; + } +} + +/* Process what follows OO:, which are the optional opt args for the cmd def. */ + +static void __add_optional_opt_line(struct cmd_context *cmdtool, struct command *cmd, int argc, char *argv[]) +{ + int takes_arg = 0; + int already; + int i; + + for (i = 0; i < argc; i++) { + if (!i && !strncmp(argv[i], "OO:", 3)) + continue; + + already = 0; + + if (_is_opt_name(argv[i])) + _add_opt_arg(cmd, argv[i], &takes_arg, &already, OPTIONAL); + else if (!strncmp(argv[i], "OO_", 3)) + _include_optional_opt_args(cmdtool, cmd, argv[i]); + else if (takes_arg) + _update_prev_opt_arg(cmdtool, cmd, argv[i], OPTIONAL); + else { + log_error("Parsing command defs: can't parse argc %d argv %s%s%s.", + i, argv[i], (i > 0) ? " prev " : "", (i > 0) ? argv[i - 1] : ""); + cmd->cmd_flags |= CMD_FLAG_PARSE_ERROR; + return; + } + + if (already && takes_arg) + i++; + } +} + +/* Process what follows IO:, which are the ignore options for the cmd def. */ + +static void _add_ignore_opt_line(struct cmd_context *cmdtool, struct command *cmd, int argc, char *argv[]) +{ + int takes_arg = 0; + int i; + + for (i = 0; i < argc; i++) { + if (!i && !strncmp(argv[i], "IO:", 3)) + continue; + if (_is_opt_name(argv[i])) + _add_opt_arg(cmd, argv[i], &takes_arg, NULL, IGNORE); + else if (takes_arg) + _update_prev_opt_arg(cmdtool, cmd, argv[i], IGNORE); + else { + log_error("Parsing command defs: can't parse argc %d argv %s%s%s.", + i, argv[i], (i > 0) ? " prev " : "", (i > 0) ? argv[i - 1] : ""); + cmd->cmd_flags |= CMD_FLAG_PARSE_ERROR; + return; + } + } +} + +/* Process what follows OP:, which are optional pos args for the cmd def. */ + +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); + } +} + +static void _add_required_opt_line(struct cmd_context *cmdtool, struct command *cmd, int argc, char *argv[]) +{ + int takes_arg = 0; + int i; + + for (i = 0; i < argc; i++) { + if (_is_opt_name(argv[i])) + _add_opt_arg(cmd, argv[i], &takes_arg, NULL, REQUIRED); + else if (takes_arg) + _update_prev_opt_arg(cmdtool, cmd, argv[i], REQUIRED); + else { + log_error("Parsing command defs: can't parse argc %d argv %s%s%s.", + i, argv[i], (i > 0) ? " prev " : "", (i > 0) ? argv[i - 1] : ""); + cmd->cmd_flags |= CMD_FLAG_PARSE_ERROR; + return; + } + } +} + +/* + * Add to required_opt_args from an OO_FOO definition. + * (This is the special case of vgchange/lvchange where one + * optional option is required, and others are then optional.) + * The set of options from OO_FOO are saved in required_opt_args, + * and flag CMD_FLAG_ANY_REQUIRED_OPT is set on the cmd indicating + * this special case. + */ +static void _include_required_opt_args(struct cmd_context *cmdtool, 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))) { + log_error("Parsing command defs: no OO line found for %s.", str); + cmd->cmd_flags |= CMD_FLAG_PARSE_ERROR; + return; + } + + if (!(line = strdup(oo_line))) { + cmd->cmd_flags |= CMD_FLAG_PARSE_ERROR; + return; + } + + _split_line(line, &line_argc, line_argv, ' '); + _add_required_opt_line(cmdtool, cmd, line_argc, line_argv); + free(line); +} + +/* Process what follows command_name, which are required opt/pos args. */ + +static void _add_required_line(struct cmd_context *cmdtool, struct command *cmd, int argc, char *argv[]) +{ + int i; + int takes_arg; + int prev_was_opt = 0, prev_was_pos = 0; + int orig_ro_count = 0; + + /* argv[0] is command name */ + + for (i = 1; i < argc; i++) { + + if (_is_opt_name(argv[i])) { + /* add new required_opt_arg */ + _add_opt_arg(cmd, argv[i], &takes_arg, NULL, REQUIRED); + prev_was_opt = 1; + prev_was_pos = 0; + + } else if (prev_was_opt && takes_arg) { + /* set value for previous required_opt_arg */ + _update_prev_opt_arg(cmdtool, cmd, argv[i], REQUIRED); + prev_was_opt = 0; + prev_was_pos = 0; + + } else if (_is_pos_name(argv[i])) { + /* add new required_pos_arg */ + _add_pos_arg(cmd, argv[i], REQUIRED); + prev_was_opt = 0; + prev_was_pos = 1; + + } else if (!strncmp(argv[i], "OO_", 3)) { + /* + * the first ro_count entries in required_opt_arg required, + * after which one or more of the next any_ro_count entries + * in required_opt_arg are required. required_opt_arg + * has a total of ro_count+any_ro_count entries. + */ + cmd->cmd_flags |= CMD_FLAG_ANY_REQUIRED_OPT; + orig_ro_count = cmd->ro_count; + + _include_required_opt_args(cmdtool, cmd, argv[i]); + + cmd->any_ro_count = cmd->ro_count - orig_ro_count; + cmd->ro_count = orig_ro_count; + + } else if (prev_was_pos) { + /* set property for previous required_pos_arg */ + _update_prev_pos_arg(cmd, argv[i], REQUIRED); + } else { + log_error("Parsing command defs: can't parse argc %d argv %s%s%s.", + i, argv[i], (i > 0) ? " prev " : "", (i > 0) ? argv[i - 1] : ""); + cmd->cmd_flags |= CMD_FLAG_PARSE_ERROR; + return; + } + } +} + +static void _add_flags(struct command *cmd, char *line) +{ + if (strstr(line, "SECONDARY_SYNTAX")) + cmd->cmd_flags |= CMD_FLAG_SECONDARY_SYNTAX; + if (strstr(line, "PREVIOUS_SYNTAX")) + cmd->cmd_flags |= CMD_FLAG_PREVIOUS_SYNTAX; +} + +static void _add_autotype(struct cmd_context *cmdtool, struct command *cmd, char *line) +{ + int line_argc; + char *line_argv[MAX_LINE_ARGC]; + + _split_line(line, &line_argc, line_argv, ' '); + + if (cmd->autotype) + cmd->autotype2 = dm_pool_strdup(cmdtool->libmem, line_argv[1]); + else + cmd->autotype = dm_pool_strdup(cmdtool->libmem, line_argv[1]); +} + +#define MAX_RULE_OPTS 64 + +static void _add_rule(struct cmd_context *cmdtool, 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) { + log_error("Parsing command defs: too many rules for cmd."); + cmd->cmd_flags |= CMD_FLAG_PARSE_ERROR; + return; + } + + 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 = dm_pool_alloc(cmdtool->libmem, MAX_RULE_OPTS * sizeof(int)))) { + log_error("Parsing command defs: no mem."); + cmd->cmd_flags |= CMD_FLAG_PARSE_ERROR; + return; + } + memset(rule->opts, 0, MAX_RULE_OPTS * sizeof(int)); + } + + if (!rule->check_opts) { + if (!(rule->check_opts = dm_pool_alloc(cmdtool->libmem, MAX_RULE_OPTS * sizeof(int)))) { + log_error("Parsing command defs: no mem."); + cmd->cmd_flags |= CMD_FLAG_PARSE_ERROR; + return; + } + memset(rule->check_opts, 0, MAX_RULE_OPTS * sizeof(int)); + } + + if (check) + rule->check_opts[rule->check_opts_count++] = _opt_str_to_num(cmd, arg); + else + rule->opts[rule->opts_count++] = _opt_str_to_num(cmd, arg); + } + + else if (!strncmp(arg, "LV_", 3)) { + lvt_enum = _lv_to_enum(cmd, 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(cmd, arg); + + if (check) + rule->check_lvp_bits |= lvp_enum_to_bit(lvp_enum); + else + rule->lvp_bits |= lvp_enum_to_bit(lvp_enum); + } + } +} + +/* The given option is common to all lvm commands (set in lvm_all). */ + +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; +} + +/* Find common options for all variants of each command name. */ + +void factor_common_options(void) +{ + int cn, opt_enum, ci, oo, ro, found; + struct command *cmd; + + for (cn = 0; command_names[cn].name; cn++) { + /* already factored */ + if (command_names[cn].variants) + continue; + + for (ci = 0; ci < COMMAND_COUNT; ci++) { + cmd = &commands[ci]; + + if (strcmp(cmd->name, command_names[cn].name)) + continue; + + command_names[cn].variants++; + } + + for (opt_enum = 0; opt_enum < ARG_COUNT; opt_enum++) { + + for (ci = 0; ci < COMMAND_COUNT; ci++) { + cmd = &commands[ci]; + + if (strcmp(cmd->name, command_names[cn].name)) + continue; + + if (cmd->ro_count || cmd->any_ro_count) + command_names[cn].variant_has_ro = 1; + if (cmd->rp_count) + command_names[cn].variant_has_rp = 1; + if (cmd->oo_count) + command_names[cn].variant_has_oo = 1; + if (cmd->op_count) + command_names[cn].variant_has_op = 1; + + for (ro = 0; ro < cmd->ro_count + cmd->any_ro_count; ro++) { + command_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)) + command_names[cn].all_options[extents_ARG] = 1; + } + for (oo = 0; oo < cmd->oo_count; oo++) + command_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 */ + command_names[cn].common_options[opt_enum] = 1; + next_opt: + ; + } + } +} + +/* FIXME: use a flag in command_name struct? */ + +int command_has_alternate_extents(const char *name) +{ + if (name[0] != 'l') + return 0; + if (!strcmp(name, "lvcreate") || + !strcmp(name, "lvresize") || + !strcmp(name, "lvextend") || + !strcmp(name, "lvreduce")) + return 1; + return 0; +} + +static int _long_name_compare(const void *on1, const void *on2) +{ + const struct opt_name * const *optname1 = on1; + const struct opt_name * const *optname2 = on2; + + return strcmp((*optname1)->long_opt + 2, (*optname2)->long_opt + 2); +} + +/* Create list of option names for printing alphabetically. */ + +static void _create_opt_names_alpha(void) +{ + int i; + + for (i = 0; i < ARG_COUNT; i++) + opt_names_alpha[i] = &opt_names[i]; + + qsort(opt_names_alpha, ARG_COUNT, sizeof(long), _long_name_compare); +} + +static int _copy_line(char *line, int max_line, int *position) +{ + int p = *position; + int i = 0; + + memset(line, 0, max_line); + + while (1) { + line[i] = _command_input[p]; + i++; + p++; + + if (_command_input[p] == '\n') { + p++; + break; + } + + if (i == (max_line - 1)) + break; + } + *position = p; + return 1; +} + +int define_commands(struct cmd_context *cmdtool, const char *run_name) +{ + struct command *cmd = NULL; + char line[MAX_LINE]; + char line_orig[MAX_LINE]; + char *line_argv[MAX_LINE_ARGC]; + const char *name; + char *n; + int line_argc; + int cmd_count = 0; + int prev_was_oo_def = 0; + int prev_was_oo = 0; + int prev_was_op = 0; + int copy_pos = 0; + int skip = 0; + int i; + + if (run_name && !strcmp(run_name, "help")) + run_name = NULL; + + _create_opt_names_alpha(); + + /* Process each line of command-lines-input.h (from command-lines.in) */ + + while (_copy_line(line, MAX_LINE, ©_pos)) { + if (line[0] == '\n') + break; + + if (!strcmp(line, "---") || !strcmp(line, "--")) + 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; + + /* New cmd def begins: command_name <required opt/pos args> */ + if ((name = _is_command_name(line_argv[0]))) { + if (cmd_count >= COMMAND_COUNT) { + return 0; + } + + /* + * FIXME: when running one specific command name, + * we can optimize by not parsing command defs + * that don't start with that command name. + */ + + cmd = &commands[cmd_count]; + cmd->command_index = cmd_count; + cmd_count++; + cmd->name = dm_pool_strdup(cmdtool->libmem, name); + + if (!cmd->name) { + /* FIXME */ + stack; + return 0; + } + + if (run_name && strcmp(run_name, name)) { + skip = 1; + prev_was_oo_def = 0; + prev_was_oo = 0; + prev_was_op = 0; + continue; + } + skip = 0; + + cmd->pos_count = 1; + _add_required_line(cmdtool, cmd, line_argc, line_argv); + + /* Every cmd gets the OO_ALL options */ + _include_optional_opt_args(cmdtool, cmd, "OO_ALL:"); + continue; + } + + /* + * All other kinds of lines are processed in the + * context of the existing command[]. + */ + + if (_is_desc_line(line_argv[0]) && !skip && cmd) { + if (cmd->desc) { + size_t newlen = strlen(cmd->desc) + strlen(line_orig) + 2; + char *newdesc = dm_pool_alloc(cmdtool->libmem, newlen); + + if (!newdesc) { + /* FIXME */ + stack; + return 0; + } + + snprintf(newdesc, newlen, "%s %s", cmd->desc, line_orig); +#ifdef MAN_PAGE_GENERATOR + free((void*)cmd->desc); +#endif + cmd->desc = newdesc; + } else if (!(cmd->desc = dm_pool_strdup(cmdtool->libmem, line_orig))) { + /* FIXME */ + stack; + return 0; + } + + continue; + } + + if (_is_autotype_line(line_argv[0]) && !skip && cmd) { + _add_autotype(cmdtool, cmd, line_orig); + continue; + } + + if (_is_flags_line(line_argv[0]) && !skip && cmd) { + _add_flags(cmd, line_orig); + continue; + } + + if (_is_rule_line(line_argv[0]) && !skip && cmd) { + _add_rule(cmdtool, cmd, line_orig); + continue; + } + + if (_is_id_line(line_argv[0]) && cmd) { +#ifdef MAN_PAGE_GENERATOR + free((void*)cmd->command_id); +#endif + cmd->command_id = dm_pool_strdup(cmdtool->libmem, line_argv[1]); + + if (!cmd->command_id) { + /* FIXME */ + stack; + return 0; + } + 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]) && !skip && cmd) { + __add_optional_opt_line(cmdtool, 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]) && !skip && cmd) { + _add_optional_pos_line(cmd, line_argc, line_argv); + prev_was_oo_def = 0; + prev_was_oo = 0; + prev_was_op = 1; + continue; + } + + /* IO: ... */ + if (_is_io_line(line_argv[0]) && !skip && cmd) { + _add_ignore_opt_line(cmdtool, cmd, line_argc, line_argv); + prev_was_oo = 0; + prev_was_op = 0; + 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 && cmd) { + __add_optional_opt_line(cmdtool, cmd, line_argc, line_argv); + continue; + } + + if (prev_was_op && cmd) { + _add_optional_pos_line(cmd, line_argc, line_argv); + continue; + } + + if (!skip) { + log_error("Parsing command defs: can't process input line %s.", line_orig); + return 0; + } + } + + for (i = 0; i < COMMAND_COUNT; i++) { + if (commands[i].cmd_flags & CMD_FLAG_PARSE_ERROR) + return 0; + } + + _include_optional_opt_args(cmdtool, &lvm_all, "OO_ALL"); + + for (i = 0; i < _oo_line_count; i++) { + struct oo_line *oo = &_oo_lines[i]; + free(oo->name); + free(oo->line); + } + memset(&_oo_lines, 0, sizeof(_oo_lines)); + _oo_line_count = 0; + + return 1; +} + +/* + * The opt_names[] table describes each option. It is indexed by the + * option typedef, e.g. size_ARG. The size_ARG entry specifies the + * option name, e.g. --size, and the kind of value it accepts, + * e.g. sizemb_VAL. + * + * The val_names[] table describes each option value type. It is indexed by + * the value typedef, e.g. sizemb_VAL. The sizemb_VAL entry specifies the + * function used to parse the value, e.g. size_mb_arg(), the string used to + * refer to the value in the command-lines.in specifications, e.g. SizeMB, + * and how the value should be displayed in a man page, e.g. Size[m|UNIT]. + * + * A problem is that these tables are independent of a particular command + * (they are created at build time), but different commands accept different + * types of values for the same option, e.g. one command will accept + * signed size values (ssizemb_VAL), while another does not accept a signed + * number, (sizemb_VAL). This function deals with this problem by tweaking + * the opt_names[] table at run time according to the specific command being run. + * i.e. it changes size_ARG to accept sizemb_VAL or ssizemb_VAL depending + * on the command. + * + * By default, size_ARG in opt_names[] is set up to accept a standard + * sizemb_VAL. The same is done for other opt_names[] entries that + * take different option values. + * + * This function overrides default opt_names[] entries at run time according + * to the command name, adjusting the value types accepted by various options. + * So, for lvresize, opt_names[sizemb_VAL] is overridden to accept + * the relative (+ or -) value type ssizemb_VAL, instead of the default + * sizemb_VAL. This way, when lvresize processes the --size value, it + * will use the ssize_mb_arg() function which accepts relative size values. + * When lvcreate processes the --size value, it uses size_mb_arg() which + * rejects signed values. + * + * The command defs in commands[] do not need to be overridden because + * the command-lines.in defs have the context of a command, and are + * described using the proper value type, e.g. this cmd def already + * uses the relative size value: "lvresize --size SSizeMB LV", + * so the commands[] entry for the cmd def already references the + * correct ssizemb_VAL. + */ +void configure_command_option_values(const char *name) +{ + if (!strcmp(name, "lvresize")) { + /* relative +|- allowed for LV, + allowed for metadata */ + opt_names[size_ARG].val_enum = ssizemb_VAL; + opt_names[extents_ARG].val_enum = sextents_VAL; + opt_names[poolmetadatasize_ARG].val_enum = psizemb_VAL; + return; + } + + if (!strcmp(name, "lvextend")) { + /* relative + allowed */ + opt_names[size_ARG].val_enum = psizemb_VAL; + opt_names[extents_ARG].val_enum = pextents_VAL; + opt_names[poolmetadatasize_ARG].val_enum = psizemb_VAL; + return; + } + + if (!strcmp(name, "lvreduce")) { + /* relative - allowed */ + opt_names[size_ARG].val_enum = nsizemb_VAL; + opt_names[extents_ARG].val_enum = nextents_VAL; + return; + } + + if (!strcmp(name, "lvconvert")) { + opt_names[mirrors_ARG].val_enum = snumber_VAL; + return; + } + + if (!strcmp(name, "lvcreate")) { + /* + * lvcreate is a bit of a mess because it has previously + * accepted + but used it as an absolute value, so we + * have to recognize it. (We don't want to show the + + * option in man/help, though, since it's confusing, + * so there's a special case when printing man/help + * output to show sizemb_VAL/extents_VAL rather than + * psizemb_VAL/pextents_VAL.) + */ + opt_names[size_ARG].val_enum = psizemb_VAL; + opt_names[extents_ARG].val_enum = pextents_VAL; + opt_names[poolmetadatasize_ARG].val_enum = psizemb_VAL; + opt_names[mirrors_ARG].val_enum = pnumber_VAL; + return; + } +} + +/* type_LVT to "type" */ + +const char *_lvt_enum_to_name(int lvt_enum) +{ + return lv_types[lvt_enum].name; +} + +static void _print_usage_description(struct command *cmd) +{ + const char *desc = cmd->desc; + char buf[MAX_LINE] = {0}; + unsigned di = 0; + int bi = 0; + + for (di = 0; di < strlen(desc); di++) { + if (!strncmp(&desc[di], "DESC:", 5)) { + if (bi) { + buf[bi] = '\0'; + printf(" %s\n", buf); + memset(buf, 0, sizeof(buf)); + bi = 0; + } + /* skip DESC: */ + di += 5; + continue; + } + + if (!bi && desc[di] == ' ') + continue; + + if (desc[di] != '\\') + buf[bi++] = desc[di]; + + if (bi == (MAX_LINE - 1)) + break; + } + + if (bi) { + buf[bi] = '\0'; + printf(" %s\n", buf); + } +} + +static void _print_val_usage(struct command *cmd, int opt_enum, int val_enum) +{ + int is_relative_opt = (opt_enum == size_ARG) || + (opt_enum == extents_ARG) || + (opt_enum == poolmetadatasize_ARG) || + (opt_enum == mirrors_ARG); + + /* + * Suppress the [+] prefix for lvcreate which we have to + * accept for backwards compat, but don't want to advertise. + */ + if (!strcmp(cmd->name, "lvcreate") && is_relative_opt) { + if (val_enum == psizemb_VAL) + val_enum = sizemb_VAL; + else if (val_enum == pextents_VAL) + val_enum = extents_VAL; + else if ((val_enum == pnumber_VAL) && (opt_enum == mirrors_ARG)) + val_enum = number_VAL; + } + + if (!val_names[val_enum].usage) + printf("%s", val_names[val_enum].name); + else + printf("%s", val_names[val_enum].usage); +} + +static void _print_usage_def(struct command *cmd, int opt_enum, struct arg_def *def) +{ + int val_enum; + int sep = 0; + + 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("|"); + _print_val_usage(cmd, opt_enum, val_enum); + sep = 1; + } + + /* Too many types have made this too long. man page has this info. */ + /* + if (val_enum == lv_VAL && def->lvt_bits) { + int lvt_enum; + 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 == vg_VAL) && (def->flags & ARG_DEF_FLAG_NEW_VG)) + printf("_new"); + if ((val_enum == lv_VAL) && (def->flags & ARG_DEF_FLAG_NEW_LV)) + printf("_new"); + } + } + + if (def->flags & ARG_DEF_FLAG_MAY_REPEAT) + printf(" ..."); +} + +void print_usage(struct command *cmd, int longhelp, int desc_first) +{ + struct command_name *cname = _find_command_name(cmd->name); + int any_req = (cmd->cmd_flags & CMD_FLAG_ANY_REQUIRED_OPT) ? 1 : 0; + int include_extents = 0; + int ro, rp, oo, op, opt_enum, first; + + /* + * Looks at all variants of each command name and figures out + * which options are common to all variants (for compact output) + */ + factor_common_options(); + + if (desc_first && cmd->desc) + _print_usage_description(cmd); + + printf(" %s", cmd->name); + + if (any_req) { + for (ro = 0; ro < cmd->ro_count; ro++) { + opt_enum = cmd->required_opt_args[ro].opt; + + if (opt_names[opt_enum].short_opt) + printf(" -%c|%s", opt_names[opt_enum].short_opt, opt_names[opt_enum].long_opt); + else + printf(" %s", opt_names[opt_enum].long_opt); + + if (cmd->required_opt_args[ro].def.val_bits) { + printf(" "); + _print_usage_def(cmd, opt_enum, &cmd->required_opt_args[ro].def); + } + } + + /* one required option in a set */ + first = 1; + + /* options with short and long */ + for (ro = cmd->ro_count; ro < cmd->ro_count + cmd->any_ro_count; ro++) { + opt_enum = cmd->required_opt_args[ro].opt; + + if (!opt_names[opt_enum].short_opt) + continue; + + if (first) + printf("\n\t("); + else + printf(",\n\t "); + first = 0; + + printf(" -%c|%s", opt_names[opt_enum].short_opt, opt_names[opt_enum].long_opt); + + if (cmd->required_opt_args[ro].def.val_bits) { + printf(" "); + _print_usage_def(cmd, opt_enum, &cmd->required_opt_args[ro].def); + } + } + + /* options with only long */ + for (ro = cmd->ro_count; ro < cmd->ro_count + cmd->any_ro_count; ro++) { + opt_enum = cmd->required_opt_args[ro].opt; + + if (opt_names[opt_enum].short_opt) + continue; + + if ((opt_enum == size_ARG) && command_has_alternate_extents(cmd->name)) + include_extents = 1; + + if (first) + printf("\n\t("); + else + printf(",\n\t "); + first = 0; + + printf(" %s", opt_names[opt_enum].long_opt); + + if (cmd->required_opt_args[ro].def.val_bits) { + printf(" "); + _print_usage_def(cmd, opt_enum, &cmd->required_opt_args[ro].def); + } + } + + printf_hyphen(')'); + } + + if (!any_req && cmd->ro_count) { + for (ro = 0; ro < cmd->ro_count; ro++) { + opt_enum = cmd->required_opt_args[ro].opt; + + if ((opt_enum == size_ARG) && command_has_alternate_extents(cmd->name)) + include_extents = 1; + + if (opt_names[opt_enum].short_opt) + printf(" -%c|%s", opt_names[opt_enum].short_opt, opt_names[opt_enum].long_opt); + else + printf(" %s", opt_names[opt_enum].long_opt); + + if (cmd->required_opt_args[ro].def.val_bits) { + printf(" "); + _print_usage_def(cmd, opt_enum, &cmd->required_opt_args[ro].def); + } + } + } + + if (cmd->rp_count) { + if (any_req) + printf("\t"); + for (rp = 0; rp < cmd->rp_count; rp++) { + if (cmd->required_pos_args[rp].def.val_bits) { + printf(" "); + _print_usage_def(cmd, 0, &cmd->required_pos_args[rp].def); + } + } + } + + if (!longhelp) + goto done; + + if (!cmd->oo_count) + goto op_count; + + if (cmd->oo_count) { + if (cmd->autotype) { + printf("\n\t"); + if (!cmd->autotype2) + printf("[ --type %s ] (implied)", cmd->autotype); + else + printf("[ --type %s|%s ] (implied)", cmd->autotype, cmd->autotype2); + } + + if (include_extents) { + printf("\n\t[ -l|--extents "); + _print_val_usage(cmd, extents_ARG, opt_names[extents_ARG].val_enum); + 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 lvm options in lvm_all which + * are printed at the end under "Common options for lvm" + * see print_common_options_lvm() + */ + + if (_is_lvm_all_opt(opt_enum)) + continue; + + /* + * When there is more than one variant, + * skip common command options from + * cname->common_options (options common + * to all variants), which are printed at + * the end under "Common options for command" + * see print_common_options_cmd() + */ + + if (cname && (cname->variants > 1) && cname->common_options[opt_enum]) + continue; + + printf("\n\t["); + + printf(" -%c|%s", opt_names[opt_enum].short_opt, opt_names[opt_enum].long_opt); + if (cmd->optional_opt_args[oo].def.val_bits) { + printf(" "); + _print_usage_def(cmd, opt_enum, &cmd->optional_opt_args[oo].def); + } + + printf(" ]"); + } + + /* 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 lvm options in lvm_all which + * are printed at the end under "Common options for lvm" + * see print_common_options_lvm() + */ + + if (_is_lvm_all_opt(opt_enum)) + continue; + + /* + * When there is more than one variant, + * skip common command options from + * cname->common_options (options common + * to all variants), which are printed at + * the end under "Common options for command" + * see print_common_options_cmd() + */ + + if (cname && (cname->variants > 1) && cname->common_options[opt_enum]) + continue; + + printf("\n\t["); + + printf(" %s", opt_names[opt_enum].long_opt); + if (cmd->optional_opt_args[oo].def.val_bits) { + printf(" "); + _print_usage_def(cmd, opt_enum, &cmd->optional_opt_args[oo].def); + } + + printf(" ]"); + } + + printf("\n\t[ COMMON_OPTIONS ]"); + } + + op_count: + if (!cmd->op_count) + goto done; + + printf("\n\t["); + + if (cmd->op_count) { + for (op = 0; op < cmd->op_count; op++) { + if (cmd->optional_pos_args[op].def.val_bits) { + printf(" "); + _print_usage_def(cmd, 0, &cmd->optional_pos_args[op].def); + } + } + } + + printf(" ]"); + done: + printf("\n"); + + if (!desc_first && cmd->desc) + _print_usage_description(cmd); + + printf("\n"); +} + +void print_usage_common_lvm(struct command_name *cname, struct command *cmd) +{ + int oo, opt_enum; + + printf(" Common options for lvm:"); + + /* print options 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; + + printf("\n\t["); + + printf(" -%c|%s", opt_names[opt_enum].short_opt, opt_names[opt_enum].long_opt); + if (lvm_all.optional_opt_args[oo].def.val_bits) { + printf(" "); + _print_usage_def(cmd, opt_enum, &lvm_all.optional_opt_args[oo].def); + } + printf(" ]"); + } + + /* print options 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; + + printf("\n\t["); + + printf(" %s", opt_names[opt_enum].long_opt); + if (lvm_all.optional_opt_args[oo].def.val_bits) { + printf(" "); + _print_usage_def(cmd, opt_enum, &lvm_all.optional_opt_args[oo].def); + } + printf(" ]"); + } + + printf("\n\n"); +} + +void print_usage_common_cmd(struct command_name *cname, struct command *cmd) +{ + int oo, opt_enum; + int found_common_command = 0; + + /* + * when there's more than one variant, options that + * are common to all commands with a common name. + */ + + if (cname->variants < 2) + return; + + 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; + found_common_command = 1; + break; + } + + if (!found_common_command) + return; + + printf(" Common options for command:"); + + /* print options with short opts */ + + 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 (!opt_names[opt_enum].short_opt) + continue; + + printf("\n\t["); + + for (oo = 0; oo < cmd->oo_count; oo++) { + if (cmd->optional_opt_args[oo].opt != opt_enum) + continue; + + printf(" -%c|%s", opt_names[opt_enum].short_opt, opt_names[opt_enum].long_opt); + if (cmd->optional_opt_args[oo].def.val_bits) { + printf(" "); + _print_usage_def(cmd, opt_enum, &cmd->optional_opt_args[oo].def); + } + break; + } + printf(" ]"); + } + + /* print options without short opts */ + + 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 (opt_names[opt_enum].short_opt) + continue; + + printf("\n\t["); + + 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_usage_def(cmd, opt_enum, &cmd->optional_opt_args[oo].def); + } + break; + } + printf(" ]"); + } + + printf("\n\n"); +} + +void print_usage_notes(struct command_name *cname) +{ + if (cname && command_has_alternate_extents(cname->name)) { + printf(" Special options for command:\n"); + printf(" [ --extents Number[PERCENT] ]\n" + " The --extents option can be used in place of --size.\n" + " The number allows an optional percent suffix.\n"); + printf("\n"); + } + + if (cname && !strcmp(cname->name, "lvcreate")) { + printf(" [ --name String ]\n" + " The --name option is not required but is typically used.\n" + " When a name is not specified, a new LV name is generated\n" + " with the \"lvol\" prefix and a unique numeric suffix.\n"); + printf("\n"); + } + + printf(" Common variables for lvm:\n" + " Variables in option or position args are capitalized,\n" + " e.g. PV, VG, LV, Size, Number, String, Tag.\n"); + printf("\n"); + + printf(" PV\n" + " Physical Volume name, a device path under /dev.\n" + " For commands managing physical extents, a PV positional arg\n" + " generally accepts a suffix indicating a range (or multiple ranges)\n" + " of PEs. When the first PE is omitted, it defaults to the start of\n" + " the device, and when the last PE is omitted it defaults to the end.\n" + " PV[:PE-PE]... is start and end range (inclusive),\n" + " PV[:PE+PE]... is start and length range (counting from 0).\n"); + printf("\n"); + + printf(" LV\n" + " Logical Volume name. See lvm(8) for valid names. An LV positional\n" + " arg generally includes the VG name and LV name, e.g. VG/LV.\n" + " LV followed by _<type> indicates that an LV of the given type is\n" + " required. (raid represents raid<N> type).\n" + " The _new suffix indicates that the LV name is new.\n"); + printf("\n"); + + printf(" Tag\n" + " Tag name. See lvm(8) for information about tag names and using\n" + " tags in place of a VG, LV or PV.\n"); + printf("\n"); + + printf(" Select\n" + " Select indicates that a required positional arg can be omitted\n" + " if the --select option is used. No arg appears in this position.\n"); + printf("\n"); + + printf(" Size[UNIT]\n" + " Size is an input number that accepts an optional unit.\n" + " Input units are always treated as base two values, regardless of\n" + " capitalization, e.g. 'k' and 'K' both refer to 1024.\n" + " The default input unit is specified by letter, followed by |UNIT.\n" + " UNIT represents other possible input units: BbBsSkKmMgGtTpPeE.\n" + " (This should not be confused with the output control --units, where\n" + " capital letters mean multiple of 1000.)\n"); + printf("\n"); +} + +#ifdef MAN_PAGE_GENERATOR + +/* + * FIXME: this just replicates the val usage strings + * that officially lives in vals.h. Should there + * be some programatic way to add man markup to + * the strings in vals.h without replicating it? + * Otherwise, this function has to be updated in + * sync with any string changes in vals.h + */ +static void _print_val_man(struct command_name *cname, int opt_enum, int val_enum) +{ + const char *str; + char *line; + char *line_argv[MAX_LINE_ARGC]; + int line_argc; + int i; + int is_relative_opt = (opt_enum == size_ARG) || + (opt_enum == extents_ARG) || + (opt_enum == poolmetadatasize_ARG) || + (opt_enum == mirrors_ARG); + + _was_hyphen = 0; + /* + * Suppress the [+] prefix for lvcreate which we have to + * accept for backwards compat, but don't want to advertise. + */ + if (!strcmp(cname->name, "lvcreate") && is_relative_opt) { + if (val_enum == psizemb_VAL) + val_enum = sizemb_VAL; + else if (val_enum == pextents_VAL) + val_enum = extents_VAL; + else if ((val_enum == pnumber_VAL) && (opt_enum == mirrors_ARG)) + val_enum = number_VAL; + } + + if (val_enum == sizemb_VAL) { + printf("\\fISize\\fP[m|UNIT]"); + return; + } + + if (val_enum == ssizemb_VAL) { + printf("[\\fB+\\fP|\\fB-\\fP]\\fISize\\fP[m|UNIT]"); + return; + } + + if (val_enum == psizemb_VAL) { + printf("[\\fB+\\fP]\\fISize\\fP[m|UNIT]"); + return; + } + + if (val_enum == nsizemb_VAL) { + printf("[\\fB-\\fP]\\fISize\\fP[m|UNIT]"); + return; + } + + if (val_enum == extents_VAL) { + printf("\\fINumber\\fP[PERCENT]"); + return; + } + + if (val_enum == sextents_VAL) { + printf("[\\fB+\\fP|\\fB-\\fP]\\fINumber\\fP[PERCENT]"); + return; + } + + if (val_enum == pextents_VAL) { + printf("[\\fB+\\fP]\\fINumber\\fP[PERCENT]"); + return; + } + + if (val_enum == nextents_VAL) { + printf("[\\fB-\\fP]\\fINumber\\fP[PERCENT]"); + return; + } + + if (val_enum == sizekb_VAL) { + printf("\\fISize\\fP[k|UNIT]"); + return; + } + + if (val_enum == ssizekb_VAL) { + printf("[\\fB+\\fP|\\fB-\\fP]\\fISize\\fP[k|UNIT]"); + return; + } + + if (val_enum == regionsizemb_VAL) { + printf("\\fISize\\fP[m|UNIT]"); + return; + } + + if (val_enum == snumber_VAL) { + printf("[\\fB+\\fP|\\fB-\\fP]\\fINumber\\fP"); + return; + } + + if (val_enum == pnumber_VAL) { + printf("[\\fB+\\fP]\\fINumber\\fP"); + return; + } + + str = val_names[val_enum].usage; + if (!str) + str = val_names[val_enum].name; + + if (!strcmp(str, "PV[:t|n|y]")) { + printf("\\fIPV\\fP[\\fB:t\\fP|\\fBn\\fP|\\fBy\\fP]"); + return; + } + + 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 (strchr(str, '|')) { + if (!(line = strdup(str))) + return; + if ((_was_hyphen = (strlen(line) > _LONG_LINE))) + /* TODO: prevent line to end with already printed space */ + printf("\\c\n.nh\n\\%%"); + _split_line(line, &line_argc, line_argv, '|'); + for (i = 0; i < line_argc; i++) { + if (i) + printf("|%s", _was_hyphen ? "\\:" : ""); + + if (strncmp(line_argv[i], "[Number]", 8) == 0) { + printf("[\\fINumber\\fP]"); + line_argv[i] += 8; + } + + if (strstr(line_argv[i], "Number")) + printf("\\fI%s\\fP", line_argv[i]); + else + printf("\\fB%s\\fP", line_argv[i]); + } + if (_was_hyphen) + printf("\n.hy"); + free(line); + return; + } + + printf("\\fB%s\\fP", str); +} + +static void _print_def_man(struct command_name *cname, int opt_enum, struct arg_def *def, int usage, uint64_t *lv_type_bits) +{ + int val_enum; + int sep = 0; + + if (lv_type_bits) + *lv_type_bits = 0; + + 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%s\\fP", def->str); + + else if (val_enum == constnum_VAL) + printf("\\fB%llu\\fP", (unsigned long long)def->num); + + else { + if (sep) printf("|"); + + if (!usage || !val_names[val_enum].usage) { + if (_was_hyphen) { + printf("\n"); + _was_hyphen = 0; + } + + /* special case to print LV1 instead of LV */ + if ((val_enum == lv_VAL) && def->lvt_bits && lv_type_bits) { + printf("\\fILV1\\fP"); + *lv_type_bits = def->lvt_bits; + } else { + printf("\\fI%s\\fP", val_names[val_enum].name); + } + } else { + _print_val_man(cname, opt_enum, val_enum); + } + + sep = 1; + } + + if (((val_enum == vg_VAL) && (def->flags & ARG_DEF_FLAG_NEW_VG)) || + ((val_enum == lv_VAL) && (def->flags & ARG_DEF_FLAG_NEW_LV))) + printf("\\fI_new\\fP"); + } + } + + if (def->flags & ARG_DEF_FLAG_MAY_REPEAT) + printf(" ..."); +} + +#define LONG_OPT_NAME_LEN 64 +static const char *_man_long_opt_name(const char *cmdname, int opt_enum) +{ + static char long_opt_name[LONG_OPT_NAME_LEN]; + const char *long_opt; + unsigned i; + + memset(&long_opt_name, 0, sizeof(long_opt_name)); + + switch (opt_enum) { + case syncaction_ARG: + long_opt = "--[raid]syncaction"; + break; + case writemostly_ARG: + long_opt = "--[raid]writemostly"; + break; + case minrecoveryrate_ARG: + long_opt = "--[raid]minrecoveryrate"; + break; + case maxrecoveryrate_ARG: + long_opt = "--[raid]maxrecoveryrate"; + break; + case writebehind_ARG: + long_opt = "--[raid]writebehind"; + break; + case vgmetadatacopies_ARG: + if (!strncmp(cmdname, "vg", 2)) + long_opt = "--[vg]metadatacopies"; + else + long_opt = "--vgmetadatacopies"; + break; + case pvmetadatacopies_ARG: + if (!strncmp(cmdname, "pv", 2)) + long_opt = "--[pv]metadatacopies"; + else + long_opt = "--pvmetadatacopies"; + break; + default: + long_opt = opt_names[opt_enum].long_opt; + break; + } + + if (strchr(long_opt, '[')) { + for (i = 0; *long_opt && i < sizeof(long_opt_name) - 1; ++long_opt, ++i) { + if (i < (sizeof(long_opt_name) - 8)) + switch(*long_opt) { + case '[': + strcpy(long_opt_name + i, "\\fP[\\fB"); + i += 6; + continue; + case ']': + strcpy(long_opt_name + i, "\\fP]\\fB"); + i += 6; + continue; + } + long_opt_name[i] = *long_opt; + } + long_opt_name[i] = 0; + return long_opt_name; + } + + return long_opt; +} + +static void _print_man_usage(char *lvmname, struct command *cmd) +{ + struct command_name *cname; + int any_req = (cmd->cmd_flags & CMD_FLAG_ANY_REQUIRED_OPT) ? 1 : 0; + int sep, ro, rp, oo, op, opt_enum; + int need_ro_indent_end = 0; + int include_extents = 0; + int lvt_enum; + uint64_t lv_type_bits = 0; + + _was_hyphen = 0; + if (!(cname = _find_command_name(cmd->name))) + return; + + printf("\\fB%s\\fP", lvmname); + + if (!any_req) + goto ro_normal; + + /* + * required options that follow command name, all required + */ + if (cmd->ro_count) { + sep = 0; + + for (ro = 0; ro < cmd->ro_count; ro++) { + + /* avoid long line wrapping */ + if ((cmd->ro_count > 2) && (sep == 2)) { + printf("\n.RS 5\n"); + need_ro_indent_end = 1; + } + + opt_enum = cmd->required_opt_args[ro].opt; + + if ((opt_enum == size_ARG) && command_has_alternate_extents(cmd->name)) + include_extents = 1; + + 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(cname, opt_enum, &cmd->required_opt_args[ro].def, 1, NULL); + } + + sep++; + } + } + + /* + * 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->any_ro_count) { + printf("\n"); + printf(".RS 4\n"); + printf("("); + + sep = 0; + + /* print required options with a short opt */ + for (ro = cmd->ro_count; ro < cmd->ro_count + cmd->any_ro_count; ro++) { + opt_enum = cmd->required_opt_args[ro].opt; + + if (!opt_names[opt_enum].short_opt) + continue; + + if (sep) { + 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(cname, opt_enum, &cmd->required_opt_args[ro].def, 1, NULL); + } + + sep++; + } + + /* print required options without a short opt */ + for (ro = cmd->ro_count; ro < cmd->ro_count + cmd->any_ro_count; ro++) { + opt_enum = cmd->required_opt_args[ro].opt; + + if (opt_names[opt_enum].short_opt) + continue; + + if (sep) { + printf("\n.br\n"); + printf(" "); + } else + printf(".ad l\n"); + + 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(cname, opt_enum, &cmd->required_opt_args[ro].def, 1, NULL); + } + + sep++; + } + + printf_hyphen(')'); + printf(".RE\n"); + } + + /* print required position args on a new line after the any_req 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(cname, 0, &cmd->required_pos_args[rp].def, 1, NULL); + } + } + + 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) { + sep = 0; + + for (ro = 0; ro < cmd->ro_count; ro++) { + + /* avoid long line wrapping */ + if ((cmd->ro_count > 2) && (sep == 2)) { + printf("\n.RS 5\n"); + need_ro_indent_end = 1; + } + + opt_enum = cmd->required_opt_args[ro].opt; + + if ((opt_enum == size_ARG) && command_has_alternate_extents(cmd->name)) + include_extents = 1; + + 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(cname, opt_enum, &cmd->required_opt_args[ro].def, 1, NULL); + } + + sep++; + } + } + + /* 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(" "); + /* Only print lv_type_bits for one LV arg (no cases exist with more) */ + _print_def_man(cname, 0, &cmd->required_pos_args[rp].def, 1, lv_type_bits ? NULL : &lv_type_bits); + } + } + + printf("\n"); + } else { + printf("\n"); + } + + if (need_ro_indent_end) + printf(".RE\n"); + + printf(".br\n"); + + oo_count: + if (!cmd->oo_count) + goto op_count; + + sep = 0; + + if (cmd->oo_count) { + printf(".RS 4\n"); + printf(".ad l\n"); + + if (cmd->autotype) { + if (!cmd->autotype2) + printf("[ \\fB--type %s\\fP ] (implied)\n", cmd->autotype); + else + printf("[ \\fB--type %s\\fP|\\fB%s\\fP ] (implied)\n", cmd->autotype, cmd->autotype2); + printf(".br\n"); + sep = 1; + } + + if (include_extents) { + /* + * NB we don't just pass extents_VAL here because the + * actual val type for extents_ARG has been adjusted + * in opt_names[] according to the command name. + */ + printf("[ \\fB-l\\fP|\\fB--extents\\fP "); + _print_val_man(cname, extents_ARG, opt_names[extents_ARG].val_enum); + + printf_hyphen(']'); + sep = 1; + } + + /* 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; + + if (_is_lvm_all_opt(opt_enum)) + continue; + + if ((cname->variants > 1) && cname->common_options[opt_enum]) + continue; + + if (sep) + printf(".br\n"); + + 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(cname, opt_enum, &cmd->optional_opt_args[oo].def, 1, NULL); + } + printf_hyphen(']'); + 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; + + if (_is_lvm_all_opt(opt_enum)) + continue; + + if ((cname->variants > 1) && cname->common_options[opt_enum]) + continue; + + if (sep) + printf(".br\n"); + + /* 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(cname, opt_enum, &cmd->optional_opt_args[oo].def, 1, NULL); + } + printf_hyphen(']'); + sep = 1; + } + + if (sep) { + printf(".br\n"); + /* space alignment without short opt */ + /* printf(" "); */ + } + printf("[ COMMON_OPTIONS ]\n"); + printf(".ad b\n"); + printf(".RE\n"); + } + + op_count: + if (!cmd->op_count) + goto out; + + 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(cname, 0, &cmd->optional_pos_args[op].def, 1, NULL); + } + } + } + + printf_hyphen(']'); + printf(".RE\n"); + +out: + printf(".P\n"); + if (!lv_type_bits) + return; + + printf(".RS 4\n"); + + if (lv_type_bits) { + printf("LV1 types:"); + for (lvt_enum = 1; lvt_enum < LVT_COUNT; lvt_enum++) { + if (lvt_bit_is_set(lv_type_bits, lvt_enum)) + printf(" %s", _lvt_enum_to_name(lvt_enum)); + } + printf("\n"); + } + printf(".RE\n"); + printf(".P\n"); +} + +/* + * common options listed in the usage section. + * + * For commands with only one variant, this is only + * the options which are common to all lvm commands + * (in lvm_all, see _is_lvm_all_opt). + * + * For commands with more than one variant, this + * is the set of options common to all variants + * (in cname->common_options), (which obviously + * includes the options common to all lvm commands.) + * + * List ordering: + * options with short+long names, alphabetically, + * then options with only long names, alphabetically + */ + +static void _print_man_usage_common_lvm(struct command *cmd) +{ + struct command_name *cname; + int i, sep, oo, opt_enum; + + if (!(cname = _find_command_name(cmd->name))) + return; + + printf("Common options for lvm:\n"); + printf(".\n"); + + sep = 0; + + printf(".RS 4\n"); + printf(".ad l\n"); + + /* print those with short opts */ + for (i = 0; i < ARG_COUNT; i++) { + opt_enum = opt_names_alpha[i]->opt_enum; + + if (!opt_names[opt_enum].short_opt) + continue; + + if (!_is_lvm_all_opt(opt_enum)) + continue; + + if (sep) + printf(".br\n"); + + 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(cname, opt_enum, &cmd->optional_opt_args[oo].def, 1, NULL); + } + printf_hyphen(']'); + sep = 1; + break; + } + + } + + /* print those without short opts */ + for (i = 0; i < ARG_COUNT; i++) { + opt_enum = opt_names_alpha[i]->opt_enum; + + if (opt_names[opt_enum].short_opt) + continue; + + if (!_is_lvm_all_opt(opt_enum)) + continue; + + if (sep) + printf(".br\n"); + + 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(cname, opt_enum, &cmd->optional_opt_args[oo].def, 1, NULL); + } + printf_hyphen(']'); + sep = 1; + break; + } + } + + printf(".ad b\n"); + printf(".RE\n"); +} + +static void _print_man_usage_common_cmd(struct command *cmd) +{ + struct command_name *cname; + int i, sep, oo, opt_enum; + int found_common_command = 0; + + if (!(cname = _find_command_name(cmd->name))) + return; + + if (cname->variants < 2) + return; + + 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; + found_common_command = 1; + break; + } + + if (!found_common_command) + return; + + printf("Common options for command:\n"); + printf(".\n"); + + sep = 0; + + printf(".RS 4\n"); + printf(".ad l\n"); + + /* print those with short opts */ + for (i = 0; i < ARG_COUNT; i++) { + opt_enum = opt_names_alpha[i]->opt_enum; + + if (!cname->common_options[opt_enum]) + continue; + + if (!opt_names[opt_enum].short_opt) + continue; + + /* common cmd options only used with variants */ + if (cname->variants < 2) + continue; + + if (_is_lvm_all_opt(opt_enum)) + continue; + + if (sep) + printf(".br\n"); + + 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(cname, opt_enum, &cmd->optional_opt_args[oo].def, 1, NULL); + } + printf_hyphen(']'); + sep = 1; + break; + } + } + + /* print those without short opts */ + for (i = 0; i < ARG_COUNT; i++) { + opt_enum = opt_names_alpha[i]->opt_enum; + + if (!cname->common_options[opt_enum]) + continue; + + if (opt_names[opt_enum].short_opt) + continue; + + /* common cmd options only used with variants */ + if (cname->variants < 2) + continue; + + if (_is_lvm_all_opt(opt_enum)) + continue; + + if (sep) + printf(".br\n"); + + 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(cname, opt_enum, &cmd->optional_opt_args[oo].def, 1, NULL); + } + printf_hyphen(']'); + sep = 1; + break; + } + } + + printf(".ad b\n"); + printf(".RE\n"); + printf(".P\n"); +} + +/* + * Format of description, when different command names have + * different descriptions: + * + * "#cmdname1" + * "text foo goes here" + * "a second line of text." + * "#cmdname2" + * "text bar goes here" + * "another line of text." + * + * When called for cmdname2, this function should just print: + * + * "text bar goes here" + * "another line of text." + */ + +static void _print_man_option_desc(struct command_name *cname, int opt_enum) +{ + const char *desc = opt_names[opt_enum].desc; + char buf[DESC_LINE]; + int started_cname = 0; + int line_count = 0; + int bi = 0; + unsigned di; + + if (desc[0] != '#') { + printf("%s", desc); + return; + } + + for (di = 0; di < strlen(desc); di++) { + buf[bi++] = desc[di]; + + if (bi == DESC_LINE) { + log_error("Parsing command defs: print_man_option_desc line too long."); + exit(EXIT_FAILURE); + } + + if (buf[bi-1] != '\n') + continue; + + if (buf[0] != '#') { + if (started_cname) { + printf("%s", buf); + line_count++; + } + + memset(buf, 0, sizeof(buf)); + bi = 0; + continue; + } + + /* Line starting with #cmdname */ + + /* + * Must be starting a new command name. + * If no lines have been printed, multiple command names + * are using the same text. If lines have been printed, + * then the start of a new command name means the end + * of text for the current command name. + */ + if (line_count && started_cname) + return; + + if (!strncmp(buf + 1, cname->name, strlen(cname->name))) { + /* The start of our command name. */ + started_cname = 1; + memset(buf, 0, sizeof(buf)); + bi = 0; + } else { + /* The start of another command name. */ + memset(buf, 0, sizeof(buf)); + bi = 0; + } + } + + if (bi && started_cname) + printf("%s", buf); +} + +/* + * Print a list of all options names for a given command name. + */ + +static void _print_man_all_options_list(struct command_name *cname) +{ + int opt_enum, val_enum; + int sep = 0; + int i; + int adl = 0; + + for (i = 0; i < ARG_COUNT; i++) { + opt_enum = opt_names_alpha[i]->opt_enum; + + if (!cname->all_options[opt_enum]) + continue; + + if (sep) + printf(".br\n"); + + if (!adl) { + printf(".ad l\n"); + adl = 1; + } + + if (opt_names[opt_enum].short_opt) { + printf(" \\fB-%c\\fP|\\fB%s\\fP", + opt_names[opt_enum].short_opt, + _man_long_opt_name(cname->name, opt_enum)); + } else { + /* spaces for alignment without short opt */ + 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(cname, opt_enum, val_enum); + } + + printf("\n"); + sep = 1; + } + + if (adl) + printf(".ad b\n"); + +} + +/* + * All options used for a given command name, along with descriptions. + */ + +static void _print_man_all_options_desc(struct command_name *cname) +{ + int opt_enum, val_enum; + int i; + int adl; + + for (i = 0; i < ARG_COUNT; i++) { + opt_enum = opt_names_alpha[i]->opt_enum; + + if (!cname->all_options[opt_enum]) + continue; + + val_enum = opt_names[opt_enum].val_enum; + + if (val_names[val_enum].usage && + (strlen(val_names[val_enum].usage) > _LONG_LINE)) { + printf(".\n.HP\n"); + printf(".ad l\n"); + adl = 1; + } else { + /* printf(".\n.TP\n"); + * ATM HTML rendering can't handle HP and TP mixing properly + * so still keeping .HP usage for this case + * untill some better workaround is found + * .TP does not need .br */ + printf(".\n.HP\n"); + adl = 0; + } + + if (opt_names[opt_enum].short_opt) { + printf("\\fB-%c\\fP|\\fB%s\\fP", + opt_names[opt_enum].short_opt, + _man_long_opt_name(cname->name, opt_enum)); + } else { + printf("\\fB%s\\fP", _man_long_opt_name(cname->name, opt_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(cname, opt_enum, val_enum); + } + + if (opt_names[opt_enum].flags & ARG_COUNTABLE) + printf(" ..."); + + printf("\n"); + if (adl) { + printf(".ad b\n"); + } + printf(".br\n"); + + if (opt_names[opt_enum].desc) { + _print_man_option_desc(cname, opt_enum); + } + } +} + +static void _print_man_all_positions_desc(struct command_name *cname) +{ + struct command *cmd; + int ci, rp, op; + int has_vg_val = 0; + int has_lv_val = 0; + int has_pv_val = 0; + int has_tag_val = 0; + int has_select_val = 0; + int has_lv_type = 0; + + for (ci = 0; ci < COMMAND_COUNT; ci++) { + cmd = &commands[ci]; + + if (strcmp(cmd->name, cname->name)) + continue; + + for (rp = 0; rp < cmd->rp_count; rp++) { + if (cmd->required_pos_args[rp].def.val_bits & val_enum_to_bit(vg_VAL)) + has_vg_val = 1; + + if (cmd->required_pos_args[rp].def.val_bits & val_enum_to_bit(lv_VAL)) { + has_lv_val = 1; + if (cmd->required_pos_args[rp].def.lvt_bits) + has_lv_type = 1; + } + + if (cmd->required_pos_args[rp].def.val_bits & val_enum_to_bit(pv_VAL)) + has_pv_val = 1; + + if (cmd->required_pos_args[rp].def.val_bits & val_enum_to_bit(tag_VAL)) + has_tag_val = 1; + + if (cmd->required_pos_args[rp].def.val_bits & val_enum_to_bit(select_VAL)) + has_select_val = 1; + } + + for (op = 0; op < cmd->op_count; op++) { + if (cmd->optional_pos_args[op].def.val_bits & val_enum_to_bit(vg_VAL)) + has_vg_val = 1; + + if (cmd->optional_pos_args[op].def.val_bits & val_enum_to_bit(lv_VAL)) { + has_lv_val = 1; + if (cmd->optional_pos_args[op].def.lvt_bits) + has_lv_type = 1; + } + + if (cmd->optional_pos_args[op].def.val_bits & val_enum_to_bit(pv_VAL)) + has_pv_val = 1; + + if (cmd->optional_pos_args[op].def.val_bits & val_enum_to_bit(tag_VAL)) + has_tag_val = 1; + + if (cmd->optional_pos_args[op].def.val_bits & val_enum_to_bit(select_VAL)) + has_select_val = 1; + } + } + + if (has_vg_val) { + printf(".TP\n"); + + printf(".I %s\n", val_names[vg_VAL].name); + printf("Volume Group name. See \\fBlvm\\fP(8) for valid names.\n"); + + if (!strcmp(cname->name, "lvcreate")) + printf("For lvcreate, the required VG positional arg may be\n" + "omitted when the VG name is included in another option,\n" + "e.g. --name VG/LV.\n"); + } + + if (has_lv_val) { + printf(".TP\n"); + + printf(".I %s\n", val_names[lv_VAL].name); + printf("Logical Volume name. See \\fBlvm\\fP(8) for valid names.\n" + "An LV positional arg generally includes the VG name and LV name, e.g. VG/LV.\n"); + + if (has_lv_type) + printf("LV1 indicates the LV must have a specific type, where the\n" + "accepted LV types are listed. (raid represents raid<N> type).\n"); + + } + + if (has_pv_val) { + printf(".TP\n"); + + printf(".I %s\n", val_names[pv_VAL].name); + printf("Physical Volume name, a device path under /dev.\n" + "For commands managing physical extents, a PV positional arg\n" + "generally accepts a suffix indicating a range (or multiple ranges)\n" + "of physical extents (PEs). When the first PE is omitted, it defaults\n" + "to the start of the device, and when the last PE is omitted it defaults to end.\n" + "Start and end range (inclusive): \\fIPV\\fP[\\fB:\\fP\\fIPE\\fP\\fB-\\fP\\fIPE\\fP]...\n" + "Start and length range (counting from 0): \\fIPV\\fP[\\fB:\\fP\\fIPE\\fP\\fB+\\fP\\fIPE\\fP]...\n"); + } + + if (has_tag_val) { + printf(".TP\n"); + + printf(".I %s\n", val_names[tag_VAL].name); + printf("Tag name. See \\fBlvm\\fP(8) for information about tag names and using tags\n" + "in place of a VG, LV or PV.\n"); + } + + if (has_select_val) { + printf(".TP\n"); + + printf(".I %s\n", val_names[select_VAL].name); + printf("Select indicates that a required positional parameter can\n" + "be omitted if the \\fB--select\\fP option is used.\n" + "No arg appears in this position.\n"); + } + + /* Every command uses a string arg somewhere. */ + + printf(".TP\n"); + printf(".I %s\n", val_names[string_VAL].name); + printf("See the option description for information about the string content.\n"); + + /* + * We could possibly check if the command accepts any option that + * uses Size, and only print this in those cases, but this seems + * so common that we should probably always print it. + */ + + printf(".TP\n"); + printf(".IR Size [UNIT]\n"); + printf("Size is an input number that accepts an optional unit.\n" + "Input units are always treated as base two values, regardless of\n" + "capitalization, e.g. 'k' and 'K' both refer to 1024.\n" + "The default input unit is specified by letter, followed by |UNIT.\n" + "UNIT represents other possible input units:\n" + ".BR b | B\nis bytes,\n.BR s | S\nis sectors of 512 bytes,\n.BR k | K\nis KiB,\n" + ".BR m | M\nis MiB,\n.BR g | G\nis GiB,\n.BR t | T\nis TiB,\n" + ".BR p | P\nis PiB,\n.BR e | E\nis EiB.\n" + "(This should not be confused with the output control --units, where\n" + "capital letters mean multiple of 1000.)\n"); + + printf(".\n.SH ENVIRONMENT VARIABLES\n.\n"); + printf("See \\fBlvm\\fP(8) for information about environment variables used by lvm.\n" + "For example, LVM_VG_NAME can generally be substituted for a required VG parameter.\n"); +} + +static void _print_desc_man(const char *desc) +{ + char buf[DESC_LINE] = {0}; + unsigned di; + 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 const 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; +} + +#define MAX_MAN_DESC (1024 * 1024) + +static int _include_description_file(char *name, char *des_file) +{ + char *buf; + int fd, r = 0; + ssize_t sz; + struct stat statbuf = { 0 }; + + if ((fd = open(des_file, O_RDONLY)) < 0) { + log_error("Failed to open description file %s.", des_file); + return 0; + } + + if (fstat(fd, &statbuf) < 0) { + log_error("Failed to stat description file %s.", des_file); + goto out_close; + } + + if (statbuf.st_size > MAX_MAN_DESC) { + log_error("Description file %s is too large.", des_file); + goto out_close; + } + + if (!(buf = malloc(statbuf.st_size + 1))) { + log_error("Failed to allocate buffer for description file %s.", des_file); + goto out_close; + } + + if ((sz = read(fd, buf, statbuf.st_size)) < 0) { + log_error("Failed to read description file %s.", des_file); + goto out_free; + } + + buf[sz] = '\0'; + printf(".\n.SH DESCRIPTION\n.\n%s", buf); + r = 1; + +out_free: + free(buf); +out_close: + (void) close(fd); + + return r; +} + +static int _print_man(char *name, char *des_file, int secondary) +{ + struct command_name *cname; + struct command *cmd, *prev_cmd = NULL; + char *lvmname = name; + int i; + + if (!strncmp(name, "lvm-", 4)) { + name[3] = ' '; + name += 4; + } + + cname = _find_command_name(name); + + printf(".TH %s 8 \"LVM TOOLS #VERSION#\" \"Red Hat, Inc.\"\n", + _upper_command_name(lvmname)); + + for (i = 0; i < COMMAND_COUNT; i++) { + + cmd = &commands[i]; + + if (prev_cmd && strcmp(prev_cmd->name, cmd->name)) { + _print_man_usage_common_cmd(prev_cmd); + _print_man_usage_common_lvm(prev_cmd); + + printf(".\n.SH OPTIONS\n.\n"); + _print_man_all_options_desc(cname); + printf(".\n.SH VARIABLES\n.\n"); + _print_man_all_positions_desc(cname); + + prev_cmd = NULL; + } + + if (cmd->cmd_flags & CMD_FLAG_PREVIOUS_SYNTAX) + continue; + + if ((cmd->cmd_flags & CMD_FLAG_SECONDARY_SYNTAX) && !secondary) + continue; + + if (strcmp(name, cmd->name)) + continue; + + if (!prev_cmd || strcmp(prev_cmd->name, cmd->name)) { + printf(".\n.SH NAME\n.\n"); + if (cname && cname->desc) + printf("%s \\(em %s\n", lvmname, cname->desc); + else + printf("%s\n", lvmname); + + printf(".\n.SH SYNOPSIS\n.\n"); + prev_cmd = cmd; + + if (!(cname = _find_command_name(cmd->name))) + return 0; + + if (cname->variant_has_ro && cname->variant_has_rp) + printf("\\fB%s\\fP \\fIoption_args\\fP \\fIposition_args\\fP\n", lvmname); + else if (cname->variant_has_ro && !cname->variant_has_rp) + printf("\\fB%s\\fP \\fIoption_args\\fP\n", lvmname); + else if (!cname->variant_has_ro && cname->variant_has_rp) + printf("\\fB%s\\fP \\fIposition_args\\fP\n", lvmname); + else if (!cname->variant_has_ro && !cname->variant_has_rp) + printf("\\fB%s\\fP\n", lvmname); + + printf(".br\n"); + + if (cname->variant_has_oo) { + printf(" [ \\fIoption_args\\fP ]\n"); + printf(".br\n"); + } + + if (cname->variant_has_op) { + printf(" [ \\fIposition_args\\fP ]\n"); + printf(".br\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_list(cname); + } + + if (des_file && !_include_description_file(lvmname, des_file)) + return 0; + + printf(".\n.SH USAGE\n.\n"); + } + + if (cmd->desc) { + _print_desc_man(cmd->desc); + printf(".P\n"); + } + + _print_man_usage(lvmname, cmd); + + if (i == (COMMAND_COUNT - 1)) { + _print_man_usage_common_cmd(cmd); + _print_man_usage_common_lvm(cmd); + + printf(".\n.SH OPTIONS\n.\n"); + _print_man_all_options_desc(cname); + printf(".\n.SH VARIABLES\n.\n"); + _print_man_all_positions_desc(cname); + } else { + if (cname->variants > 2) { + printf("\\(em\n"); + printf(".P\n"); + } + } + } + + return 1; +} + +static void _print_man_secondary(char *name) +{ + struct command *cmd; + char *lvmname = name; + int header = 0; + int i; + + if (!strncmp(name, "lvm-", 4)) + name += 4; + + for (i = 0; i < COMMAND_COUNT; i++) { + + cmd = &commands[i]; + + if (cmd->cmd_flags & CMD_FLAG_PREVIOUS_SYNTAX) + continue; + + if (!(cmd->cmd_flags & CMD_FLAG_SECONDARY_SYNTAX)) + continue; + + if (strcmp(name, cmd->name)) + continue; + + if (!header) { + printf(".\n.SH ADVANCED USAGE\n.\n"); + printf("Alternate command forms, advanced command usage, and listing of all valid syntax for completeness.\n"); + printf(".P\n"); + header = 1; + } + + if (cmd->desc) { + _print_desc_man(cmd->desc); + printf(".P\n"); + } + + _print_man_usage(lvmname, cmd); + + printf("\\(em\n"); + printf(".P\n"); + } +} + +static void _print_opt_list(const char *prefix, int *opt_list, int opt_count) +{ + int i; + int opt_enum; + + printf("%s ", prefix); + for (i = 0; i < opt_count; i++) { + opt_enum = opt_list[i]; + printf(" %s", opt_names[opt_enum].long_opt); + } + printf("\n"); +} + +/* return 1 if the lists do not match, 0 if they match */ +static int _compare_opt_lists(int *list1, int count1, int *list2, int count2, const char *type1_str, const char *type2_str) +{ + int i, j; + + if (count1 != count2) + return 1; + + for (i = 0; i < count1; i++) { + for (j = 0; j < count2; j++) { + + /* lists do not match if one has --type foo and the other --type bar */ + if ((list1[i] == type_ARG) && (list2[j] == type_ARG) && + type1_str && type2_str && strcmp(type1_str, type2_str)) { + return 1; + } + + if (list1[i] == list2[j]) + goto next; + } + return 1; + next: + ; + } + + return 0; +} + +static int _compare_cmds(struct command *cmd1, struct command *cmd2, int *all_req_opts) +{ + const char *cmd1_type_str = NULL; + const char *cmd2_type_str = NULL; + int opt_list_1[ARG_COUNT] = { 0 }; + int opt_list_2[ARG_COUNT] = { 0 }; + int opt_count_1 = 0; + int opt_count_2 = 0; + int i, j; + int r = 1; + + /* different number of required pos items means different cmds */ + if (cmd1->rp_count != cmd2->rp_count) + return 1; + + /* different types of required pos items means different cmds */ + for (i = 0; i < cmd1->rp_count; i++) { + if (cmd1->required_pos_args[i].def.val_bits != cmd2->required_pos_args[i].def.val_bits) + return 1; + } + + /* create opt list from cmd1 */ + for (i = 0; i < cmd1->ro_count; i++) { + if (!all_req_opts[cmd1->required_opt_args[i].opt]) + continue; + + opt_list_1[opt_count_1++] = cmd1->required_opt_args[i].opt; + + if (cmd1->required_opt_args[i].opt == type_ARG) + cmd1_type_str = cmd1->required_opt_args[i].def.str; + } + + /* create opt list from cmd2 */ + for (i = 0; i < cmd2->ro_count; i++) { + if (!all_req_opts[cmd2->required_opt_args[i].opt]) + continue; + + opt_list_2[opt_count_2++] = cmd2->required_opt_args[i].opt; + + if (cmd2->required_opt_args[i].opt == type_ARG) + cmd2_type_str = cmd2->required_opt_args[i].def.str; + } + + /* "--type foo" and "--type bar" are different */ + if (cmd1_type_str && cmd2_type_str && strcmp(cmd1_type_str, cmd2_type_str)) + return 1; + + /* compare opt_list_1 and opt_list_2 */ + if (!_compare_opt_lists(opt_list_1, opt_count_1, opt_list_2, opt_count_2, NULL, NULL)) { + log_error("Repeated commands %s %s", cmd1->command_id, cmd2->command_id); + log_error("cmd1: %s", cmd1->desc); + log_error("cmd2: %s", cmd2->desc); + _print_opt_list("cmd1 options: ", opt_list_1, opt_count_1); + _print_opt_list("cmd2 options: ", opt_list_2, opt_count_2); + printf("\n"); + r = 0; + } + + /* check if cmd1 matches cmd2 + one of its oo */ + for (i = 0; i < cmd2->oo_count; i++) { + /* for each cmd2 optional_opt_arg, add it to opt_list_2 + and compare opt_list_1 and opt_list_2 again */ + + /* cmd1 "--type foo" and cmd2 OO "--type bar" are different */ + if (cmd2->optional_opt_args[i].opt == type_ARG) { + if (cmd2->optional_opt_args[i].def.str && cmd1_type_str && + strcmp(cmd2->optional_opt_args[i].def.str, cmd1_type_str)) + return 1; + } + + opt_list_2[opt_count_2] = cmd2->optional_opt_args[i].opt; + + if (!_compare_opt_lists(opt_list_1, opt_count_1, opt_list_2, opt_count_2+1, NULL, NULL)) { + log_error("Repeated commands %s %s", cmd1->command_id, cmd2->command_id); + log_error("cmd1: %s", cmd1->desc); + log_error("cmd2: %s", cmd2->desc); + log_error("Included cmd2 OO: %s", opt_names[cmd2->optional_opt_args[i].opt].long_opt); + _print_opt_list("cmd1 options: ", opt_list_1, opt_count_1); + _print_opt_list("cmd2 options: ", opt_list_2, opt_count_2+1); + printf("\n"); + r = 0; + } + } + + /* check if cmd1 + an oo matches cmd2 + an oo */ + + if (!cmd1_type_str) { + for (i = 0; i < cmd1->oo_count; i++) { + if (cmd1->optional_opt_args[i].opt == type_ARG) + cmd1_type_str = cmd1->optional_opt_args[i].def.str; + } + } + if (!cmd2_type_str) { + for (j = 0; j < cmd2->oo_count; j++) { + if (cmd2->optional_opt_args[j].opt == type_ARG) + cmd2_type_str = cmd2->optional_opt_args[j].def.str; + } + } + + for (i = 0; i < cmd1->oo_count; i++) { + + for (j = 0; j < cmd2->oo_count; j++) { + if (cmd1->optional_opt_args[i].opt == cmd2->optional_opt_args[j].opt) + continue; + + opt_list_1[opt_count_1] = cmd1->optional_opt_args[i].opt; + opt_list_2[opt_count_2] = cmd2->optional_opt_args[j].opt; + + if (!_compare_opt_lists(opt_list_1, opt_count_1+1, opt_list_2, opt_count_2+1, cmd1_type_str, cmd2_type_str)) { + log_error("Repeated commands %s %s", cmd1->command_id, cmd2->command_id); + log_error("cmd1: %s", cmd1->desc); + log_error("cmd2: %s", cmd2->desc); + log_error("Included cmd1 OO: %s and cmd2 OO: %s", + opt_names[cmd1->optional_opt_args[i].opt].long_opt, + opt_names[cmd2->optional_opt_args[j].opt].long_opt); + _print_opt_list("cmd1 options: ", opt_list_1, opt_count_1+1); + _print_opt_list("cmd2 options: ", opt_list_2, opt_count_2+1); + printf("\n"); + r = 0; + } + } + } + return r; +} + +static int _check_overlap(void) +{ + int all_req_opts[ARG_COUNT] = { 0 }; + struct command *cmd1, *cmd2; + int i, j; + int r = 1; + + for (i = 0; i < COMMAND_COUNT; i++) { + cmd1 = &commands[i]; + for (j = 0; j < cmd1->ro_count; j++) + all_req_opts[cmd1->required_opt_args[j].opt] = 1; + } + + for (i = 0; i < COMMAND_COUNT; i++) { + + cmd1 = &commands[i]; + + if (cmd1->any_ro_count) + continue; + + for (j = 0; j < COMMAND_COUNT; j++) { + if (i == j) + continue; + + cmd2 = &commands[j]; + + if (cmd2->any_ro_count) + continue; + + if (strcmp(cmd1->name, cmd2->name)) + continue; + + if (!_compare_cmds(cmd1, cmd2, all_req_opts)) + r = 0; + } + } + return r; +} + +#define STDOUT_BUF_SIZE (MAX_MAN_DESC + 4 * 1024) + +int main(int argc, char *argv[]) +{ + struct cmd_context cmdtool = { 0 }; + char *cmdname = NULL; + char *desfile = NULL; + char *stdout_buf; + int primary = 0; + int secondary = 0; + int check = 0; + int r = 0; + size_t sz = STDOUT_BUF_SIZE; + + static struct option long_options[] = { + {"primary", no_argument, 0, 'p' }, + {"secondary", no_argument, 0, 's' }, + {"check", no_argument, 0, 'c' }, + {0, 0, 0, 0 } + }; + + memset(&commands, 0, sizeof(commands)); + + if (!(stdout_buf = malloc(sz))) + log_error("Failed to allocate stdout buffer; carrying on with default buffering."); + else + setbuffer(stdout, stdout_buf, sz); + + while (1) { + int c; + int option_index = 0; + + c = getopt_long(argc, argv, "psc", long_options, &option_index); + if (c == -1) + break; + + switch (c) { + case '0': + break; + case 'p': + primary = 1; + break; + case 's': + secondary = 1; + break; + case 'c': + check = 1; + break; + } + } + + if (!primary && !secondary && !check) { + log_error("Usage: %s --primary|--secondary|--check <command> [/path/to/description-file].", argv[0]); + goto out_free; + } + + if (optind < argc) { + if (!(cmdname = strdup(argv[optind++]))) { + log_error("Out of memory."); + goto out_free; + } + } else if (!check) { + log_error("Missing command name."); + goto out_free; + } + + if (optind < argc) + desfile = argv[optind++]; + + if (!define_commands(&cmdtool, NULL)) + goto out_free; + + if (!check) + configure_command_option_values(cmdname); + + factor_common_options(); + + if (primary && cmdname) + r = _print_man(cmdname, desfile, secondary); + else if (secondary && cmdname) { + r = 1; + _print_man_secondary(cmdname); + } else if (check) { + r = _check_overlap(); + } + +out_free: + if (stdout_buf) { + fflush(stdout); + setlinebuf(stdout); + free(stdout_buf); + } + + exit(r ? EXIT_SUCCESS: EXIT_FAILURE); +} + +#endif |