2 * Copyright (c) 1992, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
4 * Copyright (c) 1992, 1993, 1994, 1995, 1996
5 * Keith Bostic. All rights reserved.
7 * See the LICENSE file for redistribution information.
13 static const char sccsid[] = "$Id: search.c,v 10.26 2011/07/04 20:16:26 zy Exp $";
16 #include <sys/types.h>
17 #include <sys/queue.h>
20 #include <bitstring.h>
31 typedef enum { S_EMPTY, S_EOF, S_NOPREV, S_NOTFOUND, S_SOF, S_WRAP } smsg_t;
33 static void search_msg __P((SCR *, smsg_t));
34 static int search_init __P((SCR *, dir_t, CHAR_T *, size_t, CHAR_T **, u_int));
53 /* If the file is empty, it's a fast search. */
55 if (db_last(sp, &lno))
58 if (LF_ISSET(SEARCH_MSG))
59 search_msg(sp, S_EMPTY);
64 if (LF_ISSET(SEARCH_PARSE)) { /* Parse the string. */
66 * Use the saved pattern if no pattern specified, or if only
67 * one or two delimiter characters specified.
70 * Historically, only the pattern itself was saved, vi didn't
71 * preserve addressing or delta information.
80 if (ptrn[0] == ptrn[1]) {
84 /* Complain if we don't have a previous pattern. */
85 prev: if (sp->re == NULL) {
86 search_msg(sp, S_NOPREV);
89 /* Re-compile the search pattern if necessary. */
90 if (!F_ISSET(sp, SC_RE_SEARCH) && re_compile(sp,
91 sp->re, sp->re_len, NULL, NULL, &sp->re_c,
93 (LF_ISSET(SEARCH_MSG) ? 0 : RE_C_SILENT)))
96 /* Set the search direction. */
97 if (LF_ISSET(SEARCH_SET))
103 * Set the delimiter, and move forward to the terminating
104 * delimiter, handling escaped delimiters.
107 * Only discard an escape character if it escapes a delimiter.
109 for (delim = *ptrn, p = t = ++ptrn;; *t++ = *p++) {
110 if (--plen == 0 || p[0] == delim) {
115 if (plen > 1 && p[0] == '\\' && p[1] == delim) {
126 /* Compile the RE. */
127 if (re_compile(sp, ptrn, plen, &sp->re, &sp->re_len, &sp->re_c,
129 (LF_ISSET(SEARCH_MSG) ? 0 : RE_C_SILENT) |
130 (LF_ISSET(SEARCH_TAG) ? RE_C_TAG : 0) |
131 (LF_ISSET(SEARCH_CSCOPE) ? RE_C_CSCOPE : 0)))
134 /* Set the search direction. */
135 if (LF_ISSET(SEARCH_SET))
143 * Do a forward search.
145 * PUBLIC: int f_search __P((SCR *,
146 * PUBLIC: MARK *, MARK *, CHAR_T *, size_t, CHAR_T **, u_int));
162 int cnt, eval, rval, wrapped;
165 if (search_init(sp, FORWARD, ptrn, plen, eptrn, flags))
168 if (LF_ISSET(SEARCH_FILE)) {
172 if (db_get(sp, fm->lno, DBG_FATAL, &l, &len))
177 * If doing incremental search, start searching at the previous
178 * column, so that we search a minimal distance and still match
179 * special patterns, e.g., \< for beginning of a word.
181 * Otherwise, start searching immediately after the cursor. If
182 * at the end of the line, start searching on the next line.
183 * This is incompatible (read bug fix) with the historic vi --
184 * searches for the '$' pattern never moved forward, and the
185 * "-t foo" didn't work if the 'f' was the first character in
188 if (LF_ISSET(SEARCH_INCR)) {
189 if ((coff = fm->cno) != 0)
191 } else if (fm->cno + 1 >= len) {
194 if (db_get(sp, lno, 0, &l, &len)) {
195 if (!O_ISSET(sp, O_WRAPSCAN)) {
196 if (LF_ISSET(SEARCH_MSG))
197 search_msg(sp, S_EOF);
207 for (cnt = INTERRUPT_CHECK, rval = 1, wrapped = 0;; ++lno, coff = 0) {
211 if (LF_ISSET(SEARCH_MSG)) {
212 search_busy(sp, btype);
215 cnt = INTERRUPT_CHECK;
217 if ((wrapped && lno > fm->lno) || db_get(sp, lno, 0, &l, &len)) {
219 if (LF_ISSET(SEARCH_MSG))
220 search_msg(sp, S_NOTFOUND);
223 if (!O_ISSET(sp, O_WRAPSCAN)) {
224 if (LF_ISSET(SEARCH_MSG))
225 search_msg(sp, S_EOF);
233 /* If already at EOL, just keep going. */
234 if (len != 0 && coff == len)
237 /* Set the termination. */
238 match[0].rm_so = coff;
239 match[0].rm_eo = len;
241 #if defined(DEBUG) && 0
242 TRACE(sp, "F search: %lu from %u to %u\n",
243 lno, coff, len != 0 ? len - 1 : len);
245 /* Search the line. */
246 eval = regexec(&sp->re_c, l, 1, match,
247 (match[0].rm_so == 0 ? 0 : REG_NOTBOL) | REG_STARTEND);
248 if (eval == REG_NOMATCH)
251 if (LF_ISSET(SEARCH_MSG))
252 re_error(sp, eval, &sp->re_c);
254 (void)sp->gp->scr_bell(sp);
258 /* Warn if the search wrapped. */
259 if (wrapped && LF_ISSET(SEARCH_WMSG))
260 search_msg(sp, S_WRAP);
262 #if defined(DEBUG) && 0
263 TRACE(sp, "F search: %qu to %qu\n",
264 match[0].rm_so, match[0].rm_eo);
267 rm->cno = match[0].rm_so;
270 * If a change command, it's possible to move beyond the end
271 * of a line. Historic vi generally got this wrong (e.g. try
272 * "c?$<cr>"). Not all that sure this gets it right, there
273 * are lots of strange cases.
275 if (!LF_ISSET(SEARCH_EOL) && rm->cno >= len)
276 rm->cno = len != 0 ? len - 1 : 0;
282 if (LF_ISSET(SEARCH_MSG))
283 search_busy(sp, BUSY_OFF);
289 * Do a backward search.
291 * PUBLIC: int b_search __P((SCR *,
292 * PUBLIC: MARK *, MARK *, CHAR_T *, size_t, CHAR_T **, u_int));
307 size_t coff, last, len;
308 int cnt, eval, rval, wrapped;
311 if (search_init(sp, BACKWARD, ptrn, plen, eptrn, flags))
315 * If doing incremental search, set the "starting" position past the
316 * current column, so that we search a minimal distance and still
317 * match special patterns, e.g., \> for the end of a word. This is
318 * safe when the cursor is at the end of a line because we only use
319 * it for comparison with the location of the match.
321 * Otherwise, start searching immediately before the cursor. If in
322 * the first column, start search on the previous line.
324 if (LF_ISSET(SEARCH_INCR)) {
329 if (fm->lno == 1 && !O_ISSET(sp, O_WRAPSCAN)) {
330 if (LF_ISSET(SEARCH_MSG))
331 search_msg(sp, S_SOF);
341 for (cnt = INTERRUPT_CHECK, rval = 1, wrapped = 0;; --lno, coff = 0) {
345 if (LF_ISSET(SEARCH_MSG)) {
346 search_busy(sp, btype);
349 cnt = INTERRUPT_CHECK;
351 if ((wrapped && lno < fm->lno) || lno == 0) {
353 if (LF_ISSET(SEARCH_MSG))
354 search_msg(sp, S_NOTFOUND);
357 if (!O_ISSET(sp, O_WRAPSCAN)) {
358 if (LF_ISSET(SEARCH_MSG))
359 search_msg(sp, S_SOF);
362 if (db_last(sp, &lno))
365 if (LF_ISSET(SEARCH_MSG))
366 search_msg(sp, S_EMPTY);
374 if (db_get(sp, lno, 0, &l, &len))
377 /* Set the termination. */
379 match[0].rm_eo = len;
381 #if defined(DEBUG) && 0
382 TRACE(sp, "B search: %lu from 0 to %qu\n", lno, match[0].rm_eo);
384 /* Search the line. */
385 eval = regexec(&sp->re_c, l, 1, match,
386 (match[0].rm_eo == len ? 0 : REG_NOTEOL) | REG_STARTEND);
387 if (eval == REG_NOMATCH)
390 if (LF_ISSET(SEARCH_MSG))
391 re_error(sp, eval, &sp->re_c);
393 (void)sp->gp->scr_bell(sp);
397 /* Check for a match starting past the cursor. */
398 if (coff != 0 && match[0].rm_so >= coff)
401 /* Warn if the search wrapped. */
402 if (wrapped && LF_ISSET(SEARCH_WMSG))
403 search_msg(sp, S_WRAP);
405 #if defined(DEBUG) && 0
406 TRACE(sp, "B found: %qu to %qu\n",
407 match[0].rm_so, match[0].rm_eo);
410 * We now have the first match on the line. Step through the
411 * line character by character until find the last acceptable
412 * match. This is painful, we need a better interface to regex
416 last = match[0].rm_so++;
417 if (match[0].rm_so >= len)
419 match[0].rm_eo = len;
420 eval = regexec(&sp->re_c, l, 1, match,
421 (match[0].rm_so == 0 ? 0 : REG_NOTBOL) |
423 if (eval == REG_NOMATCH)
426 if (LF_ISSET(SEARCH_MSG))
427 re_error(sp, eval, &sp->re_c);
429 (void)sp->gp->scr_bell(sp);
432 if (coff && match[0].rm_so >= coff)
437 /* See comment in f_search(). */
438 if (!LF_ISSET(SEARCH_EOL) && last >= len)
439 rm->cno = len != 0 ? len - 1 : 0;
446 err: if (LF_ISSET(SEARCH_MSG))
447 search_busy(sp, BUSY_OFF);
453 * Display one of the search messages.
462 msgq(sp, M_ERR, "072|File empty; nothing to search");
466 "073|Reached end-of-file without finding the pattern");
469 msgq(sp, M_ERR, "074|No previous search pattern");
472 msgq(sp, M_ERR, "075|Pattern not found");
476 "076|Reached top-of-file without finding the pattern");
479 msgq(sp, M_ERR, "077|Search wrapped");
488 * Put up the busy searching message.
490 * PUBLIC: void search_busy __P((SCR *, busy_t));
497 sp->gp->scr_busy(sp, "078|Searching...", btype);