PageRenderTime 48ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/nbsd_libedit/search.c

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