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