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