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