Statistics
| Branch: | Revision:

root / readline.c @ 376253ec

History | View | Annotate | Download (12.7 kB)

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 "readline.h"
25
#include "monitor.h"
26

    
27
#define TERM_CMD_BUF_SIZE 4095
28
#define TERM_MAX_CMDS 64
29
#define NB_COMPLETIONS_MAX 256
30

    
31
#define IS_NORM 0
32
#define IS_ESC  1
33
#define IS_CSI  2
34

    
35
#define printf do_not_use_printf
36

    
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;
40

    
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;
44

    
45
static int term_esc_state;
46
static int term_esc_param;
47

    
48
static char *term_history[TERM_MAX_CMDS];
49
static int term_hist_entry = -1;
50

    
51
static int nb_completions;
52
static int completion_index;
53
static char *completions[NB_COMPLETIONS_MAX];
54

    
55
static ReadLineFunc *term_readline_func;
56
static int term_is_password;
57
static char term_prompt[256];
58
static void *term_readline_opaque;
59

    
60
void readline_show_prompt(void)
61
{
62
    Monitor *mon = cur_mon;
63

    
64
    monitor_printf(mon, "%s", term_prompt);
65
    monitor_flush(mon);
66
    term_last_cmd_buf_index = 0;
67
    term_last_cmd_buf_size = 0;
68
    term_esc_state = IS_NORM;
69
}
70

    
71
/* update the displayed command line */
72
static void term_update(void)
73
{
74
    Monitor *mon = cur_mon;
75
    int i, delta, len;
76

    
77
    if (term_cmd_buf_size != term_last_cmd_buf_size ||
78
        memcmp(term_cmd_buf, term_last_cmd_buf, term_cmd_buf_size) != 0) {
79
        for(i = 0; i < term_last_cmd_buf_index; i++) {
80
            monitor_printf(mon, "\033[D");
81
        }
82
        term_cmd_buf[term_cmd_buf_size] = '\0';
83
        if (term_is_password) {
84
            len = strlen(term_cmd_buf);
85
            for(i = 0; i < len; i++)
86
                monitor_printf(mon, "*");
87
        } else {
88
            monitor_printf(mon, "%s", term_cmd_buf);
89
        }
90
        monitor_printf(mon, "\033[K");
91
        memcpy(term_last_cmd_buf, term_cmd_buf, term_cmd_buf_size);
92
        term_last_cmd_buf_size = term_cmd_buf_size;
93
        term_last_cmd_buf_index = term_cmd_buf_size;
94
    }
95
    if (term_cmd_buf_index != term_last_cmd_buf_index) {
96
        delta = term_cmd_buf_index - term_last_cmd_buf_index;
97
        if (delta > 0) {
98
            for(i = 0;i < delta; i++) {
99
                monitor_printf(mon, "\033[C");
100
            }
101
        } else {
102
            delta = -delta;
103
            for(i = 0;i < delta; i++) {
104
                monitor_printf(mon, "\033[D");
105
            }
106
        }
107
        term_last_cmd_buf_index = term_cmd_buf_index;
108
    }
109
    monitor_flush(mon);
110
}
111

    
112
static void term_insert_char(int ch)
113
{
114
    if (term_cmd_buf_index < TERM_CMD_BUF_SIZE) {
115
        memmove(term_cmd_buf + term_cmd_buf_index + 1,
116
                term_cmd_buf + term_cmd_buf_index,
117
                term_cmd_buf_size - term_cmd_buf_index);
118
        term_cmd_buf[term_cmd_buf_index] = ch;
119
        term_cmd_buf_size++;
120
        term_cmd_buf_index++;
121
    }
122
}
123

    
124
static void term_backward_char(void)
125
{
126
    if (term_cmd_buf_index > 0) {
127
        term_cmd_buf_index--;
128
    }
129
}
130

    
131
static void term_forward_char(void)
132
{
133
    if (term_cmd_buf_index < term_cmd_buf_size) {
134
        term_cmd_buf_index++;
135
    }
136
}
137

    
138
static void term_delete_char(void)
139
{
140
    if (term_cmd_buf_index < term_cmd_buf_size) {
141
        memmove(term_cmd_buf + term_cmd_buf_index,
142
                term_cmd_buf + term_cmd_buf_index + 1,
143
                term_cmd_buf_size - term_cmd_buf_index - 1);
144
        term_cmd_buf_size--;
145
    }
146
}
147

    
148
static void term_backspace(void)
149
{
150
    if (term_cmd_buf_index > 0) {
151
        term_backward_char();
152
        term_delete_char();
153
    }
154
}
155

    
156
static void term_backword(void)
157
{
158
    int start;
159

    
160
    if (term_cmd_buf_index == 0 || term_cmd_buf_index > term_cmd_buf_size) {
161
        return;
162
    }
163

    
164
    start = term_cmd_buf_index - 1;
165

    
166
    /* find first word (backwards) */
167
    while (start > 0) {
168
        if (!qemu_isspace(term_cmd_buf[start])) {
169
            break;
170
        }
171

    
172
        --start;
173
    }
174

    
175
    /* find first space (backwards) */
176
    while (start > 0) {
177
        if (qemu_isspace(term_cmd_buf[start])) {
178
            ++start;
179
            break;
180
        }
181

    
182
        --start;
183
    }
184

    
185
    /* remove word */
186
    if (start < term_cmd_buf_index) {
187
        memmove(term_cmd_buf + start,
188
                term_cmd_buf + term_cmd_buf_index,
189
                term_cmd_buf_size - term_cmd_buf_index);
190
        term_cmd_buf_size -= term_cmd_buf_index - start;
191
        term_cmd_buf_index = start;
192
    }
193
}
194

    
195
static void term_bol(void)
196
{
197
    term_cmd_buf_index = 0;
198
}
199

    
200
static void term_eol(void)
201
{
202
    term_cmd_buf_index = term_cmd_buf_size;
203
}
204

    
205
static void term_up_char(void)
206
{
207
    int idx;
208

    
209
    if (term_hist_entry == 0)
210
        return;
211
    if (term_hist_entry == -1) {
212
        /* Find latest entry */
213
        for (idx = 0; idx < TERM_MAX_CMDS; idx++) {
214
            if (term_history[idx] == NULL)
215
                break;
216
        }
217
        term_hist_entry = idx;
218
    }
219
    term_hist_entry--;
220
    if (term_hist_entry >= 0) {
221
        pstrcpy(term_cmd_buf, sizeof(term_cmd_buf),
222
                term_history[term_hist_entry]);
223
        term_cmd_buf_index = term_cmd_buf_size = strlen(term_cmd_buf);
224
    }
225
}
226

    
227
static void term_down_char(void)
228
{
229
    if (term_hist_entry == TERM_MAX_CMDS - 1 || term_hist_entry == -1)
230
        return;
231
    if (term_history[++term_hist_entry] != NULL) {
232
        pstrcpy(term_cmd_buf, sizeof(term_cmd_buf),
233
                term_history[term_hist_entry]);
234
    } else {
235
        term_hist_entry = -1;
236
    }
237
    term_cmd_buf_index = term_cmd_buf_size = strlen(term_cmd_buf);
238
}
239

    
240
static void term_hist_add(const char *cmdline)
241
{
242
    char *hist_entry, *new_entry;
243
    int idx;
244

    
245
    if (cmdline[0] == '\0')
246
        return;
247
    new_entry = NULL;
248
    if (term_hist_entry != -1) {
249
        /* We were editing an existing history entry: replace it */
250
        hist_entry = term_history[term_hist_entry];
251
        idx = term_hist_entry;
252
        if (strcmp(hist_entry, cmdline) == 0) {
253
            goto same_entry;
254
        }
255
    }
256
    /* Search cmdline in history buffers */
257
    for (idx = 0; idx < TERM_MAX_CMDS; idx++) {
258
        hist_entry = term_history[idx];
259
        if (hist_entry == NULL)
260
            break;
261
        if (strcmp(hist_entry, cmdline) == 0) {
262
        same_entry:
263
            new_entry = hist_entry;
264
            /* Put this entry at the end of history */
265
            memmove(&term_history[idx], &term_history[idx + 1],
266
                    (TERM_MAX_CMDS - idx + 1) * sizeof(char *));
267
            term_history[TERM_MAX_CMDS - 1] = NULL;
268
            for (; idx < TERM_MAX_CMDS; idx++) {
269
                if (term_history[idx] == NULL)
270
                    break;
271
            }
272
            break;
273
        }
274
    }
275
    if (idx == TERM_MAX_CMDS) {
276
        /* Need to get one free slot */
277
        free(term_history[0]);
278
        memcpy(term_history, &term_history[1],
279
               (TERM_MAX_CMDS - 1) * sizeof(char *));
280
        term_history[TERM_MAX_CMDS - 1] = NULL;
281
        idx = TERM_MAX_CMDS - 1;
282
    }
283
    if (new_entry == NULL)
284
        new_entry = strdup(cmdline);
285
    term_history[idx] = new_entry;
286
    term_hist_entry = -1;
287
}
288

    
289
/* completion support */
290

    
291
void readline_add_completion(const char *str)
292
{
293
    if (nb_completions < NB_COMPLETIONS_MAX) {
294
        completions[nb_completions++] = qemu_strdup(str);
295
    }
296
}
297

    
298
void readline_set_completion_index(int index)
299
{
300
    completion_index = index;
301
}
302

    
303
static void term_completion(void)
304
{
305
    Monitor *mon = cur_mon;
306
    int len, i, j, max_width, nb_cols, max_prefix;
307
    char *cmdline;
308

    
309
    nb_completions = 0;
310

    
311
    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);
