File:  [Qemu by Fabrice Bellard] / qemu / readline.c
Revision 1.1.1.4 (vendor branch): download - view: text, annotated - select for diffs
Tue Apr 24 17:20:25 2018 UTC (2 years, 2 months ago) by root
Branches: qemu, MAIN
CVS tags: qemu0125, qemu0124, qemu0123, qemu0122, qemu0121, qemu0120, qemu0111, qemu0110, HEAD
qemu 0.11.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: #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 == -1)
  200:         return;
  201:     if (rs->hist_entry < READLINE_MAX_CMDS - 1 &&
  202:         rs->history[++rs->hist_entry] != NULL) {
  203: 	pstrcpy(rs->cmd_buf, sizeof(rs->cmd_buf),
  204:                 rs->history[rs->hist_entry]);
  205:     } else {
  206:         rs->cmd_buf[0] = 0;
  207: 	rs->hist_entry = -1;
  208:     }
  209:     rs->cmd_buf_index = rs->cmd_buf_size = strlen(rs->cmd_buf);
  210: }
  211: 
  212: static void readline_hist_add(ReadLineState *rs, const char *cmdline)
  213: {
  214:     char *hist_entry, *new_entry;
  215:     int idx;
  216: 
  217:     if (cmdline[0] == '\0')
  218: 	return;
  219:     new_entry = NULL;
  220:     if (rs->hist_entry != -1) {
  221: 	/* We were editing an existing history entry: replace it */
  222: 	hist_entry = rs->history[rs->hist_entry];
  223: 	idx = rs->hist_entry;
  224: 	if (strcmp(hist_entry, cmdline) == 0) {
  225: 	    goto same_entry;
  226: 	}
  227:     }
  228:     /* Search cmdline in history buffers */
  229:     for (idx = 0; idx < READLINE_MAX_CMDS; idx++) {
  230: 	hist_entry = rs->history[idx];
  231: 	if (hist_entry == NULL)
  232: 	    break;
  233: 	if (strcmp(hist_entry, cmdline) == 0) {
  234: 	same_entry:
  235: 	    new_entry = hist_entry;
  236: 	    /* Put this entry at the end of history */
  237: 	    memmove(&rs->history[idx], &rs->history[idx + 1],
  238: 		    (READLINE_MAX_CMDS - idx + 1) * sizeof(char *));
  239: 	    rs->history[READLINE_MAX_CMDS - 1] = NULL;
  240: 	    for (; idx < READLINE_MAX_CMDS; idx++) {
  241: 		if (rs->history[idx] == NULL)
  242: 		    break;
  243: 	    }
  244: 	    break;
  245: 	}
  246:     }
  247:     if (idx == READLINE_MAX_CMDS) {
  248: 	/* Need to get one free slot */
  249: 	free(rs->history[0]);
  250: 	memcpy(rs->history, &rs->history[1],
  251: 	       (READLINE_MAX_CMDS - 1) * sizeof(char *));
  252: 	rs->history[READLINE_MAX_CMDS - 1] = NULL;
  253: 	idx = READLINE_MAX_CMDS - 1;
  254:     }
  255:     if (new_entry == NULL)
  256: 	new_entry = strdup(cmdline);
  257:     rs->history[idx] = new_entry;
  258:     rs->hist_entry = -1;
  259: }
  260: 
  261: /* completion support */
  262: 
  263: void readline_add_completion(ReadLineState *rs, const char *str)
  264: {
  265:     if (rs->nb_completions < READLINE_MAX_COMPLETIONS) {
  266:         rs->completions[rs->nb_completions++] = qemu_strdup(str);
  267:     }
  268: }
  269: 
  270: void readline_set_completion_index(ReadLineState *rs, int index)
  271: {
  272:     rs->completion_index = index;
  273: }
  274: 
  275: static void readline_completion(ReadLineState *rs)
  276: {
  277:     Monitor *mon = cur_mon;
  278:     int len, i, j, max_width, nb_cols, max_prefix;
  279:     char *cmdline;
  280: 
  281:     rs->nb_completions = 0;
  282: 
  283:     cmdline = qemu_malloc(rs->cmd_buf_index + 1);
  284:     memcpy(cmdline, rs->cmd_buf, rs->cmd_buf_index);
  285:     cmdline[rs->cmd_buf_index] = '\0';
  286:     rs->completion_finder(cmdline);
  287:     qemu_free(cmdline);
  288: 
  289:     /* no completion found */
  290:     if (rs->nb_completions <= 0)
  291:         return;
  292:     if (rs->nb_completions == 1) {
  293:         len = strlen(rs->completions[0]);
  294:         for(i = rs->completion_index; i < len; i++) {
  295:             readline_insert_char(rs, rs->completions[0][i]);
  296:         }
  297:         /* extra space for next argument. XXX: make it more generic */
  298:         if (len > 0 && rs->completions[0][len - 1] != '/')
  299:             readline_insert_char(rs, ' ');
  300:     } else {
  301:         monitor_printf(mon, "\n");
  302:         max_width = 0;
  303:         max_prefix = 0;	
  304:         for(i = 0; i < rs->nb_completions; i++) {
  305:             len = strlen(rs->completions[i]);
  306:             if (i==0) {
  307:                 max_prefix = len;
  308:             } else {
  309:                 if (len < max_prefix)
  310:                     max_prefix = len;
  311:                 for(j=0; j<max_prefix; j++) {
  312:                     if (rs->completions[i][j] != rs->completions[0][j])
  313:                         max_prefix = j;
  314:                 }
  315:             }
  316:             if (len > max_width)
  317:                 max_width = len;
  318:         }
  319:         if (max_prefix > 0) 
  320:             for(i = rs->completion_index; i < max_prefix; i++) {
  321:                 readline_insert_char(rs, rs->completions[0][i]);
  322:             }
  323:         max_width += 2;
  324:         if (max_width < 10)
  325:             max_width = 10;
  326:         else if (max_width > 80)
  327:             max_width = 80;
  328:         nb_cols = 80 / max_width;
  329:         j = 0;
  330:         for(i = 0; i < rs->nb_completions; i++) {
  331:             monitor_printf(rs->mon, "%-*s", max_width, rs->completions[i]);
  332:             if (++j == nb_cols || i == (rs->nb_completions - 1)) {
  333:                 monitor_printf(rs->mon, "\n");
  334:                 j = 0;
  335:             }
  336:         }
  337:         readline_show_prompt(rs);
  338:     }
  339: }
  340: 
  341: /* return true if command handled */
  342: void readline_handle_byte(ReadLineState *rs, int ch)
  343: {
  344:     switch(rs->esc_state) {
  345:     case IS_NORM:
  346:         switch(ch) {
  347:         case 1:
  348:             readline_bol(rs);
  349:             break;
  350:         case 4:
  351:             readline_delete_char(rs);
  352:             break;
  353:         case 5:
  354:             readline_eol(rs);
  355:             break;
  356:         case 9:
  357:             readline_completion(rs);
  358:             break;
  359:         case 10:
  360:         case 13:
  361:             rs->cmd_buf[rs->cmd_buf_size] = '\0';
  362:             if (!rs->read_password)
  363:                 readline_hist_add(rs, rs->cmd_buf);
  364:             monitor_printf(rs->mon, "\n");
  365:             rs->cmd_buf_index = 0;
  366:             rs->cmd_buf_size = 0;
  367:             rs->last_cmd_buf_index = 0;
  368:             rs->last_cmd_buf_size = 0;
  369:             rs->readline_func(rs->mon, rs->cmd_buf, rs->readline_opaque);
  370:             break;
  371:         case 23:
  372:             /* ^W */
  373:             readline_backword(rs);
  374:             break;
  375:         case 27:
  376:             rs->esc_state = IS_ESC;
  377:             break;
  378:         case 127:
  379:         case 8:
  380:             readline_backspace(rs);
  381:             break;
  382: 	case 155:
  383:             rs->esc_state = IS_CSI;
  384: 	    break;
  385:         default:
  386:             if (ch >= 32) {
  387:                 readline_insert_char(rs, ch);
  388:             }
  389:             break;
  390:         }
  391:         break;
  392:     case IS_ESC:
  393:         if (ch == '[') {
  394:             rs->esc_state = IS_CSI;
  395:             rs->esc_param = 0;
  396:         } else {
  397:             rs->esc_state = IS_NORM;
  398:         }
  399:         break;
  400:     case IS_CSI:
  401:         switch(ch) {
  402: 	case 'A':
  403: 	case 'F':
  404: 	    readline_up_char(rs);
  405: 	    break;
  406: 	case 'B':
  407: 	case 'E':
  408: 	    readline_down_char(rs);
  409: 	    break;
  410:         case 'D':
  411:             readline_backward_char(rs);
  412:             break;
  413:         case 'C':
  414:             readline_forward_char(rs);
  415:             break;
  416:         case '0' ... '9':
  417:             rs->esc_param = rs->esc_param * 10 + (ch - '0');
  418:             goto the_end;
  419:         case '~':
  420:             switch(rs->esc_param) {
  421:             case 1:
  422:                 readline_bol(rs);
  423:                 break;
  424:             case 3:
  425:                 readline_delete_char(rs);
  426:                 break;
  427:             case 4:
  428:                 readline_eol(rs);
  429:                 break;
  430:             }
  431:             break;
  432:         default:
  433:             break;
  434:         }
  435:         rs->esc_state = IS_NORM;
  436:     the_end:
  437:         break;
  438:     }
  439:     readline_update(rs);
  440: }
  441: 
  442: void readline_start(ReadLineState *rs, const char *prompt, int read_password,
  443:                     ReadLineFunc *readline_func, void *opaque)
  444: {
  445:     pstrcpy(rs->prompt, sizeof(rs->prompt), prompt);
  446:     rs->readline_func = readline_func;
  447:     rs->readline_opaque = opaque;
  448:     rs->read_password = read_password;
  449:     readline_restart(rs);
  450: }
  451: 
  452: void readline_restart(ReadLineState *rs)
  453: {
  454:     rs->cmd_buf_index = 0;
  455:     rs->cmd_buf_size = 0;
  456: }
  457: 
  458: const char *readline_get_history(ReadLineState *rs, unsigned int index)
  459: {
  460:     if (index >= READLINE_MAX_CMDS)
  461:         return NULL;
  462:     return rs->history[index];
  463: }
  464: 
  465: ReadLineState *readline_init(Monitor *mon,
  466:                              ReadLineCompletionFunc *completion_finder)
  467: {
  468:     ReadLineState *rs = qemu_mallocz(sizeof(*rs));
  469: 
  470:     rs->hist_entry = -1;
  471:     rs->mon = mon;
  472:     rs->completion_finder = completion_finder;
  473: 
  474:     return rs;
  475: }

unix.superglobalmegacorp.com