1/* 2 * zle_move.c - editor movement 3 * 4 * This file is part of zsh, the Z shell. 5 * 6 * Copyright (c) 1992-1997 Paul Falstad 7 * All rights reserved. 8 * 9 * Permission is hereby granted, without written agreement and without 10 * license or royalty fees, to use, copy, modify, and distribute this 11 * software and to distribute modified versions of this software for any 12 * purpose, provided that the above copyright notice and the following 13 * two paragraphs appear in all copies of this software. 14 * 15 * In no event shall Paul Falstad or the Zsh Development Group be liable 16 * to any party for direct, indirect, special, incidental, or consequential 17 * damages arising out of the use of this software and its documentation, 18 * even if Paul Falstad and the Zsh Development Group have been advised of 19 * the possibility of such damage. 20 * 21 * Paul Falstad and the Zsh Development Group specifically disclaim any 22 * warranties, including, but not limited to, the implied warranties of 23 * merchantability and fitness for a particular purpose. The software 24 * provided hereunder is on an "as is" basis, and Paul Falstad and the 25 * Zsh Development Group have no obligation to provide maintenance, 26 * support, updates, enhancements, or modifications. 27 * 28 */ 29 30#include "zle.mdh" 31#include "zle_move.pro" 32 33static int vimarkcs[27], vimarkline[27]; 34 35#ifdef MULTIBYTE_SUPPORT 36/* 37 * Take account of combining characters when moving left. If 38 * we are on a zero-width printable wide character and are 39 * treating these as part of the base character for display purposes, 40 * move left until we reach a non-zero-width printable character 41 * (the base character). If we reach something else first, stay where we 42 * were. 43 * 44 * If setpos is non-zero, update zlecs on success. 45 * Return 1 if we were on a combining char and could move, else 0. 46 */ 47/**/ 48int 49alignmultiwordleft(int *pos, int setpos) 50{ 51 int loccs = *pos; 52 53 /* generic nothing to do test */ 54 if (!isset(COMBININGCHARS) || loccs == zlell || loccs == 0) 55 return 0; 56 57 /* need to be on combining character */ 58 if (!IS_COMBINING(zleline[loccs])) 59 return 0; 60 61 /* yes, go left */ 62 loccs--; 63 64 for (;;) { 65 if (IS_BASECHAR(zleline[loccs])) { 66 /* found start position */ 67 if (setpos) 68 *pos = loccs; 69 return 1; 70 } else if (!IS_COMBINING(zleline[loccs])) { 71 /* no go */ 72 return 0; 73 } 74 /* combining char, keep going */ 75 if (loccs-- == 0) 76 return 0; 77 } 78} 79 80 81/* 82 * Same principle when moving right. We need to check if 83 * alignmultiwordleft() would be successful in order to decide 84 * if we're on a combining character, and if so we move right to 85 * anything that isn't one. 86 */ 87/**/ 88int 89alignmultiwordright(int *pos, int setpos) 90{ 91 int loccs; 92 93 /* 94 * Are we on a suitable character? 95 */ 96 if (!alignmultiwordleft(pos, 0)) 97 return 0; 98 99 /* yes, go right */ 100 loccs = *pos + 1; 101 102 while (loccs < zlell) { 103 /* Anything other than a combining char will do here */ 104 if (!IS_COMBINING(zleline[loccs])) { 105 if (setpos) 106 *pos = loccs; 107 return 1; 108 } 109 loccs++; 110 } 111 112 if (setpos) 113 *pos = loccs; 114 return 1; 115} 116 117 118/* Move cursor right, checking for combining characters */ 119 120/**/ 121mod_export void 122inccs(void) 123{ 124 zlecs++; 125 alignmultiwordright(&zlecs, 1); 126} 127 128 129/* Move cursor left, checking for combining characters */ 130 131/**/ 132mod_export void 133deccs(void) 134{ 135 zlecs--; 136 alignmultiwordleft(&zlecs, 1); 137} 138 139/* Same utilities for general position */ 140 141/**/ 142mod_export void 143incpos(int *pos) 144{ 145 (*pos)++; 146 alignmultiwordright(pos, 1); 147} 148 149 150/**/ 151mod_export void 152decpos(int *pos) 153{ 154 (*pos)--; 155 alignmultiwordleft(pos, 1); 156} 157#endif 158 159 160/* Size of buffer in the following function */ 161#define BMC_BUFSIZE MB_CUR_MAX 162/* 163 * For a metafied string that starts at "start" and where the 164 * current position is "ptr", go back one full character, 165 * taking account of combining characters if necessary. 166 */ 167 168/**/ 169char * 170backwardmetafiedchar(char *start, char *endptr, convchar_t *retchr) 171{ 172#ifdef MULTIBYTE_SUPPORT 173 int charlen = 0; 174 char *last = NULL, *bufptr, *ptr = endptr; 175 convchar_t lastc = (convchar_t)0; /* not used, silence compiler */ 176 mbstate_t mbs; 177 size_t ret; 178 wchar_t wc; 179 VARARR(char, buf, BMC_BUFSIZE); 180 181 bufptr = buf + BMC_BUFSIZE; 182 while (ptr > start) { 183 ptr--; 184 /* 185 * Scanning backwards we're not guaranteed ever to find a 186 * valid character. If we've looked as far as we should 187 * need to, give up. 188 */ 189 if (bufptr-- == buf) 190 break; 191 charlen++; 192 if (ptr > start && ptr[-1] == Meta) 193 *bufptr = *ptr-- ^ 32; 194 else 195 *bufptr = *ptr; 196 197 /* we always need to restart the character from scratch */ 198 memset(&mbs, 0, sizeof(mbs)); 199 ret = mbrtowc(&wc, bufptr, charlen, &mbs); 200 if (ret == 0) { 201 /* NULL: unlikely, but handle anyway. */ 202 if (last) { 203 if (retchr) 204 *retchr = lastc; 205 return last; 206 } else { 207 if (retchr) 208 *retchr = wc; 209 return ptr; 210 } 211 } 212 if (ret != (size_t)-1) { 213 if (ret < (size_t)charlen) { 214 /* The last character didn't convert, so use it raw. */ 215 break; 216 } 217 if (!isset(COMBININGCHARS)) { 218 if (retchr) 219 *retchr = wc; 220 return ptr; 221 } 222 if (!IS_COMBINING(wc)) { 223 /* not a combining character... */ 224 if (last) { 225 /* 226 * ... but we were looking for a suitable base character, 227 * test it. 228 */ 229 if (IS_BASECHAR(wc)) { 230 /* 231 * Yes, this will do. 232 */ 233 if (retchr) 234 *retchr = wc; 235 return ptr; 236 } else { 237 /* No, just return the first character we found */ 238 if (retchr) 239 *retchr = lastc; 240 return last; 241 } 242 } 243 /* This is the first character, so just return it. */ 244 if (retchr) 245 *retchr = wc; 246 return ptr; 247 } 248 if (!last) { 249 /* still looking for the character immediately before ptr */ 250 last = ptr; 251 lastc = wc; 252 } 253 /* searching for base character of combining character */ 254 charlen = 0; 255 bufptr = buf + BMC_BUFSIZE; 256 } 257 /* 258 * Else keep scanning this character even if MB_INVALID: we can't 259 * expect MB_INCOMPLETE to work when moving backwards. 260 */ 261 } 262 /* 263 * Found something we didn't like, was there a good character 264 * immediately before ptr? 265 */ 266 if (last) { 267 if (retchr) 268 *retchr = lastc; 269 return last; 270 } 271 /* 272 * No, we couldn't find any good character, so just treat 273 * the last unmetafied byte we found as a character. 274 */ 275#endif 276 if (endptr > start) { 277 if (endptr > start - 1 && endptr[-2] == Meta) 278 { 279 if (retchr) 280 *retchr = (convchar_t)(endptr[-1] ^ 32); 281 return endptr - 2; 282 } 283 else 284 { 285 if (retchr) 286 *retchr = (convchar_t)endptr[-1]; 287 return endptr - 1; 288 } 289 } 290 if (retchr) 291 *retchr = (convchar_t)0; 292 return endptr; 293} 294 295 296/**/ 297int 298beginningofline(char **args) 299{ 300 int n = zmult; 301 302 if (n < 0) { 303 int ret; 304 zmult = -n; 305 ret = endofline(args); 306 zmult = n; 307 return ret; 308 } 309 while (n--) { 310 int pos; 311 312 if (zlecs == 0) 313 return 0; 314 pos = zlecs; 315 DECPOS(pos); 316 if (zleline[pos] == '\n') { 317 zlecs = pos; 318 if (!zlecs) 319 return 0; 320 } 321 322 /* works OK with combining chars since '\n' must be on its own */ 323 while (zlecs && zleline[zlecs - 1] != '\n') 324 zlecs--; 325 } 326 return 0; 327} 328 329/**/ 330int 331endofline(char **args) 332{ 333 int n = zmult; 334 335 if (n < 0) { 336 int ret; 337 zmult = -n; 338 ret = beginningofline(args); 339 zmult = n; 340 return ret; 341 } 342 while (n--) { 343 if (zlecs >= zlell) { 344 zlecs = zlell; 345 return 0; 346 } 347 if (zleline[zlecs] == '\n') 348 if (++zlecs == zlell) 349 return 0; 350 while (zlecs != zlell && zleline[zlecs] != '\n') 351 zlecs++; 352 } 353 return 0; 354} 355 356/**/ 357int 358beginningoflinehist(char **args) 359{ 360 int n = zmult; 361 362 if (n < 0) { 363 int ret; 364 zmult = -n; 365 ret = endoflinehist(args); 366 zmult = n; 367 return ret; 368 } 369 while (n) { 370 int pos; 371 372 if (zlecs == 0) 373 break; 374 pos = zlecs; 375 DECPOS(pos); 376 if (zleline[pos] == '\n') { 377 zlecs = pos; 378 if (!pos) 379 break; 380 } 381 382 /* works OK with combining chars since '\n' must be on its own */ 383 while (zlecs && zleline[zlecs - 1] != '\n') 384 zlecs--; 385 n--; 386 } 387 if (n) { 388 int m = zmult, ret; 389 390 zmult = n; 391 ret = uphistory(args); 392 zmult = m; 393 zlecs = 0; 394 return ret; 395 } 396 return 0; 397} 398 399/**/ 400int 401endoflinehist(char **args) 402{ 403 int n = zmult; 404 405 if (n < 0) { 406 int ret; 407 zmult = -n; 408 ret = beginningoflinehist(args); 409 zmult = n; 410 return ret; 411 } 412 while (n) { 413 if (zlecs >= zlell) { 414 zlecs = zlell; 415 break; 416 } 417 if (zleline[zlecs] == '\n') 418 if (++zlecs == zlell) 419 break; 420 while (zlecs != zlell && zleline[zlecs] != '\n') 421 zlecs++; 422 n--; 423 } 424 if (n) { 425 int m = zmult, ret; 426 427 zmult = n; 428 ret = downhistory(args); 429 zmult = m; 430 return ret; 431 } 432 return 0; 433} 434 435/**/ 436int 437forwardchar(char **args) 438{ 439 int n = zmult; 440 441 if (n < 0) { 442 int ret; 443 zmult = -n; 444 ret = backwardchar(args); 445 zmult = n; 446 return ret; 447 } 448 449 /* 450 * If handling combining characters with the base character, 451 * we skip over the whole set in one go, so need to check. 452 */ 453 while (zlecs < zlell && n--) 454 INCCS(); 455 return 0; 456} 457 458/**/ 459int 460backwardchar(char **args) 461{ 462 int n = zmult; 463 464 if (n < 0) { 465 int ret; 466 zmult = -n; 467 ret = forwardchar(args); 468 zmult = n; 469 return ret; 470 } 471 472 while (zlecs > 0 && n--) 473 DECCS(); 474 return 0; 475} 476 477/**/ 478int 479setmarkcommand(UNUSED(char **args)) 480{ 481 if (zmult < 0) { 482 region_active = 0; 483 return 0; 484 } 485 mark = zlecs; 486 region_active = 1; 487 return 0; 488} 489 490/**/ 491int 492exchangepointandmark(UNUSED(char **args)) 493{ 494 int x; 495 496 if (zmult == 0) { 497 region_active = 1; 498 return 0; 499 } 500 x = mark; 501 mark = zlecs; 502 zlecs = x; 503 if (zlecs > zlell) 504 zlecs = zlell; 505 if (zmult > 0) 506 region_active = 1; 507 return 0; 508} 509 510/**/ 511int 512vigotocolumn(UNUSED(char **args)) 513{ 514 int x, y, n = zmult; 515 516 findline(&x, &y); 517 if (n >= 0) { 518 if (n) 519 n--; 520 zlecs = x; 521 while (zlecs < y && n--) 522 INCCS(); 523 } else { 524 zlecs = y; 525 n = -n; 526 while (zlecs > x && n--) 527 DECCS(); 528 } 529 return 0; 530} 531 532/**/ 533int 534vimatchbracket(UNUSED(char **args)) 535{ 536 int ocs = zlecs, dir, ct; 537 unsigned char oth, me; 538 539 if ((zlecs == zlell || zleline[zlecs] == '\n') && zlecs > 0) 540 DECCS(); 541 542 otog: 543 if (zlecs == zlell || zleline[zlecs] == '\n') { 544 zlecs = ocs; 545 return 1; 546 } 547 switch (me = zleline[zlecs]) { 548 case '{': 549 dir = 1; 550 oth = '}'; 551 break; 552 case /*{*/ '}': 553 virangeflag = -virangeflag; 554 dir = -1; 555 oth = '{'; /*}*/ 556 break; 557 case '(': 558 dir = 1; 559 oth = ')'; 560 break; 561 case ')': 562 virangeflag = -virangeflag; 563 dir = -1; 564 oth = '('; 565 break; 566 case '[': 567 dir = 1; 568 oth = ']'; 569 break; 570 case ']': 571 virangeflag = -virangeflag; 572 dir = -1; 573 oth = '['; 574 break; 575 default: 576 INCCS(); 577 goto otog; 578 } 579 ct = 1; 580 while (zlecs >= 0 && zlecs < zlell && ct) { 581 if (dir < 0) 582 DECCS(); 583 else 584 INCCS(); 585 if (zleline[zlecs] == oth) 586 ct--; 587 else if (zleline[zlecs] == me) 588 ct++; 589 } 590 if (zlecs < 0 || zlecs >= zlell) { 591 zlecs = ocs; 592 return 1; 593 } else if(dir > 0 && virangeflag) 594 INCCS(); 595 return 0; 596} 597 598/**/ 599int 600viforwardchar(char **args) 601{ 602 int lim = findeol() - invicmdmode(); 603 int n = zmult; 604 605 if (n < 0) { 606 int ret; 607 zmult = -n; 608 ret = vibackwardchar(args); 609 zmult = n; 610 return ret; 611 } 612 if (zlecs >= lim) 613 return 1; 614 while (n-- && zlecs < lim) 615 INCCS(); 616 return 0; 617} 618 619/**/ 620int 621vibackwardchar(char **args) 622{ 623 int n = zmult; 624 625 if (n < 0) { 626 int ret; 627 zmult = -n; 628 ret = viforwardchar(args); 629 zmult = n; 630 return ret; 631 } 632 if (zlecs == findbol()) 633 return 1; 634 while (n-- && zlecs > 0) { 635 DECCS(); 636 if (zleline[zlecs] == '\n') { 637 zlecs++; 638 break; 639 } 640 } 641 return 0; 642} 643 644/**/ 645int 646viendofline(UNUSED(char **args)) 647{ 648 int oldcs = zlecs, n = zmult; 649 650 if (n < 1) 651 return 1; 652 while(n--) { 653 if (zlecs > zlell) { 654 zlecs = oldcs; 655 return 1; 656 } 657 zlecs = findeol() + 1; 658 } 659 DECCS(); 660 lastcol = 1<<30; 661 return 0; 662} 663 664/**/ 665int 666vibeginningofline(UNUSED(char **args)) 667{ 668 zlecs = findbol(); 669 return 0; 670} 671 672static ZLE_INT_T vfindchar; 673static int vfinddir, tailadd; 674 675/**/ 676int 677vifindnextchar(char **args) 678{ 679 if ((vfindchar = vigetkey()) != ZLEEOF) { 680 vfinddir = 1; 681 tailadd = 0; 682 return vifindchar(0, args); 683 } 684 return 1; 685} 686 687/**/ 688int 689vifindprevchar(char **args) 690{ 691 if ((vfindchar = vigetkey()) != ZLEEOF) { 692 vfinddir = -1; 693 tailadd = 0; 694 return vifindchar(0, args); 695 } 696 return 1; 697} 698 699/**/ 700int 701vifindnextcharskip(char **args) 702{ 703 if ((vfindchar = vigetkey()) != ZLEEOF) { 704 vfinddir = 1; 705 tailadd = -1; 706 return vifindchar(0, args); 707 } 708 return 1; 709} 710 711/**/ 712int 713vifindprevcharskip(char **args) 714{ 715 if ((vfindchar = vigetkey()) != ZLEEOF) { 716 vfinddir = -1; 717 tailadd = 1; 718 return vifindchar(0, args); 719 } 720 return 1; 721} 722 723/**/ 724int 725vifindchar(int repeat, char **args) 726{ 727 int ocs = zlecs, n = zmult; 728 729 if (!vfinddir) 730 return 1; 731 if (n < 0) { 732 int ret; 733 zmult = -n; 734 ret = virevrepeatfind(args); 735 zmult = n; 736 return ret; 737 } 738 if (repeat && tailadd != 0) { 739 if (vfinddir > 0) { 740 if(zlecs < zlell && (ZLE_INT_T)zleline[zlecs+1] == vfindchar) 741 INCCS(); 742 } 743 else { 744 if(zlecs > 0 && (ZLE_INT_T)zleline[zlecs-1] == vfindchar) 745 DECCS(); 746 } 747 } 748 while (n--) { 749 do { 750 if (vfinddir > 0) 751 INCCS(); 752 else 753 DECCS(); 754 } while (zlecs >= 0 && zlecs < zlell 755 && (ZLE_INT_T)zleline[zlecs] != vfindchar 756 && zleline[zlecs] != ZWC('\n')); 757 if (zlecs < 0 || zlecs >= zlell || zleline[zlecs] == ZWC('\n')) { 758 zlecs = ocs; 759 return 1; 760 } 761 } 762 if (tailadd > 0) 763 INCCS(); 764 else if (tailadd < 0) 765 DECCS(); 766 if (vfinddir == 1 && virangeflag) 767 INCCS(); 768 return 0; 769} 770 771/**/ 772int 773virepeatfind(char **args) 774{ 775 return vifindchar(1, args); 776} 777 778/**/ 779int 780virevrepeatfind(char **args) 781{ 782 int ret; 783 784 if (zmult < 0) { 785 zmult = -zmult; 786 ret = vifindchar(1, args); 787 zmult = -zmult; 788 return ret; 789 } 790 tailadd = -tailadd; 791 vfinddir = -vfinddir; 792 ret = vifindchar(1, args); 793 vfinddir = -vfinddir; 794 tailadd = -tailadd; 795 return ret; 796} 797 798/**/ 799int 800vifirstnonblank(UNUSED(char **args)) 801{ 802 zlecs = findbol(); 803 while (zlecs != zlell && ZC_iblank(zleline[zlecs])) 804 INCCS(); 805 return 0; 806} 807 808/**/ 809int 810visetmark(UNUSED(char **args)) 811{ 812 ZLE_INT_T ch; 813 814 ch = getfullchar(0); 815 if (ch < ZWC('a') || ch > ZWC('z')) 816 return 1; 817 ch -= ZWC('a'); 818 vimarkcs[ch] = zlecs; 819 vimarkline[ch] = histline; 820 return 0; 821} 822 823/**/ 824int 825vigotomark(UNUSED(char **args)) 826{ 827 ZLE_INT_T ch; 828 int oldcs = zlecs; 829 int oldline = histline; 830 831 ch = getfullchar(0); 832 if (ch == ZWC('\'') || ch == ZWC('`')) 833 ch = 26; 834 else { 835 if (ch < ZWC('a') || ch > ZWC('z')) 836 return 1; 837 ch -= ZWC('a'); 838 } 839 if (!vimarkline[ch]) 840 return 1; 841 if (curhist != vimarkline[ch] && !zle_goto_hist(vimarkline[ch], 0, 0)) { 842 vimarkline[ch] = 0; 843 return 1; 844 } 845 zlecs = vimarkcs[ch]; 846 vimarkcs[26] = oldcs; 847 vimarkline[26] = oldline; 848 if (zlecs > zlell) 849 zlecs = zlell; 850 return 0; 851} 852 853/**/ 854int 855vigotomarkline(char **args) 856{ 857 vigotomark(args); 858 return vifirstnonblank(zlenoargs); 859} 860