sim-card
/
qemu
Archived
10
0
Fork 0

completion support

git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1020 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
bellard 2004-07-14 17:21:37 +00:00
parent d1d9f42119
commit 81d0912d2d
2 changed files with 349 additions and 114 deletions

View File

@ -470,6 +470,15 @@ BlockDriverState *bdrv_find(const char *name)
return NULL;
}
void bdrv_iterate(void (*it)(void *opaque, const char *name), void *opaque)
{
BlockDriverState *bs;
for (bs = bdrv_first; bs != NULL; bs = bs->next) {
it(opaque, bs->device_name);
}
}
void bdrv_info(void)
{
BlockDriverState *bs;

454
monitor.c
View File

@ -23,8 +23,10 @@
*/
#include "vl.h"
#include "disas.h"
#include <dirent.h>
//#define DEBUG
//#define DEBUG_COMPLETION
#ifndef offsetof
#define offsetof(type, field) ((size_t) &((type *)0)->field)
@ -32,6 +34,7 @@
#define TERM_CMD_BUF_SIZE 4095
#define TERM_MAX_CMDS 64
#define NB_COMPLETIONS_MAX 256
#define IS_NORM 0
#define IS_ESC 1
@ -42,16 +45,28 @@
static char term_cmd_buf[TERM_CMD_BUF_SIZE + 1];
static int term_cmd_buf_index;
static int term_cmd_buf_size;
static char term_last_cmd_buf[TERM_CMD_BUF_SIZE + 1];
static int term_last_cmd_buf_index;
static int term_last_cmd_buf_size;
static int term_esc_state;
static int term_esc_param;
static char *term_history[TERM_MAX_CMDS];
static int term_hist_entry;
static CharDriverState *monitor_hd;
static int nb_completions;
static int completion_index;
static char *completions[NB_COMPLETIONS_MAX];
/*
* Supported types:
*
* 'F' filename
* 'B' block device name
* 's' string (accept optional quote)
* 'i' integer
* '/' optional gdb-like print format (like "/10x")
@ -71,17 +86,20 @@ typedef struct term_cmd_t {
static term_cmd_t term_cmds[];
static term_cmd_t info_cmds[];
static void add_completion(const char *str);
void term_printf(const char *fmt, ...)
{
char buf[4096];
va_list ap;
va_start(ap, fmt);
vprintf(fmt, ap);
vsnprintf(buf, sizeof(buf), fmt, ap);
qemu_chr_write(monitor_hd, buf, strlen(buf));
va_end(ap);
}
void term_flush(void)
{
fflush(stdout);
}
static int compare_cmd(const char *name, const char *list)
@ -232,8 +250,6 @@ static void do_eject(int force, const char *filename)
{
BlockDriverState *bs;
term_printf("%d %s\n", force, filename);
bs = bdrv_find(filename);
if (!bs) {
term_printf("device not found\n");
@ -674,9 +690,9 @@ static term_cmd_t term_cmds[] = {
"subcommand", "show various information about the system state" },
{ "q|quit", "", do_quit,
"", "quit the emulator" },
{ "eject", "-fs", do_eject,
{ "eject", "-fB", do_eject,
"[-f] device", "eject a removable media (use -f to force it)" },
{ "change", "sF", do_change,
{ "change", "BF", do_change,
"device filename", "change a removable media" },
{ "screendump", "F", do_screen_dump,
"filename", "save screen into PPM image 'filename'" },
@ -953,6 +969,16 @@ static int expr_unary(void)
}
next();
break;
case '\'':
pch++;
if (*pch == '\0')
expr_error("character constant expected");
n = *pch;
pch++;
if (*pch != '\'')
expr_error("missing terminating \' character");
next();
break;
case '$':
{
char buf[128], *q;
@ -1088,15 +1114,16 @@ static int get_str(char *buf, int buf_size, const char **pp)
char *q;
int c;
q = buf;
p = *pp;
while (isspace(*p))
p++;
if (*p == '\0') {
fail:
*q = '\0';
*pp = p;
return -1;
}
q = buf;
if (*p == '\"') {
p++;
while (*p != '\0' && *p != '\"') {
@ -1140,8 +1167,8 @@ static int get_str(char *buf, int buf_size, const char **pp)
}
p++;
}
*q = '\0';
}
*q = '\0';
*pp = p;
return 0;
}
@ -1204,6 +1231,7 @@ static void term_handle_command(const char *cmdline)
typestr++;
switch(c) {
case 'F':
case 'B':
case 's':
{
int ret;
@ -1221,10 +1249,17 @@ static void term_handle_command(const char *cmdline)
}
ret = get_str(buf, sizeof(buf), &p);
if (ret < 0) {
if (c == 'F')
switch(c) {
case 'F':
term_printf("%s: filename expected\n", cmdname);
else
break;
case 'B':
term_printf("%s: block device name expected\n", cmdname);
break;
default:
term_printf("%s: string expected\n", cmdname);
break;
}
goto fail;
}
str = qemu_malloc(strlen(buf) + 1);
@ -1432,19 +1467,232 @@ static void term_handle_command(const char *cmdline)
return;
}
static void term_show_prompt(void)
static void cmd_completion(const char *name, const char *list)
{
const char *p, *pstart;
char cmd[128];
int len;
p = list;
for(;;) {
pstart = p;
p = strchr(p, '|');
if (!p)
p = pstart + strlen(pstart);
len = p - pstart;
if (len > sizeof(cmd) - 2)
len = sizeof(cmd) - 2;
memcpy(cmd, pstart, len);
cmd[len] = '\0';
if (name[0] == '\0' || !strncmp(name, cmd, strlen(name))) {
add_completion(cmd);
}
if (*p == '\0')
break;
p++;
}
}
static void file_completion(const char *input)
{
DIR *ffs;
struct dirent *d;
char path[1024];
char file[1024], file_prefix[1024];
int input_path_len;
const char *p;
p = strrchr(input, '/');
if (!p) {
input_path_len = 0;
pstrcpy(file_prefix, sizeof(file_prefix), input);
strcpy(path, ".");
} else {
input_path_len = p - input + 1;
memcpy(path, input, input_path_len);
if (input_path_len > sizeof(path) - 1)
input_path_len = sizeof(path) - 1;
path[input_path_len] = '\0';
pstrcpy(file_prefix, sizeof(file_prefix), p + 1);
}
#ifdef DEBUG_COMPLETION
term_printf("input='%s' path='%s' prefix='%s'\n", input, path, file_prefix);
#endif
ffs = opendir(path);
if (!ffs)
return;
for(;;) {
struct stat sb;
d = readdir(ffs);
if (!d)
break;
if (strstart(d->d_name, file_prefix, NULL)) {
memcpy(file, input, input_path_len);
strcpy(file + input_path_len, d->d_name);
/* stat the file to find out if it's a directory.
* In that case add a slash to speed up typing long paths
*/
stat(file, &sb);
if(S_ISDIR(sb.st_mode))
strcat(file, "/");
add_completion(file);
}
}
closedir(ffs);
}
static void block_completion_it(void *opaque, const char *name)
{
const char *input = opaque;
if (input[0] == '\0' ||
!strncmp(name, (char *)input, strlen(input))) {
add_completion(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 (isspace(*p))
p++;
if (*p == '\0')
break;
if (nb_args >= MAX_ARGS)
break;
ret = get_str(buf, sizeof(buf), &p);
args[nb_args] = qemu_strdup(buf);
nb_args++;
if (ret < 0)
break;
}
*pnb_args = nb_args;
}
static void find_completion(const char *cmdline)
{
const char *cmdname;
char *args[MAX_ARGS];
int nb_args, i, len;
const char *ptype, *str;
term_cmd_t *cmd;
parse_cmdline(cmdline, &nb_args, args);
#ifdef DEBUG_COMPLETION
for(i = 0; i < nb_args; i++) {
term_printf("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 && isspace(cmdline[len - 1])) {
if (nb_args >= MAX_ARGS)
return;
args[nb_args++] = qemu_strdup("");
}
if (nb_args <= 1) {
/* command completion */
if (nb_args == 0)
cmdname = "";
else
cmdname = args[0];
completion_index = strlen(cmdname);
for(cmd = term_cmds; cmd->name != NULL; cmd++) {
cmd_completion(cmdname, cmd->name);
}
} else {
/* find the command */
for(cmd = term_cmds; cmd->name != NULL; cmd++) {
if (compare_cmd(args[0], cmd->name))
goto found;
}
return;
found:
ptype = cmd->args_type;
for(i = 0; i < nb_args - 2; i++) {
if (*ptype != '\0') {
ptype++;
while (*ptype == '?')
ptype++;
}
}
str = args[nb_args - 1];
switch(*ptype) {
case 'F':
/* file completion */
completion_index = strlen(str);
file_completion(str);
break;
case 'B':
/* block device name completion */
completion_index = strlen(str);
bdrv_iterate(block_completion_it, (void *)str);
break;
default:
break;
}
}
for(i = 0; i < nb_args; i++)
qemu_free(args[i]);
}
static void term_show_prompt2(void)
{
term_printf("(qemu) ");
fflush(stdout);
term_cmd_buf_index = 0;
term_cmd_buf_size = 0;
term_last_cmd_buf_index = 0;
term_last_cmd_buf_size = 0;
term_esc_state = IS_NORM;
}
static void term_print_cmdline (const char *cmdline)
static void term_show_prompt(void)
{
term_show_prompt();
term_printf("%s", cmdline);
term_show_prompt2();
term_cmd_buf_index = 0;
term_cmd_buf_size = 0;
}
/* update the displayed command line */
static void term_update(void)
{
int i, delta;
if (term_cmd_buf_size != term_last_cmd_buf_size ||
memcmp(term_cmd_buf, term_last_cmd_buf, term_cmd_buf_size) != 0) {
for(i = 0; i < term_last_cmd_buf_index; i++) {
term_printf("\033[D");
}
term_cmd_buf[term_cmd_buf_size] = '\0';
term_printf("%s", term_cmd_buf);
term_printf("\033[K");
memcpy(term_last_cmd_buf, term_cmd_buf, term_cmd_buf_size);
term_last_cmd_buf_size = term_cmd_buf_size;
term_last_cmd_buf_index = term_cmd_buf_size;
}
if (term_cmd_buf_index != term_last_cmd_buf_index) {
delta = term_cmd_buf_index - term_last_cmd_buf_index;
if (delta > 0) {
for(i = 0;i < delta; i++) {
term_printf("\033[C");
}
} else {
delta = -delta;
for(i = 0;i < delta; i++) {
term_printf("\033[D");
}
}
term_last_cmd_buf_index = term_cmd_buf_index;
}
term_flush();
}
@ -1456,9 +1704,7 @@ static void term_insert_char(int ch)
term_cmd_buf_size - term_cmd_buf_index);
term_cmd_buf[term_cmd_buf_index] = ch;
term_cmd_buf_size++;
term_printf("\033[@%c", ch);
term_cmd_buf_index++;
term_flush();
}
}
@ -1466,8 +1712,6 @@ static void term_backward_char(void)
{
if (term_cmd_buf_index > 0) {
term_cmd_buf_index--;
term_printf("\033[D");
term_flush();
}
}
@ -1475,8 +1719,6 @@ static void term_forward_char(void)
{
if (term_cmd_buf_index < term_cmd_buf_size) {
term_cmd_buf_index++;
term_printf("\033[C");
term_flush();
}
}
@ -1486,9 +1728,7 @@ static void term_delete_char(void)
memmove(term_cmd_buf + term_cmd_buf_index,
term_cmd_buf + term_cmd_buf_index + 1,
term_cmd_buf_size - term_cmd_buf_index - 1);
term_printf("\033[P");
term_cmd_buf_size--;
term_flush();
}
}
@ -1502,14 +1742,12 @@ static void term_backspace(void)
static void term_bol(void)
{
while (term_cmd_buf_index > 0)
term_backward_char();
term_cmd_buf_index = 0;
}
static void term_eol(void)
{
while (term_cmd_buf_index < term_cmd_buf_size)
term_forward_char();
term_cmd_buf_index = term_cmd_buf_size;
}
static void term_up_char(void)
@ -1530,8 +1768,6 @@ static void term_up_char(void)
if (term_hist_entry >= 0) {
pstrcpy(term_cmd_buf, sizeof(term_cmd_buf),
term_history[term_hist_entry]);
term_printf("\n");
term_print_cmdline(term_cmd_buf);
term_cmd_buf_index = term_cmd_buf_size = strlen(term_cmd_buf);
}
}
@ -1546,8 +1782,6 @@ static void term_down_char(void)
} else {
term_hist_entry = -1;
}
term_printf("\n");
term_print_cmdline(term_cmd_buf);
term_cmd_buf_index = term_cmd_buf_size = strlen(term_cmd_buf);
}
@ -1600,6 +1834,67 @@ static void term_hist_add(const char *cmdline)
term_hist_entry = -1;
}
/* completion support */
static void add_completion(const char *str)
{
if (nb_completions < NB_COMPLETIONS_MAX) {
completions[nb_completions++] = qemu_strdup(str);
}
}
static void term_completion(void)
{
int len, i, j, max_width, nb_cols;
char *cmdline;
nb_completions = 0;
cmdline = qemu_malloc(term_cmd_buf_index + 1);
if (!cmdline)
return;
memcpy(cmdline, term_cmd_buf, term_cmd_buf_index);
cmdline[term_cmd_buf_index] = '\0';
find_completion(cmdline);
qemu_free(cmdline);
/* no completion found */
if (nb_completions <= 0)
return;
if (nb_completions == 1) {
len = strlen(completions[0]);
for(i = completion_index; i < len; i++) {
term_insert_char(completions[0][i]);
}
/* extra space for next argument. XXX: make it more generic */
if (len > 0 && completions[0][len - 1] != '/')
term_insert_char(' ');
} else {
term_printf("\n");
max_width = 0;
for(i = 0; i < nb_completions; i++) {
len = strlen(completions[i]);
if (len > max_width)
max_width = len;
}
max_width += 2;
if (max_width < 10)
max_width = 10;
else if (max_width > 80)
max_width = 80;
nb_cols = 80 / max_width;
j = 0;
for(i = 0; i < nb_completions; i++) {
term_printf("%-*s", max_width, completions[i]);
if (++j == nb_cols || i == (nb_completions - 1)) {
term_printf("\n");
j = 0;
}
}
term_show_prompt2();
}
}
/* return true if command handled */
static void term_handle_byte(int ch)
{
@ -1609,9 +1904,15 @@ static void term_handle_byte(int ch)
case 1:
term_bol();
break;
case 4:
term_delete_char();
break;
case 5:
term_eol();
break;
case 9:
term_completion();
break;
case 10:
case 13:
term_cmd_buf[term_cmd_buf_size] = '\0';
@ -1684,104 +1985,29 @@ static void term_handle_byte(int ch)
the_end:
break;
}
}
/*************************************************************/
/* serial console support */
#define TERM_ESCAPE 0x01 /* ctrl-a is used for escape */
static int term_got_escape, term_command;
void term_print_help(void)
{
term_printf("\n"
"C-a h print this help\n"
"C-a x exit emulatior\n"
"C-a s save disk data back to file (if -snapshot)\n"
"C-a b send break (magic sysrq)\n"
"C-a c switch between console and monitor\n"
"C-a C-a send C-a\n"
);
}
/* called when a char is received */
static void term_received_byte(int ch)
{
if (!serial_console) {
/* if no serial console, handle every command */
term_handle_byte(ch);
} else {
if (term_got_escape) {
term_got_escape = 0;
switch(ch) {
case 'h':
term_print_help();
break;
case 'x':
exit(0);
break;
case 's':
{
int i;
for (i = 0; i < MAX_DISKS; i++) {
if (bs_table[i])
bdrv_commit(bs_table[i]);
}
}
break;
case 'b':
if (serial_console)
serial_receive_break(serial_console);
break;
case 'c':
if (!term_command) {
term_show_prompt();
term_command = 1;
} else {
term_command = 0;
}
break;
case TERM_ESCAPE:
goto send_char;
}
} else if (ch == TERM_ESCAPE) {
term_got_escape = 1;
} else {
send_char:
if (term_command) {
term_handle_byte(ch);
} else {
if (serial_console)
serial_receive_byte(serial_console, ch);
}
}
}
term_update();
}
static int term_can_read(void *opaque)
{
if (serial_console) {
return serial_can_receive(serial_console);
} else {
return 128;
}
return 128;
}
static void term_read(void *opaque, const uint8_t *buf, int size)
{
int i;
for(i = 0; i < size; i++)
term_received_byte(buf[i]);
term_handle_byte(buf[i]);
}
void monitor_init(void)
void monitor_init(CharDriverState *hd, int show_banner)
{
if (!serial_console) {
monitor_hd = hd;
if (show_banner) {
term_printf("QEMU %s monitor - type 'help' for more information\n",
QEMU_VERSION);
term_show_prompt();
}
term_hist_entry = -1;
qemu_add_fd_read_handler(0, term_can_read, term_read, NULL);
qemu_chr_add_read_handler(hd, term_can_read, term_read, NULL);
}