~vcs-imports/qemu/git

« back to all changes in this revision

Viewing changes to readline.c

  • Committer: bellard
  • Date: 2003-03-23 20:17:16 UTC
  • Revision ID: git-v1:3ef693a03205217a5def9318b443c8cb6de17217
distribution patches


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@44 c046a42c-6fe2-441c-8c8c-71466251a162

Show diffs side-by-side

added added

removed removed

Lines of Context:
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 "qemu-common.h"
25
 
#include "console.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
 
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
 
static void term_show_prompt2(void)
61
 
{
62
 
    term_printf("%s", term_prompt);
63
 
    term_flush();
64
 
    term_last_cmd_buf_index = 0;
65
 
    term_last_cmd_buf_size = 0;
66
 
    term_esc_state = IS_NORM;
67
 
}
68
 
 
69
 
static void term_show_prompt(void)
70
 
{
71
 
    term_show_prompt2();
72
 
    term_cmd_buf_index = 0;
73
 
    term_cmd_buf_size = 0;
74
 
}
75
 
 
76
 
/* update the displayed command line */
77
 
static void term_update(void)
78
 
{
79
 
    int i, delta, len;
80
 
 
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");
85
 
        }
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++)
90
 
                term_printf("*");
91
 
        } else {
92
 
            term_printf("%s", term_cmd_buf);
93
 
        }
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;
98
 
    }
99
 
    if (term_cmd_buf_index != term_last_cmd_buf_index) {
100
 
        delta = term_cmd_buf_index - term_last_cmd_buf_index;
101
 
        if (delta > 0) {
102
 
            for(i = 0;i < delta; i++) {
103
 
                term_printf("\033[C");
104
 
            }
105
 
        } else {
106
 
            delta = -delta;
107
 
            for(i = 0;i < delta; i++) {
108
 
                term_printf("\033[D");
109
 
            }
110
 
        }
111
 
        term_last_cmd_buf_index = term_cmd_buf_index;
112
 
    }
113
 
    term_flush();
114
 
}
115
 
 
116
 
static void term_insert_char(int ch)
117
 
{
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;
123
 
        term_cmd_buf_size++;
124
 
        term_cmd_buf_index++;
125
 
    }
126
 
}
127
 
 
128
 
static void term_backward_char(void)
129
 
{
130
 
    if (term_cmd_buf_index > 0) {
131
 
        term_cmd_buf_index--;
132
 
    }
133
 
}
134
 
 
135
 
static void term_forward_char(void)
136
 
{
137
 
    if (term_cmd_buf_index < term_cmd_buf_size) {
138
 
        term_cmd_buf_index++;
139
 
    }
140
 
}
141
 
 
142
 
static void term_delete_char(void)
143
 
{
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);
148
 
        term_cmd_buf_size--;
149
 
    }
150
 
}
151
 
 
152
 
static void term_backspace(void)
153
 
{
154
 
    if (term_cmd_buf_index > 0) {
155
 
        term_backward_char();
156
 
        term_delete_char();
157
 
    }
158
 
}
159
 
 
160
 
static void term_backword(void)
161
 
{
162
 
    int start;
163
 
 
164
 
    if (term_cmd_buf_index == 0 || term_cmd_buf_index > term_cmd_buf_size) {
165
 
        return;
166
 
    }
167
 
 
168
 
    start = term_cmd_buf_index - 1;
169
 
 
170
 
    /* find first word (backwards) */
171
 
    while (start > 0) {
172
 
        if (!isspace(term_cmd_buf[start])) {
173
 
            break;
174
 
        }
175
 
 
176
 
        --start;
177
 
    }
178
 
 
179
 
    /* find first space (backwards) */
180
 
    while (start > 0) {
181
 
        if (isspace(term_cmd_buf[start])) {
182
 
            ++start;
183
 
            break;
184
 
        }
185
 
 
186
 
        --start;
187
 
    }
188
 
 
189
 
    /* remove word */
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;
196
 
    }
197
 
}
198
 
 
199
 
static void term_bol(void)
200
 
{
201
 
    term_cmd_buf_index = 0;
202
 
}
203
 
 
204
 
static void term_eol(void)
205
 
{
206
 
    term_cmd_buf_index = term_cmd_buf_size;
207
 
}
208
 
 
209
 
static void term_up_char(void)
210
 
{
211
 
    int idx;
212
 
 
213
 
    if (term_hist_entry == 0)
214
 
        return;
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)
219
 
                break;
220
 
        }
221
 
        term_hist_entry = idx;
222
 
    }
223
 
    term_hist_entry--;
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);
228
 
    }
229
 
}
230
 
 
231
 
static void term_down_char(void)
232
 
{
233
 
    if (term_hist_entry == TERM_MAX_CMDS - 1 || term_hist_entry == -1)
234
 
        return;
235
 
    if (term_history[++term_hist_entry] != NULL) {
236
 
        pstrcpy(term_cmd_buf, sizeof(term_cmd_buf),
237
 
                term_history[term_hist_entry]);
238
 
    } else {
239
 
        term_hist_entry = -1;
240
 
    }
241
 
    term_cmd_buf_index = term_cmd_buf_size = strlen(term_cmd_buf);
242
 
}
243
 
 
244
 
