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