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