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