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