aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnthony Liguori <anthony@codemonkey.ws>2013-08-30 12:26:04 -0500
committerAnthony Liguori <anthony@codemonkey.ws>2013-08-30 12:26:04 -0500
commit4ff78e0dbcd5c795962567fdc1b31e9e03c55b07 (patch)
treec1fbee238de5abd53a619bdda62ed90aa7a27f51
parentb95fdc0e99e9b5c98bb8e2aee9eaffe160f1031b (diff)
parent7ca0e061044615e39eab2b22b8fc2791a4d77c34 (diff)
Merge remote-tracking branch 'luiz/queue/qmp' into staging
# By Wenchao Xia (15) and Stefan Weil (1) # Via Luiz Capitulino * luiz/queue/qmp: monitor: improve auto complete of "help" for single command in sub group monitor: allow "help" show message for single command in sub group monitor: support sub command in auto completion monitor: refine monitor_find_completion() monitor: support sub command in help monitor: refine parse_cmdline() monitor: code move for parse_cmdline() monitor: avoid direct use of global variable *mon_cmds monitor: split off monitor_data_init() monitor: call sortcmdlist() only one time monitor: avoid use of global *cur_mon in readline_completion() monitor: avoid use of global *cur_mon in monitor_find_completion() monitor: avoid use of global *cur_mon in block_completion_it() monitor: avoid use of global *cur_mon in file_completion() monitor: avoid use of global *cur_mon in cmd_completion() monitor: Add missing attributes to local function Message-id: 1377865357-6742-1-git-send-email-lcapitulino@redhat.com
-rw-r--r--hmp-commands.hx2
-rw-r--r--include/monitor/readline.h3
-rw-r--r--monitor.c462
-rw-r--r--readline.c5
4 files changed, 305 insertions, 167 deletions
diff --git a/hmp-commands.hx b/hmp-commands.hx
index 628807f684..65b7f6076c 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -11,7 +11,7 @@ ETEXI
{
.name = "help|?",
- .args_type = "name:s?",
+ .args_type = "name:S?",
.params = "[cmd]",
.help = "show the help",
.mhandler.cmd = do_help_cmd,
diff --git a/include/monitor/readline.h b/include/monitor/readline.h
index fc9806ecf1..0faf6e1db7 100644
--- a/include/monitor/readline.h
+++ b/include/monitor/readline.h
@@ -8,7 +8,8 @@
#define READLINE_MAX_COMPLETIONS 256
typedef void ReadLineFunc(Monitor *mon, const char *str, void *opaque);
-typedef void ReadLineCompletionFunc(const char *cmdline);
+typedef void ReadLineCompletionFunc(Monitor *mon,
+ const char *cmdline);
typedef struct ReadLineState {
char cmd_buf[READLINE_CMD_BUF_SIZE + 1];
diff --git a/monitor.c b/monitor.c
index ee9744cfb6..0aeaf6c56b 100644
--- a/monitor.c
+++ b/monitor.c
@@ -83,6 +83,7 @@
* 'F' filename
* 'B' block device name
* 's' string (accept optional quote)
+ * 'S' it just appends the rest of the string (accept optional quote)
* 'O' option string of the form NAME=VALUE,...
* parsed according to QemuOptsList given by its name
* Example: 'device:O' uses qemu_device_opts.
@@ -195,6 +196,7 @@ struct Monitor {
CPUState *mon_cpu;
BlockDriverCompletionFunc *password_completion_cb;
void *password_opaque;
+ mon_cmd_t *cmd_table;
QError *error;
QLIST_HEAD(,mon_fd_t) fds;
QLIST_ENTRY(Monitor) entry;
@@ -683,14 +685,26 @@ static int do_qmp_capabilities(Monitor *mon, const QDict *params,
static void handle_user_command(Monitor *mon, const char *cmdline);
+static void monitor_data_init(Monitor *mon)
+{
+ memset(mon, 0, sizeof(Monitor));
+ mon->outbuf = qstring_new();
+ /* Use *mon_cmds by default. */
+ mon->cmd_table = mon_cmds;
+}
+
+static void monitor_data_destroy(Monitor *mon)
+{
+ QDECREF(mon->outbuf);
+}
+
char *qmp_human_monitor_command(const char *command_line, bool has_cpu_index,
int64_t cpu_index, Error **errp)
{
char *output = NULL;
Monitor *old_mon, hmp;
- memset(&hmp, 0, sizeof(hmp));
- hmp.outbuf = qstring_new();
+ monitor_data_init(&hmp);
hmp.skip_flush = true;
old_mon = cur_mon;
@@ -716,7 +730,7 @@ char *qmp_human_monitor_command(const char *command_line, bool has_cpu_index,
}
out:
- QDECREF(hmp.outbuf);
+ monitor_data_destroy(&hmp);
return output;
}
@@ -740,33 +754,202 @@ static int compare_cmd(const char *name, const char *list)
return 0;
}
+static int get_str(char *buf, int buf_size, const char **pp)
+{
+ const char *p;
+ char *q;
+ int c;
+
+ q = buf;
+ p = *pp;
+ while (qemu_isspace(*p)) {
+ p++;
+ }
+ if (*p == '\0') {
+ fail:
+ *q = '\0';
+ *pp = p;
+ return -1;
+ }
+ if (*p == '\"') {
+ p++;
+ while (*p != '\0' && *p != '\"') {
+ if (*p == '\\') {
+ p++;
+ c = *p++;
+ switch (c) {
+ case 'n':
+ c = '\n';
+ break;
+ case 'r':
+ c = '\r';
+ break;
+ case '\\':
+ case '\'':
+ case '\"':
+ break;
+ default:
+ qemu_printf("unsupported escape code: '\\%c'\n", c);
+ goto fail;
+ }
+ if ((q - buf) < buf_size - 1) {
+ *q++ = c;
+ }
+ } else {
+ if ((q - buf) < buf_size - 1) {
+ *q++ = *p;
+ }
+ p++;
+ }
+ }
+ if (*p != '\"') {
+ qemu_printf("unterminated string\n");
+ goto fail;
+ }
+ p++;
+ } else {
+ while (*p != '\0' && !qemu_isspace(*p)) {
+ if ((q - buf) < buf_size - 1) {
+ *q++ = *p;
+ }
+ p++;
+ }
+ }
+ *q = '\0';
+ *pp = p;
+ return 0;
+}
+
+#define MAX_ARGS 16
+
+static void free_cmdline_args(char **args, int nb_args)
+{
+ int i;
+
+ assert(nb_args <= MAX_ARGS);
+
+ for (i = 0; i < nb_args; i++) {
+ g_free(args[i]);
+ }
+
+}
+
+/*
+ * Parse the command line to get valid args.
+ * @cmdline: command line to be parsed.
+ * @pnb_args: location to store the number of args, must NOT be NULL.
+ * @args: location to store the args, which should be freed by caller, must
+ * NOT be NULL.
+ *
+ * Returns 0 on success, negative on failure.
+ *
+ * NOTE: this parser is an approximate form of the real command parser. Number
+ * of args have a limit of MAX_ARGS. If cmdline contains more, it will
+ * return with failure.
+ */
+static int parse_cmdline(const char *cmdline,
+ int *pnb_args, char **args)
+{
+ const char *p;
+ int nb_args, ret;
+ char buf[1024];
+
+ p = cmdline;
+ nb_args = 0;
+ for (;;) {
+ while (qemu_isspace(*p)) {
+ p++;
+ }
+ if (*p == '\0') {
+ break;
+ }
+ if (nb_args >= MAX_ARGS) {
+ goto fail;
+ }
+ ret = get_str(buf, sizeof(buf), &p);
+ if (ret < 0) {
+ goto fail;
+ }
+ args[nb_args] = g_strdup(buf);
+ nb_args++;
+ }
+ *pnb_args = nb_args;
+ return 0;
+
+ fail:
+ free_cmdline_args(args, nb_args);
+ return -1;
+}
+
+static void help_cmd_dump_one(Monitor *mon,
+ const mon_cmd_t *cmd,
+ char **prefix_args,
+ int prefix_args_nb)
+{
+ int i;
+
+ for (i = 0; i < prefix_args_nb; i++) {
+ monitor_printf(mon, "%s ", prefix_args[i]);
+ }
+ monitor_printf(mon, "%s %s -- %s\n", cmd->name, cmd->params, cmd->help);
+}
+
+/* @args[@arg_index] is the valid command need to find in @cmds */
static void help_cmd_dump(Monitor *mon, const mon_cmd_t *cmds,
- const char *prefix, const char *name)
+ char **args, int nb_args, int arg_index)
{
const mon_cmd_t *cmd;
- for(cmd = cmds; cmd->name != NULL; cmd++) {
- if (!name || !strcmp(name, cmd->name))
- monitor_printf(mon, "%s%s %s -- %s\n", prefix, cmd->name,
- cmd->params, cmd->help);
+ /* No valid arg need to compare with, dump all in *cmds */
+ if (arg_index >= nb_args) {
+ for (cmd = cmds; cmd->name != NULL; cmd++) {
+ help_cmd_dump_one(mon, cmd, args, arg_index);
+ }
+ return;
+ }
+
+ /* Find one entry to dump */
+ for (cmd = cmds; cmd->name != NULL; cmd++) {
+ if (compare_cmd(args[arg_index], cmd->name)) {
+ if (cmd->sub_table) {
+ /* continue with next arg */
+ help_cmd_dump(mon, cmd->sub_table,
+ args, nb_args, arg_index + 1);
+ } else {
+ help_cmd_dump_one(mon, cmd, args, arg_index);
+ }
+ break;
+ }
}
}
static void help_cmd(Monitor *mon, const char *name)
{
- if (name && !strcmp(name, "info")) {
- help_cmd_dump(mon, info_cmds, "info ", NULL);
- } else {
- help_cmd_dump(mon, mon_cmds, "", name);
- if (name && !strcmp(name, "log")) {
+ char *args[MAX_ARGS];
+ int nb_args = 0;
+
+ /* 1. parse user input */
+ if (name) {
+ /* special case for log, directly dump and return */
+ if (!strcmp(name, "log")) {
const QEMULogItem *item;
monitor_printf(mon, "Log items (comma separated):\n");
monitor_printf(mon, "%-10s %s\n", "none", "remove all logs");
for (item = qemu_log_items; item->mask != 0; item++) {
monitor_printf(mon, "%-10s %s\n", item->name, item->help);
}
+ return;
+ }
+
+ if (parse_cmdline(name, &nb_args, args) < 0) {
+ return;
}
}
+
+ /* 2. dump the contents according to parsed args */
+ help_cmd_dump(mon, mon->cmd_table, args, nb_args, 0);
+
+ free_cmdline_args(args, nb_args);
}
static void do_help_cmd(Monitor *mon, const QDict *qdict)
@@ -3171,7 +3354,8 @@ static const MonitorDef monitor_defs[] = {
{ NULL },
};
-static void expr_error(Monitor *mon, const char *fmt, ...)
+static void GCC_FMT_ATTR(2, 3) QEMU_NORETURN
+expr_error(Monitor *mon, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
@@ -3420,71 +3604,6 @@ static int get_double(Monitor *mon, double *pval, const char **pp)
return 0;
}
-static int get_str(char *buf, int buf_size, const char **pp)
-{
- const char *p;
- char *q;
- int c;
-
- q = buf;
- p = *pp;
- while (qemu_isspace(*p))
- p++;
- if (*p == '\0') {
- fail:
- *q = '\0';
- *pp = p;
- return -1;
- }
- if (*p == '\"') {
- p++;
- while (*p != '\0' && *p != '\"') {
- if (*p == '\\') {
- p++;
- c = *p++;
- switch(c) {
- case 'n':
- c = '\n';
- break;
- case 'r':
- c = '\r';
- break;
- case '\\':
- case '\'':
- case '\"':
- break;
- default:
- qemu_printf("unsupported escape code: '\\%c'\n", c);
- goto fail;
- }
- if ((q - buf) < buf_size - 1) {
- *q++ = c;
- }
- } else {
- if ((q - buf) < buf_size - 1) {
- *q++ = *p;
- }
- p++;
- }
- }
- if (*p != '\"') {
- qemu_printf("unterminated string\n");
- goto fail;
- }
- p++;
- } else {
- while (*p != '\0' && !qemu_isspace(*p)) {
- if ((q - buf) < buf_size - 1) {
- *q++ = *p;
- }
- p++;
- }
- }
- *q = '\0';
- *pp = p;
- return 0;
-}
-
/*
* Store the command-name in cmdname, and return a pointer to
* the remaining of the command string.
@@ -3541,8 +3660,6 @@ static char *key_get_info(const char *type, char **key)
static int default_fmt_format = 'x';
static int default_fmt_size = 4;
-#define MAX_ARGS 16
-
static int is_valid_option(const char *c, const char *typestr)
{
char option[3];
@@ -3931,6 +4048,31 @@ static const mon_cmd_t *monitor_parse_command(Monitor *mon,
}
}
break;
+ case 'S':
+ {
+ /* package all remaining string */
+ int len;
+
+ while (qemu_isspace(*p)) {
+ p++;
+ }
+ if (*typestr == '?') {
+ typestr++;
+ if (*p == '\0') {
+ /* no remaining string: NULL argument */
+ break;
+ }
+ }
+ len = strlen(p);
+ if (len <= 0) {
+ monitor_printf(mon, "%s: string expected\n",
+ cmdname);
+ break;
+ }
+ qdict_put(qdict, key, qstring_from_str(p));
+ p += len;
+ }
+ break;
default:
bad_type:
monitor_printf(mon, "%s: unknown type '%c'\n", cmdname, c);
@@ -3984,7 +4126,7 @@ static void handle_user_command(Monitor *mon, const char *cmdline)
qdict = qdict_new();
- cmd = monitor_parse_command(mon, cmdline, 0, mon_cmds, qdict);
+ cmd = monitor_parse_command(mon, cmdline, 0, mon->cmd_table, qdict);
if (!cmd)
goto out;
@@ -4008,7 +4150,7 @@ out:
QDECREF(qdict);
}
-static void cmd_completion(const char *name, const char *list)
+static void cmd_completion(Monitor *mon, const char *name, const char *list)
{
const char *p, *pstart;
char cmd[128];
@@ -4026,7 +4168,7 @@ static void cmd_completion(const char *name, const char *list)
memcpy(cmd, pstart, len);
cmd[len] = '\0';
if (name[0] == '\0' || !strncmp(name, cmd, strlen(name))) {
- readline_add_completion(cur_mon->rs, cmd);
+ readline_add_completion(mon->rs, cmd);
}
if (*p == '\0')
break;
@@ -4034,7 +4176,7 @@ static void cmd_completion(const char *name, const char *list)
}
}
-static void file_completion(const char *input)
+static void file_completion(Monitor *mon, const char *input)
{
DIR *ffs;
struct dirent *d;
@@ -4057,7 +4199,7 @@ static void file_completion(const char *input)
pstrcpy(file_prefix, sizeof(file_prefix), p + 1);
}
#ifdef DEBUG_COMPLETION
- monitor_printf(cur_mon, "input='%s' path='%s' prefix='%s'\n",
+ monitor_printf(mon, "input='%s' path='%s' prefix='%s'\n",
input, path, file_prefix);
#endif
ffs = opendir(path);
@@ -4084,47 +4226,28 @@ static void file_completion(const char *input)
if (stat(file, &sb) == 0 && S_ISDIR(sb.st_mode)) {
pstrcat(file, sizeof(file), "/");
}
- readline_add_completion(cur_mon->rs, file);
+ readline_add_completion(mon->rs, file);
}
}
closedir(ffs);
}
+typedef struct MonitorBlockComplete {
+ Monitor *mon;
+ const char *input;
+} MonitorBlockComplete;
+
static void block_completion_it(void *opaque, BlockDriverState *bs)
{
const char *name = bdrv_get_device_name(bs);
- const char *input = opaque;
+ MonitorBlockComplete *mbc = opaque;
+ Monitor *mon = mbc->mon;
+ const char *input = mbc->input;
if (input[0] == '\0' ||
!strncmp(name, (char *)input, strlen(input))) {
- readline_add_completion(cur_mon->rs, name);
- }
-}
-
-/* NOTE: this parser is an approximate form of the real command parser */
-static void parse_cmdline(const char *cmdline,
- int *pnb_args, char **args)
-{
- const char *p;
- int nb_args, ret;
- char buf[1024];
-
- p = cmdline;
- nb_args = 0;
- for(;;) {
- while (qemu_isspace(*p))
- p++;
- if (*p == '\0')
- break;
- if (nb_args >= MAX_ARGS)
- break;
- ret = get_str(buf, sizeof(buf), &p);
- args[nb_args] = g_strdup(buf);
- nb_args++;
- if (ret < 0)
- break;
+ readline_add_completion(mon->rs, name);
}
- *pnb_args = nb_args;
}
static const char *next_arg_type(const char *typestr)
@@ -4133,49 +4256,42 @@ static const char *next_arg_type(const char *typestr)
return (p != NULL ? ++p : typestr);
}
-static void monitor_find_completion(const char *cmdline)
+static void monitor_find_completion_by_table(Monitor *mon,
+ const mon_cmd_t *cmd_table,
+ char **args,
+ int nb_args)
{
const char *cmdname;
- char *args[MAX_ARGS];
- int nb_args, i, len;
+ int i;
const char *ptype, *str;
const mon_cmd_t *cmd;
+ MonitorBlockComplete mbs;
- parse_cmdline(cmdline, &nb_args, args);
-#ifdef DEBUG_COMPLETION
- for(i = 0; i < nb_args; i++) {
- monitor_printf(cur_mon, "arg%d = '%s'\n", i, (char *)args[i]);
- }
-#endif
-
- /* if the line ends with a space, it means we want to complete the
- next arg */
- len = strlen(cmdline);
- if (len > 0 && qemu_isspace(cmdline[len - 1])) {
- if (nb_args >= MAX_ARGS) {
- goto cleanup;
- }
- args[nb_args++] = g_strdup("");
- }
if (nb_args <= 1) {
/* command completion */
if (nb_args == 0)
cmdname = "";
else
cmdname = args[0];
- readline_set_completion_index(cur_mon->rs, strlen(cmdname));
- for(cmd = mon_cmds; cmd->name != NULL; cmd++) {
- cmd_completion(cmdname, cmd->name);
+ readline_set_completion_index(mon->rs, strlen(cmdname));
+ for (cmd = cmd_table; cmd->name != NULL; cmd++) {
+ cmd_completion(mon, cmdname, cmd->name);
}
} else {
/* find the command */
- for (cmd = mon_cmds; cmd->name != NULL; cmd++) {
+ for (cmd = cmd_table; cmd->name != NULL; cmd++) {
if (compare_cmd(args[0], cmd->name)) {
break;
}
}
if (!cmd->name) {
- goto cleanup;
+ return;
+ }
+
+ if (cmd->sub_table) {
+ /* do the job again */
+ return monitor_find_completion_by_table(mon, cmd->sub_table,
+ &args[1], nb_args - 1);
}
ptype = next_arg_type(cmd->args_type);
@@ -4193,45 +4309,68 @@ static void monitor_find_completion(const char *cmdline)
switch(*ptype) {
case 'F':
/* file completion */
- readline_set_completion_index(cur_mon->rs, strlen(str));
- file_completion(str);
+ readline_set_completion_index(mon->rs, strlen(str));
+ file_completion(mon, str);
break;
case 'B':
/* block device name completion */
- readline_set_completion_index(cur_mon->rs, strlen(str));
- bdrv_iterate(block_completion_it, (void *)str);
+ mbs.mon = mon;
+ mbs.input = str;
+ readline_set_completion_index(mon->rs, strlen(str));
+ bdrv_iterate(block_completion_it, &mbs);
break;
case 's':
- /* XXX: more generic ? */
- if (!strcmp(cmd->name, "info")) {
- readline_set_completion_index(cur_mon->rs, strlen(str));
- for(cmd = info_cmds; cmd->name != NULL; cmd++) {
- cmd_completion(str, cmd->name);
- }
- } else if (!strcmp(cmd->name, "sendkey")) {
+ case 'S':
+ if (!strcmp(cmd->name, "sendkey")) {
char *sep = strrchr(str, '-');
if (sep)
str = sep + 1;
- readline_set_completion_index(cur_mon->rs, strlen(str));
+ readline_set_completion_index(mon->rs, strlen(str));
for (i = 0; i < Q_KEY_CODE_MAX; i++) {
- cmd_completion(str, QKeyCode_lookup[i]);
+ cmd_completion(mon, str, QKeyCode_lookup[i]);
}
} else if (!strcmp(cmd->name, "help|?")) {
- readline_set_completion_index(cur_mon->rs, strlen(str));
- for (cmd = mon_cmds; cmd->name != NULL; cmd++) {
- cmd_completion(str, cmd->name);
- }
+ monitor_find_completion_by_table(mon, cmd_table,
+ &args[1], nb_args - 1);
}
break;
default:
break;
}
}
+}
-cleanup:
+static void monitor_find_completion(Monitor *mon,
+ const char *cmdline)
+{
+ char *args[MAX_ARGS];
+ int nb_args, len;
+
+ /* 1. parse the cmdline */
+ if (parse_cmdline(cmdline, &nb_args, args) < 0) {
+ return;
+ }
+#ifdef DEBUG_COMPLETION
for (i = 0; i < nb_args; i++) {
- g_free(args[i]);
+ monitor_printf(mon, "arg%d = '%s'\n", i, args[i]);
+ }
+#endif
+
+ /* if the line ends with a space, it means we want to complete the
+ next arg */
+ len = strlen(cmdline);
+ if (len > 0 && qemu_isspace(cmdline[len - 1])) {
+ if (nb_args >= MAX_ARGS) {
+ goto cleanup;
+ }
+ args[nb_args++] = g_strdup("");
}
+
+ /* 2. auto complete according to args */
+ monitor_find_completion_by_table(mon, mon->cmd_table, args, nb_args);
+
+cleanup:
+ free_cmdline_args(args, nb_args);
}
static int monitor_can_read(void *opaque)
@@ -4751,11 +4890,12 @@ void monitor_init(CharDriverState *chr, int flags)
if (is_first_init) {
monitor_protocol_event_init();
+ sortcmdlist();
is_first_init = 0;
}
- mon = g_malloc0(sizeof(*mon));
- mon->outbuf = qstring_new();
+ mon = g_malloc(sizeof(*mon));
+ monitor_data_init(mon);
mon->chr = chr;
mon->flags = flags;
@@ -4780,8 +4920,6 @@ void monitor_init(CharDriverState *chr, int flags)
QLIST_INSERT_HEAD(&mon_list, mon, entry);
if (!default_mon || (flags & MONITOR_IS_DEFAULT))
default_mon = mon;
-
- sortcmdlist();
}
static void bdrv_password_cb(Monitor *mon, const char *password, void *opaque)
diff --git a/readline.c b/readline.c
index 1c0f7ee26b..abf27ddec3 100644
--- a/readline.c
+++ b/readline.c
@@ -276,7 +276,6 @@ void readline_set_completion_index(ReadLineState *rs, int index)
static void readline_completion(ReadLineState *rs)
{
- Monitor *mon = cur_mon;
int len, i, j, max_width, nb_cols, max_prefix;
char *cmdline;
@@ -285,7 +284,7 @@ static void readline_completion(ReadLineState *rs)
cmdline = g_malloc(rs->cmd_buf_index + 1);
memcpy(cmdline, rs->cmd_buf, rs->cmd_buf_index);
cmdline[rs->cmd_buf_index] = '\0';
- rs->completion_finder(cmdline);
+ rs->completion_finder(rs->mon, cmdline);
g_free(cmdline);
/* no completion found */
@@ -300,7 +299,7 @@ static void readline_completion(ReadLineState *rs)
if (len > 0 && rs->completions[0][len - 1] != '/')
readline_insert_char(rs, ' ');
} else {
- monitor_printf(mon, "\n");
+ monitor_printf(rs->mon, "\n");
max_width = 0;
max_prefix = 0;
for(i = 0; i < rs->nb_completions; i++) {