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