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