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