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 } 477 478 void readline_free(ReadLineState *rs) 479 { 480 if (rs) { 481 rs->mon = NULL; 482 rs->completion_finder = NULL; 483 qemu_free(rs); 484 } 485 } 486