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 | + | ... | ... |