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