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 25 #include "qemu/osdep.h" 26 #include "qemu-common.h" 27 #include "qemu/readline.h" 28 29 #define IS_NORM 0 30 #define IS_ESC 1 31 #define IS_CSI 2 32 #define IS_SS3 3 33 34 void readline_show_prompt(ReadLineState *rs) 35 { 36 rs->printf_func(rs->opaque, "%s", rs->prompt); 37 rs->flush_func(rs->opaque); 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 rs->printf_func(rs->opaque, "\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 rs->printf_func(rs->opaque, "*"); 58 } else { 59 rs->printf_func(rs->opaque, "%s", rs->cmd_buf); 60 } 61 rs->printf_func(rs->opaque, "\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 rs->printf_func(rs->opaque, "\033[C"); 71 } 72 } else { 73 delta = -delta; 74 for(i = 0;i < delta; i++) { 75 rs->printf_func(rs->opaque, "\033[D"); 76 } 77 } 78 rs->last_cmd_buf_index = rs->cmd_buf_index; 79 } 80 rs->flush_func(rs->opaque); 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 g_free(rs->history[0]); 251 memmove(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 = g_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 int i; 268 for (i = 0; i < rs->nb_completions; i++) { 269 if (!strcmp(rs->completions[i], str)) { 270 return; 271 } 272 } 273 rs->completions[rs->nb_completions++] = g_strdup(str); 274 } 275 } 276 277 void readline_set_completion_index(ReadLineState *rs, int index) 278 { 279 rs->completion_index = index; 280 } 281 282 static int completion_comp(const void *a, const void *b) 283 { 284 return strcmp(*(const char **) a, *(const char **) b); 285 } 286 287 static void readline_completion(ReadLineState *rs) 288 { 289 int len, i, j, max_width, nb_cols, max_prefix; 290 char *cmdline; 291 292 rs->nb_completions = 0; 293 294 cmdline = g_strndup(rs->cmd_buf, rs->cmd_buf_index); 295 rs->completion_finder(rs->opaque, cmdline); 296 g_free(cmdline); 297 298 /* no completion found */ 299 if (rs->nb_completions <= 0) 300 return; 301 if (rs->nb_completions == 1) { 302 len = strlen(rs->completions[0]); 303 for(i = rs->completion_index; i < len; i++) { 304 readline_insert_char(rs, rs->completions[0][i]); 305 } 306 /* extra space for next argument. XXX: make it more generic */ 307 if (len > 0 && rs->completions[0][len - 1] != '/') 308 readline_insert_char(rs, ' '); 309 } else { 310 qsort(rs->completions, rs->nb_completions, sizeof(char *), 311 completion_comp); 312 rs->printf_func(rs->opaque, "\n"); 313 max_width = 0; 314 max_prefix = 0; 315 for(i = 0; i < rs->nb_completions; i++) { 316 len = strlen(rs->completions[i]); 317 if (i==0) { 318 max_prefix = len; 319 } else { 320 if (len < max_prefix) 321 max_prefix = len; 322 for(j=0; j<max_prefix; j++) { 323 if (rs->completions[i][j] != rs->completions[0][j]) 324 max_prefix = j; 325 } 326 } 327 if (len > max_width) 328 max_width = len; 329 } 330 if (max_prefix > 0) 331 for(i = rs->completion_index; i < max_prefix; i++) { 332 readline_insert_char(rs, rs->completions[0][i]); 333 } 334 max_width += 2; 335 if (max_width < 10) 336 max_width = 10; 337 else if (max_width > 80) 338 max_width = 80; 339 nb_cols = 80 / max_width; 340 j = 0; 341 for(i = 0; i < rs->nb_completions; i++) { 342 rs->printf_func(rs->opaque, "%-*s", max_width, rs->completions[i]); 343 if (++j == nb_cols || i == (rs->nb_completions - 1)) { 344 rs->printf_func(rs->opaque, "\n"); 345 j = 0; 346 } 347 } 348 readline_show_prompt(rs); 349 } 350 for (i = 0; i < rs->nb_completions; i++) { 351 g_free(rs->completions[i]); 352 } 353 } 354 355 static void readline_clear_screen(ReadLineState *rs) 356 { 357 rs->printf_func(rs->opaque, "\033[2J\033[1;1H"); 358 readline_show_prompt(rs); 359 } 360 361 /* return true if command handled */ 362 void readline_handle_byte(ReadLineState *rs, int ch) 363 { 364 switch(rs->esc_state) { 365 case IS_NORM: 366 switch(ch) { 367 case 1: 368 readline_bol(rs); 369 break; 370 case 4: 371 readline_delete_char(rs); 372 break; 373 case 5: 374 readline_eol(rs); 375 break; 376 case 9: 377 readline_completion(rs); 378 break; 379 case 12: 380 readline_clear_screen(rs); 381 break; 382 case 10: 383 case 13: 384 rs->cmd_buf[rs->cmd_buf_size] = '\0'; 385 if (!rs->read_password) 386 readline_hist_add(rs, rs->cmd_buf); 387 rs->printf_func(rs->opaque, "\n"); 388 rs->cmd_buf_index = 0; 389 rs->cmd_buf_size = 0; 390 rs->last_cmd_buf_index = 0; 391 rs->last_cmd_buf_size = 0; 392 rs->readline_func(rs->opaque, rs->cmd_buf, rs->readline_opaque); 393 break; 394 case 23: 395 /* ^W */ 396 readline_backword(rs); 397 break; 398 case 27: 399 rs->esc_state = IS_ESC; 400 break; 401 case 127: 402 case 8: 403 readline_backspace(rs); 404 break; 405 case 155: 406 rs->esc_state = IS_CSI; 407 break; 408 default: 409 if (ch >= 32) { 410 readline_insert_char(rs, ch); 411 } 412 break; 413 } 414 break; 415 case IS_ESC: 416 if (ch == '[') { 417 rs->esc_state = IS_CSI; 418 rs->esc_param = 0; 419 } else if (ch == 'O') { 420 rs->esc_state = IS_SS3; 421 rs->esc_param = 0; 422 } else { 423 rs->esc_state = IS_NORM; 424 } 425 break; 426 case IS_CSI: 427 switch(ch) { 428 case 'A': 429 case 'F': 430 readline_up_char(rs); 431 break; 432 case 'B': 433 case 'E': 434 readline_down_char(rs); 435 break; 436 case 'D': 437 readline_backward_char(rs); 438 break; 439 case 'C': 440 readline_forward_char(rs); 441 break; 442 case '0' ... '9': 443 rs->esc_param = rs->esc_param * 10 + (ch - '0'); 444 goto the_end; 445 case '~': 446 switch(rs->esc_param) { 447 case 1: 448 readline_bol(rs); 449 break; 450 case 3: 451 readline_delete_char(rs); 452 break; 453 case 4: 454 readline_eol(rs); 455 break; 456 } 457 break; 458 default: 459 break; 460 } 461 rs->esc_state = IS_NORM; 462 the_end: 463 break; 464 case IS_SS3: 465 switch(ch) { 466 case 'F': 467 readline_eol(rs); 468 break; 469 case 'H': 470 readline_bol(rs); 471 break; 472 } 473 rs->esc_state = IS_NORM; 474 break; 475 } 476 readline_update(rs); 477 } 478 479 void readline_start(ReadLineState *rs, const char *prompt, int read_password, 480 ReadLineFunc *readline_func, void *opaque) 481 { 482 pstrcpy(rs->prompt, sizeof(rs->prompt), prompt); 483 rs->readline_func = readline_func; 484 rs->readline_opaque = opaque; 485 rs->read_password = read_password; 486 readline_restart(rs); 487 } 488 489 void readline_restart(ReadLineState *rs) 490 { 491 rs->cmd_buf_index = 0; 492 rs->cmd_buf_size = 0; 493 } 494 495 const char *readline_get_history(ReadLineState *rs, unsigned int index) 496 { 497 if (index >= READLINE_MAX_CMDS) 498 return NULL; 499 return rs->history[index]; 500 } 501 502 ReadLineState *readline_init(ReadLinePrintfFunc *printf_func, 503 ReadLineFlushFunc *flush_func, 504 void *opaque, 505 ReadLineCompletionFunc *completion_finder) 506 { 507 ReadLineState *rs = g_malloc0(sizeof(*rs)); 508 509 rs->hist_entry = -1; 510 rs->opaque = opaque; 511 rs->printf_func = printf_func; 512 rs->flush_func = flush_func; 513 rs->completion_finder = completion_finder; 514 515 return rs; 516 } 517