315
    qemu_free(cmdline);
316

    
317
    /* no completion found */
318
    if (nb_completions <= 0)
319
        return;
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]);
324
        }
325
        /* extra space for next argument. XXX: make it more generic */
326
        if (len > 0 && completions[0][len - 1] != '/')
327
            term_insert_char(' ');
328
    } else {
329
        monitor_printf(mon, "\n");
330
        max_width = 0;
331
        max_prefix = 0;        
332
        for(i = 0; i < nb_completions; i++) {
333
            len = strlen(completions[i]);
334
            if (i==0) {
335
                max_prefix = len;
336
            } else {
337
                if (len < max_prefix)
338
                    max_prefix = len;
339
                for(j=0; j<max_prefix; j++) {
340
                    if (completions[i][j] != completions[0][j])
341
                        max_prefix = j;
342
                }
343
            }
344
            if (len > max_width)
345
                max_width = len;
346
        }
347
        if (max_prefix > 0) 
348
            for(i = completion_index; i < max_prefix; i++) {
349
                term_insert_char(completions[0][i]);
350
            }
351
        max_width += 2;
352
        if (max_width < 10)
353
            max_width = 10;
354
        else if (max_width > 80)
355
            max_width = 80;
356
        nb_cols = 80 / max_width;
357
        j = 0;
358
        for(i = 0; i < nb_completions; i++) {
359
            monitor_printf(mon, "%-*s", max_width, completions[i]);
360
            if (++j == nb_cols || i == (nb_completions - 1)) {
361
                monitor_printf(mon, "\n");
362
                j = 0;
363
            }
364
        }
365
        readline_show_prompt();
366
    }
