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 | 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 | 482 | void bdrv_info(void) |
| 474 | 483 | { |
| 475 | 484 | BlockDriverState *bs; | ... | ... |
monitor.c
| ... | ... | @@ -23,8 +23,10 @@ |
| 23 | 23 | */ |
| 24 | 24 | #include "vl.h" |
| 25 | 25 | #include "disas.h" |
| 26 | +#include <dirent.h> | |
| 26 | 27 | |
| 27 | 28 | //#define DEBUG |
| 29 | +//#define DEBUG_COMPLETION | |
| 28 | 30 | |
| 29 | 31 | #ifndef offsetof |
| 30 | 32 | #define offsetof(type, field) ((size_t) &((type *)0)->field) |
| ... | ... | @@ -32,6 +34,7 @@ |
| 32 | 34 | |
| 33 | 35 | #define TERM_CMD_BUF_SIZE 4095 |
| 34 | 36 | #define TERM_MAX_CMDS 64 |
| 37 | +#define NB_COMPLETIONS_MAX 256 | |
| 35 | 38 | |
| 36 | 39 | #define IS_NORM 0 |
| 37 | 40 | #define IS_ESC 1 |
| ... | ... | @@ -42,16 +45,28 @@ |
| 42 | 45 | static char term_cmd_buf[TERM_CMD_BUF_SIZE + 1]; |
| 43 | 46 | static int term_cmd_buf_index; |
| 44 | 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 | 53 | static int term_esc_state; |
| 46 | 54 | static int term_esc_param; |
| 47 | 55 | |
| 48 | 56 | static char *term_history[TERM_MAX_CMDS]; |
| 49 | 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 | 66 | * Supported types: |
| 53 | 67 | * |
| 54 | 68 | * 'F' filename |
| 69 | + * 'B' block device name | |
| 55 | 70 | * 's' string (accept optional quote) |
| 56 | 71 | * 'i' integer |
| 57 | 72 | * '/' optional gdb-like print format (like "/10x") |
| ... | ... | @@ -71,17 +86,20 @@ typedef struct term_cmd_t { |
| 71 | 86 | static term_cmd_t term_cmds[]; |
| 72 | 87 | static term_cmd_t info_cmds[]; |
| 73 | 88 | |
| 89 | +static void add_completion(const char *str); | |
| 90 | + | |
| 74 | 91 | void term_printf(const char *fmt, ...) |
| 75 | 92 | { |
| 93 | + char buf[4096]; | |
| 76 | 94 | va_list ap; |
| 77 | 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 | 98 | va_end(ap); |
| 80 | 99 | } |
| 81 | 100 | |
| 82 | 101 | void term_flush(void) |
| 83 | 102 | { |
| 84 | - fflush(stdout); | |
| 85 | 103 | } |
| 86 | 104 | |
| 87 | 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 | 250 | { |
| 233 | 251 | BlockDriverState *bs; |
| 234 | 252 | |
| 235 | - term_printf("%d %s\n", force, filename); | |
| 236 | - | |
| 237 | 253 | bs = bdrv_find(filename); |
| 238 | 254 | if (!bs) { |
| 239 | 255 | term_printf("device not found\n"); |
| ... | ... | @@ -674,9 +690,9 @@ static term_cmd_t term_cmds[] = { |
| 674 | 690 | "subcommand", "show various information about the system state" }, |
| 675 | 691 | { "q|quit", "", do_quit, |
| 676 | 692 | "", "quit the emulator" }, |
| 677 | - { "eject", "-fs", do_eject, | |
| 693 | + { "eject", "-fB", do_eject, | |
| 678 | 694 | "[-f] device", "eject a removable media (use -f to force it)" }, |
| 679 | - { "change", "sF", do_change, | |
| 695 | + { "change", "BF", do_change, | |
| 680 | 696 | "device filename", "change a removable media" }, |
| 681 | 697 | { "screendump", "F", do_screen_dump, |
| 682 | 698 | "filename", "save screen into PPM image 'filename'" }, |
| ... | ... | @@ -953,6 +969,16 @@ static int expr_unary(void) |
| 953 | 969 | } |
| 954 | 970 | next(); |
| 955 | 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 | 982 | case '$': |
| 957 | 983 | { |
| 958 | 984 | char buf[128], *q; |
| ... | ... | @@ -1088,15 +1114,16 @@ static int get_str(char *buf, int buf_size, const char **pp) |
| 1088 | 1114 | char *q; |
| 1089 | 1115 | int c; |
| 1090 | 1116 | |
| 1117 | + q = buf; | |
| 1091 | 1118 | p = *pp; |
| 1092 | 1119 | while (isspace(*p)) |
| 1093 | 1120 | p++; |
| 1094 | 1121 | if (*p == '\0') { |
| 1095 | 1122 | fail: |
| 1123 | + *q = '\0'; | |
| 1096 | 1124 | *pp = p; |
| 1097 | 1125 | return -1; |
| 1098 | 1126 | } |
| 1099 | - q = buf; | |
| 1100 | 1127 | if (*p == '\"') { |
| 1101 | 1128 | p++; |
| 1102 | 1129 | while (*p != '\0' && *p != '\"') { |
| ... | ... | @@ -1140,8 +1167,8 @@ static int get_str(char *buf, int buf_size, const char **pp) |
| 1140 | 1167 | } |
| 1141 | 1168 | p++; |
| 1142 | 1169 | } |
| 1143 | - *q = '\0'; | |
| 1144 | 1170 | } |
| 1171 | + *q = '\0'; | |
| 1145 | 1172 | *pp = p; |
| 1146 | 1173 | return 0; |
| 1147 | 1174 | } |
| ... | ... | @@ -1204,6 +1231,7 @@ static void term_handle_command(const char *cmdline) |
| 1204 | 1231 | typestr++; |
| 1205 | 1232 | switch(c) { |
| 1206 | 1233 | case 'F': |
| 1234 | + case 'B': | |
| 1207 | 1235 | case 's': |
| 1208 | 1236 | { |
| 1209 | 1237 | int ret; |
| ... | ... | @@ -1221,10 +1249,17 @@ static void term_handle_command(const char *cmdline) |
| 1221 | 1249 | } |
| 1222 | 1250 | ret = get_str(buf, sizeof(buf), &p); |
| 1223 | 1251 | if (ret < 0) { |
| 1224 | - if (c == 'F') | |
| 1252 | + switch(c) { | |
| 1253 | + case 'F': | |
| 1225 | 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 | 1260 | term_printf("%s: string expected\n", cmdname); |
| 1261 | + break; | |
| 1262 | + } | |
| 1228 | 1263 | goto fail; |
| 1229 | 1264 | } |
| 1230 | 1265 | str = qemu_malloc(strlen(buf) + 1); |
| ... | ... | @@ -1432,19 +1467,232 @@ static void term_handle_command(const char *cmdline) |
| 1432 | 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 | 1651 | term_printf("(qemu) "); |
| 1438 | 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 | 1661 | term_cmd_buf_index = 0; |
| 1440 | 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 | 1696 | term_flush(); |
| 1449 | 1697 | } |
| 1450 | 1698 | |
| ... | ... | @@ -1456,9 +1704,7 @@ static void term_insert_char(int ch) |
| 1456 | 1704 | term_cmd_buf_size - term_cmd_buf_index); |
| 1457 | 1705 | term_cmd_buf[term_cmd_buf_index] = ch; |
| 1458 | 1706 | term_cmd_buf_size++; |
| 1459 | - term_printf("\033[@%c", ch); | |
| 1460 | 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 | 1712 | { |
| 1467 | 1713 | if (term_cmd_buf_index > 0) { |
| 1468 | 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 | 1719 | { |
| 1476 | 1720 | if (term_cmd_buf_index < term_cmd_buf_size) { |
| 1477 | 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 | 1728 | memmove(term_cmd_buf + term_cmd_buf_index, |
| 1487 | 1729 | term_cmd_buf + term_cmd_buf_index + 1, |
| 1488 | 1730 | term_cmd_buf_size - term_cmd_buf_index - 1); |
| 1489 | - term_printf("\033[P"); | |
| 1490 | 1731 | term_cmd_buf_size--; |
| 1491 | - term_flush(); | |
| 1492 | 1732 | } |
| 1493 | 1733 | } |
| 1494 | 1734 | |
| ... | ... | @@ -1502,14 +1742,12 @@ static void term_backspace(void) |
| 1502 | 1742 | |
| 1503 | 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 | 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 | 1753 | static void term_up_char(void) |
| ... | ... | @@ -1530,8 +1768,6 @@ static void term_up_char(void) |
| 1530 | 1768 | if (term_hist_entry >= 0) { |
| 1531 | 1769 | pstrcpy(term_cmd_buf, sizeof(term_cmd_buf), |
| 1532 | 1770 | term_history[term_hist_entry]); |
| 1533 | - term_printf("\n"); | |
| 1534 | - term_print_cmdline(term_cmd_buf); | |
| 1535 | 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 | 1782 | } else { |
| 1547 | 1783 | term_hist_entry = -1; |
| 1548 | 1784 | } |
| 1549 | - term_printf("\n"); | |
| 1550 | - term_print_cmdline(term_cmd_buf); | |
| 1551 | 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 | 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 | 1898 | /* return true if command handled */ |
| 1604 | 1899 | static void term_handle_byte(int ch) |
| 1605 | 1900 | { |
| ... | ... | @@ -1609,9 +1904,15 @@ static void term_handle_byte(int ch) |
| 1609 | 1904 | case 1: |
| 1610 | 1905 | term_bol(); |
| 1611 | 1906 | break; |
| 1907 | + case 4: | |
| 1908 | + term_delete_char(); | |
| 1909 | + break; | |
| 1612 | 1910 | case 5: |
| 1613 | 1911 | term_eol(); |
| 1614 | 1912 | break; |
| 1913 | + case 9: | |
| 1914 | + term_completion(); | |
| 1915 | + break; | |
| 1615 | 1916 | case 10: |
| 1616 | 1917 | case 13: |
| 1617 | 1918 | term_cmd_buf[term_cmd_buf_size] = '\0'; |
| ... | ... | @@ -1684,104 +1985,29 @@ static void term_handle_byte(int ch) |
| 1684 | 1985 | the_end: |
| 1685 | 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 | 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 | 1996 | static void term_read(void *opaque, const uint8_t *buf, int size) |
| 1772 | 1997 | { |
| 1773 | 1998 | int i; |
| 1774 | 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 | 2007 | term_printf("QEMU %s monitor - type 'help' for more information\n", |
| 1782 | 2008 | QEMU_VERSION); |
| 1783 | 2009 | term_show_prompt(); |
| 1784 | 2010 | } |
| 1785 | 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 | } | ... | ... |