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