1 /* $NetBSD: search.c,v 1.2 2008/12/05 22:51:42 christos Exp $ */
4 * Copyright (c) 1992, 1993, 1994
5 * The Regents of the University of California. All rights reserved.
6 * Copyright (c) 1992, 1993, 1994, 1995, 1996
7 * Keith Bostic. All rights reserved.
9 * See the LICENSE file for redistribution information.
15 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";
18 #include <sys/types.h>
19 #include <sys/queue.h>
21 #include <bitstring.h>
32 typedef enum { S_EMPTY
, S_EOF
, S_NOPREV
, S_NOTFOUND
, S_SOF
, S_WRAP
} smsg_t
;
34 static void search_msg
__P((SCR
*, smsg_t
));
35 static int search_init
__P((SCR
*, dir_t
, CHAR_T
*, size_t, CHAR_T
**, u_int
));
42 search_init(SCR
*sp
, dir_t dir
, CHAR_T
*ptrn
, size_t plen
, CHAR_T
**epp
, u_int flags
)
48 /* If the file is empty, it's a fast search. */
50 if (db_last(sp
, &lno
))
53 if (LF_ISSET(SEARCH_MSG
))
54 search_msg(sp
, S_EMPTY
);
59 if (LF_ISSET(SEARCH_PARSE
)) { /* Parse the string. */
61 * Use the saved pattern if no pattern specified, or if only
62 * one or two delimiter characters specified.
65 * Historically, only the pattern itself was saved, vi didn't
66 * preserve addressing or delta information.
75 if (ptrn
[0] == ptrn
[1]) {
79 /* Complain if we don't have a previous pattern. */
80 prev
: if (sp
->re
== NULL
) {
81 search_msg(sp
, S_NOPREV
);
84 /* Re-compile the search pattern if necessary. */
85 if (!F_ISSET(sp
, SC_RE_SEARCH
) && re_compile(sp
,
86 sp
->re
, sp
->re_len
, NULL
, NULL
, &sp
->re_c
,
87 SEARCH_CSEARCH
| SEARCH_MSG
))
90 /* Set the search direction. */
91 if (LF_ISSET(SEARCH_SET
))
97 * Set the delimiter, and move forward to the terminating
98 * delimiter, handling escaped delimiters.
101 * Only discard an escape character if it escapes a delimiter.
103 for (delim
= *ptrn
, p
= t
= ++ptrn
;; *t
++ = *p
++) {
104 if (--plen
== 0 || p
[0] == delim
) {
109 if (plen
> 1 && p
[0] == '\\' && p
[1] == delim
) {
120 /* Compile the RE. */
121 if (re_compile(sp
, ptrn
, plen
, &sp
->re
, &sp
->re_len
, &sp
->re_c
,
122 SEARCH_CSEARCH
| LF_ISSET(SEARCH_CSCOPE
| SEARCH_IC
|
123 SEARCH_LITERAL
| SEARCH_MSG
| SEARCH_TAG
)))
126 /* Set the search direction. */
127 if (LF_ISSET(SEARCH_SET
))
135 * Do a forward search.
137 * PUBLIC: int f_search __P((SCR *,
138 * PUBLIC: MARK *, MARK *, CHAR_T *, size_t, CHAR_T **, u_int));
141 f_search(SCR
*sp
, MARK
*fm
, MARK
*rm
, CHAR_T
*ptrn
, size_t plen
, CHAR_T
**eptrn
, u_int flags
)
147 int cnt
, eval
, rval
, wrapped
;
150 if (search_init(sp
, FORWARD
, ptrn
, plen
, eptrn
, flags
))
153 /* Figure out if we're going to wrap. */
154 if (!LF_ISSET(SEARCH_NOOPT
) && O_ISSET(sp
, O_WRAPSCAN
))
157 if (LF_ISSET(SEARCH_FIRST
)) {
161 if (db_get(sp
, fm
->lno
, DBG_FATAL
, &l
, &len
))
166 * If doing incremental search, start searching at the previous
167 * column, so that we search a minimal distance and still match
168 * special patterns, e.g., \< for beginning of a word.
170 * Otherwise, start searching immediately after the cursor. If
171 * at the end of the line, start searching on the next line.
172 * This is incompatible (read bug fix) with the historic vi --
173 * searches for the '$' pattern never moved forward, and the
174 * "-t foo" didn't work if the 'f' was the first character in
177 if (LF_ISSET(SEARCH_INCR
)) {
178 if ((coff
= fm
->cno
) != 0)
180 } else if (fm
->cno
+ 1 >= len
) {
183 if (db_get(sp
, lno
, 0, &l
, &len
)) {
184 if (!LF_ISSET(SEARCH_WRAP
)) {
185 if (LF_ISSET(SEARCH_MSG
))
186 search_msg(sp
, S_EOF
);
196 for (cnt
= INTERRUPT_CHECK
, rval
= 1, wrapped
= 0;; ++lno
, coff
= 0) {
200 if (LF_ISSET(SEARCH_MSG
)) {
201 search_busy(sp
, btype
);
204 cnt
= INTERRUPT_CHECK
;
206 if ((wrapped
&& lno
> fm
->lno
) ||
207 db_get(sp
, lno
, 0, &l
, &len
)) {
209 if (LF_ISSET(SEARCH_MSG
))
210 search_msg(sp
, S_NOTFOUND
);
213 if (!LF_ISSET(SEARCH_WRAP
)) {
214 if (LF_ISSET(SEARCH_MSG
))
215 search_msg(sp
, S_EOF
);
223 /* If already at EOL, just keep going. */
224 if (len
!= 0 && coff
== len
)
227 /* Set the termination. */
228 match
[0].rm_so
= coff
;
229 match
[0].rm_eo
= len
;
231 #if defined(DEBUG) && 0
232 vtrace(sp
, "F search: %lu from %u to %u\n",
233 lno
, coff
, len
!= 0 ? len
- 1 : len
);
235 /* Search the line. */
236 eval
= regexec(&sp
->re_c
, l
, 1, match
,
237 (match
[0].rm_so
== 0 ? 0 : REG_NOTBOL
) | REG_STARTEND
);
238 if (eval
== REG_NOMATCH
)
241 if (LF_ISSET(SEARCH_MSG
))
242 re_error(sp
, eval
, &sp
->re_c
);
244 (void)sp
->gp
->scr_bell(sp
);
248 /* Warn if the search wrapped. */
249 if (wrapped
&& LF_ISSET(SEARCH_WMSG
))
250 search_msg(sp
, S_WRAP
);
252 #if defined(DEBUG) && 0
253 vtrace(sp
, "F search: %qu to %qu\n",
254 match
[0].rm_so
, match
[0].rm_eo
);
257 rm
->cno
= match
[0].rm_so
;
260 * If a change command, it's possible to move beyond the end
261 * of a line. Historic vi generally got this wrong (e.g. try
262 * "c?$<cr>"). Not all that sure this gets it right, there
263 * are lots of strange cases.
265 if (!LF_ISSET(SEARCH_EOL
) && rm
->cno
>= len
)
266 rm
->cno
= len
!= 0 ? len
- 1 : 0;
272 if (LF_ISSET(SEARCH_MSG
))
273 search_busy(sp
, BUSY_OFF
);
279 * Do a backward search.
281 * PUBLIC: int b_search __P((SCR *,
282 * PUBLIC: MARK *, MARK *, CHAR_T *, size_t, CHAR_T **, u_int));
285 b_search(SCR
*sp
, MARK
*fm
, MARK
*rm
, CHAR_T
*ptrn
, size_t plen
, CHAR_T
**eptrn
, u_int flags
)
290 size_t coff
, last
, len
;
291 int cnt
, eval
, rval
, wrapped
;
294 if (search_init(sp
, BACKWARD
, ptrn
, plen
, eptrn
, flags
))
297 /* Figure out if we're going to wrap. */
298 if (!LF_ISSET(SEARCH_NOOPT
) && O_ISSET(sp
, O_WRAPSCAN
))
302 * If doing incremental search, set the "starting" position past the
303 * current column, so that we search a minimal distance and still
304 * match special patterns, e.g., \> for the end of a word. This is
305 * safe when the cursor is at the end of a line because we only use
306 * it for comparison with the location of the match.
308 * Otherwise, start searching immediately before the cursor. If in
309 * the first column, start search on the previous line.
311 if (LF_ISSET(SEARCH_INCR
)) {
316 if (fm
->lno
== 1 && !LF_ISSET(SEARCH_WRAP
)) {
317 if (LF_ISSET(SEARCH_MSG
))
318 search_msg(sp
, S_SOF
);
328 for (cnt
= INTERRUPT_CHECK
, rval
= 1, wrapped
= 0;; --lno
, coff
= 0) {
332 if (LF_ISSET(SEARCH_MSG
)) {
333 search_busy(sp
, btype
);
336 cnt
= INTERRUPT_CHECK
;
338 if ((wrapped
&& lno
< fm
->lno
) || lno
== 0) {
340 if (LF_ISSET(SEARCH_MSG
))
341 search_msg(sp
, S_NOTFOUND
);
344 if (!LF_ISSET(SEARCH_WRAP
)) {
345 if (LF_ISSET(SEARCH_MSG
))
346 search_msg(sp
, S_SOF
);
349 if (db_last(sp
, &lno
))
352 if (LF_ISSET(SEARCH_MSG
))
353 search_msg(sp
, S_EMPTY
);
361 if (db_get(sp
, lno
, 0, &l
, &len
))
364 /* Set the termination. */
366 match
[0].rm_eo
= len
;
368 #if defined(DEBUG) && 0
370 "B search: %lu from 0 to %qu\n", lno
, match
[0].rm_eo
);
372 /* Search the line. */
373 eval
= regexec(&sp
->re_c
, l
, 1, match
,
374 ((size_t)match
[0].rm_eo
== len
? 0 : REG_NOTEOL
) | REG_STARTEND
);
375 if (eval
== REG_NOMATCH
)
378 if (LF_ISSET(SEARCH_MSG
))
379 re_error(sp
, eval
, &sp
->re_c
);
381 (void)sp
->gp
->scr_bell(sp
);
385 /* Check for a match starting past the cursor. */
386 if (coff
!= 0 && (size_t)match
[0].rm_so
>= coff
)
389 /* Warn if the search wrapped. */
390 if (wrapped
&& LF_ISSET(SEARCH_WMSG
))
391 search_msg(sp
, S_WRAP
);
393 #if defined(DEBUG) && 0
394 vtrace(sp
, "B found: %qu to %qu\n",
395 match
[0].rm_so
, match
[0].rm_eo
);
398 * We now have the first match on the line. Step through the
399 * line character by character until find the last acceptable
400 * match. This is painful, we need a better interface to regex
404 last
= match
[0].rm_so
++;
405 if ((size_t)match
[0].rm_so
>= len
)
407 match
[0].rm_eo
= len
;
408 eval
= regexec(&sp
->re_c
, l
, 1, match
,
409 (match
[0].rm_so
== 0 ? 0 : REG_NOTBOL
) |
411 if (eval
== REG_NOMATCH
)
414 if (LF_ISSET(SEARCH_MSG
))
415 re_error(sp
, eval
, &sp
->re_c
);
417 (void)sp
->gp
->scr_bell(sp
);
420 if (coff
&& (size_t)match
[0].rm_so
>= coff
)
425 /* See comment in f_search(). */
426 if (!LF_ISSET(SEARCH_EOL
) && last
>= len
)
427 rm
->cno
= len
!= 0 ? len
- 1 : 0;
434 err
: if (LF_ISSET(SEARCH_MSG
))
435 search_busy(sp
, BUSY_OFF
);
441 * Display one of the search messages.
444 search_msg(SCR
*sp
, smsg_t msg
)
448 msgq(sp
, M_ERR
, "072|File empty; nothing to search");
452 "073|Reached end-of-file without finding the pattern");
455 msgq(sp
, M_ERR
, "074|No previous search pattern");
458 msgq(sp
, M_ERR
, "075|Pattern not found");
462 "076|Reached top-of-file without finding the pattern");
465 msgq(sp
, M_ERR
, "077|Search wrapped");
474 * Put up the busy searching message.
476 * PUBLIC: void search_busy __P((SCR *, busy_t));
479 search_busy(SCR
*sp
, busy_t btype
)
481 sp
->gp
->scr_busy(sp
, "078|Searching...", btype
);