10150cd81SStefan Hajnoczi /*
20150cd81SStefan Hajnoczi * QEMU readline utility
30150cd81SStefan Hajnoczi *
40150cd81SStefan Hajnoczi * Copyright (c) 2003-2004 Fabrice Bellard
50150cd81SStefan Hajnoczi *
60150cd81SStefan Hajnoczi * Permission is hereby granted, free of charge, to any person obtaining a copy
70150cd81SStefan Hajnoczi * of this software and associated documentation files (the "Software"), to deal
80150cd81SStefan Hajnoczi * in the Software without restriction, including without limitation the rights
90150cd81SStefan Hajnoczi * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
100150cd81SStefan Hajnoczi * copies of the Software, and to permit persons to whom the Software is
110150cd81SStefan Hajnoczi * furnished to do so, subject to the following conditions:
120150cd81SStefan Hajnoczi *
130150cd81SStefan Hajnoczi * The above copyright notice and this permission notice shall be included in
140150cd81SStefan Hajnoczi * all copies or substantial portions of the Software.
150150cd81SStefan Hajnoczi *
160150cd81SStefan Hajnoczi * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
170150cd81SStefan Hajnoczi * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
180150cd81SStefan Hajnoczi * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
190150cd81SStefan Hajnoczi * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
200150cd81SStefan Hajnoczi * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
210150cd81SStefan Hajnoczi * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
220150cd81SStefan Hajnoczi * THE SOFTWARE.
230150cd81SStefan Hajnoczi */
240150cd81SStefan Hajnoczi
25aafd7584SPeter Maydell #include "qemu/osdep.h"
260150cd81SStefan Hajnoczi #include "qemu/readline.h"
27856dfd8aSMarkus Armbruster #include "qemu/ctype.h"
28f348b6d1SVeronia Bahaa #include "qemu/cutils.h"
290150cd81SStefan Hajnoczi
300150cd81SStefan Hajnoczi #define IS_NORM 0
310150cd81SStefan Hajnoczi #define IS_ESC 1
320150cd81SStefan Hajnoczi #define IS_CSI 2
330150cd81SStefan Hajnoczi #define IS_SS3 3
340150cd81SStefan Hajnoczi
readline_show_prompt(ReadLineState * rs)350150cd81SStefan Hajnoczi void readline_show_prompt(ReadLineState *rs)
360150cd81SStefan Hajnoczi {
370150cd81SStefan Hajnoczi rs->printf_func(rs->opaque, "%s", rs->prompt);
380150cd81SStefan Hajnoczi rs->flush_func(rs->opaque);
390150cd81SStefan Hajnoczi rs->last_cmd_buf_index = 0;
400150cd81SStefan Hajnoczi rs->last_cmd_buf_size = 0;
410150cd81SStefan Hajnoczi rs->esc_state = IS_NORM;
420150cd81SStefan Hajnoczi }
430150cd81SStefan Hajnoczi
440150cd81SStefan Hajnoczi /* update the displayed command line */
readline_update(ReadLineState * rs)450150cd81SStefan Hajnoczi static void readline_update(ReadLineState *rs)
460150cd81SStefan Hajnoczi {
470150cd81SStefan Hajnoczi int i, delta, len;
480150cd81SStefan Hajnoczi
490150cd81SStefan Hajnoczi if (rs->cmd_buf_size != rs->last_cmd_buf_size ||
500150cd81SStefan Hajnoczi memcmp(rs->cmd_buf, rs->last_cmd_buf, rs->cmd_buf_size) != 0) {
510150cd81SStefan Hajnoczi for (i = 0; i < rs->last_cmd_buf_index; i++) {
520150cd81SStefan Hajnoczi rs->printf_func(rs->opaque, "\033[D");
530150cd81SStefan Hajnoczi }
540150cd81SStefan Hajnoczi rs->cmd_buf[rs->cmd_buf_size] = '\0';
550150cd81SStefan Hajnoczi if (rs->read_password) {
560150cd81SStefan Hajnoczi len = strlen(rs->cmd_buf);
57e238a99eSJules Irenge for (i = 0; i < len; i++) {
580150cd81SStefan Hajnoczi rs->printf_func(rs->opaque, "*");
59e238a99eSJules Irenge }
600150cd81SStefan Hajnoczi } else {
610150cd81SStefan Hajnoczi rs->printf_func(rs->opaque, "%s", rs->cmd_buf);
620150cd81SStefan Hajnoczi }
630150cd81SStefan Hajnoczi rs->printf_func(rs->opaque, "\033[K");
640150cd81SStefan Hajnoczi memcpy(rs->last_cmd_buf, rs->cmd_buf, rs->cmd_buf_size);
650150cd81SStefan Hajnoczi rs->last_cmd_buf_size = rs->cmd_buf_size;
660150cd81SStefan Hajnoczi rs->last_cmd_buf_index = rs->cmd_buf_size;
670150cd81SStefan Hajnoczi }
680150cd81SStefan Hajnoczi if (rs->cmd_buf_index != rs->last_cmd_buf_index) {
690150cd81SStefan Hajnoczi delta = rs->cmd_buf_index - rs->last_cmd_buf_index;
700150cd81SStefan Hajnoczi if (delta > 0) {
710150cd81SStefan Hajnoczi for (i = 0; i < delta; i++) {
720150cd81SStefan Hajnoczi rs->printf_func(rs->opaque, "\033[C");
730150cd81SStefan Hajnoczi }
740150cd81SStefan Hajnoczi } else {
750150cd81SStefan Hajnoczi delta = -delta;
760150cd81SStefan Hajnoczi for (i = 0; i < delta; i++) {
770150cd81SStefan Hajnoczi rs->printf_func(rs->opaque, "\033[D");
780150cd81SStefan Hajnoczi }
790150cd81SStefan Hajnoczi }
800150cd81SStefan Hajnoczi rs->last_cmd_buf_index = rs->cmd_buf_index;
810150cd81SStefan Hajnoczi }
820150cd81SStefan Hajnoczi rs->flush_func(rs->opaque);
830150cd81SStefan Hajnoczi }
840150cd81SStefan Hajnoczi
readline_insert_char(ReadLineState * rs,int ch)850150cd81SStefan Hajnoczi static void readline_insert_char(ReadLineState *rs, int ch)
860150cd81SStefan Hajnoczi {
870150cd81SStefan Hajnoczi if (rs->cmd_buf_index < READLINE_CMD_BUF_SIZE) {
880150cd81SStefan Hajnoczi memmove(rs->cmd_buf + rs->cmd_buf_index + 1,
890150cd81SStefan Hajnoczi rs->cmd_buf + rs->cmd_buf_index,
900150cd81SStefan Hajnoczi rs->cmd_buf_size - rs->cmd_buf_index);
910150cd81SStefan Hajnoczi rs->cmd_buf[rs->cmd_buf_index] = ch;
920150cd81SStefan Hajnoczi rs->cmd_buf_size++;
930150cd81SStefan Hajnoczi rs->cmd_buf_index++;
940150cd81SStefan Hajnoczi }
950150cd81SStefan Hajnoczi }
960150cd81SStefan Hajnoczi
readline_backward_char(ReadLineState * rs)970150cd81SStefan Hajnoczi static void readline_backward_char(ReadLineState *rs)
980150cd81SStefan Hajnoczi {
990150cd81SStefan Hajnoczi if (rs->cmd_buf_index > 0) {
1000150cd81SStefan Hajnoczi rs->cmd_buf_index--;
1010150cd81SStefan Hajnoczi }
1020150cd81SStefan Hajnoczi }
1030150cd81SStefan Hajnoczi
readline_forward_char(ReadLineState * rs)1040150cd81SStefan Hajnoczi static void readline_forward_char(ReadLineState *rs)
1050150cd81SStefan Hajnoczi {
1060150cd81SStefan Hajnoczi if (rs->cmd_buf_index < rs->cmd_buf_size) {
1070150cd81SStefan Hajnoczi rs->cmd_buf_index++;
1080150cd81SStefan Hajnoczi }
1090150cd81SStefan Hajnoczi }
1100150cd81SStefan Hajnoczi
readline_delete_char(ReadLineState * rs)1110150cd81SStefan Hajnoczi static void readline_delete_char(ReadLineState *rs)
1120150cd81SStefan Hajnoczi {
1130150cd81SStefan Hajnoczi if (rs->cmd_buf_index < rs->cmd_buf_size) {
1140150cd81SStefan Hajnoczi memmove(rs->cmd_buf + rs->cmd_buf_index,
1150150cd81SStefan Hajnoczi rs->cmd_buf + rs->cmd_buf_index + 1,
1160150cd81SStefan Hajnoczi rs->cmd_buf_size - rs->cmd_buf_index - 1);
1170150cd81SStefan Hajnoczi rs->cmd_buf_size--;
1180150cd81SStefan Hajnoczi }
1190150cd81SStefan Hajnoczi }
1200150cd81SStefan Hajnoczi
readline_backspace(ReadLineState * rs)1210150cd81SStefan Hajnoczi static void readline_backspace(ReadLineState *rs)
1220150cd81SStefan Hajnoczi {
1230150cd81SStefan Hajnoczi if (rs->cmd_buf_index > 0) {
1240150cd81SStefan Hajnoczi readline_backward_char(rs);
1250150cd81SStefan Hajnoczi readline_delete_char(rs);
1260150cd81SStefan Hajnoczi }
1270150cd81SStefan Hajnoczi }
1280150cd81SStefan Hajnoczi
readline_backword(ReadLineState * rs)1290150cd81SStefan Hajnoczi static void readline_backword(ReadLineState *rs)
1300150cd81SStefan Hajnoczi {
1310150cd81SStefan Hajnoczi int start;
1320150cd81SStefan Hajnoczi
1330150cd81SStefan Hajnoczi if (rs->cmd_buf_index == 0 || rs->cmd_buf_index > rs->cmd_buf_size) {
1340150cd81SStefan Hajnoczi return;
1350150cd81SStefan Hajnoczi }
1360150cd81SStefan Hajnoczi
1370150cd81SStefan Hajnoczi start = rs->cmd_buf_index - 1;
1380150cd81SStefan Hajnoczi
1390150cd81SStefan Hajnoczi /* find first word (backwards) */
1400150cd81SStefan Hajnoczi while (start > 0) {
1410150cd81SStefan Hajnoczi if (!qemu_isspace(rs->cmd_buf[start])) {
1420150cd81SStefan Hajnoczi break;
1430150cd81SStefan Hajnoczi }
1440150cd81SStefan Hajnoczi
1450150cd81SStefan Hajnoczi --start;
1460150cd81SStefan Hajnoczi }
1470150cd81SStefan Hajnoczi
1480150cd81SStefan Hajnoczi /* find first space (backwards) */
1490150cd81SStefan Hajnoczi while (start > 0) {
1500150cd81SStefan Hajnoczi if (qemu_isspace(rs->cmd_buf[start])) {
1510150cd81SStefan Hajnoczi ++start;
1520150cd81SStefan Hajnoczi break;
1530150cd81SStefan Hajnoczi }
1540150cd81SStefan Hajnoczi
1550150cd81SStefan Hajnoczi --start;
1560150cd81SStefan Hajnoczi }
1570150cd81SStefan Hajnoczi
1580150cd81SStefan Hajnoczi /* remove word */
1590150cd81SStefan Hajnoczi if (start < rs->cmd_buf_index) {
1600150cd81SStefan Hajnoczi memmove(rs->cmd_buf + start,
1610150cd81SStefan Hajnoczi rs->cmd_buf + rs->cmd_buf_index,
1620150cd81SStefan Hajnoczi rs->cmd_buf_size - rs->cmd_buf_index);
1630150cd81SStefan Hajnoczi rs->cmd_buf_size -= rs->cmd_buf_index - start;
1640150cd81SStefan Hajnoczi rs->cmd_buf_index = start;
1650150cd81SStefan Hajnoczi }
1660150cd81SStefan Hajnoczi }
1670150cd81SStefan Hajnoczi
readline_bol(ReadLineState * rs)1680150cd81SStefan Hajnoczi static void readline_bol(ReadLineState *rs)
1690150cd81SStefan Hajnoczi {
1700150cd81SStefan Hajnoczi rs->cmd_buf_index = 0;
1710150cd81SStefan Hajnoczi }
1720150cd81SStefan Hajnoczi
readline_eol(ReadLineState * rs)1730150cd81SStefan Hajnoczi static void readline_eol(ReadLineState *rs)
1740150cd81SStefan Hajnoczi {
1750150cd81SStefan Hajnoczi rs->cmd_buf_index = rs->cmd_buf_size;
1760150cd81SStefan Hajnoczi }
1770150cd81SStefan Hajnoczi
readline_up_char(ReadLineState * rs)1780150cd81SStefan Hajnoczi static void readline_up_char(ReadLineState *rs)
1790150cd81SStefan Hajnoczi {
1800150cd81SStefan Hajnoczi int idx;
1810150cd81SStefan Hajnoczi
182e238a99eSJules Irenge if (rs->hist_entry == 0) {
1830150cd81SStefan Hajnoczi return;
184e238a99eSJules Irenge }
1850150cd81SStefan Hajnoczi if (rs->hist_entry == -1) {
1860150cd81SStefan Hajnoczi /* Find latest entry */
1870150cd81SStefan Hajnoczi for (idx = 0; idx < READLINE_MAX_CMDS; idx++) {
188e238a99eSJules Irenge if (rs->history[idx] == NULL) {
1890150cd81SStefan Hajnoczi break;
1900150cd81SStefan Hajnoczi }
191e238a99eSJules Irenge }
1920150cd81SStefan Hajnoczi rs->hist_entry = idx;
1930150cd81SStefan Hajnoczi }
1940150cd81SStefan Hajnoczi rs->hist_entry--;
1950150cd81SStefan Hajnoczi if (rs->hist_entry >= 0) {
1960150cd81SStefan Hajnoczi pstrcpy(rs->cmd_buf, sizeof(rs->cmd_buf),
1970150cd81SStefan Hajnoczi rs->history[rs->hist_entry]);
1980150cd81SStefan Hajnoczi rs->cmd_buf_index = rs->cmd_buf_size = strlen(rs->cmd_buf);
1990150cd81SStefan Hajnoczi }
2000150cd81SStefan Hajnoczi }
2010150cd81SStefan Hajnoczi
readline_down_char(ReadLineState * rs)2020150cd81SStefan Hajnoczi static void readline_down_char(ReadLineState *rs)
2030150cd81SStefan Hajnoczi {
204e238a99eSJules Irenge if (rs->hist_entry == -1) {
2050150cd81SStefan Hajnoczi return;
206e238a99eSJules Irenge }
2070150cd81SStefan Hajnoczi if (rs->hist_entry < READLINE_MAX_CMDS - 1 &&
2080150cd81SStefan Hajnoczi rs->history[++rs->hist_entry] != NULL) {
2090150cd81SStefan Hajnoczi pstrcpy(rs->cmd_buf, sizeof(rs->cmd_buf),
2100150cd81SStefan Hajnoczi rs->history[rs->hist_entry]);
2110150cd81SStefan Hajnoczi } else {
2120150cd81SStefan Hajnoczi rs->cmd_buf[0] = 0;
2130150cd81SStefan Hajnoczi rs->hist_entry = -1;
2140150cd81SStefan Hajnoczi }
2150150cd81SStefan Hajnoczi rs->cmd_buf_index = rs->cmd_buf_size = strlen(rs->cmd_buf);
2160150cd81SStefan Hajnoczi }
2170150cd81SStefan Hajnoczi
readline_hist_add(ReadLineState * rs,const char * cmdline)2180150cd81SStefan Hajnoczi static void readline_hist_add(ReadLineState *rs, const char *cmdline)
2190150cd81SStefan Hajnoczi {
2200150cd81SStefan Hajnoczi char *hist_entry, *new_entry;
2210150cd81SStefan Hajnoczi int idx;
2220150cd81SStefan Hajnoczi
223e238a99eSJules Irenge if (cmdline[0] == '\0') {
2240150cd81SStefan Hajnoczi return;
225e238a99eSJules Irenge }
2260150cd81SStefan Hajnoczi new_entry = NULL;
2270150cd81SStefan Hajnoczi if (rs->hist_entry != -1) {
2280150cd81SStefan Hajnoczi /* We were editing an existing history entry: replace it */
2290150cd81SStefan Hajnoczi hist_entry = rs->history[rs->hist_entry];
2300150cd81SStefan Hajnoczi idx = rs->hist_entry;
2310150cd81SStefan Hajnoczi if (strcmp(hist_entry, cmdline) == 0) {
2320150cd81SStefan Hajnoczi goto same_entry;
2330150cd81SStefan Hajnoczi }
2340150cd81SStefan Hajnoczi }
2350150cd81SStefan Hajnoczi /* Search cmdline in history buffers */
2360150cd81SStefan Hajnoczi for (idx = 0; idx < READLINE_MAX_CMDS; idx++) {
2370150cd81SStefan Hajnoczi hist_entry = rs->history[idx];
238e238a99eSJules Irenge if (hist_entry == NULL) {
2390150cd81SStefan Hajnoczi break;
240e238a99eSJules Irenge }
2410150cd81SStefan Hajnoczi if (strcmp(hist_entry, cmdline) == 0) {
2420150cd81SStefan Hajnoczi same_entry:
243593621f3SAlex Chen if (idx == READLINE_MAX_CMDS - 1) {
244593621f3SAlex Chen return;
245593621f3SAlex Chen }
2460150cd81SStefan Hajnoczi new_entry = hist_entry;
2470150cd81SStefan Hajnoczi /* Put this entry at the end of history */
2480150cd81SStefan Hajnoczi memmove(&rs->history[idx], &rs->history[idx + 1],
2490150cd81SStefan Hajnoczi (READLINE_MAX_CMDS - (idx + 1)) * sizeof(char *));
2500150cd81SStefan Hajnoczi rs->history[READLINE_MAX_CMDS - 1] = NULL;
2510150cd81SStefan Hajnoczi for (; idx < READLINE_MAX_CMDS; idx++) {
252e238a99eSJules Irenge if (rs->history[idx] == NULL) {
2530150cd81SStefan Hajnoczi break;
2540150cd81SStefan Hajnoczi }
255e238a99eSJules Irenge }
2560150cd81SStefan Hajnoczi break;
2570150cd81SStefan Hajnoczi }
2580150cd81SStefan Hajnoczi }
2590150cd81SStefan Hajnoczi if (idx == READLINE_MAX_CMDS) {
2600150cd81SStefan Hajnoczi /* Need to get one free slot */
2610150cd81SStefan Hajnoczi g_free(rs->history[0]);
2620150cd81SStefan Hajnoczi memmove(rs->history, &rs->history[1],
2630150cd81SStefan Hajnoczi (READLINE_MAX_CMDS - 1) * sizeof(char *));
2640150cd81SStefan Hajnoczi rs->history[READLINE_MAX_CMDS - 1] = NULL;
2650150cd81SStefan Hajnoczi idx = READLINE_MAX_CMDS - 1;
2660150cd81SStefan Hajnoczi }
267e238a99eSJules Irenge if (new_entry == NULL) {
2680150cd81SStefan Hajnoczi new_entry = g_strdup(cmdline);
269e238a99eSJules Irenge }
2700150cd81SStefan Hajnoczi rs->history[idx] = new_entry;
2710150cd81SStefan Hajnoczi rs->hist_entry = -1;
2720150cd81SStefan Hajnoczi }
2730150cd81SStefan Hajnoczi
readline_kill_line(ReadLineState * rs)274*e1e55d34SManos Pitsidianakis static void readline_kill_line(ReadLineState *rs)
275*e1e55d34SManos Pitsidianakis {
276*e1e55d34SManos Pitsidianakis while (rs->cmd_buf_index > 0) {
277*e1e55d34SManos Pitsidianakis readline_backward_char(rs);
278*e1e55d34SManos Pitsidianakis readline_delete_char(rs);
279*e1e55d34SManos Pitsidianakis }
280*e1e55d34SManos Pitsidianakis }
281*e1e55d34SManos Pitsidianakis
2820150cd81SStefan Hajnoczi /* completion support */
2830150cd81SStefan Hajnoczi
readline_add_completion(ReadLineState * rs,const char * str)2840150cd81SStefan Hajnoczi void readline_add_completion(ReadLineState *rs, const char *str)
2850150cd81SStefan Hajnoczi {
2860150cd81SStefan Hajnoczi if (rs->nb_completions < READLINE_MAX_COMPLETIONS) {
287e70871d8SHani Benhabiles int i;
288e70871d8SHani Benhabiles for (i = 0; i < rs->nb_completions; i++) {
289e70871d8SHani Benhabiles if (!strcmp(rs->completions[i], str)) {
290e70871d8SHani Benhabiles return;
291e70871d8SHani Benhabiles }
292e70871d8SHani Benhabiles }
2930150cd81SStefan Hajnoczi rs->completions[rs->nb_completions++] = g_strdup(str);
2940150cd81SStefan Hajnoczi }
2950150cd81SStefan Hajnoczi }
2960150cd81SStefan Hajnoczi
readline_add_completion_of(ReadLineState * rs,const char * pfx,const char * str)29752f50b1eSMarkus Armbruster void readline_add_completion_of(ReadLineState *rs,
29852f50b1eSMarkus Armbruster const char *pfx, const char *str)
29952f50b1eSMarkus Armbruster {
30052f50b1eSMarkus Armbruster if (!strncmp(str, pfx, strlen(pfx))) {
30152f50b1eSMarkus Armbruster readline_add_completion(rs, str);
30252f50b1eSMarkus Armbruster }
30352f50b1eSMarkus Armbruster }
30452f50b1eSMarkus Armbruster
readline_set_completion_index(ReadLineState * rs,int index)3050150cd81SStefan Hajnoczi void readline_set_completion_index(ReadLineState *rs, int index)
3060150cd81SStefan Hajnoczi {
3070150cd81SStefan Hajnoczi rs->completion_index = index;
3080150cd81SStefan Hajnoczi }
3090150cd81SStefan Hajnoczi
completion_comp(const void * a,const void * b)310307b2f01SHani Benhabiles static int completion_comp(const void *a, const void *b)
311307b2f01SHani Benhabiles {
312307b2f01SHani Benhabiles return strcmp(*(const char **) a, *(const char **) b);
313307b2f01SHani Benhabiles }
314307b2f01SHani Benhabiles
readline_completion(ReadLineState * rs)3150150cd81SStefan Hajnoczi static void readline_completion(ReadLineState *rs)
3160150cd81SStefan Hajnoczi {
3170150cd81SStefan Hajnoczi int len, i, j, max_width, nb_cols, max_prefix;
3180150cd81SStefan Hajnoczi char *cmdline;
3190150cd81SStefan Hajnoczi
3200150cd81SStefan Hajnoczi rs->nb_completions = 0;
3210150cd81SStefan Hajnoczi
3226ad7c326SMichael Tokarev cmdline = g_strndup(rs->cmd_buf, rs->cmd_buf_index);
3230150cd81SStefan Hajnoczi rs->completion_finder(rs->opaque, cmdline);
3240150cd81SStefan Hajnoczi g_free(cmdline);
3250150cd81SStefan Hajnoczi
3260150cd81SStefan Hajnoczi /* no completion found */
327e238a99eSJules Irenge if (rs->nb_completions <= 0) {
3280150cd81SStefan Hajnoczi return;
329e238a99eSJules Irenge }
3300150cd81SStefan Hajnoczi if (rs->nb_completions == 1) {
3310150cd81SStefan Hajnoczi len = strlen(rs->completions[0]);
3320150cd81SStefan Hajnoczi for (i = rs->completion_index; i < len; i++) {
3330150cd81SStefan Hajnoczi readline_insert_char(rs, rs->completions[0][i]);
3340150cd81SStefan Hajnoczi }
3350150cd81SStefan Hajnoczi /* extra space for next argument. XXX: make it more generic */
336e238a99eSJules Irenge if (len > 0 && rs->completions[0][len - 1] != '/') {
3370150cd81SStefan Hajnoczi readline_insert_char(rs, ' ');
338e238a99eSJules Irenge }
3390150cd81SStefan Hajnoczi } else {
340307b2f01SHani Benhabiles qsort(rs->completions, rs->nb_completions, sizeof(char *),
341307b2f01SHani Benhabiles completion_comp);
3420150cd81SStefan Hajnoczi rs->printf_func(rs->opaque, "\n");
3430150cd81SStefan Hajnoczi max_width = 0;
3440150cd81SStefan Hajnoczi max_prefix = 0;
3450150cd81SStefan Hajnoczi for (i = 0; i < rs->nb_completions; i++) {
3460150cd81SStefan Hajnoczi len = strlen(rs->completions[i]);
3470150cd81SStefan Hajnoczi if (i == 0) {
3480150cd81SStefan Hajnoczi max_prefix = len;
3490150cd81SStefan Hajnoczi } else {
350e238a99eSJules Irenge if (len < max_prefix) {
3510150cd81SStefan Hajnoczi max_prefix = len;
352e238a99eSJules Irenge }
3530150cd81SStefan Hajnoczi for (j = 0; j < max_prefix; j++) {
354e238a99eSJules Irenge if (rs->completions[i][j] != rs->completions[0][j]) {
3550150cd81SStefan Hajnoczi max_prefix = j;
3560150cd81SStefan Hajnoczi }
3570150cd81SStefan Hajnoczi }
358e238a99eSJules Irenge }
359e238a99eSJules Irenge if (len > max_width) {
3600150cd81SStefan Hajnoczi max_width = len;
3610150cd81SStefan Hajnoczi }
362e238a99eSJules Irenge }
3630150cd81SStefan Hajnoczi if (max_prefix > 0)
3640150cd81SStefan Hajnoczi for (i = rs->completion_index; i < max_prefix; i++) {
3650150cd81SStefan Hajnoczi readline_insert_char(rs, rs->completions[0][i]);
3660150cd81SStefan Hajnoczi }
3670150cd81SStefan Hajnoczi max_width += 2;
368e238a99eSJules Irenge if (max_width < 10) {
3690150cd81SStefan Hajnoczi max_width = 10;
370e238a99eSJules Irenge } else if (max_width > 80) {
3710150cd81SStefan Hajnoczi max_width = 80;
372e238a99eSJules Irenge }
3730150cd81SStefan Hajnoczi nb_cols = 80 / max_width;
3740150cd81SStefan Hajnoczi j = 0;
3750150cd81SStefan Hajnoczi for (i = 0; i < rs->nb_completions; i++) {
3760150cd81SStefan Hajnoczi rs->printf_func(rs->opaque, "%-*s", max_width, rs->completions[i]);
3770150cd81SStefan Hajnoczi if (++j == nb_cols || i == (rs->nb_completions - 1)) {
3780150cd81SStefan Hajnoczi rs->printf_func(rs->opaque, "\n");
3790150cd81SStefan Hajnoczi j = 0;
3800150cd81SStefan Hajnoczi }
3810150cd81SStefan Hajnoczi }
3820150cd81SStefan Hajnoczi readline_show_prompt(rs);
3830150cd81SStefan Hajnoczi }
3840150cd81SStefan Hajnoczi for (i = 0; i < rs->nb_completions; i++) {
3850150cd81SStefan Hajnoczi g_free(rs->completions[i]);
3860150cd81SStefan Hajnoczi }
3870150cd81SStefan Hajnoczi }
3880150cd81SStefan Hajnoczi
readline_clear_screen(ReadLineState * rs)389075ccb6cSHani Benhabiles static void readline_clear_screen(ReadLineState *rs)
390075ccb6cSHani Benhabiles {
391075ccb6cSHani Benhabiles rs->printf_func(rs->opaque, "\033[2J\033[1;1H");
392075ccb6cSHani Benhabiles readline_show_prompt(rs);
393075ccb6cSHani Benhabiles }
394075ccb6cSHani Benhabiles
3950150cd81SStefan Hajnoczi /* return true if command handled */
readline_handle_byte(ReadLineState * rs,int ch)3960150cd81SStefan Hajnoczi void readline_handle_byte(ReadLineState *rs, int ch)
3970150cd81SStefan Hajnoczi {
3980150cd81SStefan Hajnoczi switch (rs->esc_state) {
3990150cd81SStefan Hajnoczi case IS_NORM:
4000150cd81SStefan Hajnoczi switch (ch) {
4010150cd81SStefan Hajnoczi case 1:
4020150cd81SStefan Hajnoczi readline_bol(rs);
4030150cd81SStefan Hajnoczi break;
4040150cd81SStefan Hajnoczi case 4:
4050150cd81SStefan Hajnoczi readline_delete_char(rs);
4060150cd81SStefan Hajnoczi break;
4070150cd81SStefan Hajnoczi case 5:
4080150cd81SStefan Hajnoczi readline_eol(rs);
4090150cd81SStefan Hajnoczi break;
4100150cd81SStefan Hajnoczi case 9:
4110150cd81SStefan Hajnoczi readline_completion(rs);
4120150cd81SStefan Hajnoczi break;
413075ccb6cSHani Benhabiles case 12:
414075ccb6cSHani Benhabiles readline_clear_screen(rs);
415075ccb6cSHani Benhabiles break;
4169051350dSManos Pitsidianakis case 10: /* fallthrough */
4170150cd81SStefan Hajnoczi case 13:
4180150cd81SStefan Hajnoczi rs->cmd_buf[rs->cmd_buf_size] = '\0';
419e238a99eSJules Irenge if (!rs->read_password) {
4200150cd81SStefan Hajnoczi readline_hist_add(rs, rs->cmd_buf);
421e238a99eSJules Irenge }
4220150cd81SStefan Hajnoczi rs->printf_func(rs->opaque, "\n");
4230150cd81SStefan Hajnoczi rs->cmd_buf_index = 0;
4240150cd81SStefan Hajnoczi rs->cmd_buf_size = 0;
4250150cd81SStefan Hajnoczi rs->last_cmd_buf_index = 0;
4260150cd81SStefan Hajnoczi rs->last_cmd_buf_size = 0;
4270150cd81SStefan Hajnoczi rs->readline_func(rs->opaque, rs->cmd_buf, rs->readline_opaque);
4280150cd81SStefan Hajnoczi break;
42996c99e38SManos Pitsidianakis case 14:
43096c99e38SManos Pitsidianakis /* ^N Next line in history */
43196c99e38SManos Pitsidianakis readline_down_char(rs);
43296c99e38SManos Pitsidianakis break;
43396c99e38SManos Pitsidianakis case 16:
43496c99e38SManos Pitsidianakis /* ^P Prev line in history */
43596c99e38SManos Pitsidianakis readline_up_char(rs);
43696c99e38SManos Pitsidianakis break;
437*e1e55d34SManos Pitsidianakis case 21:
438*e1e55d34SManos Pitsidianakis /* ^U Kill backward from point to the beginning of the line. */
439*e1e55d34SManos Pitsidianakis readline_kill_line(rs);
440*e1e55d34SManos Pitsidianakis break;
4410150cd81SStefan Hajnoczi case 23:
4420150cd81SStefan Hajnoczi /* ^W */
4430150cd81SStefan Hajnoczi readline_backword(rs);
4440150cd81SStefan Hajnoczi break;
4450150cd81SStefan Hajnoczi case 27:
4460150cd81SStefan Hajnoczi rs->esc_state = IS_ESC;
4470150cd81SStefan Hajnoczi break;
4489051350dSManos Pitsidianakis case 127: /* fallthrough */
4490150cd81SStefan Hajnoczi case 8:
4500150cd81SStefan Hajnoczi readline_backspace(rs);
4510150cd81SStefan Hajnoczi break;
4520150cd81SStefan Hajnoczi case 155:
4530150cd81SStefan Hajnoczi rs->esc_state = IS_CSI;
4540150cd81SStefan Hajnoczi break;
4550150cd81SStefan Hajnoczi default:
4560150cd81SStefan Hajnoczi if (ch >= 32) {
4570150cd81SStefan Hajnoczi readline_insert_char(rs, ch);
4580150cd81SStefan Hajnoczi }
4590150cd81SStefan Hajnoczi break;
4600150cd81SStefan Hajnoczi }
4610150cd81SStefan Hajnoczi break;
4620150cd81SStefan Hajnoczi case IS_ESC:
4630150cd81SStefan Hajnoczi if (ch == '[') {
4640150cd81SStefan Hajnoczi rs->esc_state = IS_CSI;
4650150cd81SStefan Hajnoczi rs->esc_param = 0;
4660150cd81SStefan Hajnoczi } else if (ch == 'O') {
4670150cd81SStefan Hajnoczi rs->esc_state = IS_SS3;
4680150cd81SStefan Hajnoczi rs->esc_param = 0;
4690150cd81SStefan Hajnoczi } else {
4700150cd81SStefan Hajnoczi rs->esc_state = IS_NORM;
4710150cd81SStefan Hajnoczi }
4720150cd81SStefan Hajnoczi break;
4730150cd81SStefan Hajnoczi case IS_CSI:
4740150cd81SStefan Hajnoczi switch (ch) {
4759051350dSManos Pitsidianakis case 'A': /* fallthrough */
4760150cd81SStefan Hajnoczi case 'F':
4770150cd81SStefan Hajnoczi readline_up_char(rs);
4780150cd81SStefan Hajnoczi break;
4799051350dSManos Pitsidianakis case 'B': /* fallthrough */
4800150cd81SStefan Hajnoczi case 'E':
4810150cd81SStefan Hajnoczi readline_down_char(rs);
4820150cd81SStefan Hajnoczi break;
4830150cd81SStefan Hajnoczi case 'D':
4840150cd81SStefan Hajnoczi readline_backward_char(rs);
4850150cd81SStefan Hajnoczi break;
4860150cd81SStefan Hajnoczi case 'C':
4870150cd81SStefan Hajnoczi readline_forward_char(rs);
4880150cd81SStefan Hajnoczi break;
4890150cd81SStefan Hajnoczi case '0' ... '9':
4900150cd81SStefan Hajnoczi rs->esc_param = rs->esc_param * 10 + (ch - '0');
4910150cd81SStefan Hajnoczi goto the_end;
4920150cd81SStefan Hajnoczi case '~':
4930150cd81SStefan Hajnoczi switch (rs->esc_param) {
4940150cd81SStefan Hajnoczi case 1:
4950150cd81SStefan Hajnoczi readline_bol(rs);
4960150cd81SStefan Hajnoczi break;
4970150cd81SStefan Hajnoczi case 3:
4980150cd81SStefan Hajnoczi readline_delete_char(rs);
4990150cd81SStefan Hajnoczi break;
5000150cd81SStefan Hajnoczi case 4:
5010150cd81SStefan Hajnoczi readline_eol(rs);
5020150cd81SStefan Hajnoczi break;
5039051350dSManos Pitsidianakis default:
5049051350dSManos Pitsidianakis break;
5050150cd81SStefan Hajnoczi }
5060150cd81SStefan Hajnoczi break;
5070150cd81SStefan Hajnoczi default:
5080150cd81SStefan Hajnoczi break;
5090150cd81SStefan Hajnoczi }
5100150cd81SStefan Hajnoczi rs->esc_state = IS_NORM;
5119051350dSManos Pitsidianakis /* fallthrough */
5120150cd81SStefan Hajnoczi the_end:
5130150cd81SStefan Hajnoczi break;
5140150cd81SStefan Hajnoczi case IS_SS3:
5150150cd81SStefan Hajnoczi switch (ch) {
5160150cd81SStefan Hajnoczi case 'F':
5170150cd81SStefan Hajnoczi readline_eol(rs);
5180150cd81SStefan Hajnoczi break;
5190150cd81SStefan Hajnoczi case 'H':
5200150cd81SStefan Hajnoczi readline_bol(rs);
5210150cd81SStefan Hajnoczi break;
5229051350dSManos Pitsidianakis default:
5239051350dSManos Pitsidianakis break;
5240150cd81SStefan Hajnoczi }
5250150cd81SStefan Hajnoczi rs->esc_state = IS_NORM;
5260150cd81SStefan Hajnoczi break;
5279051350dSManos Pitsidianakis default:
5289051350dSManos Pitsidianakis break;
5290150cd81SStefan Hajnoczi }
5300150cd81SStefan Hajnoczi readline_update(rs);
5310150cd81SStefan Hajnoczi }
5320150cd81SStefan Hajnoczi
readline_start(ReadLineState * rs,const char * prompt,int read_password,ReadLineFunc * readline_func,void * opaque)5330150cd81SStefan Hajnoczi void readline_start(ReadLineState *rs, const char *prompt, int read_password,
5340150cd81SStefan Hajnoczi ReadLineFunc *readline_func, void *opaque)
5350150cd81SStefan Hajnoczi {
5360150cd81SStefan Hajnoczi pstrcpy(rs->prompt, sizeof(rs->prompt), prompt);
5370150cd81SStefan Hajnoczi rs->readline_func = readline_func;
5380150cd81SStefan Hajnoczi rs->readline_opaque = opaque;
5390150cd81SStefan Hajnoczi rs->read_password = read_password;
5400150cd81SStefan Hajnoczi readline_restart(rs);
5410150cd81SStefan Hajnoczi }
5420150cd81SStefan Hajnoczi
readline_restart(ReadLineState * rs)5430150cd81SStefan Hajnoczi void readline_restart(ReadLineState *rs)
5440150cd81SStefan Hajnoczi {
5450150cd81SStefan Hajnoczi rs->cmd_buf_index = 0;
5460150cd81SStefan Hajnoczi rs->cmd_buf_size = 0;
5470150cd81SStefan Hajnoczi }
5480150cd81SStefan Hajnoczi
readline_get_history(ReadLineState * rs,unsigned int index)5490150cd81SStefan Hajnoczi const char *readline_get_history(ReadLineState *rs, unsigned int index)
5500150cd81SStefan Hajnoczi {
551e238a99eSJules Irenge if (index >= READLINE_MAX_CMDS) {
5520150cd81SStefan Hajnoczi return NULL;
553e238a99eSJules Irenge }
5540150cd81SStefan Hajnoczi return rs->history[index];
5550150cd81SStefan Hajnoczi }
5560150cd81SStefan Hajnoczi
readline_free(ReadLineState * rs)557e5dc1a6cSMarc-André Lureau void readline_free(ReadLineState *rs)
558e5dc1a6cSMarc-André Lureau {
559e5dc1a6cSMarc-André Lureau int i;
560e5dc1a6cSMarc-André Lureau
561e5dc1a6cSMarc-André Lureau if (!rs) {
562e5dc1a6cSMarc-André Lureau return;
563e5dc1a6cSMarc-André Lureau }
564e5dc1a6cSMarc-André Lureau for (i = 0; i < READLINE_MAX_CMDS; i++) {
565e5dc1a6cSMarc-André Lureau g_free(rs->history[i]);
566e5dc1a6cSMarc-André Lureau }
567e5dc1a6cSMarc-André Lureau g_free(rs);
568e5dc1a6cSMarc-André Lureau }
569e5dc1a6cSMarc-André Lureau
readline_init(ReadLinePrintfFunc * printf_func,ReadLineFlushFunc * flush_func,void * opaque,ReadLineCompletionFunc * completion_finder)5700150cd81SStefan Hajnoczi ReadLineState *readline_init(ReadLinePrintfFunc *printf_func,
5710150cd81SStefan Hajnoczi ReadLineFlushFunc *flush_func,
5720150cd81SStefan Hajnoczi void *opaque,
5730150cd81SStefan Hajnoczi ReadLineCompletionFunc *completion_finder)
5740150cd81SStefan Hajnoczi {
575e5dc1a6cSMarc-André Lureau ReadLineState *rs = g_new0(ReadLineState, 1);
5760150cd81SStefan Hajnoczi
5770150cd81SStefan Hajnoczi rs->hist_entry = -1;
5780150cd81SStefan Hajnoczi rs->opaque = opaque;
5790150cd81SStefan Hajnoczi rs->printf_func = printf_func;
5800150cd81SStefan Hajnoczi rs->flush_func = flush_func;
5810150cd81SStefan Hajnoczi rs->completion_finder = completion_finder;
5820150cd81SStefan Hajnoczi
5830150cd81SStefan Hajnoczi return rs;
5840150cd81SStefan Hajnoczi }
585