1 /* $NetBSD: chared.c,v 1.25 2005/08/08 01:41:30 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.25 2005/08/08 01:41:30 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 __P((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 unsigned int size; 64 65 /* Save entire line for undo */ 66 size = el->el_line.lastchar - el->el_line.buffer; 67 vu->len = size; 68 vu->cursor = el->el_line.cursor - el->el_line.buffer; 69 memcpy(vu->buf, el->el_line.buffer, size); 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 memcpy(k->buf, ptr, size +0u); 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, num +0u)) 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 = 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 = 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("*?_-.[]~=", 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)((unsigned char) *p)) 242 p--; 243 while ((p >= low) && (*wtest)((unsigned char) *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)((unsigned char) *p)) 264 p++; 265 while ((p < high) && (*wtest)((unsigned char) *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)((unsigned char) *p); 284 while ((p < high) && (*wtest)((unsigned char) *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((unsigned char) *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((unsigned char) *p)) 314 p--; 315 test = (*wtest)((unsigned char) *p); 316 while ((p >= low) && (*wtest)((unsigned char) *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 #ifdef notdef 330 /* c__number(): 331 * Ignore character p points to, return number appearing after that. 332 * A '$' by itself means a big number; "$-" is for negative; '^' means 1. 333 * Return p pointing to last char used. 334 */ 335 protected char * 336 c__number( 337 char *p, /* character position */ 338 int *num, /* Return value */ 339 int dval) /* dval is the number to subtract from like $-3 */ 340 { 341 int i; 342 int sign = 1; 343 344 if (*++p == '^') { 345 *num = 1; 346 return (p); 347 } 348 if (*p == '$') { 349 if (*++p != '-') { 350 *num = 0x7fffffff; /* Handle $ */ 351 return (--p); 352 } 353 sign = -1; /* Handle $- */ 354 ++p; 355 } 356 for (i = 0; isdigit((unsigned char) *p); i = 10 * i + *p++ - '0') 357 continue; 358 *num = (sign < 0 ? dval - i : i); 359 return (--p); 360 } 361 #endif 362 363 /* cv_delfini(): 364 * Finish vi delete action 365 */ 366 protected void 367 cv_delfini(EditLine *el) 368 { 369 int size; 370 int action = el->el_chared.c_vcmd.action; 371 372 if (action & INSERT) 373 el->el_map.current = el->el_map.key; 374 375 if (el->el_chared.c_vcmd.pos == 0) 376 /* sanity */ 377 return; 378 379 size = el->el_line.cursor - el->el_chared.c_vcmd.pos; 380 if (size == 0) 381 size = 1; 382 el->el_line.cursor = el->el_chared.c_vcmd.pos; 383 if (action & YANK) { 384 if (size > 0) 385 cv_yank(el, el->el_line.cursor, size); 386 else 387 cv_yank(el, el->el_line.cursor + size, -size); 388 } else { 389 if (size > 0) { 390 c_delafter(el, size); 391 re_refresh_cursor(el); 392 } else { 393 c_delbefore(el, -size); 394 el->el_line.cursor += size; 395 } 396 } 397 el->el_chared.c_vcmd.action = NOP; 398 } 399 400 401 #ifdef notdef 402 /* ce__endword(): 403 * Go to the end of this word according to emacs 404 */ 405 protected char * 406 ce__endword(char *p, char *high, int n) 407 { 408 p++; 409 410 while (n--) { 411 while ((p < high) && isspace((unsigned char) *p)) 412 p++; 413 while ((p < high) && !isspace((unsigned char) *p)) 414 p++; 415 } 416 417 p--; 418 return (p); 419 } 420 #endif 421 422 423 /* cv__endword(): 424 * Go to the end of this word according to vi 425 */ 426 protected char * 427 cv__endword(char *p, char *high, int n, int (*wtest)(int)) 428 { 429 int test; 430 431 p++; 432 433 while (n--) { 434 while ((p < high) && isspace((unsigned char) *p)) 435 p++; 436 437 test = (*wtest)((unsigned char) *p); 438 while ((p < high) && (*wtest)((unsigned char) *p) == test) 439 p++; 440 } 441 p--; 442 return (p); 443 } 444 445 /* ch_init(): 446 * Initialize the character editor 447 */ 448 protected int 449 ch_init(EditLine *el) 450 { 451 c_macro_t *ma = &el->el_chared.c_macro; 452 453 el->el_line.buffer = (char *) el_malloc(EL_BUFSIZ); 454 if (el->el_line.buffer == NULL) 455 return (-1); 456 457 (void) memset(el->el_line.buffer, 0, EL_BUFSIZ); 458 el->el_line.cursor = el->el_line.buffer; 459 el->el_line.lastchar = el->el_line.buffer; 460 el->el_line.limit = &el->el_line.buffer[EL_BUFSIZ - EL_LEAVE]; 461 462 el->el_chared.c_undo.buf = (char *) el_malloc(EL_BUFSIZ); 463 if (el->el_chared.c_undo.buf == NULL) 464 return (-1); 465 (void) memset(el->el_chared.c_undo.buf, 0, EL_BUFSIZ); 466 el->el_chared.c_undo.len = -1; 467 el->el_chared.c_undo.cursor = 0; 468 el->el_chared.c_redo.buf = (char *) el_malloc(EL_BUFSIZ); 469 if (el->el_chared.c_redo.buf == NULL) 470 return (-1); 471 el->el_chared.c_redo.pos = el->el_chared.c_redo.buf; 472 el->el_chared.c_redo.lim = el->el_chared.c_redo.buf + EL_BUFSIZ; 473 el->el_chared.c_redo.cmd = ED_UNASSIGNED; 474 475 el->el_chared.c_vcmd.action = NOP; 476 el->el_chared.c_vcmd.pos = el->el_line.buffer; 477 478 el->el_chared.c_kill.buf = (char *) el_malloc(EL_BUFSIZ); 479 if (el->el_chared.c_kill.buf == NULL) 480 return (-1); 481 (void) memset(el->el_chared.c_kill.buf, 0, EL_BUFSIZ); 482 el->el_chared.c_kill.mark = el->el_line.buffer; 483 el->el_chared.c_kill.last = el->el_chared.c_kill.buf; 484 485 el->el_map.current = el->el_map.key; 486 487 el->el_state.inputmode = MODE_INSERT; /* XXX: save a default */ 488 el->el_state.doingarg = 0; 489 el->el_state.metanext = 0; 490 el->el_state.argument = 1; 491 el->el_state.lastcmd = ED_UNASSIGNED; 492 493 ma->level = -1; 494 ma->offset = 0; 495 ma->macro = (char **) el_malloc(EL_MAXMACRO * sizeof(char *)); 496 if (ma->macro == NULL) 497 return (-1); 498 return (0); 499 } 500 501 /* ch_reset(): 502 * Reset the character editor 503 */ 504 protected void 505 ch_reset(EditLine *el, int mclear) 506 { 507 el->el_line.cursor = el->el_line.buffer; 508 el->el_line.lastchar = el->el_line.buffer; 509 510 el->el_chared.c_undo.len = -1; 511 el->el_chared.c_undo.cursor = 0; 512 513 el->el_chared.c_vcmd.action = NOP; 514 el->el_chared.c_vcmd.pos = el->el_line.buffer; 515 516 el->el_chared.c_kill.mark = el->el_line.buffer; 517 518 el->el_map.current = el->el_map.key; 519 520 el->el_state.inputmode = MODE_INSERT; /* XXX: save a default */ 521 el->el_state.doingarg = 0; 522 el->el_state.metanext = 0; 523 el->el_state.argument = 1; 524 el->el_state.lastcmd = ED_UNASSIGNED; 525 526 el->el_history.eventno = 0; 527 528 if (mclear) 529 ch__clearmacro(el); 530 } 531 532 private void 533 ch__clearmacro(el) 534 EditLine *el; 535 { 536 c_macro_t *ma = &el->el_chared.c_macro; 537 while (ma->level >= 0) 538 el_free((ptr_t)ma->macro[ma->level--]); 539 } 540 541 /* ch_enlargebufs(): 542 * Enlarge line buffer to be able to hold twice as much characters. 543 * Returns 1 if successful, 0 if not. 544 */ 545 protected int 546 ch_enlargebufs(el, addlen) 547 EditLine *el; 548 size_t addlen; 549 { 550 size_t sz, newsz; 551 char *newbuffer, *oldbuf, *oldkbuf; 552 553 sz = el->el_line.limit - el->el_line.buffer + EL_LEAVE; 554 newsz = sz * 2; 555 /* 556 * If newly required length is longer than current buffer, we need 557 * to make the buffer big enough to hold both old and new stuff. 558 */ 559 if (addlen > sz) { 560 while(newsz - sz < addlen) 561 newsz *= 2; 562 } 563 564 /* 565 * Reallocate line buffer. 566 */ 567 newbuffer = el_realloc(el->el_line.buffer, newsz); 568 if (!newbuffer) 569 return 0; 570 571 /* zero the newly added memory, leave old data in */ 572 (void) memset(&newbuffer[sz], 0, newsz - sz); 573 574 oldbuf = el->el_line.buffer; 575 576 el->el_line.buffer = newbuffer; 577 el->el_line.cursor = newbuffer + (el->el_line.cursor - oldbuf); 578 el->el_line.lastchar = newbuffer + (el->el_line.lastchar - oldbuf); 579 /* don't set new size until all buffers are enlarged */ 580 el->el_line.limit = &newbuffer[sz - EL_LEAVE]; 581 582 /* 583 * Reallocate kill buffer. 584 */ 585 newbuffer = el_realloc(el->el_chared.c_kill.buf, newsz); 586 if (!newbuffer) 587 return 0; 588 589 /* zero the newly added memory, leave old data in */ 590 (void) memset(&newbuffer[sz], 0, newsz - sz); 591 592 oldkbuf = el->el_chared.c_kill.buf; 593 594 el->el_chared.c_kill.buf = newbuffer; 595 el->el_chared.c_kill.last = newbuffer + 596 (el->el_chared.c_kill.last - oldkbuf); 597 el->el_chared.c_kill.mark = el->el_line.buffer + 598 (el->el_chared.c_kill.mark - oldbuf); 599 600 /* 601 * Reallocate undo buffer. 602 */ 603 newbuffer = el_realloc(el->el_chared.c_undo.buf, newsz); 604 if (!newbuffer) 605 return 0; 606 607 /* zero the newly added memory, leave old data in */ 608 (void) memset(&newbuffer[sz], 0, newsz - sz); 609 el->el_chared.c_undo.buf = newbuffer; 610 611 newbuffer = el_realloc(el->el_chared.c_redo.buf, newsz); 612 if (!newbuffer) 613 return 0; 614 el->el_chared.c_redo.pos = newbuffer + 615 (el->el_chared.c_redo.pos - el->el_chared.c_redo.buf); 616 el->el_chared.c_redo.lim = newbuffer + 617 (el->el_chared.c_redo.lim - el->el_chared.c_redo.buf); 618 el->el_chared.c_redo.buf = newbuffer; 619 620 if (!hist_enlargebuf(el, sz, newsz)) 621 return 0; 622 623 /* Safe to set enlarged buffer size */ 624 el->el_line.limit = &el->el_line.buffer[newsz - EL_LEAVE]; 625 return 1; 626 } 627 628 /* ch_end(): 629 * Free the data structures used by the editor 630 */ 631 protected void 632 ch_end(EditLine *el) 633 { 634 el_free((ptr_t) el->el_line.buffer); 635 el->el_line.buffer = NULL; 636 el->el_line.limit = NULL; 637 el_free((ptr_t) el->el_chared.c_undo.buf); 638 el->el_chared.c_undo.buf = NULL; 639 el_free((ptr_t) el->el_chared.c_redo.buf); 640 el->el_chared.c_redo.buf = NULL; 641 el->el_chared.c_redo.pos = NULL; 642 el->el_chared.c_redo.lim = NULL; 643 el->el_chared.c_redo.cmd = ED_UNASSIGNED; 644 el_free((ptr_t) el->el_chared.c_kill.buf); 645 el->el_chared.c_kill.buf = NULL; 646 ch_reset(el, 1); 647 el_free((ptr_t) el->el_chared.c_macro.macro); 648 el->el_chared.c_macro.macro = NULL; 649 } 650 651 652 /* el_insertstr(): 653 * Insert string at cursorI 654 */ 655 public int 656 el_insertstr(EditLine *el, const char *s) 657 { 658 size_t len; 659 660 if ((len = strlen(s)) == 0) 661 return (-1); 662 if (el->el_line.lastchar + len >= el->el_line.limit) { 663 if (!ch_enlargebufs(el, len)) 664 return (-1); 665 } 666 667 c_insert(el, (int)len); 668 while (*s) 669 *el->el_line.cursor++ = *s++; 670 return (0); 671 } 672 673 674 /* el_deletestr(): 675 * Delete num characters before the cursor 676 */ 677 public void 678 el_deletestr(EditLine *el, int n) 679 { 680 if (n <= 0) 681 return; 682 683 if (el->el_line.cursor < &el->el_line.buffer[n]) 684 return; 685 686 c_delbefore(el, n); /* delete before dot */ 687 el->el_line.cursor -= n; 688 if (el->el_line.cursor < el->el_line.buffer) 689 el->el_line.cursor = el->el_line.buffer; 690 } 691 692 /* c_gets(): 693 * Get a string 694 */ 695 protected int 696 c_gets(EditLine *el, char *buf, const char *prompt) 697 { 698 char ch; 699 int len; 700 char *cp = el->el_line.buffer; 701 702 if (prompt) { 703 len = strlen(prompt); 704 memcpy(cp, prompt, len + 0u); 705 cp += len; 706 } 707 len = 0; 708 709 for (;;) { 710 el->el_line.cursor = cp; 711 *cp = ' '; 712 el->el_line.lastchar = cp + 1; 713 re_refresh(el); 714 715 if (el_getc(el, &ch) != 1) { 716 ed_end_of_file(el, 0); 717 len = -1; 718 break; 719 } 720 721 switch (ch) { 722 723 case 0010: /* Delete and backspace */ 724 case 0177: 725 if (len <= 0) { 726 len = -1; 727 break; 728 } 729 cp--; 730 continue; 731 732 case 0033: /* ESC */ 733 case '\r': /* Newline */ 734 case '\n': 735 buf[len] = ch; 736 break; 737 738 default: 739 if (len >= EL_BUFSIZ - 16) 740 term_beep(el); 741 else { 742 buf[len++] = ch; 743 *cp++ = ch; 744 } 745 continue; 746 } 747 break; 748 } 749 750 el->el_line.buffer[0] = '\0'; 751 el->el_line.lastchar = el->el_line.buffer; 752 el->el_line.cursor = el->el_line.buffer; 753 return len; 754 } 755 756 757 /* c_hpos(): 758 * Return the current horizontal position of the cursor 759 */ 760 protected int 761 c_hpos(EditLine *el) 762 { 763 char *ptr; 764 765 /* 766 * Find how many characters till the beginning of this line. 767 */ 768 if (el->el_line.cursor == el->el_line.buffer) 769 return (0); 770 else { 771 for (ptr = el->el_line.cursor - 1; 772 ptr >= el->el_line.buffer && *ptr != '\n'; 773 ptr--) 774 continue; 775 return (el->el_line.cursor - ptr - 1); 776 } 777 } 778