Statistics
| Branch: | Revision:

root / readline.c @ 0497d2f4

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 == READLINE_MAX_CMDS - 1 || rs->hist_entry == -1)
200
        return;
201
    if (rs->history[++rs->hist_entry] != NULL) {
202
        pstrcpy(rs->cmd_buf, sizeof(rs->cmd_buf),
203
                rs->history[rs->hist_entry]);
204
    } else {
205
        rs->hist_entry = -1;
206
    }
207
    rs->cmd_buf_index = rs->cmd_buf_size = strlen(rs->cmd_buf);
208
}
209

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

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

    
259
/* completion support */
260

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

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

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

    
279
    rs->nb_completions = 0;
280

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

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

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

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

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

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

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

    
468
    if (!rs)
469
        return NULL;
470

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

    
475
    return rs;
476
}