static void term_hist_add(const char *cmdline)
245
 
{
246
 
    char *hist_entry, *new_entry;
247
 
    int idx;
248
 
 
249
 
    if (cmdline[0] == '\0')
250
 
        return;
251
 
    new_entry = NULL;
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) {
257
 
            goto same_entry;
258
 
        }
259
 
    }
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)
264
 
            break;
265
 
        if (strcmp(hist_entry, cmdline) == 0) {
266
 
        same_entry:
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)
274
 
                    break;
275
 
            }
276
 
            break;
277
 
        }
278
 
    }
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;
286
 
    }
287
 
    if (new_entry == NULL)
288
 
        new_entry = strdup(cmdline);
289
 
    term_history[idx] = new_entry;
290
 
    term_hist_entry = -1;
291
 
}
292
 
 
293
 
/* completion support */
294
 
 
295
 
void add_completion(const char *str)
296
 
{
297
 
    if (nb_completions < NB_COMPLETIONS_MAX) {
298
 
        completions[nb_completions++] = qemu_strdup(str);
299
 
    }
300
 
}
301
 
 
302
 
static void term_completion(void)
303
 
{
304
 
    int len, i, j, max_width, nb_cols, max_prefix;
305
 
    char *cmdline;
306
 
 
307
 
    nb_completions = 0;
308
 
 
309
 
    cmdline = qemu_malloc(term_cmd_buf_index + 1);
310
 
    if (!cmdline)
311
 
        return;
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
 
        term_printf("\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
 
            term_printf("%-*s", max_width, completions[i]);
360
 
            if (++j == nb_cols || i == (nb_completions - 1)) {
361
 
                term_printf("\n");
362
 
                j = 0;
363
 
            }
364
 
        }
365
 
        term_show_prompt2();
366
 
    }
367
 
}
368
 
 
369
 
/* return true if command handled */
370
 
void readline_handle_byte(int ch)
371
 
{
372
 
    switch(term_esc_state) {
373
 
    case IS_NORM:
374
 
        switch(ch) {
375
 
        case 1:
376
 
            term_bol();
377
 
            break;
378
 
        case 4:
379
 
            term_delete_char();
380
 
            break;
381
 
        case 5:
382
 
            term_eol();
383
 
            break;
384
 
        case 9:
385
 
            term_completion();
386
 
            break;
387
 
        case 10:
388
 
        case 13:
389
 
            term_cmd_buf[term_cmd_buf_size] = '\0';
390
 
            if (!term_is_password)
391
 
                term_hist_add(term_cmd_buf);
392
 
            term_printf("\n");
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);
399
 
            break;
400
 
        case 23:
401
 
            /* ^W */
402
 
            term_backword();
403
 
            break;
404
 
        case 27:
405
 
            term_esc_state = IS_ESC;
406
 
            break;
407
 
        case 127:
408
 
        case 8:
409
 
            term_backspace();
410
 
            break;
411
 
        case 155:
412
 
            term_esc_state = IS_CSI;
413
 
            break;
414
 
        default:
415
 
            if (ch >= 32) {
416
 
                term_insert_char(ch);
417
 
            }
418
 
            break;
419
 
        }
420
 
        break;
421
 
    case IS_ESC:
422
 
        if (ch == '[') {
423
 
            term_esc_state = IS_CSI;
424
 
            term_esc_param = 0;
425
 
        } else {
426
 
            term_esc_state = IS_NORM;
427
 
        }
428
 
        break;
429
 
    case IS_CSI:
430
 
        switch(ch) {
431
 
        case 'A':
432
 
        case 'F':
433
 
            term_up_char();
434
 
            break;
435
 
        case 'B':
436
 
        case 'E':
437
 
            term_down_char();
438
 
            break;
439
 
        case 'D':
440
 
            term_backward_char();
441
 
            break;
442
 
        case 'C':
443
 
            term_forward_char();
444
 
            break;
445
 
        case '0' ... '9':
446
 
            term_esc_param = term_esc_param * 10 + (ch - '0');
447
 
            goto the_end;
448
 
        case '~':
449
 
            switch(term_esc_param) {
450
 
            case 1:
451
 
                term_bol();
452
 
                break;
453
 
            case 3:
454
 
                term_delete_char();
455
 
                break;
456
 
            case 4:
457
 
                term_eol();
458
 
                break;
459
 
            }
460
 
            break;
461
 
        default:
462
 
            break;
463
 
        }
464
 
        term_esc_state = IS_NORM;
465
 
    the_end:
466
 
        break;
467
 
    }
468
 
    term_update();
469
 
}
470
 
 
471
 
void readline_start(const char *prompt, int is_password,
472
 
                    ReadLineFunc *readline_func, void *opaque)
473
 
{
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;
478
 
    term_show_prompt();
479
 
}
480
 
 
481
 
const char *readline_get_history(unsigned int index)
482
 
{
483
 
    if (index >= TERM_MAX_CMDS)
484
 
        return NULL;
485
 
    return term_history[index];
486
 
}
487
 
 
488