File:  [Qemu by Fabrice Bellard] / qemu / readline.c
Revision 1.1.1.5 (vendor branch): download - view: text, annotated - select for diffs
Tue Apr 24 18:23:25 2018 UTC (3 years, 1 month ago) by root
Branches: qemu, MAIN
CVS tags: qemu1000, qemu0151, qemu0150, qemu0141, qemu0140, qemu0130, HEAD
qemu 0.13.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 "readline.h"
   25: #include "monitor.h"
   26: 
   27: #define IS_NORM 0
   28: #define IS_ESC  1
   29: #define IS_CSI  2
   30: 
   31: #undef printf
   32: #define printf do_not_use_printf
   33: 
   34: void readline_show_prompt(ReadLineState *rs)
   35: {
   36:     monitor_printf(rs->mon, "%s", rs->prompt);
   37:     monitor_flush(rs->mon);
   38:     rs->last_cmd_buf_index = 0;
   39:     rs->last_cmd_buf_size = 0;
   40:     rs->esc_state = IS_NORM;
   41: }
   42: 
   43: /* update the displayed command line */
   44: static void readline_update(ReadLineState *rs)
   45: {
   46:     int i, delta, len;
   47: 
   48:     if (rs->cmd_buf_size != rs->last_cmd_buf_size ||
   49:         memcmp(rs->cmd_buf, rs->last_cmd_buf, rs->cmd_buf_size) != 0) {
   50:         for(i = 0; i < rs->last_cmd_buf_index; i++) {
   51:             monitor_printf(rs->mon, "\033[D");
   52:         }
   53:         rs->cmd_buf[rs->cmd_buf_size] = '\0';
   54:         if (rs->read_password) {
   55:             len = strlen(rs->cmd_buf);
   56:             for(i = 0; i < len; i++)
   57:                 monitor_printf(rs->mon, "*");
   58:         } else {
   59:             monitor_printf(rs->mon, "%s", rs->cmd_buf);
   60:         }
   61:         monitor_printf(rs->mon, "\033[K");
   62:         memcpy(rs->last_cmd_buf, rs->cmd_buf, rs->cmd_buf_size);
   63:         rs->last_cmd_buf_size = rs->cmd_buf_size;
   64:         rs->last_cmd_buf_index = rs->cmd_buf_size;
   65:     }
   66:     if (rs->cmd_buf_index != rs->last_cmd_buf_index) {
   67:         delta = rs->cmd_buf_index - rs->last_cmd_buf_index;
   68:         if (delta > 0) {
   69:             for(i = 0;i < delta; i++) {
   70:                 monitor_printf(rs->mon, "\033[C");
   71:             }
   72:         } else {
   73:             delta = -delta;
   74:             for(i = 0;i < delta; i++) {
   75:                 monitor_printf(rs->mon, "\033[D");
   76:             }
   77:         }
   78:         rs->last_cmd_buf_index = rs->cmd_buf_index;
   79:     }
   80:     monitor_flush(rs->mon);
   81: }
   82: 
   83: static void readline_insert_char(ReadLineState *rs, int ch)
   84: {
   85:     if (rs->cmd_buf_index < READLINE_CMD_BUF_SIZE) {
   86:         memmove(rs->cmd_buf + rs->cmd_buf_index + 1,
   87:                 rs->cmd_buf + rs->cmd_buf_index,
   88:                 rs->cmd_buf_size - rs->cmd_buf_index);
   89:         rs->cmd_buf[rs->cmd_buf_index] = ch;
   90:         rs->cmd_buf_size++;
   91:         rs->cmd_buf_index++;
   92:     }
   93: }
   94: 
   95: static void readline_backward_char(ReadLineState *rs)
   96: {
   97:     if (rs->cmd_buf_index > 0) {
   98:         rs->cmd_buf_index--;
   99:     }
  100: }
  101: 
  102: static void readline_forward_char(ReadLineState *rs)
  103: {
  104:     if (rs->cmd_buf_index < rs->cmd_buf_size) {
  105:         rs->cmd_buf_index++;
  106:     }
  107: }
  108: 
  109: static void readline_delete_char(ReadLineState *rs)
  110: {
  111:     if (rs->cmd_buf_index < rs->cmd_buf_size) {
  112:         memmove(rs->cmd_buf + rs->cmd_buf_index,
  113:                 rs->cmd_buf + rs->cmd_buf_index + 1,
  114:                 rs->cmd_buf_size - rs->cmd_buf_index - 1);
  115:         rs->cmd_buf_size--;
  116:     }
  117: }
  118: 
  119: static void readline_backspace(ReadLineState *rs)
  120: {
  121:     if (rs->cmd_buf_index > 0) {
  122:         readline_backward_char(rs);
  123:         readline_delete_char(rs);
  124:     }
  125: }
  126: 
  127: static void readline_backword(ReadLineState *rs)
  128: {
  129:     int start;
  130: 
  131:     if (rs->cmd_buf_index == 0 || rs->cmd_buf_index > rs->cmd_buf_size) {
  132:         return;
  133:     }
  134: 
  135:     start = rs->cmd_buf_index - 1;
  136: 
  137:     /* find first word (backwards) */
  138:     while (start > 0) {
  139:         if (!qemu_isspace(rs->cmd_buf[start])) {
  140:             break;
  141:         }
  142: 
  143:         --start;
  144:     }
  145: 
  146:     /* find first space (backwards) */
  147:     while (start > 0) {
  148:         if (qemu_isspace(rs->cmd_buf[start])) {
  149:             ++start;
  150:             break;
  151:         }
  152: 
  153:         --start;
  154:     }
  155: 
  156:     /* remove word */
  157:     if (start < rs->cmd_buf_index) {
  158:         memmove(rs->cmd_buf + start,
  159:                 rs->cmd_buf + rs->cmd_buf_index,
  160:                 rs->cmd_buf_size - rs->cmd_buf_index);
  161:         rs->cmd_buf_size -= rs->cmd_buf_index - start;
  162:         rs->cmd_buf_index = start;
  163:     }
  164: }
  165: 
  166: static void readline_bol(ReadLineState *rs)
  167: {
  168:     rs->cmd_buf_index = 0;
  169: }
  170: 
  171: static void readline_eol(ReadLineState *rs)
  172: {
  173:     rs->cmd_buf_index = rs->cmd_buf_size;
  174: }
  175: 
  176: static void readline_up_char(ReadLineState *rs)
  177: {
  178:     int idx;
  179: 
  180:     if (rs->hist_entry == 0)
  181: 	return;
  182:     if (rs->hist_entry == -1) {
  183: 	/* Find latest entry */
  184: 	for (idx = 0; idx < READLINE_MAX_CMDS; idx++) {
  185: 	    if (rs->history[idx] == NULL)
  186: 		break;
  187: 	}
  188: 	rs->hist_entry = idx;
  189:     }
  190:     rs->hist_entry--;
  191:     if (rs->hist_entry >= 0) {
  192: 	pstrcpy(rs->cmd_buf, sizeof(rs->cmd_buf),
  193:                 rs->history[rs->hist_entry]);
  194: 	rs->cmd_buf_index = rs->cmd_buf_size = strlen(rs->cmd_buf);
  195:     }
  196: }
  197: 
  198: static void readline_down_char(ReadLineState *rs)
  199: {
  200:     if (rs->hist_entry == -1)
  201:         return;
  202:     if (rs->hist_entry < READLINE_MAX_CMDS - 1 &&
  203:         rs->history[++rs->hist_entry] != NULL) {
  204: 	pstrcpy(rs->cmd_buf, sizeof(rs->cmd_buf),
  205:                 rs->history[rs->hist_entry]);
  206:     } else {
  207:         rs->cmd_buf[0] = 0;
  208: 	rs->hist_entry = -1;
  209:     }
  210:     rs->cmd_buf_index = rs->cmd_buf_size = strlen(rs->cmd_buf);
  211: }
  212: 
  213: static void readline_hist_add(ReadLineState *rs, const char *cmdline)
  214: {
  215:     char *hist_entry, *new_entry;
  216:     int idx;
  217: 
  218:     if (cmdline[0] == '\0')
  219: 	return;
  220:     new_entry = NULL;
  221:     if (rs->hist_entry != -1) {
  222: 	/* We were editing an existing history entry: replace it */
  223: 	hist_entry = rs->history[rs->hist_entry];
  224: 	idx = rs->hist_entry;
  225: 	if (strcmp(hist_entry, cmdline) == 0) {
  226: 	    goto same_entry;
  227: 	}
  228:     }
  229:     /* Search cmdline in history buffers */
  230:     for (idx = 0; idx < READLINE_MAX_CMDS; idx++) {
  231: 	hist_entry = rs->history[idx];
  232: 	if (hist_entry == NULL)
  233: 	    break;
  234: 	if (strcmp(hist_entry, cmdline) == 0) {
  235: 	same_entry:
  236: 	    new_entry = hist_entry;
  237: 	    /* Put this entry at the end of history */
  238: 	    memmove(&rs->history[idx], &rs->history[idx + 1],
  239: 		    (READLINE_MAX_CMDS - idx + 1) * sizeof(char *));
  240: 	    rs->history[READLINE_MAX_CMDS - 1] = NULL;
  241: 	    for (; idx < READLINE_MAX_CMDS; idx++) {
  242: 		if (rs->history[idx] == NULL)
  243: 		    break;
  244: 	    }
  245: 	    break;
  246: 	}
  247:     }
  248:     if (idx == READLINE_MAX_CMDS) {
  249: 	/* Need to get one free slot */
  250: 	free(rs->history[0]);
  251: 	memcpy(rs->history, &rs->history[1],
  252: 	       (READLINE_MAX_CMDS - 1) * sizeof(char *));
  253: 	rs->history[READLINE_MAX_CMDS - 1] = NULL;
  254: 	idx = READLINE_MAX_CMDS - 1;
  255:     }
  256:     if (new_entry == NULL)
  257: 	new_entry = strdup(cmdline);
  258:     rs->history[idx] = new_entry;
  259:     rs->hist_entry = -1;
  260: }
  261: 
  262: /* completion support */
  263: 
  264: void readline_add_completion(ReadLineState *rs, const char *str)
  265: {
  266:     if (rs->nb_completions < READLINE_MAX_COMPLETIONS) {
  267:         rs->completions[rs->nb_completions++] = qemu_strdup(str);
  268:     }
  269: }
  270: 
  271: void readline_set_completion_index(ReadLineState *rs, int index)
  272: {
  273:     rs->completion_index = index;
  274: }
  275: 
  276: static void readline_completion(ReadLineState *rs)
  277: {
  278:     Monitor *mon = cur_mon;
  279:     int len, i, j, max_width, nb_cols, max_prefix;
  280:     char *cmdline;
  281: 
  282:     rs->nb_completions = 0;
  283: 
  284:     cmdline = qemu_malloc(rs->cmd_buf_index + 1);
  285:     memcpy(cmdline, rs->cmd_buf, rs->cmd_buf_index);
  286:     cmdline[rs->cmd_buf_index] = '\0';
  287:     rs->completion_finder(cmdline);
  288:     qemu_free(cmdline);
  289: 
  290:     /* no completion found */
  291:     if (rs->nb_completions <= 0)
  292:         return;
  293:     if (rs->nb_completions == 1) {
  294:         len = strlen(rs->completions[0]);
  295:         for(i = rs->completion_index; i < len; i++) {
  296:             readline_insert_char(rs, rs->completions[0][i]);
  297:         }
  298:         /* extra space for next argument. XXX: make it more generic */
  299:         if (len > 0 && rs->completions[0][len - 1] != '/')
  300:             readline_insert_char(rs, ' ');
  301:     } else {
  302:         monitor_printf(mon, "\n");
  303:         max_width = 0;
  304:         max_prefix = 0;	
  305:         for(i = 0; i < rs->nb_completions; i++) {
  306:             len = strlen(rs->completions[i]);
  307:             if (i==0) {
  308:                 max_prefix = len;
  309:             } else {
  310:                 if (len < max_prefix)
  311:                     max_prefix = len;
  312:                 for(j=0; j<max_prefix; j++) {
  313:                     if (rs->completions[i][j] != rs->completions[0][j])
  314:                         max_prefix = j;
  315:                 }
  316:             }
  317:             if (len > max_width)
  318:                 max_width = len;
  319:         }
  320:         if (max_prefix > 0) 
  321:             for(i = rs->completion_index; i < max_prefix; i++) {
  322:                 readline_insert_char(rs, rs->completions[0][i]);
  323:             }
  324:         max_width += 2;
  325:         if (max_width < 10)
  326:             max_width = 10;
  327:         else if (max_width > 80)
  328:             max_width = 80;
  329:         nb_cols = 80 / max_width;
  330:         j = 0;
  331:         for(i = 0; i < rs->nb_completions; i++) {
  332:             monitor_printf(rs->mon, "%-*s", max_width, rs->completions[i]);
  333:             if (++j == nb_cols || i == (rs->nb_completions - 1)) {
  334:                 monitor_printf(rs->mon, "\n");
  335:                 j = 0;
  336:             }
  337:         }
  338:         readline_show_prompt(rs);
  339:     }
  340: }
  341: 
  342: /* return true if command handled */
  343: void readline_handle_byte(ReadLineState *rs, int ch)
  344: {
  345:     switch(rs->esc_state) {
  346:     case IS_NORM:
  347:         switch(ch) {
  348:         case 1:
  349:             readline_bol(rs);
  350:             break;
  351:         case 4:
  352:             readline_delete_char(rs);
  353:             break;
  354:         case 5:
  355:             readline_eol(rs);
  356:             break;
  357:         case 9:
  358:             readline_completion(rs);
  359:             break;
  360:         case 10:
  361:         case 13:
  362:             rs->cmd_buf[rs->cmd_buf_size] = '\0';
  363:             if (!rs->read_password)
  364:                 readline_hist_add(rs, rs->cmd_buf);
  365:             monitor_printf(rs->mon, "\n");
  366:             rs->cmd_buf_index = 0;
  367:             rs->cmd_buf_size = 0;
  368:             rs->last_cmd_buf_index = 0;
  369:             rs->last_cmd_buf_size = 0;
  370:             rs->readline_func(rs->mon, rs->cmd_buf, rs->readline_opaque);
  371:             break;
  372:         case 23:
  373:             /* ^W */
  374:             readline_backword(rs);
  375:             break;
  376:         case 27:
  377:             rs->esc_state = IS_ESC;
  378:             break;
  379:         case 127:
  380:         case 8:
  381:             readline_backspace(rs);
  382:             break;
  383: 	case 155:
  384:             rs->esc_state = IS_CSI;
  385: 	    break;
  386:         default:
  387:             if (ch >= 32) {
  388:                 readline_insert_char(rs, ch);
  389:             }
  390:             break;
  391:         }
  392:         break;
  393:     case IS_ESC:
  394:         if (ch == '[') {
  395:             rs->esc_state = IS_CSI;
  396:             rs->esc_param = 0;
  397:         } else {
  398:             rs->esc_state = IS_NORM;
  399:         }
  400:         break;
  401:     case IS_CSI:
  402:         switch(ch) {
  403: 	case 'A':
  404: 	case 'F':
  405: 	    readline_up_char(rs);
  406: 	    break;
  407: 	case 'B':
  408: 	case 'E':
  409: 	    readline_down_char(rs);
  410: 	    break;
  411:         case 'D':
  412:             readline_backward_char(rs);
  413:             break;
  414:         case 'C':
  415:             readline_forward_char(rs);
  416:             break;
  417:         case '0' ... '9':
  418:             rs->esc_param = rs->esc_param * 10 + (ch - '0');
  419:             goto the_end;
  420:         case '~':
  421:             switch(rs->esc_param) {
  422:             case 1:
  423:                 readline_bol(rs);
  424:                 break;
  425:             case 3:
  426:                 readline_delete_char(rs);
  427:                 break;
  428:             case 4:
  429:                 readline_eol(rs);
  430:                 break;
  431:             }
  432:             break;
  433:         default:
  434:             break;
  435:         }
  436:         rs->esc_state = IS_NORM;
  437:     the_end:
  438:         break;
  439:     }
  440:     readline_update(rs);
  441: }
  442: 
  443: void readline_start(ReadLineState *rs, const char *prompt, int read_password,
  444:                     ReadLineFunc *readline_func, void *opaque)
  445: {
  446:     pstrcpy(rs->prompt, sizeof(rs->prompt), prompt);
  447:     rs->readline_func = readline_func;
  448:     rs->readline_opaque = opaque;
  449:     rs->read_password = read_password;
  450:     readline_restart(rs);
  451: }
  452: 
  453: void readline_restart(ReadLineState *rs)
  454: {
  455:     rs->cmd_buf_index = 0;
  456:     rs->cmd_buf_size = 0;
  457: }
  458: 
  459: const char *readline_get_history(ReadLineState *rs, unsigned int index)
  460: {
  461:     if (index >= READLINE_MAX_CMDS)
  462:         return NULL;
  463:     return rs->history[index];
  464: }
  465: 
  466: ReadLineState *readline_init(Monitor *mon,
  467:                              ReadLineCompletionFunc *completion_finder)
  468: {
  469:     ReadLineState *rs = qemu_mallocz(sizeof(*rs));
  470: 
  471:     rs->hist_entry = -1;
  472:     rs->mon = mon;
  473:     rs->completion_finder = completion_finder;
  474: 
  475:     return rs;
  476: }

unix.superglobalmegacorp.com