Annotation of qemu/readline.c, revision 1.1.1.3

1.1       root        1: /*
                      2:  * QEMU readline utility
1.1.1.2   root        3:  *
1.1       root        4:  * Copyright (c) 2003-2004 Fabrice Bellard
1.1.1.2   root        5:  *
1.1       root        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:  */
1.1.1.2   root       24: #include "qemu-common.h"
                     25: #include "console.h"
1.1       root       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: 
1.1.1.2   root      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) {
1.1.1.3 ! root      172:         if (!qemu_isspace(term_cmd_buf[start])) {
1.1.1.2   root      173:             break;
                    174:         }
                    175: 
                    176:         --start;
                    177:     }
                    178: 
                    179:     /* find first space (backwards) */
                    180:     while (start > 0) {
1.1.1.3 ! root      181:         if (qemu_isspace(term_cmd_buf[start])) {
1.1.1.2   root      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: 
1.1       root      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) {
1.1.1.2   root      225:        pstrcpy(term_cmd_buf, sizeof(term_cmd_buf),
1.1       root      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],
1.1.1.2   root      270:                    (TERM_MAX_CMDS - idx + 1) * sizeof(char *));
1.1       root      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],
1.1.1.2   root      283:               (TERM_MAX_CMDS - 1) * sizeof(char *));
1.1       root      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: {
1.1.1.2   root      304:     int len, i, j, max_width, nb_cols, max_prefix;
1.1       root      305:     char *cmdline;
                    306: 
                    307:     nb_completions = 0;
1.1.1.2   root      308: 
1.1       root      309:     cmdline = qemu_malloc(term_cmd_buf_index + 1);
                    310:     memcpy(cmdline, term_cmd_buf, term_cmd_buf_index);
                    311:     cmdline[term_cmd_buf_index] = '\0';
                    312:     readline_find_completion(cmdline);
                    313:     qemu_free(cmdline);
                    314: 
                    315:     /* no completion found */
                    316:     if (nb_completions <= 0)
                    317:         return;
                    318:     if (nb_completions == 1) {
                    319:         len = strlen(completions[0]);
                    320:         for(i = completion_index; i < len; i++) {
                    321:             term_insert_char(completions[0][i]);
                    322:         }
                    323:         /* extra space for next argument. XXX: make it more generic */
                    324:         if (len > 0 && completions[0][len - 1] != '/')
                    325:             term_insert_char(' ');
                    326:     } else {
                    327:         term_printf("\n");
                    328:         max_width = 0;
1.1.1.2   root      329:         max_prefix = 0;        
1.1       root      330:         for(i = 0; i < nb_completions; i++) {
                    331:             len = strlen(completions[i]);
1.1.1.2   root      332:             if (i==0) {
                    333:                 max_prefix = len;
                    334:             } else {
                    335:                 if (len < max_prefix)
                    336:                     max_prefix = len;
                    337:                 for(j=0; j<max_prefix; j++) {
                    338:                     if (completions[i][j] != completions[0][j])
                    339:                         max_prefix = j;
                    340:                 }
                    341:             }
1.1       root      342:             if (len > max_width)
                    343:                 max_width = len;
                    344:         }
1.1.1.2   root      345:         if (max_prefix > 0) 
                    346:             for(i = completion_index; i < max_prefix; i++) {
                    347:                 term_insert_char(completions[0][i]);
                    348:             }
1.1       root      349:         max_width += 2;
                    350:         if (max_width < 10)
                    351:             max_width = 10;
                    352:         else if (max_width > 80)
                    353:             max_width = 80;
                    354:         nb_cols = 80 / max_width;
                    355:         j = 0;
                    356:         for(i = 0; i < nb_completions; i++) {
                    357:             term_printf("%-*s", max_width, completions[i]);
                    358:             if (++j == nb_cols || i == (nb_completions - 1)) {
                    359:                 term_printf("\n");
                    360:                 j = 0;
                    361:             }
                    362:         }
                    363:         term_show_prompt2();
                    364:     }
                    365: }
                    366: 
                    367: /* return true if command handled */
                    368: void readline_handle_byte(int ch)
                    369: {
                    370:     switch(term_esc_state) {
                    371:     case IS_NORM:
                    372:         switch(ch) {
                    373:         case 1:
                    374:             term_bol();
                    375:             break;
                    376:         case 4:
                    377:             term_delete_char();
                    378:             break;
                    379:         case 5:
                    380:             term_eol();
                    381:             break;
                    382:         case 9:
                    383:             term_completion();
                    384:             break;
                    385:         case 10:
                    386:         case 13:
                    387:             term_cmd_buf[term_cmd_buf_size] = '\0';
                    388:             if (!term_is_password)
                    389:                 term_hist_add(term_cmd_buf);
                    390:             term_printf("\n");
1.1.1.2   root      391:             term_cmd_buf_index = 0;
                    392:             term_cmd_buf_size = 0;
                    393:             term_last_cmd_buf_index = 0;
                    394:             term_last_cmd_buf_size = 0;
1.1       root      395:             /* NOTE: readline_start can be called here */
                    396:             term_readline_func(term_readline_opaque, term_cmd_buf);
                    397:             break;
1.1.1.2   root      398:         case 23:
                    399:             /* ^W */
                    400:             term_backword();
                    401:             break;
1.1       root      402:         case 27:
                    403:             term_esc_state = IS_ESC;
                    404:             break;
                    405:         case 127:
                    406:         case 8:
                    407:             term_backspace();
                    408:             break;
                    409:        case 155:
                    410:             term_esc_state = IS_CSI;
                    411:            break;
                    412:         default:
                    413:             if (ch >= 32) {
                    414:                 term_insert_char(ch);
                    415:             }
                    416:             break;
                    417:         }
                    418:         break;
                    419:     case IS_ESC:
                    420:         if (ch == '[') {
                    421:             term_esc_state = IS_CSI;
                    422:             term_esc_param = 0;
                    423:         } else {
                    424:             term_esc_state = IS_NORM;
                    425:         }
                    426:         break;
                    427:     case IS_CSI:
                    428:         switch(ch) {
                    429:        case 'A':
                    430:        case 'F':
                    431:            term_up_char();
                    432:            break;
                    433:        case 'B':
                    434:        case 'E':
                    435:            term_down_char();
                    436:            break;
                    437:         case 'D':
                    438:             term_backward_char();
                    439:             break;
                    440:         case 'C':
                    441:             term_forward_char();
                    442:             break;
                    443:         case '0' ... '9':
                    444:             term_esc_param = term_esc_param * 10 + (ch - '0');
                    445:             goto the_end;
                    446:         case '~':
                    447:             switch(term_esc_param) {
                    448:             case 1:
                    449:                 term_bol();
                    450:                 break;
                    451:             case 3:
                    452:                 term_delete_char();
                    453:                 break;
                    454:             case 4:
                    455:                 term_eol();
                    456:                 break;
                    457:             }
                    458:             break;
                    459:         default:
                    460:             break;
                    461:         }
                    462:         term_esc_state = IS_NORM;
                    463:     the_end:
                    464:         break;
                    465:     }
                    466:     term_update();
                    467: }
                    468: 
                    469: void readline_start(const char *prompt, int is_password,
                    470:                     ReadLineFunc *readline_func, void *opaque)
                    471: {
                    472:     pstrcpy(term_prompt, sizeof(term_prompt), prompt);
                    473:     term_readline_func = readline_func;
                    474:     term_readline_opaque = opaque;
                    475:     term_is_password = is_password;
                    476:     term_show_prompt();
                    477: }
                    478: 
                    479: const char *readline_get_history(unsigned int index)
                    480: {
                    481:     if (index >= TERM_MAX_CMDS)
                    482:         return NULL;
                    483:     return term_history[index];
                    484: }

unix.superglobalmegacorp.com