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