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