1 /* $NetBSD: search.c,v 1.8 1999/07/02 15:21:27 simonb 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[] = "@(#)search.c 8.1 (Berkeley) 6/4/93"; 43 #else 44 __RCSID("$NetBSD: search.c,v 1.8 1999/07/02 15:21:27 simonb Exp $"); 45 #endif 46 #endif /* not lint && not SCCSID */ 47 48 /* 49 * search.c: History and character search functions 50 */ 51 #include "sys.h" 52 #include <stdlib.h> 53 #if defined(REGEX) 54 #include <regex.h> 55 #elif defined(REGEXP) 56 #include <regexp.h> 57 #endif 58 #include "el.h" 59 60 /* 61 * Adjust cursor in vi mode to include the character under it 62 */ 63 #define EL_CURSOR(el) \ 64 ((el)->el_line.cursor + (((el)->el_map.type == MAP_VI) && \ 65 ((el)->el_map.current == (el)->el_map.alt))) 66 67 /* search_init(): 68 * Initialize the search stuff 69 */ 70 protected int 71 search_init(el) 72 EditLine *el; 73 { 74 el->el_search.patbuf = (char *) el_malloc(EL_BUFSIZ); 75 el->el_search.patlen = 0; 76 el->el_search.patdir = -1; 77 el->el_search.chacha = '\0'; 78 el->el_search.chadir = -1; 79 return 0; 80 } 81 82 83 /* search_end(): 84 * Initialize the search stuff 85 */ 86 protected void 87 search_end(el) 88 EditLine *el; 89 { 90 el_free((ptr_t) el->el_search.patbuf); 91 el->el_search.patbuf = NULL; 92 } 93 94 #ifdef REGEXP 95 /* regerror(): 96 * Handle regular expression errors 97 */ 98 public void 99 /*ARGSUSED*/ 100 regerror(msg) 101 const char *msg; 102 { 103 } 104 #endif 105 106 /* el_match(): 107 * Return if string matches pattern 108 */ 109 protected int 110 el_match(str, pat) 111 const char *str; 112 const char *pat; 113 { 114 #if defined (REGEX) 115 regex_t re; 116 int rv; 117 #elif defined (REGEXP) 118 regexp *rp; 119 int rv; 120 #else 121 extern char *re_comp __P((const char *)); 122 extern int re_exec __P((const char *)); 123 #endif 124 125 if (strstr(str, pat) != NULL) 126 return 1; 127 128 #if defined(REGEX) 129 if (regcomp(&re, pat, 0) == 0) { 130 rv = regexec(&re, str, 0, NULL, 0) == 0; 131 regfree(&re); 132 } else { 133 rv = 0; 134 } 135 return rv; 136 #elif defined(REGEXP) 137 if ((re = regcomp(pat)) != NULL) { 138 rv = regexec(re, str); 139 free((ptr_t) re); 140 } else { 141 rv = 0; 142 } 143 return rv; 144 #else 145 if (re_comp(pat) != NULL) 146 return 0; 147 else 148 return re_exec(str) == 1; 149 #endif 150 } 151 152 153 /* c_hmatch(): 154 * return True if the pattern matches the prefix 155 */ 156 protected int 157 c_hmatch(el, str) 158 EditLine *el; 159 const char *str; 160 { 161 #ifdef SDEBUG 162 (void) fprintf(el->el_errfile, "match `%s' with `%s'\n", 163 el->el_search.patbuf, str); 164 #endif /* SDEBUG */ 165 166 return el_match(str, el->el_search.patbuf); 167 } 168 169 170 /* c_setpat(): 171 * Set the history seatch pattern 172 */ 173 protected void 174 c_setpat(el) 175 EditLine *el; 176 { 177 if (el->el_state.lastcmd != ED_SEARCH_PREV_HISTORY && 178 el->el_state.lastcmd != ED_SEARCH_NEXT_HISTORY) { 179 el->el_search.patlen = EL_CURSOR(el) - el->el_line.buffer; 180 if (el->el_search.patlen >= EL_BUFSIZ) 181 el->el_search.patlen = EL_BUFSIZ -1; 182 if (el->el_search.patlen != 0) { 183 (void) strncpy(el->el_search.patbuf, el->el_line.buffer, 184 el->el_search.patlen); 185 el->el_search.patbuf[el->el_search.patlen] = '\0'; 186 } 187 else 188 el->el_search.patlen = strlen(el->el_search.patbuf); 189 } 190 #ifdef SDEBUG 191 (void) fprintf(el->el_errfile, "\neventno = %d\n", el->el_history.eventno); 192 (void) fprintf(el->el_errfile, "patlen = %d\n", el->el_search.patlen); 193 (void) fprintf(el->el_errfile, "patbuf = \"%s\"\n", el->el_search.patbuf); 194 (void) fprintf(el->el_errfile, "cursor %d lastchar %d\n", 195 EL_CURSOR(el) - el->el_line.buffer, 196 el->el_line.lastchar - el->el_line.buffer); 197 #endif 198 } 199 200 201 /* ce_inc_search(): 202 * Emacs incremental search 203 */ 204 protected el_action_t 205 ce_inc_search(el, dir) 206 EditLine *el; 207 int dir; 208 { 209 static char STRfwd[] = { 'f', 'w', 'd', '\0' }, 210 STRbck[] = { 'b', 'c', 'k', '\0' }; 211 static char pchar = ':'; /* ':' = normal, '?' = failed */ 212 static char endcmd[2] = { '\0', '\0' }; 213 char ch, *cp, *ocursor = el->el_line.cursor, oldpchar = pchar; 214 215 el_action_t ret = CC_NORM; 216 217 int ohisteventno = el->el_history.eventno, 218 oldpatlen = el->el_search.patlen, 219 newdir = dir, 220 done, redo; 221 222 if (el->el_line.lastchar + sizeof(STRfwd) / sizeof(char) + 2 + 223 el->el_search.patlen >= el->el_line.limit) 224 return CC_ERROR; 225 226 for (;;) { 227 228 if (el->el_search.patlen == 0) { /* first round */ 229 pchar = ':'; 230 #ifdef ANCHOR 231 el->el_search.patbuf[el->el_search.patlen++] = '.'; 232 el->el_search.patbuf[el->el_search.patlen++] = '*'; 233 #endif 234 } 235 done = redo = 0; 236 *el->el_line.lastchar++ = '\n'; 237 for (cp = newdir == ED_SEARCH_PREV_HISTORY ? STRbck : STRfwd; 238 *cp; *el->el_line.lastchar++ = *cp++) 239 continue; 240 *el->el_line.lastchar++ = pchar; 241 for (cp = &el->el_search.patbuf[1]; 242 cp < &el->el_search.patbuf[el->el_search.patlen]; 243 *el->el_line.lastchar++ = *cp++) 244 continue; 245 *el->el_line.lastchar = '\0'; 246 re_refresh(el); 247 248 if (el_getc(el, &ch) != 1) 249 return ed_end_of_file(el, 0); 250 251 switch (el->el_map.current[(unsigned char) ch]) { 252 case ED_INSERT: 253 case ED_DIGIT: 254 if (el->el_search.patlen > EL_BUFSIZ - 3) 255 term_beep(el); 256 else { 257 el->el_search.patbuf[el->el_search.patlen++] = ch; 258 *el->el_line.lastchar++ = ch; 259 *el->el_line.lastchar = '\0'; 260 re_refresh(el); 261 } 262 break; 263 264 case EM_INC_SEARCH_NEXT: 265 newdir = ED_SEARCH_NEXT_HISTORY; 266 redo++; 267 break; 268 269 case EM_INC_SEARCH_PREV: 270 newdir = ED_SEARCH_PREV_HISTORY; 271 redo++; 272 break; 273 274 case ED_DELETE_PREV_CHAR: 275 if (el->el_search.patlen > 1) 276 done++; 277 else 278 term_beep(el); 279 break; 280 281 default: 282 switch (ch) { 283 case 0007: /* ^G: Abort */ 284 ret = CC_ERROR; 285 done++; 286 break; 287 288 case 0027: /* ^W: Append word */ 289 /* No can do if globbing characters in pattern */ 290 for (cp = &el->el_search.patbuf[1]; ; cp++) 291 if (cp >= &el->el_search.patbuf[el->el_search.patlen]) { 292 el->el_line.cursor += el->el_search.patlen - 1; 293 cp = c__next_word(el->el_line.cursor, 294 el->el_line.lastchar, 1, ce__isword); 295 while (el->el_line.cursor < cp && 296 *el->el_line.cursor != '\n') { 297 if (el->el_search.patlen > EL_BUFSIZ - 3) { 298 term_beep(el); 299 break; 300 } 301 el->el_search.patbuf[el->el_search.patlen++] = 302 *el->el_line.cursor; 303 *el->el_line.lastchar++ = *el->el_line.cursor++; 304 } 305 el->el_line.cursor = ocursor; 306 *el->el_line.lastchar = '\0'; 307 re_refresh(el); 308 break; 309 } else if (isglob(*cp)) { 310 term_beep(el); 311 break; 312 } 313 break; 314 315 default: /* Terminate and execute cmd */ 316 endcmd[0] = ch; 317 el_push(el, endcmd); 318 /*FALLTHROUGH*/ 319 320 case 0033: /* ESC: Terminate */ 321 ret = CC_REFRESH; 322 done++; 323 break; 324 } 325 break; 326 } 327 328 while (el->el_line.lastchar > el->el_line.buffer && 329 *el->el_line.lastchar != '\n') 330 *el->el_line.lastchar-- = '\0'; 331 *el->el_line.lastchar = '\0'; 332 333 if (!done) { 334 335 /* Can't search if unmatched '[' */ 336 for (cp = &el->el_search.patbuf[el->el_search.patlen-1], ch = ']'; 337 cp > el->el_search.patbuf; cp--) 338 if (*cp == '[' || *cp == ']') { 339 ch = *cp; 340 break; 341 } 342 343 if (el->el_search.patlen > 1 && ch != '[') { 344 if (redo && newdir == dir) { 345 if (pchar == '?') { /* wrap around */ 346 el->el_history.eventno = 347 newdir == ED_SEARCH_PREV_HISTORY ? 0 : 0x7fffffff; 348 if (hist_get(el) == CC_ERROR) 349 /* el->el_history.eventno was fixed by first call */ 350 (void) hist_get(el); 351 el->el_line.cursor = newdir == ED_SEARCH_PREV_HISTORY ? 352 el->el_line.lastchar : el->el_line.buffer; 353 } else 354 el->el_line.cursor += 355 newdir == ED_SEARCH_PREV_HISTORY ? -1 : 1; 356 } 357 #ifdef ANCHOR 358 el->el_search.patbuf[el->el_search.patlen++] = '.'; 359 el->el_search.patbuf[el->el_search.patlen++] = '*'; 360 #endif 361 el->el_search.patbuf[el->el_search.patlen] = '\0'; 362 if (el->el_line.cursor < el->el_line.buffer || 363 el->el_line.cursor > el->el_line.lastchar || 364 (ret = ce_search_line(el, &el->el_search.patbuf[1], 365 newdir)) == CC_ERROR) { 366 /* avoid c_setpat */ 367 el->el_state.lastcmd = (el_action_t) newdir; 368 ret = newdir == ED_SEARCH_PREV_HISTORY ? 369 ed_search_prev_history(el, 0) : 370 ed_search_next_history(el, 0); 371 if (ret != CC_ERROR) { 372 el->el_line.cursor = newdir == ED_SEARCH_PREV_HISTORY ? 373 el->el_line.lastchar : el->el_line.buffer; 374 (void) ce_search_line(el, &el->el_search.patbuf[1], 375 newdir); 376 } 377 } 378 el->el_search.patbuf[--el->el_search.patlen] = '\0'; 379 if (ret == CC_ERROR) { 380 term_beep(el); 381 if (el->el_history.eventno != ohisteventno) { 382 el->el_history.eventno = ohisteventno; 383 if (hist_get(el) == CC_ERROR) 384 return CC_ERROR; 385 } 386 el->el_line.cursor = ocursor; 387 pchar = '?'; 388 } else { 389 pchar = ':'; 390 } 391 } 392 393 ret = ce_inc_search(el, newdir); 394 395 if (ret == CC_ERROR && pchar == '?' && oldpchar == ':') 396 /* break abort of failed search at last non-failed */ 397 ret = CC_NORM; 398 399 } 400 401 if (ret == CC_NORM || (ret == CC_ERROR && oldpatlen == 0)) { 402 /* restore on normal return or error exit */ 403 pchar = oldpchar; 404 el->el_search.patlen = oldpatlen; 405 if (el->el_history.eventno != ohisteventno) { 406 el->el_history.eventno = ohisteventno; 407 if (hist_get(el) == CC_ERROR) 408 return CC_ERROR; 409 } 410 el->el_line.cursor = ocursor; 411 if (ret == CC_ERROR) 412 re_refresh(el); 413 } 414 if (done || ret != CC_NORM) 415 return ret; 416 } 417 } 418 419 420 /* cv_search(): 421 * Vi search. 422 */ 423 protected el_action_t 424 cv_search(el, dir) 425 EditLine *el; 426 int dir; 427 { 428 char ch; 429 char tmpbuf[EL_BUFSIZ]; 430 int tmplen; 431 432 tmplen = 0; 433 #ifdef ANCHOR 434 tmpbuf[tmplen++] = '.'; 435 tmpbuf[tmplen++] = '*'; 436 #endif 437 438 el->el_line.buffer[0] = '\0'; 439 el->el_line.lastchar = el->el_line.buffer; 440 el->el_line.cursor = el->el_line.buffer; 441 el->el_search.patdir = dir; 442 443 c_insert(el, 2); /* prompt + '\n' */ 444 *el->el_line.cursor++ = '\n'; 445 *el->el_line.cursor++ = dir == ED_SEARCH_PREV_HISTORY ? '/' : '?'; 446 re_refresh(el); 447 448 #ifdef ANCHOR 449 # define LEN 2 450 #else 451 # define LEN 0 452 #endif 453 454 tmplen = c_gets(el, &tmpbuf[LEN]) + LEN; 455 ch = tmpbuf[tmplen]; 456 tmpbuf[tmplen] = '\0'; 457 458 if (tmplen == LEN) { 459 /* 460 * Use the old pattern, but wild-card it. 461 */ 462 if (el->el_search.patlen == 0) { 463 el->el_line.buffer[0] = '\0'; 464 el->el_line.lastchar = el->el_line.buffer; 465 el->el_line.cursor = el->el_line.buffer; 466 re_refresh(el); 467 return CC_ERROR; 468 } 469 #ifdef ANCHOR 470 if (el->el_search.patbuf[0] != '.' && el->el_search.patbuf[0] != '*') { 471 (void)strncpy(tmpbuf, el->el_search.patbuf, sizeof(tmpbuf) - 1); 472 el->el_search.patbuf[0] = '.'; 473 el->el_search.patbuf[1] = '*'; 474 (void)strncpy(&el->el_search.patbuf[2], tmpbuf, EL_BUFSIZ - 3); 475 el->el_search.patlen++; 476 el->el_search.patbuf[el->el_search.patlen++] = '.'; 477 el->el_search.patbuf[el->el_search.patlen++] = '*'; 478 el->el_search.patbuf[el->el_search.patlen] = '\0'; 479 } 480 #endif 481 } 482 else { 483 #ifdef ANCHOR 484 tmpbuf[tmplen++] = '.'; 485 tmpbuf[tmplen++] = '*'; 486 #endif 487 tmpbuf[tmplen] = '\0'; 488 (void)strncpy(el->el_search.patbuf, tmpbuf, EL_BUFSIZ - 1); 489 el->el_search.patlen = tmplen; 490 } 491 el->el_state.lastcmd = (el_action_t) dir; /* avoid c_setpat */ 492 el->el_line.cursor = el->el_line.lastchar = el->el_line.buffer; 493 if ((dir == ED_SEARCH_PREV_HISTORY ? ed_search_prev_history(el, 0) : 494 ed_search_next_history(el, 0)) == CC_ERROR) { 495 re_refresh(el); 496 return CC_ERROR; 497 } 498 else { 499 if (ch == 0033) { 500 re_refresh(el); 501 *el->el_line.lastchar++ = '\n'; 502 *el->el_line.lastchar = '\0'; 503 re_goto_bottom(el); 504 return CC_NEWLINE; 505 } 506 else 507 return CC_REFRESH; 508 } 509 } 510 511 512 /* ce_search_line(): 513 * Look for a pattern inside a line 514 */ 515 protected el_action_t 516 ce_search_line(el, pattern, dir) 517 EditLine *el; 518 char *pattern; 519 int dir; 520 { 521 char *cp; 522 523 if (dir == ED_SEARCH_PREV_HISTORY) { 524 for (cp = el->el_line.cursor; cp >= el->el_line.buffer; cp--) 525 if (el_match(cp, pattern)) { 526 el->el_line.cursor = cp; 527 return CC_NORM; 528 } 529 return CC_ERROR; 530 } else { 531 for (cp = el->el_line.cursor; *cp != '\0' && 532 cp < el->el_line.limit; cp++) 533 if (el_match(cp, pattern)) { 534 el->el_line.cursor = cp; 535 return CC_NORM; 536 } 537 return CC_ERROR; 538 } 539 } 540 541 542 /* cv_repeat_srch(): 543 * Vi repeat search 544 */ 545 protected el_action_t 546 cv_repeat_srch(el, c) 547 EditLine *el; 548 int c; 549 { 550 #ifdef SDEBUG 551 (void) fprintf(el->el_errfile, "dir %d patlen %d patbuf %s\n", 552 c, el->el_search.patlen, el->el_search.patbuf); 553 #endif 554 555 el->el_state.lastcmd = (el_action_t) c; /* Hack to stop c_setpat */ 556 el->el_line.lastchar = el->el_line.buffer; 557 558 switch (c) { 559 case ED_SEARCH_NEXT_HISTORY: 560 return ed_search_next_history(el, 0); 561 case ED_SEARCH_PREV_HISTORY: 562 return ed_search_prev_history(el, 0); 563 default: 564 return CC_ERROR; 565 } 566 } 567 568 569 /* cv_csearch_back(): 570 * Vi character search reverse 571 */ 572 protected el_action_t 573 cv_csearch_back(el, ch, count, tflag) 574 EditLine *el; 575 int ch, count, tflag; 576 { 577 char *cp; 578 579 cp = el->el_line.cursor; 580 while (count--) { 581 if (*cp == ch) 582 cp--; 583 while (cp > el->el_line.buffer && *cp != ch) 584 cp--; 585 } 586 587 if (cp < el->el_line.buffer || (cp == el->el_line.buffer && *cp != ch)) 588 return CC_ERROR; 589 590 if (*cp == ch && tflag) 591 cp++; 592 593 el->el_line.cursor = cp; 594 595 if (el->el_chared.c_vcmd.action & DELETE) { 596 el->el_line.cursor++; 597 cv_delfini(el); 598 return CC_REFRESH; 599 } 600 601 re_refresh_cursor(el); 602 return CC_NORM; 603 } 604 605 606 /* cv_csearch_fwd(): 607 * Vi character search forward 608 */ 609 protected el_action_t 610 cv_csearch_fwd(el, ch, count, tflag) 611 EditLine *el; 612 int ch, count, tflag; 613 { 614 char *cp; 615 616 cp = el->el_line.cursor; 617 while (count--) { 618 if(*cp == ch) 619 cp++; 620 while (cp < el->el_line.lastchar && *cp != ch) 621 cp++; 622 } 623 624 if (cp >= el->el_line.lastchar) 625 return CC_ERROR; 626 627 if (*cp == ch && tflag) 628 cp--; 629 630 el->el_line.cursor = cp; 631 632 if (el->el_chared.c_vcmd.action & DELETE) { 633 el->el_line.cursor++; 634 cv_delfini(el); 635 return CC_REFRESH; 636 } 637 re_refresh_cursor(el); 638 return CC_NORM; 639 } 640