Statistics
| Branch: | Revision:

root / readline.c @ 076d2471

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 IS_NORM 0
28
#define IS_ESC  1
29
#define IS_CSI  2
30

    
31
#define printf do_not_use_printf
32

    
33
void readline_show_prompt(ReadLineState *rs)
34
{
35
    monitor_printf(rs->mon, "%s", rs->prompt);
36
    monitor_flush(rs->mon);
37
    rs->last_cmd_buf_index = 0;
38
    rs->last_cmd_buf_size = 0;
39
    rs->esc_state = IS_NORM;
40
}
41

    
42
/* update the displayed command line */
43
static void readline_update(ReadLineState *rs)
44
{
45
    int i, delta, len;
46

    
47
    if (rs->cmd_buf_size != rs->last_cmd_buf_size ||
48
        memcmp(rs->cmd_buf, rs->last_cmd_buf, rs->cmd_buf_size) != 0) {
49
        for(i = 0; i < rs->last_cmd_buf_index; i++) {
50
            monitor_printf(rs->mon, "\033[D");
51
        }
52
        rs->cmd_buf[rs->cmd_buf_size] = '\0';
53
        if (rs->read_password) {
54
            len = strlen(rs->cmd_buf);
55
            for(i = 0; i < len; i++)
56
                monitor_printf(rs->mon, "*");
57
        } else {
58
            monitor_printf(rs->mon, "%s", rs->cmd_buf);
59
        }
60
        monitor_printf(rs->mon, "\033[K");
61
        memcpy(rs->last_cmd_buf, rs->cmd_buf, rs->cmd_buf_size);
62
        rs->last_cmd_buf_size = rs->cmd_buf_size;
63
        rs->last_cmd_buf_index = rs->cmd_buf_size;
64
    }
65
    if (rs->cmd_buf_index != rs->last_cmd_buf_index) {
66
        delta = rs->cmd_buf_index - rs->last_cmd_buf_index;
67
        if (delta > 0) {
68
            for(i = 0;i < delta; i++) {
69
                monitor_printf(rs->mon, "\033[C");
70
            }
71
        } else {
72
            delta = -delta;
73
            for(i = 0;i < delta; i++) {
74
                monitor_printf(rs->mon, "\033[D");
75
            }
76
        }
77
        rs->last_cmd_buf_index = rs->cmd_buf_index;
78
    }
79
    monitor_flush(rs->mon);
80
}
81

    
82
static void readline_insert_char(ReadLineState *rs, int ch)
83
{
84
    if (rs->cmd_buf_index < READLINE_CMD_BUF_SIZE) {
85
        memmove(rs->cmd_buf + rs->cmd_buf_index + 1,
86
                rs->cmd_buf + rs->cmd_buf_index,
87
                rs->cmd_buf_size - rs->cmd_buf_index);
88
        rs->cmd_buf[rs->cmd_buf_index] = ch;
89
        rs->cmd_buf_size++;
90
        rs->cmd_buf_index++;
91
    }
92
}
93

    
94
static void readline_backward_char(ReadLineState *rs)
95
{
96
    if (rs->cmd_buf_index > 0) {
97
        rs->cmd_buf_index--;
98
    }
99
}
100

    
101
static void readline_forward_char(ReadLineState *rs)
102
{
103
    if (rs->cmd_buf_index < rs->cmd_buf_size) {
104
        rs->cmd_buf_index++;
105
    }
106
}
107

    
108
static void readline_delete_char(ReadLineState *rs)
109
{
110
    if (rs->cmd_buf_index < rs->cmd_buf_size) {
111
        memmove(rs->cmd_buf + rs->cmd_buf_index,
112
                rs->cmd_buf + rs->cmd_buf_index + 1,
113
                rs->cmd_buf_size - rs->cmd_buf_index - 1);
114
        rs->cmd_buf_size--;
115
    }
116
}
117

    
118
static void readline_backspace(ReadLineState *rs)
119
{
120
    if (rs->cmd_buf_index > 0) {
121
        readline_backward_char(rs);
122
        readline_delete_char(rs);
123
    }
124
}
125

    
126
static void readline_backword(ReadLineState *rs)
127
{
128
    int start;
129

    
130
    if (rs->cmd_buf_index == 0 || rs->cmd_buf_index > rs->cmd_buf_size) {
131
        return;
132
    }
133

    
134
    start = rs->cmd_buf_index - 1;
135

    
136
    /* find first word (backwards) */
137
    while (start > 0) {
138
        if (!qemu_isspace(rs->cmd_buf[start])) {
139
            break;
140
        }
141

    
142
        --start;
143
    }
144

    
145
    /* find first space (backwards) */
146
    while (start > 0) {
147
        if (qemu_isspace(rs->cmd_buf[start])) {
148
            ++start;
149
            break;
150
        }
151

    
152
        --start;
153
    }
154

    
155
    /* remove word */
156
    if (start < rs->cmd_buf_index) {
157
        memmove(rs->cmd_buf + start,
158
                rs->cmd_buf + rs->cmd_buf_index,
159
                rs->cmd_buf_size - rs->cmd_buf_index);
160
        rs->cmd_buf_size -= rs->cmd_buf_index - start;
161
        rs->cmd_buf_index = start;
162
    }
163
}
164

    
165
static void readline_bol(ReadLineState *rs)
166
{
167
    rs->cmd_buf_index = 0;
168
}
169

    
170
static void readline_eol(ReadLineState *rs)
171
{
172
    rs->cmd_buf_index = rs->cmd_buf_size;
173
}
174

    
175
static void readline_up_char(ReadLineState *rs)
176
{
177
    int idx;
178

    
179
    if (rs->hist_entry == 0)
180
        return;
181
    if (rs->hist_entry == -1) {
182
        /* Find latest entry */
183
        for (idx = 0; idx < READLINE_MAX_CMDS; idx++) {
184
            if (rs->history[idx] == NULL)
185
                break;
186
        }
187
        rs->hist_entry = idx;
188
    }
189
    rs->hist_entry--;
190
    if (rs->hist_entry >= 0) {
191
        pstrcpy(rs->cmd_buf, sizeof(rs->cmd_buf),
192
                rs->history[rs->hist_entry]);
193
        rs->cmd_buf_index = rs->cmd_buf_size = strlen(rs->cmd_buf);
194
    }
195
}
196

    
197
static void readline_down_char(ReadLineState *rs)
198
{
199
    if (rs->hist_entry == -1)
200
        return;
201
    if (rs->hist_entry < READLINE_MAX_CMDS - 1 &&
202
        rs->history[++rs->hist_entry] != NULL) {
203
        pstrcpy(rs->cmd_buf, sizeof(rs->cmd_buf),
204
                rs->history[rs->hist_entry]);
205
    } else {
206
        rs->cmd_buf[0] = 0;
207
        rs->hist_entry = -1;
208
    }
209
    rs->cmd_buf_index = rs->cmd_buf_size = strlen(rs->cmd_buf);
210
}
211

    
212
static void readline_hist_add(ReadLineState *rs, const char *cmdline)
213
{
214
    char *hist_entry, *new_entry;
215
    int idx;
216

    
217
    if (cmdline[0] == '\0')
218
        return;
219
    new_entry = NULL;
220
    if (rs->hist_entry != -1) {
221
        /* We were editing an existing history entry: replace it */
222
        hist_entry = rs->history[rs->hist_entry];
223
        idx = rs->hist_entry;
224
        if (strcmp(hist_entry, cmdline) == 0) {
225
            goto same_entry;
226
        }
227
    }
228
    /* Search cmdline in history buffers */
229
    for (idx = 0; idx < READLINE_MAX_CMDS; idx++) {
230
        hist_entry = rs->history[idx];
231
        if (hist_entry == NULL)
232
            break;
233
        if (strcmp(hist_entry, cmdline) == 0) {
234
        same_entry:
235
            new_entry = hist_entry;
236
            /* Put this entry at the end of history */
237
            memmove(&rs->history[idx], &rs->history[idx + 1],
238
                    (READLINE_MAX_CMDS - idx + 1) * sizeof(char *));
239
            rs->history[READLINE_MAX_CMDS - 1] = NULL;
240
            for (; idx < READLINE_MAX_CMDS; idx++) {
241
                if (rs->history[idx] == NULL)
242
                    break;
243
            }
244
            break;
245
        }
246
    }
247
    if (idx == READLINE_MAX_CMDS) {
248
        /* Need to get one free slot */
249
        free(rs->history[0]);
250
        memcpy(rs->history, &rs->history[1],
251
               (READLINE_MAX_CMDS - 1) * sizeof(char *));
252
        rs->history[READLINE_MAX_CMDS - 1] = NULL;
253
        idx = READLINE_MAX_CMDS - 1;
254
    }
255
    if (new_entry == NULL)
256
        new_entry = strdup(cmdline);
257
    rs->history[idx] = new_entry;
258
    rs->hist_entry = -1;
259
}
260

    
261
/* completion support */
262

    
263
void readline_add_completion(ReadLineState *rs, const char *str)
264
{
265
    if (rs->nb_completions < READLINE_MAX_COMPLETIONS) {
266
        rs->completions[rs->nb_completions++] = qemu_strdup(str);
267
    }
268
}
269

    
270
void readline_set_completion_index(ReadLineState *rs, int index)
271
{
272
    rs->completion_index = index;
273
}
274

    
275
static void readline_completion(ReadLineState *rs)
276
{
277
    Monitor *mon = cur_mon;
278
    int len, i, j, max_width, nb_cols, max_prefix;
279
    char *cmdline;
280

    
281
    rs->nb_completions = 0;
282

    
283
    cmdline = qemu_malloc(rs->cmd_buf_index + 1);
284
    memcpy(cmdline, rs->cmd_buf, rs->cmd_buf_index);
285
    cmdline[rs->cmd_buf_index] = '\0';
286
    rs->completion_finder(cmdline);
287
    qemu_free(cmdline);
288

    
289
    /* no completion found */
290
    if (rs->nb_completions <= 0)
291
        return;
292
    if (rs->nb_completions == 1) {
293
        len = strlen(rs->completions[0]);
294
        for(i = rs->completion_index; i < len; i++) {
295
            readline_insert_char(rs, rs->completions[0][i]);
296
        }
297
        /* extra space for next argument. XXX: make it more generic */
298
        if (len > 0 && rs->completions[0][len - 1] != '/')
299
            readline_insert_char(rs, ' ');
300
    } else {
301
        monitor_printf(mon, "\n");
302
        max_width = 0;
303
        max_prefix = 0;        
304
        for(i = 0; i < rs->nb_completions; i++) {
305
            len = strlen(rs->completions[i]);
306
            if (i==0) {
307
                max_prefix = len;
308
            } else {
309
                if (len < max_prefix)
310
                    max_prefix = len;
311
                for(j=0; j<max_prefix; j++) {
312
                    if (rs->completions[i][j] != rs->completions[0][j])
313
                        max_prefix = j;
314
                }
315
            }
316
            if (len > max_width)
317
                max_width = len;
318
        }
319
        if (max_prefix > 0) 
320
            for(i = rs->completion_index; i < max_prefix; i++) {
321
                readline_insert_char(rs, rs->completions[0][i]);
322
            }
323
        max_width += 2;
324
        if (max_width < 10)
325
            max_width = 10;
326
        else if (max_width > 80)
327
            max_width = 80;
328
        nb_cols = 80 / max_width;
329
        j = 0;
330
        for(i = 0; i < rs->nb_completions; i++) {
331
            monitor_printf(rs->mon, "%-*s", max_width, rs->completions[i]);
332
            if (++j == nb_cols || i == (rs->nb_completions - 1)) {
333
                monitor_printf(rs->mon, "\n");
334
                j = 0;
335
            }
336
        }
337
        readline_show_prompt(rs);
338
    }
339
}
340

    
341
/* return true if command handled */
342
void readline_handle_byte(ReadLineState *rs, int ch)
343
{
344
    switch(rs->esc_state) {
345
    case IS_NORM:
346
        switch(ch) {
347
        case 1:
348
            readline_bol(rs);
349
            break;
350
        case 4:
351
            readline_delete_char(rs);
352
            break;
353
        case 5:
354
            readline_eol(rs);
355
            break;
356
        case 9:
357
            readline_completion(rs);
358
            break;
359
        case 10:
360
        case 13:
361
            rs->cmd_buf[rs->cmd_buf_size] = '\0';
362
            if (!rs->read_password)
363
                readline_hist_add(rs, rs->cmd_buf);
364
            monitor_printf(rs->mon, "\n");
365
            rs->cmd_buf_index = 0;
366
            rs->cmd_buf_size = 0;
367
            rs->last_cmd_buf_index = 0;
368
            rs->last_cmd_buf_size = 0;
369
            rs->readline_func(rs->mon, rs->cmd_buf, rs->readline_opaque);
370
            break;
371
        case 23:
372
            /* ^W */
373
            readline_backword(rs);
374
            break;
375
        case 27:
376
            rs->esc_state = IS_ESC;
377
            break;
378
        case 127:
379
        case 8:
380
            readline_backspace(rs);
381
            break;
382
        case 155:
383
            rs->esc_state = IS_CSI;
384
            break;
385
        default:
386
            if (ch >= 32) {
387
                readline_insert_char(rs, ch);
388
            }
389
            break;
390
        }
391
        break;
392
    case IS_ESC:
393
        if (ch == '[') {
394
            rs->esc_state = IS_CSI;
395
            rs->esc_param = 0;
396
        } else {
397
            rs->esc_state = IS_NORM;
398
        }
399
        break;
400
    case IS_CSI:
401
        switch(ch) {
402
        case 'A':
403
        case 'F':
404
            readline_up_char(rs);
405
            break;
406
        case 'B':
407
        case 'E':
408
            readline_down_char(rs);
409
            break;
410
        case 'D':
411
            readline_backward_char(rs);
412
            break;
413
        case 'C':
414
            readline_forward_char(rs);
415
            break;
416
        case '0' ... '9':
417
            rs->esc_param = rs->esc_param * 10 + (ch - '0');
418
            goto the_end;
419
        case '~':
420
            switch(rs->esc_param) {
421
            case 1:
422
                readline_bol(rs);
423
                break;
424
            case 3:
425
                readline_delete_char(rs);
426
                break;
427
            case 4:
428
                readline_eol(rs);
429
                break;
430
            }
431
            break;
432
        default:
433
            break;
434
        }
435
        rs->esc_state = IS_NORM;
436
    the_end:
437
        break;
438
    }
439
    readline_update(rs);
440
}
441

    
442
void readline_start(ReadLineState *rs, const char *prompt, int read_password,
443
                    ReadLineFunc *readline_func, void *opaque)
444
{
445
    pstrcpy(rs->prompt, sizeof(rs->prompt), prompt);
446
    rs->readline_func = readline_func;
447
    rs->readline_opaque = opaque;
448
    rs->read_password = read_password;
449
    readline_restart(rs);
450
}
451

    
452
void readline_restart(ReadLineState *rs)
453
{
454
    rs->cmd_buf_index = 0;
455
    rs->cmd_buf_size = 0;
456
}
457

    
458
const char *readline_get_history(ReadLineState *rs, unsigned int index)
459
{
460
    if (index >= READLINE_MAX_CMDS)
461
        return NULL;
462
    return rs->history[index];
463
}
464

    
465
ReadLineState *readline_init(Monitor *mon,
466
                             ReadLineCompletionFunc *completion_finder)
467
{
468
    ReadLineState *rs = qemu_mallocz(sizeof(*rs));
469

    
470
    rs->hist_entry = -1;
471
    rs->mon = mon;
472
    rs->completion_finder = completion_finder;
473

    
474
    return rs;
475
}