summaryrefslogtreecommitdiff
path: root/usr.bin/vi/common/search.c
diff options
context:
space:
mode:
authorTheo de Raadt <deraadt@cvs.openbsd.org>1996-05-22 11:37:15 +0000
committerTheo de Raadt <deraadt@cvs.openbsd.org>1996-05-22 11:37:15 +0000
commit0157a77a51c5e35e093ae03581f66dea010edcc8 (patch)
tree5e8bd32aa4d2b5ed37b7cf3ad26e8bdfc7f20a04 /usr.bin/vi/common/search.c
parent806021be093ad00ce2022a532c0f4cc99b0065ac (diff)
new vi
Diffstat (limited to 'usr.bin/vi/common/search.c')
-rw-r--r--usr.bin/vi/common/search.c923
1 files changed, 296 insertions, 627 deletions
diff --git a/usr.bin/vi/common/search.c b/usr.bin/vi/common/search.c
index f7e96ebda71..fbbaf76ab97 100644
--- a/usr.bin/vi/common/search.c
+++ b/usr.bin/vi/common/search.c
@@ -1,131 +1,109 @@
/*-
* Copyright (c) 1992, 1993, 1994
* The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
*
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
+ * See the LICENSE file for redistribution information.
*/
+#include "config.h"
+
#ifndef lint
-static char sccsid[] = "@(#)search.c 8.48 (Berkeley) 8/17/94";
+static const char sccsid[] = "@(#)search.c 10.23 (Berkeley) 5/15/96";
#endif /* not lint */
#include <sys/types.h>
#include <sys/queue.h>
-#include <sys/time.h>
#include <bitstring.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
-#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <termios.h>
#include <unistd.h>
-#include "compat.h"
-#include <db.h>
-#include <regex.h>
+#include "common.h"
-#include "vi.h"
+typedef enum { S_EMPTY, S_EOF, S_NOPREV, S_NOTFOUND, S_SOF, S_WRAP } smsg_t;
-static int check_delta __P((SCR *, EXF *, long, recno_t));
-static int ctag_conv __P((SCR *, char **, int *));
-static int get_delta __P((SCR *, char **, long *, u_int *));
-static int resetup __P((SCR *, regex_t **, enum direction,
- char *, char **, long *, u_int *));
+static void search_msg __P((SCR *, smsg_t));
+static int search_setup __P((SCR *, dir_t, char *, char **, u_int));
/*
- * resetup --
- * Set up a search for a regular expression.
+ * search_setup --
+ * Set up a search.
*/
static int
-resetup(sp, rep, dir, ptrn, epp, deltap, flagp)
+search_setup(sp, dir, ptrn, epp, flags)
SCR *sp;
- regex_t **rep;
- enum direction dir;
+ dir_t dir;
char *ptrn, **epp;
- long *deltap;
- u_int *flagp;
-{
u_int flags;
- int delim, eval, re_flags, replaced;
+{
+ recno_t lno;
+ int delim;
char *p, *t;
- /* Set return information the default. */
- *deltap = 0;
-
- /*
- * Use saved pattern if no pattern supplied, or if only a delimiter
- * character is supplied. Only the pattern was saved, historic vi
- * did not reuse any delta supplied.
- */
- flags = *flagp;
- if (ptrn == NULL)
- goto prev;
- if (ptrn[1] == '\0') {
- if (epp != NULL)
- *epp = ptrn + 1;
- goto prev;
- }
- if (ptrn[0] == ptrn[1] && ptrn[2] == '\0') {
- if (epp != NULL)
- *epp = ptrn + 2;
-prev: if (!F_ISSET(sp, S_SRE_SET)) {
- msgq(sp, M_ERR, "No previous search pattern");
+ /* If the file is empty, it's a fast search. */
+ if (sp->lno <= 1) {
+ if (db_last(sp, &lno))
+ return (1);
+ if (lno == 0) {
+ if (LF_ISSET(SEARCH_MSG))
+ search_msg(sp, S_EMPTY);
return (1);
}
- *rep = &sp->sre;
+ }
- /* Empty patterns set the direction. */
- if (LF_ISSET(SEARCH_SET)) {
- F_SET(sp, S_SRE_SET);
- sp->searchdir = dir;
- sp->sre = **rep;
+ if (LF_ISSET(SEARCH_PARSE)) { /* Parse the string. */
+ /*
+ * Use the saved pattern if no pattern supplied, or if only
+ * the delimiter character supplied.
+ *
+ * !!!
+ * Only the pattern itself was saved, historically vi didn't
+ * reuse addressing or delta information.
+ */
+ if (ptrn == NULL)
+ goto prev;
+ if (ptrn[1] == '\0') {
+ if (epp != NULL)
+ *epp = ptrn + 1;
+ goto prev;
}
- return (0);
- }
+ if (ptrn[0] == ptrn[1]) {
+ if (epp != NULL)
+ *epp = ptrn + 2;
- re_flags = 0; /* Set RE flags. */
- if (O_ISSET(sp, O_EXTENDED))
- re_flags |= REG_EXTENDED;
- if (O_ISSET(sp, O_IGNORECASE))
- re_flags |= REG_ICASE;
+ /* Complain if we don't have a previous pattern. */
+prev: if (sp->re == NULL) {
+ search_msg(sp, S_NOPREV);
+ return (1);
+ }
+ /* Compile the search pattern if necessary. */
+ if (!F_ISSET(sp, SC_RE_SEARCH) &&
+ re_compile(sp, sp->re, NULL, NULL, &sp->re_c,
+ RE_C_SEARCH |
+ (LF_ISSET(SEARCH_MSG) ? 0 : RE_C_SILENT)))
+ return (1);
- replaced = 0;
- if (LF_ISSET(SEARCH_PARSE)) { /* Parse the string. */
- /* Set delimiter. */
- delim = *ptrn++;
+ /* Set the search direction. */
+ if (LF_ISSET(SEARCH_SET))
+ sp->searchdir = dir;
+ return (0);
+ }
- /* Find terminating delimiter, handling escaped delimiters. */
- for (p = t = ptrn;;) {
+ /*
+ * Set the delimiter, and move forward to the terminating
+ * delimiter, handling escaped delimiters.
+ *
+ * QUOTING NOTE:
+ * Only discard an escape character if it escapes a delimiter.
+ */
+ for (delim = *ptrn, p = t = ++ptrn;; *t++ = *p++) {
if (p[0] == '\0' || p[0] == delim) {
if (p[0] == delim)
++p;
@@ -134,204 +112,107 @@ prev: if (!F_ISSET(sp, S_SRE_SET)) {
}
if (p[1] == delim && p[0] == '\\')
++p;
- *t++ = *p++;
- }
-
- /*
- * If characters after the terminating delimiter, it may
- * be an error, or may be an offset. In either case, we
- * return the end of the string, whatever it may be.
- */
- if (*p) {
- if (get_delta(sp, &p, deltap, flagp))
- return (1);
- if (*p && LF_ISSET(SEARCH_TERM)) {
- msgq(sp, M_ERR,
- "Characters after search string and/or delta");
- return (1);
- }
}
if (epp != NULL)
*epp = p;
-
- /* Check for "/ " or other such silliness. */
- if (*ptrn == '\0')
- goto prev;
-
- if (re_conv(sp, &ptrn, &replaced))
- return (1);
- } else if (LF_ISSET(SEARCH_TAG)) {
- if (ctag_conv(sp, &ptrn, &replaced))
- return (1);
- re_flags &= ~(REG_EXTENDED | REG_ICASE);
}
/* Compile the RE. */
- if (eval = regcomp(*rep, ptrn, re_flags))
- re_error(sp, eval, *rep);
- else if (LF_ISSET(SEARCH_SET)) {
- F_SET(sp, S_SRE_SET);
+ if (re_compile(sp, ptrn, &sp->re, &sp->re_len, &sp->re_c,
+ RE_C_SEARCH |
+ (LF_ISSET(SEARCH_MSG) ? 0 : RE_C_SILENT) |
+ (LF_ISSET(SEARCH_TAG) ? RE_C_TAG : 0) |
+ (LF_ISSET(SEARCH_CSCOPE) ? RE_C_CSCOPE : 0)))
+ return (1);
+
+ /* Set the search direction. */
+ if (LF_ISSET(SEARCH_SET))
sp->searchdir = dir;
- sp->sre = **rep;
- }
- /* Free up any extra memory. */
- if (replaced)
- FREE_SPACE(sp, ptrn, 0);
- return (eval);
+ return (0);
}
/*
- * ctag_conv --
- * Convert a tags search path into something that the POSIX
- * 1003.2 RE functions can handle.
+ * f_search --
+ * Do a forward search.
+ *
+ * PUBLIC: int f_search __P((SCR *, MARK *, MARK *, char *, char **, u_int));
*/
-static int
-ctag_conv(sp, ptrnp, replacedp)
- SCR *sp;
- char **ptrnp;
- int *replacedp;
-{
- size_t blen, len;
- int lastdollar;
- char *bp, *p, *t;
-
- *replacedp = 0;
-
- len = strlen(p = *ptrnp);
-
- /* Max memory usage is 2 times the length of the string. */
- GET_SPACE_RET(sp, bp, blen, len * 2);
-
- t = bp;
-
- /* The last character is a '/' or '?', we just strip it. */
- if (p[len - 1] == '/' || p[len - 1] == '?')
- p[len - 1] = '\0';
-
- /* The next-to-last character is a '$', and it's magic. */
- if (p[len - 2] == '$') {
- lastdollar = 1;
- p[len - 2] = '\0';
- } else
- lastdollar = 0;
-
- /* The first character is a '/' or '?', we just strip it. */
- if (p[0] == '/' || p[0] == '?')
- ++p;
-
- /* The second character is a '^', and it's magic. */
- if (p[0] == '^')
- *t++ = *p++;
-
- /*
- * Escape every other magic character we can find, stripping the
- * backslashes ctags inserts to escape the search delimiter
- * characters.
- */
- while (p[0]) {
- /* Ctags escapes the search delimiter characters. */
- if (p[0] == '\\' && (p[1] == '/' || p[1] == '?'))
- ++p;
- else if (strchr("^.[]$*", p[0]))
- *t++ = '\\';
- *t++ = *p++;
- }
- if (lastdollar)
- *t++ = '$';
- *t++ = '\0';
-
- *ptrnp = bp;
- *replacedp = 1;
- return (0);
-}
-
-#define EMPTYMSG "File empty; nothing to search"
-#define EOFMSG "Reached end-of-file without finding the pattern"
-#define NOTFOUND "Pattern not found"
-#define SOFMSG "Reached top-of-file without finding the pattern"
-#define WRAPMSG "Search wrapped"
-
int
-f_search(sp, ep, fm, rm, ptrn, eptrn, flagp)
+f_search(sp, fm, rm, ptrn, eptrn, flags)
SCR *sp;
- EXF *ep;
MARK *fm, *rm;
char *ptrn, **eptrn;
- u_int *flagp;
+ u_int flags;
{
- regmatch_t match[1];
- regex_t *re, lre;
+ busy_t btype;
recno_t lno;
+ regmatch_t match[1];
size_t coff, len;
- long delta;
- u_int flags;
- int btear, eval, rval, wrapped;
+ int cnt, eval, rval, wrapped;
char *l;
- if (file_lline(sp, ep, &lno))
- return (1);
- flags = *flagp;
- if (lno == 0) {
- if (LF_ISSET(SEARCH_MSG))
- msgq(sp, M_INFO, EMPTYMSG);
- return (1);
- }
-
- re = &lre;
- if (resetup(sp, &re, FORWARD, ptrn, eptrn, &delta, flagp))
+ if (search_setup(sp, FORWARD, ptrn, eptrn, flags))
return (1);
- /*
- * Start searching immediately after the cursor. If at the end of the
- * line, start searching on the next line. This is incompatible (read
- * bug fix) with the historic vi -- searches for the '$' pattern never
- * moved forward, and "-t foo" didn't work if "foo" was the first thing
- * in the file.
- */
if (LF_ISSET(SEARCH_FILE)) {
lno = 1;
coff = 0;
} else {
- if ((l = file_gline(sp, ep, fm->lno, &len)) == NULL) {
- GETLINE_ERR(sp, fm->lno);
+ if (db_get(sp, fm->lno, DBG_FATAL, &l, &len))
return (1);
- }
- if (fm->cno + 1 >= len) {
- if (fm->lno == lno) {
+ lno = fm->lno;
+
+ /*
+ * If doing incremental search, start searching at the previous
+ * column, so that we search a minimal distance and still match
+ * special patterns, e.g., \< for beginning of a word.
+ *
+ * Otherwise, start searching immediately after the cursor. If
+ * at the end of the line, start searching on the next line.
+ * This is incompatible (read bug fix) with the historic vi --
+ * searches for the '$' pattern never moved forward, and the
+ * "-t foo" didn't work if the 'f' was the first character in
+ * the file.
+ */
+ if (LF_ISSET(SEARCH_INCR)) {
+ if ((coff = fm->cno) != 0)
+ --coff;
+ } else if (fm->cno + 1 >= len) {
+ coff = 0;
+ lno = fm->lno + 1;
+ if (db_get(sp, lno, 0, &l, &len)) {
if (!O_ISSET(sp, O_WRAPSCAN)) {
if (LF_ISSET(SEARCH_MSG))
- msgq(sp, M_INFO, EOFMSG);
+ search_msg(sp, S_EOF);
return (1);
}
lno = 1;
- } else
- lno = fm->lno + 1;
- coff = 0;
- } else {
- lno = fm->lno;
+ }
+ } else
coff = fm->cno + 1;
- }
}
- /* Turn on busy message. */
- btear = F_ISSET(sp, S_EXSILENT) ? 0 : !busy_on(sp, "Searching...");
-
- for (rval = 1, wrapped = 0;; ++lno, coff = 0) {
- if (INTERRUPTED(sp)) {
- msgq(sp, M_INFO, "Interrupted.");
- break;
+ btype = BUSY_ON;
+ for (cnt = INTERRUPT_CHECK, rval = 1, wrapped = 0;; ++lno, coff = 0) {
+ if (cnt-- == 0) {
+ if (INTERRUPTED(sp))
+ break;
+ if (LF_ISSET(SEARCH_MSG)) {
+ search_busy(sp, btype);
+ btype = BUSY_UPDATE;
+ }
+ cnt = INTERRUPT_CHECK;
}
- if (wrapped && lno > fm->lno ||
- (l = file_gline(sp, ep, lno, &len)) == NULL) {
+ if (wrapped && lno > fm->lno || db_get(sp, lno, 0, &l, &len)) {
if (wrapped) {
if (LF_ISSET(SEARCH_MSG))
- msgq(sp, M_INFO, NOTFOUND);
+ search_msg(sp, S_NOTFOUND);
break;
}
if (!O_ISSET(sp, O_WRAPSCAN)) {
if (LF_ISSET(SEARCH_MSG))
- msgq(sp, M_INFO, EOFMSG);
+ search_msg(sp, S_EOF);
break;
}
lno = 0;
@@ -340,7 +221,7 @@ f_search(sp, ep, fm, rm, ptrn, eptrn, flagp)
}
/* If already at EOL, just keep going. */
- if (len && coff == len)
+ if (len != 0 && coff == len)
continue;
/* Set the termination. */
@@ -349,125 +230,141 @@ f_search(sp, ep, fm, rm, ptrn, eptrn, flagp)
#if defined(DEBUG) && 0
TRACE(sp, "F search: %lu from %u to %u\n",
- lno, coff, len ? len - 1 : len);
+ lno, coff, len != 0 ? len - 1 : len);
#endif
/* Search the line. */
- eval = regexec(re, l, 1, match,
+ eval = regexec(&sp->re_c, l, 1, match,
(match[0].rm_so == 0 ? 0 : REG_NOTBOL) | REG_STARTEND);
if (eval == REG_NOMATCH)
continue;
if (eval != 0) {
- re_error(sp, eval, re);
+ if (LF_ISSET(SEARCH_MSG))
+ re_error(sp, eval, &sp->re_c);
+ else
+ (void)sp->gp->scr_bell(sp);
break;
}
- /* Warn if wrapped. */
- if (wrapped && O_ISSET(sp, O_WARN) && LF_ISSET(SEARCH_MSG))
- msgq(sp, M_VINFO, WRAPMSG);
-
/*
- * If an offset, see if it's legal. It's possible to match
- * past the end of the line with $, so check for that case.
+ * XXX
+ * Warn if the search wrapped. This message is only displayed
+ * if there are no keys in the queue. The problem is that the
+ * command is going to succeed, and the message isn't an error,
+ * it's informational in nature. If a macro causes it to be
+ * output repeatedly, e.g., the pattern only occurs once in the
+ * file and wrapscan is set, you can lose, particularly if the
+ * macro does something like:
+ * :map K /pattern/^MjK
+ * Each new search will display the message and the /pattern/
+ * will immediately overwrite it, with strange results. The
+ * System V vi displays the "wrapped" message multiple times,
+ * but it's overwritten each time, so it's not as noticeable.
+ * Since we don't discard messages, it's a real problem.
*/
- if (delta) {
- if (check_delta(sp, ep, delta, lno))
- break;
- rm->lno = delta + lno;
- rm->cno = 0;
- } else {
+ if (wrapped && LF_ISSET(SEARCH_MSG) && !KEYS_WAITING(sp))
+ search_msg(sp, S_WRAP);
+
#if defined(DEBUG) && 0
- TRACE(sp, "found: %qu to %qu\n",
- match[0].rm_so, match[0].rm_eo);
+ TRACE(sp, "F search: %qu to %qu\n",
+ match[0].rm_so, match[0].rm_eo);
#endif
- rm->lno = lno;
- rm->cno = match[0].rm_so;
-
- /*
- * If a change command, it's possible to move beyond
- * the end of a line. Historic vi generally got this
- * wrong (try "c?$<cr>"). Not all that sure this gets
- * it right, there are lots of strange cases.
- */
- if (!LF_ISSET(SEARCH_EOL) && rm->cno >= len)
- rm->cno = len ? len - 1 : 0;
- }
+ rm->lno = lno;
+ rm->cno = match[0].rm_so;
+
+ /*
+ * If a change command, it's possible to move beyond the end
+ * of a line. Historic vi generally got this wrong (e.g. try
+ * "c?$<cr>"). Not all that sure this gets it right, there
+ * are lots of strange cases.
+ */
+ if (!LF_ISSET(SEARCH_EOL) && rm->cno >= len)
+ rm->cno = len != 0 ? len - 1 : 0;
+
rval = 0;
break;
}
- /* Turn off busy message, interrupts. */
- if (btear)
- busy_off(sp);
+ if (LF_ISSET(SEARCH_MSG))
+ search_busy(sp, BUSY_OFF);
return (rval);
}
+/*
+ * b_search --
+ * Do a backward search.
+ *
+ * PUBLIC: int b_search __P((SCR *, MARK *, MARK *, char *, char **, u_int));
+ */
int
-b_search(sp, ep, fm, rm, ptrn, eptrn, flagp)
+b_search(sp, fm, rm, ptrn, eptrn, flags)
SCR *sp;
- EXF *ep;
MARK *fm, *rm;
char *ptrn, **eptrn;
- u_int *flagp;
+ u_int flags;
{
- regmatch_t match[1];
- regex_t *re, lre;
+ busy_t btype;
recno_t lno;
- size_t coff, len, last;
- long delta;
- u_int flags;
- int btear, eval, rval, wrapped;
+ regmatch_t match[1];
+ size_t coff, last, len;
+ int cnt, eval, rval, wrapped;
char *l;
- if (file_lline(sp, ep, &lno))
- return (1);
- flags = *flagp;
- if (lno == 0) {
- if (LF_ISSET(SEARCH_MSG))
- msgq(sp, M_INFO, EMPTYMSG);
- return (1);
- }
-
- re = &lre;
- if (resetup(sp, &re, BACKWARD, ptrn, eptrn, &delta, flagp))
+ if (search_setup(sp, BACKWARD, ptrn, eptrn, flags))
return (1);
- /* If in the first column, start searching on the previous line. */
- if (fm->cno == 0) {
- if (fm->lno == 1) {
- if (!O_ISSET(sp, O_WRAPSCAN)) {
+ /*
+ * If doing incremental search, set the "starting" position past the
+ * current column, so that we search a minimal distance and still
+ * match special patterns, e.g., \> for the end of a word. This is
+ * safe when the cursor is at the end of a line because we only use
+ * it for comparison with the location of the match.
+ *
+ * Otherwise, start searching immediately before the cursor. If in
+ * the first column, start search on the previous line.
+ */
+ if (LF_ISSET(SEARCH_INCR)) {
+ lno = fm->lno;
+ coff = fm->cno + 1;
+ } else {
+ if (fm->cno == 0) {
+ if (fm->lno == 1 && !O_ISSET(sp, O_WRAPSCAN)) {
if (LF_ISSET(SEARCH_MSG))
- msgq(sp, M_INFO, SOFMSG);
+ search_msg(sp, S_SOF);
return (1);
}
- } else
lno = fm->lno - 1;
- } else
- lno = fm->lno;
-
- /* Turn on busy message. */
- btear = F_ISSET(sp, S_EXSILENT) ? 0 : !busy_on(sp, "Searching...");
+ } else
+ lno = fm->lno;
+ coff = fm->cno;
+ }
- for (rval = 1, wrapped = 0, coff = fm->cno;; --lno, coff = 0) {
- if (INTERRUPTED(sp)) {
- msgq(sp, M_INFO, "Interrupted.");
- break;
+ btype = BUSY_ON;
+ for (cnt = INTERRUPT_CHECK, rval = 1, wrapped = 0;; --lno, coff = 0) {
+ if (cnt-- == 0) {
+ if (INTERRUPTED(sp))
+ break;
+ if (LF_ISSET(SEARCH_MSG)) {
+ search_busy(sp, btype);
+ btype = BUSY_UPDATE;
+ }
+ cnt = INTERRUPT_CHECK;
}
if (wrapped && lno < fm->lno || lno == 0) {
if (wrapped) {
if (LF_ISSET(SEARCH_MSG))
- msgq(sp, M_INFO, NOTFOUND);
+ search_msg(sp, S_NOTFOUND);
break;
}
if (!O_ISSET(sp, O_WRAPSCAN)) {
if (LF_ISSET(SEARCH_MSG))
- msgq(sp, M_INFO, SOFMSG);
+ search_msg(sp, S_SOF);
break;
}
- if (file_lline(sp, ep, &lno))
- goto err;
+ if (db_last(sp, &lno))
+ break;
if (lno == 0) {
if (LF_ISSET(SEARCH_MSG))
- msgq(sp, M_INFO, EMPTYMSG);
+ search_msg(sp, S_EMPTY);
break;
}
++lno;
@@ -475,8 +372,8 @@ b_search(sp, ep, fm, rm, ptrn, eptrn, flagp)
continue;
}
- if ((l = file_gline(sp, ep, lno, &len)) == NULL)
- goto err;
+ if (db_get(sp, lno, 0, &l, &len))
+ break;
/* Set the termination. */
match[0].rm_so = 0;
@@ -486,12 +383,15 @@ b_search(sp, ep, fm, rm, ptrn, eptrn, flagp)
TRACE(sp, "B search: %lu from 0 to %qu\n", lno, match[0].rm_eo);
#endif
/* Search the line. */
- eval = regexec(re, l, 1, match,
+ eval = regexec(&sp->re_c, l, 1, match,
(match[0].rm_eo == len ? 0 : REG_NOTEOL) | REG_STARTEND);
if (eval == REG_NOMATCH)
continue;
if (eval != 0) {
- re_error(sp, eval, re);
+ if (LF_ISSET(SEARCH_MSG))
+ re_error(sp, eval, &sp->re_c);
+ else
+ (void)sp->gp->scr_bell(sp);
break;
}
@@ -499,335 +399,104 @@ b_search(sp, ep, fm, rm, ptrn, eptrn, flagp)
if (coff != 0 && match[0].rm_so >= coff)
continue;
- /* Warn if wrapped. */
- if (wrapped && O_ISSET(sp, O_WARN) && LF_ISSET(SEARCH_MSG))
- msgq(sp, M_VINFO, WRAPMSG);
+ /*
+ * XXX
+ * See the comment in f_search() for more information.
+ */
+ if (wrapped && LF_ISSET(SEARCH_MSG) && !KEYS_WAITING(sp))
+ search_msg(sp, S_WRAP);
- if (delta) {
- if (check_delta(sp, ep, delta, lno))
- break;
- rm->lno = delta + lno;
- rm->cno = 0;
- } else {
#if defined(DEBUG) && 0
- TRACE(sp, "found: %qu to %qu\n",
- match[0].rm_so, match[0].rm_eo);
+ TRACE(sp, "B found: %qu to %qu\n",
+ match[0].rm_so, match[0].rm_eo);
#endif
- /*
- * We now have the first match on the line. Step
- * through the line character by character until we
- * find the last acceptable match. This is painful,
- * we need a better interface to regex to make this
- * work.
- */
- for (;;) {
- last = match[0].rm_so++;
- if (match[0].rm_so >= len)
- break;
- match[0].rm_eo = len;
- eval = regexec(re, l, 1, match,
- (match[0].rm_so == 0 ? 0 : REG_NOTBOL) |
- REG_STARTEND);
- if (eval == REG_NOMATCH)
- break;
- if (eval != 0) {
- re_error(sp, eval, re);
- goto err;
- }
- if (coff && match[0].rm_so >= coff)
- break;
+ /*
+ * We now have the first match on the line. Step through the
+ * line character by character until find the last acceptable
+ * match. This is painful, we need a better interface to regex
+ * to make this work.
+ */
+ for (;;) {
+ last = match[0].rm_so++;
+ if (match[0].rm_so >= len)
+ break;
+ match[0].rm_eo = len;
+ eval = regexec(&sp->re_c, l, 1, match,
+ (match[0].rm_so == 0 ? 0 : REG_NOTBOL) |
+ REG_STARTEND);
+ if (eval == REG_NOMATCH)
+ break;
+ if (eval != 0) {
+ if (LF_ISSET(SEARCH_MSG))
+ re_error(sp, eval, &sp->re_c);
+ else
+ (void)sp->gp->scr_bell(sp);
+ goto err;
}
- rm->lno = lno;
-
- /* See comment in f_search(). */
- if (!LF_ISSET(SEARCH_EOL) && last >= len)
- rm->cno = len ? len - 1 : 0;
- else
- rm->cno = last;
+ if (coff && match[0].rm_so >= coff)
+ break;
}
+ rm->lno = lno;
+
+ /* See comment in f_search(). */
+ if (!LF_ISSET(SEARCH_EOL) && last >= len)
+ rm->cno = len != 0 ? len - 1 : 0;
+ else
+ rm->cno = last;
rval = 0;
break;
}
- /* Turn off busy message, interrupts. */
-err: if (btear)
- busy_off(sp);
+err: if (LF_ISSET(SEARCH_MSG))
+ search_busy(sp, BUSY_OFF);
return (rval);
}
/*
- * re_conv --
- * Convert vi's regular expressions into something that the
- * the POSIX 1003.2 RE functions can handle.
- *
- * There are three conversions we make to make vi's RE's (specifically
- * the global, search, and substitute patterns) work with POSIX RE's.
- *
- * 1: If O_MAGIC is not set, strip backslashes from the magic character
- * set (.[]*~) that have them, and add them to the ones that don't.
- * 2: If O_MAGIC is not set, the string "\~" is replaced with the text
- * from the last substitute command's replacement string. If O_MAGIC
- * is set, it's the string "~".
- * 3: The pattern \<ptrn\> does "word" searches, convert it to use the
- * new RE escapes.
+ * search_msg --
+ * Display one of the search messages.
*/
-int
-re_conv(sp, ptrnp, replacedp)
+static void
+search_msg(sp, msg)
SCR *sp;
- char **ptrnp;
- int *replacedp;
+ smsg_t msg;
{
- size_t blen, needlen;
- int magic;
- char *bp, *p, *t;
-
- /*
- * First pass through, we figure out how much space we'll need.
- * We do it in two passes, on the grounds that most of the time
- * the user is doing a search and won't have magic characters.
- * That way we can skip the malloc and memmove's.
- */
- for (p = *ptrnp, magic = 0, needlen = 0; *p != '\0'; ++p)
- switch (*p) {
- case '\\':
- switch (*++p) {
- case '<':
- magic = 1;
- needlen += sizeof(RE_WSTART);
- break;
- case '>':
- magic = 1;
- needlen += sizeof(RE_WSTOP);
- break;
- case '~':
- if (!O_ISSET(sp, O_MAGIC)) {
- magic = 1;
- needlen += sp->repl_len;
- }
- break;
- case '.':
- case '[':
- case ']':
- case '*':
- if (!O_ISSET(sp, O_MAGIC)) {
- magic = 1;
- needlen += 1;
- }
- break;
- default:
- needlen += 2;
- }
- break;
- case '~':
- if (O_ISSET(sp, O_MAGIC)) {
- magic = 1;
- needlen += sp->repl_len;
- }
- break;
- case '.':
- case '[':
- case ']':
- case '*':
- if (!O_ISSET(sp, O_MAGIC)) {
- magic = 1;
- needlen += 2;
- }
- break;
- default:
- needlen += 1;
- break;
- }
-
- if (!magic) {
- *replacedp = 0;
- return (0);
+ switch (msg) {
+ case S_EMPTY:
+ msgq(sp, M_ERR, "072|File empty; nothing to search");
+ break;
+ case S_EOF:
+ msgq(sp, M_ERR,
+ "073|Reached end-of-file without finding the pattern");
+ break;
+ case S_NOPREV:
+ msgq(sp, M_ERR, "074|No previous search pattern");
+ break;
+ case S_NOTFOUND:
+ msgq(sp, M_ERR, "075|Pattern not found");
+ break;
+ case S_SOF:
+ msgq(sp, M_ERR,
+ "076|Reached top-of-file without finding the pattern");
+ break;
+ case S_WRAP:
+ msgq(sp, M_ERR, "077|Search wrapped");
+ break;
+ default:
+ abort();
}
-
- /*
- * Get enough memory to hold the final pattern.
- *
- * XXX
- * It's nul-terminated, for now.
- */
- GET_SPACE_RET(sp, bp, blen, needlen + 1);
-
- for (p = *ptrnp, t = bp; *p != '\0'; ++p)
- switch (*p) {
- case '\\':
- switch (*++p) {
- case '<':
- memmove(t, RE_WSTART, sizeof(RE_WSTART) - 1);
- t += sizeof(RE_WSTART) - 1;
- break;
- case '>':
- memmove(t, RE_WSTOP, sizeof(RE_WSTOP) - 1);
- t += sizeof(RE_WSTOP) - 1;
- break;
- case '~':
- if (O_ISSET(sp, O_MAGIC))
- *t++ = '~';
- else {
- memmove(t, sp->repl, sp->repl_len);
- t += sp->repl_len;
- }
- break;
- case '.':
- case '[':
- case ']':
- case '*':
- if (O_ISSET(sp, O_MAGIC))
- *t++ = '\\';
- *t++ = *p;
- break;
- default:
- *t++ = '\\';
- *t++ = *p;
- }
- break;
- case '~':
- if (O_ISSET(sp, O_MAGIC)) {
- memmove(t, sp->repl, sp->repl_len);
- t += sp->repl_len;
- } else
- *t++ = '~';
- break;
- case '.':
- case '[':
- case ']':
- case '*':
- if (!O_ISSET(sp, O_MAGIC))
- *t++ = '\\';
- *t++ = *p;
- break;
- default:
- *t++ = *p;
- break;
- }
- *t = '\0';
-
- *ptrnp = bp;
- *replacedp = 1;
- return (0);
}
/*
- * get_delta --
- * Get a line delta. The trickiness is that the delta can be pretty
- * complicated, i.e. "+3-2+3++- ++" is allowed.
+ * search_busy --
+ * Put up the busy searching message.
*
- * !!!
- * In historic vi, if you had a delta on a search pattern which was used as
- * a motion command, the command became a line mode command regardless of the
- * cursor positions. A fairly common trick is to use a delta of "+0" to make
- * the command a line mode command. This is the only place that knows about
- * delta's, so we set the return flag information here.
- */
-static int
-get_delta(sp, dp, valp, flagp)
- SCR *sp;
- char **dp;
- long *valp;
- u_int *flagp;
-{
- char *p;
- long val, tval;
-
- for (tval = 0, p = *dp; *p != '\0'; *flagp |= SEARCH_DELTA) {
- if (isblank(*p)) {
- ++p;
- continue;
- }
- if (*p == '+' || *p == '-') {
- if (!isdigit(*(p + 1))) {
- if (*p == '+') {
- if (tval == LONG_MAX)
- goto overflow;
- ++tval;
- } else {
- if (tval == LONG_MIN)
- goto underflow;
- --tval;
- }
- ++p;
- continue;
- }
- } else
- if (!isdigit(*p))
- break;
-
- errno = 0;
- val = strtol(p, &p, 10);
- if (errno == ERANGE) {
- if (val == LONG_MAX)
-overflow: msgq(sp, M_ERR, "Delta value overflow");
- else if (val == LONG_MIN)
-underflow: msgq(sp, M_ERR, "Delta value underflow");
- else
- msgq(sp, M_SYSERR, NULL);
- return (1);
- }
- if (val >= 0) {
- if (LONG_MAX - val < tval)
- goto overflow;
- } else
- if (-(LONG_MIN - tval) > val)
- goto underflow;
- tval += val;
- }
- *dp = p;
- *valp = tval;
- return (0);
-}
-
-/*
- * check_delta --
- * Check a line delta to see if it's legal.
- */
-static int
-check_delta(sp, ep, delta, lno)
- SCR *sp;
- EXF *ep;
- long delta;
- recno_t lno;
-{
- /* A delta can overflow a record number. */
- if (delta < 0) {
- if (lno < LONG_MAX && delta >= (long)lno) {
- msgq(sp, M_ERR, "Search offset before line 1");
- return (1);
- }
- } else {
- if (ULONG_MAX - lno < delta) {
- msgq(sp, M_ERR, "Delta value overflow");
- return (1);
- }
- if (file_gline(sp, ep, lno + delta, NULL) == NULL) {
- msgq(sp, M_ERR, "Search offset past end-of-file");
- return (1);
- }
- }
- return (0);
-}
-
-/*
- * re_error --
- * Report a regular expression error.
+ * PUBLIC: void search_busy __P((SCR *, busy_t));
*/
void
-re_error(sp, errcode, preg)
+search_busy(sp, btype)
SCR *sp;
- int errcode;
- regex_t *preg;
+ busy_t btype;
{
- size_t s;
- char *oe;
-
- s = regerror(errcode, preg, "", 0);
- if ((oe = malloc(s)) == NULL)
- msgq(sp, M_SYSERR, NULL);
- else {
- (void)regerror(errcode, preg, oe, s);
- msgq(sp, M_ERR, "RE error: %s", oe);
- }
- free(oe);
+ sp->gp->scr_busy(sp, "078|Searching...", btype);
}