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