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