Annotation of qemu/readline.c, revision 1.1.1.2

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) {
        !           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: 
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:     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;
1.1.1.2 ! root      331:         max_prefix = 0;        
1.1       root      332:         for(i = 0; i < nb_completions; i++) {
                    333:             len = strlen(completions[i]);
1.1.1.2 ! root      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:             }
1.1       root      344:             if (len > max_width)
                    345:                 max_width = len;
                    346:         }
1.1.1.2 ! root      347:         if (max_prefix > 0) 
        !           348:             for(i = completion_index; i < max_prefix; i++) {
        !           349:                 term_insert_char(completions[0][i]);
        !           350:             }
1.1       root      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");
1.1.1.2 ! root      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;
1.1       root      397:             /* NOTE: readline_start can be called here */
                    398:             term_readline_func(term_readline_opaque, term_cmd_buf);
                    399:             break;
1.1.1.2 ! root      400:         case 23:
        !           401:             /* ^W */
        !           402:             term_backword();
        !           403:             break;
1.1       root      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: 

unix.superglobalmegacorp.com