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