mirror of
git://sourceware.org/git/lvm2.git
synced 2025-01-02 01:18:26 +03:00
man-generator: add option to check for repeated command definitions
Compares cmd defs based on two principles for avoiding repeated commands (where a given command could match more than one cmd def): . a cmd def should be a unique combination of required option args and position args . avoid adding optional options to a cmd def that if used would make the command match a different cmd def FIXME: record when repeated cmd defs are found so we can avoid reporting them twice, e.g. once for A vs B and second time for B vs A.
This commit is contained in:
parent
a616abba03
commit
7b77226df2
223
tools/command.c
223
tools/command.c
@ -3654,6 +3654,211 @@ static void _print_man_secondary(char *name)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
#define STDOUT_BUF_SIZE (MAX_MAN_DESC + 4 * 1024)
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
@ -3664,12 +3869,14 @@ int main(int argc, char *argv[])
|
|||||||
char *stdout_buf;
|
char *stdout_buf;
|
||||||
int primary = 0;
|
int primary = 0;
|
||||||
int secondary = 0;
|
int secondary = 0;
|
||||||
|
int check = 0;
|
||||||
int r = 0;
|
int r = 0;
|
||||||
size_t sz = STDOUT_BUF_SIZE;
|
size_t sz = STDOUT_BUF_SIZE;
|
||||||
|
|
||||||
static struct option long_options[] = {
|
static struct option long_options[] = {
|
||||||
{"primary", no_argument, 0, 'p' },
|
{"primary", no_argument, 0, 'p' },
|
||||||
{"secondary", no_argument, 0, 's' },
|
{"secondary", no_argument, 0, 's' },
|
||||||
|
{"check", no_argument, 0, 'c' },
|
||||||
{0, 0, 0, 0 }
|
{0, 0, 0, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -3684,7 +3891,7 @@ int main(int argc, char *argv[])
|
|||||||
int c;
|
int c;
|
||||||
int option_index = 0;
|
int option_index = 0;
|
||||||
|
|
||||||
c = getopt_long(argc, argv, "ps", long_options, &option_index);
|
c = getopt_long(argc, argv, "psc", long_options, &option_index);
|
||||||
if (c == -1)
|
if (c == -1)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -3697,11 +3904,14 @@ int main(int argc, char *argv[])
|
|||||||
case 's':
|
case 's':
|
||||||
secondary = 1;
|
secondary = 1;
|
||||||
break;
|
break;
|
||||||
|
case 'c':
|
||||||
|
check = 1;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!primary && !secondary) {
|
if (!primary && !secondary && !check) {
|
||||||
log_error("Usage: %s --primary|--secondary <command> [/path/to/description-file].", argv[0]);
|
log_error("Usage: %s --primary|--secondary|--check <command> [/path/to/description-file].", argv[0]);
|
||||||
goto out_free;
|
goto out_free;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3710,7 +3920,7 @@ int main(int argc, char *argv[])
|
|||||||
log_error("Out of memory.");
|
log_error("Out of memory.");
|
||||||
goto out_free;
|
goto out_free;
|
||||||
}
|
}
|
||||||
} else {
|
} else if (!check) {
|
||||||
log_error("Missing command name.");
|
log_error("Missing command name.");
|
||||||
goto out_free;
|
goto out_free;
|
||||||
}
|
}
|
||||||
@ -3720,7 +3930,8 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
define_commands(&cmdtool, NULL);
|
define_commands(&cmdtool, NULL);
|
||||||
|
|
||||||
configure_command_option_values(cmdname);
|
if (!check)
|
||||||
|
configure_command_option_values(cmdname);
|
||||||
|
|
||||||
factor_common_options();
|
factor_common_options();
|
||||||
|
|
||||||
@ -3729,6 +3940,8 @@ int main(int argc, char *argv[])
|
|||||||
else if (secondary) {
|
else if (secondary) {
|
||||||
r = 1;
|
r = 1;
|
||||||
_print_man_secondary(cmdname);
|
_print_man_secondary(cmdname);
|
||||||
|
} else if (check) {
|
||||||
|
r = _check_overlap();
|
||||||
}
|
}
|
||||||
|
|
||||||
out_free:
|
out_free:
|
||||||
|
Loading…
Reference in New Issue
Block a user