2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-30 13:58:14 +00:00

db-ctl-base: Librarize code in parse_options().

This commit extracts general code from parse_options() and puts it into
db-ctl-base module.

Signed-off-by: Alex Wang <alexw@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
This commit is contained in:
Alex Wang 2015-06-11 08:19:15 -07:00
parent 07ff77ccb8
commit 51a73ff06e
3 changed files with 231 additions and 214 deletions

View File

@ -49,6 +49,114 @@ struct ovsdb_idl_txn *the_idl_txn;
static struct shash all_commands = SHASH_INITIALIZER(&all_commands); static struct shash all_commands = SHASH_INITIALIZER(&all_commands);
static struct option *
find_option(const char *name, struct option *options, size_t n_options)
{
size_t i;
for (i = 0; i < n_options; i++) {
if (!strcmp(options[i].name, name)) {
return &options[i];
}
}
return NULL;
}
static struct option *
add_option(struct option **optionsp, size_t *n_optionsp,
size_t *allocated_optionsp)
{
if (*n_optionsp >= *allocated_optionsp) {
*optionsp = x2nrealloc(*optionsp, allocated_optionsp,
sizeof **optionsp);
}
return &(*optionsp)[(*n_optionsp)++];
}
/* Converts the command arguments into format that can be parsed by
* bash completion script.
*
* Therein, arguments will be attached with following prefixes:
*
* !argument :: The argument is required
* ?argument :: The argument is optional
* *argument :: The argument may appear any number (0 or more) times
* +argument :: The argument may appear one or more times
*
*/
static void
print_command_arguments(const struct ctl_command_syntax *command)
{
/*
* The argument string is parsed in reverse. We use a stack 'oew_stack' to
* keep track of nested optionals. Whenever a ']' is encountered, we push
* a bit to 'oew_stack'. The bit is set to 1 if the ']' is not nested.
* Subsequently, we pop an entry everytime '[' is met.
*
* We use 'whole_word_is_optional' value to decide whether or not a ! or +
* should be added on encountering a space: if the optional surrounds the
* whole word then it shouldn't be, but if it is only a part of the word
* (i.e. [key=]value), it should be.
*/
uint32_t oew_stack = 0;
const char *arguments = command->arguments;
int length = strlen(arguments);
if (!length) {
return;
}
/* Output buffer, written backward from end. */
char *output = xmalloc(2 * length);
char *outp = output + 2 * length;
*--outp = '\0';
bool in_repeated = false;
bool whole_word_is_optional = false;
for (const char *inp = arguments + length; inp > arguments; ) {
switch (*--inp) {
case ']':
oew_stack <<= 1;
if (inp[1] == '\0' || inp[1] == ' ' || inp[1] == '.') {
oew_stack |= 1;
}
break;
case '[':
/* Checks if the whole word is optional, and sets the
* 'whole_word_is_optional' accordingly. */
if ((inp == arguments || inp[-1] == ' ') && oew_stack & 1) {
*--outp = in_repeated ? '*' : '?';
whole_word_is_optional = true;
} else {
*--outp = '?';
whole_word_is_optional = false;
}
oew_stack >>= 1;
break;
case ' ':
if (!whole_word_is_optional) {
*--outp = in_repeated ? '+' : '!';
}
*--outp = ' ';
in_repeated = false;
whole_word_is_optional = false;
break;
case '.':
in_repeated = true;
break;
default:
*--outp = *inp;
break;
}
}
if (arguments[0] != '[' && outp != output + 2 * length - 1) {
*--outp = in_repeated ? '+' : '!';
}
printf("%s", outp);
free(output);
}
static void static void
die_if_error(char *error) die_if_error(char *error)
{ {
@ -1388,6 +1496,63 @@ parse_command(int argc, char *argv[], struct shash *local_options,
} }
/* Given pointer to dynamic array 'options_p', array's current size
* 'allocated_options_p' and number of added options 'n_options_p',
* adds all command options to the array. Enlarges the array if
* necessary. */
void
ctl_add_cmd_options(struct option **options_p, size_t *n_options_p,
size_t *allocated_options_p, int opt_val)
{
struct option *o;
const struct shash_node *node;
size_t n_existing_options = *n_options_p;
SHASH_FOR_EACH (node, ctl_get_all_commands()) {
const struct ctl_command_syntax *p = node->data;
if (p->options[0]) {
char *save_ptr = NULL;
char *name;
char *s;
s = xstrdup(p->options);
for (name = strtok_r(s, ",", &save_ptr); name != NULL;
name = strtok_r(NULL, ",", &save_ptr)) {
char *equals;
int has_arg;
ovs_assert(name[0] == '-' && name[1] == '-' && name[2]);
name += 2;
equals = strchr(name, '=');
if (equals) {
has_arg = required_argument;
*equals = '\0';
} else {
has_arg = no_argument;
}
o = find_option(name, *options_p, *n_options_p);
if (o) {
ovs_assert(o - *options_p >= n_existing_options);
ovs_assert(o->has_arg == has_arg);
} else {
o = add_option(options_p, n_options_p, allocated_options_p);
o->name = xstrdup(name);
o->has_arg = has_arg;
o->flag = NULL;
o->val = opt_val;
}
}
free(s);
}
}
o = add_option(options_p, n_options_p, allocated_options_p);
memset(o, 0, sizeof *o);
}
/* Parses command-line input for commands. */ /* Parses command-line input for commands. */
struct ctl_command * struct ctl_command *
ctl_parse_commands(int argc, char *argv[], struct shash *local_options, ctl_parse_commands(int argc, char *argv[], struct shash *local_options,
@ -1427,6 +1592,61 @@ ctl_parse_commands(int argc, char *argv[], struct shash *local_options,
return commands; return commands;
} }
/* Prints all registered commands. */
void
ctl_print_commands(void)
{
const struct shash_node *node;
SHASH_FOR_EACH (node, ctl_get_all_commands()) {
const struct ctl_command_syntax *p = node->data;
char *options = xstrdup(p->options);
char *options_begin = options;
char *item;
for (item = strsep(&options, ","); item != NULL;
item = strsep(&options, ",")) {
if (item[0] != '\0') {
printf("[%s] ", item);
}
}
printf(",%s,", p->name);
print_command_arguments(p);
printf("\n");
free(options_begin);
}
exit(EXIT_SUCCESS);
}
/* Given array of options 'options', prints them. */
void
ctl_print_options(const struct option *options)
{
for (; options->name; options++) {
const struct option *o = options;
printf("--%s%s\n", o->name, o->has_arg ? "=ARG" : "");
if (o->flag == NULL && o->val > 0 && o->val <= UCHAR_MAX) {
printf("-%c%s\n", o->val, o->has_arg ? " ARG" : "");
}
}
exit(EXIT_SUCCESS);
}
/* Returns the default local database path. */
char *
ctl_default_db(void)
{
static char *def;
if (!def) {
def = xasprintf("unix:%s/db.sock", ovs_rundir());
}
return def;
}
/* Returns true if it looks like this set of arguments might modify the /* Returns true if it looks like this set of arguments might modify the
* database, otherwise false. (Not very smart, so it's prone to false * database, otherwise false. (Not very smart, so it's prone to false
* positives.) */ * positives.) */

View File

@ -55,6 +55,7 @@ extern struct ovsdb_idl *the_idl;
extern struct ovsdb_idl_txn *the_idl_txn; extern struct ovsdb_idl_txn *the_idl_txn;
void ctl_init(void); void ctl_init(void);
char *ctl_default_db(void);
OVS_NO_RETURN void ctl_exit(int status); OVS_NO_RETURN void ctl_exit(int status);
OVS_NO_RETURN void ctl_fatal(const char *, ...) OVS_PRINTF_FORMAT(1, 2); OVS_NO_RETURN void ctl_fatal(const char *, ...) OVS_PRINTF_FORMAT(1, 2);
@ -145,6 +146,10 @@ struct ctl_command {
bool ctl_might_write_to_db(char **argv); bool ctl_might_write_to_db(char **argv);
const char *ctl_get_db_cmd_usage(void); const char *ctl_get_db_cmd_usage(void);
void ctl_print_commands(void);
void ctl_print_options(const struct option *);
void ctl_add_cmd_options(struct option **, size_t *n_options_p,
size_t *allocated_options_p, int opt_val);
void ctl_register_commands(const struct ctl_command_syntax *); void ctl_register_commands(const struct ctl_command_syntax *);
const struct shash *ctl_get_all_commands(void); const struct shash *ctl_get_all_commands(void);
struct ctl_command *ctl_parse_commands(int argc, char *argv[], struct ctl_command *ctl_parse_commands(int argc, char *argv[],

View File

@ -31,7 +31,6 @@
#include "command-line.h" #include "command-line.h"
#include "compiler.h" #include "compiler.h"
#include "dirs.h"
#include "dynamic-string.h" #include "dynamic-string.h"
#include "fatal-signal.h" #include "fatal-signal.h"
#include "hash.h" #include "hash.h"
@ -85,10 +84,7 @@ static bool retry;
static struct table_style table_style = TABLE_STYLE_DEFAULT; static struct table_style table_style = TABLE_STYLE_DEFAULT;
static void vsctl_cmd_init(void); static void vsctl_cmd_init(void);
static char *default_db(void);
OVS_NO_RETURN static void usage(void); OVS_NO_RETURN static void usage(void);
OVS_NO_RETURN static void print_vsctl_commands(void);
OVS_NO_RETURN static void print_vsctl_options(const struct option *options);
static void parse_options(int argc, char *argv[], struct shash *local_options); static void parse_options(int argc, char *argv[], struct shash *local_options);
static void run_prerequisites(struct ctl_command[], size_t n_commands, static void run_prerequisites(struct ctl_command[], size_t n_commands,
struct ovsdb_idl *); struct ovsdb_idl *);
@ -188,30 +184,6 @@ main(int argc, char *argv[])
} }
} }
static struct option *
find_option(const char *name, struct option *options, size_t n_options)
{
size_t i;
for (i = 0; i < n_options; i++) {
if (!strcmp(options[i].name, name)) {
return &options[i];
}
}
return NULL;
}
static struct option *
add_option(struct option **optionsp, size_t *n_optionsp,
size_t *allocated_optionsp)
{
if (*n_optionsp >= *allocated_optionsp) {
*optionsp = x2nrealloc(*optionsp, allocated_optionsp,
sizeof **optionsp);
}
return &(*optionsp)[(*n_optionsp)++];
}
static void static void
parse_options(int argc, char *argv[], struct shash *local_options) parse_options(int argc, char *argv[], struct shash *local_options)
{ {
@ -250,8 +222,7 @@ parse_options(int argc, char *argv[], struct shash *local_options)
const int n_global_long_options = ARRAY_SIZE(global_long_options) - 1; const int n_global_long_options = ARRAY_SIZE(global_long_options) - 1;
char *tmp, *short_options; char *tmp, *short_options;
const struct shash_node *node; struct option *options;
struct option *options, *o;
size_t allocated_options; size_t allocated_options;
size_t n_options; size_t n_options;
size_t i; size_t i;
@ -267,50 +238,7 @@ parse_options(int argc, char *argv[], struct shash *local_options)
options = xmemdup(global_long_options, sizeof global_long_options); options = xmemdup(global_long_options, sizeof global_long_options);
allocated_options = ARRAY_SIZE(global_long_options); allocated_options = ARRAY_SIZE(global_long_options);
n_options = n_global_long_options; n_options = n_global_long_options;
SHASH_FOR_EACH (node, ctl_get_all_commands()) { ctl_add_cmd_options(&options, &n_options, &allocated_options, OPT_LOCAL);
const struct ctl_command_syntax *p = node->data;
if (p->options[0]) {
char *save_ptr = NULL;
char *name;
char *s;
s = xstrdup(p->options);
for (name = strtok_r(s, ",", &save_ptr); name != NULL;
name = strtok_r(NULL, ",", &save_ptr)) {
char *equals;
int has_arg;
ovs_assert(name[0] == '-' && name[1] == '-' && name[2]);
name += 2;
equals = strchr(name, '=');
if (equals) {
has_arg = required_argument;
*equals = '\0';
} else {
has_arg = no_argument;
}
o = find_option(name, options, n_options);
if (o) {
ovs_assert(o - options >= n_global_long_options);
ovs_assert(o->has_arg == has_arg);
} else {
o = add_option(&options, &n_options, &allocated_options);
o->name = xstrdup(name);
o->has_arg = has_arg;
o->flag = NULL;
o->val = OPT_LOCAL;
}
}
free(s);
}
}
o = add_option(&options, &n_options, &allocated_options);
memset(o, 0, sizeof *o);
table_style.format = TF_LIST; table_style.format = TF_LIST;
for (;;) { for (;;) {
@ -357,10 +285,10 @@ parse_options(int argc, char *argv[], struct shash *local_options)
usage(); usage();
case OPT_COMMANDS: case OPT_COMMANDS:
print_vsctl_commands(); ctl_print_commands();
case OPT_OPTIONS: case OPT_OPTIONS:
print_vsctl_options(global_long_options); ctl_print_options(global_long_options);
case 'V': case 'V':
ovs_print_version(0, 0); ovs_print_version(0, 0);
@ -398,7 +326,7 @@ parse_options(int argc, char *argv[], struct shash *local_options)
free(short_options); free(short_options);
if (!db) { if (!db) {
db = default_db(); db = ctl_default_db();
} }
for (i = n_global_long_options; options[i].name; i++) { for (i = n_global_long_options; options[i].name; i++) {
@ -479,7 +407,7 @@ Options:\n\
-t, --timeout=SECS wait at most SECS seconds for ovs-vswitchd\n\ -t, --timeout=SECS wait at most SECS seconds for ovs-vswitchd\n\
--dry-run do not commit changes to database\n\ --dry-run do not commit changes to database\n\
--oneline print exactly one line of output per command\n", --oneline print exactly one line of output per command\n",
program_name, program_name, ctl_get_db_cmd_usage(), default_db()); program_name, program_name, ctl_get_db_cmd_usage(), ctl_default_db());
vlog_usage(); vlog_usage();
printf("\ printf("\
--no-syslog equivalent to --verbose=vsctl:syslog:warn\n"); --no-syslog equivalent to --verbose=vsctl:syslog:warn\n");
@ -491,142 +419,6 @@ Other options:\n\
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} }
/* Converts the command arguments into format that can be parsed by
* bash completion script.
*
* Therein, arguments will be attached with following prefixes:
*
* !argument :: The argument is required
* ?argument :: The argument is optional
* *argument :: The argument may appear any number (0 or more) times
* +argument :: The argument may appear one or more times
*
*/
static void
print_command_arguments(const struct ctl_command_syntax *command)
{
/*
* The argument string is parsed in reverse. We use a stack 'oew_stack' to
* keep track of nested optionals. Whenever a ']' is encountered, we push
* a bit to 'oew_stack'. The bit is set to 1 if the ']' is not nested.
* Subsequently, we pop an entry everytime '[' is met.
*
* We use 'whole_word_is_optional' value to decide whether or not a ! or +
* should be added on encountering a space: if the optional surrounds the
* whole word then it shouldn't be, but if it is only a part of the word
* (i.e. [key=]value), it should be.
*/
uint32_t oew_stack = 0;
const char *arguments = command->arguments;
int length = strlen(arguments);
if (!length) {
return;
}
/* Output buffer, written backward from end. */
char *output = xmalloc(2 * length);
char *outp = output + 2 * length;
*--outp = '\0';
bool in_repeated = false;
bool whole_word_is_optional = false;
for (const char *inp = arguments + length; inp > arguments; ) {
switch (*--inp) {
case ']':
oew_stack <<= 1;
if (inp[1] == '\0' || inp[1] == ' ' || inp[1] == '.') {
oew_stack |= 1;
}
break;
case '[':
/* Checks if the whole word is optional, and sets the
* 'whole_word_is_optional' accordingly. */
if ((inp == arguments || inp[-1] == ' ') && oew_stack & 1) {
*--outp = in_repeated ? '*' : '?';
whole_word_is_optional = true;
} else {
*--outp = '?';
whole_word_is_optional = false;
}
oew_stack >>= 1;
break;
case ' ':
if (!whole_word_is_optional) {
*--outp = in_repeated ? '+' : '!';
}
*--outp = ' ';
in_repeated = false;
whole_word_is_optional = false;
break;
case '.':
in_repeated = true;
break;
default:
*--outp = *inp;
break;
}
}
if (arguments[0] != '[' && outp != output + 2 * length - 1) {
*--outp = in_repeated ? '+' : '!';
}
printf("%s", outp);
free(output);
}
static void
print_vsctl_commands(void)
{
const struct shash_node *node;
SHASH_FOR_EACH (node, ctl_get_all_commands()) {
const struct ctl_command_syntax *p = node->data;
char *options = xstrdup(p->options);
char *options_begin = options;
char *item;
for (item = strsep(&options, ","); item != NULL;
item = strsep(&options, ",")) {
if (item[0] != '\0') {
printf("[%s] ", item);
}
}
printf(",%s,", p->name);
print_command_arguments(p);
printf("\n");
free(options_begin);
}
exit(EXIT_SUCCESS);
}
static void
print_vsctl_options(const struct option *options)
{
for (; options->name; options++) {
const struct option *o = options;
printf("--%s%s\n", o->name, o->has_arg ? "=ARG" : "");
if (o->flag == NULL && o->val > 0 && o->val <= UCHAR_MAX) {
printf("-%c%s\n", o->val, o->has_arg ? " ARG" : "");
}
}
exit(EXIT_SUCCESS);
}
static char *
default_db(void)
{
static char *def;
if (!def) {
def = xasprintf("unix:%s/db.sock", ovs_rundir());
}
return def;
}
/* ovs-vsctl specific context. Inherits the 'struct ctl_context' as base. */ /* ovs-vsctl specific context. Inherits the 'struct ctl_context' as base. */
struct vsctl_context { struct vsctl_context {