367
}
368

    
369
/* return true if command handled */
370
void readline_handle_byte(int ch)
371
{
372
    Monitor *mon = cur_mon;
373

    
374
    switch(term_esc_state) {
375
    case IS_NORM:
376
        switch(ch) {
377
        case 1:
378
            term_bol();
379
            break;
380
        case 4:
381
            term_delete_char();
382
            break;
383
        case 5:
384
            term_eol();
385
            break;
386
        case 9:
387
            term_completion();
388
            break;
389
        case 10:
390
        case 13:
391
            term_cmd_buf[term_cmd_buf_size] = '\0';
392
            if (!term_is_password)
393
                term_hist_add(term_cmd_buf);
394
            monitor_printf(mon, "\n");
395
            term_cmd_buf_index = 0;
396
            term_cmd_buf_size = 0;
397
            term_last_cmd_buf_index = 0;
398
            term_last_cmd_buf_size = 0;
399
            /* NOTE: readline_start can be called here */
400
            term_readline_func(mon, term_cmd_buf, term_readline_opaque);
401
            break;
402
        case 23:
403
            /* ^W */
404
            term_backword();
405
            break;
406
        case 27:
407
            term_esc_state = IS_ESC;
408
            break;
409
        case 127:
410
        case 8:
411
            term_backspace();
412
            break;
413
        case 155:
414
            term_esc_state = IS_CSI;
415
            break;
416
        default:
417
            if (ch >= 32) {
418
                term_insert_char(ch);
419
            }
420
            break;
421
        }
422
        break;
423
    case IS_ESC:
424
        if (ch == '[') {
425
            term_esc_state = IS_CSI;
426
            term_esc_param = 0;
427
        } else {
428
            term_esc_state = IS_NORM;
429
        }
430
        break;
431
    case IS_CSI:
432
        switch(ch) {
433
        case 'A':
434
        case 'F':
435
            term_up_char();
436
            break;
437
        case 'B':
438
        case 'E':
439
            term_down_char();
440
            break;
441
        case 'D':
442
            term_backward_char();
443
            break;
444
        case 'C':
445
            term_forward_char();
446
            break;
447
        case '0' ... '9':
448
            term_esc_param = term_esc_param * 10 + (ch - '0');
449
            goto the_end;
450
        case '~':
451
            switch(term_esc_param) {
452
            case 1:
453
                term_bol();
454
                break;
455
            case 3:
456
                term_delete_char();
457
                break;
458
            case 4:
459
                term_eol();
460
                break;
461
            }
462
            break;
463
        default:
464
            break;
465
        }
466
        term_esc_state = IS_NORM;
467
    the_end:
468
        break;
469
    }
470
    term_update();
471
}
472

    
473
void readline_start(const char *prompt, int is_password,
474
                    ReadLineFunc *readline_func, void *opaque)
475
{
476
    pstrcpy(term_prompt, sizeof(term_prompt), prompt);
477
    term_readline_func = readline_func;
478
    term_readline_opaque = opaque;
479
    term_is_password = is_password;
480
    term_cmd_buf_index = 0;
481
    term_cmd_buf_size = 0;
482
}
483

    
484
const char *readline_get_history(unsigned int index)
485
{
486
    if (index >= TERM_MAX_CMDS)
487
        return NULL;
488
    return term_history[index];
489
}