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.32 1994/01/09 17:55:45 bostic Exp $ (Berkeley) $Date: 1994/01/09 17:55:45 $";
12 #include <sys/types.h>
21 #include "interrupt.h"
23 static int check_delta
__P((SCR
*, EXF
*, long, recno_t
));
24 static int ctag_conv
__P((SCR
*, char **, int *));
25 static int get_delta
__P((SCR
*, char **, long *, u_int
*));
26 static int resetup
__P((SCR
*, regex_t
**, enum direction
,
27 char *, char **, long *, u_int
*));
28 static void search_intr
__P((int));
32 * Set up a search for a regular expression.
35 resetup(sp
, rep
, dir
, ptrn
, epp
, deltap
, flagp
)
44 int delim
, eval
, re_flags
, replaced
;
47 /* Set return information the default. */
51 * Use saved pattern if no pattern supplied, or if only a delimiter
52 * character is supplied. Only the pattern was saved, historic vi
53 * did not reuse any delta supplied.
58 if (ptrn
[1] == '\0') {
63 if (ptrn
[0] == ptrn
[1] && ptrn
[2] == '\0') {
66 prev
: if (!F_ISSET(sp
, S_SRE_SET
)) {
67 msgq(sp
, M_ERR
, "No previous search pattern.");
72 /* Empty patterns set the direction. */
73 if (LF_ISSET(SEARCH_SET
)) {
81 re_flags
= 0; /* Set RE flags. */
82 if (O_ISSET(sp
, O_EXTENDED
))
83 re_flags
|= REG_EXTENDED
;
84 if (O_ISSET(sp
, O_IGNORECASE
))
85 re_flags
|= REG_ICASE
;
87 if (LF_ISSET(SEARCH_PARSE
)) { /* Parse the string. */
91 /* Find terminating delimiter, handling escaped delimiters. */
92 for (p
= t
= ptrn
;;) {
93 if (p
[0] == '\0' || p
[0] == delim
) {
99 if (p
[1] == delim
&& p
[0] == '\\')
105 * If characters after the terminating delimiter, it may
106 * be an error, or may be an offset. In either case, we
107 * return the end of the string, whatever it may be.
110 if (get_delta(sp
, &p
, deltap
, flagp
))
112 if (*p
&& LF_ISSET(SEARCH_TERM
)) {
114 "Characters after search string and/or delta.");
121 /* Check for "/ " or other such silliness. */
125 if (re_conv(sp
, &ptrn
, &replaced
))
127 } else if (LF_ISSET(SEARCH_TAG
)) {
128 if (ctag_conv(sp
, &ptrn
, &replaced
))
130 re_flags
&= ~(REG_EXTENDED
| REG_ICASE
);
133 /* Compile the RE. */
134 if (eval
= regcomp(*rep
, ptrn
, re_flags
))
135 re_error(sp
, eval
, *rep
);
136 else if (LF_ISSET(SEARCH_SET
)) {
137 F_SET(sp
, S_SRE_SET
);
142 /* Free up any extra memory. */
144 FREE_SPACE(sp
, ptrn
, 0);
150 * Convert a tags search path into something that the POSIX
151 * 1003.2 RE functions can handle.
154 ctag_conv(sp
, ptrnp
, replacedp
)
165 len
= strlen(p
= *ptrnp
);
167 /* Max memory usage is 2 times the length of the string. */
168 GET_SPACE_RET(sp
, bp
, blen
, len
* 2);
172 /* The last charcter is a '/' or '?', we just strip it. */
173 if (p
[len
- 1] == '/' || p
[len
- 1] == '?')
176 /* The next-to-last character is a '$', and it's magic. */
177 if (p
[len
- 2] == '$') {
183 /* The first character is a '/' or '?', we just strip it. */
184 if (p
[0] == '/' || p
[0] == '?')
187 /* The second character is a '^', and it's magic. */
192 * Escape every other magic character we can find, stripping the
193 * backslashes ctags inserts to escape the search delimiter
197 /* Ctags escapes the search delimiter characters. */
198 if (p
[0] == '\\' && (p
[1] == '/' || p
[1] == '?'))
200 else if (strchr("^.[]$*", p
[0]))
215 * Set the interrupt bit in any screen that is interruptible.
218 * In the future this may be a problem. The user should be able to move to
219 * another screen and keep typing while this runs. If so, and the user has
220 * more than one search/global (see ex/ex_global.c) running, it will be hard
221 * to decide which one to stop.
229 for (sp
= __global_list
->dq
.cqh_first
;
230 sp
!= (void *)&__global_list
->dq
; sp
= sp
->q
.cqe_next
)
231 if (F_ISSET(sp
, S_INTERRUPTIBLE
))
232 F_SET(sp
, S_INTERRUPTED
);
235 #define EMPTYMSG "File empty; nothing to search."
236 #define EOFMSG "Reached end-of-file without finding the pattern."
237 #define NOTFOUND "Pattern not found."
238 #define SOFMSG "Reached top-of-file without finding the pattern."
239 #define WRAPMSG "Search wrapped."
242 f_search(sp
, ep
, fm
, rm
, ptrn
, eptrn
, flagp
)
256 int eval
, rval
, wrapped
;
259 if (file_lline(sp
, ep
, &lno
))
263 if (LF_ISSET(SEARCH_MSG
))
264 msgq(sp
, M_INFO
, EMPTYMSG
);
269 if (resetup(sp
, &re
, FORWARD
, ptrn
, eptrn
, &delta
, flagp
))
273 * Start searching immediately after the cursor. If at the end of the
274 * line, start searching on the next line. This is incompatible (read
275 * bug fix) with the historic vi -- searches for the '$' pattern never
276 * moved forward, and "-t foo" didn't work if "foo" was the first thing
279 if (LF_ISSET(SEARCH_FILE
)) {
283 if ((l
= file_gline(sp
, ep
, fm
->lno
, &len
)) == NULL
) {
284 GETLINE_ERR(sp
, fm
->lno
);
287 if (fm
->cno
+ 1 >= len
) {
288 if (fm
->lno
== lno
) {
289 if (!O_ISSET(sp
, O_WRAPSCAN
)) {
290 if (LF_ISSET(SEARCH_MSG
))
291 msgq(sp
, M_INFO
, EOFMSG
);
305 * Set up busy message, interrupts.
307 * F_search is called from the ex_tagfirst() routine, which runs
308 * before the screen really exists. Make sure we don't step on
311 if (sp
->s_position
!= NULL
)
312 busy_on(sp
, 1, "Searching...");
313 SET_UP_INTERRUPTS(search_intr
);
315 for (rval
= 1, wrapped
= 0;; ++lno
, coff
= 0) {
316 if (F_ISSET(sp
, S_INTERRUPTED
)) {
317 msgq(sp
, M_INFO
, "Interrupted.");
320 if (wrapped
&& lno
> fm
->lno
||
321 (l
= file_gline(sp
, ep
, lno
, &len
)) == NULL
) {
323 if (LF_ISSET(SEARCH_MSG
))
324 msgq(sp
, M_INFO
, NOTFOUND
);
327 if (!O_ISSET(sp
, O_WRAPSCAN
)) {
328 if (LF_ISSET(SEARCH_MSG
))
329 msgq(sp
, M_INFO
, EOFMSG
);
337 /* If already at EOL, just keep going. */
338 if (len
&& coff
== len
)
341 /* Set the termination. */
342 match
[0].rm_so
= coff
;
343 match
[0].rm_eo
= len
;
345 #if defined(DEBUG) && 0
346 TRACE(sp
, "F search: %lu from %u to %u\n",
347 lno
, coff
, len
? len
- 1 : len
);
349 /* Search the line. */
350 eval
= regexec(re
, l
, 1, match
,
351 (match
[0].rm_so
== 0 ? 0 : REG_NOTBOL
) | REG_STARTEND
);
352 if (eval
== REG_NOMATCH
)
355 re_error(sp
, eval
, re
);
359 /* Warn if wrapped. */
360 if (wrapped
&& O_ISSET(sp
, O_WARN
) && LF_ISSET(SEARCH_MSG
))
361 msgq(sp
, M_INFO
, WRAPMSG
);
364 * If an offset, see if it's legal. It's possible to match
365 * past the end of the line with $, so check for that case.
368 if (check_delta(sp
, ep
, delta
, lno
))
370 rm
->lno
= delta
+ lno
;
373 #if defined(DEBUG) && 0
374 TRACE(sp
, "found: %qu to %qu\n",
375 match
[0].rm_so
, match
[0].rm_eo
);
378 rm
->cno
= match
[0].rm_so
;
381 * If a change command, it's possible to move beyond
382 * the end of a line. Historic vi generally got this
383 * wrong (try "c?$<cr>"). Not all that sure this gets
384 * it right, there are lots of strange cases.
386 if (!LF_ISSET(SEARCH_EOL
) && rm
->cno
>= len
)
387 rm
->cno
= len
? len
- 1 : 0;
395 /* Turn off busy message, interrupts. */
396 if (sp
->s_position
!= NULL
)
398 TEAR_DOWN_INTERRUPTS
;
404 b_search(sp
, ep
, fm
, rm
, ptrn
, eptrn
, flagp
)
415 size_t coff
, len
, last
;
418 int eval
, rval
, wrapped
;
421 if (file_lline(sp
, ep
, &lno
))
425 if (LF_ISSET(SEARCH_MSG
))
426 msgq(sp
, M_INFO
, EMPTYMSG
);
431 if (resetup(sp
, &re
, BACKWARD
, ptrn
, eptrn
, &delta
, flagp
))
434 /* If in the first column, start searching on the previous line. */
437 if (!O_ISSET(sp
, O_WRAPSCAN
)) {
438 if (LF_ISSET(SEARCH_MSG
))
439 msgq(sp
, M_INFO
, SOFMSG
);
447 /* Turn on busy message, interrupts. */
448 busy_on(sp
, 1, "Searching...");
450 if (F_ISSET(sp
->gp
, G_ISFROMTTY
))
451 SET_UP_INTERRUPTS(search_intr
);
453 for (rval
= 1, wrapped
= 0, coff
= fm
->cno
;; --lno
, coff
= 0) {
454 if (F_ISSET(sp
, S_INTERRUPTED
)) {
455 msgq(sp
, M_INFO
, "Interrupted.");
458 if (wrapped
&& lno
< fm
->lno
|| lno
== 0) {
460 if (LF_ISSET(SEARCH_MSG
))
461 msgq(sp
, M_INFO
, NOTFOUND
);
464 if (!O_ISSET(sp
, O_WRAPSCAN
)) {
465 if (LF_ISSET(SEARCH_MSG
))
466 msgq(sp
, M_INFO
, SOFMSG
);
469 if (file_lline(sp
, ep
, &lno
))
472 if (LF_ISSET(SEARCH_MSG
))
473 msgq(sp
, M_INFO
, EMPTYMSG
);
481 if ((l
= file_gline(sp
, ep
, lno
, &len
)) == NULL
)
484 /* Set the termination. */
486 match
[0].rm_eo
= coff
? coff
: len
;
488 #if defined(DEBUG) && 0
489 TRACE(sp
, "B search: %lu from 0 to %qu\n", lno
, match
[0].rm_eo
);
491 /* Search the line. */
492 eval
= regexec(re
, l
, 1, match
,
493 (match
[0].rm_eo
== len
? 0 : REG_NOTEOL
) | REG_STARTEND
);
494 if (eval
== REG_NOMATCH
)
497 re_error(sp
, eval
, re
);
501 /* Warn if wrapped. */
502 if (wrapped
&& O_ISSET(sp
, O_WARN
) && LF_ISSET(SEARCH_MSG
))
503 msgq(sp
, M_INFO
, WRAPMSG
);
506 if (check_delta(sp
, ep
, delta
, lno
))
508 rm
->lno
= delta
+ lno
;
511 #if defined(DEBUG) && 0
512 TRACE(sp
, "found: %qu to %qu\n",
513 match
[0].rm_so
, match
[0].rm_eo
);
516 * Find the last acceptable one in this line. This
517 * is really painful, we need a cleaner interface to
518 * regexec to make this possible.
521 last
= match
[0].rm_so
;
522 match
[0].rm_so
= match
[0].rm_eo
+ 1;
523 if (match
[0].rm_so
>= len
||
524 coff
&& match
[0].rm_so
>= coff
)
526 match
[0].rm_eo
= coff
? coff
: len
;
527 eval
= regexec(re
, l
, 1, match
,
528 (match
[0].rm_so
== 0 ? 0 : REG_NOTBOL
) |
530 if (eval
== REG_NOMATCH
)
533 re_error(sp
, eval
, re
);
539 /* See comment in f_search(). */
540 if (!LF_ISSET(SEARCH_EOL
) && last
>= len
)
541 rm
->cno
= len
? len
- 1 : 0;
549 /* Turn off busy message, interrupts. */
553 if (F_ISSET(sp
->gp
, G_ISFROMTTY
))
554 TEAR_DOWN_INTERRUPTS
;
561 * Convert vi's regular expressions into something that the
562 * the POSIX 1003.2 RE functions can handle.
564 * There are three conversions we make to make vi's RE's (specifically
565 * the global, search, and substitute patterns) work with POSIX RE's.
567 * 1: If O_MAGIC is not set, strip backslashes from the magic character
568 * set (.[]*~) that have them, and add them to the ones that don't.
569 * 2: If O_MAGIC is not set, the string "\~" is replaced with the text
570 * from the last substitute command's replacement string. If O_MAGIC
571 * is set, it's the string "~".
572 * 3: The pattern \<ptrn\> does "word" searches, convert it to use the
576 re_conv(sp
, ptrnp
, replacedp
)
581 size_t blen
, needlen
;
586 * First pass through, we figure out how much space we'll need.
587 * We do it in two passes, on the grounds that most of the time
588 * the user is doing a search and won't have magic characters.
589 * That way we can skip the malloc and memmove's.
591 for (p
= *ptrnp
, magic
= 0, needlen
= 0; *p
!= '\0'; ++p
)
597 needlen
+= sizeof(RE_WSTART
);
601 needlen
+= sizeof(RE_WSTOP
);
604 if (!O_ISSET(sp
, O_MAGIC
)) {
606 needlen
+= sp
->repl_len
;
613 if (!O_ISSET(sp
, O_MAGIC
)) {
623 if (O_ISSET(sp
, O_MAGIC
)) {
625 needlen
+= sp
->repl_len
;
632 if (!O_ISSET(sp
, O_MAGIC
)) {
648 * Get enough memory to hold the final pattern.
651 * It's nul-terminated, for now.
653 GET_SPACE_RET(sp
, bp
, blen
, needlen
+ 1);
655 for (p
= *ptrnp
, t
= bp
; *p
!= '\0'; ++p
)
660 memmove(t
, RE_WSTART
, sizeof(RE_WSTART
) - 1);
661 t
+= sizeof(RE_WSTART
) - 1;
664 memmove(t
, RE_WSTOP
, sizeof(RE_WSTOP
) - 1);
665 t
+= sizeof(RE_WSTOP
) - 1;
668 if (O_ISSET(sp
, O_MAGIC
))
671 memmove(t
, sp
->repl
, sp
->repl_len
);
679 if (O_ISSET(sp
, O_MAGIC
))
689 if (O_ISSET(sp
, O_MAGIC
)) {
690 memmove(t
, sp
->repl
, sp
->repl_len
);
699 if (!O_ISSET(sp
, O_MAGIC
))
716 * Get a line delta. The trickiness is that the delta can be pretty
717 * complicated, i.e. "+3-2+3++- ++" is allowed.
720 * In historic vi, if you had a delta on a search pattern which was used as
721 * a motion command, the command became a line mode command regardless of the
722 * cursor positions. A fairly common trick is to use a delta of "+0" to make
723 * the command a line mode command. This is the only place that knows about
724 * delta's, so we set the return flag information here.
727 get_delta(sp
, dp
, valp
, flagp
)
736 for (tval
= 0, p
= *dp
; *p
!= '\0'; *flagp
|= SEARCH_DELTA
) {
741 if (*p
== '+' || *p
== '-') {
742 if (!isdigit(*(p
+ 1))) {
744 if (tval
== LONG_MAX
)
748 if (tval
== LONG_MIN
)
760 val
= strtol(p
, &p
, 10);
761 if (errno
== ERANGE
) {
763 overflow
: msgq(sp
, M_ERR
, "Delta value overflow.");
764 else if (val
== LONG_MIN
)
765 underflow
: msgq(sp
, M_ERR
, "Delta value underflow.");
767 msgq(sp
, M_SYSERR
, NULL
);
771 if (LONG_MAX
- val
< tval
)
774 if (-(LONG_MIN
- tval
) > val
)
785 * Check a line delta to see if it's legal.
788 check_delta(sp
, ep
, delta
, lno
)
794 /* A delta can overflow a record number. */
796 if (lno
< LONG_MAX
&& delta
>= (long)lno
) {
797 msgq(sp
, M_ERR
, "Search offset before line 1.");
801 if (ULONG_MAX
- lno
< delta
) {
802 msgq(sp
, M_ERR
, "Delta value overflow.");
805 if (file_gline(sp
, ep
, lno
+ delta
, NULL
) == NULL
) {
806 msgq(sp
, M_ERR
, "Search offset past end-of-file.");
815 * Report a regular expression error.
818 re_error(sp
, errcode
, preg
)
826 s
= regerror(errcode
, preg
, "", 0);
827 if ((oe
= malloc(s
)) == NULL
)
828 msgq(sp
, M_SYSERR
, NULL
);
830 (void)regerror(errcode
, preg
, oe
, s
);
831 msgq(sp
, M_ERR
, "RE error: %s", oe
);