2 * Copyright (c) 1992, 1993
3 * The Regents of the University of California. All rights reserved.
5 * %sccs.include.redist.c%
9 static char sccsid
[] = "$Id: search.c,v 8.28 1993/11/18 16:16:07 bostic Exp $ (Berkeley) $Date: 1993/11/18 16:16:07 $";
12 #include <sys/types.h>
22 static int check_delta
__P((SCR
*, EXF
*, long, recno_t
));
23 static int ctag_conv
__P((SCR
*, char **, int *));
24 static int get_delta
__P((SCR
*, char **, long *, u_int
*));
25 static int resetup
__P((SCR
*, regex_t
**, enum direction
,
26 char *, char **, long *, u_int
*));
27 static void search_intr
__P((int));
31 * Set up a search for a regular expression.
34 resetup(sp
, rep
, dir
, ptrn
, epp
, deltap
, flagp
)
43 int delim
, eval
, re_flags
, replaced
;
46 /* Set return information the default. */
50 * Use saved pattern if no pattern supplied, or if only a delimiter
51 * character is supplied. Only the pattern was saved, historic vi
52 * did not reuse any delta supplied.
55 if (ptrn
== NULL
|| ptrn
[1] == '\0') {
56 if (!F_ISSET(sp
, S_SRE_SET
)) {
57 noprev
: msgq(sp
, M_INFO
, "No previous search pattern.");
62 /* Empty patterns set the direction. */
63 if (LF_ISSET(SEARCH_SET
)) {
71 re_flags
= 0; /* Set RE flags. */
72 if (O_ISSET(sp
, O_EXTENDED
))
73 re_flags
|= REG_EXTENDED
;
74 if (O_ISSET(sp
, O_IGNORECASE
))
75 re_flags
|= REG_ICASE
;
77 if (LF_ISSET(SEARCH_PARSE
)) { /* Parse the string. */
81 /* Find terminating delimiter, handling escaped delimiters. */
82 for (p
= t
= ptrn
;;) {
83 if (p
[0] == '\0' || p
[0] == delim
) {
89 if (p
[1] == delim
&& p
[0] == '\\')
95 * If characters after the terminating delimiter, it may
96 * be an error, or may be an offset. In either case, we
97 * return the end of the string, whatever it may be, or
98 * change the end pointer to reference a NULL. Don't just
99 * whack the string, in case it's text space.
102 if (get_delta(sp
, &p
, deltap
, flagp
))
104 if (*p
&& LF_ISSET(SEARCH_TERM
)) {
106 "Characters after search string and/or delta.");
113 * STATIC: NEVER WRITTEN.
114 * Can't be const, because the normal case isn't.
121 /* If the pattern was empty, use the previous pattern. */
122 if (ptrn
== NULL
|| *ptrn
== '\0') {
123 if (!F_ISSET(sp
, S_SRE_SET
))
129 if (re_conv(sp
, &ptrn
, &replaced
))
131 } else if (LF_ISSET(SEARCH_TAG
)) {
132 if (ctag_conv(sp
, &ptrn
, &replaced
))
134 re_flags
&= ~(REG_EXTENDED
| REG_ICASE
);
137 /* Compile the RE. */
138 if (eval
= regcomp(*rep
, ptrn
, re_flags
))
139 re_error(sp
, eval
, *rep
);
140 else if (LF_ISSET(SEARCH_SET
)) {
141 F_SET(sp
, S_SRE_SET
);
146 /* Free up any extra memory. */
148 FREE_SPACE(sp
, ptrn
, 0);
154 * Convert a tags search path into something that the POSIX
155 * 1003.2 RE functions can handle.
158 ctag_conv(sp
, ptrnp
, replacedp
)
169 len
= strlen(p
= *ptrnp
);
171 /* Max memory usage is 2 times the length of the string. */
172 GET_SPACE(sp
, bp
, blen
, len
* 2);
176 /* The last charcter is a '/' or '?', we just strip it. */
177 if (p
[len
- 1] == '/' || p
[len
- 1] == '?')
180 /* The next-to-last character is a '$', and it's magic. */
181 if (p
[len
- 2] == '$') {
187 /* The first character is a '/' or '?', we just strip it. */
188 if (p
[0] == '/' || p
[0] == '?')
191 /* The second character is a '^', and it's magic. */
196 * Escape every other magic character we can find, stripping the
197 * backslashes ctags inserts to escape the search delimiter
201 /* Ctags escapes the search delimiter characters. */
202 if (p
[0] == '\\' && (p
[1] == '/' || p
[1] == '?'))
204 else if (strchr("^.[]$*", p
[0]))
220 * ISIG turns on VINTR, VQUIT and VSUSP. We want VINTR to interrupt the
221 * search, so we install a handler. VQUIT is ignored by main() because
222 * nvi never wants to catch it. A handler for VSUSP should have been
223 * installed by the screen code.
225 #define SET_UP_INTERRUPTS { \
226 act.sa_handler = search_intr; \
227 sigemptyset(&act.sa_mask); \
229 if (isig = !sigaction(SIGINT, &act, &oact)) { \
230 istate = F_ISSET(sp, S_INTERRUPTIBLE); \
231 F_CLR(sp, S_INTERRUPTED); \
232 F_SET(sp, S_INTERRUPTIBLE); \
233 if (tcgetattr(STDIN_FILENO, &term)) { \
234 msgq(sp, M_SYSERR, "tcgetattr"); \
238 nterm.c_lflag |= ISIG; \
239 if (tcsetattr(STDIN_FILENO, \
240 TCSANOW | TCSASOFT, &nterm)) { \
241 msgq(sp, M_SYSERR, "tcsetattr"); \
247 #define TEAR_DOWN_INTERRUPTS { \
249 if (sigaction(SIGINT, &oact, NULL)) \
250 msgq(sp, M_SYSERR, "signal"); \
251 if (tcsetattr(STDIN_FILENO, TCSANOW | TCSASOFT, &term)) \
252 msgq(sp, M_SYSERR, "tcsetattr"); \
253 F_CLR(sp, S_INTERRUPTED); \
255 F_CLR(sp, S_INTERRUPTIBLE); \
261 * Set the interrupt bit in any screen that is interruptible.
264 * In the future this may be a problem. The user should be able to move to
265 * another screen and keep typing while this runs. If so, and the user has
266 * more than one search/global (see ex/ex_global.c) running, it will be hard
267 * to decide which one to stop.
275 for (sp
= __global_list
->dq
.cqh_first
;
276 sp
!= (void *)&__global_list
->dq
; sp
= sp
->q
.cqe_next
)
277 if (F_ISSET(sp
, S_INTERRUPTIBLE
))
278 F_SET(sp
, S_INTERRUPTED
);
281 #define EMPTYMSG "File empty; nothing to search."
282 #define EOFMSG "Reached end-of-file without finding the pattern."
283 #define NOTFOUND "Pattern not found."
284 #define SOFMSG "Reached top-of-file without finding the pattern."
285 #define WRAPMSG "Search wrapped."
288 f_search(sp
, ep
, fm
, rm
, ptrn
, eptrn
, flagp
)
295 struct sigaction act
, oact
;
296 struct termios nterm
, term
;
303 int eval
, isig
, rval
, wrapped
;
306 if (file_lline(sp
, ep
, &lno
))
310 if (LF_ISSET(SEARCH_MSG
))
311 msgq(sp
, M_INFO
, EMPTYMSG
);
316 if (resetup(sp
, &re
, FORWARD
, ptrn
, eptrn
, &delta
, flagp
))
320 * Start searching immediately after the cursor. If at the end of the
321 * line, start searching on the next line. This is incompatible (read
322 * bug fix) with the historic vi -- searches for the '$' pattern never
323 * moved forward, and "-t foo" didn't work if "foo" was the first thing
326 if (LF_ISSET(SEARCH_FILE
)) {
330 if ((l
= file_gline(sp
, ep
, fm
->lno
, &len
)) == NULL
) {
331 GETLINE_ERR(sp
, fm
->lno
);
334 if (fm
->cno
+ 1 >= len
) {
335 if (fm
->lno
== lno
) {
336 if (!O_ISSET(sp
, O_WRAPSCAN
)) {
337 if (LF_ISSET(SEARCH_MSG
))
338 msgq(sp
, M_INFO
, EOFMSG
);
352 * Set up busy message, interrupts.
354 * F_search is called from the ex_tagfirst() routine, which runs
355 * before the screen really exists. Make sure we don't step on
358 if (sp
->s_position
!= NULL
)
359 busy_on(sp
, 1, "Searching...");
361 if (F_ISSET(sp
->gp
, G_ISFROMTTY
))
364 for (rval
= 1, wrapped
= 0;; ++lno
, coff
= 0) {
365 if (F_ISSET(sp
, S_INTERRUPTED
)) {
366 msgq(sp
, M_INFO
, "Interrupted.");
369 if (wrapped
&& lno
> fm
->lno
||
370 (l
= file_gline(sp
, ep
, lno
, &len
)) == NULL
) {
372 if (LF_ISSET(SEARCH_MSG
))
373 msgq(sp
, M_INFO
, NOTFOUND
);
376 if (!O_ISSET(sp
, O_WRAPSCAN
)) {
377 if (LF_ISSET(SEARCH_MSG
))
378 msgq(sp
, M_INFO
, EOFMSG
);
386 /* If already at EOL, just keep going. */
387 if (len
&& coff
== len
)
390 /* Set the termination. */
391 match
[0].rm_so
= coff
;
392 match
[0].rm_eo
= len
;
394 #if defined(DEBUG) && 0
395 TRACE(sp
, "F search: %lu from %u to %u\n",
396 lno
, coff
, len
? len
- 1 : len
);
398 /* Search the line. */
399 eval
= regexec(re
, l
, 1, match
,
400 (match
[0].rm_so
== 0 ? 0 : REG_NOTBOL
) | REG_STARTEND
);
401 if (eval
== REG_NOMATCH
)
404 re_error(sp
, eval
, re
);
408 /* Warn if wrapped. */
409 if (wrapped
&& O_ISSET(sp
, O_WARN
) && LF_ISSET(SEARCH_MSG
))
410 msgq(sp
, M_INFO
, WRAPMSG
);
413 * If an offset, see if it's legal. It's possible to match
414 * past the end of the line with $, so check for that case.
417 if (check_delta(sp
, ep
, delta
, lno
))
419 rm
->lno
= delta
+ lno
;
422 #if defined(DEBUG) && 0
423 TRACE(sp
, "found: %qu to %qu\n",
424 match
[0].rm_so
, match
[0].rm_eo
);
427 rm
->cno
= match
[0].rm_so
;
430 * If a change command, it's possible to move beyond
431 * the end of a line. Historic vi generally got this
432 * wrong (try "c?$<cr>"). Not all that sure this gets
433 * it right, there are lots of strange cases.
435 if (!LF_ISSET(SEARCH_EOL
) && rm
->cno
>= len
)
436 rm
->cno
= len
? len
- 1 : 0;
442 /* Turn off busy message, interrupts. */
443 if (sp
->s_position
!= NULL
)
446 if (F_ISSET(sp
->gp
, G_ISFROMTTY
))
447 TEAR_DOWN_INTERRUPTS
;
453 b_search(sp
, ep
, fm
, rm
, ptrn
, eptrn
, flagp
)
460 struct sigaction act
, oact
;
461 struct termios nterm
, term
;
465 size_t coff
, len
, last
;
468 int eval
, isig
, rval
, wrapped
;
471 if (file_lline(sp
, ep
, &lno
))
475 if (LF_ISSET(SEARCH_MSG
))
476 msgq(sp
, M_INFO
, EMPTYMSG
);
481 if (resetup(sp
, &re
, BACKWARD
, ptrn
, eptrn
, &delta
, flagp
))
484 /* If in the first column, start searching on the previous line. */
487 if (!O_ISSET(sp
, O_WRAPSCAN
)) {
488 if (LF_ISSET(SEARCH_MSG
))
489 msgq(sp
, M_INFO
, SOFMSG
);
497 /* Turn on busy message, interrupts. */
498 busy_on(sp
, 1, "Searching...");
500 if (F_ISSET(sp
->gp
, G_ISFROMTTY
))
503 for (rval
= 1, wrapped
= 0, coff
= fm
->cno
;; --lno
, coff
= 0) {
504 if (F_ISSET(sp
, S_INTERRUPTED
)) {
505 msgq(sp
, M_INFO
, "Interrupted.");
508 if (wrapped
&& lno
< fm
->lno
|| lno
== 0) {
510 if (LF_ISSET(SEARCH_MSG
))
511 msgq(sp
, M_INFO
, NOTFOUND
);
514 if (!O_ISSET(sp
, O_WRAPSCAN
)) {
515 if (LF_ISSET(SEARCH_MSG
))
516 msgq(sp
, M_INFO
, SOFMSG
);
519 if (file_lline(sp
, ep
, &lno
))
522 if (LF_ISSET(SEARCH_MSG
))
523 msgq(sp
, M_INFO
, EMPTYMSG
);
531 if ((l
= file_gline(sp
, ep
, lno
, &len
)) == NULL
)
534 /* Set the termination. */
536 match
[0].rm_eo
= coff
? coff
: len
;
538 #if defined(DEBUG) && 0
539 TRACE(sp
, "B search: %lu from 0 to %qu\n", lno
, match
[0].rm_eo
);
541 /* Search the line. */
542 eval
= regexec(re
, l
, 1, match
,
543 (match
[0].rm_eo
== len
? 0 : REG_NOTEOL
) | REG_STARTEND
);
544 if (eval
== REG_NOMATCH
)
547 re_error(sp
, eval
, re
);
551 /* Warn if wrapped. */
552 if (wrapped
&& O_ISSET(sp
, O_WARN
) && LF_ISSET(SEARCH_MSG
))
553 msgq(sp
, M_INFO
, WRAPMSG
);
556 if (check_delta(sp
, ep
, delta
, lno
))
558 rm
->lno
= delta
+ lno
;
561 #if defined(DEBUG) && 0
562 TRACE(sp
, "found: %qu to %qu\n",
563 match
[0].rm_so
, match
[0].rm_eo
);
566 * Find the last acceptable one in this line. This
567 * is really painful, we need a cleaner interface to
568 * regexec to make this possible.
571 last
= match
[0].rm_so
;
572 match
[0].rm_so
= match
[0].rm_eo
+ 1;
573 if (match
[0].rm_so
>= len
||
574 coff
&& match
[0].rm_so
>= coff
)
576 match
[0].rm_eo
= coff
? coff
: len
;
577 eval
= regexec(re
, l
, 1, match
,
578 (match
[0].rm_so
== 0 ? 0 : REG_NOTBOL
) |
580 if (eval
== REG_NOMATCH
)
583 re_error(sp
, eval
, re
);
589 /* See comment in f_search(). */
590 if (!LF_ISSET(SEARCH_EOL
) && last
>= len
)
591 rm
->cno
= len
? len
- 1 : 0;
599 /* Turn off busy message, interrupts. */
602 if (F_ISSET(sp
->gp
, G_ISFROMTTY
))
603 TEAR_DOWN_INTERRUPTS
;
610 * Convert vi's regular expressions into something that the
611 * the POSIX 1003.2 RE functions can handle.
613 * There are three conversions we make to make vi's RE's (specifically
614 * the global, search, and substitute patterns) work with POSIX RE's.
616 * 1: If O_MAGIC is not set, strip backslashes from the magic character
617 * set (.[]*~) that have them, and add them to the ones that don't.
618 * 2: If O_MAGIC is not set, the string "\~" is replaced with the text
619 * from the last substitute command's replacement string. If O_MAGIC
620 * is set, it's the string "~".
621 * 3: The pattern \<ptrn\> does "word" searches, convert it to use the
625 re_conv(sp
, ptrnp
, replacedp
)
630 size_t blen
, needlen
;
635 * First pass through, we figure out how much space we'll need.
636 * We do it in two passes, on the grounds that most of the time
637 * the user is doing a search and won't have magic characters.
638 * That way we can skip the malloc and memmove's.
640 for (p
= *ptrnp
, magic
= 0, needlen
= 0; *p
!= '\0'; ++p
)
646 needlen
+= sizeof(RE_WSTART
);
650 needlen
+= sizeof(RE_WSTOP
);
653 if (!O_ISSET(sp
, O_MAGIC
)) {
655 needlen
+= sp
->repl_len
;
662 if (!O_ISSET(sp
, O_MAGIC
)) {
672 if (O_ISSET(sp
, O_MAGIC
)) {
674 needlen
+= sp
->repl_len
;
681 if (!O_ISSET(sp
, O_MAGIC
)) {
697 * Get enough memory to hold the final pattern.
700 * It's nul-terminated, for now.
702 GET_SPACE(sp
, bp
, blen
, needlen
+ 1);
704 for (p
= *ptrnp
, t
= bp
; *p
!= '\0'; ++p
)
709 memmove(t
, RE_WSTART
, sizeof(RE_WSTART
) - 1);
710 t
+= sizeof(RE_WSTART
) - 1;
713 memmove(t
, RE_WSTOP
, sizeof(RE_WSTOP
) - 1);
714 t
+= sizeof(RE_WSTOP
) - 1;
717 if (O_ISSET(sp
, O_MAGIC
))
720 memmove(t
, sp
->repl
, sp
->repl_len
);
728 if (O_ISSET(sp
, O_MAGIC
))
738 if (O_ISSET(sp
, O_MAGIC
)) {
739 memmove(t
, sp
->repl
, sp
->repl_len
);
748 if (!O_ISSET(sp
, O_MAGIC
))
765 * Get a line delta. The trickiness is that the delta can be pretty
766 * complicated, i.e. "+3-2+3++- ++" is allowed.
769 * In historic vi, if you had a delta on a search pattern which was used as
770 * a motion command, the command became a line mode command regardless of the
771 * cursor positions. A fairly common trick is to use a delta of "+0" to make
772 * the command a line mode command. This is the only place that knows about
773 * delta's, so we set the return flag information here.
776 get_delta(sp
, dp
, valp
, flagp
)
785 for (tval
= 0, p
= *dp
; *p
!= '\0'; *flagp
|= SEARCH_DELTA
) {
790 if (*p
== '+' || *p
== '-') {
791 if (!isdigit(*(p
+ 1))) {
793 if (tval
== LONG_MAX
)
797 if (tval
== LONG_MIN
)
809 val
= strtol(p
, &p
, 10);
810 if (errno
== ERANGE
) {
812 overflow
: msgq(sp
, M_ERR
, "Delta value overflow.");
813 else if (val
== LONG_MIN
)
814 underflow
: msgq(sp
, M_ERR
, "Delta value underflow.");
816 msgq(sp
, M_SYSERR
, NULL
);
820 if (LONG_MAX
- val
< tval
)
823 if (-(LONG_MIN
- tval
) > val
)
834 * Check a line delta to see if it's legal.
837 check_delta(sp
, ep
, delta
, lno
)
843 /* A delta can overflow a record number. */
845 if (lno
< LONG_MAX
&& delta
>= (long)lno
) {
846 msgq(sp
, M_ERR
, "Search offset before line 1.");
850 if (ULONG_MAX
- lno
< delta
) {
851 msgq(sp
, M_ERR
, "Delta value overflow.");
854 if (file_gline(sp
, ep
, lno
+ delta
, NULL
) == NULL
) {
855 msgq(sp
, M_ERR
, "Search offset past end-of-file.");
864 * Report a regular expression error.
867 re_error(sp
, errcode
, preg
)
875 s
= regerror(errcode
, preg
, "", 0);
876 if ((oe
= malloc(s
)) == NULL
)
877 msgq(sp
, M_SYSERR
, NULL
);
879 (void)regerror(errcode
, preg
, oe
, s
);
880 msgq(sp
, M_ERR
, "RE error: %s", oe
);