00001 /* $NetBSD: search.c,v 1.20 2004/11/04 01:16:03 christos Exp $ */ 00002 00003 /*- 00004 * Copyright (c) 1992, 1993 00005 * The Regents of the University of California. All rights reserved. 00006 * 00007 * This code is derived from software contributed to Berkeley by 00008 * Christos Zoulas of Cornell University. 00009 * 00010 * Redistribution and use in source and binary forms, with or without 00011 * modification, are permitted provided that the following conditions 00012 * are met: 00013 * 1. Redistributions of source code must retain the above copyright 00014 * notice, this list of conditions and the following disclaimer. 00015 * 2. Redistributions in binary form must reproduce the above copyright 00016 * notice, this list of conditions and the following disclaimer in the 00017 * documentation and/or other materials provided with the distribution. 00018 * 3. Neither the name of the University nor the names of its contributors 00019 * may be used to endorse or promote products derived from this software 00020 * without specific prior written permission. 00021 * 00022 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 00023 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 00024 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 00025 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 00026 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 00027 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 00028 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 00029 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 00030 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 00031 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 00032 * SUCH DAMAGE. 00033 */ 00034 00035 #include <config.h> 00036 00037 /* 00038 * search.c: History and character search functions 00039 */ 00040 #include <sys/types.h> 00041 #include <stdlib.h> 00042 #if defined(REGEX) 00043 #include <regex.h> 00044 #elif defined(REGEXP) 00045 #include <regexp.h> 00046 #endif 00047 #include "el.h" 00048 00049 /* 00050 * Adjust cursor in vi mode to include the character under it 00051 */ 00052 #define EL_CURSOR(el) \ 00053 ((el)->el_line.cursor + (((el)->el_map.type == MAP_VI) && \ 00054 ((el)->el_map.current == (el)->el_map.alt))) 00055 00056 /* search_init(): 00057 * Initialize the search stuff 00058 */ 00059 protected int 00060 search_init(EditLine *el) 00061 { 00062 00063 el->el_search.patbuf = (char *) el_malloc(EL_BUFSIZ); 00064 if (el->el_search.patbuf == NULL) 00065 return (-1); 00066 el->el_search.patlen = 0; 00067 el->el_search.patdir = -1; 00068 el->el_search.chacha = '\0'; 00069 el->el_search.chadir = CHAR_FWD; 00070 el->el_search.chatflg = 0; 00071 return (0); 00072 } 00073 00074 00075 /* search_end(): 00076 * Initialize the search stuff 00077 */ 00078 protected void 00079 search_end(EditLine *el) 00080 { 00081 00082 el_free((ptr_t) el->el_search.patbuf); 00083 el->el_search.patbuf = NULL; 00084 } 00085 00086 00087 #ifdef REGEXP 00088 /* regerror(): 00089 * Handle regular expression errors 00090 */ 00091 public void 00092 /*ARGSUSED*/ 00093 regerror(const char *msg) 00094 { 00095 } 00096 #endif 00097 00098 00099 /* el_match(): 00100 * Return if string matches pattern 00101 */ 00102 protected int 00103 el_match(const char *str, const char *pat) 00104 { 00105 #if defined (REGEX) 00106 regex_t re; 00107 int rv; 00108 #elif defined (REGEXP) 00109 regexp *rp; 00110 int rv; 00111 #else 00112 extern char *re_comp(const char *); 00113 extern int re_exec(const char *); 00114 #endif 00115 00116 if (strstr(str, pat) != NULL) 00117 return (1); 00118 00119 #if defined(REGEX) 00120 if (regcomp(&re, pat, 0) == 0) { 00121 rv = regexec(&re, str, 0, NULL, 0) == 0; 00122 regfree(&re); 00123 } else { 00124 rv = 0; 00125 } 00126 return (rv); 00127 #elif defined(REGEXP) 00128 if ((re = regcomp(pat)) != NULL) { 00129 rv = regexec(re, str); 00130 free((ptr_t) re); 00131 } else { 00132 rv = 0; 00133 } 00134 return (rv); 00135 #else 00136 if (re_comp(pat) != NULL) 00137 return (0); 00138 else 00139 return (re_exec(str) == 1); 00140 #endif 00141 } 00142 00143 00144 /* c_hmatch(): 00145 * return True if the pattern matches the prefix 00146 */ 00147 protected int 00148 c_hmatch(EditLine *el, const char *str) 00149 { 00150 #ifdef SDEBUG 00151 (void) fprintf(el->el_errfile, "match `%s' with `%s'\n", 00152 el->el_search.patbuf, str); 00153 #endif /* SDEBUG */ 00154 00155 return (el_match(str, el->el_search.patbuf)); 00156 } 00157 00158 00159 /* c_setpat(): 00160 * Set the history seatch pattern 00161 */ 00162 protected void 00163 c_setpat(EditLine *el) 00164 { 00165 if (el->el_state.lastcmd != ED_SEARCH_PREV_HISTORY && 00166 el->el_state.lastcmd != ED_SEARCH_NEXT_HISTORY) { 00167 el->el_search.patlen = EL_CURSOR(el) - el->el_line.buffer; 00168 if (el->el_search.patlen >= EL_BUFSIZ) 00169 el->el_search.patlen = EL_BUFSIZ - 1; 00170 if (el->el_search.patlen != 0) { 00171 (void) strncpy(el->el_search.patbuf, el->el_line.buffer, 00172 el->el_search.patlen); 00173 el->el_search.patbuf[el->el_search.patlen] = '\0'; 00174 } else 00175 el->el_search.patlen = strlen(el->el_search.patbuf); 00176 } 00177 #ifdef SDEBUG 00178 (void) fprintf(el->el_errfile, "\neventno = %d\n", 00179 el->el_history.eventno); 00180 (void) fprintf(el->el_errfile, "patlen = %d\n", el->el_search.patlen); 00181 (void) fprintf(el->el_errfile, "patbuf = \"%s\"\n", 00182 el->el_search.patbuf); 00183 (void) fprintf(el->el_errfile, "cursor %d lastchar %d\n", 00184 EL_CURSOR(el) - el->el_line.buffer, 00185 el->el_line.lastchar - el->el_line.buffer); 00186 #endif 00187 } 00188 00189 00190 /* ce_inc_search(): 00191 * Emacs incremental search 00192 */ 00193 protected el_action_t 00194 ce_inc_search(EditLine *el, int dir) 00195 { 00196 static const char STRfwd[] = {'f', 'w', 'd', '\0'}, 00197 STRbck[] = {'b', 'c', 'k', '\0'}; 00198 static char pchar = ':';/* ':' = normal, '?' = failed */ 00199 static char endcmd[2] = {'\0', '\0'}; 00200 char ch, *ocursor = el->el_line.cursor, oldpchar = pchar; 00201 const char *cp; 00202 00203 el_action_t ret = CC_NORM; 00204 00205 int ohisteventno = el->el_history.eventno; 00206 int oldpatlen = el->el_search.patlen; 00207 int newdir = dir; 00208 int done, redo; 00209 00210 if (el->el_line.lastchar + sizeof(STRfwd) / sizeof(char) + 2 + 00211 el->el_search.patlen >= el->el_line.limit) 00212 return (CC_ERROR); 00213 00214 for (;;) { 00215 00216 if (el->el_search.patlen == 0) { /* first round */ 00217 pchar = ':'; 00218 #ifdef ANCHOR 00219 #define LEN 2 00220 el->el_search.patbuf[el->el_search.patlen++] = '.'; 00221 el->el_search.patbuf[el->el_search.patlen++] = '*'; 00222 #else 00223 #define LEN 0 00224 #endif 00225 } 00226 done = redo = 0; 00227 *el->el_line.lastchar++ = '\n'; 00228 for (cp = (newdir == ED_SEARCH_PREV_HISTORY) ? STRbck : STRfwd; 00229 *cp; *el->el_line.lastchar++ = *cp++) 00230 continue; 00231 *el->el_line.lastchar++ = pchar; 00232 for (cp = &el->el_search.patbuf[LEN]; 00233 cp < &el->el_search.patbuf[el->el_search.patlen]; 00234 *el->el_line.lastchar++ = *cp++) 00235 continue; 00236 *el->el_line.lastchar = '\0'; 00237 re_refresh(el); 00238 00239 if (el_getc(el, &ch) != 1) 00240 return (ed_end_of_file(el, 0)); 00241 00242 switch (el->el_map.current[(unsigned char) ch]) { 00243 case ED_INSERT: 00244 case ED_DIGIT: 00245 if (el->el_search.patlen >= EL_BUFSIZ - LEN) 00246 term_beep(el); 00247 else { 00248 el->el_search.patbuf[el->el_search.patlen++] = 00249 ch; 00250 *el->el_line.lastchar++ = ch; 00251 *el->el_line.lastchar = '\0'; 00252 re_refresh(el); 00253 } 00254 break; 00255 00256 case EM_INC_SEARCH_NEXT: 00257 newdir = ED_SEARCH_NEXT_HISTORY; 00258 redo++; 00259 break; 00260 00261 case EM_INC_SEARCH_PREV: 00262 newdir = ED_SEARCH_PREV_HISTORY; 00263 redo++; 00264 break; 00265 00266 case EM_DELETE_PREV_CHAR: 00267 case ED_DELETE_PREV_CHAR: 00268 if (el->el_search.patlen > LEN) 00269 done++; 00270 else 00271 term_beep(el); 00272 break; 00273 00274 default: 00275 switch (ch) { 00276 case 0007: /* ^G: Abort */ 00277 ret = CC_ERROR; 00278 done++; 00279 break; 00280 00281 case 0027: /* ^W: Append word */ 00282 /* No can do if globbing characters in pattern */ 00283 for (cp = &el->el_search.patbuf[LEN];; cp++) 00284 if (cp >= &el->el_search.patbuf[ 00285 el->el_search.patlen]) { 00286 el->el_line.cursor += 00287 el->el_search.patlen - LEN - 1; 00288 cp = c__next_word(el->el_line.cursor, 00289 el->el_line.lastchar, 1, 00290 ce__isword); 00291 while (el->el_line.cursor < cp && 00292 *el->el_line.cursor != '\n') { 00293 if (el->el_search.patlen >= 00294 EL_BUFSIZ - LEN) { 00295 term_beep(el); 00296 break; 00297 } 00298 el->el_search.patbuf[el->el_search.patlen++] = 00299 *el->el_line.cursor; 00300 *el->el_line.lastchar++ = 00301 *el->el_line.cursor++; 00302 } 00303 el->el_line.cursor = ocursor; 00304 *el->el_line.lastchar = '\0'; 00305 re_refresh(el); 00306 break; 00307 } else if (isglob(*cp)) { 00308 term_beep(el); 00309 break; 00310 } 00311 break; 00312 00313 default: /* Terminate and execute cmd */ 00314 endcmd[0] = ch; 00315 el_push(el, endcmd); 00316 /* FALLTHROUGH */ 00317 00318 case 0033: /* ESC: Terminate */ 00319 ret = CC_REFRESH; 00320 done++; 00321 break; 00322 } 00323 break; 00324 } 00325 00326 while (el->el_line.lastchar > el->el_line.buffer && 00327 *el->el_line.lastchar != '\n') 00328 *el->el_line.lastchar-- = '\0'; 00329 *el->el_line.lastchar = '\0'; 00330 00331 if (!done) { 00332 00333 /* Can't search if unmatched '[' */ 00334 for (cp = &el->el_search.patbuf[el->el_search.patlen-1], 00335 ch = ']'; 00336 cp >= &el->el_search.patbuf[LEN]; 00337 cp--) 00338 if (*cp == '[' || *cp == ']') { 00339 ch = *cp; 00340 break; 00341 } 00342 if (el->el_search.patlen > LEN && ch != '[') { 00343 if (redo && newdir == dir) { 00344 if (pchar == '?') { /* wrap around */ 00345 el->el_history.eventno = 00346 newdir == ED_SEARCH_PREV_HISTORY ? 0 : 0x7fffffff; 00347 if (hist_get(el) == CC_ERROR) 00348 /* el->el_history.event 00349 * no was fixed by 00350 * first call */ 00351 (void) hist_get(el); 00352 el->el_line.cursor = newdir == 00353 ED_SEARCH_PREV_HISTORY ? 00354 el->el_line.lastchar : 00355 el->el_line.buffer; 00356 } else 00357 el->el_line.cursor += 00358 newdir == 00359 ED_SEARCH_PREV_HISTORY ? 00360 -1 : 1; 00361 } 00362 #ifdef ANCHOR 00363 el->el_search.patbuf[el->el_search.patlen++] = 00364 '.'; 00365 el->el_search.patbuf[el->el_search.patlen++] = 00366 '*'; 00367 #endif 00368 el->el_search.patbuf[el->el_search.patlen] = 00369 '\0'; 00370 if (el->el_line.cursor < el->el_line.buffer || 00371 el->el_line.cursor > el->el_line.lastchar || 00372 (ret = ce_search_line(el, newdir)) 00373 == CC_ERROR) { 00374 /* avoid c_setpat */ 00375 el->el_state.lastcmd = 00376 (el_action_t) newdir; 00377 ret = newdir == ED_SEARCH_PREV_HISTORY ? 00378 ed_search_prev_history(el, 0) : 00379 ed_search_next_history(el, 0); 00380 if (ret != CC_ERROR) { 00381 el->el_line.cursor = newdir == 00382 ED_SEARCH_PREV_HISTORY ? 00383 el->el_line.lastchar : 00384 el->el_line.buffer; 00385 (void) ce_search_line(el, 00386 newdir); 00387 } 00388 } 00389 el->el_search.patlen -= LEN; 00390 el->el_search.patbuf[el->el_search.patlen] = 00391 '\0'; 00392 if (ret == CC_ERROR) { 00393 term_beep(el); 00394 if (el->el_history.eventno != 00395 ohisteventno) { 00396 el->el_history.eventno = 00397 ohisteventno; 00398 if (hist_get(el) == CC_ERROR) 00399 return (CC_ERROR); 00400 } 00401 el->el_line.cursor = ocursor; 00402 pchar = '?'; 00403 } else { 00404 pchar = ':'; 00405 } 00406 } 00407 ret = ce_inc_search(el, newdir); 00408 00409 if (ret == CC_ERROR && pchar == '?' && oldpchar == ':') 00410 /* 00411 * break abort of failed search at last 00412 * non-failed 00413 */ 00414 ret = CC_NORM; 00415 00416 } 00417 if (ret == CC_NORM || (ret == CC_ERROR && oldpatlen == 0)) { 00418 /* restore on normal return or error exit */ 00419 pchar = oldpchar; 00420 el->el_search.patlen = oldpatlen; 00421 if (el->el_history.eventno != ohisteventno) { 00422 el->el_history.eventno = ohisteventno; 00423 if (hist_get(el) == CC_ERROR) 00424 return (CC_ERROR); 00425 } 00426 el->el_line.cursor = ocursor; 00427 if (ret == CC_ERROR) 00428 re_refresh(el); 00429 } 00430 if (done || ret != CC_NORM) 00431 return (ret); 00432 } 00433 } 00434 00435 00436 /* cv_search(): 00437 * Vi search. 00438 */ 00439 protected el_action_t 00440 cv_search(EditLine *el, int dir) 00441 { 00442 char ch; 00443 char tmpbuf[EL_BUFSIZ]; 00444 int tmplen; 00445 00446 #ifdef ANCHOR 00447 tmpbuf[0] = '.'; 00448 tmpbuf[1] = '*'; 00449 #endif 00450 tmplen = LEN; 00451 00452 el->el_search.patdir = dir; 00453 00454 tmplen = c_gets(el, &tmpbuf[LEN], 00455 dir == ED_SEARCH_PREV_HISTORY ? "\n/" : "\n?" ); 00456 if (tmplen == -1) 00457 return CC_REFRESH; 00458 00459 tmplen += LEN; 00460 ch = tmpbuf[tmplen]; 00461 tmpbuf[tmplen] = '\0'; 00462 00463 if (tmplen == LEN) { 00464 /* 00465 * Use the old pattern, but wild-card it. 00466 */ 00467 if (el->el_search.patlen == 0) { 00468 re_refresh(el); 00469 return (CC_ERROR); 00470 } 00471 #ifdef ANCHOR 00472 if (el->el_search.patbuf[0] != '.' && 00473 el->el_search.patbuf[0] != '*') { 00474 (void) strncpy(tmpbuf, el->el_search.patbuf, 00475 sizeof(tmpbuf) - 1); 00476 el->el_search.patbuf[0] = '.'; 00477 el->el_search.patbuf[1] = '*'; 00478 (void) strncpy(&el->el_search.patbuf[2], tmpbuf, 00479 EL_BUFSIZ - 3); 00480 el->el_search.patlen++; 00481 el->el_search.patbuf[el->el_search.patlen++] = '.'; 00482 el->el_search.patbuf[el->el_search.patlen++] = '*'; 00483 el->el_search.patbuf[el->el_search.patlen] = '\0'; 00484 } 00485 #endif 00486 } else { 00487 #ifdef ANCHOR 00488 tmpbuf[tmplen++] = '.'; 00489 tmpbuf[tmplen++] = '*'; 00490 #endif 00491 tmpbuf[tmplen] = '\0'; 00492 (void) strncpy(el->el_search.patbuf, tmpbuf, EL_BUFSIZ - 1); 00493 el->el_search.patlen = tmplen; 00494 } 00495 el->el_state.lastcmd = (el_action_t) dir; /* avoid c_setpat */ 00496 el->el_line.cursor = el->el_line.lastchar = el->el_line.buffer; 00497 if ((dir == ED_SEARCH_PREV_HISTORY ? ed_search_prev_history(el, 0) : 00498 ed_search_next_history(el, 0)) == CC_ERROR) { 00499 re_refresh(el); 00500 return (CC_ERROR); 00501 } 00502 if (ch == 0033) { 00503 re_refresh(el); 00504 return ed_newline(el, 0); 00505 } 00506 return (CC_REFRESH); 00507 } 00508 00509 00510 /* ce_search_line(): 00511 * Look for a pattern inside a line 00512 */ 00513 protected el_action_t 00514 ce_search_line(EditLine *el, int dir) 00515 { 00516 char *cp = el->el_line.cursor; 00517 char *pattern = el->el_search.patbuf; 00518 char oc, *ocp; 00519 #ifdef ANCHOR 00520 ocp = &pattern[1]; 00521 oc = *ocp; 00522 *ocp = '^'; 00523 #else 00524 ocp = pattern; 00525 oc = *ocp; 00526 #endif 00527 00528 if (dir == ED_SEARCH_PREV_HISTORY) { 00529 for (; cp >= el->el_line.buffer; cp--) { 00530 if (el_match(cp, ocp)) { 00531 *ocp = oc; 00532 el->el_line.cursor = cp; 00533 return (CC_NORM); 00534 } 00535 } 00536 *ocp = oc; 00537 return (CC_ERROR); 00538 } else { 00539 for (; *cp != '\0' && cp < el->el_line.limit; cp++) { 00540 if (el_match(cp, ocp)) { 00541 *ocp = oc; 00542 el->el_line.cursor = cp; 00543 return (CC_NORM); 00544 } 00545 } 00546 *ocp = oc; 00547 return (CC_ERROR); 00548 } 00549 } 00550 00551 00552 /* cv_repeat_srch(): 00553 * Vi repeat search 00554 */ 00555 protected el_action_t 00556 cv_repeat_srch(EditLine *el, int c) 00557 { 00558 00559 #ifdef SDEBUG 00560 (void) fprintf(el->el_errfile, "dir %d patlen %d patbuf %s\n", 00561 c, el->el_search.patlen, el->el_search.patbuf); 00562 #endif 00563 00564 el->el_state.lastcmd = (el_action_t) c; /* Hack to stop c_setpat */ 00565 el->el_line.lastchar = el->el_line.buffer; 00566 00567 switch (c) { 00568 case ED_SEARCH_NEXT_HISTORY: 00569 return (ed_search_next_history(el, 0)); 00570 case ED_SEARCH_PREV_HISTORY: 00571 return (ed_search_prev_history(el, 0)); 00572 default: 00573 return (CC_ERROR); 00574 } 00575 } 00576 00577 00578 /* cv_csearch(): 00579 * Vi character search 00580 */ 00581 protected el_action_t 00582 cv_csearch(EditLine *el, int direction, int ch, int count, int tflag) 00583 { 00584 char *cp; 00585 00586 if (ch == 0) 00587 return CC_ERROR; 00588 00589 if (ch == -1) { 00590 char c; 00591 if (el_getc(el, &c) != 1) 00592 return ed_end_of_file(el, 0); 00593 ch = c; 00594 } 00595 00596 /* Save for ';' and ',' commands */ 00597 el->el_search.chacha = ch; 00598 el->el_search.chadir = direction; 00599 el->el_search.chatflg = tflag; 00600 00601 cp = el->el_line.cursor; 00602 while (count--) { 00603 if (*cp == ch) 00604 cp += direction; 00605 for (;;cp += direction) { 00606 if (cp >= el->el_line.lastchar) 00607 return CC_ERROR; 00608 if (cp < el->el_line.buffer) 00609 return CC_ERROR; 00610 if (*cp == ch) 00611 break; 00612 } 00613 } 00614 00615 if (tflag) 00616 cp -= direction; 00617 00618 el->el_line.cursor = cp; 00619 00620 if (el->el_chared.c_vcmd.action != NOP) { 00621 if (direction > 0) 00622 el->el_line.cursor++; 00623 cv_delfini(el); 00624 return CC_REFRESH; 00625 } 00626 return CC_CURSOR; 00627 }
1.4.7

