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