Commit 7e2515e87c41e2e658aaed466e11cbdf1ea8bcb1
1 parent
3d2cfdf1
separated readline from monitor code - added password input support - added output buffer
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1034 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
2 changed files
with
521 additions
and
385 deletions
monitor.c
| ... | ... | @@ -32,36 +32,6 @@ |
| 32 | 32 | #define offsetof(type, field) ((size_t) &((type *)0)->field) |
| 33 | 33 | #endif |
| 34 | 34 | |
| 35 | -#define TERM_CMD_BUF_SIZE 4095 | |
| 36 | -#define TERM_MAX_CMDS 64 | |
| 37 | -#define NB_COMPLETIONS_MAX 256 | |
| 38 | - | |
| 39 | -#define IS_NORM 0 | |
| 40 | -#define IS_ESC 1 | |
| 41 | -#define IS_CSI 2 | |
| 42 | - | |
| 43 | -#define printf do_not_use_printf | |
| 44 | - | |
| 45 | -static char term_cmd_buf[TERM_CMD_BUF_SIZE + 1]; | |
| 46 | -static int term_cmd_buf_index; | |
| 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 | - | |
| 53 | -static int term_esc_state; | |
| 54 | -static int term_esc_param; | |
| 55 | - | |
| 56 | -static char *term_history[TERM_MAX_CMDS]; | |
| 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 | - | |
| 64 | - | |
| 65 | 35 | /* |
| 66 | 36 | * Supported types: |
| 67 | 37 | * |
| ... | ... | @@ -83,23 +53,52 @@ typedef struct term_cmd_t { |
| 83 | 53 | const char *help; |
| 84 | 54 | } term_cmd_t; |
| 85 | 55 | |
| 56 | +static CharDriverState *monitor_hd; | |
| 57 | + | |
| 86 | 58 | static term_cmd_t term_cmds[]; |
| 87 | 59 | static term_cmd_t info_cmds[]; |
| 88 | 60 | |
| 89 | -static void add_completion(const char *str); | |
| 61 | +static char term_outbuf[1024]; | |
| 62 | +static int term_outbuf_index; | |
| 90 | 63 | |
| 91 | -void term_printf(const char *fmt, ...) | |
| 64 | +static void monitor_start_input(void); | |
| 65 | + | |
| 66 | +void term_flush(void) | |
| 67 | +{ | |
| 68 | + if (term_outbuf_index > 0) { | |
| 69 | + qemu_chr_write(monitor_hd, term_outbuf, term_outbuf_index); | |
| 70 | + term_outbuf_index = 0; | |
| 71 | + } | |
| 72 | +} | |
| 73 | + | |
| 74 | +/* flush at every end of line or if the buffer is full */ | |
| 75 | +void term_puts(const char *str) | |
| 76 | +{ | |
| 77 | + int c; | |
| 78 | + for(;;) { | |
| 79 | + c = *str++; | |
| 80 | + if (c == '\0') | |
| 81 | + break; | |
| 82 | + term_outbuf[term_outbuf_index++] = c; | |
| 83 | + if (term_outbuf_index >= sizeof(term_outbuf) || | |
| 84 | + c == '\n') | |
| 85 | + term_flush(); | |
| 86 | + } | |
| 87 | +} | |
| 88 | + | |
| 89 | +void term_vprintf(const char *fmt, va_list ap) | |
| 92 | 90 | { |
| 93 | 91 | char buf[4096]; |
| 94 | - va_list ap; | |
| 95 | - va_start(ap, fmt); | |
| 96 | 92 | vsnprintf(buf, sizeof(buf), fmt, ap); |
| 97 | - qemu_chr_write(monitor_hd, buf, strlen(buf)); | |
| 98 | - va_end(ap); | |
| 93 | + term_puts(buf); | |
| 99 | 94 | } |
| 100 | 95 | |
| 101 | -void term_flush(void) | |
| 96 | +void term_printf(const char *fmt, ...) | |
| 102 | 97 | { |
| 98 | + va_list ap; | |
| 99 | + va_start(ap, fmt); | |
| 100 | + term_vprintf(fmt, ap); | |
| 101 | + va_end(ap); | |
| 103 | 102 | } |
| 104 | 103 | |
| 105 | 104 | static int compare_cmd(const char *name, const char *list) |
| ... | ... | @@ -159,8 +158,9 @@ static void do_commit(void) |
| 159 | 158 | int i; |
| 160 | 159 | |
| 161 | 160 | for (i = 0; i < MAX_DISKS; i++) { |
| 162 | - if (bs_table[i]) | |
| 161 | + if (bs_table[i]) { | |
| 163 | 162 | bdrv_commit(bs_table[i]); |
| 163 | + } | |
| 164 | 164 | } |
| 165 | 165 | } |
| 166 | 166 | |
| ... | ... | @@ -215,11 +215,14 @@ static void do_info_registers(void) |
| 215 | 215 | static void do_info_history (void) |
| 216 | 216 | { |
| 217 | 217 | int i; |
| 218 | - | |
| 219 | - for (i = 0; i < TERM_MAX_CMDS; i++) { | |
| 220 | - if (term_history[i] == NULL) | |
| 221 | - break; | |
| 222 | - term_printf("%d: '%s'\n", i, term_history[i]); | |
| 218 | + const char *str; | |
| 219 | + | |
| 220 | + i = 0; | |
| 221 | + for(;;) { | |
| 222 | + str = readline_get_history(i); | |
| 223 | + if (!str) | |
| 224 | + break; | |
| 225 | + term_printf("%d: '%s'\n", i, str); | |
| 223 | 226 | } |
| 224 | 227 | } |
| 225 | 228 | |
| ... | ... | @@ -261,6 +264,8 @@ static void do_eject(int force, const char *filename) |
| 261 | 264 | static void do_change(const char *device, const char *filename) |
| 262 | 265 | { |
| 263 | 266 | BlockDriverState *bs; |
| 267 | + int i; | |
| 268 | + char password[256]; | |
| 264 | 269 | |
| 265 | 270 | bs = bdrv_find(device); |
| 266 | 271 | if (!bs) { |
| ... | ... | @@ -270,6 +275,15 @@ static void do_change(const char *device, const char *filename) |
| 270 | 275 | if (eject_device(bs, 0) < 0) |
| 271 | 276 | return; |
| 272 | 277 | bdrv_open(bs, filename, 0); |
| 278 | + if (bdrv_is_encrypted(bs)) { | |
| 279 | + term_printf("%s is encrypted.\n", device); | |
| 280 | + for(i = 0; i < 3; i++) { | |
| 281 | + monitor_readline("Password: ", 1, password, sizeof(password)); | |
| 282 | + if (bdrv_set_key(bs, password) == 0) | |
| 283 | + break; | |
| 284 | + term_printf("invalid password\n"); | |
| 285 | + } | |
| 286 | + } | |
| 273 | 287 | } |
| 274 | 288 | |
| 275 | 289 | static void do_screen_dump(const char *filename) |
| ... | ... | @@ -1178,7 +1192,7 @@ static int default_fmt_size = 4; |
| 1178 | 1192 | |
| 1179 | 1193 | #define MAX_ARGS 16 |
| 1180 | 1194 | |
| 1181 | -static void term_handle_command(const char *cmdline) | |
| 1195 | +static void monitor_handle_command(const char *cmdline) | |
| 1182 | 1196 | { |
| 1183 | 1197 | const char *p, *pstart, *typestr; |
| 1184 | 1198 | char *q; |
| ... | ... | @@ -1577,7 +1591,7 @@ static void parse_cmdline(const char *cmdline, |
| 1577 | 1591 | *pnb_args = nb_args; |
| 1578 | 1592 | } |
| 1579 | 1593 | |
| 1580 | -static void find_completion(const char *cmdline) | |
| 1594 | +void readline_find_completion(const char *cmdline) | |
| 1581 | 1595 | { |
| 1582 | 1596 | const char *cmdname; |
| 1583 | 1597 | char *args[MAX_ARGS]; |
| ... | ... | @@ -1646,368 +1660,65 @@ static void find_completion(const char *cmdline) |
| 1646 | 1660 | qemu_free(args[i]); |
| 1647 | 1661 | } |
| 1648 | 1662 | |
| 1649 | -static void term_show_prompt2(void) | |
| 1650 | -{ | |
| 1651 | - term_printf("(qemu) "); | |
| 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(); | |
| 1661 | - term_cmd_buf_index = 0; | |
| 1662 | - term_cmd_buf_size = 0; | |
| 1663 | -} | |
| 1664 | - | |
| 1665 | -/* update the displayed command line */ | |
| 1666 | -static void term_update(void) | |
| 1667 | -{ | |
| 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 | - } | |
| 1696 | - term_flush(); | |
| 1697 | -} | |
| 1698 | - | |
| 1699 | -static void term_insert_char(int ch) | |
| 1700 | -{ | |
| 1701 | - if (term_cmd_buf_index < TERM_CMD_BUF_SIZE) { | |
| 1702 | - memmove(term_cmd_buf + term_cmd_buf_index + 1, | |
| 1703 | - term_cmd_buf + term_cmd_buf_index, | |
| 1704 | - term_cmd_buf_size - term_cmd_buf_index); | |
| 1705 | - term_cmd_buf[term_cmd_buf_index] = ch; | |
| 1706 | - term_cmd_buf_size++; | |
| 1707 | - term_cmd_buf_index++; | |
| 1708 | - } | |
| 1709 | -} | |
| 1710 | - | |
| 1711 | -static void term_backward_char(void) | |
| 1712 | -{ | |
| 1713 | - if (term_cmd_buf_index > 0) { | |
| 1714 | - term_cmd_buf_index--; | |
| 1715 | - } | |
| 1716 | -} | |
| 1717 | - | |
| 1718 | -static void term_forward_char(void) | |
| 1719 | -{ | |
| 1720 | - if (term_cmd_buf_index < term_cmd_buf_size) { | |
| 1721 | - term_cmd_buf_index++; | |
| 1722 | - } | |
| 1723 | -} | |
| 1724 | - | |
| 1725 | -static void term_delete_char(void) | |
| 1726 | -{ | |
| 1727 | - if (term_cmd_buf_index < term_cmd_buf_size) { | |
| 1728 | - memmove(term_cmd_buf + term_cmd_buf_index, | |
| 1729 | - term_cmd_buf + term_cmd_buf_index + 1, | |
| 1730 | - term_cmd_buf_size - term_cmd_buf_index - 1); | |
| 1731 | - term_cmd_buf_size--; | |
| 1732 | - } | |
| 1733 | -} | |
| 1734 | - | |
| 1735 | -static void term_backspace(void) | |
| 1663 | +static int term_can_read(void *opaque) | |
| 1736 | 1664 | { |
| 1737 | - if (term_cmd_buf_index > 0) { | |
| 1738 | - term_backward_char(); | |
| 1739 | - term_delete_char(); | |
| 1740 | - } | |
| 1665 | + return 128; | |
| 1741 | 1666 | } |
| 1742 | 1667 | |
| 1743 | -static void term_bol(void) | |
| 1668 | +static void term_read(void *opaque, const uint8_t *buf, int size) | |
| 1744 | 1669 | { |
| 1745 | - term_cmd_buf_index = 0; | |
| 1670 | + int i; | |
| 1671 | + for(i = 0; i < size; i++) | |
| 1672 | + readline_handle_byte(buf[i]); | |
| 1746 | 1673 | } |
| 1747 | 1674 | |
| 1748 | -static void term_eol(void) | |
| 1749 | -{ | |
| 1750 | - term_cmd_buf_index = term_cmd_buf_size; | |
| 1751 | -} | |
| 1675 | +static void monitor_start_input(void); | |
| 1752 | 1676 | |
| 1753 | -static void term_up_char(void) | |
| 1677 | +static void monitor_handle_command1(void *opaque, const char *cmdline) | |
| 1754 | 1678 | { |
| 1755 | - int idx; | |
| 1756 | - | |
| 1757 | - if (term_hist_entry == 0) | |
| 1758 | - return; | |
| 1759 | - if (term_hist_entry == -1) { | |
| 1760 | - /* Find latest entry */ | |
| 1761 | - for (idx = 0; idx < TERM_MAX_CMDS; idx++) { | |
| 1762 | - if (term_history[idx] == NULL) | |
| 1763 | - break; | |
| 1764 | - } | |
| 1765 | - term_hist_entry = idx; | |
| 1766 | - } | |
| 1767 | - term_hist_entry--; | |
| 1768 | - if (term_hist_entry >= 0) { | |
| 1769 | - pstrcpy(term_cmd_buf, sizeof(term_cmd_buf), | |
| 1770 | - term_history[term_hist_entry]); | |
| 1771 | - term_cmd_buf_index = term_cmd_buf_size = strlen(term_cmd_buf); | |
| 1772 | - } | |
| 1679 | + monitor_handle_command(cmdline); | |
| 1680 | + monitor_start_input(); | |
| 1773 | 1681 | } |
| 1774 | 1682 | |
| 1775 | -static void term_down_char(void) | |
| 1683 | +static void monitor_start_input(void) | |
| 1776 | 1684 | { |
| 1777 | - if (term_hist_entry == TERM_MAX_CMDS - 1 || term_hist_entry == -1) | |
| 1778 | - return; | |
| 1779 | - if (term_history[++term_hist_entry] != NULL) { | |
| 1780 | - pstrcpy(term_cmd_buf, sizeof(term_cmd_buf), | |
| 1781 | - term_history[term_hist_entry]); | |
| 1782 | - } else { | |
| 1783 | - term_hist_entry = -1; | |
| 1784 | - } | |
| 1785 | - term_cmd_buf_index = term_cmd_buf_size = strlen(term_cmd_buf); | |
| 1685 | + readline_start("(qemu) ", 0, monitor_handle_command1, NULL); | |
| 1786 | 1686 | } |
| 1787 | 1687 | |
| 1788 | -static void term_hist_add(const char *cmdline) | |
| 1688 | +void monitor_init(CharDriverState *hd, int show_banner) | |
| 1789 | 1689 | { |
| 1790 | - char *hist_entry, *new_entry; | |
| 1791 | - int idx; | |
| 1792 | - | |
| 1793 | - if (cmdline[0] == '\0') | |
| 1794 | - return; | |
| 1795 | - new_entry = NULL; | |
| 1796 | - if (term_hist_entry != -1) { | |
| 1797 | - /* We were editing an existing history entry: replace it */ | |
| 1798 | - hist_entry = term_history[term_hist_entry]; | |
| 1799 | - idx = term_hist_entry; | |
| 1800 | - if (strcmp(hist_entry, cmdline) == 0) { | |
| 1801 | - goto same_entry; | |
| 1802 | - } | |
| 1803 | - } | |
| 1804 | - /* Search cmdline in history buffers */ | |
| 1805 | - for (idx = 0; idx < TERM_MAX_CMDS; idx++) { | |
| 1806 | - hist_entry = term_history[idx]; | |
| 1807 | - if (hist_entry == NULL) | |
| 1808 | - break; | |
| 1809 | - if (strcmp(hist_entry, cmdline) == 0) { | |
| 1810 | - same_entry: | |
| 1811 | - new_entry = hist_entry; | |
| 1812 | - /* Put this entry at the end of history */ | |
| 1813 | - memmove(&term_history[idx], &term_history[idx + 1], | |
| 1814 | - &term_history[TERM_MAX_CMDS] - &term_history[idx + 1]); | |
| 1815 | - term_history[TERM_MAX_CMDS - 1] = NULL; | |
| 1816 | - for (; idx < TERM_MAX_CMDS; idx++) { | |
| 1817 | - if (term_history[idx] == NULL) | |
| 1818 | - break; | |
| 1819 | - } | |
| 1820 | - break; | |
| 1821 | - } | |
| 1822 | - } | |
| 1823 | - if (idx == TERM_MAX_CMDS) { | |
| 1824 | - /* Need to get one free slot */ | |
| 1825 | - free(term_history[0]); | |
| 1826 | - memcpy(term_history, &term_history[1], | |
| 1827 | - &term_history[TERM_MAX_CMDS] - &term_history[1]); | |
| 1828 | - term_history[TERM_MAX_CMDS - 1] = NULL; | |
| 1829 | - idx = TERM_MAX_CMDS - 1; | |
| 1690 | + monitor_hd = hd; | |
| 1691 | + if (show_banner) { | |
| 1692 | + term_printf("QEMU %s monitor - type 'help' for more information\n", | |
| 1693 | + QEMU_VERSION); | |
| 1830 | 1694 | } |
| 1831 | - if (new_entry == NULL) | |
| 1832 | - new_entry = strdup(cmdline); | |
| 1833 | - term_history[idx] = new_entry; | |
| 1834 | - term_hist_entry = -1; | |
| 1695 | + qemu_chr_add_read_handler(hd, term_can_read, term_read, NULL); | |
| 1696 | + monitor_start_input(); | |
| 1835 | 1697 | } |
| 1836 | 1698 | |
| 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 | -} | |
| 1699 | +/* XXX: use threads ? */ | |
| 1700 | +/* modal monitor readline */ | |
| 1701 | +static int monitor_readline_started; | |
| 1702 | +static char *monitor_readline_buf; | |
| 1703 | +static int monitor_readline_buf_size; | |
| 1845 | 1704 | |
| 1846 | -static void term_completion(void) | |
| 1705 | +static void monitor_readline_cb(void *opaque, const char *input) | |
| 1847 | 1706 | { |
| 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 | - } | |
| 1707 | + pstrcpy(monitor_readline_buf, monitor_readline_buf_size, input); | |
| 1708 | + monitor_readline_started = 0; | |
| 1896 | 1709 | } |
| 1897 | 1710 | |
| 1898 | -/* return true if command handled */ | |
| 1899 | -static void term_handle_byte(int ch) | |
| 1711 | +void monitor_readline(const char *prompt, int is_password, | |
| 1712 | + char *buf, int buf_size) | |
| 1900 | 1713 | { |
| 1901 | - switch(term_esc_state) { | |
| 1902 | - case IS_NORM: | |
| 1903 | - switch(ch) { | |
| 1904 | - case 1: | |
| 1905 | - term_bol(); | |
| 1906 | - break; | |
| 1907 | - case 4: | |
| 1908 | - term_delete_char(); | |
| 1909 | - break; | |
| 1910 | - case 5: | |
| 1911 | - term_eol(); | |
| 1912 | - break; | |
| 1913 | - case 9: | |
| 1914 | - term_completion(); | |
| 1915 | - break; | |
| 1916 | - case 10: | |
| 1917 | - case 13: | |
| 1918 | - term_cmd_buf[term_cmd_buf_size] = '\0'; | |
| 1919 | - term_hist_add(term_cmd_buf); | |
| 1920 | - term_printf("\n"); | |
| 1921 | - term_handle_command(term_cmd_buf); | |
| 1922 | - term_show_prompt(); | |
| 1923 | - break; | |
| 1924 | - case 27: | |
| 1925 | - term_esc_state = IS_ESC; | |
| 1926 | - break; | |
| 1927 | - case 127: | |
| 1928 | - case 8: | |
| 1929 | - term_backspace(); | |
| 1930 | - break; | |
| 1931 | - case 155: | |
| 1932 | - term_esc_state = IS_CSI; | |
| 1933 | - break; | |
| 1934 | - default: | |
| 1935 | - if (ch >= 32) { | |
| 1936 | - term_insert_char(ch); | |
| 1937 | - } | |
| 1938 | - break; | |
| 1939 | - } | |
| 1940 | - break; | |
| 1941 | - case IS_ESC: | |
| 1942 | - if (ch == '[') { | |
| 1943 | - term_esc_state = IS_CSI; | |
| 1944 | - term_esc_param = 0; | |
| 1945 | - } else { | |
| 1946 | - term_esc_state = IS_NORM; | |
| 1947 | - } | |
| 1948 | - break; | |
| 1949 | - case IS_CSI: | |
| 1950 | - switch(ch) { | |
| 1951 | - case 'A': | |
| 1952 | - case 'F': | |
| 1953 | - term_up_char(); | |
| 1954 | - break; | |
| 1955 | - case 'B': | |
| 1956 | - case 'E': | |
| 1957 | - term_down_char(); | |
| 1958 | - break; | |
| 1959 | - case 'D': | |
| 1960 | - term_backward_char(); | |
| 1961 | - break; | |
| 1962 | - case 'C': | |
| 1963 | - term_forward_char(); | |
| 1964 | - break; | |
| 1965 | - case '0' ... '9': | |
| 1966 | - term_esc_param = term_esc_param * 10 + (ch - '0'); | |
| 1967 | - goto the_end; | |
| 1968 | - case '~': | |
| 1969 | - switch(term_esc_param) { | |
| 1970 | - case 1: | |
| 1971 | - term_bol(); | |
| 1972 | - break; | |
| 1973 | - case 3: | |
| 1974 | - term_delete_char(); | |
| 1975 | - break; | |
| 1976 | - case 4: | |
| 1977 | - term_eol(); | |
| 1978 | - break; | |
| 1979 | - } | |
| 1980 | - break; | |
| 1981 | - default: | |
| 1982 | - break; | |
| 1983 | - } | |
| 1984 | - term_esc_state = IS_NORM; | |
| 1985 | - the_end: | |
| 1986 | - break; | |
| 1714 | + if (is_password) { | |
| 1715 | + qemu_chr_send_event(monitor_hd, CHR_EVENT_FOCUS); | |
| 1987 | 1716 | } |
| 1988 | - term_update(); | |
| 1989 | -} | |
| 1990 | - | |
| 1991 | -static int term_can_read(void *opaque) | |
| 1992 | -{ | |
| 1993 | - return 128; | |
| 1994 | -} | |
| 1995 | - | |
| 1996 | -static void term_read(void *opaque, const uint8_t *buf, int size) | |
| 1997 | -{ | |
| 1998 | - int i; | |
| 1999 | - for(i = 0; i < size; i++) | |
| 2000 | - term_handle_byte(buf[i]); | |
| 2001 | -} | |
| 2002 | - | |
| 2003 | -void monitor_init(CharDriverState *hd, int show_banner) | |
| 2004 | -{ | |
| 2005 | - monitor_hd = hd; | |
| 2006 | - if (show_banner) { | |
| 2007 | - term_printf("QEMU %s monitor - type 'help' for more information\n", | |
| 2008 | - QEMU_VERSION); | |
| 2009 | - term_show_prompt(); | |
| 1717 | + readline_start(prompt, is_password, monitor_readline_cb, NULL); | |
| 1718 | + monitor_readline_buf = buf; | |
| 1719 | + monitor_readline_buf_size = buf_size; | |
| 1720 | + monitor_readline_started = 1; | |
| 1721 | + while (monitor_readline_started) { | |
| 1722 | + main_loop_wait(10); | |
| 2010 | 1723 | } |
| 2011 | - term_hist_entry = -1; | |
| 2012 | - qemu_chr_add_read_handler(hd, term_can_read, term_read, NULL); | |
| 2013 | 1724 | } | ... | ... |
readline.c
0 โ 100644
| 1 | +/* | |
| 2 | + * QEMU readline utility | |
| 3 | + * | |
| 4 | + * Copyright (c) 2003-2004 Fabrice Bellard | |
| 5 | + * | |
| 6 | + * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 7 | + * of this software and associated documentation files (the "Software"), to deal | |
| 8 | + * in the Software without restriction, including without limitation the rights | |
| 9 | + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 10 | + * copies of the Software, and to permit persons to whom the Software is | |
| 11 | + * furnished to do so, subject to the following conditions: | |
| 12 | + * | |
| 13 | + * The above copyright notice and this permission notice shall be included in | |
| 14 | + * all copies or substantial portions of the Software. | |
| 15 | + * | |
| 16 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 17 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 18 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| 19 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 20 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 21 | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 22 | + * THE SOFTWARE. | |
| 23 | + */ | |
| 24 | +#include "vl.h" | |
| 25 | + | |
| 26 | +#define TERM_CMD_BUF_SIZE 4095 | |
| 27 | +#define TERM_MAX_CMDS 64 | |
| 28 | +#define NB_COMPLETIONS_MAX 256 | |
| 29 | + | |
| 30 | +#define IS_NORM 0 | |
| 31 | +#define IS_ESC 1 | |
| 32 | +#define IS_CSI 2 | |
| 33 | + | |
| 34 | +#define printf do_not_use_printf | |
| 35 | + | |
| 36 | +static char term_cmd_buf[TERM_CMD_BUF_SIZE + 1]; | |
| 37 | +static int term_cmd_buf_index; | |
| 38 | +static int term_cmd_buf_size; | |
| 39 | + | |
| 40 | +static char term_last_cmd_buf[TERM_CMD_BUF_SIZE + 1]; | |
| 41 | +static int term_last_cmd_buf_index; | |
| 42 | +static int term_last_cmd_buf_size; | |
| 43 | + | |
| 44 | +static int term_esc_state; | |
| 45 | +static int term_esc_param; | |
| 46 | + | |
| 47 | +static char *term_history[TERM_MAX_CMDS]; | |
| 48 | +static int term_hist_entry = -1; | |
| 49 | + | |
| 50 | +static int nb_completions; | |
| 51 | +int completion_index; | |
| 52 | +static char *completions[NB_COMPLETIONS_MAX]; | |
| 53 | + | |
| 54 | +static ReadLineFunc *term_readline_func; | |
| 55 | +static int term_is_password; | |
| 56 | +static char term_prompt[256]; | |
| 57 | +static void *term_readline_opaque; | |
| 58 | + | |
| 59 | +static void term_show_prompt2(void) | |
| 60 | +{ | |
| 61 | + term_printf("%s", term_prompt); | |
| 62 | + term_flush(); | |
| 63 | + term_last_cmd_buf_index = 0; | |
| 64 | + term_last_cmd_buf_size = 0; | |
| 65 | + term_esc_state = IS_NORM; | |
| 66 | +} | |
| 67 | + | |
| 68 | +static void term_show_prompt(void) | |
| 69 | +{ | |
| 70 | + term_show_prompt2(); | |
| 71 | + term_cmd_buf_index = 0; | |
| 72 | + term_cmd_buf_size = 0; | |
| 73 | +} | |
| 74 | + | |
| 75 | +/* update the displayed command line */ | |
| 76 | +static void term_update(void) | |
| 77 | +{ | |
| 78 | + int i, delta, len; | |
| 79 | + | |
| 80 | + if (term_cmd_buf_size != term_last_cmd_buf_size || | |
| 81 | + memcmp(term_cmd_buf, term_last_cmd_buf, term_cmd_buf_size) != 0) { | |
| 82 | + for(i = 0; i < term_last_cmd_buf_index; i++) { | |
| 83 | + term_printf("\033[D"); | |
| 84 | + } | |
| 85 | + term_cmd_buf[term_cmd_buf_size] = '\0'; | |
| 86 | + if (term_is_password) { | |
| 87 | + len = strlen(term_cmd_buf); | |
| 88 | + for(i = 0; i < len; i++) | |
| 89 | + term_printf("*"); | |
| 90 | + } else { | |
| 91 | + term_printf("%s", term_cmd_buf); | |
| 92 | + } | |
| 93 | + term_printf("\033[K"); | |
| 94 | + memcpy(term_last_cmd_buf, term_cmd_buf, term_cmd_buf_size); | |
| 95 | + term_last_cmd_buf_size = term_cmd_buf_size; | |
| 96 | + term_last_cmd_buf_index = term_cmd_buf_size; | |
| 97 | + } | |
| 98 | + if (term_cmd_buf_index != term_last_cmd_buf_index) { | |
| 99 | + delta = term_cmd_buf_index - term_last_cmd_buf_index; | |
| 100 | + if (delta > 0) { | |
| 101 | + for(i = 0;i < delta; i++) { | |
| 102 | + term_printf("\033[C"); | |
| 103 | + } | |
| 104 | + } else { | |
| 105 | + delta = -delta; | |
| 106 | + for(i = 0;i < delta; i++) { | |
| 107 | + term_printf("\033[D"); | |
| 108 | + } | |
| 109 | + } | |
| 110 | + term_last_cmd_buf_index = term_cmd_buf_index; | |
| 111 | + } | |
| 112 | + term_flush(); | |
| 113 | +} | |
| 114 | + | |
| 115 | +static void term_insert_char(int ch) | |
| 116 | +{ | |
| 117 | + if (term_cmd_buf_index < TERM_CMD_BUF_SIZE) { | |
| 118 | + memmove(term_cmd_buf + term_cmd_buf_index + 1, | |
| 119 | + term_cmd_buf + term_cmd_buf_index, | |
| 120 | + term_cmd_buf_size - term_cmd_buf_index); | |
| 121 | + term_cmd_buf[term_cmd_buf_index] = ch; | |
| 122 | + term_cmd_buf_size++; | |
| 123 | + term_cmd_buf_index++; | |
| 124 | + } | |
| 125 | +} | |
| 126 | + | |
| 127 | +static void term_backward_char(void) | |
| 128 | +{ | |
| 129 | + if (term_cmd_buf_index > 0) { | |
| 130 | + term_cmd_buf_index--; | |
| 131 | + } | |
| 132 | +} | |
| 133 | + | |
| 134 | +static void term_forward_char(void) | |
| 135 | +{ | |
| 136 | + if (term_cmd_buf_index < term_cmd_buf_size) { | |
| 137 | + term_cmd_buf_index++; | |
| 138 | + } | |
| 139 | +} | |
| 140 | + | |
| 141 | +static void term_delete_char(void) | |
| 142 | +{ | |
| 143 | + if (term_cmd_buf_index < term_cmd_buf_size) { | |
| 144 | + memmove(term_cmd_buf + term_cmd_buf_index, | |
| 145 | + term_cmd_buf + term_cmd_buf_index + 1, | |
| 146 | + term_cmd_buf_size - term_cmd_buf_index - 1); | |
| 147 | + term_cmd_buf_size--; | |
| 148 | + } | |
| 149 | +} | |
| 150 | + | |
| 151 | +static void term_backspace(void) | |
| 152 | +{ | |
| 153 | + if (term_cmd_buf_index > 0) { | |
| 154 | + term_backward_char(); | |
| 155 | + term_delete_char(); | |
| 156 | + } | |
| 157 | +} | |
| 158 | + | |
| 159 | +static void term_bol(void) | |
| 160 | +{ | |
| 161 | + term_cmd_buf_index = 0; | |
| 162 | +} | |
| 163 | + | |
| 164 | +static void term_eol(void) | |
| 165 | +{ | |
| 166 | + term_cmd_buf_index = term_cmd_buf_size; | |
| 167 | +} | |
| 168 | + | |
| 169 | +static void term_up_char(void) | |
| 170 | +{ | |
| 171 | + int idx; | |
| 172 | + | |
| 173 | + if (term_hist_entry == 0) | |
| 174 | + return; | |
| 175 | + if (term_hist_entry == -1) { | |
| 176 | + /* Find latest entry */ | |
| 177 | + for (idx = 0; idx < TERM_MAX_CMDS; idx++) { | |
| 178 | + if (term_history[idx] == NULL) | |
| 179 | + break; | |
| 180 | + } | |
| 181 | + term_hist_entry = idx; | |
| 182 | + } | |
| 183 | + term_hist_entry--; | |
| 184 | + if (term_hist_entry >= 0) { | |
| 185 | + pstrcpy(term_cmd_buf, sizeof(term_cmd_buf), | |
| 186 | + term_history[term_hist_entry]); | |
| 187 | + term_cmd_buf_index = term_cmd_buf_size = strlen(term_cmd_buf); | |
| 188 | + } | |
| 189 | +} | |
| 190 | + | |
| 191 | +static void term_down_char(void) | |
| 192 | +{ | |
| 193 | + if (term_hist_entry == TERM_MAX_CMDS - 1 || term_hist_entry == -1) | |
| 194 | + return; | |
| 195 | + if (term_history[++term_hist_entry] != NULL) { | |
| 196 | + pstrcpy(term_cmd_buf, sizeof(term_cmd_buf), | |
| 197 | + term_history[term_hist_entry]); | |
| 198 | + } else { | |
| 199 | + term_hist_entry = -1; | |
| 200 | + } | |
| 201 | + term_cmd_buf_index = term_cmd_buf_size = strlen(term_cmd_buf); | |
| 202 | +} | |
| 203 | + | |
| 204 | +static void term_hist_add(const char *cmdline) | |
| 205 | +{ | |
| 206 | + char *hist_entry, *new_entry; | |
| 207 | + int idx; | |
| 208 | + | |
| 209 | + if (cmdline[0] == '\0') | |
| 210 | + return; | |
| 211 | + new_entry = NULL; | |
| 212 | + if (term_hist_entry != -1) { | |
| 213 | + /* We were editing an existing history entry: replace it */ | |
| 214 | + hist_entry = term_history[term_hist_entry]; | |
| 215 | + idx = term_hist_entry; | |
| 216 | + if (strcmp(hist_entry, cmdline) == 0) { | |
| 217 | + goto same_entry; | |
| 218 | + } | |
| 219 | + } | |
| 220 | + /* Search cmdline in history buffers */ | |
| 221 | + for (idx = 0; idx < TERM_MAX_CMDS; idx++) { | |
| 222 | + hist_entry = term_history[idx]; | |
| 223 | + if (hist_entry == NULL) | |
| 224 | + break; | |
| 225 | + if (strcmp(hist_entry, cmdline) == 0) { | |
| 226 | + same_entry: | |
| 227 | + new_entry = hist_entry; | |
| 228 | + /* Put this entry at the end of history */ | |
| 229 | + memmove(&term_history[idx], &term_history[idx + 1], | |
| 230 | + &term_history[TERM_MAX_CMDS] - &term_history[idx + 1]); | |
| 231 | + term_history[TERM_MAX_CMDS - 1] = NULL; | |
| 232 | + for (; idx < TERM_MAX_CMDS; idx++) { | |
| 233 | + if (term_history[idx] == NULL) | |
| 234 | + break; | |
| 235 | + } | |
| 236 | + break; | |
| 237 | + } | |
| 238 | + } | |
| 239 | + if (idx == TERM_MAX_CMDS) { | |
| 240 | + /* Need to get one free slot */ | |
| 241 | + free(term_history[0]); | |
| 242 | + memcpy(term_history, &term_history[1], | |
| 243 | + &term_history[TERM_MAX_CMDS] - &term_history[1]); | |
| 244 | + term_history[TERM_MAX_CMDS - 1] = NULL; | |
| 245 | + idx = TERM_MAX_CMDS - 1; | |
| 246 | + } | |
| 247 | + if (new_entry == NULL) | |
| 248 | + new_entry = strdup(cmdline); | |
| 249 | + term_history[idx] = new_entry; | |
| 250 | + term_hist_entry = -1; | |
| 251 | +} | |
| 252 | + | |
| 253 | +/* completion support */ | |
| 254 | + | |
| 255 | +void add_completion(const char *str) | |
| 256 | +{ | |
| 257 | + if (nb_completions < NB_COMPLETIONS_MAX) { | |
| 258 | + completions[nb_completions++] = qemu_strdup(str); | |
| 259 | + } | |
| 260 | +} | |
| 261 | + | |
| 262 | +static void term_completion(void) | |
| 263 | +{ | |
| 264 | + int len, i, j, max_width, nb_cols; | |
| 265 | + char *cmdline; | |
| 266 | + | |
| 267 | + nb_completions = 0; | |
| 268 | + | |
| 269 | + cmdline = qemu_malloc(term_cmd_buf_index + 1); | |
| 270 | + if (!cmdline) | |
| 271 | + return; | |
| 272 | + memcpy(cmdline, term_cmd_buf, term_cmd_buf_index); | |
| 273 | + cmdline[term_cmd_buf_index] = '\0'; | |
| 274 | + readline_find_completion(cmdline); | |
| 275 | + qemu_free(cmdline); | |
| 276 | + | |
| 277 | + /* no completion found */ | |
| 278 | + if (nb_completions <= 0) | |
| 279 | + return; | |
| 280 | + if (nb_completions == 1) { | |
| 281 | + len = strlen(completions[0]); | |
| 282 | + for(i = completion_index; i < len; i++) { | |
| 283 | + term_insert_char(completions[0][i]); | |
| 284 | + } | |
| 285 | + /* extra space for next argument. XXX: make it more generic */ | |
| 286 | + if (len > 0 && completions[0][len - 1] != '/') | |
| 287 | + term_insert_char(' '); | |
| 288 | + } else { | |
| 289 | + term_printf("\n"); | |
| 290 | + max_width = 0; | |
| 291 | + for(i = 0; i < nb_completions; i++) { | |
| 292 | + len = strlen(completions[i]); | |
| 293 | + if (len > max_width) | |
| 294 | + max_width = len; | |
| 295 | + } | |
| 296 | + max_width += 2; | |
| 297 | + if (max_width < 10) | |
| 298 | + max_width = 10; | |
| 299 | + else if (max_width > 80) | |
| 300 | + max_width = 80; | |
| 301 | + nb_cols = 80 / max_width; | |
| 302 | + j = 0; | |
| 303 | + for(i = 0; i < nb_completions; i++) { | |
| 304 | + term_printf("%-*s", max_width, completions[i]); | |
| 305 | + if (++j == nb_cols || i == (nb_completions - 1)) { | |
| 306 | + term_printf("\n"); | |
| 307 | + j = 0; | |
| 308 | + } | |
| 309 | + } | |
| 310 | + term_show_prompt2(); | |
| 311 | + } | |
| 312 | +} | |
| 313 | + | |
| 314 | +/* return true if command handled */ | |
| 315 | +void readline_handle_byte(int ch) | |
| 316 | +{ | |
| 317 | + switch(term_esc_state) { | |
| 318 | + case IS_NORM: | |
| 319 | + switch(ch) { | |
| 320 | + case 1: | |
| 321 | + term_bol(); | |
| 322 | + break; | |
| 323 | + case 4: | |
| 324 | + term_delete_char(); | |
| 325 | + break; | |
| 326 | + case 5: | |
| 327 | + term_eol(); | |
| 328 | + break; | |
| 329 | + case 9: | |
| 330 | + term_completion(); | |
| 331 | + break; | |
| 332 | + case 10: | |
| 333 | + case 13: | |
| 334 | + term_cmd_buf[term_cmd_buf_size] = '\0'; | |
| 335 | + if (!term_is_password) | |
| 336 | + term_hist_add(term_cmd_buf); | |
| 337 | + term_printf("\n"); | |
| 338 | + /* NOTE: readline_start can be called here */ | |
| 339 | + term_readline_func(term_readline_opaque, term_cmd_buf); | |
| 340 | + break; | |
| 341 | + case 27: | |
| 342 | + term_esc_state = IS_ESC; | |
| 343 | + break; | |
| 344 | + case 127: | |
| 345 | + case 8: | |
| 346 | + term_backspace(); | |
| 347 | + break; | |
| 348 | + case 155: | |
| 349 | + term_esc_state = IS_CSI; | |
| 350 | + break; | |
| 351 | + default: | |
| 352 | + if (ch >= 32) { | |
| 353 | + term_insert_char(ch); | |
| 354 | + } | |
| 355 | + break; | |
| 356 | + } | |
| 357 | + break; | |
| 358 | + case IS_ESC: | |
| 359 | + if (ch == '[') { | |
| 360 | + term_esc_state = IS_CSI; | |
| 361 | + term_esc_param = 0; | |
| 362 | + } else { | |
| 363 | + term_esc_state = IS_NORM; | |
| 364 | + } | |
| 365 | + break; | |
| 366 | + case IS_CSI: | |
| 367 | + switch(ch) { | |
| 368 | + case 'A': | |
| 369 | + case 'F': | |
| 370 | + term_up_char(); | |
| 371 | + break; | |
| 372 | + case 'B': | |
| 373 | + case 'E': | |
| 374 | + term_down_char(); | |
| 375 | + break; | |
| 376 | + case 'D': | |
| 377 | + term_backward_char(); | |
| 378 | + break; | |
| 379 | + case 'C': | |
| 380 | + term_forward_char(); | |
| 381 | + break; | |
| 382 | + case '0' ... '9': | |
| 383 | + term_esc_param = term_esc_param * 10 + (ch - '0'); | |
| 384 | + goto the_end; | |
| 385 | + case '~': | |
| 386 | + switch(term_esc_param) { | |
| 387 | + case 1: | |
| 388 | + term_bol(); | |
| 389 | + break; | |
| 390 | + case 3: | |
| 391 | + term_delete_char(); | |
| 392 | + break; | |
| 393 | + case 4: | |
| 394 | + term_eol(); | |
| 395 | + break; | |
| 396 | + } | |
| 397 | + break; | |
| 398 | + default: | |
| 399 | + break; | |
| 400 | + } | |
| 401 | + term_esc_state = IS_NORM; | |
| 402 | + the_end: | |
| 403 | + break; | |
| 404 | + } | |
| 405 | + term_update(); | |
| 406 | +} | |
| 407 | + | |
| 408 | +void readline_start(const char *prompt, int is_password, | |
| 409 | + ReadLineFunc *readline_func, void *opaque) | |
| 410 | +{ | |
| 411 | + pstrcpy(term_prompt, sizeof(term_prompt), prompt); | |
| 412 | + term_readline_func = readline_func; | |
| 413 | + term_readline_opaque = opaque; | |
| 414 | + term_is_password = is_password; | |
| 415 | + term_show_prompt(); | |
| 416 | +} | |
| 417 | + | |
| 418 | +const char *readline_get_history(unsigned int index) | |
| 419 | +{ | |
| 420 | + if (index >= TERM_MAX_CMDS) | |
| 421 | + return NULL; | |
| 422 | + return term_history[index]; | |
| 423 | +} | |
| 424 | + | |
| 425 | + | ... | ... |