search.c revision 3285
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 regmatch_t mb; 113#else 114#ifndef REGEXP 115 extern char *re_comp __P((const char *)); 116 extern int re_exec __P((const char *)); 117#else 118 regexp *re; 119 int rv; 120#endif 121#endif 122 123 if (strstr(str, pat) != NULL) 124 return 1; 125#ifdef REGEXEC 126 if (regcomp(&re, pat, REG_EXTENDED) != 0) 127 return 0; 128 return (regexec(&re, str, 1, &mb, 0) == 0); 129#else 130#ifndef REGEXP 131 if (re_comp(pat) != NULL) 132 return 0; 133 else 134 return re_exec(str) == 1; 135#else 136 if ((re = regcomp(pat)) != NULL) { 137 rv = regexec(re, str); 138 free((ptr_t) re); 139 } 140 else 141 rv = 0; 142 return rv; 143#endif 144#endif 145} 146 147 148/* c_hmatch(): 149 * return True if the pattern matches the prefix 150 */ 151protected int 152c_hmatch(el, str) 153 EditLine *el; 154 const char *str; 155{ 156#ifdef SDEBUG 157 (void) fprintf(el->el_errfile, "match `%s' with `%s'\n", 158 el->el_search.patbuf, str); 159#endif /* SDEBUG */ 160 161 return el_match(str, el->el_search.patbuf); 162} 163 164 165/* c_setpat(): 166 * Set the history seatch pattern 167 */ 168protected void 169c_setpat(el) 170 EditLine *el; 171{ 172 if (el->el_state.lastcmd != ED_SEARCH_PREV_HISTORY && 173 el->el_state.lastcmd != ED_SEARCH_NEXT_HISTORY) { 174 el->el_search.patlen = EL_CURSOR(el) - el->el_line.buffer; 175 if (el->el_search.patlen >= EL_BUFSIZ) 176 el->el_search.patlen = EL_BUFSIZ -1; 177 if (el->el_search.patlen >= 0) { 178 (void) strncpy(el->el_search.patbuf, el->el_line.buffer, 179 el->el_search.patlen); 180 el->el_search.patbuf[el->el_search.patlen] = '\0'; 181 } 182 else 183 el->el_search.patlen = strlen(el->el_search.patbuf); 184 } 185#ifdef SDEBUG 186 (void) fprintf(el->el_errfile, "\neventno = %d\n", el->el_history.eventno); 187 (void) fprintf(el->el_errfile, "patlen = %d\n", el->el_search.patlen); 188 (void) fprintf(el->el_errfile, "patbuf = \"%s\"\n", el->el_search.patbuf); 189 (void) fprintf(el->el_errfile, "cursor %d lastchar %d\n", 190 EL_CURSOR(el) - el->el_line.buffer, 191 el->el_line.lastchar - el->el_line.buffer); 192#endif 193} 194 195 196/* ce_inc_search(): 197 * Emacs incremental search 198 */ 199protected el_action_t 200ce_inc_search(el, dir) 201 EditLine *el; 202 int dir; 203{ 204 static char STRfwd[] = { 'f', 'w', 'd', '\0' }, 205 STRbck[] = { 'b', 'c', 'k', '\0' }; 206 static char pchar = ':'; /* ':' = normal, '?' = failed */ 207 static char endcmd[2] = { '\0', '\0' }; 208 char ch, *cp, *ocursor = el->el_line.cursor, oldpchar = pchar; 209 210 el_action_t ret = CC_NORM; 211 212 int ohisteventno = el->el_history.eventno, 213 oldpatlen = el->el_search.patlen, 214 newdir = dir, 215 done, redo; 216 217 if (el->el_line.lastchar + sizeof(STRfwd) / sizeof(char) + 2 + 218 el->el_search.patlen >= el->el_line.limit) 219 return CC_ERROR; 220 221 for (;;) { 222 223 if (el->el_search.patlen == 0) { /* first round */ 224 pchar = ':'; 225#ifdef ANCHOR 226 el->el_search.patbuf[el->el_search.patlen++] = '.'; 227 el->el_search.patbuf[el->el_search.patlen++] = '*'; 228#endif 229 } 230 done = redo = 0; 231 *el->el_line.lastchar++ = '\n'; 232 for (cp = newdir == ED_SEARCH_PREV_HISTORY ? STRbck : STRfwd; 233 *cp; *el->el_line.lastchar++ = *cp++) 234 continue; 235 *el->el_line.lastchar++ = pchar; 236 for (cp = &el->el_search.patbuf[1]; 237 cp < &el->el_search.patbuf[el->el_search.patlen]; 238 *el->el_line.lastchar++ = *cp++) 239 continue; 240 *el->el_line.lastchar = '\0'; 241 re_refresh(el); 242 243 if (el_getc(el, &ch) != 1) 244 return ed_end_of_file(el, 0); 245 246 switch (el->el_map.current[(unsigned char) ch]) { 247 case ED_INSERT: 248 case ED_DIGIT: 249 if (el->el_search.patlen > EL_BUFSIZ - 3) 250 term_beep(el); 251 else { 252 el->el_search.patbuf[el->el_search.patlen++] = ch; 253 *el->el_line.lastchar++ = ch; 254 *el->el_line.lastchar = '\0'; 255 re_refresh(el); 256 } 257 break; 258 259 case EM_INC_SEARCH_NEXT: 260 newdir = ED_SEARCH_NEXT_HISTORY; 261 redo++; 262 break; 263 264 case EM_INC_SEARCH_PREV: 265 newdir = ED_SEARCH_PREV_HISTORY; 266 redo++; 267 break; 268 269 case ED_DELETE_PREV_CHAR: 270 if (el->el_search.patlen > 1) 271 done++; 272 else 273 term_beep(el); 274 break; 275 276 default: 277 switch (ch) { 278 case 0007: /* ^G: Abort */ 279 ret = CC_ERROR; 280 done++; 281 break; 282 283 case 0027: /* ^W: Append word */ 284 /* No can do if globbing characters in pattern */ 285 for (cp = &el->el_search.patbuf[1]; ; cp++) 286 if (cp >= &el->el_search.patbuf[el->el_search.patlen]) { 287 el->el_line.cursor += el->el_search.patlen - 1; 288 cp = c__next_word(el->el_line.cursor, 289 el->el_line.lastchar, 1, ce__isword); 290 while (el->el_line.cursor < cp && 291 *el->el_line.cursor != '\n') { 292 if (el->el_search.patlen > EL_BUFSIZ - 3) { 293 term_beep(el); 294 break; 295 } 296 el->el_search.patbuf[el->el_search.patlen++] = 297 *el->el_line.cursor; 298 *el->el_line.lastchar++ = *el->el_line.cursor++; 299 } 300 el->el_line.cursor = ocursor; 301 *el->el_line.lastchar = '\0'; 302 re_refresh(el); 303 break; 304 } else if (isglob(*cp)) { 305 term_beep(el); 306 break; 307 } 308 break; 309 310 default: /* Terminate and execute cmd */ 311 endcmd[0] = ch; 312 el_push(el, endcmd); 313 /*FALLTHROUGH*/ 314 315 case 0033: /* ESC: Terminate */ 316 ret = CC_REFRESH; 317 done++; 318 break; 319 } 320 break; 321 } 322 323 while (el->el_line.lastchar > el->el_line.buffer && 324 *el->el_line.lastchar != '\n') 325 *el->el_line.lastchar-- = '\0'; 326 *el->el_line.lastchar = '\0'; 327 328 if (!done) { 329 330 /* Can't search if unmatched '[' */ 331 for (cp = &el->el_search.patbuf[el->el_search.patlen-1], ch = ']'; 332 cp > el->el_search.patbuf; cp--) 333 if (*cp == '[' || *cp == ']') { 334 ch = *cp; 335 break; 336 } 337 338 if (el->el_search.patlen > 1 && ch != '[') { 339 if (redo && newdir == dir) { 340 if (pchar == '?') { /* wrap around */ 341 el->el_history.eventno = 342 newdir == ED_SEARCH_PREV_HISTORY ? 0 : 0x7fffffff; 343 if (hist_get(el) == CC_ERROR) 344 /* el->el_history.eventno was fixed by first call */ 345 (void) hist_get(el); 346 el->el_line.cursor = newdir == ED_SEARCH_PREV_HISTORY ? 347 el->el_line.lastchar : el->el_line.buffer; 348 } else 349 el->el_line.cursor += 350 newdir == ED_SEARCH_PREV_HISTORY ? -1 : 1; 351 } 352#ifdef ANCHOR 353 el->el_search.patbuf[el->el_search.patlen++] = '.'; 354 el->el_search.patbuf[el->el_search.patlen++] = '*'; 355#endif 356 el->el_search.patbuf[el->el_search.patlen] = '\0'; 357 if (el->el_line.cursor < el->el_line.buffer || 358 el->el_line.cursor > el->el_line.lastchar || 359 (ret = ce_search_line(el, &el->el_search.patbuf[1], 360 newdir)) == CC_ERROR) { 361 /* avoid c_setpat */ 362 el->el_state.lastcmd = (el_action_t) newdir; 363 ret = newdir == ED_SEARCH_PREV_HISTORY ? 364 ed_search_prev_history(el, 0) : 365 ed_search_next_history(el, 0); 366 if (ret != CC_ERROR) { 367 el->el_line.cursor = newdir == ED_SEARCH_PREV_HISTORY ? 368 el->el_line.lastchar : el->el_line.buffer; 369 (void) ce_search_line(el, &el->el_search.patbuf[1], 370 newdir); 371 } 372 } 373 el->el_search.patbuf[--el->el_search.patlen] = '\0'; 374 if (ret == CC_ERROR) { 375 term_beep(el); 376 if (el->el_history.eventno != ohisteventno) { 377 el->el_history.eventno = ohisteventno; 378 if (hist_get(el) == CC_ERROR) 379 return CC_ERROR; 380 } 381 el->el_line.cursor = ocursor; 382 pchar = '?'; 383 } else { 384 pchar = ':'; 385 } 386 } 387 388 ret = ce_inc_search(el, newdir); 389 390 if (ret == CC_ERROR && pchar == '?' && oldpchar == ':') 391 /* break abort of failed search at last non-failed */ 392 ret = CC_NORM; 393 394 } 395 396 if (ret == CC_NORM || (ret == CC_ERROR && oldpatlen == 0)) { 397 /* restore on normal return or error exit */ 398 pchar = oldpchar; 399 el->el_search.patlen = oldpatlen; 400 if (el->el_history.eventno != ohisteventno) { 401 el->el_history.eventno = ohisteventno; 402 if (hist_get(el) == CC_ERROR) 403 return CC_ERROR; 404 } 405 el->el_line.cursor = ocursor; 406 if (ret == CC_ERROR) 407 re_refresh(el); 408 } 409 if (done || ret != CC_NORM) 410 return ret; 411 } 412} 413 414 415/* cv_search(): 416 * Vi search. 417 */ 418protected el_action_t 419cv_search(el, dir) 420 EditLine *el; 421 int dir; 422{ 423 char ch; 424 char tmpbuf[EL_BUFSIZ]; 425 int tmplen; 426 427 tmplen = 0; 428#ifdef ANCHOR 429 tmpbuf[tmplen++] = '.'; 430 tmpbuf[tmplen++] = '*'; 431#endif 432 433 el->el_line.buffer[0] = '\0'; 434 el->el_line.lastchar = el->el_line.buffer; 435 el->el_line.cursor = el->el_line.buffer; 436 el->el_search.patdir = dir; 437 438 c_insert(el, 2); /* prompt + '\n' */ 439 *el->el_line.cursor++ = '\n'; 440 *el->el_line.cursor++ = dir == ED_SEARCH_PREV_HISTORY ? '/' : '?'; 441 re_refresh(el); 442 443#ifdef ANCHOR 444# define LEN 2 445#else 446# define LEN 0 447#endif 448 449 tmplen = c_gets(el, &tmpbuf[LEN]) + LEN; 450 ch = tmpbuf[tmplen]; 451 tmpbuf[tmplen] = '\0'; 452 453 if (tmplen == LEN) { 454 /* 455 * Use the old pattern, but wild-card it. 456 */ 457 if (el->el_search.patlen == 0) { 458 el->el_line.buffer[0] = '\0'; 459 el->el_line.lastchar = el->el_line.buffer; 460 el->el_line.cursor = el->el_line.buffer; 461 re_refresh(el); 462 return CC_ERROR; 463 } 464#ifdef ANCHOR 465 if (el->el_search.patbuf[0] != '.' && el->el_search.patbuf[0] != '*') { 466 (void) strcpy(tmpbuf, el->el_search.patbuf); 467 el->el_search.patbuf[0] = '.'; 468 el->el_search.patbuf[1] = '*'; 469 (void) strcpy(&el->el_search.patbuf[2], tmpbuf); 470 el->el_search.patlen++; 471 el->el_search.patbuf[el->el_search.patlen++] = '.'; 472 el->el_search.patbuf[el->el_search.patlen++] = '*'; 473 el->el_search.patbuf[el->el_search.patlen] = '\0'; 474 } 475#endif 476 } 477 else { 478#ifdef ANCHOR 479 tmpbuf[tmplen++] = '.'; 480 tmpbuf[tmplen++] = '*'; 481#endif 482 tmpbuf[tmplen] = '\0'; 483 (void) strcpy(el->el_search.patbuf, tmpbuf); 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