1 /* $NetBSD: search.c,v 1.3 2014/01/26 21:43:45 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.
13 #include <sys/cdefs.h>
16 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 ";
19 __RCSID("$NetBSD: search.c,v 1.3 2014/01/26 21:43:45 christos Exp $");
22 #include <sys/types.h>
23 #include <sys/queue.h>
25 #include <bitstring.h>
36 typedef enum { S_EMPTY
, S_EOF
, S_NOPREV
, S_NOTFOUND
, S_SOF
, S_WRAP
} smsg_t
;
38 static void search_msg
__P((SCR
*, smsg_t
));
39 static int search_init
__P((SCR
*, dir_t
, CHAR_T
*, size_t, CHAR_T
**, u_int
));
46 search_init(SCR
*sp
, dir_t dir
, CHAR_T
*ptrn
, size_t plen
, CHAR_T
**epp
, u_int flags
)
52 /* If the file is empty, it's a fast search. */
54 if (db_last(sp
, &lno
))
57 if (LF_ISSET(SEARCH_MSG
))
58 search_msg(sp
, S_EMPTY
);
63 if (LF_ISSET(SEARCH_PARSE
)) { /* Parse the string. */
65 * Use the saved pattern if no pattern specified, or if only
66 * one or two delimiter characters specified.
69 * Historically, only the pattern itself was saved, vi didn't
70 * preserve addressing or delta information.
79 if (ptrn
[0] == ptrn
[1]) {
83 /* Complain if we don't have a previous pattern. */
84 prev
: if (sp
->re
== NULL
) {
85 search_msg(sp
, S_NOPREV
);
88 /* Re-compile the search pattern if necessary. */
89 if (!F_ISSET(sp
, SC_RE_SEARCH
) && re_compile(sp
,
90 sp
->re
, sp
->re_len
, NULL
, NULL
, &sp
->re_c
,
91 SEARCH_CSEARCH
| SEARCH_MSG
))
94 /* Set the search direction. */
95 if (LF_ISSET(SEARCH_SET
))
101 * Set the delimiter, and move forward to the terminating
102 * delimiter, handling escaped delimiters.
105 * Only discard an escape character if it escapes a delimiter.
107 for (delim
= *ptrn
, p
= t
= ++ptrn
;; *t
++ = *p
++) {
108 if (--plen
== 0 || p
[0] == delim
) {
113 if (plen
> 1 && p
[0] == '\\' && p
[1] == delim
) {
124 /* Compile the RE. */
125 if (re_compile(sp
, ptrn
, plen
, &sp
->re
, &sp
->re_len
, &sp
->re_c
,
126 SEARCH_CSEARCH
| LF_ISSET(SEARCH_CSCOPE
| SEARCH_EXTEND
|
127 SEARCH_IC
| SEARCH_LITERAL
| SEARCH_MSG
| SEARCH_TAG
)))
130 /* Set the search direction. */
131 if (LF_ISSET(SEARCH_SET
))
139 * Do a forward search.
141 * PUBLIC: int f_search __P((SCR *,
142 * PUBLIC: MARK *, MARK *, CHAR_T *, size_t, CHAR_T **, u_int));
145 f_search(SCR
*sp
, MARK
*fm
, MARK
*rm
, CHAR_T
*ptrn
, size_t plen
, CHAR_T
**eptrn
, u_int flags
)
151 int cnt
, eval
, rval
, wrapped
;
154 if (search_init(sp
, FORWARD
, ptrn
, plen
, eptrn
, flags
))
157 /* Figure out if we're going to wrap. */
158 if (!LF_ISSET(SEARCH_NOOPT
) && O_ISSET(sp
, O_WRAPSCAN
))
161 if (LF_ISSET(SEARCH_FIRST
)) {
165 if (db_get(sp
, fm
->lno
, DBG_FATAL
, &l
, &len
))
170 * If doing incremental search, start searching at the previous
171 * column, so that we search a minimal distance and still match
172 * special patterns, e.g., \< for beginning of a word.
174 * Otherwise, start searching immediately after the cursor. If
175 * at the end of the line, start searching on the next line.
176 * This is incompatible (read bug fix) with the historic vi --
177 * searches for the '$' pattern never moved forward, and the
178 * "-t foo" didn't work if the 'f' was the first character in
181 if (LF_ISSET(SEARCH_INCR
)) {
182 if ((coff
= fm
->cno
) != 0)
184 } else if (fm
->cno
+ 1 >= len
) {
187 if (db_get(sp
, lno
, 0, &l
, &len
)) {
188 if (!LF_ISSET(SEARCH_WRAP
)) {
189 if (LF_ISSET(SEARCH_MSG
))
190 search_msg(sp
, S_EOF
);
200 for (cnt
= INTERRUPT_CHECK
, rval
= 1, wrapped
= 0;; ++lno
, coff
= 0) {
204 if (LF_ISSET(SEARCH_MSG
)) {
205 search_busy(sp
, btype
);
208 cnt
= INTERRUPT_CHECK
;
210 if ((wrapped
&& lno
> fm
->lno
) ||
211 db_get(sp
, lno
, 0, &l
, &len
)) {
213 if (LF_ISSET(SEARCH_MSG
))
214 search_msg(sp
, S_NOTFOUND
);
217 if (!LF_ISSET(SEARCH_WRAP
)) {
218 if (LF_ISSET(SEARCH_MSG
))
219 search_msg(sp
, S_EOF
);
227 /* If already at EOL, just keep going. */
228 if (len
!= 0 && coff
== len
)
231 /* Set the termination. */
232 match
[0].rm_so
= coff
;
233 match
[0].rm_eo
= len
;
235 #if defined(DEBUG) && 0
236 vtrace(sp
, "F search: %lu from %u to %u\n",
237 lno
, coff
, len
!= 0 ? len
- 1 : len
);
239 /* Search the line. */
240 eval
= regexec(&sp
->re_c
, l
, 1, match
,
241 (match
[0].rm_so
== 0 ? 0 : REG_NOTBOL
) | REG_STARTEND
);
242 if (eval
== REG_NOMATCH
)
245 if (LF_ISSET(SEARCH_MSG
))
246 re_error(sp
, eval
, &sp
->re_c
);
248 (void)sp
->gp
->scr_bell(sp
);
252 /* Warn if the search wrapped. */
253 if (wrapped
&& LF_ISSET(SEARCH_WMSG
))
254 search_msg(sp
, S_WRAP
);
256 #if defined(DEBUG) && 0
257 vtrace(sp
, "F search: %qu to %qu\n",
258 match
[0].rm_so
, match
[0].rm_eo
);
261 rm
->cno
= match
[0].rm_so
;
264 * If a change command, it's possible to move beyond the end
265 * of a line. Historic vi generally got this wrong (e.g. try
266 * "c?$<cr>"). Not all that sure this gets it right, there
267 * are lots of strange cases.
269 if (!LF_ISSET(SEARCH_EOL
) && rm
->cno
>= len
)
270 rm
->cno
= len
!= 0 ? len
- 1 : 0;
276 if (LF_ISSET(SEARCH_MSG
))
277 search_busy(sp
, BUSY_OFF
);
283 * Do a backward search.
285 * PUBLIC: int b_search __P((SCR *,
286 * PUBLIC: MARK *, MARK *, CHAR_T *, size_t, CHAR_T **, u_int));
289 b_search(SCR
*sp
, MARK
*fm
, MARK
*rm
, CHAR_T
*ptrn
, size_t plen
, CHAR_T
**eptrn
, u_int flags
)
294 size_t coff
, last
, len
;
295 int cnt
, eval
, rval
, wrapped
;
298 if (search_init(sp
, BACKWARD
, ptrn
, plen
, eptrn
, flags
))
301 /* Figure out if we're going to wrap. */
302 if (!LF_ISSET(SEARCH_NOOPT
) && O_ISSET(sp
, O_WRAPSCAN
))
306 * If doing incremental search, set the "starting" position past the
307 * current column, so that we search a minimal distance and still
308 * match special patterns, e.g., \> for the end of a word. This is
309 * safe when the cursor is at the end of a line because we only use
310 * it for comparison with the location of the match.
312 * Otherwise, start searching immediately before the cursor. If in
313 * the first column, start search on the previous line.
315 if (LF_ISSET(SEARCH_INCR
)) {
320 if (fm
->lno
== 1 && !LF_ISSET(SEARCH_WRAP
)) {
321 if (LF_ISSET(SEARCH_MSG
))
322 search_msg(sp
, S_SOF
);
332 for (cnt
= INTERRUPT_CHECK
, rval
= 1, wrapped
= 0;; --lno
, coff
= 0) {
336 if (LF_ISSET(SEARCH_MSG
)) {
337 search_busy(sp
, btype
);
340 cnt
= INTERRUPT_CHECK
;
342 if ((wrapped
&& lno
< fm
->lno
) || lno
== 0) {
344 if (LF_ISSET(SEARCH_MSG
))
345 search_msg(sp
, S_NOTFOUND
);
348 if (!LF_ISSET(SEARCH_WRAP
)) {
349 if (LF_ISSET(SEARCH_MSG
))
350 search_msg(sp
, S_SOF
);
353 if (db_last(sp
, &lno
))
356 if (LF_ISSET(SEARCH_MSG
))
357 search_msg(sp
, S_EMPTY
);
365 if (db_get(sp
, lno
, 0, &l
, &len
))
368 /* Set the termination. */
370 match
[0].rm_eo
= len
;
372 #if defined(DEBUG) && 0
374 "B search: %lu from 0 to %qu\n", lno
, match
[0].rm_eo
);
376 /* Search the line. */
377 eval
= regexec(&sp
->re_c
, l
, 1, match
,
378 ((size_t)match
[0].rm_eo
== len
? 0 : REG_NOTEOL
) | REG_STARTEND
);
379 if (eval
== REG_NOMATCH
)
382 if (LF_ISSET(SEARCH_MSG
))
383 re_error(sp
, eval
, &sp
->re_c
);
385 (void)sp
->gp
->scr_bell(sp
);
389 /* Check for a match starting past the cursor. */
390 if (coff
!= 0 && (size_t)match
[0].rm_so
>= coff
)
393 /* Warn if the search wrapped. */
394 if (wrapped
&& LF_ISSET(SEARCH_WMSG
))
395 search_msg(sp
, S_WRAP
);
397 #if defined(DEBUG) && 0
398 vtrace(sp
, "B found: %qu to %qu\n",
399 match
[0].rm_so
, match
[0].rm_eo
);
402 * We now have the first match on the line. Step through the
403 * line character by character until find the last acceptable
404 * match. This is painful, we need a better interface to regex
408 last
= match
[0].rm_so
++;
409 if ((size_t)match
[0].rm_so
>= len
)
411 match
[0].rm_eo
= len
;
412 eval
= regexec(&sp
->re_c
, l
, 1, match
,
413 (match
[0].rm_so
== 0 ? 0 : REG_NOTBOL
) |
415 if (eval
== REG_NOMATCH
)
418 if (LF_ISSET(SEARCH_MSG
))
419 re_error(sp
, eval
, &sp
->re_c
);
421 (void)sp
->gp
->scr_bell(sp
);
424 if (coff
&& (size_t)match
[0].rm_so
>= coff
)
429 /* See comment in f_search(). */
430 if (!LF_ISSET(SEARCH_EOL
) && last
>= len
)
431 rm
->cno
= len
!= 0 ? len
- 1 : 0;
438 err
: if (LF_ISSET(SEARCH_MSG
))
439 search_busy(sp
, BUSY_OFF
);
445 * Display one of the search messages.
448 search_msg(SCR
*sp
, smsg_t msg
)
452 msgq(sp
, M_ERR
, "072|File empty; nothing to search");
456 "073|Reached end-of-file without finding the pattern");
459 msgq(sp
, M_ERR
, "074|No previous search pattern");
462 msgq(sp
, M_ERR
, "075|Pattern not found");
466 "076|Reached top-of-file without finding the pattern");
469 msgq(sp
, M_ERR
, "077|Search wrapped");
478 * Put up the busy searching message.
480 * PUBLIC: void search_busy __P((SCR *, busy_t));
483 search_busy(SCR
*sp
, busy_t btype
)
485 sp
->gp
->scr_busy(sp
, "078|Searching...", btype
);