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