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