2
* QEMU readline utility
4
* Copyright (c) 2003-2004 Fabrice Bellard
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:
13
* The above copyright notice and this permission notice shall be included in
14
* all copies or substantial portions of the Software.
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
24
#include "qemu-common.h"
27
#define TERM_CMD_BUF_SIZE 4095
28
#define TERM_MAX_CMDS 64
29
#define NB_COMPLETIONS_MAX 256
35
#define printf do_not_use_printf
37
static char term_cmd_buf[TERM_CMD_BUF_SIZE + 1];
38
static int term_cmd_buf_index;
39
static int term_cmd_buf_size;
41
static char term_last_cmd_buf[TERM_CMD_BUF_SIZE + 1];
42
static int term_last_cmd_buf_index;
43
static int term_last_cmd_buf_size;
45
static int term_esc_state;
46
static int term_esc_param;
48
static char *term_history[TERM_MAX_CMDS];
49
static int term_hist_entry = -1;
51
static int nb_completions;
53
static char *completions[NB_COMPLETIONS_MAX];
55
static ReadLineFunc *term_readline_func;
56
static int term_is_password;
57
static char term_prompt[256];
58
static void *term_readline_opaque;
60
static void term_show_prompt2(void)
62
term_printf("%s", term_prompt);
64
term_last_cmd_buf_index = 0;
65
term_last_cmd_buf_size = 0;
66
term_esc_state = IS_NORM;
69
static void term_show_prompt(void)
72
term_cmd_buf_index = 0;
73
term_cmd_buf_size = 0;
76
/* update the displayed command line */
77
static void term_update(void)
81
if (term_cmd_buf_size != term_last_cmd_buf_size ||
82
memcmp(term_cmd_buf, term_last_cmd_buf, term_cmd_buf_size) != 0) {
83
for(i = 0; i < term_last_cmd_buf_index; i++) {
84
term_printf("\033[D");
86
term_cmd_buf[term_cmd_buf_size] = '\0';
87
if (term_is_password) {
88
len = strlen(term_cmd_buf);
89
for(i = 0; i < len; i++)
92
term_printf("%s", term_cmd_buf);
94
term_printf("\033[K");
95
memcpy(term_last_cmd_buf, term_cmd_buf, term_cmd_buf_size);
96
term_last_cmd_buf_size = term_cmd_buf_size;
97
term_last_cmd_buf_index = term_cmd_buf_size;
99
if (term_cmd_buf_index != term_last_cmd_buf_index) {
100
delta = term_cmd_buf_index - term_last_cmd_buf_index;
102
for(i = 0;i < delta; i++) {
103
term_printf("\033[C");
107
for(i = 0;i < delta; i++) {
108
term_printf("\033[D");
111
term_last_cmd_buf_index = term_cmd_buf_index;
116
static void term_insert_char(int ch)
118
if (term_cmd_buf_index < TERM_CMD_BUF_SIZE) {
119
memmove(term_cmd_buf + term_cmd_buf_index + 1,
120
term_cmd_buf + term_cmd_buf_index,
121
term_cmd_buf_size - term_cmd_buf_index);
122
term_cmd_buf[term_cmd_buf_index] = ch;
124
term_cmd_buf_index++;
128
static void term_backward_char(void)
130
if (term_cmd_buf_index > 0) {
131
term_cmd_buf_index--;
135
static void term_forward_char(void)
137
if (term_cmd_buf_index < term_cmd_buf_size) {
138
term_cmd_buf_index++;
142
static void term_delete_char(void)
144
if (term_cmd_buf_index < term_cmd_buf_size) {
145
memmove(term_cmd_buf + term_cmd_buf_index,
146
term_cmd_buf + term_cmd_buf_index + 1,
147
term_cmd_buf_size - term_cmd_buf_index - 1);
152
static void term_backspace(void)
154
if (term_cmd_buf_index > 0) {
155
term_backward_char();
160
static void term_backword(void)
164
if (term_cmd_buf_index == 0 || term_cmd_buf_index > term_cmd_buf_size) {
168
start = term_cmd_buf_index - 1;
170
/* find first word (backwards) */
172
if (!isspace(term_cmd_buf[start])) {
179
/* find first space (backwards) */
181
if (isspace(term_cmd_buf[start])) {
190
if (start < term_cmd_buf_index) {
191
memmove(term_cmd_buf + start,
192
term_cmd_buf + term_cmd_buf_index,
193
term_cmd_buf_size - term_cmd_buf_index);
194
term_cmd_buf_size -= term_cmd_buf_index - start;
195
term_cmd_buf_index = start;
199
static void term_bol(void)
201
term_cmd_buf_index = 0;
204
static void term_eol(void)
206
term_cmd_buf_index = term_cmd_buf_size;
209
static void term_up_char(void)
213
if (term_hist_entry == 0)
215
if (term_hist_entry == -1) {
216
/* Find latest entry */
217
for (idx = 0; idx < TERM_MAX_CMDS; idx++) {
218
if (term_history[idx] == NULL)
221
term_hist_entry = idx;
224
if (term_hist_entry >= 0) {
225
pstrcpy(term_cmd_buf, sizeof(term_cmd_buf),
226
term_history[term_hist_entry]);
227
term_cmd_buf_index = term_cmd_buf_size = strlen(term_cmd_buf);
231
static void term_down_char(void)
233
if (term_hist_entry == TERM_MAX_CMDS - 1 || term_hist_entry == -1)
235
if (term_history[++term_hist_entry] != NULL) {
236
pstrcpy(term_cmd_buf, sizeof(term_cmd_buf),
237
term_history[term_hist_entry]);
239
term_hist_entry = -1;
241
term_cmd_buf_index = term_cmd_buf_size = strlen(term_cmd_buf);
244
static void term_hist_add(const char *cmdline)
246
char *hist_entry, *new_entry;
249
if (cmdline[0] == '\0')
252
if (term_hist_entry != -1) {
253
/* We were editing an existing history entry: replace it */
254
hist_entry = term_history[term_hist_entry];
255
idx = term_hist_entry;
256
if (strcmp(hist_entry, cmdline) == 0) {
260
/* Search cmdline in history buffers */
261
for (idx = 0; idx < TERM_MAX_CMDS; idx++) {
262
hist_entry = term_history[idx];
263
if (hist_entry == NULL)
265
if (strcmp(hist_entry, cmdline) == 0) {
267
new_entry = hist_entry;
268
/* Put this entry at the end of history */
269
memmove(&term_history[idx], &term_history[idx + 1],
270
(TERM_MAX_CMDS - idx + 1) * sizeof(char *));
271
term_history[TERM_MAX_CMDS - 1] = NULL;
272
for (; idx < TERM_MAX_CMDS; idx++) {
273
if (term_history[idx] == NULL)
279
if (idx == TERM_MAX_CMDS) {
280
/* Need to get one free slot */
281
free(term_history[0]);
282
memcpy(term_history, &term_history[1],
283
(TERM_MAX_CMDS - 1) * sizeof(char *));
284
term_history[TERM_MAX_CMDS - 1] = NULL;
285
idx = TERM_MAX_CMDS - 1;
287
if (new_entry == NULL)
288
new_entry = strdup(cmdline);
289
term_history[idx] = new_entry;
290
term_hist_entry = -1;
293
/* completion support */
295
void add_completion(const char *str)
297
if (nb_completions < NB_COMPLETIONS_MAX) {
298
completions[nb_completions++] = qemu_strdup(str);
302
static void term_completion(void)
304
int len, i, j, max_width, nb_cols, max_prefix;
309
cmdline = qemu_malloc(term_cmd_buf_index + 1);
312
memcpy(cmdline, term_cmd_buf, term_cmd_buf_index);
313
cmdline[term_cmd_buf_index] = '\0';
314
readline_find_completion(cmdline);
317
/* no completion found */
318
if (nb_completions <= 0)
320
if (nb_completions == 1) {
321
len = strlen(completions[0]);
322
for(i = completion_index; i < len; i++) {
323
term_insert_char(completions[0][i]);
325
/* extra space for next argument. XXX: make it more generic */
326
if (len > 0 && completions[0][len - 1] != '/')
327
term_insert_char(' ');
332
for(i = 0; i < nb_completions; i++) {
333
len = strlen(completions[i]);
337
if (len < max_prefix)
339
for(j=0; j<max_prefix; j++) {
340
if (completions[i][j] != completions[0][j])
348
for(i = completion_index; i < max_prefix; i++) {
349
term_insert_char(completions[0][i]);
354
else if (max_width > 80)
356
nb_cols = 80 / max_width;
358
for(i = 0; i < nb_completions; i++) {
359
term_printf("%-*s", max_width, completions[i]);
360
if (++j == nb_cols || i == (nb_completions - 1)) {
369
/* return true if command handled */
370
void readline_handle_byte(int ch)
372
switch(term_esc_state) {
389
term_cmd_buf[term_cmd_buf_size] = '\0';
390
if (!term_is_password)
391
term_hist_add(term_cmd_buf);
393
term_cmd_buf_index = 0;
394
term_cmd_buf_size = 0;
395
term_last_cmd_buf_index = 0;
396
term_last_cmd_buf_size = 0;
397
/* NOTE: readline_start can be called here */
398
term_readline_func(term_readline_opaque, term_cmd_buf);
405
term_esc_state = IS_ESC;
412
term_esc_state = IS_CSI;
416
term_insert_char(ch);
423
term_esc_state = IS_CSI;
426
term_esc_state = IS_NORM;
440
term_backward_char();
446
term_esc_param = term_esc_param * 10 + (ch - '0');
449
switch(term_esc_param) {
464
term_esc_state = IS_NORM;
471
void readline_start(const char *prompt, int is_password,
472
ReadLineFunc *readline_func, void *opaque)
474
pstrcpy(term_prompt, sizeof(term_prompt), prompt);
475
term_readline_func = readline_func;
476
term_readline_opaque = opaque;
477
term_is_password = is_password;
481
const char *readline_get_history(unsigned int index)
483
if (index >= TERM_MAX_CMDS)
485
return term_history[index];