input.c (23016B)
1 2#include "common.h" 3#include "blob.h" 4#include "view.h" 5#include "input.h" 6 7#include <stdlib.h> 8#include <stdio.h> 9#include <string.h> 10#include <errno.h> 11#include <ctype.h> 12#include <setjmp.h> 13 14#include <time.h> 15#include <sys/time.h> 16 17extern jmp_buf jmp_mainloop; /* hyx.c */ 18 19void input_init(struct input *input, struct view *view) 20{ 21 memset(input, 0, sizeof(*input)); 22 input->view = view; 23} 24 25void input_free(struct input *input) 26{ 27 free(input->search.needle); 28} 29 30/* 31 * The C standard forbids assigning arbitrary integers to an enum, 32 * hence we do it the other way round: assign enumerated constants 33 * to a general integer type. 34 */ 35typedef uint16_t key; 36enum { 37 KEY_INTERRUPTED = 0x1000, 38 KEY_SPECIAL_ESCAPE, 39 KEY_SPECIAL_DELETE, 40 KEY_SPECIAL_UP, KEY_SPECIAL_DOWN, KEY_SPECIAL_RIGHT, KEY_SPECIAL_LEFT, 41 KEY_SPECIAL_PGUP, KEY_SPECIAL_PGDOWN, 42 KEY_SPECIAL_HOME, KEY_SPECIAL_END, 43}; 44 45static key getch() 46{ 47 int c; 48 errno = 0; 49 if (EOF == (c = getc(stdin))) { 50 if (errno == EINTR) 51 return KEY_INTERRUPTED; 52 pdie("getc"); 53 } 54 return c; 55} 56 57static void ungetch(int c) 58{ 59 if (c != ungetc(c, stdin)) 60 pdie("ungetc"); 61} 62 63static key get_key() 64{ 65 static const struct itimerval timeout = {{0}, {CONFIG_WAIT_ESCAPE / 1000000, CONFIG_WAIT_ESCAPE % 1000000}}; 66 static const struct itimerval stop = {0}; 67 68 /* FIXME Perhaps it'd be easier to just memorize everything after an escape 69 * key until the timeout hits and parse it once we got the whole sequence? */ 70 static enum { 71 none, 72 discard, 73 have_escape, 74 have_bracket, 75 need_tilde, 76 } state = none; 77 static key r; 78 static uint64_t tick; 79 80 key k; 81 82 /* If we already saw an escape key and we're at the beginning of the 83 * function again, it is likely we were interrupted by the timer. 84 * Check if we've waited long enough for the rest of an escape sequence; 85 * if yes, we either return the escape key or stop discarding input. */ 86 if ((state == have_escape || state == discard) 87 && monotonic_microtime() - tick >= CONFIG_WAIT_ESCAPE) { 88 switch (state) { 89 case have_escape: 90 state = none; 91 r = KEY_SPECIAL_ESCAPE; 92 goto stop_timer; 93 case discard: 94 state = none; 95 if (setitimer(ITIMER_REAL, &stop, NULL)) 96 pdie("setitimer"); 97 break; 98 default: 99 die("unexpected state"); 100 } 101 } 102 103next: 104 105 /* This might be a window size change or a timer interrupt, so we need to 106 * go up to the main loop. The state machine is untouched by this; we 107 * can simply continue where we were as soon as we're called again. */ 108 if ((k = getch()) == KEY_INTERRUPTED) 109 longjmp(jmp_mainloop, 0); 110 111 switch (state) { 112 113 case none: 114 if (k != 0x1b) 115 return k; 116 117 state = have_escape; 118start_timer: 119 tick = monotonic_microtime(); 120 if (setitimer(ITIMER_REAL, &timeout, NULL)) 121 pdie("setitimer"); 122 goto next; 123 124 case discard: 125 goto next; 126 127 case have_escape: 128 if (k != '[') { 129 ungetch(k); 130 state = none; 131 r = KEY_SPECIAL_ESCAPE; 132 goto stop_timer; 133 } 134 state = have_bracket; 135 goto next; 136 137 case have_bracket: 138 switch (k) { 139 case 'A': state = none; r = KEY_SPECIAL_UP; goto stop_timer; 140 case 'B': state = none; r = KEY_SPECIAL_DOWN; goto stop_timer; 141 case 'C': state = none; r = KEY_SPECIAL_RIGHT; goto stop_timer; 142 case 'D': state = none; r = KEY_SPECIAL_LEFT; goto stop_timer; 143 case 'F': state = none; r = KEY_SPECIAL_END; goto stop_timer; 144 case 'H': state = none; r = KEY_SPECIAL_HOME; goto stop_timer; 145 case '3': state = need_tilde; r = KEY_SPECIAL_DELETE; goto next; 146 case '5': state = need_tilde; r = KEY_SPECIAL_PGUP; goto next; 147 case '6': state = need_tilde; r = KEY_SPECIAL_PGDOWN; goto next; 148 case '7': state = need_tilde; r = KEY_SPECIAL_HOME; goto next; 149 case '8': state = need_tilde; r = KEY_SPECIAL_END; goto next; 150 default: 151discard_sequence: 152 /* We don't know this one. Enter discarding state and 153 * wait for all the characters to come in. */ 154 state = discard; 155 goto start_timer; 156 } 157 158 case need_tilde: 159 if (k != '~') 160 goto discard_sequence; 161 state = none; 162stop_timer: 163 setitimer(ITIMER_REAL, &stop, NULL); 164 return r; 165 } 166 167 __builtin_unreachable(); 168} 169 170static void do_reset_soft(struct input *input) 171{ 172 input->low_nibble = 0; 173 input->cur_val = 0; 174} 175 176static void toggle_mode_select(struct input *input) 177{ 178 struct view *V = input->view; 179 struct blob *B = V->blob; 180 181 switch (input->mode) { 182 case INPUT: 183 if (!blob_length(B)) 184 break; 185 input->mode = SELECT; 186 input->sel = (input->cur -= (input->cur >= blob_length(B))); 187 view_dirty_at(V, input->cur); 188 break; 189 case SELECT: 190 input->mode = INPUT; 191 view_dirty_fromto(input->view, input->sel, input->cur + 1); 192 view_dirty_fromto(input->view, input->cur, input->sel + 1); 193 break; 194 } 195} 196 197static size_t cur_bound(struct input const *input) 198{ 199 size_t bound = blob_length(input->view->blob); 200 bound += !bound || (input->mode == INPUT && input->input_mode.insert); 201 assert(bound >= 1); 202 return bound; 203} 204 205static size_t sat_sub_step(size_t x, size_t y, size_t z, size_t _) 206{ 207 (void) _; assert(z); 208 return x >= y * z ? x - y * z : x % z; 209} 210 211static size_t sat_add_step(size_t x, size_t y, size_t z, size_t b) 212{ 213 assert(z); assert(x < b); 214 return x + y * z < b ? x + y * z : b - 1 - (b - 1 - x) % z; 215} 216 217enum cur_move_direction { MOVE_LEFT, MOVE_RIGHT }; 218static void cur_move_rel(struct input *input, enum cur_move_direction dir, size_t off, size_t step) 219{ 220 assert(input->cur <= cur_bound(input)); 221 222 struct view *V = input->view; 223 224 do_reset_soft(input); 225 view_dirty_at(V, input->cur); 226 switch (dir) { 227 case MOVE_LEFT: input->cur = sat_sub_step(input->cur, off, step, cur_bound(input)); break; 228 case MOVE_RIGHT: input->cur = sat_add_step(input->cur, off, step, cur_bound(input)); break; 229 default: die("unexpected direction"); 230 } 231 assert(input->cur < cur_bound(input)); 232 view_dirty_at(V, input->cur); 233 view_adjust(V); 234} 235 236static void cur_adjust(struct input *input) 237{ 238 struct view *V = input->view; 239 240 do_reset_soft(input); 241 if (input->cur >= cur_bound(input)) { 242 view_dirty_at(V, input->cur); 243 input->cur = cur_bound(input) - 1; 244 view_dirty_at(V, input->cur); 245 view_adjust(V); 246 } 247} 248 249static void do_reset_hard(struct input *input) 250{ 251 if (input->mode == SELECT) 252 toggle_mode_select(input); 253 input->input_mode.insert = input->input_mode.ascii = false; 254 cur_adjust(input); 255 view_dirty_at(input->view, input->cur); 256} 257 258static void toggle_mode_insert(struct input *input) 259{ 260 struct view *V = input->view; 261 262 if (!blob_can_move(V->blob)) { 263 view_error(V, "can't insert: file is memory-mapped."); 264 return; 265 } 266 267 if (input->mode != INPUT) 268 return; 269 input->input_mode.insert = !input->input_mode.insert; 270 cur_adjust(input); 271 view_dirty_at(V, input->cur); 272} 273 274static void toggle_mode_ascii(struct input *input) 275{ 276 struct view *V = input->view; 277 278 if (input->mode != INPUT) 279 return; 280 input->input_mode.ascii = !input->input_mode.ascii; 281 view_dirty_at(V, input->cur); 282} 283 284static void do_yank(struct input *input) 285{ 286 switch (input->mode) { 287 case INPUT: 288 input->sel = input->cur; 289 input->mode = SELECT; 290 /* fall-through */ 291 case SELECT: 292 blob_yank(input->view->blob, min(input->sel, input->cur), absdiff(input->sel, input->cur) + 1); 293 toggle_mode_select(input); 294 } 295} 296 297static size_t do_paste(struct input *input) 298{ 299 struct view *V = input->view; 300 struct blob *B = V->blob; 301 size_t retval; 302 303 if (input->mode != INPUT) 304 return 0; 305 view_adjust(input->view); 306 do_reset_soft(input); 307 retval = blob_paste(B, input->cur, input->input_mode.insert ? INSERT : REPLACE); 308 view_recompute(V, false); 309 if (input->input_mode.insert) 310 view_dirty_from(input->view, input->cur); 311 else 312 view_dirty_fromto(input->view, input->cur, input->cur + input->view->blob->clipboard.len); 313 314 return retval; 315} 316 317static bool do_delete(struct input *input, bool back) 318{ 319 struct view *V = input->view; 320 struct blob *B = V->blob; 321 322 if (!blob_can_move(B)) { 323 view_error(V, "can't delete: file is memory-mapped."); 324 return false; 325 } 326 if (!blob_length(B)) 327 return false; 328 329 if (back) { 330 if (!input->cur) 331 return false; 332 cur_move_rel(input, MOVE_LEFT, 1, 1); 333 if (!input->input_mode.insert) 334 return true; 335 } 336 337 switch (input->mode) { 338 case INPUT: 339 input->mode = SELECT; 340 cur_adjust(input); 341 input->sel = input->cur; 342 /* fall-through */ 343 case SELECT: 344 do_reset_soft(input); 345 do_yank(input); 346 if (input->cur > input->sel) { 347 size_t tmp = input->cur; 348 input->cur = input->sel; 349 input->sel = tmp; 350 } 351 blob_delete(B, input->cur, input->sel - input->cur + 1, true); 352 view_recompute(V, false); 353 cur_adjust(input); 354 view_dirty_from(V, input->cur); 355 view_adjust(V); 356 } 357 return true; 358} 359 360static void do_quit(struct input *input, bool *quit, bool force) 361{ 362 struct view *V = input->view; 363 if (force || blob_is_saved(V->blob)) 364 *quit = true; 365 else 366 view_error(V, "unsaved changes! use :q! if you are sure."); 367} 368 369static void do_search_cont(struct input *input, ssize_t dir) 370{ 371 struct view *V = input->view; 372 size_t blen = blob_length(V->blob); 373 374 if (!blen) 375 return; 376 377 size_t cur = dir > 0 ? min(input->cur, blen-1) : input->cur; 378 ssize_t pos = blob_search(V->blob, input->search.needle, input->search.len, (cur + blen + dir) % blen, dir); 379 380 if (pos < 0) 381 return; 382 383 view_dirty_at(V, input->cur); 384 input->cur = pos; 385 view_dirty_at(V, input->cur); 386 view_adjust(V); 387} 388 389static void do_inc_dec(struct input *input, byte diff) 390{ 391 struct view *V = input->view; 392 struct blob *B = V->blob; 393 394 /* should we do anything for selections? */ 395 if (input->mode != INPUT) 396 return; 397 398 if (input->cur >= blob_length(B)) 399 return; 400 401 byte b = blob_at(B, input->cur) + diff; 402 blob_replace(input->view->blob, input->cur, &b, 1, true); 403 view_dirty_at(V, input->cur); 404} 405 406void do_home_end(struct input *input, size_t soft, size_t hard) 407{ 408 assert(soft <= cur_bound(input)); 409 assert(hard <= cur_bound(input)); 410 411 struct view *V = input->view; 412 413 do_reset_soft(input); 414 view_dirty_at(V, input->cur); 415 input->cur = input->cur == soft ? hard : soft; 416 view_dirty_at(V, input->cur); 417 if (input->mode == SELECT) 418 view_dirty_from(V, 0); /* FIXME suboptimal */ 419 view_adjust(V); 420} 421 422void do_pgup_pgdown(struct input *input, size_t (*f)(size_t, size_t, size_t, size_t)) 423{ 424 struct view *V = input->view; 425 426 do_reset_soft(input); 427 input->cur = f(input->cur, V->rows, V->cols, cur_bound(input)); 428 V->start = f(V->start, V->rows, V->cols, cur_bound(input)); 429 view_dirty_from(V, 0); 430 view_adjust(V); 431} 432 433 434void input_cmd(struct input *input, bool *quit); 435void input_search(struct input *input); 436 437void input_get(struct input *input, bool *quit) 438{ 439 key k; 440 byte b; 441 442 struct view *V = input->view; 443 struct blob *B = V->blob; 444 445 k = get_key(); 446 447 if (input->mode == INPUT) { 448 449 if (input->input_mode.ascii && isprint(k)) { 450 451 /* ascii input */ 452 453 if (!blob_length(B)) 454 input->input_mode.insert = true; 455 456 b = k; 457 if (input->input_mode.insert) { 458 blob_insert(B, input->cur, &b, sizeof(b), true); 459 view_recompute(V, false); 460 view_dirty_from(V, input->cur); 461 } 462 else { 463 blob_replace(B, input->cur, &b, sizeof(b), true); 464 view_dirty_at(V, input->cur); 465 } 466 467 cur_move_rel(input, MOVE_RIGHT, 1, 1); 468 return; 469 } 470 471 if ((k >= '0' && k <= '9') || (k >= 'a' && k <= 'f')) { 472 473 /* hex input */ 474 475 if (!blob_length(B)) 476 input->input_mode.insert = true; 477 478 if (input->input_mode.insert) { 479 if (!input->low_nibble) 480 input->cur_val = 0; 481 input->cur_val |= (k > '9' ? k - 'a' + 10 : k - '0') << 4 * (input->low_nibble = !input->low_nibble); 482 if (input->low_nibble) { 483 blob_insert(B, input->cur, &input->cur_val, sizeof(input->cur_val), true); 484 view_recompute(V, false); 485 view_dirty_from(V, input->cur); 486 } 487 else { 488 blob_replace(B, input->cur, &input->cur_val, sizeof(input->cur_val), true); 489 view_dirty_at(V, input->cur); 490 cur_move_rel(input, MOVE_RIGHT, 1, 1); 491 return; 492 } 493 } 494 else { 495 input->cur_val = input->cur < blob_length(B) ? blob_at(B, input->cur) : 0; 496 input->cur_val = input->cur_val & 0xf << 4 * input->low_nibble; 497 input->cur_val |= (k > '9' ? k - 'a' + 10 : k - '0') << 4 * (input->low_nibble = !input->low_nibble); 498 blob_replace(B, input->cur, &input->cur_val, sizeof(input->cur_val), true); 499 view_dirty_at(V, input->cur); 500 501 if (!input->low_nibble) { 502 cur_move_rel(input, MOVE_RIGHT, 1, 1); 503 return; 504 } 505 506 } 507 508 view_adjust(V); 509 return; 510 } 511 512 } 513 514 /* function keys */ 515 516 switch (k) { 517 518 case KEY_SPECIAL_ESCAPE: 519 do_reset_hard(input); 520 break; 521 522 case 0x7f: /* backspace */ 523 do_delete(input, true); 524 break; 525 526 case 'x': 527 case KEY_SPECIAL_DELETE: 528 do_delete(input, false); 529 break; 530 531 case 'q': 532 do_quit(input, quit, false); 533 break; 534 535 case 'v': 536 toggle_mode_select(input); 537 break; 538 539 case 'y': 540 do_yank(input); 541 break; 542 543 case 's': 544 if (input->mode == SELECT && input->cur > input->sel) { 545 size_t tmp = input->sel; 546 input->sel = input->cur; 547 input->cur = tmp; 548 } 549 if (do_delete(input, false) && !input->input_mode.insert) 550 toggle_mode_insert(input); 551 break; 552 553 case 'p': 554 do_paste(input); 555 break; 556 557 case 'P': 558 cur_move_rel(input, MOVE_RIGHT, do_paste(input), 1); 559 break; 560 561 case 'i': 562 toggle_mode_insert(input); 563 break; 564 565 case '\t': 566 toggle_mode_ascii(input); 567 break; 568 569 case 'u': 570 if (input->mode != INPUT) break; 571 if (!blob_undo(B, &input->cur)) 572 break; 573 view_recompute(V, false); 574 cur_adjust(input); 575 view_adjust(V); 576 view_dirty_from(V, 0); /* FIXME suboptimal */ 577 break; 578 579 case 0x12: /* ctrl + R */ 580 if (input->mode != INPUT) break; 581 if (!blob_redo(B, &input->cur)) 582 break; 583 view_recompute(V, false); 584 cur_adjust(input); 585 view_adjust(V); 586 view_dirty_from(V, 0); /* FIXME suboptimal */ 587 break; 588 589 case 0x7: /* ctrl + G */ 590 { 591 char buf[256]; 592 snprintf(buf, sizeof(buf), "\"%s\" %s%s %zd/%zd bytes --%zd%%--", 593 input->view->blob->filename, 594 input->view->blob->alloc == BLOB_MMAP ? "[mmap]" : "", 595 input->view->blob->saved_dist ? "[modified]" : "[saved]", 596 input->cur, 597 blob_length(input->view->blob), 598 ((input->cur+1) * 100) / blob_length(input->view->blob)); 599 view_message(V, buf, NULL); 600 } 601 break; 602 603 case 0xc: /* ctrl + L */ 604 view_dirty_from(V, 0); 605 break; 606 607 case ':': 608 printf("\x1b[%uH", V->rows); /* move to last line */ 609 view_text(V, false); 610 printf(":"); 611 input_cmd(input, quit); 612 view_dirty_from(V, 0); 613 view_visual(V); 614 break; 615 616 case '/': 617 printf("\x1b[%uH", V->rows); /* move to last line */ 618 view_text(V, false); 619 printf("/"); 620 input_search(input); 621 view_dirty_from(V, 0); 622 view_visual(V); 623 break; 624 625 case 'n': 626 do_search_cont(input, +1); 627 break; 628 629 case 'N': 630 do_search_cont(input, -1); 631 break; 632 633 case 0x1: /* ctrl + A */ 634 do_inc_dec(input, 1); 635 break; 636 637 case 0x18: /* ctrl + X */ 638 do_inc_dec(input, -1); 639 break; 640 641 case 'j': 642 case KEY_SPECIAL_DOWN: 643 cur_move_rel(input, MOVE_RIGHT, 1, V->cols); 644 break; 645 646 case 'k': 647 case KEY_SPECIAL_UP: 648 cur_move_rel(input, MOVE_LEFT, 1, V->cols); 649 break; 650 651 case 'l': 652 case KEY_SPECIAL_RIGHT: 653 cur_move_rel(input, MOVE_RIGHT, 1, 1); 654 break; 655 656 case 'h': 657 case KEY_SPECIAL_LEFT: 658 cur_move_rel(input, MOVE_LEFT, 1, 1); 659 break; 660 661 case '^': 662 cur_move_rel(input, MOVE_LEFT, (input->cur - V->start) % V->cols, 1); 663 break; 664 665 case '$': 666 cur_move_rel(input, MOVE_RIGHT, V->cols-1 - (input->cur - V->start) % V->cols, 1); 667 break; 668 669 case 'g': 670 case KEY_SPECIAL_HOME: 671 do_home_end(input, min(V->start, cur_bound(input) - 1), 0); 672 break; 673 674 case 'G': 675 case KEY_SPECIAL_END: 676 do_home_end(input, min(V->start + V->rows * V->cols - 1, cur_bound(input) - 1), cur_bound(input) - 1); 677 break; 678 679 case 0x15: /* ctrl + U */ 680 case KEY_SPECIAL_PGUP: 681 do_pgup_pgdown(input, sat_sub_step); 682 break; 683 684 case 0x4: /* ctrl + D */ 685 case KEY_SPECIAL_PGDOWN: 686 do_pgup_pgdown(input, sat_add_step); 687 break; 688 689 case '[': 690 view_set_cols(V, true, -1); 691 break; 692 693 case ']': 694 view_set_cols(V, true, +1); 695 break; 696 697 } 698} 699 700void input_cmd(struct input *input, bool *quit) 701{ 702 char buf[0x100], *p; 703 unsigned long long n; 704 705 if (!fgets_retry(buf, sizeof(buf), stdin)) 706 pdie("fgets"); 707 708 if ((p = strchr(buf, '\n'))) 709 *p = 0; 710 711 if (!(p = strtok(buf, " "))) 712 return; 713 else if (!strcmp(p, "w") || !strcmp(p, "wq")) { 714 switch (blob_save(input->view->blob, strtok(NULL, " "))) { 715 case BLOB_SAVE_OK: 716 if (!strcmp(p, "wq")) 717 do_quit(input, quit, false); 718 break; 719 case BLOB_SAVE_FILENAME: 720 view_error(input->view, "can't save: no filename."); 721 break; 722 case BLOB_SAVE_NONEXISTENT: 723 view_error(input->view, "can't save: nonexistent path."); 724 break; 725 case BLOB_SAVE_PERMISSIONS: 726 view_error(input->view, "can't save: insufficient permissions."); 727 break; 728 case BLOB_SAVE_BUSY: 729 view_error(input->view, "can't save: file is busy."); 730 break; 731 default: 732 die("can't save: unknown error"); 733 } 734 } 735 else if (!strcmp(p, "q") || !strcmp(p, "q!")) { 736 do_quit(input, quit, !strcmp(p, "q!")); 737 } 738 else if (!strcmp(p, "color")) { 739 if ((p = strtok(NULL, " "))) 740 input->view->color = *p == '1' || *p == 'y'; 741 } 742 else if (!strcmp(p, "columns")) { 743 if ((p = strtok(NULL, " "))) { 744 if (!strcmp(p, "auto")) { 745 view_set_cols(input->view, false, 0); 746 } 747 else { 748 n = strtoull(p, &p, 0); 749 if (!*p) 750 view_set_cols(input->view, false, n); 751 } 752 } 753 } 754 else { 755 /* try to interpret the input as an offset */ 756 n = strtoull(p, &p, 0); 757 if (!*p) { 758 view_dirty_at(input->view, input->cur); 759 if (n < cur_bound(input)) 760 input->cur = n; 761 view_dirty_at(input->view, input->cur); 762 view_adjust(input->view); 763 } 764 } 765} 766 767static unsigned unhex_digit(char c) 768{ 769 assert(isxdigit(c)); 770 if (c >= '0' && c <= '9') 771 return c - '0'; 772 else if (c >= 'a' && c <= 'f') 773 return c - 'a' + 10; 774 else if (c >= 'A' && c <= 'F') 775 return c - 'A' + 10; 776 die("not a hex digit"); 777} 778 779static size_t unhex(byte **ret, char const *hex) 780{ 781 size_t len = 0; 782 *ret = malloc_strict(strlen(hex) / 2); 783 for (char const *p = hex; *p; ) { 784 while (isspace(*p)) ++p; 785 if (!(isxdigit(p[0]) && isxdigit(p[1]))) { 786 free(*ret); 787 *ret = NULL; 788 return 0; 789 } 790 (*ret)[len] = unhex_digit(*p++) << 4; 791 (*ret)[len++] |= unhex_digit(*p++); 792 } 793 *ret = realloc_strict(*ret, len); /* shrink to what we actually used */ 794 return len; 795} 796 797/* NB: this accepts some technically invalid inputs */ 798static size_t utf8_to_ucs2(byte **ret, char const *str) 799{ 800 size_t len = 0; 801 *ret = malloc_strict(2 * strlen(str)); 802 for (uint32_t c, b; (c = *str++); ) { 803 if (!(c & 0x80)) b = 0; 804 else if ((c & 0xe0) == 0xc0) c &= 0x1f, b = 1; 805 else if ((c & 0xf0) == 0xe0) c &= 0x0f, b = 2; 806 else if ((c & 0xf8) == 0xf0) c &= 0x07, b = 3; 807 else { 808bad: 809 free(*ret); 810 *ret = NULL; 811 return 0; 812 } 813 while (b--) { 814 if ((*str & 0xc0) != 0x80) goto bad; 815 c <<= 6, c |= (*str++ & 0x3f); 816 } 817 if (c >> 16) goto bad; /* not representable */ 818 (*ret)[len++] = c >> 0; 819 (*ret)[len++] = c >> 8; 820 } 821 *ret = realloc_strict(*ret, len); /* shrink to what we actually used */ 822 return len; 823} 824 825void input_search(struct input *input) 826{ 827 char buf[0x100], *p, *q; 828 829 if (!fgets_retry(buf, sizeof(buf), stdin)) 830 pdie("fgets"); 831 832 if ((p = strchr(buf, '\n'))) 833 *p = 0; 834 835 input->search.len = 0; 836 free(input->search.needle); 837 input->search.needle = NULL; 838 839 if (!(p = strtok(buf, " "))) 840 return; 841 else if (!strcmp(p, "x") || !strcmp(p, "w")) { 842 size_t (*fun)(byte **, char const *) = (*p == 'x') ? unhex : utf8_to_ucs2; 843 if (!(q = strtok(NULL, " "))) { 844 q = p; 845 goto str; 846 } 847 input->search.len = fun(&input->search.needle, q); 848 } 849 else if (!strcmp(p, "s")) { 850 if (!(q = strtok(NULL, ""))) 851 q = p; 852str: 853 input->search.len = strlen(q); 854 input->search.needle = (byte *) strdup(q); 855 } 856 else if (!(input->search.len = unhex(&input->search.needle, p))) { 857 q = p; 858 goto str; 859 } 860 861 do_search_cont(input, +1); 862} 863