Commit 7e2515e87c41e2e658aaed466e11cbdf1ea8bcb1

Authored by bellard
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,36 +32,6 @@
32 #define offsetof(type, field) ((size_t) &((type *)0)->field) 32 #define offsetof(type, field) ((size_t) &((type *)0)->field)
33 #endif 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 * Supported types: 36 * Supported types:
67 * 37 *
@@ -83,23 +53,52 @@ typedef struct term_cmd_t { @@ -83,23 +53,52 @@ typedef struct term_cmd_t {
83 const char *help; 53 const char *help;
84 } term_cmd_t; 54 } term_cmd_t;
85 55
  56 +static CharDriverState *monitor_hd;
  57 +
86 static term_cmd_t term_cmds[]; 58 static term_cmd_t term_cmds[];
87 static term_cmd_t info_cmds[]; 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 char buf[4096]; 91 char buf[4096];
94 - va_list ap;  
95 - va_start(ap, fmt);  
96 vsnprintf(buf, sizeof(buf), fmt, ap); 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 static int compare_cmd(const char *name, const char *list) 104 static int compare_cmd(const char *name, const char *list)
@@ -159,8 +158,9 @@ static void do_commit(void) @@ -159,8 +158,9 @@ static void do_commit(void)
159 int i; 158 int i;
160 159
161 for (i = 0; i < MAX_DISKS; i++) { 160 for (i = 0; i < MAX_DISKS; i++) {
162 - if (bs_table[i]) 161 + if (bs_table[i]) {
163 bdrv_commit(bs_table[i]); 162 bdrv_commit(bs_table[i]);
  163 + }
164 } 164 }
165 } 165 }
166 166
@@ -215,11 +215,14 @@ static void do_info_registers(void) @@ -215,11 +215,14 @@ static void do_info_registers(void)
215 static void do_info_history (void) 215 static void do_info_history (void)
216 { 216 {
217 int i; 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,6 +264,8 @@ static void do_eject(int force, const char *filename)
261 static void do_change(const char *device, const char *filename) 264 static void do_change(const char *device, const char *filename)
262 { 265 {
263 BlockDriverState *bs; 266 BlockDriverState *bs;
  267 + int i;
  268 + char password[256];
264 269
265 bs = bdrv_find(device); 270 bs = bdrv_find(device);
266 if (!bs) { 271 if (!bs) {
@@ -270,6 +275,15 @@ static void do_change(const char *device, const char *filename) @@ -270,6 +275,15 @@ static void do_change(const char *device, const char *filename)
270 if (eject_device(bs, 0) < 0) 275 if (eject_device(bs, 0) < 0)
271 return; 276 return;
272 bdrv_open(bs, filename, 0); 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 static void do_screen_dump(const char *filename) 289 static void do_screen_dump(const char *filename)
@@ -1178,7 +1192,7 @@ static int default_fmt_size = 4; @@ -1178,7 +1192,7 @@ static int default_fmt_size = 4;
1178 1192
1179 #define MAX_ARGS 16 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 const char *p, *pstart, *typestr; 1197 const char *p, *pstart, *typestr;
1184 char *q; 1198 char *q;
@@ -1577,7 +1591,7 @@ static void parse_cmdline(const char *cmdline, @@ -1577,7 +1591,7 @@ static void parse_cmdline(const char *cmdline,
1577 *pnb_args = nb_args; 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 const char *cmdname; 1596 const char *cmdname;
1583 char *args[MAX_ARGS]; 1597 char *args[MAX_ARGS];
@@ -1646,368 +1660,65 @@ static void find_completion(const char *cmdline) @@ -1646,368 +1660,65 @@ static void find_completion(const char *cmdline)
1646 qemu_free(args[i]); 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 +