File:  [Qemu by Fabrice Bellard] / qemu / readline.c
Revision 1.1.1.3 (vendor branch): download - view: text, annotated - select for diffs
Tue Apr 24 16:50:22 2018 UTC (2 years, 2 months ago) by root
Branches: qemu, MAIN
CVS tags: qemu0105, qemu0104, qemu0103, qemu0102, qemu0101, qemu0100, HEAD
qemu 0.10.0

    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 (!qemu_isspace(term_cmd_buf[start])) {
  173:             break;
  174:         }
  175: 
  176:         --start;
  177:     }
  178: 
  179:     /* find first space (backwards) */
  180:     while (start > 0) {
  181:         if (qemu_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:     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;
  329:         max_prefix = 0;	
  330:         for(i = 0; i < nb_completions; i++) {
  331:             len = strlen(completions[i]);
  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:             }
  342:             if (len > max_width)
  343:                 max_width = len;
  344:         }
  345:         if (max_prefix > 0) 
  346:             for(i = completion_index; i < max_prefix; i++) {
  347:                 term_insert_char(completions[0][i]);
  348:             }
  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");
  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;
  395:             /* NOTE: readline_start can be called here */
  396:             term_readline_func(term_readline_opaque, term_cmd_buf);
  397:             break;
  398:         case 23:
  399:             /* ^W */
  400:             term_backword();
  401:             break;
  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