1 /* 2 * Command line editing and history 3 * Copyright (c) 2010-2011, Jouni Malinen <j@w1.fi> 4 * 5 * This software may be distributed under the terms of the BSD license. 6 * See README for more details. 7 */ 8 9 #include "includes.h" 10 #include <termios.h> 11 12 #include "common.h" 13 #include "eloop.h" 14 #include "list.h" 15 #include "edit.h" 16 17 #define CMD_BUF_LEN 256 18 static char cmdbuf[CMD_BUF_LEN]; 19 static int cmdbuf_pos = 0; 20 static int cmdbuf_len = 0; 21 static char currbuf[CMD_BUF_LEN]; 22 static int currbuf_valid = 0; 23 static const char *ps2 = NULL; 24 25 #define HISTORY_MAX 100 26 27 struct edit_history { 28 struct dl_list list; 29 char str[1]; 30 }; 31 32 static struct dl_list history_list; 33 static struct edit_history *history_curr; 34 35 static void *edit_cb_ctx; 36 static void (*edit_cmd_cb)(void *ctx, char *cmd); 37 static void (*edit_eof_cb)(void *ctx); 38 static char ** (*edit_completion_cb)(void *ctx, const char *cmd, int pos) = 39 NULL; 40 41 static struct termios prevt, newt; 42 43 44 #define CLEAR_END_LINE "\e[K" 45 46 47 void edit_clear_line(void) 48 { 49 int i; 50 putchar('\r'); 51 for (i = 0; i < cmdbuf_len + 2 + (ps2 ? (int) os_strlen(ps2) : 0); i++) 52 putchar(' '); 53 } 54 55 56 static void move_start(void) 57 { 58 cmdbuf_pos = 0; 59 edit_redraw(); 60 } 61 62 63 static void move_end(void) 64 { 65 cmdbuf_pos = cmdbuf_len; 66 edit_redraw(); 67 } 68 69 70 static void move_left(void) 71 { 72 if (cmdbuf_pos > 0) { 73 cmdbuf_pos--; 74 edit_redraw(); 75 } 76 } 77 78 79 static void move_right(void) 80 { 81 if (cmdbuf_pos < cmdbuf_len) { 82 cmdbuf_pos++; 83 edit_redraw(); 84 } 85 } 86 87 88 static void move_word_left(void) 89 { 90 while (cmdbuf_pos > 0 && cmdbuf[cmdbuf_pos - 1] == ' ') 91 cmdbuf_pos--; 92 while (cmdbuf_pos > 0 && cmdbuf[cmdbuf_pos - 1] != ' ') 93 cmdbuf_pos--; 94 edit_redraw(); 95 } 96 97 98 static void move_word_right(void) 99 { 100 while (cmdbuf_pos < cmdbuf_len && cmdbuf[cmdbuf_pos] == ' ') 101 cmdbuf_pos++; 102 while (cmdbuf_pos < cmdbuf_len && cmdbuf[cmdbuf_pos] != ' ') 103 cmdbuf_pos++; 104 edit_redraw(); 105 } 106 107 108 static void delete_left(void) 109 { 110 if (cmdbuf_pos == 0) 111 return; 112 113 edit_clear_line(); 114 os_memmove(cmdbuf + cmdbuf_pos - 1, cmdbuf + cmdbuf_pos, 115 cmdbuf_len - cmdbuf_pos); 116 cmdbuf_pos--; 117 cmdbuf_len--; 118 edit_redraw(); 119 } 120 121 122 static void delete_current(void) 123 { 124 if (cmdbuf_pos == cmdbuf_len) 125 return; 126 127 edit_clear_line(); 128 os_memmove(cmdbuf + cmdbuf_pos, cmdbuf + cmdbuf_pos + 1, 129 cmdbuf_len - cmdbuf_pos); 130 cmdbuf_len--; 131 edit_redraw(); 132 } 133 134 135 static void delete_word(void) 136 { 137 int pos; 138 139 edit_clear_line(); 140 pos = cmdbuf_pos; 141 while (pos > 0 && cmdbuf[pos - 1] == ' ') 142 pos--; 143 while (pos > 0 && cmdbuf[pos - 1] != ' ') 144 pos--; 145 os_memmove(cmdbuf + pos, cmdbuf + cmdbuf_pos, cmdbuf_len - cmdbuf_pos); 146 cmdbuf_len -= cmdbuf_pos - pos; 147 cmdbuf_pos = pos; 148 edit_redraw(); 149 } 150 151 152 static void clear_left(void) 153 { 154 if (cmdbuf_pos == 0) 155 return; 156 157 edit_clear_line(); 158 os_memmove(cmdbuf, cmdbuf + cmdbuf_pos, cmdbuf_len - cmdbuf_pos); 159 cmdbuf_len -= cmdbuf_pos; 160 cmdbuf_pos = 0; 161 edit_redraw(); 162 } 163 164 165 static void clear_right(void) 166 { 167 if (cmdbuf_pos == cmdbuf_len) 168 return; 169 170 edit_clear_line(); 171 cmdbuf_len = cmdbuf_pos; 172 edit_redraw(); 173 } 174 175 176 static void history_add(const char *str) 177 { 178 struct edit_history *h, *match = NULL, *last = NULL; 179 size_t len, count = 0; 180 181 if (str[0] == '\0') 182 return; 183 184 dl_list_for_each(h, &history_list, struct edit_history, list) { 185 if (os_strcmp(str, h->str) == 0) { 186 match = h; 187 break; 188 } 189 last = h; 190 count++; 191 } 192 193 if (match) { 194 dl_list_del(&h->list); 195 dl_list_add(&history_list, &h->list); 196 history_curr = h; 197 return; 198 } 199 200 if (count >= HISTORY_MAX && last) { 201 dl_list_del(&last->list); 202 os_free(last); 203 } 204 205 len = os_strlen(str); 206 h = os_zalloc(sizeof(*h) + len); 207 if (h == NULL) 208 return; 209 dl_list_add(&history_list, &h->list); 210 os_strlcpy(h->str, str, len + 1); 211 history_curr = h; 212 } 213 214 215 static void history_use(void) 216 { 217 edit_clear_line(); 218 cmdbuf_len = cmdbuf_pos = os_strlen(history_curr->str); 219 os_memcpy(cmdbuf, history_curr->str, cmdbuf_len); 220 edit_redraw(); 221 } 222 223 224 static void history_prev(void) 225 { 226 if (history_curr == NULL) 227 return; 228 229 if (history_curr == 230 dl_list_first(&history_list, struct edit_history, list)) { 231 if (!currbuf_valid) { 232 cmdbuf[cmdbuf_len] = '\0'; 233 os_memcpy(currbuf, cmdbuf, cmdbuf_len + 1); 234 currbuf_valid = 1; 235 history_use(); 236 return; 237 } 238 } 239 240 if (history_curr == 241 dl_list_last(&history_list, struct edit_history, list)) 242 return; 243 244 history_curr = dl_list_entry(history_curr->list.next, 245 struct edit_history, list); 246 history_use(); 247 } 248 249 250 static void history_next(void) 251 { 252 if (history_curr == NULL || 253 history_curr == 254 dl_list_first(&history_list, struct edit_history, list)) { 255 if (currbuf_valid) { 256 currbuf_valid = 0; 257 edit_clear_line(); 258 cmdbuf_len = cmdbuf_pos = os_strlen(currbuf); 259 os_memcpy(cmdbuf, currbuf, cmdbuf_len); 260 edit_redraw(); 261 } 262 return; 263 } 264 265 history_curr = dl_list_entry(history_curr->list.prev, 266 struct edit_history, list); 267 history_use(); 268 } 269 270 271 static void history_read(const char *fname) 272 { 273 FILE *f; 274 char buf[CMD_BUF_LEN], *pos; 275 276 f = fopen(fname, "r"); 277 if (f == NULL) 278 return; 279 280 while (fgets(buf, CMD_BUF_LEN, f)) { 281 for (pos = buf; *pos; pos++) { 282 if (*pos == '\r' || *pos == '\n') { 283 *pos = '\0'; 284 break; 285 } 286 } 287 history_add(buf); 288 } 289 290 fclose(f); 291 } 292 293 294 static void history_write(const char *fname, 295 int (*filter_cb)(void *ctx, const char *cmd)) 296 { 297 FILE *f; 298 struct edit_history *h; 299 300 f = fopen(fname, "w"); 301 if (f == NULL) 302 return; 303 304 dl_list_for_each_reverse(h, &history_list, struct edit_history, list) { 305 if (filter_cb && filter_cb(edit_cb_ctx, h->str)) 306 continue; 307 fprintf(f, "%s\n", h->str); 308 } 309 310 fclose(f); 311 } 312 313 314 static void history_debug_dump(void) 315 { 316 struct edit_history *h; 317 edit_clear_line(); 318 printf("\r"); 319 dl_list_for_each_reverse(h, &history_list, struct edit_history, list) 320 printf("%s%s\n", h == history_curr ? "[C]" : "", h->str); 321 if (currbuf_valid) 322 printf("{%s}\n", currbuf); 323 edit_redraw(); 324 } 325 326 327 static void insert_char(int c) 328 { 329 if (cmdbuf_len >= (int) sizeof(cmdbuf) - 1) 330 return; 331 if (cmdbuf_len == cmdbuf_pos) { 332 cmdbuf[cmdbuf_pos++] = c; 333 cmdbuf_len++; 334 putchar(c); 335 fflush(stdout); 336 } else { 337 os_memmove(cmdbuf + cmdbuf_pos + 1, cmdbuf + cmdbuf_pos, 338 cmdbuf_len - cmdbuf_pos); 339 cmdbuf[cmdbuf_pos++] = c; 340 cmdbuf_len++; 341 edit_redraw(); 342 } 343 } 344 345 346 static void process_cmd(void) 347 { 348 349 if (cmdbuf_len == 0) { 350 printf("\n%s> ", ps2 ? ps2 : ""); 351 fflush(stdout); 352 return; 353 } 354 printf("\n"); 355 cmdbuf[cmdbuf_len] = '\0'; 356 history_add(cmdbuf); 357 cmdbuf_pos = 0; 358 cmdbuf_len = 0; 359 edit_cmd_cb(edit_cb_ctx, cmdbuf); 360 printf("%s> ", ps2 ? ps2 : ""); 361 fflush(stdout); 362 } 363 364 365 static void free_completions(char **c) 366 { 367 int i; 368 if (c == NULL) 369 return; 370 for (i = 0; c[i]; i++) 371 os_free(c[i]); 372 os_free(c); 373 } 374 375 376 static int filter_strings(char **c, char *str, size_t len) 377 { 378 int i, j; 379 380 for (i = 0, j = 0; c[j]; j++) { 381 if (os_strncasecmp(c[j], str, len) == 0) { 382 if (i != j) { 383 c[i] = c[j]; 384 c[j] = NULL; 385 } 386 i++; 387 } else { 388 os_free(c[j]); 389 c[j] = NULL; 390 } 391 } 392 c[i] = NULL; 393 return i; 394 } 395 396 397 static int common_len(const char *a, const char *b) 398 { 399 int len = 0; 400 while (a[len] && a[len] == b[len]) 401 len++; 402 return len; 403 } 404 405 406 static int max_common_length(char **c) 407 { 408 int len, i; 409 410 len = os_strlen(c[0]); 411 for (i = 1; c[i]; i++) { 412 int same = common_len(c[0], c[i]); 413 if (same < len) 414 len = same; 415 } 416 417 return len; 418 } 419 420 421 static int cmp_str(const void *a, const void *b) 422 { 423 return os_strcmp(* (const char **) a, * (const char **) b); 424 } 425 426 static void complete(int list) 427 { 428 char **c; 429 int i, len, count; 430 int start, end; 431 int room, plen, add_space; 432 433 if (edit_completion_cb == NULL) 434 return; 435 436 cmdbuf[cmdbuf_len] = '\0'; 437 c = edit_completion_cb(edit_cb_ctx, cmdbuf, cmdbuf_pos); 438 if (c == NULL) 439 return; 440 441 end = cmdbuf_pos; 442 start = end; 443 while (start > 0 && cmdbuf[start - 1] != ' ') 444 start--; 445 plen = end - start; 446 447 count = filter_strings(c, &cmdbuf[start], plen); 448 if (count == 0) { 449 free_completions(c); 450 return; 451 } 452 453 len = max_common_length(c); 454 if (len <= plen && count > 1) { 455 if (list) { 456 qsort(c, count, sizeof(char *), cmp_str); 457 edit_clear_line(); 458 printf("\r"); 459 for (i = 0; c[i]; i++) 460 printf("%s%s", i > 0 ? " " : "", c[i]); 461 printf("\n"); 462 edit_redraw(); 463 } 464 free_completions(c); 465 return; 466 } 467 len -= plen; 468 469 room = sizeof(cmdbuf) - 1 - cmdbuf_len; 470 if (room < len) 471 len = room; 472 add_space = count == 1 && len < room; 473 474 os_memmove(cmdbuf + cmdbuf_pos + len + add_space, cmdbuf + cmdbuf_pos, 475 cmdbuf_len - cmdbuf_pos); 476 os_memcpy(&cmdbuf[cmdbuf_pos - plen], c[0], plen + len); 477 if (add_space) 478 cmdbuf[cmdbuf_pos + len] = ' '; 479 480 cmdbuf_pos += len + add_space; 481 cmdbuf_len += len + add_space; 482 483 edit_redraw(); 484 485 free_completions(c); 486 } 487 488 489 enum edit_key_code { 490 EDIT_KEY_NONE = 256, 491 EDIT_KEY_TAB, 492 EDIT_KEY_UP, 493 EDIT_KEY_DOWN, 494 EDIT_KEY_RIGHT, 495 EDIT_KEY_LEFT, 496 EDIT_KEY_ENTER, 497 EDIT_KEY_BACKSPACE, 498 EDIT_KEY_INSERT, 499 EDIT_KEY_DELETE, 500 EDIT_KEY_HOME, 501 EDIT_KEY_END, 502 EDIT_KEY_PAGE_UP, 503 EDIT_KEY_PAGE_DOWN, 504 EDIT_KEY_F1, 505 EDIT_KEY_F2, 506 EDIT_KEY_F3, 507 EDIT_KEY_F4, 508 EDIT_KEY_F5, 509 EDIT_KEY_F6, 510 EDIT_KEY_F7, 511 EDIT_KEY_F8, 512 EDIT_KEY_F9, 513 EDIT_KEY_F10, 514 EDIT_KEY_F11, 515 EDIT_KEY_F12, 516 EDIT_KEY_CTRL_UP, 517 EDIT_KEY_CTRL_DOWN, 518 EDIT_KEY_CTRL_RIGHT, 519 EDIT_KEY_CTRL_LEFT, 520 EDIT_KEY_CTRL_A, 521 EDIT_KEY_CTRL_B, 522 EDIT_KEY_CTRL_D, 523 EDIT_KEY_CTRL_E, 524 EDIT_KEY_CTRL_F, 525 EDIT_KEY_CTRL_G, 526 EDIT_KEY_CTRL_H, 527 EDIT_KEY_CTRL_J, 528 EDIT_KEY_CTRL_K, 529 EDIT_KEY_CTRL_L, 530 EDIT_KEY_CTRL_N, 531 EDIT_KEY_CTRL_O, 532 EDIT_KEY_CTRL_P, 533 EDIT_KEY_CTRL_R, 534 EDIT_KEY_CTRL_T, 535 EDIT_KEY_CTRL_U, 536 EDIT_KEY_CTRL_V, 537 EDIT_KEY_CTRL_W, 538 EDIT_KEY_ALT_UP, 539 EDIT_KEY_ALT_DOWN, 540 EDIT_KEY_ALT_RIGHT, 541 EDIT_KEY_ALT_LEFT, 542 EDIT_KEY_SHIFT_UP, 543 EDIT_KEY_SHIFT_DOWN, 544 EDIT_KEY_SHIFT_RIGHT, 545 EDIT_KEY_SHIFT_LEFT, 546 EDIT_KEY_ALT_SHIFT_UP, 547 EDIT_KEY_ALT_SHIFT_DOWN, 548 EDIT_KEY_ALT_SHIFT_RIGHT, 549 EDIT_KEY_ALT_SHIFT_LEFT, 550 EDIT_KEY_EOF 551 }; 552 553 static void show_esc_buf(const char *esc_buf, char c, int i) 554 { 555 edit_clear_line(); 556 printf("\rESC buffer '%s' c='%c' [%d]\n", esc_buf, c, i); 557 edit_redraw(); 558 } 559 560 561 static enum edit_key_code esc_seq_to_key1_no(char last) 562 { 563 switch (last) { 564 case 'A': 565 return EDIT_KEY_UP; 566 case 'B': 567 return EDIT_KEY_DOWN; 568 case 'C': 569 return EDIT_KEY_RIGHT; 570 case 'D': 571 return EDIT_KEY_LEFT; 572 default: 573 return EDIT_KEY_NONE; 574 } 575 } 576 577 578 static enum edit_key_code esc_seq_to_key1_shift(char last) 579 { 580 switch (last) { 581 case 'A': 582 return EDIT_KEY_SHIFT_UP; 583 case 'B': 584 return EDIT_KEY_SHIFT_DOWN; 585 case 'C': 586 return EDIT_KEY_SHIFT_RIGHT; 587 case 'D': 588 return EDIT_KEY_SHIFT_LEFT; 589 default: 590 return EDIT_KEY_NONE; 591 } 592 } 593 594 595 static enum edit_key_code esc_seq_to_key1_alt(char last) 596 { 597 switch (last) { 598 case 'A': 599 return EDIT_KEY_ALT_UP; 600 case 'B': 601 return EDIT_KEY_ALT_DOWN; 602 case 'C': 603 return EDIT_KEY_ALT_RIGHT; 604 case 'D': 605 return EDIT_KEY_ALT_LEFT; 606 default: 607 return EDIT_KEY_NONE; 608 } 609 } 610 611 612 static enum edit_key_code esc_seq_to_key1_alt_shift(char last) 613 { 614 switch (last) { 615 case 'A': 616 return EDIT_KEY_ALT_SHIFT_UP; 617 case 'B': 618 return EDIT_KEY_ALT_SHIFT_DOWN; 619 case 'C': 620 return EDIT_KEY_ALT_SHIFT_RIGHT; 621 case 'D': 622 return EDIT_KEY_ALT_SHIFT_LEFT; 623 default: 624 return EDIT_KEY_NONE; 625 } 626 } 627 628 629 static enum edit_key_code esc_seq_to_key1_ctrl(char last) 630 { 631 switch (last) { 632 case 'A': 633 return EDIT_KEY_CTRL_UP; 634 case 'B': 635 return EDIT_KEY_CTRL_DOWN; 636 case 'C': 637 return EDIT_KEY_CTRL_RIGHT; 638 case 'D': 639 return EDIT_KEY_CTRL_LEFT; 640 default: 641 return EDIT_KEY_NONE; 642 } 643 } 644 645 646 static enum edit_key_code esc_seq_to_key1(int param1, int param2, char last) 647 { 648 /* ESC-[<param1>;<param2><last> */ 649 650 if (param1 < 0 && param2 < 0) 651 return esc_seq_to_key1_no(last); 652 653 if (param1 == 1 && param2 == 2) 654 return esc_seq_to_key1_shift(last); 655 656 if (param1 == 1 && param2 == 3) 657 return esc_seq_to_key1_alt(last); 658 659 if (param1 == 1 && param2 == 4) 660 return esc_seq_to_key1_alt_shift(last); 661 662 if (param1 == 1 && param2 == 5) 663 return esc_seq_to_key1_ctrl(last); 664 665 if (param2 < 0) { 666 if (last != '~') 667 return EDIT_KEY_NONE; 668 switch (param1) { 669 case 2: 670 return EDIT_KEY_INSERT; 671 case 3: 672 return EDIT_KEY_DELETE; 673 case 5: 674 return EDIT_KEY_PAGE_UP; 675 case 6: 676 return EDIT_KEY_PAGE_DOWN; 677 case 15: 678 return EDIT_KEY_F5; 679 case 17: 680 return EDIT_KEY_F6; 681 case 18: 682 return EDIT_KEY_F7; 683 case 19: 684 return EDIT_KEY_F8; 685 case 20: 686 return EDIT_KEY_F9; 687 case 21: 688 return EDIT_KEY_F10; 689 case 23: 690 return EDIT_KEY_F11; 691 case 24: 692 return EDIT_KEY_F12; 693 } 694 } 695 696 return EDIT_KEY_NONE; 697 } 698 699 700 static enum edit_key_code esc_seq_to_key2(int param1, int param2, char last) 701 { 702 /* ESC-O<param1>;<param2><last> */ 703 704 if (param1 >= 0 || param2 >= 0) 705 return EDIT_KEY_NONE; 706 707 switch (last) { 708 case 'F': 709 return EDIT_KEY_END; 710 case 'H': 711 return EDIT_KEY_HOME; 712 case 'P': 713 return EDIT_KEY_F1; 714 case 'Q': 715 return EDIT_KEY_F2; 716 case 'R': 717 return EDIT_KEY_F3; 718 case 'S': 719 return EDIT_KEY_F4; 720 default: 721 return EDIT_KEY_NONE; 722 } 723 } 724 725 726 static enum edit_key_code esc_seq_to_key(char *seq) 727 { 728 char last, *pos; 729 int param1 = -1, param2 = -1; 730 enum edit_key_code ret = EDIT_KEY_NONE; 731 732 last = '\0'; 733 for (pos = seq; *pos; pos++) 734 last = *pos; 735 736 if (seq[1] >= '0' && seq[1] <= '9') { 737 param1 = atoi(&seq[1]); 738 pos = os_strchr(seq, ';'); 739 if (pos) 740 param2 = atoi(pos + 1); 741 } 742 743 if (seq[0] == '[') 744 ret = esc_seq_to_key1(param1, param2, last); 745 else if (seq[0] == 'O') 746 ret = esc_seq_to_key2(param1, param2, last); 747 748 if (ret != EDIT_KEY_NONE) 749 return ret; 750 751 edit_clear_line(); 752 printf("\rUnknown escape sequence '%s'\n", seq); 753 edit_redraw(); 754 return EDIT_KEY_NONE; 755 } 756 757 758 static enum edit_key_code edit_read_key(int sock) 759 { 760 int c; 761 unsigned char buf[1]; 762 int res; 763 static int esc = -1; 764 static char esc_buf[7]; 765 766 res = read(sock, buf, 1); 767 if (res < 0) 768 perror("read"); 769 if (res <= 0) 770 return EDIT_KEY_EOF; 771 772 c = buf[0]; 773 774 if (esc >= 0) { 775 if (c == 27 /* ESC */) { 776 esc = 0; 777 return EDIT_KEY_NONE; 778 } 779 780 if (esc == 6) { 781 show_esc_buf(esc_buf, c, 0); 782 esc = -1; 783 } else { 784 esc_buf[esc++] = c; 785 esc_buf[esc] = '\0'; 786 } 787 } 788 789 if (esc == 1) { 790 if (esc_buf[0] != '[' && esc_buf[0] != 'O') { 791 show_esc_buf(esc_buf, c, 1); 792 esc = -1; 793 return EDIT_KEY_NONE; 794 } else 795 return EDIT_KEY_NONE; /* Escape sequence continues */ 796 } 797 798 if (esc > 1) { 799 if ((c >= '0' && c <= '9') || c == ';') 800 return EDIT_KEY_NONE; /* Escape sequence continues */ 801 802 if (c == '~' || (c >= 'A' && c <= 'Z')) { 803 esc = -1; 804 return esc_seq_to_key(esc_buf); 805 } 806 807 show_esc_buf(esc_buf, c, 2); 808 esc = -1; 809 return EDIT_KEY_NONE; 810 } 811 812 switch (c) { 813 case 1: 814 return EDIT_KEY_CTRL_A; 815 case 2: 816 return EDIT_KEY_CTRL_B; 817 case 4: 818 return EDIT_KEY_CTRL_D; 819 case 5: 820 return EDIT_KEY_CTRL_E; 821 case 6: 822 return EDIT_KEY_CTRL_F; 823 case 7: 824 return EDIT_KEY_CTRL_G; 825 case 8: 826 return EDIT_KEY_CTRL_H; 827 case 9: 828 return EDIT_KEY_TAB; 829 case 10: 830 return EDIT_KEY_CTRL_J; 831 case 13: /* CR */ 832 return EDIT_KEY_ENTER; 833 case 11: 834 return EDIT_KEY_CTRL_K; 835 case 12: 836 return EDIT_KEY_CTRL_L; 837 case 14: 838 return EDIT_KEY_CTRL_N; 839 case 15: 840 return EDIT_KEY_CTRL_O; 841 case 16: 842 return EDIT_KEY_CTRL_P; 843 case 18: 844 return EDIT_KEY_CTRL_R; 845 case 20: 846 return EDIT_KEY_CTRL_T; 847 case 21: 848 return EDIT_KEY_CTRL_U; 849 case 22: 850 return EDIT_KEY_CTRL_V; 851 case 23: 852 return EDIT_KEY_CTRL_W; 853 case 27: /* ESC */ 854 esc = 0; 855 return EDIT_KEY_NONE; 856 case 127: 857 return EDIT_KEY_BACKSPACE; 858 default: 859 return c; 860 } 861 } 862 863 864 static char search_buf[21]; 865 static int search_skip; 866 867 static char * search_find(void) 868 { 869 struct edit_history *h; 870 size_t len = os_strlen(search_buf); 871 int skip = search_skip; 872 873 if (len == 0) 874 return NULL; 875 876 dl_list_for_each(h, &history_list, struct edit_history, list) { 877 if (os_strstr(h->str, search_buf)) { 878 if (skip == 0) 879 return h->str; 880 skip--; 881 } 882 } 883 884 search_skip = 0; 885 return NULL; 886 } 887 888 889 static void search_redraw(void) 890 { 891 char *match = search_find(); 892 printf("\rsearch '%s': %s" CLEAR_END_LINE, 893 search_buf, match ? match : ""); 894 printf("\rsearch '%s", search_buf); 895 fflush(stdout); 896 } 897 898 899 static void search_start(void) 900 { 901 edit_clear_line(); 902 search_buf[0] = '\0'; 903 search_skip = 0; 904 search_redraw(); 905 } 906 907 908 static void search_clear(void) 909 { 910 search_redraw(); 911 printf("\r" CLEAR_END_LINE); 912 } 913 914 915 static void search_stop(void) 916 { 917 char *match = search_find(); 918 search_buf[0] = '\0'; 919 search_clear(); 920 if (match) { 921 os_strlcpy(cmdbuf, match, CMD_BUF_LEN); 922 cmdbuf_len = os_strlen(cmdbuf); 923 cmdbuf_pos = cmdbuf_len; 924 } 925 edit_redraw(); 926 } 927 928 929 static void search_cancel(void) 930 { 931 search_buf[0] = '\0'; 932 search_clear(); 933 edit_redraw(); 934 } 935 936 937 static void search_backspace(void) 938 { 939 size_t len; 940 len = os_strlen(search_buf); 941 if (len == 0) 942 return; 943 search_buf[len - 1] = '\0'; 944 search_skip = 0; 945 search_redraw(); 946 } 947 948 949 static void search_next(void) 950 { 951 search_skip++; 952 search_find(); 953 search_redraw(); 954 } 955 956 957 static void search_char(char c) 958 { 959 size_t len; 960 len = os_strlen(search_buf); 961 if (len == sizeof(search_buf) - 1) 962 return; 963 search_buf[len] = c; 964 search_buf[len + 1] = '\0'; 965 search_skip = 0; 966 search_redraw(); 967 } 968 969 970 static enum edit_key_code search_key(enum edit_key_code c) 971 { 972 switch (c) { 973 case EDIT_KEY_ENTER: 974 case EDIT_KEY_CTRL_J: 975 case EDIT_KEY_LEFT: 976 case EDIT_KEY_RIGHT: 977 case EDIT_KEY_HOME: 978 case EDIT_KEY_END: 979 case EDIT_KEY_CTRL_A: 980 case EDIT_KEY_CTRL_E: 981 search_stop(); 982 return c; 983 case EDIT_KEY_DOWN: 984 case EDIT_KEY_UP: 985 search_cancel(); 986 return EDIT_KEY_EOF; 987 case EDIT_KEY_CTRL_H: 988 case EDIT_KEY_BACKSPACE: 989 search_backspace(); 990 break; 991 case EDIT_KEY_CTRL_R: 992 search_next(); 993 break; 994 default: 995 if (c >= 32 && c <= 255) 996 search_char(c); 997 break; 998 } 999 1000 return EDIT_KEY_NONE; 1001 } 1002 1003 1004 static void edit_read_char(int sock, void *eloop_ctx, void *sock_ctx) 1005 { 1006 static int last_tab = 0; 1007 static int search = 0; 1008 enum edit_key_code c; 1009 1010 c = edit_read_key(sock); 1011 1012 if (search) { 1013 c = search_key(c); 1014 if (c == EDIT_KEY_NONE) 1015 return; 1016 search = 0; 1017 if (c == EDIT_KEY_EOF) 1018 return; 1019 } 1020 1021 if (c != EDIT_KEY_TAB && c != EDIT_KEY_NONE) 1022 last_tab = 0; 1023 1024 switch (c) { 1025 case EDIT_KEY_NONE: 1026 break; 1027 case EDIT_KEY_EOF: 1028 edit_eof_cb(edit_cb_ctx); 1029 break; 1030 case EDIT_KEY_TAB: 1031 complete(last_tab); 1032 last_tab = 1; 1033 break; 1034 case EDIT_KEY_UP: 1035 case EDIT_KEY_CTRL_P: 1036 history_prev(); 1037 break; 1038 case EDIT_KEY_DOWN: 1039 case EDIT_KEY_CTRL_N: 1040 history_next(); 1041 break; 1042 case EDIT_KEY_RIGHT: 1043 case EDIT_KEY_CTRL_F: 1044 move_right(); 1045 break; 1046 case EDIT_KEY_LEFT: 1047 case EDIT_KEY_CTRL_B: 1048 move_left(); 1049 break; 1050 case EDIT_KEY_CTRL_RIGHT: 1051 move_word_right(); 1052 break; 1053 case EDIT_KEY_CTRL_LEFT: 1054 move_word_left(); 1055 break; 1056 case EDIT_KEY_DELETE: 1057 delete_current(); 1058 break; 1059 case EDIT_KEY_END: 1060 move_end(); 1061 break; 1062 case EDIT_KEY_HOME: 1063 case EDIT_KEY_CTRL_A: 1064 move_start(); 1065 break; 1066 case EDIT_KEY_F2: 1067 history_debug_dump(); 1068 break; 1069 case EDIT_KEY_CTRL_D: 1070 if (cmdbuf_len > 0) { 1071 delete_current(); 1072 return; 1073 } 1074 printf("\n"); 1075 edit_eof_cb(edit_cb_ctx); 1076 break; 1077 case EDIT_KEY_CTRL_E: 1078 move_end(); 1079 break; 1080 case EDIT_KEY_CTRL_H: 1081 case EDIT_KEY_BACKSPACE: 1082 delete_left(); 1083 break; 1084 case EDIT_KEY_ENTER: 1085 case EDIT_KEY_CTRL_J: 1086 process_cmd(); 1087 break; 1088 case EDIT_KEY_CTRL_K: 1089 clear_right(); 1090 break; 1091 case EDIT_KEY_CTRL_L: 1092 edit_clear_line(); 1093 edit_redraw(); 1094 break; 1095 case EDIT_KEY_CTRL_R: 1096 search = 1; 1097 search_start(); 1098 break; 1099 case EDIT_KEY_CTRL_U: 1100 clear_left(); 1101 break; 1102 case EDIT_KEY_CTRL_W: 1103 delete_word(); 1104 break; 1105 default: 1106 if (c >= 32 && c <= 255) 1107 insert_char(c); 1108 break; 1109 } 1110 } 1111 1112 1113 int edit_init(void (*cmd_cb)(void *ctx, char *cmd), 1114 void (*eof_cb)(void *ctx), 1115 char ** (*completion_cb)(void *ctx, const char *cmd, int pos), 1116 void *ctx, const char *history_file, const char *ps) 1117 { 1118 currbuf[0] = '\0'; 1119 dl_list_init(&history_list); 1120 history_curr = NULL; 1121 if (history_file) 1122 history_read(history_file); 1123 1124 edit_cb_ctx = ctx; 1125 edit_cmd_cb = cmd_cb; 1126 edit_eof_cb = eof_cb; 1127 edit_completion_cb = completion_cb; 1128 1129 tcgetattr(STDIN_FILENO, &prevt); 1130 newt = prevt; 1131 newt.c_lflag &= ~(ICANON | ECHO); 1132 tcsetattr(STDIN_FILENO, TCSANOW, &newt); 1133 1134 eloop_register_read_sock(STDIN_FILENO, edit_read_char, NULL, NULL); 1135 1136 ps2 = ps; 1137 printf("%s> ", ps2 ? ps2 : ""); 1138 fflush(stdout); 1139 1140 return 0; 1141 } 1142 1143 1144 void edit_deinit(const char *history_file, 1145 int (*filter_cb)(void *ctx, const char *cmd)) 1146 { 1147 struct edit_history *h; 1148 if (history_file) 1149 history_write(history_file, filter_cb); 1150 while ((h = dl_list_first(&history_list, struct edit_history, list))) { 1151 dl_list_del(&h->list); 1152 os_free(h); 1153 } 1154 edit_clear_line(); 1155 putchar('\r'); 1156 fflush(stdout); 1157 eloop_unregister_read_sock(STDIN_FILENO); 1158 tcsetattr(STDIN_FILENO, TCSANOW, &prevt); 1159 } 1160 1161 1162 void edit_redraw(void) 1163 { 1164 char tmp; 1165 cmdbuf[cmdbuf_len] = '\0'; 1166 printf("\r%s> %s", ps2 ? ps2 : "", cmdbuf); 1167 if (cmdbuf_pos != cmdbuf_len) { 1168 tmp = cmdbuf[cmdbuf_pos]; 1169 cmdbuf[cmdbuf_pos] = '\0'; 1170 printf("\r%s> %s", ps2 ? ps2 : "", cmdbuf); 1171 cmdbuf[cmdbuf_pos] = tmp; 1172 } 1173 fflush(stdout); 1174 } 1175