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