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