1 /* $NetBSD: search.c,v 1.2 2013/11/22 15:52:05 christos Exp $ */
3 * Copyright (c) 1992, 1993, 1994
4 * The Regents of the University of California. All rights reserved.
5 * Copyright (c) 1992, 1993, 1994, 1995, 1996
6 * Keith Bostic. All rights reserved.
8 * See the LICENSE file for redistribution information.
14 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 ";
17 #include <sys/types.h>
18 #include <sys/queue.h>
20 #include <bitstring.h>
31 typedef enum { S_EMPTY
, S_EOF
, S_NOPREV
, S_NOTFOUND
, S_SOF
, S_WRAP
} smsg_t
;
33 static void search_msg
__P((SCR
*, smsg_t
));
34 static int search_init
__P((SCR
*, dir_t
, CHAR_T
*, size_t, CHAR_T
**, u_int
));
41 search_init(SCR
*sp
, dir_t dir
, CHAR_T
*ptrn
, size_t plen
, CHAR_T
**epp
, u_int flags
)
47 /* If the file is empty, it's a fast search. */
49 if (db_last(sp
, &lno
))
52 if (LF_ISSET(SEARCH_MSG
))
53 search_msg(sp
, S_EMPTY
);
58 if (LF_ISSET(SEARCH_PARSE
)) { /* Parse the string. */
60 * Use the saved pattern if no pattern specified, or if only
61 * one or two delimiter characters specified.
64 * Historically, only the pattern itself was saved, vi didn't
65 * preserve addressing or delta information.
74 if (ptrn
[0] == ptrn
[1]) {
78 /* Complain if we don't have a previous pattern. */
79 prev
: if (sp
->re
== NULL
) {
80 search_msg(sp
, S_NOPREV
);
83 /* Re-compile the search pattern if necessary. */
84 if (!F_ISSET(sp
, SC_RE_SEARCH
) && re_compile(sp
,
85 sp
->re
, sp
->re_len
, NULL
, NULL
, &sp
->re_c
,
86 SEARCH_CSEARCH
| SEARCH_MSG
))
89 /* Set the search direction. */
90 if (LF_ISSET(SEARCH_SET
))
96 * Set the delimiter, and move forward to the terminating
97 * delimiter, handling escaped delimiters.
100 * Only discard an escape character if it escapes a delimiter.
102 for (delim
= *ptrn
, p
= t
= ++ptrn
;; *t
++ = *p
++) {
103 if (--plen
== 0 || p
[0] == delim
) {
108 if (plen
> 1 && p
[0] == '\\' && p
[1] == delim
) {
119 /* Compile the RE. */
120 if (re_compile(sp
, ptrn
, plen
, &sp
->re
, &sp
->re_len
, &sp
->re_c
,
121 SEARCH_CSEARCH
| LF_ISSET(SEARCH_CSCOPE
| SEARCH_EXTEND
|
122 SEARCH_IC
| SEARCH_LITERAL
| SEARCH_MSG
| SEARCH_TAG
)))
125 /* Set the search direction. */
126 if (LF_ISSET(SEARCH_SET
))
134 * Do a forward search.
136 * PUBLIC: int f_search __P((SCR *,
137 * PUBLIC: MARK *, MARK *, CHAR_T *, size_t, CHAR_T **, u_int));
140 f_search(SCR
*sp
, MARK
*fm
, MARK
*rm
, CHAR_T
*ptrn
, size_t plen
, CHAR_T
**eptrn
, u_int flags
)
146 int cnt
, eval
, rval
, wrapped
;
149 if (search_init(sp
, FORWARD
, ptrn
, plen
, eptrn
, flags
))
152 /* Figure out if we're going to wrap. */
153 if (!LF_ISSET(SEARCH_NOOPT
) && O_ISSET(sp
, O_WRAPSCAN
))
156 if (LF_ISSET(SEARCH_FIRST
)) {
160 if (db_get(sp
, fm
->lno
, DBG_FATAL
, &l
, &len
))
165 * If doing incremental search, start searching at the previous
166 * column, so that we search a minimal distance and still match
167 * special patterns, e.g., \< for beginning of a word.
169 * Otherwise, start searching immediately after the cursor. If
170 * at the end of the line, start searching on the next line.
171 * This is incompatible (read bug fix) with the historic vi --
172 * searches for the '$' pattern never moved forward, and the
173 * "-t foo" didn't work if the 'f' was the first character in
176 if (LF_ISSET(SEARCH_INCR
)) {
177 if ((coff
= fm
->cno
) != 0)
179 } else if (fm
->cno
+ 1 >= len
) {
182 if (db_get(sp
, lno
, 0, &l
, &len
)) {
183 if (!LF_ISSET(SEARCH_WRAP
)) {
184 if (LF_ISSET(SEARCH_MSG
))
185 search_msg(sp
, S_EOF
);
195 for (cnt
= INTERRUPT_CHECK
, rval
= 1, wrapped
= 0;; ++lno
, coff
= 0) {
199 if (LF_ISSET(SEARCH_MSG
)) {
200 search_busy(sp
, btype
);
203 cnt
= INTERRUPT_CHECK
;
205 if ((wrapped
&& lno
> fm
->lno
) ||
206 db_get(sp
, lno
, 0, &l
, &len
)) {
208 if (LF_ISSET(SEARCH_MSG
))
209 search_msg(sp
, S_NOTFOUND
);
212 if (!LF_ISSET(SEARCH_WRAP
)) {
213 if (LF_ISSET(SEARCH_MSG
))
214 search_msg(sp
, S_EOF
);
222 /* If already at EOL, just keep going. */
223 if (len
!= 0 && coff
== len
)
226 /* Set the termination. */
227 match
[0].rm_so
= coff
;
228 match
[0].rm_eo
= len
;
230 #if defined(DEBUG) && 0
231 vtrace(sp
, "F search: %lu from %u to %u\n",
232 lno
, coff
, len
!= 0 ? len
- 1 : len
);
234 /* Search the line. */
235 eval
= regexec(&sp
->re_c
, l
, 1, match
,
236 (match
[0].rm_so
== 0 ? 0 : REG_NOTBOL
) | REG_STARTEND
);
237 if (eval
== REG_NOMATCH
)
240 if (LF_ISSET(SEARCH_MSG
))
241 re_error(sp
, eval
, &sp
->re_c
);
243 (void)sp
->gp
->scr_bell(sp
);
247 /* Warn if the search wrapped. */
248 if (wrapped
&& LF_ISSET(SEARCH_WMSG
))
249 search_msg(sp
, S_WRAP
);
251 #if defined(DEBUG) && 0
252 vtrace(sp
, "F search: %qu to %qu\n",
253 match
[0].rm_so
, match
[0].rm_eo
);
256 rm
->cno
= match
[0].rm_so
;
259 * If a change command, it's possible to move beyond the end
260 * of a line. Historic vi generally got this wrong (e.g. try
261 * "c?$<cr>"). Not all that sure this gets it right, there
262 * are lots of strange cases.
264 if (!LF_ISSET(SEARCH_EOL
) && rm
->cno
>= len
)
265 rm
->cno
= len
!= 0 ? len
- 1 : 0;
271 if (LF_ISSET(SEARCH_MSG
))
272 search_busy(sp
, BUSY_OFF
);
278 * Do a backward search.
280 * PUBLIC: int b_search __P((SCR *,
281 * PUBLIC: MARK *, MARK *, CHAR_T *, size_t, CHAR_T **, u_int));
284 b_search(SCR
*sp
, MARK
*fm
, MARK
*rm
, CHAR_T
*ptrn
, size_t plen
, CHAR_T
**eptrn
, u_int flags
)
289 size_t coff
, last
, len
;
290 int cnt
, eval
, rval
, wrapped
;
293 if (search_init(sp
, BACKWARD
, ptrn
, plen
, eptrn
, flags
))
296 /* Figure out if we're going to wrap. */
297 if (!LF_ISSET(SEARCH_NOOPT
) && O_ISSET(sp
, O_WRAPSCAN
))
301 * If doing incremental search, set the "starting" position past the
302 * current column, so that we search a minimal distance and still
303 * match special patterns, e.g., \> for the end of a word. This is
304 * safe when the cursor is at the end of a line because we only use
305 * it for comparison with the location of the match.
307 * Otherwise, start searching immediately before the cursor. If in
308 * the first column, start search on the previous line.
310 if (LF_ISSET(SEARCH_INCR
)) {
315 if (fm
->lno
== 1 && !LF_ISSET(SEARCH_WRAP
)) {
316 if (LF_ISSET(SEARCH_MSG
))
317 search_msg(sp
, S_SOF
);
327 for (cnt
= INTERRUPT_CHECK
, rval
= 1, wrapped
= 0;; --lno
, coff
= 0) {
331 if (LF_ISSET(SEARCH_MSG
)) {
332 search_busy(sp
, btype
);
335 cnt
= INTERRUPT_CHECK
;
337 if ((wrapped
&& lno
< fm
->lno
) || lno
== 0) {
339 if (LF_ISSET(SEARCH_MSG
))
340 search_msg(sp
, S_NOTFOUND
);
343 if (!LF_ISSET(SEARCH_WRAP
)) {
344 if (LF_ISSET(SEARCH_MSG
))
345 search_msg(sp
, S_SOF
);
348 if (db_last(sp
, &lno
))
351 if (LF_ISSET(SEARCH_MSG
))
352 search_msg(sp
, S_EMPTY
);
360 if (db_get(sp
, lno
, 0, &l
, &len
))
363 /* Set the termination. */
365 match
[0].rm_eo
= len
;
367 #if defined(DEBUG) && 0
369 "B search: %lu from 0 to %qu\n", lno
, match
[0].rm_eo
);
371 /* Search the line. */
372 eval
= regexec(&sp
->re_c
, l
, 1, match
,
373 ((size_t)match
[0].rm_eo
== len
? 0 : REG_NOTEOL
) | REG_STARTEND
);
374 if (eval
== REG_NOMATCH
)
377 if (LF_ISSET(SEARCH_MSG
))
378 re_error(sp
, eval
, &sp
->re_c
);
380 (void)sp
->gp
->scr_bell(sp
);
384 /* Check for a match starting past the cursor. */
385 if (coff
!= 0 && (size_t)match
[0].rm_so
>= coff
)
388 /* Warn if the search wrapped. */
389 if (wrapped
&& LF_ISSET(SEARCH_WMSG
))
390 search_msg(sp
, S_WRAP
);
392 #if defined(DEBUG) && 0
393 vtrace(sp
, "B found: %qu to %qu\n",
394 match
[0].rm_so
, match
[0].rm_eo
);
397 * We now have the first match on the line. Step through the
398 * line character by character until find the last acceptable
399 * match. This is painful, we need a better interface to regex
403 last
= match
[0].rm_so
++;
404 if ((size_t)match
[0].rm_so
>= len
)
406 match
[0].rm_eo
= len
;
407 eval
= regexec(&sp
->re_c
, l
, 1, match
,
408 (match
[0].rm_so
== 0 ? 0 : REG_NOTBOL
) |
410 if (eval
== REG_NOMATCH
)
413 if (LF_ISSET(SEARCH_MSG
))
414 re_error(sp
, eval
, &sp
->re_c
);
416 (void)sp
->gp
->scr_bell(sp
);
419 if (coff
&& (size_t)match
[0].rm_so
>= coff
)
424 /* See comment in f_search(). */
425 if (!LF_ISSET(SEARCH_EOL
) && last
>= len
)
426 rm
->cno
= len
!= 0 ? len
- 1 : 0;
433 err
: if (LF_ISSET(SEARCH_MSG
))
434 search_busy(sp
, BUSY_OFF
);
440 * Display one of the search messages.
443 search_msg(SCR
*sp
, smsg_t msg
)
447 msgq(sp
, M_ERR
, "072|File empty; nothing to search");
451 "073|Reached end-of-file without finding the pattern");
454 msgq(sp
, M_ERR
, "074|No previous search pattern");
457 msgq(sp
, M_ERR
, "075|Pattern not found");
461 "076|Reached top-of-file without finding the pattern");
464 msgq(sp
, M_ERR
, "077|Search wrapped");
473 * Put up the busy searching message.
475 * PUBLIC: void search_busy __P((SCR *, busy_t));
478 search_busy(SCR
*sp
, busy_t btype
)
480 sp
->gp
->scr_busy(sp
, "078|Searching...", btype
);