xref: /dflybsd-src/contrib/nvi2/common/search.c (revision 07bc39c2f4bbca56f12568e06d89da17f2eeb965)
1e0b8e63eSJohn Marino /*-
2e0b8e63eSJohn Marino  * Copyright (c) 1992, 1993, 1994
3e0b8e63eSJohn Marino  *	The Regents of the University of California.  All rights reserved.
4e0b8e63eSJohn Marino  * Copyright (c) 1992, 1993, 1994, 1995, 1996
5e0b8e63eSJohn Marino  *	Keith Bostic.  All rights reserved.
6e0b8e63eSJohn Marino  *
7e0b8e63eSJohn Marino  * See the LICENSE file for redistribution information.
8e0b8e63eSJohn Marino  */
9e0b8e63eSJohn Marino 
10e0b8e63eSJohn Marino #include "config.h"
11e0b8e63eSJohn Marino 
12e0b8e63eSJohn Marino #include <sys/types.h>
13e0b8e63eSJohn Marino #include <sys/queue.h>
14e0b8e63eSJohn Marino #include <sys/time.h>
15e0b8e63eSJohn Marino 
16e0b8e63eSJohn Marino #include <bitstring.h>
17e0b8e63eSJohn Marino #include <ctype.h>
18e0b8e63eSJohn Marino #include <errno.h>
19e0b8e63eSJohn Marino #include <limits.h>
20e0b8e63eSJohn Marino #include <stdio.h>
21e0b8e63eSJohn Marino #include <stdlib.h>
22e0b8e63eSJohn Marino #include <string.h>
23e0b8e63eSJohn Marino #include <unistd.h>
24e0b8e63eSJohn Marino 
25e0b8e63eSJohn Marino #include "common.h"
26e0b8e63eSJohn Marino 
27e0b8e63eSJohn Marino typedef enum { S_EMPTY, S_EOF, S_NOPREV, S_NOTFOUND, S_SOF, S_WRAP } smsg_t;
28e0b8e63eSJohn Marino 
29e0b8e63eSJohn Marino static void	search_msg(SCR *, smsg_t);
30e0b8e63eSJohn Marino static int	search_init(SCR *, dir_t, CHAR_T *, size_t, CHAR_T **, u_int);
31e0b8e63eSJohn Marino 
32e0b8e63eSJohn Marino /*
33e0b8e63eSJohn Marino  * search_init --
34e0b8e63eSJohn Marino  *	Set up a search.
35e0b8e63eSJohn Marino  */
36e0b8e63eSJohn Marino static int
search_init(SCR * sp,dir_t dir,CHAR_T * ptrn,size_t plen,CHAR_T ** epp,u_int flags)37*b1ac2ebbSDaniel Fojt search_init(SCR *sp, dir_t dir, CHAR_T *ptrn, size_t plen, CHAR_T **epp,
38e0b8e63eSJohn Marino     u_int flags)
39e0b8e63eSJohn Marino {
40e0b8e63eSJohn Marino 	recno_t lno;
41e0b8e63eSJohn Marino 	int delim;
42e0b8e63eSJohn Marino 	CHAR_T *p, *t;
43e0b8e63eSJohn Marino 
44e0b8e63eSJohn Marino 	/* If the file is empty, it's a fast search. */
45e0b8e63eSJohn Marino 	if (sp->lno <= 1) {
46e0b8e63eSJohn Marino 		if (db_last(sp, &lno))
47e0b8e63eSJohn Marino 			return (1);
48e0b8e63eSJohn Marino 		if (lno == 0) {
49e0b8e63eSJohn Marino 			if (LF_ISSET(SEARCH_MSG))
50e0b8e63eSJohn Marino 				search_msg(sp, S_EMPTY);
51e0b8e63eSJohn Marino 			return (1);
52e0b8e63eSJohn Marino 		}
53e0b8e63eSJohn Marino 	}
54e0b8e63eSJohn Marino 
55e0b8e63eSJohn Marino 	if (LF_ISSET(SEARCH_PARSE)) {		/* Parse the string. */
56e0b8e63eSJohn Marino 		/*
57e0b8e63eSJohn Marino 		 * Use the saved pattern if no pattern specified, or if only
58e0b8e63eSJohn Marino 		 * one or two delimiter characters specified.
59e0b8e63eSJohn Marino 		 *
60e0b8e63eSJohn Marino 		 * !!!
61e0b8e63eSJohn Marino 		 * Historically, only the pattern itself was saved, vi didn't
62e0b8e63eSJohn Marino 		 * preserve addressing or delta information.
63e0b8e63eSJohn Marino 		 */
64e0b8e63eSJohn Marino 		if (ptrn == NULL)
65e0b8e63eSJohn Marino 			goto prev;
66e0b8e63eSJohn Marino 		if (plen == 1) {
67e0b8e63eSJohn Marino 			if (epp != NULL)
68e0b8e63eSJohn Marino 				*epp = ptrn + 1;
69e0b8e63eSJohn Marino 			goto prev;
70e0b8e63eSJohn Marino 		}
71e0b8e63eSJohn Marino 		if (ptrn[0] == ptrn[1]) {
72e0b8e63eSJohn Marino 			if (epp != NULL)
73e0b8e63eSJohn Marino 				*epp = ptrn + 2;
74e0b8e63eSJohn Marino 
75e0b8e63eSJohn Marino 			/* Complain if we don't have a previous pattern. */
76e0b8e63eSJohn Marino prev:			if (sp->re == NULL) {
77e0b8e63eSJohn Marino 				search_msg(sp, S_NOPREV);
78e0b8e63eSJohn Marino 				return (1);
79e0b8e63eSJohn Marino 			}
80e0b8e63eSJohn Marino 			/* Re-compile the search pattern if necessary. */
81e0b8e63eSJohn Marino 			if (!F_ISSET(sp, SC_RE_SEARCH) && re_compile(sp,
82e0b8e63eSJohn Marino 			    sp->re, sp->re_len, NULL, NULL, &sp->re_c,
83e0b8e63eSJohn Marino 			    RE_C_SEARCH |
84e0b8e63eSJohn Marino 			    (LF_ISSET(SEARCH_MSG) ? 0 : RE_C_SILENT)))
85e0b8e63eSJohn Marino 				return (1);
86e0b8e63eSJohn Marino 
87e0b8e63eSJohn Marino 			/* Set the search direction. */
88e0b8e63eSJohn Marino 			if (LF_ISSET(SEARCH_SET))
89e0b8e63eSJohn Marino 				sp->searchdir = dir;
90e0b8e63eSJohn Marino 			return (0);
91e0b8e63eSJohn Marino 		}
92e0b8e63eSJohn Marino 
93e0b8e63eSJohn Marino 		/*
94e0b8e63eSJohn Marino 		 * Set the delimiter, and move forward to the terminating
95e0b8e63eSJohn Marino 		 * delimiter, handling escaped delimiters.
96e0b8e63eSJohn Marino 		 *
97e0b8e63eSJohn Marino 		 * QUOTING NOTE:
98e0b8e63eSJohn Marino 		 * Only discard an escape character if it escapes a delimiter.
99e0b8e63eSJohn Marino 		 */
100e0b8e63eSJohn Marino 		for (delim = *ptrn, p = t = ++ptrn;; *t++ = *p++) {
101e0b8e63eSJohn Marino 			if (--plen == 0 || p[0] == delim) {
102e0b8e63eSJohn Marino 				if (plen != 0)
103e0b8e63eSJohn Marino 					++p;
104e0b8e63eSJohn Marino 				break;
105e0b8e63eSJohn Marino 			}
106e0b8e63eSJohn Marino 			if (plen > 1 && p[0] == '\\' && p[1] == delim) {
107e0b8e63eSJohn Marino 				++p;
108e0b8e63eSJohn Marino 				--plen;
109e0b8e63eSJohn Marino 			}
110e0b8e63eSJohn Marino 		}
111e0b8e63eSJohn Marino 		if (epp != NULL)
112e0b8e63eSJohn Marino 			*epp = p;
113e0b8e63eSJohn Marino 
114e0b8e63eSJohn Marino 		plen = t - ptrn;
115e0b8e63eSJohn Marino 	}
116e0b8e63eSJohn Marino 
117e0b8e63eSJohn Marino 	/* Compile the RE. */
118e0b8e63eSJohn Marino 	if (re_compile(sp, ptrn, plen, &sp->re, &sp->re_len, &sp->re_c,
119e0b8e63eSJohn Marino 	    RE_C_SEARCH |
120e0b8e63eSJohn Marino 	    (LF_ISSET(SEARCH_MSG) ? 0 : RE_C_SILENT) |
121e0b8e63eSJohn Marino 	    (LF_ISSET(SEARCH_TAG) ? RE_C_TAG : 0) |
122e0b8e63eSJohn Marino 	    (LF_ISSET(SEARCH_CSCOPE) ? RE_C_CSCOPE : 0)))
123e0b8e63eSJohn Marino 		return (1);
124e0b8e63eSJohn Marino 
125e0b8e63eSJohn Marino 	/* Set the search direction. */
126e0b8e63eSJohn Marino 	if (LF_ISSET(SEARCH_SET))
127e0b8e63eSJohn Marino 		sp->searchdir = dir;
128e0b8e63eSJohn Marino 
129e0b8e63eSJohn Marino 	return (0);
130e0b8e63eSJohn Marino }
131e0b8e63eSJohn Marino 
132e0b8e63eSJohn Marino /*
133e0b8e63eSJohn Marino  * f_search --
134e0b8e63eSJohn Marino  *	Do a forward search.
135e0b8e63eSJohn Marino  *
136e0b8e63eSJohn Marino  * PUBLIC: int f_search(SCR *,
137e0b8e63eSJohn Marino  * PUBLIC:    MARK *, MARK *, CHAR_T *, size_t, CHAR_T **, u_int);
138e0b8e63eSJohn Marino  */
139e0b8e63eSJohn Marino int
f_search(SCR * sp,MARK * fm,MARK * rm,CHAR_T * ptrn,size_t plen,CHAR_T ** eptrn,u_int flags)140*b1ac2ebbSDaniel Fojt f_search(SCR *sp, MARK *fm, MARK *rm, CHAR_T *ptrn, size_t plen,
141*b1ac2ebbSDaniel Fojt     CHAR_T **eptrn, u_int flags)
142e0b8e63eSJohn Marino {
143e0b8e63eSJohn Marino 	busy_t btype;
144e0b8e63eSJohn Marino 	recno_t lno;
145e0b8e63eSJohn Marino 	regmatch_t match[1];
146e0b8e63eSJohn Marino 	size_t coff, len;
147e0b8e63eSJohn Marino 	int cnt, eval, rval, wrapped = 0;
148e0b8e63eSJohn Marino 	CHAR_T *l;
149e0b8e63eSJohn Marino 
150e0b8e63eSJohn Marino 	if (search_init(sp, FORWARD, ptrn, plen, eptrn, flags))
151e0b8e63eSJohn Marino 		return (1);
152e0b8e63eSJohn Marino 
153e0b8e63eSJohn Marino 	if (LF_ISSET(SEARCH_FILE)) {
154e0b8e63eSJohn Marino 		lno = 1;
155e0b8e63eSJohn Marino 		coff = 0;
156e0b8e63eSJohn Marino 	} else {
157e0b8e63eSJohn Marino 		if (db_get(sp, fm->lno, DBG_FATAL, &l, &len))
158e0b8e63eSJohn Marino 			return (1);
159e0b8e63eSJohn Marino 		lno = fm->lno;
160e0b8e63eSJohn Marino 
161e0b8e63eSJohn Marino 		/*
162e0b8e63eSJohn Marino 		 * If doing incremental search, start searching at the previous
163e0b8e63eSJohn Marino 		 * column, so that we search a minimal distance and still match
164e0b8e63eSJohn Marino 		 * special patterns, e.g., \< for beginning of a word.
165e0b8e63eSJohn Marino 		 *
166e0b8e63eSJohn Marino 		 * Otherwise, start searching immediately after the cursor.  If
167e0b8e63eSJohn Marino 		 * at the end of the line, start searching on the next line.
168e0b8e63eSJohn Marino 		 * This is incompatible (read bug fix) with the historic vi --
169e0b8e63eSJohn Marino 		 * searches for the '$' pattern never moved forward, and the
170e0b8e63eSJohn Marino 		 * "-t foo" didn't work if the 'f' was the first character in
171e0b8e63eSJohn Marino 		 * the file.
172e0b8e63eSJohn Marino 		 */
173e0b8e63eSJohn Marino 		if (LF_ISSET(SEARCH_INCR)) {
174e0b8e63eSJohn Marino 			if ((coff = fm->cno) != 0)
175e0b8e63eSJohn Marino 				--coff;
176e0b8e63eSJohn Marino 		} else if (fm->cno + 1 >= len) {
177e0b8e63eSJohn Marino 			coff = 0;
178e0b8e63eSJohn Marino 			lno = fm->lno + 1;
179e0b8e63eSJohn Marino 			if (db_get(sp, lno, 0, &l, &len)) {
180e0b8e63eSJohn Marino 				if (!O_ISSET(sp, O_WRAPSCAN)) {
181e0b8e63eSJohn Marino 					if (LF_ISSET(SEARCH_MSG))
182e0b8e63eSJohn Marino 						search_msg(sp, S_EOF);
183e0b8e63eSJohn Marino 					return (1);
184e0b8e63eSJohn Marino 				}
185e0b8e63eSJohn Marino 				lno = 1;
186e0b8e63eSJohn Marino 				wrapped = 1;
187e0b8e63eSJohn Marino 			}
188e0b8e63eSJohn Marino 		} else
189e0b8e63eSJohn Marino 			coff = fm->cno + 1;
190e0b8e63eSJohn Marino 	}
191e0b8e63eSJohn Marino 
192e0b8e63eSJohn Marino 	btype = BUSY_ON;
193e0b8e63eSJohn Marino 	for (cnt = INTERRUPT_CHECK, rval = 1;; ++lno, coff = 0) {
194e0b8e63eSJohn Marino 		if (cnt-- == 0) {
195e0b8e63eSJohn Marino 			if (INTERRUPTED(sp))
196e0b8e63eSJohn Marino 				break;
197e0b8e63eSJohn Marino 			if (LF_ISSET(SEARCH_MSG)) {
198e0b8e63eSJohn Marino 				search_busy(sp, btype);
199e0b8e63eSJohn Marino 				btype = BUSY_UPDATE;
200e0b8e63eSJohn Marino 			}
201e0b8e63eSJohn Marino 			cnt = INTERRUPT_CHECK;
202e0b8e63eSJohn Marino 		}
203e0b8e63eSJohn Marino 		if ((wrapped && lno > fm->lno) || db_get(sp, lno, 0, &l, &len)) {
204e0b8e63eSJohn Marino 			if (wrapped) {
205e0b8e63eSJohn Marino 				if (LF_ISSET(SEARCH_MSG))
206e0b8e63eSJohn Marino 					search_msg(sp, S_NOTFOUND);
207e0b8e63eSJohn Marino 				break;
208e0b8e63eSJohn Marino 			}
209e0b8e63eSJohn Marino 			if (!O_ISSET(sp, O_WRAPSCAN)) {
210e0b8e63eSJohn Marino 				if (LF_ISSET(SEARCH_MSG))
211e0b8e63eSJohn Marino 					search_msg(sp, S_EOF);
212e0b8e63eSJohn Marino 				break;
213e0b8e63eSJohn Marino 			}
214e0b8e63eSJohn Marino 			lno = 0;
215e0b8e63eSJohn Marino 			wrapped = 1;
216e0b8e63eSJohn Marino 			continue;
217e0b8e63eSJohn Marino 		}
218e0b8e63eSJohn Marino 
219e0b8e63eSJohn Marino 		/* If already at EOL, just keep going. */
220e0b8e63eSJohn Marino 		if (len != 0 && coff == len)
221e0b8e63eSJohn Marino 			continue;
222e0b8e63eSJohn Marino 
223e0b8e63eSJohn Marino 		/* Set the termination. */
224e0b8e63eSJohn Marino 		match[0].rm_so = coff;
225e0b8e63eSJohn Marino 		match[0].rm_eo = len;
226e0b8e63eSJohn Marino 
227e0b8e63eSJohn Marino #if defined(DEBUG) && 0
228e0b8e63eSJohn Marino 		TRACE(sp, "F search: %lu from %u to %u\n",
229e0b8e63eSJohn Marino 		    lno, coff, len != 0 ? len - 1 : len);
230e0b8e63eSJohn Marino #endif
231e0b8e63eSJohn Marino 		/* Search the line. */
232e0b8e63eSJohn Marino 		eval = regexec(&sp->re_c, l, 1, match,
233e0b8e63eSJohn Marino 		    (match[0].rm_so == 0 ? 0 : REG_NOTBOL) | REG_STARTEND);
234e0b8e63eSJohn Marino 		if (eval == REG_NOMATCH)
235e0b8e63eSJohn Marino 			continue;
236e0b8e63eSJohn Marino 		if (eval != 0) {
237e0b8e63eSJohn Marino 			if (LF_ISSET(SEARCH_MSG))
238e0b8e63eSJohn Marino 				re_error(sp, eval, &sp->re_c);
239e0b8e63eSJohn Marino 			else
240e0b8e63eSJohn Marino 				(void)sp->gp->scr_bell(sp);
241e0b8e63eSJohn Marino 			break;
242e0b8e63eSJohn Marino 		}
243e0b8e63eSJohn Marino 
244e0b8e63eSJohn Marino 		/* Warn if the search wrapped. */
245e0b8e63eSJohn Marino 		if (wrapped && LF_ISSET(SEARCH_WMSG))
246e0b8e63eSJohn Marino 			search_msg(sp, S_WRAP);
247e0b8e63eSJohn Marino 
248e0b8e63eSJohn Marino #if defined(DEBUG) && 0
249e0b8e63eSJohn Marino 		TRACE(sp, "F search: %qu to %qu\n",
250e0b8e63eSJohn Marino 		    match[0].rm_so, match[0].rm_eo);
251e0b8e63eSJohn Marino #endif
252e0b8e63eSJohn Marino 		rm->lno = lno;
253e0b8e63eSJohn Marino 		rm->cno = match[0].rm_so;
254e0b8e63eSJohn Marino 
255e0b8e63eSJohn Marino 		/*
256e0b8e63eSJohn Marino 		 * If a change command, it's possible to move beyond the end
257e0b8e63eSJohn Marino 		 * of a line.  Historic vi generally got this wrong (e.g. try
258e0b8e63eSJohn Marino 		 * "c?$<cr>").  Not all that sure this gets it right, there
259e0b8e63eSJohn Marino 		 * are lots of strange cases.
260e0b8e63eSJohn Marino 		 */
261e0b8e63eSJohn Marino 		if (!LF_ISSET(SEARCH_EOL) && rm->cno >= len)
262e0b8e63eSJohn Marino 			rm->cno = len != 0 ? len - 1 : 0;
263e0b8e63eSJohn Marino 
264e0b8e63eSJohn Marino 		rval = 0;
265e0b8e63eSJohn Marino 		break;
266e0b8e63eSJohn Marino 	}
267e0b8e63eSJohn Marino 
268e0b8e63eSJohn Marino 	if (LF_ISSET(SEARCH_MSG))
269e0b8e63eSJohn Marino 		search_busy(sp, BUSY_OFF);
270e0b8e63eSJohn Marino 	return (rval);
271e0b8e63eSJohn Marino }
272e0b8e63eSJohn Marino 
273e0b8e63eSJohn Marino /*
274e0b8e63eSJohn Marino  * b_search --
275e0b8e63eSJohn Marino  *	Do a backward search.
276e0b8e63eSJohn Marino  *
277e0b8e63eSJohn Marino  * PUBLIC: int b_search(SCR *,
278e0b8e63eSJohn Marino  * PUBLIC:    MARK *, MARK *, CHAR_T *, size_t, CHAR_T **, u_int);
279e0b8e63eSJohn Marino  */
280e0b8e63eSJohn Marino int
b_search(SCR * sp,MARK * fm,MARK * rm,CHAR_T * ptrn,size_t plen,CHAR_T ** eptrn,u_int flags)281*b1ac2ebbSDaniel Fojt b_search(SCR *sp, MARK *fm, MARK *rm, CHAR_T *ptrn, size_t plen,
282*b1ac2ebbSDaniel Fojt     CHAR_T **eptrn, u_int flags)
283e0b8e63eSJohn Marino {
284e0b8e63eSJohn Marino 	busy_t btype;
285e0b8e63eSJohn Marino 	recno_t lno;
286e0b8e63eSJohn Marino 	regmatch_t match[1];
287e0b8e63eSJohn Marino 	size_t coff, last, len;
288e0b8e63eSJohn Marino 	int cnt, eval, rval, wrapped;
289e0b8e63eSJohn Marino 	CHAR_T *l;
290e0b8e63eSJohn Marino 
291e0b8e63eSJohn Marino 	if (search_init(sp, BACKWARD, ptrn, plen, eptrn, flags))
292e0b8e63eSJohn Marino 		return (1);
293e0b8e63eSJohn Marino 
294e0b8e63eSJohn Marino 	/*
295e0b8e63eSJohn Marino 	 * If doing incremental search, set the "starting" position past the
296e0b8e63eSJohn Marino 	 * current column, so that we search a minimal distance and still
297e0b8e63eSJohn Marino 	 * match special patterns, e.g., \> for the end of a word.  This is
298e0b8e63eSJohn Marino 	 * safe when the cursor is at the end of a line because we only use
299e0b8e63eSJohn Marino 	 * it for comparison with the location of the match.
300e0b8e63eSJohn Marino 	 *
301e0b8e63eSJohn Marino 	 * Otherwise, start searching immediately before the cursor.  If in
302e0b8e63eSJohn Marino 	 * the first column, start search on the previous line.
303e0b8e63eSJohn Marino 	 */
304e0b8e63eSJohn Marino 	if (LF_ISSET(SEARCH_INCR)) {
305e0b8e63eSJohn Marino 		lno = fm->lno;
306e0b8e63eSJohn Marino 		coff = fm->cno + 1;
307e0b8e63eSJohn Marino 	} else {
308e0b8e63eSJohn Marino 		if (fm->cno == 0) {
309e0b8e63eSJohn Marino 			if (fm->lno == 1 && !O_ISSET(sp, O_WRAPSCAN)) {
310e0b8e63eSJohn Marino 				if (LF_ISSET(SEARCH_MSG))
311e0b8e63eSJohn Marino 					search_msg(sp, S_SOF);
312e0b8e63eSJohn Marino 				return (1);
313e0b8e63eSJohn Marino 			}
314e0b8e63eSJohn Marino 			lno = fm->lno - 1;
315e0b8e63eSJohn Marino 		} else
316e0b8e63eSJohn Marino 			lno = fm->lno;
317e0b8e63eSJohn Marino 		coff = fm->cno;
318e0b8e63eSJohn Marino 	}
319e0b8e63eSJohn Marino 
320e0b8e63eSJohn Marino 	btype = BUSY_ON;
321e0b8e63eSJohn Marino 	for (cnt = INTERRUPT_CHECK, rval = 1, wrapped = 0;; --lno, coff = 0) {
322e0b8e63eSJohn Marino 		if (cnt-- == 0) {
323e0b8e63eSJohn Marino 			if (INTERRUPTED(sp))
324e0b8e63eSJohn Marino 				break;
325e0b8e63eSJohn Marino 			if (LF_ISSET(SEARCH_MSG)) {
326e0b8e63eSJohn Marino 				search_busy(sp, btype);
327e0b8e63eSJohn Marino 				btype = BUSY_UPDATE;
328e0b8e63eSJohn Marino 			}
329e0b8e63eSJohn Marino 			cnt = INTERRUPT_CHECK;
330e0b8e63eSJohn Marino 		}
331e0b8e63eSJohn Marino 		if ((wrapped && lno < fm->lno) || lno == 0) {
332e0b8e63eSJohn Marino 			if (wrapped) {
333e0b8e63eSJohn Marino 				if (LF_ISSET(SEARCH_MSG))
334e0b8e63eSJohn Marino 					search_msg(sp, S_NOTFOUND);
335e0b8e63eSJohn Marino 				break;
336e0b8e63eSJohn Marino 			}
337e0b8e63eSJohn Marino 			if (!O_ISSET(sp, O_WRAPSCAN)) {
338e0b8e63eSJohn Marino 				if (LF_ISSET(SEARCH_MSG))
339e0b8e63eSJohn Marino 					search_msg(sp, S_SOF);
340e0b8e63eSJohn Marino 				break;
341e0b8e63eSJohn Marino 			}
342e0b8e63eSJohn Marino 			if (db_last(sp, &lno))
343e0b8e63eSJohn Marino 				break;
344e0b8e63eSJohn Marino 			if (lno == 0) {
345e0b8e63eSJohn Marino 				if (LF_ISSET(SEARCH_MSG))
346e0b8e63eSJohn Marino 					search_msg(sp, S_EMPTY);
347e0b8e63eSJohn Marino 				break;
348e0b8e63eSJohn Marino 			}
349e0b8e63eSJohn Marino 			++lno;
350e0b8e63eSJohn Marino 			wrapped = 1;
351e0b8e63eSJohn Marino 			continue;
352e0b8e63eSJohn Marino 		}
353e0b8e63eSJohn Marino 
354e0b8e63eSJohn Marino 		if (db_get(sp, lno, 0, &l, &len))
355e0b8e63eSJohn Marino 			break;
356e0b8e63eSJohn Marino 
357e0b8e63eSJohn Marino 		/* Set the termination. */
358e0b8e63eSJohn Marino 		match[0].rm_so = 0;
359e0b8e63eSJohn Marino 		match[0].rm_eo = len;
360e0b8e63eSJohn Marino 
361e0b8e63eSJohn Marino #if defined(DEBUG) && 0
362e0b8e63eSJohn Marino 		TRACE(sp, "B search: %lu from 0 to %qu\n", lno, match[0].rm_eo);
363e0b8e63eSJohn Marino #endif
364e0b8e63eSJohn Marino 		/* Search the line. */
365e0b8e63eSJohn Marino 		eval = regexec(&sp->re_c, l, 1, match,
366e0b8e63eSJohn Marino 		    (match[0].rm_eo == len ? 0 : REG_NOTEOL) | REG_STARTEND);
367e0b8e63eSJohn Marino 		if (eval == REG_NOMATCH)
368e0b8e63eSJohn Marino 			continue;
369e0b8e63eSJohn Marino 		if (eval != 0) {
370e0b8e63eSJohn Marino 			if (LF_ISSET(SEARCH_MSG))
371e0b8e63eSJohn Marino 				re_error(sp, eval, &sp->re_c);
372e0b8e63eSJohn Marino 			else
373e0b8e63eSJohn Marino 				(void)sp->gp->scr_bell(sp);
374e0b8e63eSJohn Marino 			break;
375e0b8e63eSJohn Marino 		}
376e0b8e63eSJohn Marino 
377e0b8e63eSJohn Marino 		/* Check for a match starting past the cursor. */
378e0b8e63eSJohn Marino 		if (coff != 0 && match[0].rm_so >= coff)
379e0b8e63eSJohn Marino 			continue;
380e0b8e63eSJohn Marino 
381e0b8e63eSJohn Marino 		/* Warn if the search wrapped. */
382e0b8e63eSJohn Marino 		if (wrapped && LF_ISSET(SEARCH_WMSG))
383e0b8e63eSJohn Marino 			search_msg(sp, S_WRAP);
384e0b8e63eSJohn Marino 
385e0b8e63eSJohn Marino #if defined(DEBUG) && 0
386e0b8e63eSJohn Marino 		TRACE(sp, "B found: %qu to %qu\n",
387e0b8e63eSJohn Marino 		    match[0].rm_so, match[0].rm_eo);
388e0b8e63eSJohn Marino #endif
389e0b8e63eSJohn Marino 		/*
390e0b8e63eSJohn Marino 		 * We now have the first match on the line.  Step through the
391e0b8e63eSJohn Marino 		 * line character by character until find the last acceptable
392e0b8e63eSJohn Marino 		 * match.  This is painful, we need a better interface to regex
393e0b8e63eSJohn Marino 		 * to make this work.
394e0b8e63eSJohn Marino 		 */
395e0b8e63eSJohn Marino 		for (;;) {
396e0b8e63eSJohn Marino 			last = match[0].rm_so++;
397e0b8e63eSJohn Marino 			if (match[0].rm_so >= len)
398e0b8e63eSJohn Marino 				break;
399e0b8e63eSJohn Marino 			match[0].rm_eo = len;
400e0b8e63eSJohn Marino 			eval = regexec(&sp->re_c, l, 1, match,
401e0b8e63eSJohn Marino 			    (match[0].rm_so == 0 ? 0 : REG_NOTBOL) |
402e0b8e63eSJohn Marino 			    REG_STARTEND);
403e0b8e63eSJohn Marino 			if (eval == REG_NOMATCH)
404e0b8e63eSJohn Marino 				break;
405e0b8e63eSJohn Marino 			if (eval != 0) {
406e0b8e63eSJohn Marino 				if (LF_ISSET(SEARCH_MSG))
407e0b8e63eSJohn Marino 					re_error(sp, eval, &sp->re_c);
408e0b8e63eSJohn Marino 				else
409e0b8e63eSJohn Marino 					(void)sp->gp->scr_bell(sp);
410e0b8e63eSJohn Marino 				goto err;
411e0b8e63eSJohn Marino 			}
412e0b8e63eSJohn Marino 			if (coff && match[0].rm_so >= coff)
413e0b8e63eSJohn Marino 				break;
414e0b8e63eSJohn Marino 		}
415e0b8e63eSJohn Marino 		rm->lno = lno;
416e0b8e63eSJohn Marino 
417e0b8e63eSJohn Marino 		/* See comment in f_search(). */
418e0b8e63eSJohn Marino 		if (!LF_ISSET(SEARCH_EOL) && last >= len)
419e0b8e63eSJohn Marino 			rm->cno = len != 0 ? len - 1 : 0;
420e0b8e63eSJohn Marino 		else
421e0b8e63eSJohn Marino 			rm->cno = last;
422e0b8e63eSJohn Marino 		rval = 0;
423e0b8e63eSJohn Marino 		break;
424e0b8e63eSJohn Marino 	}
425e0b8e63eSJohn Marino 
426e0b8e63eSJohn Marino err:	if (LF_ISSET(SEARCH_MSG))
427e0b8e63eSJohn Marino 		search_busy(sp, BUSY_OFF);
428e0b8e63eSJohn Marino 	return (rval);
429e0b8e63eSJohn Marino }
430e0b8e63eSJohn Marino 
431e0b8e63eSJohn Marino /*
432e0b8e63eSJohn Marino  * search_msg --
433e0b8e63eSJohn Marino  *	Display one of the search messages.
434e0b8e63eSJohn Marino  */
435e0b8e63eSJohn Marino static void
search_msg(SCR * sp,smsg_t msg)436*b1ac2ebbSDaniel Fojt search_msg(SCR *sp, smsg_t msg)
437e0b8e63eSJohn Marino {
438e0b8e63eSJohn Marino 	switch (msg) {
439e0b8e63eSJohn Marino 	case S_EMPTY:
440e0b8e63eSJohn Marino 		msgq(sp, M_ERR, "072|File empty; nothing to search");
441e0b8e63eSJohn Marino 		break;
442e0b8e63eSJohn Marino 	case S_EOF:
443e0b8e63eSJohn Marino 		msgq(sp, M_ERR,
444e0b8e63eSJohn Marino 		    "073|Reached end-of-file without finding the pattern");
445e0b8e63eSJohn Marino 		break;
446e0b8e63eSJohn Marino 	case S_NOPREV:
447e0b8e63eSJohn Marino 		msgq(sp, M_ERR, "074|No previous search pattern");
448e0b8e63eSJohn Marino 		break;
449e0b8e63eSJohn Marino 	case S_NOTFOUND:
450e0b8e63eSJohn Marino 		msgq(sp, M_ERR, "075|Pattern not found");
451e0b8e63eSJohn Marino 		break;
452e0b8e63eSJohn Marino 	case S_SOF:
453e0b8e63eSJohn Marino 		msgq(sp, M_ERR,
454e0b8e63eSJohn Marino 		    "076|Reached top-of-file without finding the pattern");
455e0b8e63eSJohn Marino 		break;
456e0b8e63eSJohn Marino 	case S_WRAP:
457e0b8e63eSJohn Marino 		msgq(sp, M_ERR, "077|Search wrapped");
458e0b8e63eSJohn Marino 		break;
459e0b8e63eSJohn Marino 	default:
460e0b8e63eSJohn Marino 		abort();
461e0b8e63eSJohn Marino 	}
462e0b8e63eSJohn Marino }
463e0b8e63eSJohn Marino 
464e0b8e63eSJohn Marino /*
465e0b8e63eSJohn Marino  * search_busy --
466e0b8e63eSJohn Marino  *	Put up the busy searching message.
467e0b8e63eSJohn Marino  *
468e0b8e63eSJohn Marino  * PUBLIC: void search_busy(SCR *, busy_t);
469e0b8e63eSJohn Marino  */
470e0b8e63eSJohn Marino void
search_busy(SCR * sp,busy_t btype)471*b1ac2ebbSDaniel Fojt search_busy(SCR *sp, busy_t btype)
472e0b8e63eSJohn Marino {
473e0b8e63eSJohn Marino 	sp->gp->scr_busy(sp, "078|Searching...", btype);
474e0b8e63eSJohn Marino }
475