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