From 9c3175cc15fbe8d3528375d1389dad40b19b7665 Mon Sep 17 00:00:00 2001 From: Stefan Weil Date: Thu, 22 Aug 2013 21:30:09 +0200 Subject: monitor: Add missing attributes to local function Function expr_error gets a format string and variable arguments like printf. It also never returns. Add the necessary attributes. Signed-off-by: Stefan Weil Signed-off-by: Luiz Capitulino --- monitor.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/monitor.c b/monitor.c index ee9744cfb6..8c23e29d8f 100644 --- a/monitor.c +++ b/monitor.c @@ -3171,7 +3171,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); -- cgit v1.2.1 From cd5c6bba1b75d4faebb58345d2661d5e42600fab Mon Sep 17 00:00:00 2001 From: Wenchao Xia Date: Tue, 27 Aug 2013 20:38:13 +0800 Subject: monitor: avoid use of global *cur_mon in cmd_completion() A new local variable *mon is added in monitor_find_completion() to make compile pass, which will be removed later in conversion patch for monitor_find_completion(). Signed-off-by: Wenchao Xia Reviewed-by: Eric Blake Signed-off-by: Luiz Capitulino --- monitor.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/monitor.c b/monitor.c index 8c23e29d8f..770ab6ecf2 100644 --- a/monitor.c +++ b/monitor.c @@ -4009,7 +4009,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]; @@ -4027,7 +4027,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; @@ -4141,6 +4141,7 @@ static void monitor_find_completion(const char *cmdline) int nb_args, i, len; const char *ptype, *str; const mon_cmd_t *cmd; + Monitor *mon = cur_mon; parse_cmdline(cmdline, &nb_args, args); #ifdef DEBUG_COMPLETION @@ -4166,7 +4167,7 @@ static void monitor_find_completion(const char *cmdline) 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); + cmd_completion(mon, cmdname, cmd->name); } } else { /* find the command */ @@ -4207,7 +4208,7 @@ static void monitor_find_completion(const char *cmdline) 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); + cmd_completion(mon, str, cmd->name); } } else if (!strcmp(cmd->name, "sendkey")) { char *sep = strrchr(str, '-'); @@ -4215,12 +4216,12 @@ static void monitor_find_completion(const char *cmdline) str = sep + 1; readline_set_completion_index(cur_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); + cmd_completion(mon, str, cmd->name); } } break; -- cgit v1.2.1 From cb8f68b104f8d14f0ad856012cac7bd27f183799 Mon Sep 17 00:00:00 2001 From: Wenchao Xia Date: Tue, 27 Aug 2013 20:38:14 +0800 Subject: monitor: avoid use of global *cur_mon in file_completion() Signed-off-by: Wenchao Xia Reviewed-by: Eric Blake Signed-off-by: Luiz Capitulino --- monitor.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/monitor.c b/monitor.c index 770ab6ecf2..3a0b40cfb2 100644 --- a/monitor.c +++ b/monitor.c @@ -4035,7 +4035,7 @@ static void cmd_completion(Monitor *mon, 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; @@ -4058,7 +4058,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); @@ -4085,7 +4085,7 @@ 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); @@ -4196,7 +4196,7 @@ static void monitor_find_completion(const char *cmdline) case 'F': /* file completion */ readline_set_completion_index(cur_mon->rs, strlen(str)); - file_completion(str); + file_completion(mon, str); break; case 'B': /* block device name completion */ -- cgit v1.2.1 From 599a926abcf581732d449163a96fd9a4cc80091a Mon Sep 17 00:00:00 2001 From: Wenchao Xia Date: Tue, 27 Aug 2013 20:38:15 +0800 Subject: monitor: avoid use of global *cur_mon in block_completion_it() Signed-off-by: Wenchao Xia Reviewed-by: Eric Blake Signed-off-by: Luiz Capitulino --- monitor.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/monitor.c b/monitor.c index 3a0b40cfb2..d01aa622d3 100644 --- a/monitor.c +++ b/monitor.c @@ -4091,14 +4091,21 @@ static void file_completion(Monitor *mon, const char *input) 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); + readline_add_completion(mon->rs, name); } } @@ -4142,6 +4149,7 @@ static void monitor_find_completion(const char *cmdline) const char *ptype, *str; const mon_cmd_t *cmd; Monitor *mon = cur_mon; + MonitorBlockComplete mbs; parse_cmdline(cmdline, &nb_args, args); #ifdef DEBUG_COMPLETION @@ -4200,8 +4208,10 @@ static void monitor_find_completion(const char *cmdline) 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 ? */ -- cgit v1.2.1 From d2674b2cf7db7dce865f3c2b89f0e36d1657a3b5 Mon Sep 17 00:00:00 2001 From: Wenchao Xia Date: Tue, 27 Aug 2013 20:38:16 +0800 Subject: monitor: avoid use of global *cur_mon in monitor_find_completion() Parameter *mon is added, and local variable *mon added in previous patch is removed. The caller readline_completion(), pass rs->mon as value, which should be initialized in readline_init() called by monitor_init(). Signed-off-by: Wenchao Xia Reviewed-by: Eric Blake Signed-off-by: Luiz Capitulino --- include/monitor/readline.h | 3 ++- monitor.c | 18 +++++++++--------- readline.c | 2 +- 3 files changed, 12 insertions(+), 11 deletions(-) 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 d01aa622d3..b8f79a5dfa 100644 --- a/monitor.c +++ b/monitor.c @@ -4141,20 +4141,20 @@ 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(Monitor *mon, + const char *cmdline) { const char *cmdname; char *args[MAX_ARGS]; int nb_args, i, len; const char *ptype, *str; const mon_cmd_t *cmd; - Monitor *mon = cur_mon; 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]); + for (i = 0; i < nb_args; i++) { + monitor_printf(mon, "arg%d = '%s'\n", i, args[i]); } #endif @@ -4173,7 +4173,7 @@ static void monitor_find_completion(const char *cmdline) cmdname = ""; else cmdname = args[0]; - readline_set_completion_index(cur_mon->rs, strlen(cmdname)); + readline_set_completion_index(mon->rs, strlen(cmdname)); for(cmd = mon_cmds; cmd->name != NULL; cmd++) { cmd_completion(mon, cmdname, cmd->name); } @@ -4203,7 +4203,7 @@ static void monitor_find_completion(const char *cmdline) switch(*ptype) { case 'F': /* file completion */ - readline_set_completion_index(cur_mon->rs, strlen(str)); + readline_set_completion_index(mon->rs, strlen(str)); file_completion(mon, str); break; case 'B': @@ -4216,7 +4216,7 @@ static void monitor_find_completion(const char *cmdline) case 's': /* XXX: more generic ? */ if (!strcmp(cmd->name, "info")) { - readline_set_completion_index(cur_mon->rs, strlen(str)); + readline_set_completion_index(mon->rs, strlen(str)); for(cmd = info_cmds; cmd->name != NULL; cmd++) { cmd_completion(mon, str, cmd->name); } @@ -4224,12 +4224,12 @@ static void monitor_find_completion(const char *cmdline) 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(mon, str, QKeyCode_lookup[i]); } } else if (!strcmp(cmd->name, "help|?")) { - readline_set_completion_index(cur_mon->rs, strlen(str)); + readline_set_completion_index(mon->rs, strlen(str)); for (cmd = mon_cmds; cmd->name != NULL; cmd++) { cmd_completion(mon, str, cmd->name); } diff --git a/readline.c b/readline.c index 1c0f7ee26b..c91b324cb1 100644 --- a/readline.c +++ b/readline.c @@ -285,7 +285,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 */ -- cgit v1.2.1 From d1a9756ab8c2c2578cbcb325efffe0b0af916944 Mon Sep 17 00:00:00 2001 From: Wenchao Xia Date: Tue, 27 Aug 2013 20:38:17 +0800 Subject: monitor: avoid use of global *cur_mon in readline_completion() Now all completion functions do not use *cur_mon any more, instead they use rs->mon. In short, structure ReadLineState decide where the complete action would be taken now. Tested with the case that qemu have two telnet monitors, auto completion function works normal. Signed-off-by: Wenchao Xia Reviewed-by: Eric Blake Signed-off-by: Luiz Capitulino --- readline.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/readline.c b/readline.c index c91b324cb1..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; @@ -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++) { -- cgit v1.2.1 From d038317c357efef9d8d362e526c5f5071f505a04 Mon Sep 17 00:00:00 2001 From: Wenchao Xia Date: Tue, 27 Aug 2013 20:38:18 +0800 Subject: monitor: call sortcmdlist() only one time It doesn't need to be done for every monitor, so change it. Signed-off-by: Wenchao Xia Signed-off-by: Luiz Capitulino --- monitor.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/monitor.c b/monitor.c index b8f79a5dfa..457948d5cc 100644 --- a/monitor.c +++ b/monitor.c @@ -4763,6 +4763,7 @@ void monitor_init(CharDriverState *chr, int flags) if (is_first_init) { monitor_protocol_event_init(); + sortcmdlist(); is_first_init = 0; } @@ -4792,8 +4793,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) -- cgit v1.2.1 From b01fe89e91268c6b02720735643020746610e6d8 Mon Sep 17 00:00:00 2001 From: Wenchao Xia Date: Tue, 27 Aug 2013 20:38:19 +0800 Subject: monitor: split off monitor_data_init() In qmp_human_monitor_command(), the monitor need to initialized for basic functionalities, and later more init code will be added, so split off this function. Note that it is different with QMP mode monitor which accept json string from monitor's input, qmp_human_monitor_command() retrieve the human style command from QMP input, then send the command to a normal mode monitor. Signed-off-by: Wenchao Xia Signed-off-by: Luiz Capitulino --- monitor.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/monitor.c b/monitor.c index 457948d5cc..1f645e1a80 100644 --- a/monitor.c +++ b/monitor.c @@ -683,14 +683,24 @@ 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(); +} + +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 +726,7 @@ char *qmp_human_monitor_command(const char *command_line, bool has_cpu_index, } out: - QDECREF(hmp.outbuf); + monitor_data_destroy(&hmp); return output; } @@ -4767,8 +4777,8 @@ void monitor_init(CharDriverState *chr, int flags) 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; -- cgit v1.2.1 From 7717239dc1778e94a6210e62e1ec2ba720168eec Mon Sep 17 00:00:00 2001 From: Wenchao Xia Date: Tue, 27 Aug 2013 20:38:20 +0800 Subject: monitor: avoid direct use of global variable *mon_cmds New member *cmd_table is added in structure Monitor to avoid direct usage of *mon_cmds. Now monitor have an associated command table, when global variable *info_cmds is also discarded, structure Monitor would gain full control about how to deal with user input. Signed-off-by: Wenchao Xia Signed-off-by: Luiz Capitulino --- monitor.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/monitor.c b/monitor.c index 1f645e1a80..ce2a1eeb77 100644 --- a/monitor.c +++ b/monitor.c @@ -195,6 +195,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; @@ -687,6 +688,8 @@ 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) @@ -767,7 +770,7 @@ 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); + help_cmd_dump(mon, mon->cmd_table, "", name); if (name && !strcmp(name, "log")) { const QEMULogItem *item; monitor_printf(mon, "Log items (comma separated):\n"); @@ -3995,7 +3998,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; @@ -4184,12 +4187,12 @@ static void monitor_find_completion(Monitor *mon, else cmdname = args[0]; readline_set_completion_index(mon->rs, strlen(cmdname)); - for(cmd = mon_cmds; cmd->name != NULL; cmd++) { + for (cmd = mon->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 = mon->cmd_table; cmd->name != NULL; cmd++) { if (compare_cmd(args[0], cmd->name)) { break; } @@ -4240,7 +4243,7 @@ static void monitor_find_completion(Monitor *mon, } } else if (!strcmp(cmd->name, "help|?")) { readline_set_completion_index(mon->rs, strlen(str)); - for (cmd = mon_cmds; cmd->name != NULL; cmd++) { + for (cmd = mon->cmd_table; cmd->name != NULL; cmd++) { cmd_completion(mon, str, cmd->name); } } -- cgit v1.2.1 From f5438c0500bb22c97b30987d2e0eab953416c7c5 Mon Sep 17 00:00:00 2001 From: Wenchao Xia Date: Tue, 27 Aug 2013 20:38:21 +0800 Subject: monitor: code move for parse_cmdline() help_cmd() need this function later, so move it. get_str() is called by parse_cmdline() so it is moved also. Some code style error reported by check script, is also fixed. Signed-off-by: Wenchao Xia Reviewed-by: Eric Blake Signed-off-by: Luiz Capitulino --- monitor.c | 191 ++++++++++++++++++++++++++++++++------------------------------ 1 file changed, 98 insertions(+), 93 deletions(-) diff --git a/monitor.c b/monitor.c index ce2a1eeb77..76121471f4 100644 --- a/monitor.c +++ b/monitor.c @@ -753,6 +753,104 @@ 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 + +/* 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; + } + } + *pnb_args = nb_args; +} + static void help_cmd_dump(Monitor *mon, const mon_cmd_t *cmds, const char *prefix, const char *name) { @@ -3434,71 +3532,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. @@ -3555,8 +3588,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]; @@ -4122,32 +4153,6 @@ static void block_completion_it(void *opaque, BlockDriverState *bs) } } -/* 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; - } - *pnb_args = nb_args; -} - static const char *next_arg_type(const char *typestr) { const char *p = strchr(typestr, ':'); -- cgit v1.2.1 From dcc70cdf0932172fc5cf27617a3b033ca58d0176 Mon Sep 17 00:00:00 2001 From: Wenchao Xia Date: Tue, 27 Aug 2013 20:38:22 +0800 Subject: monitor: refine parse_cmdline() Since this function will be used by help_cmd() later, so improve it to make it more generic and easier to use. free_cmdline_args() is added too as paired function to free the result. One change of this function is that, when the valid args in input exceed the limit of MAX_ARGS, it fails now, instead of return with MAX_ARGS of parsed args in old code. This should not impact much since it is rare that user input many args in monitor's "help" and auto complete scenario. Signed-off-by: Wenchao Xia Reviewed-by: Eric Blake Signed-off-by: Luiz Capitulino --- monitor.c | 51 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 40 insertions(+), 11 deletions(-) diff --git a/monitor.c b/monitor.c index 76121471f4..aae1babb2d 100644 --- a/monitor.c +++ b/monitor.c @@ -821,9 +821,33 @@ static int get_str(char *buf, int buf_size, const char **pp) #define MAX_ARGS 16 -/* NOTE: this parser is an approximate form of the real command parser */ -static void parse_cmdline(const char *cmdline, - int *pnb_args, char **args) +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; @@ -839,16 +863,21 @@ static void parse_cmdline(const char *cmdline, break; } if (nb_args >= MAX_ARGS) { - break; + goto fail; } ret = get_str(buf, sizeof(buf), &p); - args[nb_args] = g_strdup(buf); - nb_args++; if (ret < 0) { - break; + 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(Monitor *mon, const mon_cmd_t *cmds, @@ -4169,7 +4198,9 @@ static void monitor_find_completion(Monitor *mon, const mon_cmd_t *cmd; MonitorBlockComplete mbs; - parse_cmdline(cmdline, &nb_args, args); + if (parse_cmdline(cmdline, &nb_args, args) < 0) { + return; + } #ifdef DEBUG_COMPLETION for (i = 0; i < nb_args; i++) { monitor_printf(mon, "arg%d = '%s'\n", i, args[i]); @@ -4259,9 +4290,7 @@ static void monitor_find_completion(Monitor *mon, } cleanup: - for (i = 0; i < nb_args; i++) { - g_free(args[i]); - } + free_cmdline_args(args, nb_args); } static int monitor_can_read(void *opaque) -- cgit v1.2.1 From 66855495fbcca9411a21e6eba6a3a0385007c96d Mon Sep 17 00:00:00 2001 From: Wenchao Xia Date: Tue, 27 Aug 2013 20:38:23 +0800 Subject: monitor: support sub command in help The old code in help_cmd() uses global 'info_cmds' and treats it as a special case. Actually 'info_cmds' is a sub command group of 'mon_cmds', in order to avoid direct use of it, help_cmd() needs to change its work mechanism to support sub command and not treat it as a special case any more. To support sub command, help_cmd() will first parse the input and then call help_cmd_dump(), which works as a reentrant function. When it meets a sub command, it simply enters the function again. Since help dumping needs to know whole input to printf full help message include prefix, for example, "help info block" need to printf prefix "info", so help_cmd_dump() takes all args from input and extra parameter arg_index to identify the progress. Another function help_cmd_dump_one() is introduced to printf the prefix and command's help message. Now help supports sub command, so later if another sub command group is added in any depth, help will automatically work for it. Still "help info block" will show error since command parser reject additional parameter, which can be improved later. "log" is still treated as a special case. Signed-off-by: Wenchao Xia Signed-off-by: Luiz Capitulino --- monitor.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 52 insertions(+), 10 deletions(-) diff --git a/monitor.c b/monitor.c index aae1babb2d..39c87534d8 100644 --- a/monitor.c +++ b/monitor.c @@ -880,33 +880,75 @@ static int parse_cmdline(const char *cmdline, 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->cmd_table, "", 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) -- cgit v1.2.1 From c35b6400338897847bbab1b0f65d89552636579a Mon Sep 17 00:00:00 2001 From: Wenchao Xia Date: Tue, 27 Aug 2013 20:38:24 +0800 Subject: monitor: refine monitor_find_completion() In order to support sub command in auto completion, a reentrant function is needed, so monitor_find_completion() is split into two parts. The first part does parsing of user input which need to be done only once, the second part does the auto completion job according to the parsing result, which contains the necessary code to support sub command and works as the reentrant function. The global "info_cmds" is still used in second part, which will be replaced by sub command code later. Signed-off-by: Wenchao Xia Reviewed-by: Eric Blake Signed-off-by: Luiz Capitulino --- monitor.c | 65 ++++++++++++++++++++++++++++++++++++++------------------------- 1 file changed, 39 insertions(+), 26 deletions(-) diff --git a/monitor.c b/monitor.c index 39c87534d8..424d30c8d2 100644 --- a/monitor.c +++ b/monitor.c @@ -4230,34 +4230,17 @@ static const char *next_arg_type(const char *typestr) return (p != NULL ? ++p : typestr); } -static void monitor_find_completion(Monitor *mon, - 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; - if (parse_cmdline(cmdline, &nb_args, args) < 0) { - return; - } -#ifdef DEBUG_COMPLETION - for (i = 0; i < nb_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(""); - } if (nb_args <= 1) { /* command completion */ if (nb_args == 0) @@ -4265,18 +4248,18 @@ static void monitor_find_completion(Monitor *mon, else cmdname = args[0]; readline_set_completion_index(mon->rs, strlen(cmdname)); - for (cmd = mon->cmd_table; cmd->name != NULL; cmd++) { + for (cmd = cmd_table; cmd->name != NULL; cmd++) { cmd_completion(mon, cmdname, cmd->name); } } else { /* find the command */ - for (cmd = mon->cmd_table; 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; } ptype = next_arg_type(cmd->args_type); @@ -4321,7 +4304,7 @@ static void monitor_find_completion(Monitor *mon, } } else if (!strcmp(cmd->name, "help|?")) { readline_set_completion_index(mon->rs, strlen(str)); - for (cmd = mon->cmd_table; cmd->name != NULL; cmd++) { + for (cmd = cmd_table; cmd->name != NULL; cmd++) { cmd_completion(mon, str, cmd->name); } } @@ -4330,6 +4313,36 @@ static void monitor_find_completion(Monitor *mon, break; } } +} + +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++) { + 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); -- cgit v1.2.1 From d903a779cf2f1fa5cd12076411a00b835f1b7b26 Mon Sep 17 00:00:00 2001 From: Wenchao Xia Date: Tue, 27 Aug 2013 20:38:25 +0800 Subject: monitor: support sub command in auto completion This patch allows auto completion work normal for sub command case, "info block [DEVICE]" can auto complete now, by re-enter the completion function. In original code "info" is treated as a special case, now it is treated as a sub command group, global variable info_cmds is not used any more. "help" command is still treated as a special case, since it is not a sub command group but want to auto complete command in root command table. Signed-off-by: Wenchao Xia Reviewed-by: Eric Blake Signed-off-by: Luiz Capitulino --- monitor.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/monitor.c b/monitor.c index 424d30c8d2..c44c7111f3 100644 --- a/monitor.c +++ b/monitor.c @@ -4262,6 +4262,12 @@ static void monitor_find_completion_by_table(Monitor *mon, 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); for(i = 0; i < nb_args - 2; i++) { if (*ptype != '\0') { @@ -4288,13 +4294,7 @@ static void monitor_find_completion_by_table(Monitor *mon, bdrv_iterate(block_completion_it, &mbs); break; case 's': - /* XXX: more generic ? */ - if (!strcmp(cmd->name, "info")) { - readline_set_completion_index(mon->rs, strlen(str)); - for(cmd = info_cmds; cmd->name != NULL; cmd++) { - cmd_completion(mon, str, cmd->name); - } - } else if (!strcmp(cmd->name, "sendkey")) { + if (!strcmp(cmd->name, "sendkey")) { char *sep = strrchr(str, '-'); if (sep) str = sep + 1; -- cgit v1.2.1 From 129be006d63ba90b788de6b1610892e02ef5eaba Mon Sep 17 00:00:00 2001 From: Wenchao Xia Date: Tue, 27 Aug 2013 20:38:26 +0800 Subject: monitor: allow "help" show message for single command in sub group A new parameter type 'S' is introduced to allow user input any string. "help info block" works normal now. Signed-off-by: Wenchao Xia Reviewed-by: Eric Blake Signed-off-by: Luiz Capitulino --- hmp-commands.hx | 2 +- monitor.c | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index 8c6b91a9c7..c161fe9f47 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/monitor.c b/monitor.c index c44c7111f3..721c74d273 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. @@ -4047,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); @@ -4294,6 +4320,7 @@ static void monitor_find_completion_by_table(Monitor *mon, bdrv_iterate(block_completion_it, &mbs); break; case 's': + case 'S': if (!strcmp(cmd->name, "sendkey")) { char *sep = strrchr(str, '-'); if (sep) -- cgit v1.2.1 From 7ca0e061044615e39eab2b22b8fc2791a4d77c34 Mon Sep 17 00:00:00 2001 From: Wenchao Xia Date: Tue, 27 Aug 2013 20:38:27 +0800 Subject: monitor: improve auto complete of "help" for single command in sub group Now special case "help *" in auto completion can work with sub commands, such as "help info u*". Signed-off-by: Wenchao Xia Reviewed-by: Eric Blake Signed-off-by: Luiz Capitulino --- monitor.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/monitor.c b/monitor.c index 721c74d273..0aeaf6c56b 100644 --- a/monitor.c +++ b/monitor.c @@ -4330,10 +4330,8 @@ static void monitor_find_completion_by_table(Monitor *mon, cmd_completion(mon, str, QKeyCode_lookup[i]); } } else if (!strcmp(cmd->name, "help|?")) { - readline_set_completion_index(mon->rs, strlen(str)); - for (cmd = cmd_table; cmd->name != NULL; cmd++) { - cmd_completion(mon, str, cmd->name); - } + monitor_find_completion_by_table(mon, cmd_table, + &args[1], nb_args - 1); } break; default: -- cgit v1.2.1