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