Commit 81d0912d2d221c41d649d2c36812afddf74634d2
1 parent
d1d9f421
completion support
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1020 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
2 changed files
with
348 additions
and
113 deletions
block.c
| @@ -470,6 +470,15 @@ BlockDriverState *bdrv_find(const char *name) | @@ -470,6 +470,15 @@ BlockDriverState *bdrv_find(const char *name) | ||
| 470 | return NULL; | 470 | return NULL; |
| 471 | } | 471 | } |
| 472 | 472 | ||
| 473 | +void bdrv_iterate(void (*it)(void *opaque, const char *name), void *opaque) | ||
| 474 | +{ | ||
| 475 | + BlockDriverState *bs; | ||
| 476 | + | ||
| 477 | + for (bs = bdrv_first; bs != NULL; bs = bs->next) { | ||
| 478 | + it(opaque, bs->device_name); | ||
| 479 | + } | ||
| 480 | +} | ||
| 481 | + | ||
| 473 | void bdrv_info(void) | 482 | void bdrv_info(void) |
| 474 | { | 483 | { |
| 475 | BlockDriverState *bs; | 484 | BlockDriverState *bs; |
monitor.c
| @@ -23,8 +23,10 @@ | @@ -23,8 +23,10 @@ | ||
| 23 | */ | 23 | */ |
| 24 | #include "vl.h" | 24 | #include "vl.h" |
| 25 | #include "disas.h" | 25 | #include "disas.h" |
| 26 | +#include <dirent.h> | ||
| 26 | 27 | ||
| 27 | //#define DEBUG | 28 | //#define DEBUG |
| 29 | +//#define DEBUG_COMPLETION | ||
| 28 | 30 | ||
| 29 | #ifndef offsetof | 31 | #ifndef offsetof |
| 30 | #define offsetof(type, field) ((size_t) &((type *)0)->field) | 32 | #define offsetof(type, field) ((size_t) &((type *)0)->field) |
| @@ -32,6 +34,7 @@ | @@ -32,6 +34,7 @@ | ||
| 32 | 34 | ||
| 33 | #define TERM_CMD_BUF_SIZE 4095 | 35 | #define TERM_CMD_BUF_SIZE 4095 |
| 34 | #define TERM_MAX_CMDS 64 | 36 | #define TERM_MAX_CMDS 64 |
| 37 | +#define NB_COMPLETIONS_MAX 256 | ||
| 35 | 38 | ||
| 36 | #define IS_NORM 0 | 39 | #define IS_NORM 0 |
| 37 | #define IS_ESC 1 | 40 | #define IS_ESC 1 |
| @@ -42,16 +45,28 @@ | @@ -42,16 +45,28 @@ | ||
| 42 | static char term_cmd_buf[TERM_CMD_BUF_SIZE + 1]; | 45 | static char term_cmd_buf[TERM_CMD_BUF_SIZE + 1]; |
| 43 | static int term_cmd_buf_index; | 46 | static int term_cmd_buf_index; |
| 44 | static int term_cmd_buf_size; | 47 | static int term_cmd_buf_size; |
| 48 | + | ||
| 49 | +static char term_last_cmd_buf[TERM_CMD_BUF_SIZE + 1]; | ||
| 50 | +static int term_last_cmd_buf_index; | ||
| 51 | +static int term_last_cmd_buf_size; | ||
| 52 | + | ||
| 45 | static int term_esc_state; | 53 | static int term_esc_state; |
| 46 | static int term_esc_param; | 54 | static int term_esc_param; |
| 47 | 55 | ||
| 48 | static char *term_history[TERM_MAX_CMDS]; | 56 | static char *term_history[TERM_MAX_CMDS]; |
| 49 | static int term_hist_entry; | 57 | static int term_hist_entry; |
| 58 | +static CharDriverState *monitor_hd; | ||
| 59 | + | ||
| 60 | +static int nb_completions; | ||
| 61 | +static int completion_index; | ||
| 62 | +static char *completions[NB_COMPLETIONS_MAX]; | ||
| 63 | + | ||
| 50 | 64 | ||
| 51 | /* | 65 | /* |
| 52 | * Supported types: | 66 | * Supported types: |
| 53 | * | 67 | * |
| 54 | * 'F' filename | 68 | * 'F' filename |
| 69 | + * 'B' block device name | ||
| 55 | * 's' string (accept optional quote) | 70 | * 's' string (accept optional quote) |
| 56 | * 'i' integer | 71 | * 'i' integer |
| 57 | * '/' optional gdb-like print format (like "/10x") | 72 | * '/' optional gdb-like print format (like "/10x") |
| @@ -71,17 +86,20 @@ typedef struct term_cmd_t { | @@ -71,17 +86,20 @@ typedef struct term_cmd_t { | ||
| 71 | static term_cmd_t term_cmds[]; | 86 | static term_cmd_t term_cmds[]; |
| 72 | static term_cmd_t info_cmds[]; | 87 | static term_cmd_t info_cmds[]; |
| 73 | 88 | ||
| 89 | +static void add_completion(const char *str); | ||
| 90 | + | ||
| 74 | void term_printf(const char *fmt, ...) | 91 | void term_printf(const char *fmt, ...) |
| 75 | { | 92 | { |
| 93 | + char buf[4096]; | ||
| 76 | va_list ap; | 94 | va_list ap; |
| 77 | va_start(ap, fmt); | 95 | va_start(ap, fmt); |
| 78 | - vprintf(fmt, ap); | 96 | + vsnprintf(buf, sizeof(buf), fmt, ap); |
| 97 | + qemu_chr_write(monitor_hd, buf, strlen(buf)); | ||
| 79 | va_end(ap); | 98 | va_end(ap); |
| 80 | } | 99 | } |
| 81 | 100 | ||
| 82 | void term_flush(void) | 101 | void term_flush(void) |
| 83 | { | 102 | { |
| 84 | - fflush(stdout); | ||
| 85 | } | 103 | } |
| 86 | 104 | ||
| 87 | static int compare_cmd(const char *name, const char *list) | 105 | static int compare_cmd(const char *name, const char *list) |
| @@ -232,8 +250,6 @@ static void do_eject(int force, const char *filename) | @@ -232,8 +250,6 @@ static void do_eject(int force, const char *filename) | ||
| 232 | { | 250 | { |
| 233 | BlockDriverState *bs; | 251 | BlockDriverState *bs; |
| 234 | 252 | ||
| 235 | - term_printf("%d %s\n", force, filename); | ||
| 236 | - | ||
| 237 | bs = bdrv_find(filename); | 253 | bs = bdrv_find(filename); |
| 238 | if (!bs) { | 254 | if (!bs) { |
| 239 | term_printf("device not found\n"); | 255 | term_printf("device not found\n"); |
| @@ -674,9 +690,9 @@ static term_cmd_t term_cmds[] = { | @@ -674,9 +690,9 @@ static term_cmd_t term_cmds[] = { | ||
| 674 | "subcommand", "show various information about the system state" }, | 690 | "subcommand", "show various information about the system state" }, |
| 675 | { "q|quit", "", do_quit, | 691 | { "q|quit", "", do_quit, |
| 676 | "", "quit the emulator" }, | 692 | "", "quit the emulator" }, |
| 677 | - { "eject", "-fs", do_eject, | 693 | + { "eject", "-fB", do_eject, |
| 678 | "[-f] device", "eject a removable media (use -f to force it)" }, | 694 | "[-f] device", "eject a removable media (use -f to force it)" }, |
| 679 | - { "change", "sF", do_change, | 695 | + { "change", "BF", do_change, |
| 680 | "device filename", "change a removable media" }, | 696 | "device filename", "change a removable media" }, |
| 681 | { "screendump", "F", do_screen_dump, | 697 | { "screendump", "F", do_screen_dump, |
| 682 | "filename", "save screen into PPM image 'filename'" }, | 698 | "filename", "save screen into PPM image 'filename'" }, |
| @@ -953,6 +969,16 @@ static int expr_unary(void) | @@ -953,6 +969,16 @@ static int expr_unary(void) | ||
| 953 | } | 969 | } |
| 954 | next(); | 970 | next(); |
| 955 | break; | 971 | break; |
| 972 | + case '\'': | ||
| 973 | + pch++; | ||
| 974 | + if (*pch == '\0') | ||
| 975 | + expr_error("character constant expected"); | ||
| 976 | + n = *pch; | ||
| 977 | + pch++; | ||
| 978 | + if (*pch != '\'') | ||
| 979 | + expr_error("missing terminating \' character"); | ||
| 980 | + next(); | ||
| 981 | + break; | ||
| 956 | case '$': | 982 | case '$': |
| 957 | { | 983 | { |
| 958 | char buf[128], *q; | 984 | char buf[128], *q; |
| @@ -1088,15 +1114,16 @@ static int get_str(char *buf, int buf_size, const char **pp) | @@ -1088,15 +1114,16 @@ static int get_str(char *buf, int buf_size, const char **pp) | ||
| 1088 | char *q; | 1114 | char *q; |
| 1089 | int c; | 1115 | int c; |
| 1090 | 1116 | ||
| 1117 | + q = buf; | ||
| 1091 | p = *pp; | 1118 | p = *pp; |
| 1092 | while (isspace(*p)) | 1119 | while (isspace(*p)) |
| 1093 | p++; | 1120 | p++; |
| 1094 | if (*p == '\0') { | 1121 | if (*p == '\0') { |
| 1095 | fail: | 1122 | fail: |
| 1123 | + *q = '\0'; | ||
| 1096 | *pp = p; | 1124 | *pp = p; |
| 1097 | return -1; | 1125 | return -1; |
| 1098 | } | 1126 | } |
| 1099 | - q = buf; | ||
| 1100 | if (*p == '\"') { | 1127 | if (*p == '\"') { |
| 1101 | p++; | 1128 | p++; |
| 1102 | while (*p != '\0' && *p != '\"') { | 1129 | while (*p != '\0' && *p != '\"') { |
| @@ -1140,8 +1167,8 @@ static int get_str(char *buf, int buf_size, const char **pp) | @@ -1140,8 +1167,8 @@ static int get_str(char *buf, int buf_size, const char **pp) | ||
| 1140 | } | 1167 | } |
| 1141 | p++; | 1168 | p++; |
| 1142 | } | 1169 | } |
| 1143 | - *q = '\0'; | ||
| 1144 | } | 1170 | } |
| 1171 | + *q = '\0'; | ||
| 1145 | *pp = p; | 1172 | *pp = p; |
| 1146 | return 0; | 1173 | return 0; |
| 1147 | } | 1174 | } |
| @@ -1204,6 +1231,7 @@ static void term_handle_command(const char *cmdline) | @@ -1204,6 +1231,7 @@ static void term_handle_command(const char *cmdline) | ||
| 1204 | typestr++; | 1231 | typestr++; |
| 1205 | switch(c) { | 1232 | switch(c) { |
| 1206 | case 'F': | 1233 | case 'F': |
| 1234 | + case 'B': | ||
| 1207 | case 's': | 1235 | case 's': |
| 1208 | { | 1236 | { |
| 1209 | int ret; | 1237 | int ret; |
| @@ -1221,10 +1249,17 @@ static void term_handle_command(const char *cmdline) | @@ -1221,10 +1249,17 @@ static void term_handle_command(const char *cmdline) | ||
| 1221 | } | 1249 | } |
| 1222 | ret = get_str(buf, sizeof(buf), &p); | 1250 | ret = get_str(buf, sizeof(buf), &p); |
| 1223 | if (ret < 0) { | 1251 | if (ret < 0) { |
| 1224 | - if (c == 'F') | 1252 | + switch(c) { |
| 1253 | + case 'F': | ||
| 1225 | term_printf("%s: filename expected\n", cmdname); | 1254 | term_printf("%s: filename expected\n", cmdname); |
| 1226 | - else | 1255 | + break; |
| 1256 | + case 'B': | ||
| 1257 | + term_printf("%s: block device name expected\n", cmdname); | ||
| 1258 | + break; | ||
| 1259 | + default: | ||
| 1227 | term_printf("%s: string expected\n", cmdname); | 1260 | term_printf("%s: string expected\n", cmdname); |
| 1261 | + break; | ||
| 1262 | + } | ||
| 1228 | goto fail; | 1263 | goto fail; |
| 1229 | } | 1264 | } |
| 1230 | str = qemu_malloc(strlen(buf) + 1); | 1265 | str = qemu_malloc(strlen(buf) + 1); |
| @@ -1432,19 +1467,232 @@ static void term_handle_command(const char *cmdline) | @@ -1432,19 +1467,232 @@ static void term_handle_command(const char *cmdline) | ||
| 1432 | return; | 1467 | return; |
| 1433 | } | 1468 | } |
| 1434 | 1469 | ||
| 1435 | -static void term_show_prompt(void) | 1470 | +static void cmd_completion(const char *name, const char *list) |
| 1471 | +{ | ||
| 1472 | + const char *p, *pstart; | ||
| 1473 | + char cmd[128]; | ||
| 1474 | + int len; | ||
| 1475 | + | ||
| 1476 | + p = list; | ||
| 1477 | + for(;;) { | ||
| 1478 | + pstart = p; | ||
| 1479 | + p = strchr(p, '|'); | ||
| 1480 | + if (!p) | ||
| 1481 | + p = pstart + strlen(pstart); | ||
| 1482 | + len = p - pstart; | ||
| 1483 | + if (len > sizeof(cmd) - 2) | ||
| 1484 | + len = sizeof(cmd) - 2; | ||
| 1485 | + memcpy(cmd, pstart, len); | ||
| 1486 | + cmd[len] = '\0'; | ||
| 1487 | + if (name[0] == '\0' || !strncmp(name, cmd, strlen(name))) { | ||
| 1488 | + add_completion(cmd); | ||
| 1489 | + } | ||
| 1490 | + if (*p == '\0') | ||
| 1491 | + break; | ||
| 1492 | + p++; | ||
| 1493 | + } | ||
| 1494 | +} | ||
| 1495 | + | ||
| 1496 | +static void file_completion(const char *input) | ||
| 1497 | +{ | ||
| 1498 | + DIR *ffs; | ||
| 1499 | + struct dirent *d; | ||
| 1500 | + char path[1024]; | ||
| 1501 | + char file[1024], file_prefix[1024]; | ||
| 1502 | + int input_path_len; | ||
| 1503 | + const char *p; | ||
| 1504 | + | ||
| 1505 | + p = strrchr(input, '/'); | ||
| 1506 | + if (!p) { | ||
| 1507 | + input_path_len = 0; | ||
| 1508 | + pstrcpy(file_prefix, sizeof(file_prefix), input); | ||
| 1509 | + strcpy(path, "."); | ||
| 1510 | + } else { | ||
| 1511 | + input_path_len = p - input + 1; | ||
| 1512 | + memcpy(path, input, input_path_len); | ||
| 1513 | + if (input_path_len > sizeof(path) - 1) | ||
| 1514 | + input_path_len = sizeof(path) - 1; | ||
| 1515 | + path[input_path_len] = '\0'; | ||
| 1516 | + pstrcpy(file_prefix, sizeof(file_prefix), p + 1); | ||
| 1517 | + } | ||
| 1518 | +#ifdef DEBUG_COMPLETION | ||
| 1519 | + term_printf("input='%s' path='%s' prefix='%s'\n", input, path, file_prefix); | ||
| 1520 | +#endif | ||
| 1521 | + ffs = opendir(path); | ||
| 1522 | + if (!ffs) | ||
| 1523 | + return; | ||
| 1524 | + for(;;) { | ||
| 1525 | + struct stat sb; | ||
| 1526 | + d = readdir(ffs); | ||
| 1527 | + if (!d) | ||
| 1528 | + break; | ||
| 1529 | + if (strstart(d->d_name, file_prefix, NULL)) { | ||
| 1530 | + memcpy(file, input, input_path_len); | ||
| 1531 | + strcpy(file + input_path_len, d->d_name); | ||
| 1532 | + /* stat the file to find out if it's a directory. | ||
| 1533 | + * In that case add a slash to speed up typing long paths | ||
| 1534 | + */ | ||
| 1535 | + stat(file, &sb); | ||
| 1536 | + if(S_ISDIR(sb.st_mode)) | ||
| 1537 | + strcat(file, "/"); | ||
| 1538 | + add_completion(file); | ||
| 1539 | + } | ||
| 1540 | + } | ||
| 1541 | + closedir(ffs); | ||
| 1542 | +} | ||
| 1543 | + | ||
| 1544 | +static void block_completion_it(void *opaque, const char *name) | ||
| 1545 | +{ | ||
| 1546 | + const char *input = opaque; | ||
| 1547 | + | ||
| 1548 | + if (input[0] == '\0' || | ||
| 1549 | + !strncmp(name, (char *)input, strlen(input))) { | ||
| 1550 | + add_completion(name); | ||
| 1551 | + } | ||
| 1552 | +} | ||
| 1553 | + | ||
| 1554 | +/* NOTE: this parser is an approximate form of the real command parser */ | ||
| 1555 | +static void parse_cmdline(const char *cmdline, | ||
| 1556 | + int *pnb_args, char **args) | ||
| 1557 | +{ | ||
| 1558 | + const char *p; | ||
| 1559 | + int nb_args, ret; | ||
| 1560 | + char buf[1024]; | ||
| 1561 | + | ||
| 1562 | + p = cmdline; | ||
| 1563 | + nb_args = 0; | ||
| 1564 | + for(;;) { | ||
| 1565 | + while (isspace(*p)) | ||
| 1566 | + p++; | ||
| 1567 | + if (*p == '\0') | ||
| 1568 | + break; | ||
| 1569 | + if (nb_args >= MAX_ARGS) | ||
| 1570 | + break; | ||
| 1571 | + ret = get_str(buf, sizeof(buf), &p); | ||
| 1572 | + args[nb_args] = qemu_strdup(buf); | ||
| 1573 | + nb_args++; | ||
| 1574 | + if (ret < 0) | ||
| 1575 | + break; | ||
| 1576 | + } | ||
| 1577 | + *pnb_args = nb_args; | ||
| 1578 | +} | ||
| 1579 | + | ||
| 1580 | +static void find_completion(const char *cmdline) | ||
| 1581 | +{ | ||
| 1582 | + const char *cmdname; | ||
| 1583 | + char *args[MAX_ARGS]; | ||
| 1584 | + int nb_args, i, len; | ||
| 1585 | + const char *ptype, *str; | ||
| 1586 | + term_cmd_t *cmd; | ||
| 1587 | + | ||
| 1588 | + parse_cmdline(cmdline, &nb_args, args); | ||
| 1589 | +#ifdef DEBUG_COMPLETION | ||
| 1590 | + for(i = 0; i < nb_args; i++) { | ||
| 1591 | + term_printf("arg%d = '%s'\n", i, (char *)args[i]); | ||
| 1592 | + } | ||
| 1593 | +#endif | ||
| 1594 | + | ||
| 1595 | + /* if the line ends with a space, it means we want to complete the | ||
| 1596 | + next arg */ | ||
| 1597 | + len = strlen(cmdline); | ||
| 1598 | + if (len > 0 && isspace(cmdline[len - 1])) { | ||
| 1599 | + if (nb_args >= MAX_ARGS) | ||
| 1600 | + return; | ||
| 1601 | + args[nb_args++] = qemu_strdup(""); | ||
| 1602 | + } | ||
| 1603 | + if (nb_args <= 1) { | ||
| 1604 | + /* command completion */ | ||
| 1605 | + if (nb_args == 0) | ||
| 1606 | + cmdname = ""; | ||
| 1607 | + else | ||
| 1608 | + cmdname = args[0]; | ||
| 1609 | + completion_index = strlen(cmdname); | ||
| 1610 | + for(cmd = term_cmds; cmd->name != NULL; cmd++) { | ||
| 1611 | + cmd_completion(cmdname, cmd->name); | ||
| 1612 | + } | ||
| 1613 | + } else { | ||
| 1614 | + /* find the command */ | ||
| 1615 | + for(cmd = term_cmds; cmd->name != NULL; cmd++) { | ||
| 1616 | + if (compare_cmd(args[0], cmd->name)) | ||
| 1617 | + goto found; | ||
| 1618 | + } | ||
| 1619 | + return; | ||
| 1620 | + found: | ||
| 1621 | + ptype = cmd->args_type; | ||
| 1622 | + for(i = 0; i < nb_args - 2; i++) { | ||
| 1623 | + if (*ptype != '\0') { | ||
| 1624 | + ptype++; | ||
| 1625 | + while (*ptype == '?') | ||
| 1626 | + ptype++; | ||
| 1627 | + } | ||
| 1628 | + } | ||
| 1629 | + str = args[nb_args - 1]; | ||
| 1630 | + switch(*ptype) { | ||
| 1631 | + case 'F': | ||
| 1632 | + /* file completion */ | ||
| 1633 | + completion_index = strlen(str); | ||
| 1634 | + file_completion(str); | ||
| 1635 | + break; | ||
| 1636 | + case 'B': | ||
| 1637 | + /* block device name completion */ | ||
| 1638 | + completion_index = strlen(str); | ||
| 1639 | + bdrv_iterate(block_completion_it, (void *)str); | ||
| 1640 | + break; | ||
| 1641 | + default: | ||
| 1642 | + break; | ||
| 1643 | + } | ||
| 1644 | + } | ||
| 1645 | + for(i = 0; i < nb_args; i++) | ||
| 1646 | + qemu_free(args[i]); | ||
| 1647 | +} | ||
| 1648 | + | ||
| 1649 | +static void term_show_prompt2(void) | ||
| 1436 | { | 1650 | { |
| 1437 | term_printf("(qemu) "); | 1651 | term_printf("(qemu) "); |
| 1438 | fflush(stdout); | 1652 | fflush(stdout); |
| 1653 | + term_last_cmd_buf_index = 0; | ||
| 1654 | + term_last_cmd_buf_size = 0; | ||
| 1655 | + term_esc_state = IS_NORM; | ||
| 1656 | +} | ||
| 1657 | + | ||
| 1658 | +static void term_show_prompt(void) | ||
| 1659 | +{ | ||
| 1660 | + term_show_prompt2(); | ||
| 1439 | term_cmd_buf_index = 0; | 1661 | term_cmd_buf_index = 0; |
| 1440 | term_cmd_buf_size = 0; | 1662 | term_cmd_buf_size = 0; |
| 1441 | - term_esc_state = IS_NORM; | ||
| 1442 | } | 1663 | } |
| 1443 | 1664 | ||
| 1444 | -static void term_print_cmdline (const char *cmdline) | 1665 | +/* update the displayed command line */ |
| 1666 | +static void term_update(void) | ||
| 1445 | { | 1667 | { |
| 1446 | - term_show_prompt(); | ||
| 1447 | - term_printf("%s", cmdline); | 1668 | + int i, delta; |
| 1669 | + | ||
| 1670 | + if (term_cmd_buf_size != term_last_cmd_buf_size || | ||
| 1671 | + memcmp(term_cmd_buf, term_last_cmd_buf, term_cmd_buf_size) != 0) { | ||
| 1672 | + for(i = 0; i < term_last_cmd_buf_index; i++) { | ||
| 1673 | + term_printf("\033[D"); | ||
| 1674 | + } | ||
| 1675 | + term_cmd_buf[term_cmd_buf_size] = '\0'; | ||
| 1676 | + term_printf("%s", term_cmd_buf); | ||
| 1677 | + term_printf("\033[K"); | ||
| 1678 | + memcpy(term_last_cmd_buf, term_cmd_buf, term_cmd_buf_size); | ||
| 1679 | + term_last_cmd_buf_size = term_cmd_buf_size; | ||
| 1680 | + term_last_cmd_buf_index = term_cmd_buf_size; | ||
| 1681 | + } | ||
| 1682 | + if (term_cmd_buf_index != term_last_cmd_buf_index) { | ||
| 1683 | + delta = term_cmd_buf_index - term_last_cmd_buf_index; | ||
| 1684 | + if (delta > 0) { | ||
| 1685 | + for(i = 0;i < delta; i++) { | ||
| 1686 | + term_printf("\033[C"); | ||
| 1687 | + } | ||
| 1688 | + } else { | ||
| 1689 | + delta = -delta; | ||
| 1690 | + for(i = 0;i < delta; i++) { | ||
| 1691 | + term_printf("\033[D"); | ||
| 1692 | + } | ||
| 1693 | + } | ||
| 1694 | + term_last_cmd_buf_index = term_cmd_buf_index; | ||
| 1695 | + } | ||
| 1448 | term_flush(); | 1696 | term_flush(); |
| 1449 | } | 1697 | } |
| 1450 | 1698 | ||
| @@ -1456,9 +1704,7 @@ static void term_insert_char(int ch) | @@ -1456,9 +1704,7 @@ static void term_insert_char(int ch) | ||
| 1456 | term_cmd_buf_size - term_cmd_buf_index); | 1704 | term_cmd_buf_size - term_cmd_buf_index); |
| 1457 | term_cmd_buf[term_cmd_buf_index] = ch; | 1705 | term_cmd_buf[term_cmd_buf_index] = ch; |
| 1458 | term_cmd_buf_size++; | 1706 | term_cmd_buf_size++; |
| 1459 | - term_printf("\033[@%c", ch); | ||
| 1460 | term_cmd_buf_index++; | 1707 | term_cmd_buf_index++; |
| 1461 | - term_flush(); | ||
| 1462 | } | 1708 | } |
| 1463 | } | 1709 | } |
| 1464 | 1710 | ||
| @@ -1466,8 +1712,6 @@ static void term_backward_char(void) | @@ -1466,8 +1712,6 @@ static void term_backward_char(void) | ||
| 1466 | { | 1712 | { |
| 1467 | if (term_cmd_buf_index > 0) { | 1713 | if (term_cmd_buf_index > 0) { |
| 1468 | term_cmd_buf_index--; | 1714 | term_cmd_buf_index--; |
| 1469 | - term_printf("\033[D"); | ||
| 1470 | - term_flush(); | ||
| 1471 | } | 1715 | } |
| 1472 | } | 1716 | } |
| 1473 | 1717 | ||
| @@ -1475,8 +1719,6 @@ static void term_forward_char(void) | @@ -1475,8 +1719,6 @@ static void term_forward_char(void) | ||
| 1475 | { | 1719 | { |
| 1476 | if (term_cmd_buf_index < term_cmd_buf_size) { | 1720 | if (term_cmd_buf_index < term_cmd_buf_size) { |
| 1477 | term_cmd_buf_index++; | 1721 | term_cmd_buf_index++; |
| 1478 | - term_printf("\033[C"); | ||
| 1479 | - term_flush(); | ||
| 1480 | } | 1722 | } |
| 1481 | } | 1723 | } |
| 1482 | 1724 | ||
| @@ -1486,9 +1728,7 @@ static void term_delete_char(void) | @@ -1486,9 +1728,7 @@ static void term_delete_char(void) | ||
| 1486 | memmove(term_cmd_buf + term_cmd_buf_index, | 1728 | memmove(term_cmd_buf + term_cmd_buf_index, |
| 1487 | term_cmd_buf + term_cmd_buf_index + 1, | 1729 | term_cmd_buf + term_cmd_buf_index + 1, |
| 1488 | term_cmd_buf_size - term_cmd_buf_index - 1); | 1730 | term_cmd_buf_size - term_cmd_buf_index - 1); |
| 1489 | - term_printf("\033[P"); | ||
| 1490 | term_cmd_buf_size--; | 1731 | term_cmd_buf_size--; |
| 1491 | - term_flush(); | ||
| 1492 | } | 1732 | } |
| 1493 | } | 1733 | } |
| 1494 | 1734 | ||
| @@ -1502,14 +1742,12 @@ static void term_backspace(void) | @@ -1502,14 +1742,12 @@ static void term_backspace(void) | ||
| 1502 | 1742 | ||
| 1503 | static void term_bol(void) | 1743 | static void term_bol(void) |
| 1504 | { | 1744 | { |
| 1505 | - while (term_cmd_buf_index > 0) | ||
| 1506 | - term_backward_char(); | 1745 | + term_cmd_buf_index = 0; |
| 1507 | } | 1746 | } |
| 1508 | 1747 | ||
| 1509 | static void term_eol(void) | 1748 | static void term_eol(void) |
| 1510 | { | 1749 | { |
| 1511 | - while (term_cmd_buf_index < term_cmd_buf_size) | ||
| 1512 | - term_forward_char(); | 1750 | + term_cmd_buf_index = term_cmd_buf_size; |
| 1513 | } | 1751 | } |
| 1514 | 1752 | ||
| 1515 | static void term_up_char(void) | 1753 | static void term_up_char(void) |
| @@ -1530,8 +1768,6 @@ static void term_up_char(void) | @@ -1530,8 +1768,6 @@ static void term_up_char(void) | ||
| 1530 | if (term_hist_entry >= 0) { | 1768 | if (term_hist_entry >= 0) { |
| 1531 | pstrcpy(term_cmd_buf, sizeof(term_cmd_buf), | 1769 | pstrcpy(term_cmd_buf, sizeof(term_cmd_buf), |
| 1532 | term_history[term_hist_entry]); | 1770 | term_history[term_hist_entry]); |
| 1533 | - term_printf("\n"); | ||
| 1534 | - term_print_cmdline(term_cmd_buf); | ||
| 1535 | term_cmd_buf_index = term_cmd_buf_size = strlen(term_cmd_buf); | 1771 | term_cmd_buf_index = term_cmd_buf_size = strlen(term_cmd_buf); |
| 1536 | } | 1772 | } |
| 1537 | } | 1773 | } |
| @@ -1546,8 +1782,6 @@ static void term_down_char(void) | @@ -1546,8 +1782,6 @@ static void term_down_char(void) | ||
| 1546 | } else { | 1782 | } else { |
| 1547 | term_hist_entry = -1; | 1783 | term_hist_entry = -1; |
| 1548 | } | 1784 | } |
| 1549 | - term_printf("\n"); | ||
| 1550 | - term_print_cmdline(term_cmd_buf); | ||
| 1551 | term_cmd_buf_index = term_cmd_buf_size = strlen(term_cmd_buf); | 1785 | term_cmd_buf_index = term_cmd_buf_size = strlen(term_cmd_buf); |
| 1552 | } | 1786 | } |
| 1553 | 1787 | ||
| @@ -1600,6 +1834,67 @@ static void term_hist_add(const char *cmdline) | @@ -1600,6 +1834,67 @@ static void term_hist_add(const char *cmdline) | ||
| 1600 | term_hist_entry = -1; | 1834 | term_hist_entry = -1; |
| 1601 | } | 1835 | } |
| 1602 | 1836 | ||
| 1837 | +/* completion support */ | ||
| 1838 | + | ||
| 1839 | +static void add_completion(const char *str) | ||
| 1840 | +{ | ||
| 1841 | + if (nb_completions < NB_COMPLETIONS_MAX) { | ||
| 1842 | + completions[nb_completions++] = qemu_strdup(str); | ||
| 1843 | + } | ||
| 1844 | +} | ||
| 1845 | + | ||
| 1846 | +static void term_completion(void) | ||
| 1847 | +{ | ||
| 1848 | + int len, i, j, max_width, nb_cols; | ||
| 1849 | + char *cmdline; | ||
| 1850 | + | ||
| 1851 | + nb_completions = 0; | ||
| 1852 | + | ||
| 1853 | + cmdline = qemu_malloc(term_cmd_buf_index + 1); | ||
| 1854 | + if (!cmdline) | ||
| 1855 | + return; | ||
| 1856 | + memcpy(cmdline, term_cmd_buf, term_cmd_buf_index); | ||
| 1857 | + cmdline[term_cmd_buf_index] = '\0'; | ||
| 1858 | + find_completion(cmdline); | ||
| 1859 | + qemu_free(cmdline); | ||
| 1860 | + | ||
| 1861 | + /* no completion found */ | ||
| 1862 | + if (nb_completions <= 0) | ||
| 1863 | + return; | ||
| 1864 | + if (nb_completions == 1) { | ||
| 1865 | + len = strlen(completions[0]); | ||
| 1866 | + for(i = completion_index; i < len; i++) { | ||
| 1867 | + term_insert_char(completions[0][i]); | ||
| 1868 | + } | ||
| 1869 | + /* extra space for next argument. XXX: make it more generic */ | ||
| 1870 | + if (len > 0 && completions[0][len - 1] != '/') | ||
| 1871 | + term_insert_char(' '); | ||
| 1872 | + } else { | ||
| 1873 | + term_printf("\n"); | ||
| 1874 | + max_width = 0; | ||
| 1875 | + for(i = 0; i < nb_completions; i++) { | ||
| 1876 | + len = strlen(completions[i]); | ||
| 1877 | + if (len > max_width) | ||
| 1878 | + max_width = len; | ||
| 1879 | + } | ||
| 1880 | + max_width += 2; | ||
| 1881 | + if (max_width < 10) | ||
| 1882 | + max_width = 10; | ||
| 1883 | + else if (max_width > 80) | ||
| 1884 | + max_width = 80; | ||
| 1885 | + nb_cols = 80 / max_width; | ||
| 1886 | + j = 0; | ||
| 1887 | + for(i = 0; i < nb_completions; i++) { | ||
| 1888 | + term_printf("%-*s", max_width, completions[i]); | ||
| 1889 | + if (++j == nb_cols || i == (nb_completions - 1)) { | ||
| 1890 | + term_printf("\n"); | ||
| 1891 | + j = 0; | ||
| 1892 | + } | ||
| 1893 | + } | ||
| 1894 | + term_show_prompt2(); | ||
| 1895 | + } | ||
| 1896 | +} | ||
| 1897 | + | ||
| 1603 | /* return true if command handled */ | 1898 | /* return true if command handled */ |
| 1604 | static void term_handle_byte(int ch) | 1899 | static void term_handle_byte(int ch) |
| 1605 | { | 1900 | { |
| @@ -1609,9 +1904,15 @@ static void term_handle_byte(int ch) | @@ -1609,9 +1904,15 @@ static void term_handle_byte(int ch) | ||
| 1609 | case 1: | 1904 | case 1: |
| 1610 | term_bol(); | 1905 | term_bol(); |
| 1611 | break; | 1906 | break; |
| 1907 | + case 4: | ||
| 1908 | + term_delete_char(); | ||
| 1909 | + break; | ||
| 1612 | case 5: | 1910 | case 5: |
| 1613 | term_eol(); | 1911 | term_eol(); |
| 1614 | break; | 1912 | break; |
| 1913 | + case 9: | ||
| 1914 | + term_completion(); | ||
| 1915 | + break; | ||
| 1615 | case 10: | 1916 | case 10: |
| 1616 | case 13: | 1917 | case 13: |
| 1617 | term_cmd_buf[term_cmd_buf_size] = '\0'; | 1918 | term_cmd_buf[term_cmd_buf_size] = '\0'; |
| @@ -1684,104 +1985,29 @@ static void term_handle_byte(int ch) | @@ -1684,104 +1985,29 @@ static void term_handle_byte(int ch) | ||
| 1684 | the_end: | 1985 | the_end: |
| 1685 | break; | 1986 | break; |
| 1686 | } | 1987 | } |
| 1687 | -} | ||
| 1688 | - | ||
| 1689 | -/*************************************************************/ | ||
| 1690 | -/* serial console support */ | ||
| 1691 | - | ||
| 1692 | -#define TERM_ESCAPE 0x01 /* ctrl-a is used for escape */ | ||
| 1693 | - | ||
| 1694 | -static int term_got_escape, term_command; | ||
| 1695 | - | ||
| 1696 | -void term_print_help(void) | ||
| 1697 | -{ | ||
| 1698 | - term_printf("\n" | ||
| 1699 | - "C-a h print this help\n" | ||
| 1700 | - "C-a x exit emulatior\n" | ||
| 1701 | - "C-a s save disk data back to file (if -snapshot)\n" | ||
| 1702 | - "C-a b send break (magic sysrq)\n" | ||
| 1703 | - "C-a c switch between console and monitor\n" | ||
| 1704 | - "C-a C-a send C-a\n" | ||
| 1705 | - ); | ||
| 1706 | -} | ||
| 1707 | - | ||
| 1708 | -/* called when a char is received */ | ||
| 1709 | -static void term_received_byte(int ch) | ||
| 1710 | -{ | ||
| 1711 | - if (!serial_console) { | ||
| 1712 | - /* if no serial console, handle every command */ | ||
| 1713 | - term_handle_byte(ch); | ||
| 1714 | - } else { | ||
| 1715 | - if (term_got_escape) { | ||
| 1716 | - term_got_escape = 0; | ||
| 1717 | - switch(ch) { | ||
| 1718 | - case 'h': | ||
| 1719 | - term_print_help(); | ||
| 1720 | - break; | ||
| 1721 | - case 'x': | ||
| 1722 | - exit(0); | ||
| 1723 | - break; | ||
| 1724 | - case 's': | ||
| 1725 | - { | ||
| 1726 | - int i; | ||
| 1727 | - for (i = 0; i < MAX_DISKS; i++) { | ||
| 1728 | - if (bs_table[i]) | ||
| 1729 | - bdrv_commit(bs_table[i]); | ||
| 1730 | - } | ||
| 1731 | - } | ||
| 1732 | - break; | ||
| 1733 | - case 'b': | ||
| 1734 | - if (serial_console) | ||
| 1735 | - serial_receive_break(serial_console); | ||
| 1736 | - break; | ||
| 1737 | - case 'c': | ||
| 1738 | - if (!term_command) { | ||
| 1739 | - term_show_prompt(); | ||
| 1740 | - term_command = 1; | ||
| 1741 | - } else { | ||
| 1742 | - term_command = 0; | ||
| 1743 | - } | ||
| 1744 | - break; | ||
| 1745 | - case TERM_ESCAPE: | ||
| 1746 | - goto send_char; | ||
| 1747 | - } | ||
| 1748 | - } else if (ch == TERM_ESCAPE) { | ||
| 1749 | - term_got_escape = 1; | ||
| 1750 | - } else { | ||
| 1751 | - send_char: | ||
| 1752 | - if (term_command) { | ||
| 1753 | - term_handle_byte(ch); | ||
| 1754 | - } else { | ||
| 1755 | - if (serial_console) | ||
| 1756 | - serial_receive_byte(serial_console, ch); | ||
| 1757 | - } | ||
| 1758 | - } | ||
| 1759 | - } | 1988 | + term_update(); |
| 1760 | } | 1989 | } |
| 1761 | 1990 | ||
| 1762 | static int term_can_read(void *opaque) | 1991 | static int term_can_read(void *opaque) |
| 1763 | { | 1992 | { |
| 1764 | - if (serial_console) { | ||
| 1765 | - return serial_can_receive(serial_console); | ||
| 1766 | - } else { | ||
| 1767 | - return 128; | ||
| 1768 | - } | 1993 | + return 128; |
| 1769 | } | 1994 | } |
| 1770 | 1995 | ||
| 1771 | static void term_read(void *opaque, const uint8_t *buf, int size) | 1996 | static void term_read(void *opaque, const uint8_t *buf, int size) |
| 1772 | { | 1997 | { |
| 1773 | int i; | 1998 | int i; |
| 1774 | for(i = 0; i < size; i++) | 1999 | for(i = 0; i < size; i++) |
| 1775 | - term_received_byte(buf[i]); | 2000 | + term_handle_byte(buf[i]); |
| 1776 | } | 2001 | } |
| 1777 | 2002 | ||
| 1778 | -void monitor_init(void) | 2003 | +void monitor_init(CharDriverState *hd, int show_banner) |
| 1779 | { | 2004 | { |
| 1780 | - if (!serial_console) { | 2005 | + monitor_hd = hd; |
| 2006 | + if (show_banner) { | ||
| 1781 | term_printf("QEMU %s monitor - type 'help' for more information\n", | 2007 | term_printf("QEMU %s monitor - type 'help' for more information\n", |
| 1782 | QEMU_VERSION); | 2008 | QEMU_VERSION); |
| 1783 | term_show_prompt(); | 2009 | term_show_prompt(); |
| 1784 | } | 2010 | } |
| 1785 | term_hist_entry = -1; | 2011 | term_hist_entry = -1; |
| 1786 | - qemu_add_fd_read_handler(0, term_can_read, term_read, NULL); | 2012 | + qemu_chr_add_read_handler(hd, term_can_read, term_read, NULL); |
| 1787 | } | 2013 | } |