1 /* $NetBSD: chared.c,v 1.21 2003/11/02 20:08:41 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.21 2003/11/02 20:08:41 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 /* 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_delbefore(): 139 * Delete num characters before the cursor 140 */ 141 protected void 142 c_delbefore(EditLine *el, int num) 143 { 144 145 if (el->el_line.cursor - num < el->el_line.buffer) 146 num = el->el_line.cursor - el->el_line.buffer; 147 148 if (el->el_map.current != el->el_map.emacs) { 149 cv_undo(el); 150 cv_yank(el, el->el_line.cursor - num, num); 151 } 152 153 if (num > 0) { 154 char *cp; 155 156 for (cp = el->el_line.cursor - num; 157 cp <= el->el_line.lastchar; 158 cp++) 159 *cp = cp[num]; 160 161 el->el_line.lastchar -= num; 162 } 163 } 164 165 166 /* ce__isword(): 167 * Return if p is part of a word according to emacs 168 */ 169 protected int 170 ce__isword(int p) 171 { 172 return (isalnum(p) || strchr("*?_-.[]~=", p) != NULL); 173 } 174 175 176 /* cv__isword(): 177 * Return if p is part of a word according to vi 178 */ 179 protected int 180 cv__isword(int p) 181 { 182 if (isalnum(p) || p == '_') 183 return 1; 184 if (isgraph(p)) 185 return 2; 186 return 0; 187 } 188 189 190 /* cv__isWord(): 191 * Return if p is part of a big word according to vi 192 */ 193 protected int 194 cv__isWord(int p) 195 { 196 return (!isspace(p)); 197 } 198 199 200 /* c__prev_word(): 201 * Find the previous word 202 */ 203 protected char * 204 c__prev_word(char *p, char *low, int n, int (*wtest)(int)) 205 { 206 p--; 207 208 while (n--) { 209 while ((p >= low) && !(*wtest)((unsigned char) *p)) 210 p--; 211 while ((p >= low) && (*wtest)((unsigned char) *p)) 212 p--; 213 } 214 215 /* cp now points to one character before the word */ 216 p++; 217 if (p < low) 218 p = low; 219 /* cp now points where we want it */ 220 return (p); 221 } 222 223 224 /* c__next_word(): 225 * Find the next word 226 */ 227 protected char * 228 c__next_word(char *p, char *high, int n, int (*wtest)(int)) 229 { 230 while (n--) { 231 while ((p < high) && !(*wtest)((unsigned char) *p)) 232 p++; 233 while ((p < high) && (*wtest)((unsigned char) *p)) 234 p++; 235 } 236 if (p > high) 237 p = high; 238 /* p now points where we want it */ 239 return (p); 240 } 241 242 /* cv_next_word(): 243 * Find the next word vi style 244 */ 245 protected char * 246 cv_next_word(EditLine *el, char *p, char *high, int n, int (*wtest)(int)) 247 { 248 int test; 249 250 while (n--) { 251 test = (*wtest)((unsigned char) *p); 252 while ((p < high) && (*wtest)((unsigned char) *p) == test) 253 p++; 254 /* 255 * vi historically deletes with cw only the word preserving the 256 * trailing whitespace! This is not what 'w' does.. 257 */ 258 if (n || el->el_chared.c_vcmd.action != (DELETE|INSERT)) 259 while ((p < high) && isspace((unsigned char) *p)) 260 p++; 261 } 262 263 /* p now points where we want it */ 264 if (p > high) 265 return (high); 266 else 267 return (p); 268 } 269 270 271 /* cv_prev_word(): 272 * Find the previous word vi style 273 */ 274 protected char * 275 cv_prev_word(char *p, char *low, int n, int (*wtest)(int)) 276 { 277 int test; 278 279 p--; 280 while (n--) { 281 while ((p > low) && isspace((unsigned char) *p)) 282 p--; 283 test = (*wtest)((unsigned char) *p); 284 while ((p >= low) && (*wtest)((unsigned char) *p) == test) 285 p--; 286 } 287 p++; 288 289 /* p now points where we want it */ 290 if (p < low) 291 return (low); 292 else 293 return (p); 294 } 295 296 297 #ifdef notdef 298 /* c__number(): 299 * Ignore character p points to, return number appearing after that. 300 * A '$' by itself means a big number; "$-" is for negative; '^' means 1. 301 * Return p pointing to last char used. 302 */ 303 protected char * 304 c__number( 305 char *p, /* character position */ 306 int *num, /* Return value */ 307 int dval) /* dval is the number to subtract from like $-3 */ 308 { 309 int i; 310 int sign = 1; 311 312 if (*++p == '^') { 313 *num = 1; 314 return (p); 315 } 316 if (*p == '$') { 317 if (*++p != '-') { 318 *num = 0x7fffffff; /* Handle $ */ 319 return (--p); 320 } 321 sign = -1; /* Handle $- */ 322 ++p; 323 } 324 for (i = 0; isdigit((unsigned char) *p); i = 10 * i + *p++ - '0') 325 continue; 326 *num = (sign < 0 ? dval - i : i); 327 return (--p); 328 } 329 #endif 330 331 /* cv_delfini(): 332 * Finish vi delete action 333 */ 334 protected void 335 cv_delfini(EditLine *el) 336 { 337 int size; 338 int action = el->el_chared.c_vcmd.action; 339 340 if (action & INSERT) 341 el->el_map.current = el->el_map.key; 342 343 if (el->el_chared.c_vcmd.pos == 0) 344 /* sanity */ 345 return; 346 347 size = el->el_line.cursor - el->el_chared.c_vcmd.pos; 348 if (size == 0) 349 size = 1; 350 el->el_line.cursor = el->el_chared.c_vcmd.pos; 351 if (action & YANK) { 352 if (size > 0) 353 cv_yank(el, el->el_line.cursor, size); 354 else 355 cv_yank(el, el->el_line.cursor + size, -size); 356 } else { 357 if (size > 0) { 358 c_delafter(el, size); 359 re_refresh_cursor(el); 360 } else { 361 c_delbefore(el, -size); 362 el->el_line.cursor += size; 363 } 364 } 365 el->el_chared.c_vcmd.action = NOP; 366 } 367 368 369 #ifdef notdef 370 /* ce__endword(): 371 * Go to the end of this word according to emacs 372 */ 373 protected char * 374 ce__endword(char *p, char *high, int n) 375 { 376 p++; 377 378 while (n--) { 379 while ((p < high) && isspace((unsigned char) *p)) 380 p++; 381 while ((p < high) && !isspace((unsigned char) *p)) 382 p++; 383 } 384 385 p--; 386 return (p); 387 } 388 #endif 389 390 391 /* cv__endword(): 392 * Go to the end of this word according to vi 393 */ 394 protected char * 395 cv__endword(char *p, char *high, int n, int (*wtest)(int)) 396 { 397 int test; 398 399 p++; 400 401 while (n--) { 402 while ((p < high) && isspace((unsigned char) *p)) 403 p++; 404 405 test = (*wtest)((unsigned char) *p); 406 while ((p < high) && (*wtest)((unsigned char) *p) == test) 407 p++; 408 } 409 p--; 410 return (p); 411 } 412 413 /* ch_init(): 414 * Initialize the character editor 415 */ 416 protected int 417 ch_init(EditLine *el) 418 { 419 el->el_line.buffer = (char *) el_malloc(EL_BUFSIZ); 420 if (el->el_line.buffer == NULL) 421 return (-1); 422 423 (void) memset(el->el_line.buffer, 0, EL_BUFSIZ); 424 el->el_line.cursor = el->el_line.buffer; 425 el->el_line.lastchar = el->el_line.buffer; 426 el->el_line.limit = &el->el_line.buffer[EL_BUFSIZ - EL_LEAVE]; 427 428 el->el_chared.c_undo.buf = (char *) el_malloc(EL_BUFSIZ); 429 if (el->el_chared.c_undo.buf == NULL) 430 return (-1); 431 (void) memset(el->el_chared.c_undo.buf, 0, EL_BUFSIZ); 432 el->el_chared.c_undo.len = -1; 433 el->el_chared.c_undo.cursor = 0; 434 el->el_chared.c_redo.buf = (char *) el_malloc(EL_BUFSIZ); 435 if (el->el_chared.c_redo.buf == NULL) 436 return (-1); 437 el->el_chared.c_redo.pos = el->el_chared.c_redo.buf; 438 el->el_chared.c_redo.lim = el->el_chared.c_redo.buf + EL_BUFSIZ; 439 el->el_chared.c_redo.cmd = ED_UNASSIGNED; 440 441 el->el_chared.c_vcmd.action = NOP; 442 el->el_chared.c_vcmd.pos = el->el_line.buffer; 443 444 el->el_chared.c_kill.buf = (char *) el_malloc(EL_BUFSIZ); 445 if (el->el_chared.c_kill.buf == NULL) 446 return (-1); 447 (void) memset(el->el_chared.c_kill.buf, 0, EL_BUFSIZ); 448 el->el_chared.c_kill.mark = el->el_line.buffer; 449 el->el_chared.c_kill.last = el->el_chared.c_kill.buf; 450 451 el->el_map.current = el->el_map.key; 452 453 el->el_state.inputmode = MODE_INSERT; /* XXX: save a default */ 454 el->el_state.doingarg = 0; 455 el->el_state.metanext = 0; 456 el->el_state.argument = 1; 457 el->el_state.lastcmd = ED_UNASSIGNED; 458 459 el->el_chared.c_macro.level = -1; 460 el->el_chared.c_macro.offset = 0; 461 el->el_chared.c_macro.macro = (char **) el_malloc(EL_MAXMACRO * 462 sizeof(char *)); 463 if (el->el_chared.c_macro.macro == NULL) 464 return (-1); 465 return (0); 466 } 467 468 /* ch_reset(): 469 * Reset the character editor 470 */ 471 protected void 472 ch_reset(EditLine *el) 473 { 474 el->el_line.cursor = el->el_line.buffer; 475 el->el_line.lastchar = el->el_line.buffer; 476 477 el->el_chared.c_undo.len = -1; 478 el->el_chared.c_undo.cursor = 0; 479 480 el->el_chared.c_vcmd.action = NOP; 481 el->el_chared.c_vcmd.pos = el->el_line.buffer; 482 483 el->el_chared.c_kill.mark = el->el_line.buffer; 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 el->el_chared.c_macro.level = -1; 494 495 el->el_history.eventno = 0; 496 } 497 498 /* ch_enlargebufs(): 499 * Enlarge line buffer to be able to hold twice as much characters. 500 * Returns 1 if successful, 0 if not. 501 */ 502 protected int 503 ch_enlargebufs(el, addlen) 504 EditLine *el; 505 size_t addlen; 506 { 507 size_t sz, newsz; 508 char *newbuffer, *oldbuf, *oldkbuf; 509 510 sz = el->el_line.limit - el->el_line.buffer + EL_LEAVE; 511 newsz = sz * 2; 512 /* 513 * If newly required length is longer than current buffer, we need 514 * to make the buffer big enough to hold both old and new stuff. 515 */ 516 if (addlen > sz) { 517 while(newsz - sz < addlen) 518 newsz *= 2; 519 } 520 521 /* 522 * Reallocate line buffer. 523 */ 524 newbuffer = el_realloc(el->el_line.buffer, newsz); 525 if (!newbuffer) 526 return 0; 527 528 /* zero the newly added memory, leave old data in */ 529 (void) memset(&newbuffer[sz], 0, newsz - sz); 530 531 oldbuf = el->el_line.buffer; 532 533 el->el_line.buffer = newbuffer; 534 el->el_line.cursor = newbuffer + (el->el_line.cursor - oldbuf); 535 el->el_line.lastchar = newbuffer + (el->el_line.lastchar - oldbuf); 536 /* don't set new size until all buffers are enlarged */ 537 el->el_line.limit = &newbuffer[sz - EL_LEAVE]; 538 539 /* 540 * Reallocate kill buffer. 541 */ 542 newbuffer = el_realloc(el->el_chared.c_kill.buf, newsz); 543 if (!newbuffer) 544 return 0; 545 546 /* zero the newly added memory, leave old data in */ 547 (void) memset(&newbuffer[sz], 0, newsz - sz); 548 549 oldkbuf = el->el_chared.c_kill.buf; 550 551 el->el_chared.c_kill.buf = newbuffer; 552 el->el_chared.c_kill.last = newbuffer + 553 (el->el_chared.c_kill.last - oldkbuf); 554 el->el_chared.c_kill.mark = el->el_line.buffer + 555 (el->el_chared.c_kill.mark - oldbuf); 556 557 /* 558 * Reallocate undo buffer. 559 */ 560 newbuffer = el_realloc(el->el_chared.c_undo.buf, newsz); 561 if (!newbuffer) 562 return 0; 563 564 /* zero the newly added memory, leave old data in */ 565 (void) memset(&newbuffer[sz], 0, newsz - sz); 566 el->el_chared.c_undo.buf = newbuffer; 567 568 newbuffer = el_realloc(el->el_chared.c_redo.buf, newsz); 569 if (!newbuffer) 570 return 0; 571 el->el_chared.c_redo.pos = newbuffer + 572 (el->el_chared.c_redo.pos - el->el_chared.c_redo.buf); 573 el->el_chared.c_redo.lim = newbuffer + 574 (el->el_chared.c_redo.lim - el->el_chared.c_redo.buf); 575 el->el_chared.c_redo.buf = newbuffer; 576 577 if (!hist_enlargebuf(el, sz, newsz)) 578 return 0; 579 580 /* Safe to set enlarged buffer size */ 581 el->el_line.limit = &el->el_line.buffer[newsz - EL_LEAVE]; 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((ptr_t) el->el_line.buffer); 592 el->el_line.buffer = NULL; 593 el->el_line.limit = NULL; 594 el_free((ptr_t) el->el_chared.c_undo.buf); 595 el->el_chared.c_undo.buf = NULL; 596 el_free((ptr_t) 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((ptr_t) el->el_chared.c_kill.buf); 602 el->el_chared.c_kill.buf = NULL; 603 el_free((ptr_t) el->el_chared.c_macro.macro); 604 el->el_chared.c_macro.macro = NULL; 605 ch_reset(el); 606 } 607 608 609 /* el_insertstr(): 610 * Insert string at cursorI 611 */ 612 public int 613 el_insertstr(EditLine *el, const char *s) 614 { 615 size_t len; 616 617 if ((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 /* c_gets(): 650 * Get a string 651 */ 652 protected int 653 c_gets(EditLine *el, char *buf, const char *prompt) 654 { 655 char ch; 656 int len; 657 char *cp = el->el_line.buffer; 658 659 if (prompt) { 660 len = strlen(prompt); 661 memcpy(cp, prompt, len + 0u); 662 cp += len; 663 } 664 len = 0; 665 666 for (;;) { 667 el->el_line.cursor = cp; 668 *cp = ' '; 669 el->el_line.lastchar = cp + 1; 670 re_refresh(el); 671 672 if (el_getc(el, &ch) != 1) { 673 ed_end_of_file(el, 0); 674 len = -1; 675 break; 676 } 677 678 switch (ch) { 679 680 case 0010: /* Delete and backspace */ 681 case 0177: 682 if (len <= 0) { 683 len = -1; 684 break; 685 } 686 cp--; 687 continue; 688 689 case 0033: /* ESC */ 690 case '\r': /* Newline */ 691 case '\n': 692 buf[len] = ch; 693 break; 694 695 default: 696 if (len >= EL_BUFSIZ - 16) 697 term_beep(el); 698 else { 699 buf[len++] = ch; 700 *cp++ = ch; 701 } 702 continue; 703 } 704 break; 705 } 706 707 el->el_line.buffer[0] = '\0'; 708 el->el_line.lastchar = el->el_line.buffer; 709 el->el_line.cursor = el->el_line.buffer; 710 return len; 711 } 712 713 714 /* c_hpos(): 715 * Return the current horizontal position of the cursor 716 */ 717 protected int 718 c_hpos(EditLine *el) 719 { 720 char *ptr; 721 722 /* 723 * Find how many characters till the beginning of this line. 724 */ 725 if (el->el_line.cursor == el->el_line.buffer) 726 return (0); 727 else { 728 for (ptr = el->el_line.cursor - 1; 729 ptr >= el->el_line.buffer && *ptr != '\n'; 730 ptr--) 731 continue; 732 return (el->el_line.cursor - ptr - 1); 733 } 734 } 735