search.c 16 KB

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