xref: /openbmc/qemu/util/readline.c (revision 52f50b1e)
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 
2740150cd81SStefan Hajnoczi /* completion support */
2750150cd81SStefan Hajnoczi 
readline_add_completion(ReadLineState * rs,const char * str)2760150cd81SStefan Hajnoczi void readline_add_completion(ReadLineState *rs, const char *str)
2770150cd81SStefan Hajnoczi {
2780150cd81SStefan Hajnoczi     if (rs->nb_completions < READLINE_MAX_COMPLETIONS) {
279e70871d8SHani Benhabiles         int i;
280e70871d8SHani Benhabiles         for (i = 0; i < rs->nb_completions; i++) {
281e70871d8SHani Benhabiles             if (!strcmp(rs->completions[i], str)) {
282e70871d8SHani Benhabiles                 return;
283e70871d8SHani Benhabiles             }
284e70871d8SHani Benhabiles         }
2850150cd81SStefan Hajnoczi         rs->completions[rs->nb_completions++] = g_strdup(str);
2860150cd81SStefan Hajnoczi     }
2870150cd81SStefan Hajnoczi }
2880150cd81SStefan Hajnoczi 
readline_add_completion_of(ReadLineState * rs,const char * pfx,const char * str)289*52f50b1eSMarkus Armbruster void readline_add_completion_of(ReadLineState *rs,
290*52f50b1eSMarkus Armbruster                                 const char *pfx, const char *str)
291*52f50b1eSMarkus Armbruster {
292*52f50b1eSMarkus Armbruster     if (!strncmp(str, pfx, strlen(pfx))) {
293*52f50b1eSMarkus Armbruster         readline_add_completion(rs, str);
294*52f50b1eSMarkus Armbruster     }
295*52f50b1eSMarkus Armbruster }
296*52f50b1eSMarkus Armbruster 
readline_set_completion_index(ReadLineState * rs,int index)2970150cd81SStefan Hajnoczi void readline_set_completion_index(ReadLineState *rs, int index)
2980150cd81SStefan Hajnoczi {
2990150cd81SStefan Hajnoczi     rs->completion_index = index;
3000150cd81SStefan Hajnoczi }
3010150cd81SStefan Hajnoczi 
completion_comp(const void * a,const void * b)302307b2f01SHani Benhabiles static int completion_comp(const void *a, const void *b)
303307b2f01SHani Benhabiles {
304307b2f01SHani Benhabiles     return strcmp(*(const char **) a, *(const char **) b);
305307b2f01SHani Benhabiles }
306307b2f01SHani Benhabiles 
readline_completion(ReadLineState * rs)3070150cd81SStefan Hajnoczi static void readline_completion(ReadLineState *rs)
3080150cd81SStefan Hajnoczi {
3090150cd81SStefan Hajnoczi     int len, i, j, max_width, nb_cols, max_prefix;
3100150cd81SStefan Hajnoczi     char *cmdline;
3110150cd81SStefan Hajnoczi 
3120150cd81SStefan Hajnoczi     rs->nb_completions = 0;
3130150cd81SStefan Hajnoczi 
3146ad7c326SMichael Tokarev     cmdline = g_strndup(rs->cmd_buf, rs->cmd_buf_index);
3150150cd81SStefan Hajnoczi     rs->completion_finder(rs->opaque, cmdline);
3160150cd81SStefan Hajnoczi     g_free(cmdline);
3170150cd81SStefan Hajnoczi 
3180150cd81SStefan Hajnoczi     /* no completion found */
319e238a99eSJules Irenge     if (rs->nb_completions <= 0) {
3200150cd81SStefan Hajnoczi         return;
321e238a99eSJules Irenge     }
3220150cd81SStefan Hajnoczi     if (rs->nb_completions == 1) {
3230150cd81SStefan Hajnoczi         len = strlen(rs->completions[0]);
3240150cd81SStefan Hajnoczi         for (i = rs->completion_index; i < len; i++) {
3250150cd81SStefan Hajnoczi             readline_insert_char(rs, rs->completions[0][i]);
3260150cd81SStefan Hajnoczi         }
3270150cd81SStefan Hajnoczi         /* extra space for next argument. XXX: make it more generic */
328e238a99eSJules Irenge         if (len > 0 && rs->completions[0][len - 1] != '/') {
3290150cd81SStefan Hajnoczi             readline_insert_char(rs, ' ');
330e238a99eSJules Irenge         }
3310150cd81SStefan Hajnoczi     } else {
332307b2f01SHani Benhabiles         qsort(rs->completions, rs->nb_completions, sizeof(char *),
333307b2f01SHani Benhabiles               completion_comp);
3340150cd81SStefan Hajnoczi         rs->printf_func(rs->opaque, "\n");
3350150cd81SStefan Hajnoczi         max_width = 0;
3360150cd81SStefan Hajnoczi         max_prefix = 0;
3370150cd81SStefan Hajnoczi         for (i = 0; i < rs->nb_completions; i++) {
3380150cd81SStefan Hajnoczi             len = strlen(rs->completions[i]);
3390150cd81SStefan Hajnoczi             if (i == 0) {
3400150cd81SStefan Hajnoczi                 max_prefix = len;
3410150cd81SStefan Hajnoczi             } else {
342e238a99eSJules Irenge                 if (len < max_prefix) {
3430150cd81SStefan Hajnoczi                     max_prefix = len;
344e238a99eSJules Irenge                 }
3450150cd81SStefan Hajnoczi                 for (j = 0; j < max_prefix; j++) {
346e238a99eSJules Irenge                     if (rs->completions[i][j] != rs->completions[0][j]) {
3470150cd81SStefan Hajnoczi                         max_prefix = j;
3480150cd81SStefan Hajnoczi                     }
3490150cd81SStefan Hajnoczi                 }
350e238a99eSJules Irenge             }
351e238a99eSJules Irenge             if (len > max_width) {
3520150cd81SStefan Hajnoczi                 max_width = len;
3530150cd81SStefan Hajnoczi             }
354e238a99eSJules Irenge         }
3550150cd81SStefan Hajnoczi         if (max_prefix > 0)
3560150cd81SStefan Hajnoczi             for (i = rs->completion_index; i < max_prefix; i++) {
3570150cd81SStefan Hajnoczi                 readline_insert_char(rs, rs->completions[0][i]);
3580150cd81SStefan Hajnoczi             }
3590150cd81SStefan Hajnoczi         max_width += 2;
360e238a99eSJules Irenge         if (max_width < 10) {
3610150cd81SStefan Hajnoczi             max_width = 10;
362e238a99eSJules Irenge         } else if (max_width > 80) {
3630150cd81SStefan Hajnoczi             max_width = 80;
364e238a99eSJules Irenge         }
3650150cd81SStefan Hajnoczi         nb_cols = 80 / max_width;
3660150cd81SStefan Hajnoczi         j = 0;
3670150cd81SStefan Hajnoczi         for (i = 0; i < rs->nb_completions; i++) {
3680150cd81SStefan Hajnoczi             rs->printf_func(rs->opaque, "%-*s", max_width, rs->completions[i]);
3690150cd81SStefan Hajnoczi             if (++j == nb_cols || i == (rs->nb_completions - 1)) {
3700150cd81SStefan Hajnoczi                 rs->printf_func(rs->opaque, "\n");
3710150cd81SStefan Hajnoczi                 j = 0;
3720150cd81SStefan Hajnoczi             }
3730150cd81SStefan Hajnoczi         }
3740150cd81SStefan Hajnoczi         readline_show_prompt(rs);
3750150cd81SStefan Hajnoczi     }
3760150cd81SStefan Hajnoczi     for (i = 0; i < rs->nb_completions; i++) {
3770150cd81SStefan Hajnoczi         g_free(rs->completions[i]);
3780150cd81SStefan Hajnoczi     }
3790150cd81SStefan Hajnoczi }
3800150cd81SStefan Hajnoczi 
readline_clear_screen(ReadLineState * rs)381075ccb6cSHani Benhabiles static void readline_clear_screen(ReadLineState *rs)
382075ccb6cSHani Benhabiles {
383075ccb6cSHani Benhabiles     rs->printf_func(rs->opaque, "\033[2J\033[1;1H");
384075ccb6cSHani Benhabiles     readline_show_prompt(rs);
385075ccb6cSHani Benhabiles }
386075ccb6cSHani Benhabiles 
3870150cd81SStefan Hajnoczi /* return true if command handled */
readline_handle_byte(ReadLineState * rs,int ch)3880150cd81SStefan Hajnoczi void readline_handle_byte(ReadLineState *rs, int ch)
3890150cd81SStefan Hajnoczi {
3900150cd81SStefan Hajnoczi     switch (rs->esc_state) {
3910150cd81SStefan Hajnoczi     case IS_NORM:
3920150cd81SStefan Hajnoczi         switch (ch) {
3930150cd81SStefan Hajnoczi         case 1:
3940150cd81SStefan Hajnoczi             readline_bol(rs);
3950150cd81SStefan Hajnoczi             break;
3960150cd81SStefan Hajnoczi         case 4:
3970150cd81SStefan Hajnoczi             readline_delete_char(rs);
3980150cd81SStefan Hajnoczi             break;
3990150cd81SStefan Hajnoczi         case 5:
4000150cd81SStefan Hajnoczi             readline_eol(rs);
4010150cd81SStefan Hajnoczi             break;
4020150cd81SStefan Hajnoczi         case 9:
4030150cd81SStefan Hajnoczi             readline_completion(rs);
4040150cd81SStefan Hajnoczi             break;
405075ccb6cSHani Benhabiles         case 12:
406075ccb6cSHani Benhabiles             readline_clear_screen(rs);
407075ccb6cSHani Benhabiles             break;
4080150cd81SStefan Hajnoczi         case 10:
4090150cd81SStefan Hajnoczi         case 13:
4100150cd81SStefan Hajnoczi             rs->cmd_buf[rs->cmd_buf_size] = '\0';
411e238a99eSJules Irenge             if (!rs->read_password) {
4120150cd81SStefan Hajnoczi                 readline_hist_add(rs, rs->cmd_buf);
413e238a99eSJules Irenge             }
4140150cd81SStefan Hajnoczi             rs->printf_func(rs->opaque, "\n");
4150150cd81SStefan Hajnoczi             rs->cmd_buf_index = 0;
4160150cd81SStefan Hajnoczi             rs->cmd_buf_size = 0;
4170150cd81SStefan Hajnoczi             rs->last_cmd_buf_index = 0;
4180150cd81SStefan Hajnoczi             rs->last_cmd_buf_size = 0;
4190150cd81SStefan Hajnoczi             rs->readline_func(rs->opaque, rs->cmd_buf, rs->readline_opaque);
4200150cd81SStefan Hajnoczi             break;
4210150cd81SStefan Hajnoczi         case 23:
4220150cd81SStefan Hajnoczi             /* ^W */
4230150cd81SStefan Hajnoczi             readline_backword(rs);
4240150cd81SStefan Hajnoczi             break;
4250150cd81SStefan Hajnoczi         case 27:
4260150cd81SStefan Hajnoczi             rs->esc_state = IS_ESC;
4270150cd81SStefan Hajnoczi             break;
4280150cd81SStefan Hajnoczi         case 127:
4290150cd81SStefan Hajnoczi         case 8:
4300150cd81SStefan Hajnoczi             readline_backspace(rs);
4310150cd81SStefan Hajnoczi             break;
4320150cd81SStefan Hajnoczi         case 155:
4330150cd81SStefan Hajnoczi             rs->esc_state = IS_CSI;
4340150cd81SStefan Hajnoczi             break;
4350150cd81SStefan Hajnoczi         default:
4360150cd81SStefan Hajnoczi             if (ch >= 32) {
4370150cd81SStefan Hajnoczi                 readline_insert_char(rs, ch);
4380150cd81SStefan Hajnoczi             }
4390150cd81SStefan Hajnoczi             break;
4400150cd81SStefan Hajnoczi         }
4410150cd81SStefan Hajnoczi         break;
4420150cd81SStefan Hajnoczi     case IS_ESC:
4430150cd81SStefan Hajnoczi         if (ch == '[') {
4440150cd81SStefan Hajnoczi             rs->esc_state = IS_CSI;
4450150cd81SStefan Hajnoczi             rs->esc_param = 0;
4460150cd81SStefan Hajnoczi         } else if (ch == 'O') {
4470150cd81SStefan Hajnoczi             rs->esc_state = IS_SS3;
4480150cd81SStefan Hajnoczi             rs->esc_param = 0;
4490150cd81SStefan Hajnoczi         } else {
4500150cd81SStefan Hajnoczi             rs->esc_state = IS_NORM;
4510150cd81SStefan Hajnoczi         }
4520150cd81SStefan Hajnoczi         break;
4530150cd81SStefan Hajnoczi     case IS_CSI:
4540150cd81SStefan Hajnoczi         switch (ch) {
4550150cd81SStefan Hajnoczi         case 'A':
4560150cd81SStefan Hajnoczi         case 'F':
4570150cd81SStefan Hajnoczi             readline_up_char(rs);
4580150cd81SStefan Hajnoczi             break;
4590150cd81SStefan Hajnoczi         case 'B':
4600150cd81SStefan Hajnoczi         case 'E':
4610150cd81SStefan Hajnoczi             readline_down_char(rs);
4620150cd81SStefan Hajnoczi             break;
4630150cd81SStefan Hajnoczi         case 'D':
4640150cd81SStefan Hajnoczi             readline_backward_char(rs);
4650150cd81SStefan Hajnoczi             break;
4660150cd81SStefan Hajnoczi         case 'C':
4670150cd81SStefan Hajnoczi             readline_forward_char(rs);
4680150cd81SStefan Hajnoczi             break;
4690150cd81SStefan Hajnoczi         case '0' ... '9':
4700150cd81SStefan Hajnoczi             rs->esc_param = rs->esc_param * 10 + (ch - '0');
4710150cd81SStefan Hajnoczi             goto the_end;
4720150cd81SStefan Hajnoczi         case '~':
4730150cd81SStefan Hajnoczi             switch (rs->esc_param) {
4740150cd81SStefan Hajnoczi             case 1:
4750150cd81SStefan Hajnoczi                 readline_bol(rs);
4760150cd81SStefan Hajnoczi                 break;
4770150cd81SStefan Hajnoczi             case 3:
4780150cd81SStefan Hajnoczi                 readline_delete_char(rs);
4790150cd81SStefan Hajnoczi                 break;
4800150cd81SStefan Hajnoczi             case 4:
4810150cd81SStefan Hajnoczi                 readline_eol(rs);
4820150cd81SStefan Hajnoczi                 break;
4830150cd81SStefan Hajnoczi             }
4840150cd81SStefan Hajnoczi             break;
4850150cd81SStefan Hajnoczi         default:
4860150cd81SStefan Hajnoczi             break;
4870150cd81SStefan Hajnoczi         }
4880150cd81SStefan Hajnoczi         rs->esc_state = IS_NORM;
4890150cd81SStefan Hajnoczi     the_end:
4900150cd81SStefan Hajnoczi         break;
4910150cd81SStefan Hajnoczi     case IS_SS3:
4920150cd81SStefan Hajnoczi         switch (ch) {
4930150cd81SStefan Hajnoczi         case 'F':
4940150cd81SStefan Hajnoczi             readline_eol(rs);
4950150cd81SStefan Hajnoczi             break;
4960150cd81SStefan Hajnoczi         case 'H':
4970150cd81SStefan Hajnoczi             readline_bol(rs);
4980150cd81SStefan Hajnoczi             break;
4990150cd81SStefan Hajnoczi         }
5000150cd81SStefan Hajnoczi         rs->esc_state = IS_NORM;
5010150cd81SStefan Hajnoczi         break;
5020150cd81SStefan Hajnoczi     }
5030150cd81SStefan Hajnoczi     readline_update(rs);
5040150cd81SStefan Hajnoczi }
5050150cd81SStefan Hajnoczi 
readline_start(ReadLineState * rs,const char * prompt,int read_password,ReadLineFunc * readline_func,void * opaque)5060150cd81SStefan Hajnoczi void readline_start(ReadLineState *rs, const char *prompt, int read_password,
5070150cd81SStefan Hajnoczi                     ReadLineFunc *readline_func, void *opaque)
5080150cd81SStefan Hajnoczi {
5090150cd81SStefan Hajnoczi     pstrcpy(rs->prompt, sizeof(rs->prompt), prompt);
5100150cd81SStefan Hajnoczi     rs->readline_func = readline_func;
5110150cd81SStefan Hajnoczi     rs->readline_opaque = opaque;
5120150cd81SStefan Hajnoczi     rs->read_password = read_password;
5130150cd81SStefan Hajnoczi     readline_restart(rs);
5140150cd81SStefan Hajnoczi }
5150150cd81SStefan Hajnoczi 
readline_restart(ReadLineState * rs)5160150cd81SStefan Hajnoczi void readline_restart(ReadLineState *rs)
5170150cd81SStefan Hajnoczi {
5180150cd81SStefan Hajnoczi     rs->cmd_buf_index = 0;
5190150cd81SStefan Hajnoczi     rs->cmd_buf_size = 0;
5200150cd81SStefan Hajnoczi }
5210150cd81SStefan Hajnoczi 
readline_get_history(ReadLineState * rs,unsigned int index)5220150cd81SStefan Hajnoczi const char *readline_get_history(ReadLineState *rs, unsigned int index)
5230150cd81SStefan Hajnoczi {
524e238a99eSJules Irenge     if (index >= READLINE_MAX_CMDS) {
5250150cd81SStefan Hajnoczi         return NULL;
526e238a99eSJules Irenge     }
5270150cd81SStefan Hajnoczi     return rs->history[index];
5280150cd81SStefan Hajnoczi }
5290150cd81SStefan Hajnoczi 
readline_free(ReadLineState * rs)530e5dc1a6cSMarc-André Lureau void readline_free(ReadLineState *rs)
531e5dc1a6cSMarc-André Lureau {
532e5dc1a6cSMarc-André Lureau     int i;
533e5dc1a6cSMarc-André Lureau 
534e5dc1a6cSMarc-André Lureau     if (!rs) {
535e5dc1a6cSMarc-André Lureau         return;
536e5dc1a6cSMarc-André Lureau     }
537e5dc1a6cSMarc-André Lureau     for (i = 0; i < READLINE_MAX_CMDS; i++) {
538e5dc1a6cSMarc-André Lureau         g_free(rs->history[i]);
539e5dc1a6cSMarc-André Lureau     }
540e5dc1a6cSMarc-André Lureau     g_free(rs);
541e5dc1a6cSMarc-André Lureau }
542e5dc1a6cSMarc-André Lureau 
readline_init(ReadLinePrintfFunc * printf_func,ReadLineFlushFunc * flush_func,void * opaque,ReadLineCompletionFunc * completion_finder)5430150cd81SStefan Hajnoczi ReadLineState *readline_init(ReadLinePrintfFunc *printf_func,
5440150cd81SStefan Hajnoczi                              ReadLineFlushFunc *flush_func,
5450150cd81SStefan Hajnoczi                              void *opaque,
5460150cd81SStefan Hajnoczi                              ReadLineCompletionFunc *completion_finder)
5470150cd81SStefan Hajnoczi {
548e5dc1a6cSMarc-André Lureau     ReadLineState *rs = g_new0(ReadLineState, 1);
5490150cd81SStefan Hajnoczi 
5500150cd81SStefan Hajnoczi     rs->hist_entry = -1;
5510150cd81SStefan Hajnoczi     rs->opaque = opaque;
5520150cd81SStefan Hajnoczi     rs->printf_func = printf_func;
5530150cd81SStefan Hajnoczi     rs->flush_func = flush_func;
5540150cd81SStefan Hajnoczi     rs->completion_finder = completion_finder;
5550150cd81SStefan Hajnoczi 
5560150cd81SStefan Hajnoczi     return rs;
5570150cd81SStefan Hajnoczi }
558