Annotation of qemu/readline.c, revision 1.1

1.1     ! root        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 "vl.h"
        !            25: 
        !            26: #define TERM_CMD_BUF_SIZE 4095
        !            27: #define TERM_MAX_CMDS 64
        !            28: #define NB_COMPLETIONS_MAX 256
        !            29: 
        !            30: #define IS_NORM 0
        !            31: #define IS_ESC  1
        !            32: #define IS_CSI  2
        !            33: 
        !            34: #define printf do_not_use_printf
        !            35: 
        !            36: static char term_cmd_buf[TERM_CMD_BUF_SIZE + 1];
        !            37: static int term_cmd_buf_index;
        !            38: static int term_cmd_buf_size;
        !            39: 
        !            40: static char term_last_cmd_buf[TERM_CMD_BUF_SIZE + 1];
        !            41: static int term_last_cmd_buf_index;
        !            42: static int term_last_cmd_buf_size;
        !            43: 
        !            44: static int term_esc_state;
        !            45: static int term_esc_param;
        !            46: 
        !            47: static char *term_history[TERM_MAX_CMDS];
        !            48: static int term_hist_entry = -1;
        !            49: 
        !            50: static int nb_completions;
        !            51: int completion_index;
        !            52: static char *completions[NB_COMPLETIONS_MAX];
        !            53: 
        !            54: static ReadLineFunc *term_readline_func;
        !            55: static int term_is_password;
        !            56: static char term_prompt[256];
        !            57: static void *term_readline_opaque;
        !            58: 
        !            59: static void term_show_prompt2(void)
        !            60: {
        !            61:     term_printf("%s", term_prompt);
        !            62:     term_flush();
        !            63:     term_last_cmd_buf_index = 0;
        !            64:     term_last_cmd_buf_size = 0;
        !            65:     term_esc_state = IS_NORM;
        !            66: }
        !            67: 
        !            68: static void term_show_prompt(void)
        !            69: {
        !            70:     term_show_prompt2();
        !            71:     term_cmd_buf_index = 0;
        !            72:     term_cmd_buf_size = 0;
        !            73: }
        !            74: 
        !            75: /* update the displayed command line */
        !            76: static void term_update(void)
        !            77: {
        !            78:     int i, delta, len;
        !            79: 
        !            80:     if (term_cmd_buf_size != term_last_cmd_buf_size ||
        !            81:         memcmp(term_cmd_buf, term_last_cmd_buf, term_cmd_buf_size) != 0) {
        !            82:         for(i = 0; i < term_last_cmd_buf_index; i++) {
        !            83:             term_printf("\033[D");
        !            84:         }
        !            85:         term_cmd_buf[term_cmd_buf_size] = '\0';
        !            86:         if (term_is_password) {
        !            87:             len = strlen(term_cmd_buf);
        !            88:             for(i = 0; i < len; i++)
        !            89:                 term_printf("*");
        !            90:         } else {
        !            91:             term_printf("%s", term_cmd_buf);
        !            92:         }
        !            93:         term_printf("\033[K");
        !            94:         memcpy(term_last_cmd_buf, term_cmd_buf, term_cmd_buf_size);
        !            95:         term_last_cmd_buf_size = term_cmd_buf_size;
        !            96:         term_last_cmd_buf_index = term_cmd_buf_size;
        !            97:     }
        !            98:     if (term_cmd_buf_index != term_last_cmd_buf_index) {
        !            99:         delta = term_cmd_buf_index - term_last_cmd_buf_index;
        !           100:         if (delta > 0) {
        !           101:             for(i = 0;i < delta; i++) {
        !           102:                 term_printf("\033[C");
        !           103:             }
        !           104:         } else {
        !           105:             delta = -delta;
        !           106:             for(i = 0;i < delta; i++) {
        !           107:                 term_printf("\033[D");
        !           108:             }
        !           109:         }
        !           110:         term_last_cmd_buf_index = term_cmd_buf_index;
        !           111:     }
        !           112:     term_flush();
        !           113: }
        !           114: 
        !           115: static void term_insert_char(int ch)
        !           116: {
        !           117:     if (term_cmd_buf_index < TERM_CMD_BUF_SIZE) {
        !           118:         memmove(term_cmd_buf + term_cmd_buf_index + 1,
        !           119:                 term_cmd_buf + term_cmd_buf_index,
        !           120:                 term_cmd_buf_size - term_cmd_buf_index);
        !           121:         term_cmd_buf[term_cmd_buf_index] = ch;
        !           122:         term_cmd_buf_size++;
        !           123:         term_cmd_buf_index++;
        !           124:     }
        !           125: }
        !           126: 
        !           127: static void term_backward_char(void)
        !           128: {
        !           129:     if (term_cmd_buf_index > 0) {
        !           130:         term_cmd_buf_index--;
        !           131:     }
        !           132: }
        !           133: 
        !           134: static void term_forward_char(void)
        !           135: {
        !           136:     if (term_cmd_buf_index < term_cmd_buf_size) {
        !           137:         term_cmd_buf_index++;
        !           138:     }
        !           139: }
        !           140: 
        !           141: static void term_delete_char(void)
        !           142: {
        !           143:     if (term_cmd_buf_index < term_cmd_buf_size) {
        !           144:         memmove(term_cmd_buf + term_cmd_buf_index,
        !           145:                 term_cmd_buf + term_cmd_buf_index + 1,
        !           146:                 term_cmd_buf_size - term_cmd_buf_index - 1);
        !           147:         term_cmd_buf_size--;
        !           148:     }
        !           149: }
        !           150: 
        !           151: static void term_backspace(void)
        !           152: {
        !           153:     if (term_cmd_buf_index > 0) {
        !           154:         term_backward_char();
        !           155:         term_delete_char();
        !           156:     }
        !           157: }
        !           158: 
        !           159: static void term_bol(void)
        !           160: {
        !           161:     term_cmd_buf_index = 0;
        !           162: }
        !           163: 
        !           164: static void term_eol(void)
        !           165: {
        !           166:     term_cmd_buf_index = term_cmd_buf_size;
        !           167: }
        !           168: 
        !           169: static void term_up_char(void)
        !           170: {
        !           171:     int idx;
        !           172: 
        !           173:     if (term_hist_entry == 0)
        !           174:        return;
        !           175:     if (term_hist_entry == -1) {
        !           176:        /* Find latest entry */
        !           177:        for (idx = 0; idx < TERM_MAX_CMDS; idx++) {
        !           178:            if (term_history[idx] == NULL)
        !           179:                break;
        !           180:        }
        !           181:        term_hist_entry = idx;
        !           182:     }
        !           183:     term_hist_entry--;
        !           184:     if (term_hist_entry >= 0) {
        !           185:        pstrcpy(term_cmd_buf, sizeof(term_cmd_buf), 
        !           186:                 term_history[term_hist_entry]);
        !           187:        term_cmd_buf_index = term_cmd_buf_size = strlen(term_cmd_buf);
        !           188:     }
        !           189: }
        !           190: 
        !           191: static void term_down_char(void)
        !           192: {
        !           193:     if (term_hist_entry == TERM_MAX_CMDS - 1 || term_hist_entry == -1)
        !           194:        return;
        !           195:     if (term_history[++term_hist_entry] != NULL) {
        !           196:        pstrcpy(term_cmd_buf, sizeof(term_cmd_buf),
        !           197:                 term_history[term_hist_entry]);
        !           198:     } else {
        !           199:        term_hist_entry = -1;
        !           200:     }
        !           201:     term_cmd_buf_index = term_cmd_buf_size = strlen(term_cmd_buf);
        !           202: }
        !           203: 
        !           204: static void term_hist_add(const char *cmdline)
        !           205: {
        !           206:     char *hist_entry, *new_entry;
        !           207:     int idx;
        !           208: 
        !           209:     if (cmdline[0] == '\0')
        !           210:        return;
        !           211:     new_entry = NULL;
        !           212:     if (term_hist_entry != -1) {
        !           213:        /* We were editing an existing history entry: replace it */
        !           214:        hist_entry = term_history[term_hist_entry];
        !           215:        idx = term_hist_entry;
        !           216:        if (strcmp(hist_entry, cmdline) == 0) {
        !           217:            goto same_entry;
        !           218:        }
        !           219:     }
        !           220:     /* Search cmdline in history buffers */
        !           221:     for (idx = 0; idx < TERM_MAX_CMDS; idx++) {
        !           222:        hist_entry = term_history[idx];
        !           223:        if (hist_entry == NULL)
        !           224:            break;
        !           225:        if (strcmp(hist_entry, cmdline) == 0) {
        !           226:        same_entry:
        !           227:            new_entry = hist_entry;
        !           228:            /* Put this entry at the end of history */
        !           229:            memmove(&term_history[idx], &term_history[idx + 1],
        !           230:                    &term_history[TERM_MAX_CMDS] - &term_history[idx + 1]);
        !           231:            term_history[TERM_MAX_CMDS - 1] = NULL;
        !           232:            for (; idx < TERM_MAX_CMDS; idx++) {
        !           233:                if (term_history[idx] == NULL)
        !           234:                    break;
        !           235:            }
        !           236:            break;
        !           237:        }
        !           238:     }
        !           239:     if (idx == TERM_MAX_CMDS) {
        !           240:        /* Need to get one free slot */
        !           241:        free(term_history[0]);
        !           242:        memcpy(term_history, &term_history[1],
        !           243:               &term_history[TERM_MAX_CMDS] - &term_history[1]);
        !           244:        term_history[TERM_MAX_CMDS - 1] = NULL;
        !           245:        idx = TERM_MAX_CMDS - 1;
        !           246:     }
        !           247:     if (new_entry == NULL)
        !           248:        new_entry = strdup(cmdline);
        !           249:     term_history[idx] = new_entry;
        !           250:     term_hist_entry = -1;
        !           251: }
        !           252: 
        !           253: /* completion support */
        !           254: 
        !           255: void add_completion(const char *str)
        !           256: {
        !           257:     if (nb_completions < NB_COMPLETIONS_MAX) {
        !           258:         completions[nb_completions++] = qemu_strdup(str);
        !           259:     }
        !           260: }
        !           261: 
        !           262: static void term_completion(void)
        !           263: {
        !           264:     int len, i, j, max_width, nb_cols;
        !           265:     char *cmdline;
        !           266: 
        !           267:     nb_completions = 0;
        !           268:     
        !           269:     cmdline = qemu_malloc(term_cmd_buf_index + 1);
        !           270:     if (!cmdline)
        !           271:         return;
        !           272:     memcpy(cmdline, term_cmd_buf, term_cmd_buf_index);
        !           273:     cmdline[term_cmd_buf_index] = '\0';
        !           274:     readline_find_completion(cmdline);
        !           275:     qemu_free(cmdline);
        !           276: 
        !           277:     /* no completion found */
        !           278:     if (nb_completions <= 0)
        !           279:         return;
        !           280:     if (nb_completions == 1) {
        !           281:         len = strlen(completions[0]);
        !           282:         for(i = completion_index; i < len; i++) {
        !           283:             term_insert_char(completions[0][i]);
        !           284:         }
        !           285:         /* extra space for next argument. XXX: make it more generic */
        !           286:         if (len > 0 && completions[0][len - 1] != '/')
        !           287:             term_insert_char(' ');
        !           288:     } else {
        !           289:         term_printf("\n");
        !           290:         max_width = 0;
        !           291:         for(i = 0; i < nb_completions; i++) {
        !           292:             len = strlen(completions[i]);
        !           293:             if (len > max_width)
        !           294:                 max_width = len;
        !           295:         }
        !           296:         max_width += 2;
        !           297:         if (max_width < 10)
        !           298:             max_width = 10;
        !           299:         else if (max_width > 80)
        !           300:             max_width = 80;
        !           301:         nb_cols = 80 / max_width;
        !           302:         j = 0;
        !           303:         for(i = 0; i < nb_completions; i++) {
        !           304:             term_printf("%-*s", max_width, completions[i]);
        !           305:             if (++j == nb_cols || i == (nb_completions - 1)) {
        !           306:                 term_printf("\n");
        !           307:                 j = 0;
        !           308:             }
        !           309:         }
        !           310:         term_show_prompt2();
        !           311:     }
        !           312: }
        !           313: 
        !           314: /* return true if command handled */
        !           315: void readline_handle_byte(int ch)
        !           316: {
        !           317:     switch(term_esc_state) {
        !           318:     case IS_NORM:
        !           319:         switch(ch) {
        !           320:         case 1:
        !           321:             term_bol();
        !           322:             break;
        !           323:         case 4:
        !           324:             term_delete_char();
        !           325:             break;
        !           326:         case 5:
        !           327:             term_eol();
        !           328:             break;
        !           329:         case 9:
        !           330:             term_completion();
        !           331:             break;
        !           332:         case 10:
        !           333:         case 13:
        !           334:             term_cmd_buf[term_cmd_buf_size] = '\0';
        !           335:             if (!term_is_password)
        !           336:                 term_hist_add(term_cmd_buf);
        !           337:             term_printf("\n");
        !           338:             /* NOTE: readline_start can be called here */
        !           339:             term_readline_func(term_readline_opaque, term_cmd_buf);
        !           340:             break;
        !           341:         case 27:
        !           342:             term_esc_state = IS_ESC;
        !           343:             break;
        !           344:         case 127:
        !           345:         case 8:
        !           346:             term_backspace();
        !           347:             break;
        !           348:        case 155:
        !           349:             term_esc_state = IS_CSI;
        !           350:            break;
        !           351:         default:
        !           352:             if (ch >= 32) {
        !           353:                 term_insert_char(ch);
        !           354:             }
        !           355:             break;
        !           356:         }
        !           357:         break;
        !           358:     case IS_ESC:
        !           359:         if (ch == '[') {
        !           360:             term_esc_state = IS_CSI;
        !           361:             term_esc_param = 0;
        !           362:         } else {
        !           363:             term_esc_state = IS_NORM;
        !           364:         }
        !           365:         break;
        !           366:     case IS_CSI:
        !           367:         switch(ch) {
        !           368:        case 'A':
        !           369:        case 'F':
        !           370:            term_up_char();
        !           371:            break;
        !           372:        case 'B':
        !           373:        case 'E':
        !           374:            term_down_char();
        !           375:            break;
        !           376:         case 'D':
        !           377:             term_backward_char();
        !           378:             break;
        !           379:         case 'C':
        !           380:             term_forward_char();
        !           381:             break;
        !           382:         case '0' ... '9':
        !           383:             term_esc_param = term_esc_param * 10 + (ch - '0');
        !           384:             goto the_end;
        !           385:         case '~':
        !           386:             switch(term_esc_param) {
        !           387:             case 1:
        !           388:                 term_bol();
        !           389:                 break;
        !           390:             case 3:
        !           391:                 term_delete_char();
        !           392:                 break;
        !           393:             case 4:
        !           394:                 term_eol();
        !           395:                 break;
        !           396:             }
        !           397:             break;
        !           398:         default:
        !           399:             break;
        !           400:         }
        !           401:         term_esc_state = IS_NORM;
        !           402:     the_end:
        !           403:         break;
        !           404:     }
        !           405:     term_update();
        !           406: }
        !           407: 
        !           408: void readline_start(const char *prompt, int is_password,
        !           409:                     ReadLineFunc *readline_func, void *opaque)
        !           410: {
        !           411:     pstrcpy(term_prompt, sizeof(term_prompt), prompt);
        !           412:     term_readline_func = readline_func;
        !           413:     term_readline_opaque = opaque;
        !           414:     term_is_password = is_password;
        !           415:     term_show_prompt();
        !           416: }
        !           417: 
        !           418: const char *readline_get_history(unsigned int index)
        !           419: {
        !           420:     if (index >= TERM_MAX_CMDS)
        !           421:         return NULL;
        !           422:     return term_history[index];
        !           423: }
        !           424: 
        !           425: 

unix.superglobalmegacorp.com