mirror of https://github.com/xemu-project/xemu.git
completion support
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1020 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
parent
d1d9f42119
commit
81d0912d2d
9
block.c
9
block.c
|
@ -470,6 +470,15 @@ BlockDriverState *bdrv_find(const char *name)
|
||||||
return NULL;
|
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)
|
void bdrv_info(void)
|
||||||
{
|
{
|
||||||
BlockDriverState *bs;
|
BlockDriverState *bs;
|
||||||
|
|
454
monitor.c
454
monitor.c
|
@ -23,8 +23,10 @@
|
||||||
*/
|
*/
|
||||||
#include "vl.h"
|
#include "vl.h"
|
||||||
#include "disas.h"
|
#include "disas.h"
|
||||||
|
#include <dirent.h>
|
||||||
|
|
||||||
//#define DEBUG
|
//#define DEBUG
|
||||||
|
//#define DEBUG_COMPLETION
|
||||||
|
|
||||||
#ifndef offsetof
|
#ifndef offsetof
|
||||||
#define offsetof(type, field) ((size_t) &((type *)0)->field)
|
#define offsetof(type, field) ((size_t) &((type *)0)->field)
|
||||||
|
@ -32,6 +34,7 @@
|
||||||
|
|
||||||
#define TERM_CMD_BUF_SIZE 4095
|
#define TERM_CMD_BUF_SIZE 4095
|
||||||
#define TERM_MAX_CMDS 64
|
#define TERM_MAX_CMDS 64
|
||||||
|
#define NB_COMPLETIONS_MAX 256
|
||||||
|
|
||||||
#define IS_NORM 0
|
#define IS_NORM 0
|
||||||
#define IS_ESC 1
|
#define IS_ESC 1
|
||||||
|
@ -42,16 +45,28 @@
|
||||||
static char term_cmd_buf[TERM_CMD_BUF_SIZE + 1];
|
static char term_cmd_buf[TERM_CMD_BUF_SIZE + 1];
|
||||||
static int term_cmd_buf_index;
|
static int term_cmd_buf_index;
|
||||||
static int term_cmd_buf_size;
|
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_state;
|
||||||
static int term_esc_param;
|
static int term_esc_param;
|
||||||
|
|
||||||
static char *term_history[TERM_MAX_CMDS];
|
static char *term_history[TERM_MAX_CMDS];
|
||||||
static int term_hist_entry;
|
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:
|
* Supported types:
|
||||||
*
|
*
|
||||||
* 'F' filename
|
* 'F' filename
|
||||||
|
* 'B' block device name
|
||||||
* 's' string (accept optional quote)
|
* 's' string (accept optional quote)
|
||||||
* 'i' integer
|
* 'i' integer
|
||||||
* '/' optional gdb-like print format (like "/10x")
|
* '/' 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 term_cmds[];
|
||||||
static term_cmd_t info_cmds[];
|
static term_cmd_t info_cmds[];
|
||||||
|
|
||||||
|
static void add_completion(const char *str);
|
||||||
|
|
||||||
void term_printf(const char *fmt, ...)
|
void term_printf(const char *fmt, ...)
|
||||||
{
|
{
|
||||||
|
char buf[4096];
|
||||||
va_list ap;
|
va_list ap;
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
vprintf(fmt, ap);
|
vsnprintf(buf, sizeof(buf), fmt, ap);
|
||||||
|
qemu_chr_write(monitor_hd, buf, strlen(buf));
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
void term_flush(void)
|
void term_flush(void)
|
||||||
{
|
{
|
||||||
fflush(stdout);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int compare_cmd(const char *name, const char *list)
|
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;
|
BlockDriverState *bs;
|
||||||
|
|
||||||
term_printf("%d %s\n", force, filename);
|
|
||||||
|
|
||||||
bs = bdrv_find(filename);
|
bs = bdrv_find(filename);
|
||||||
if (!bs) {
|
if (!bs) {
|
||||||
term_printf("device not found\n");
|
term_printf("device not found\n");
|
||||||
|
@ -674,9 +690,9 @@ static term_cmd_t term_cmds[] = {
|
||||||
"subcommand", "show various information about the system state" },
|
"subcommand", "show various information about the system state" },
|
||||||
{ "q|quit", "", do_quit,
|
{ "q|quit", "", do_quit,
|
||||||
"", "quit the emulator" },
|
"", "quit the emulator" },
|
||||||
{ "eject", "-fs", do_eject,
|
{ "eject", "-fB", do_eject,
|
||||||
"[-f] device", "eject a removable media (use -f to force it)" },
|
"[-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" },
|
"device filename", "change a removable media" },
|
||||||
{ "screendump", "F", do_screen_dump,
|
{ "screendump", "F", do_screen_dump,
|
||||||
"filename", "save screen into PPM image 'filename'" },
|
"filename", "save screen into PPM image 'filename'" },
|
||||||
|
@ -953,6 +969,16 @@ static int expr_unary(void)
|
||||||
}
|
}
|
||||||
next();
|
next();
|
||||||
break;
|
break;
|
||||||
|
case '\'':
|
||||||
|
pch++;
|
||||||
|
if (*pch == '\0')
|
||||||
|
expr_error("character constant expected");
|
||||||
|
n = *pch;
|
||||||
|
pch++;
|
||||||
|
if (*pch != '\'')
|
||||||
|
expr_error("missing terminating \' character");
|
||||||
|
next();
|
||||||
|
break;
|
||||||
case '$':
|
case '$':
|
||||||
{
|
{
|
||||||
char buf[128], *q;
|
char buf[128], *q;
|
||||||
|
@ -1088,15 +1114,16 @@ static int get_str(char *buf, int buf_size, const char **pp)
|
||||||
char *q;
|
char *q;
|
||||||
int c;
|
int c;
|
||||||
|
|
||||||
|
q = buf;
|
||||||
p = *pp;
|
p = *pp;
|
||||||
while (isspace(*p))
|
while (isspace(*p))
|
||||||
p++;
|
p++;
|
||||||
if (*p == '\0') {
|
if (*p == '\0') {
|
||||||
fail:
|
fail:
|
||||||
|
*q = '\0';
|
||||||
*pp = p;
|
*pp = p;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
q = buf;
|
|
||||||
if (*p == '\"') {
|
if (*p == '\"') {
|
||||||
p++;
|
p++;
|
||||||
while (*p != '\0' && *p != '\"') {
|
while (*p != '\0' && *p != '\"') {
|
||||||
|
@ -1140,8 +1167,8 @@ static int get_str(char *buf, int buf_size, const char **pp)
|
||||||
}
|
}
|
||||||
p++;
|
p++;
|
||||||
}
|
}
|
||||||
*q = '\0';
|
|
||||||
}
|
}
|
||||||
|
*q = '\0';
|
||||||
*pp = p;
|
*pp = p;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1204,6 +1231,7 @@ static void term_handle_command(const char *cmdline)
|
||||||
typestr++;
|
typestr++;
|
||||||
switch(c) {
|
switch(c) {
|
||||||
case 'F':
|
case 'F':
|
||||||
|
case 'B':
|
||||||
case 's':
|
case 's':
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -1221,10 +1249,17 @@ static void term_handle_command(const char *cmdline)
|
||||||
}
|
}
|
||||||
ret = get_str(buf, sizeof(buf), &p);
|
ret = get_str(buf, sizeof(buf), &p);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
if (c == 'F')
|
switch(c) {
|
||||||
|
case 'F':
|
||||||
term_printf("%s: filename expected\n", cmdname);
|
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);
|
term_printf("%s: string expected\n", cmdname);
|
||||||
|
break;
|
||||||
|
}
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
str = qemu_malloc(strlen(buf) + 1);
|
str = qemu_malloc(strlen(buf) + 1);
|
||||||
|
@ -1432,19 +1467,232 @@ static void term_handle_command(const char *cmdline)
|
||||||
return;
|
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) ");
|
term_printf("(qemu) ");
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
term_cmd_buf_index = 0;
|
term_last_cmd_buf_index = 0;
|
||||||
term_cmd_buf_size = 0;
|
term_last_cmd_buf_size = 0;
|
||||||
term_esc_state = IS_NORM;
|
term_esc_state = IS_NORM;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void term_print_cmdline (const char *cmdline)
|
static void term_show_prompt(void)
|
||||||
{
|
{
|
||||||
term_show_prompt();
|
term_show_prompt2();
|
||||||
term_printf("%s", cmdline);
|
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();
|
term_flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1456,9 +1704,7 @@ static void term_insert_char(int ch)
|
||||||
term_cmd_buf_size - term_cmd_buf_index);
|
term_cmd_buf_size - term_cmd_buf_index);
|
||||||
term_cmd_buf[term_cmd_buf_index] = ch;
|
term_cmd_buf[term_cmd_buf_index] = ch;
|
||||||
term_cmd_buf_size++;
|
term_cmd_buf_size++;
|
||||||
term_printf("\033[@%c", ch);
|
|
||||||
term_cmd_buf_index++;
|
term_cmd_buf_index++;
|
||||||
term_flush();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1466,8 +1712,6 @@ static void term_backward_char(void)
|
||||||
{
|
{
|
||||||
if (term_cmd_buf_index > 0) {
|
if (term_cmd_buf_index > 0) {
|
||||||
term_cmd_buf_index--;
|
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) {
|
if (term_cmd_buf_index < term_cmd_buf_size) {
|
||||||
term_cmd_buf_index++;
|
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,
|
memmove(term_cmd_buf + term_cmd_buf_index,
|
||||||
term_cmd_buf + term_cmd_buf_index + 1,
|
term_cmd_buf + term_cmd_buf_index + 1,
|
||||||
term_cmd_buf_size - term_cmd_buf_index - 1);
|
term_cmd_buf_size - term_cmd_buf_index - 1);
|
||||||
term_printf("\033[P");
|
|
||||||
term_cmd_buf_size--;
|
term_cmd_buf_size--;
|
||||||
term_flush();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1502,14 +1742,12 @@ static void term_backspace(void)
|
||||||
|
|
||||||
static void term_bol(void)
|
static void term_bol(void)
|
||||||
{
|
{
|
||||||
while (term_cmd_buf_index > 0)
|
term_cmd_buf_index = 0;
|
||||||
term_backward_char();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void term_eol(void)
|
static void term_eol(void)
|
||||||
{
|
{
|
||||||
while (term_cmd_buf_index < term_cmd_buf_size)
|
term_cmd_buf_index = term_cmd_buf_size;
|
||||||
term_forward_char();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void term_up_char(void)
|
static void term_up_char(void)
|
||||||
|
@ -1530,8 +1768,6 @@ static void term_up_char(void)
|
||||||
if (term_hist_entry >= 0) {
|
if (term_hist_entry >= 0) {
|
||||||
pstrcpy(term_cmd_buf, sizeof(term_cmd_buf),
|
pstrcpy(term_cmd_buf, sizeof(term_cmd_buf),
|
||||||
term_history[term_hist_entry]);
|
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);
|
term_cmd_buf_index = term_cmd_buf_size = strlen(term_cmd_buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1546,8 +1782,6 @@ static void term_down_char(void)
|
||||||
} else {
|
} else {
|
||||||
term_hist_entry = -1;
|
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);
|
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;
|
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 */
|
/* return true if command handled */
|
||||||
static void term_handle_byte(int ch)
|
static void term_handle_byte(int ch)
|
||||||
{
|
{
|
||||||
|
@ -1609,9 +1904,15 @@ static void term_handle_byte(int ch)
|
||||||
case 1:
|
case 1:
|
||||||
term_bol();
|
term_bol();
|
||||||
break;
|
break;
|
||||||
|
case 4:
|
||||||
|
term_delete_char();
|
||||||
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
term_eol();
|
term_eol();
|
||||||
break;
|
break;
|
||||||
|
case 9:
|
||||||
|
term_completion();
|
||||||
|
break;
|
||||||
case 10:
|
case 10:
|
||||||
case 13:
|
case 13:
|
||||||
term_cmd_buf[term_cmd_buf_size] = '\0';
|
term_cmd_buf[term_cmd_buf_size] = '\0';
|
||||||
|
@ -1684,104 +1985,29 @@ static void term_handle_byte(int ch)
|
||||||
the_end:
|
the_end:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
term_update();
|
||||||
|
|
||||||
/*************************************************************/
|
|
||||||
/* 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int term_can_read(void *opaque)
|
static int term_can_read(void *opaque)
|
||||||
{
|
{
|
||||||
if (serial_console) {
|
return 128;
|
||||||
return serial_can_receive(serial_console);
|
|
||||||
} else {
|
|
||||||
return 128;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void term_read(void *opaque, const uint8_t *buf, int size)
|
static void term_read(void *opaque, const uint8_t *buf, int size)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
for(i = 0; i < size; 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",
|
term_printf("QEMU %s monitor - type 'help' for more information\n",
|
||||||
QEMU_VERSION);
|
QEMU_VERSION);
|
||||||
term_show_prompt();
|
term_show_prompt();
|
||||||
}
|
}
|
||||||
term_hist_entry = -1;
|
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);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue