1/* $NetBSD: chared.c,v 1.49 2016/02/24 14:29:21 christos Exp $ */ 2 3/*- 4 * Copyright (c) 1992, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Christos Zoulas of Cornell University. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35#include "config.h" 36#if !defined(lint) && !defined(SCCSID) 37#if 0 38static char sccsid[] = "@(#)chared.c 8.1 (Berkeley) 6/4/93"; 39#else 40__RCSID("$NetBSD: chared.c,v 1.49 2016/02/24 14:29:21 christos Exp $"); 41#endif 42#endif /* not lint && not SCCSID */ 43#include <sys/cdefs.h> 44__FBSDID("$FreeBSD: stable/11/lib/libedit/chared.c 313981 2017-02-20 03:33:59Z pfg $"); 45 46/* 47 * chared.c: Character editor utilities 48 */ 49#include <ctype.h> 50#include <stdlib.h> 51#include <string.h> 52 53#include "el.h" 54#include "common.h" 55 56private void ch__clearmacro (EditLine *); 57 58/* value to leave unused in line buffer */ 59#define EL_LEAVE 2 60 61/* cv_undo(): 62 * Handle state for the vi undo command 63 */ 64protected void 65cv_undo(EditLine *el) 66{ 67 c_undo_t *vu = &el->el_chared.c_undo; 68 c_redo_t *r = &el->el_chared.c_redo; 69 size_t size; 70 71 /* Save entire line for undo */ 72 size = (size_t)(el->el_line.lastchar - el->el_line.buffer); 73 vu->len = (ssize_t)size; 74 vu->cursor = (int)(el->el_line.cursor - el->el_line.buffer); 75 (void)memcpy(vu->buf, el->el_line.buffer, size * sizeof(*vu->buf)); 76 77 /* save command info for redo */ 78 r->count = el->el_state.doingarg ? el->el_state.argument : 0; 79 r->action = el->el_chared.c_vcmd.action; 80 r->pos = r->buf; 81 r->cmd = el->el_state.thiscmd; 82 r->ch = el->el_state.thisch; 83} 84 85/* cv_yank(): 86 * Save yank/delete data for paste 87 */ 88protected void 89cv_yank(EditLine *el, const Char *ptr, int size) 90{ 91 c_kill_t *k = &el->el_chared.c_kill; 92 93 (void)memcpy(k->buf, ptr, (size_t)size * sizeof(*k->buf)); 94 k->last = k->buf + size; 95} 96 97 98/* c_insert(): 99 * Insert num characters 100 */ 101protected void 102c_insert(EditLine *el, int num) 103{ 104 Char *cp; 105 106 if (el->el_line.lastchar + num >= el->el_line.limit) { 107 if (!ch_enlargebufs(el, (size_t)num)) 108 return; /* can't go past end of buffer */ 109 } 110 111 if (el->el_line.cursor < el->el_line.lastchar) { 112 /* if I must move chars */ 113 for (cp = el->el_line.lastchar; cp >= el->el_line.cursor; cp--) 114 cp[num] = *cp; 115 } 116 el->el_line.lastchar += num; 117} 118 119 120/* c_delafter(): 121 * Delete num characters after the cursor 122 */ 123protected void 124c_delafter(EditLine *el, int num) 125{ 126 127 if (el->el_line.cursor + num > el->el_line.lastchar) 128 num = (int)(el->el_line.lastchar - el->el_line.cursor); 129 130 if (el->el_map.current != el->el_map.emacs) { 131 cv_undo(el); 132 cv_yank(el, el->el_line.cursor, num); 133 } 134 135 if (num > 0) { 136 Char *cp; 137 138 for (cp = el->el_line.cursor; cp <= el->el_line.lastchar; cp++) 139 *cp = cp[num]; 140 141 el->el_line.lastchar -= num; 142 } 143} 144 145 146/* c_delafter1(): 147 * Delete the character after the cursor, do not yank 148 */ 149protected void 150c_delafter1(EditLine *el) 151{ 152 Char *cp; 153 154 for (cp = el->el_line.cursor; cp <= el->el_line.lastchar; cp++) 155 *cp = cp[1]; 156 157 el->el_line.lastchar--; 158} 159 160 161/* c_delbefore(): 162 * Delete num characters before the cursor 163 */ 164protected void 165c_delbefore(EditLine *el, int num) 166{ 167 168 if (el->el_line.cursor - num < el->el_line.buffer) 169 num = (int)(el->el_line.cursor - el->el_line.buffer); 170 171 if (el->el_map.current != el->el_map.emacs) { 172 cv_undo(el); 173 cv_yank(el, el->el_line.cursor - num, num); 174 } 175 176 if (num > 0) { 177 Char *cp; 178 179 for (cp = el->el_line.cursor - num; 180 cp <= el->el_line.lastchar; 181 cp++) 182 *cp = cp[num]; 183 184 el->el_line.lastchar -= num; 185 } 186} 187 188 189/* c_delbefore1(): 190 * Delete the character before the cursor, do not yank 191 */ 192protected void 193c_delbefore1(EditLine *el) 194{ 195 Char *cp; 196 197 for (cp = el->el_line.cursor - 1; cp <= el->el_line.lastchar; cp++) 198 *cp = cp[1]; 199 200 el->el_line.lastchar--; 201} 202 203 204/* ce__isword(): 205 * Return if p is part of a word according to emacs 206 */ 207protected int 208ce__isword(wint_t p) 209{ 210 return Isalnum(p) || Strchr(STR("*?_-.[]~="), p) != NULL; 211} 212 213 214/* cv__isword(): 215 * Return if p is part of a word according to vi 216 */ 217protected int 218cv__isword(wint_t p) 219{ 220 if (Isalnum(p) || p == '_') 221 return 1; 222 if (Isgraph(p)) 223 return 2; 224 return 0; 225} 226 227 228/* cv__isWord(): 229 * Return if p is part of a big word according to vi 230 */ 231protected int 232cv__isWord(wint_t p) 233{ 234 return !Isspace(p); 235} 236 237 238/* c__prev_word(): 239 * Find the previous word 240 */ 241protected Char * 242c__prev_word(Char *p, Char *low, int n, int (*wtest)(wint_t)) 243{ 244 p--; 245 246 while (n--) { 247 while ((p >= low) && !(*wtest)(*p)) 248 p--; 249 while ((p >= low) && (*wtest)(*p)) 250 p--; 251 } 252 253 /* cp now points to one character before the word */ 254 p++; 255 if (p < low) 256 p = low; 257 /* cp now points where we want it */ 258 return p; 259} 260 261 262/* c__next_word(): 263 * Find the next word 264 */ 265protected Char * 266c__next_word(Char *p, Char *high, int n, int (*wtest)(wint_t)) 267{ 268 while (n--) { 269 while ((p < high) && !(*wtest)(*p)) 270 p++; 271 while ((p < high) && (*wtest)(*p)) 272 p++; 273 } 274 if (p > high) 275 p = high; 276 /* p now points where we want it */ 277 return p; 278} 279 280/* cv_next_word(): 281 * Find the next word vi style 282 */ 283protected Char * 284cv_next_word(EditLine *el, Char *p, Char *high, int n, int (*wtest)(wint_t)) 285{ 286 int test; 287 288 while (n--) { 289 test = (*wtest)(*p); 290 while ((p < high) && (*wtest)(*p) == test) 291 p++; 292 /* 293 * vi historically deletes with cw only the word preserving the 294 * trailing whitespace! This is not what 'w' does.. 295 */ 296 if (n || el->el_chared.c_vcmd.action != (DELETE|INSERT)) 297 while ((p < high) && Isspace(*p)) 298 p++; 299 } 300 301 /* p now points where we want it */ 302 if (p > high) 303 return high; 304 else 305 return p; 306} 307 308 309/* cv_prev_word(): 310 * Find the previous word vi style 311 */ 312protected Char * 313cv_prev_word(Char *p, Char *low, int n, int (*wtest)(wint_t)) 314{ 315 int test; 316 317 p--; 318 while (n--) { 319 while ((p > low) && Isspace(*p)) 320 p--; 321 test = (*wtest)(*p); 322 while ((p >= low) && (*wtest)(*p) == test) 323 p--; 324 } 325 p++; 326 327 /* p now points where we want it */ 328 if (p < low) 329 return low; 330 else 331 return p; 332} 333 334 335/* cv_delfini(): 336 * Finish vi delete action 337 */ 338protected void 339cv_delfini(EditLine *el) 340{ 341 int size; 342 int action = el->el_chared.c_vcmd.action; 343 344 if (action & INSERT) 345 el->el_map.current = el->el_map.key; 346 347 if (el->el_chared.c_vcmd.pos == 0) 348 /* sanity */ 349 return; 350 351 size = (int)(el->el_line.cursor - el->el_chared.c_vcmd.pos); 352 if (size == 0) 353 size = 1; 354 el->el_line.cursor = el->el_chared.c_vcmd.pos; 355 if (action & YANK) { 356 if (size > 0) 357 cv_yank(el, el->el_line.cursor, size); 358 else 359 cv_yank(el, el->el_line.cursor + size, -size); 360 } else { 361 if (size > 0) { 362 c_delafter(el, size); 363 re_refresh_cursor(el); 364 } else { 365 c_delbefore(el, -size); 366 el->el_line.cursor += size; 367 } 368 } 369 el->el_chared.c_vcmd.action = NOP; 370} 371 372 373/* cv__endword(): 374 * Go to the end of this word according to vi 375 */ 376protected Char * 377cv__endword(Char *p, Char *high, int n, int (*wtest)(wint_t)) 378{ 379 int test; 380 381 p++; 382 383 while (n--) { 384 while ((p < high) && Isspace(*p)) 385 p++; 386 387 test = (*wtest)(*p); 388 while ((p < high) && (*wtest)(*p) == test) 389 p++; 390 } 391 p--; 392 return p; 393} 394 395/* ch_init(): 396 * Initialize the character editor 397 */ 398protected int 399ch_init(EditLine *el) 400{ 401 c_macro_t *ma = &el->el_chared.c_macro; 402 403 el->el_line.buffer = el_malloc(EL_BUFSIZ * 404 sizeof(*el->el_line.buffer)); 405 if (el->el_line.buffer == NULL) 406 return -1; 407 408 (void) memset(el->el_line.buffer, 0, EL_BUFSIZ * 409 sizeof(*el->el_line.buffer)); 410 el->el_line.cursor = el->el_line.buffer; 411 el->el_line.lastchar = el->el_line.buffer; 412 el->el_line.limit = &el->el_line.buffer[EL_BUFSIZ - EL_LEAVE]; 413 414 el->el_chared.c_undo.buf = el_malloc(EL_BUFSIZ * 415 sizeof(*el->el_chared.c_undo.buf)); 416 if (el->el_chared.c_undo.buf == NULL) 417 return -1; 418 (void) memset(el->el_chared.c_undo.buf, 0, EL_BUFSIZ * 419 sizeof(*el->el_chared.c_undo.buf)); 420 el->el_chared.c_undo.len = -1; 421 el->el_chared.c_undo.cursor = 0; 422 el->el_chared.c_redo.buf = el_malloc(EL_BUFSIZ * 423 sizeof(*el->el_chared.c_redo.buf)); 424 if (el->el_chared.c_redo.buf == NULL) 425 return -1; 426 el->el_chared.c_redo.pos = el->el_chared.c_redo.buf; 427 el->el_chared.c_redo.lim = el->el_chared.c_redo.buf + EL_BUFSIZ; 428 el->el_chared.c_redo.cmd = ED_UNASSIGNED; 429 430 el->el_chared.c_vcmd.action = NOP; 431 el->el_chared.c_vcmd.pos = el->el_line.buffer; 432 433 el->el_chared.c_kill.buf = el_malloc(EL_BUFSIZ * 434 sizeof(*el->el_chared.c_kill.buf)); 435 if (el->el_chared.c_kill.buf == NULL) 436 return -1; 437 (void) memset(el->el_chared.c_kill.buf, 0, EL_BUFSIZ * 438 sizeof(*el->el_chared.c_kill.buf)); 439 el->el_chared.c_kill.mark = el->el_line.buffer; 440 el->el_chared.c_kill.last = el->el_chared.c_kill.buf; 441 el->el_chared.c_resizefun = NULL; 442 el->el_chared.c_resizearg = NULL; 443 el->el_chared.c_aliasfun = NULL; 444 el->el_chared.c_aliasarg = NULL; 445 446 el->el_map.current = el->el_map.key; 447 448 el->el_state.inputmode = MODE_INSERT; /* XXX: save a default */ 449 el->el_state.doingarg = 0; 450 el->el_state.metanext = 0; 451 el->el_state.argument = 1; 452 el->el_state.lastcmd = ED_UNASSIGNED; 453 454 ma->level = -1; 455 ma->offset = 0; 456 ma->macro = el_malloc(EL_MAXMACRO * sizeof(*ma->macro)); 457 if (ma->macro == NULL) 458 return -1; 459 return 0; 460} 461 462/* ch_reset(): 463 * Reset the character editor 464 */ 465protected void 466ch_reset(EditLine *el, int mclear) 467{ 468 el->el_line.cursor = el->el_line.buffer; 469 el->el_line.lastchar = el->el_line.buffer; 470 471 el->el_chared.c_undo.len = -1; 472 el->el_chared.c_undo.cursor = 0; 473 474 el->el_chared.c_vcmd.action = NOP; 475 el->el_chared.c_vcmd.pos = el->el_line.buffer; 476 477 el->el_chared.c_kill.mark = el->el_line.buffer; 478 479 el->el_map.current = el->el_map.key; 480 481 el->el_state.inputmode = MODE_INSERT; /* XXX: save a default */ 482 el->el_state.doingarg = 0; 483 el->el_state.metanext = 0; 484 el->el_state.argument = 1; 485 el->el_state.lastcmd = ED_UNASSIGNED; 486 487 el->el_history.eventno = 0; 488 489 if (mclear) 490 ch__clearmacro(el); 491} 492 493private void 494ch__clearmacro(EditLine *el) 495{ 496 c_macro_t *ma = &el->el_chared.c_macro; 497 while (ma->level >= 0) 498 el_free(ma->macro[ma->level--]); 499} 500 501/* ch_enlargebufs(): 502 * Enlarge line buffer to be able to hold twice as much characters. 503 * Returns 1 if successful, 0 if not. 504 */ 505protected int 506ch_enlargebufs(EditLine *el, size_t addlen) 507{ 508 size_t sz, newsz; 509 Char *newbuffer, *oldbuf, *oldkbuf; 510 511 sz = (size_t)(el->el_line.limit - el->el_line.buffer + EL_LEAVE); 512 newsz = sz * 2; 513 /* 514 * If newly required length is longer than current buffer, we need 515 * to make the buffer big enough to hold both old and new stuff. 516 */ 517 if (addlen > sz) { 518 while(newsz - sz < addlen) 519 newsz *= 2; 520 } 521 522 /* 523 * Reallocate line buffer. 524 */ 525 newbuffer = el_realloc(el->el_line.buffer, newsz * sizeof(*newbuffer)); 526 if (!newbuffer) 527 return 0; 528 529 /* zero the newly added memory, leave old data in */ 530 (void) memset(&newbuffer[sz], 0, (newsz - sz) * sizeof(*newbuffer)); 531 532 oldbuf = el->el_line.buffer; 533 534 el->el_line.buffer = newbuffer; 535 el->el_line.cursor = newbuffer + (el->el_line.cursor - oldbuf); 536 el->el_line.lastchar = newbuffer + (el->el_line.lastchar - oldbuf); 537 /* don't set new size until all buffers are enlarged */ 538 el->el_line.limit = &newbuffer[sz - EL_LEAVE]; 539 540 /* 541 * Reallocate kill buffer. 542 */ 543 newbuffer = el_realloc(el->el_chared.c_kill.buf, newsz * 544 sizeof(*newbuffer)); 545 if (!newbuffer) 546 return 0; 547 548 /* zero the newly added memory, leave old data in */ 549 (void) memset(&newbuffer[sz], 0, (newsz - sz) * sizeof(*newbuffer)); 550 551 oldkbuf = el->el_chared.c_kill.buf; 552 553 el->el_chared.c_kill.buf = newbuffer; 554 el->el_chared.c_kill.last = newbuffer + 555 (el->el_chared.c_kill.last - oldkbuf); 556 el->el_chared.c_kill.mark = el->el_line.buffer + 557 (el->el_chared.c_kill.mark - oldbuf); 558 559 /* 560 * Reallocate undo buffer. 561 */ 562 newbuffer = el_realloc(el->el_chared.c_undo.buf, 563 newsz * sizeof(*newbuffer)); 564 if (!newbuffer) 565 return 0; 566 567 /* zero the newly added memory, leave old data in */ 568 (void) memset(&newbuffer[sz], 0, (newsz - sz) * sizeof(*newbuffer)); 569 el->el_chared.c_undo.buf = newbuffer; 570 571 newbuffer = el_realloc(el->el_chared.c_redo.buf, 572 newsz * sizeof(*newbuffer)); 573 if (!newbuffer) 574 return 0; 575 el->el_chared.c_redo.pos = newbuffer + 576 (el->el_chared.c_redo.pos - el->el_chared.c_redo.buf); 577 el->el_chared.c_redo.lim = newbuffer + 578 (el->el_chared.c_redo.lim - el->el_chared.c_redo.buf); 579 el->el_chared.c_redo.buf = newbuffer; 580 581 if (!hist_enlargebuf(el, sz, newsz)) 582 return 0; 583 584 /* Safe to set enlarged buffer size */ 585 el->el_line.limit = &el->el_line.buffer[newsz - EL_LEAVE]; 586 if (el->el_chared.c_resizefun) 587 (*el->el_chared.c_resizefun)(el, el->el_chared.c_resizearg); 588 return 1; 589} 590 591/* ch_end(): 592 * Free the data structures used by the editor 593 */ 594protected void 595ch_end(EditLine *el) 596{ 597 el_free(el->el_line.buffer); 598 el->el_line.buffer = NULL; 599 el->el_line.limit = NULL; 600 el_free(el->el_chared.c_undo.buf); 601 el->el_chared.c_undo.buf = NULL; 602 el_free(el->el_chared.c_redo.buf); 603 el->el_chared.c_redo.buf = NULL; 604 el->el_chared.c_redo.pos = NULL; 605 el->el_chared.c_redo.lim = NULL; 606 el->el_chared.c_redo.cmd = ED_UNASSIGNED; 607 el_free(el->el_chared.c_kill.buf); 608 el->el_chared.c_kill.buf = NULL; 609 ch_reset(el, 1); 610 el_free(el->el_chared.c_macro.macro); 611 el->el_chared.c_macro.macro = NULL; 612} 613 614 615/* el_insertstr(): 616 * Insert string at cursorI 617 */ 618public int 619FUN(el,insertstr)(EditLine *el, const Char *s) 620{ 621 size_t len; 622 623 if (s == NULL || (len = Strlen(s)) == 0) 624 return -1; 625 if (el->el_line.lastchar + len >= el->el_line.limit) { 626 if (!ch_enlargebufs(el, len)) 627 return -1; 628 } 629 630 c_insert(el, (int)len); 631 while (*s) 632 *el->el_line.cursor++ = *s++; 633 return 0; 634} 635 636 637/* el_deletestr(): 638 * Delete num characters before the cursor 639 */ 640public void 641el_deletestr(EditLine *el, int n) 642{ 643 if (n <= 0) 644 return; 645 646 if (el->el_line.cursor < &el->el_line.buffer[n]) 647 return; 648 649 c_delbefore(el, n); /* delete before dot */ 650 el->el_line.cursor -= n; 651 if (el->el_line.cursor < el->el_line.buffer) 652 el->el_line.cursor = el->el_line.buffer; 653} 654 655/* el_cursor(): 656 * Move the cursor to the left or the right of the current position 657 */ 658public int 659el_cursor(EditLine *el, int n) 660{ 661 if (n == 0) 662 goto out; 663 664 el->el_line.cursor += n; 665 666 if (el->el_line.cursor < el->el_line.buffer) 667 el->el_line.cursor = el->el_line.buffer; 668 if (el->el_line.cursor > el->el_line.lastchar) 669 el->el_line.cursor = el->el_line.lastchar; 670out: 671 return (int)(el->el_line.cursor - el->el_line.buffer); 672} 673 674/* c_gets(): 675 * Get a string 676 */ 677protected int 678c_gets(EditLine *el, Char *buf, const Char *prompt) 679{ 680 wchar_t wch; 681 ssize_t len; 682 Char *cp = el->el_line.buffer, ch; 683 684 if (prompt) { 685 len = (ssize_t)Strlen(prompt); 686 (void)memcpy(cp, prompt, (size_t)len * sizeof(*cp)); 687 cp += len; 688 } 689 len = 0; 690 691 for (;;) { 692 el->el_line.cursor = cp; 693 *cp = ' '; 694 el->el_line.lastchar = cp + 1; 695 re_refresh(el); 696 697 if (el_wgetc(el, &wch) != 1) { 698 ed_end_of_file(el, 0); 699 len = -1; 700 break; 701 } 702 ch = (Char)wch; 703 704 switch (ch) { 705 706 case L'\b': /* Delete and backspace */ 707 case 0177: 708 if (len == 0) { 709 len = -1; 710 break; 711 } 712 len--; 713 cp--; 714 continue; 715 716 case 0033: /* ESC */ 717 case L'\r': /* Newline */ 718 case L'\n': 719 buf[len] = ch; 720 break; 721 722 default: 723 if (len >= (ssize_t)(EL_BUFSIZ - 16)) 724 terminal_beep(el); 725 else { 726 buf[len++] = ch; 727 *cp++ = ch; 728 } 729 continue; 730 } 731 break; 732 } 733 734 el->el_line.buffer[0] = '\0'; 735 el->el_line.lastchar = el->el_line.buffer; 736 el->el_line.cursor = el->el_line.buffer; 737 return (int)len; 738} 739 740 741/* c_hpos(): 742 * Return the current horizontal position of the cursor 743 */ 744protected int 745c_hpos(EditLine *el) 746{ 747 Char *ptr; 748 749 /* 750 * Find how many characters till the beginning of this line. 751 */ 752 if (el->el_line.cursor == el->el_line.buffer) 753 return 0; 754 else { 755 for (ptr = el->el_line.cursor - 1; 756 ptr >= el->el_line.buffer && *ptr != '\n'; 757 ptr--) 758 continue; 759 return (int)(el->el_line.cursor - ptr - 1); 760 } 761} 762 763protected int 764ch_resizefun(EditLine *el, el_zfunc_t f, void *a) 765{ 766 el->el_chared.c_resizefun = f; 767 el->el_chared.c_resizearg = a; 768 return 0; 769} 770 771protected int 772ch_aliasfun(EditLine *el, el_afunc_t f, void *a) 773{ 774 el->el_chared.c_aliasfun = f; 775 el->el_chared.c_aliasarg = a; 776 return 0; 777} 778