source: trunk/third/nvi/vi/v_search.c @ 14302

Revision 14302, 12.6 KB checked in by ghudson, 25 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r14301, which included commits to RCS files with non-trunk default branches.
RevLine 
[14301]1/*-
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.
6 *
7 * See the LICENSE file for redistribution information.
8 */
9
10#include "config.h"
11
12#ifndef lint
13static const char sccsid[] = "@(#)v_search.c    10.18 (Berkeley) 9/19/96";
14#endif /* not lint */
15
16#include <sys/types.h>
17#include <sys/queue.h>
18#include <sys/time.h>
19
20#include <bitstring.h>
21#include <ctype.h>
22#include <errno.h>
23#include <limits.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27
28#include "../common/common.h"
29#include "vi.h"
30
31static int v_exaddr __P((SCR *, VICMD *, dir_t));
32static int v_search __P((SCR *, VICMD *, char *, size_t, u_int, dir_t));
33
34/*
35 * v_srch -- [count]?RE[? offset]
36 *      Ex address search backward.
37 *
38 * PUBLIC: int v_searchb __P((SCR *, VICMD *));
39 */
40int
41v_searchb(sp, vp)
42        SCR *sp;
43        VICMD *vp;
44{
45        return (v_exaddr(sp, vp, BACKWARD));
46}
47
48/*
49 * v_searchf -- [count]/RE[/ offset]
50 *      Ex address search forward.
51 *
52 * PUBLIC: int v_searchf __P((SCR *, VICMD *));
53 */
54int
55v_searchf(sp, vp)
56        SCR *sp;
57        VICMD *vp;
58{
59        return (v_exaddr(sp, vp, FORWARD));
60}
61
62/*
63 * v_exaddr --
64 *      Do a vi search (which is really an ex address).
65 */
66static int
67v_exaddr(sp, vp, dir)
68        SCR *sp;
69        VICMD *vp;
70        dir_t dir;
71{
72        static EXCMDLIST fake = { "search" };
73        EXCMD *cmdp;
74        GS *gp;
75        TEXT *tp;
76        recno_t s_lno;
77        size_t len, s_cno, tlen;
78        int err, nb, type;
79        char *cmd, *t, buf[20];
80
81        /*
82         * !!!
83         * If using the search command as a motion, any addressing components
84         * are lost, i.e. y/ptrn/+2, when repeated, is the same as y/ptrn/.
85         */
86        if (F_ISSET(vp, VC_ISDOT))
87                return (v_search(sp, vp,
88                    NULL, 0, SEARCH_PARSE | SEARCH_MSG | SEARCH_SET, dir));
89
90        /* Get the search pattern. */
91        if (v_tcmd(sp, vp, dir == BACKWARD ? CH_BSEARCH : CH_FSEARCH,
92            TXT_BS | TXT_CR | TXT_ESCAPE | TXT_PROMPT |
93            (O_ISSET(sp, O_SEARCHINCR) ? TXT_SEARCHINCR : 0)))
94                return (1);
95
96        tp = sp->tiq.cqh_first;
97
98        /* If the user backspaced over the prompt, do nothing. */
99        if (tp->term == TERM_BS)
100                return (1);
101
102        /*
103         * If the user was doing an incremental search, then we've already
104         * updated the cursor and moved to the right location.  Return the
105         * correct values, we're done.
106         */
107        if (tp->term == TERM_SEARCH) {
108                vp->m_stop.lno = sp->lno;
109                vp->m_stop.cno = sp->cno;
110                if (ISMOTION(vp))
111                        return (v_correct(sp, vp, 0));
112                vp->m_final = vp->m_stop;
113                return (0);
114        }
115
116        /*
117         * If the user entered <escape> or <carriage-return>, the length is
118         * 1 and the right thing will happen, i.e. the prompt will be used
119         * as a command character.
120         *
121         * Build a fake ex command structure.
122         */
123        gp = sp->gp;
124        gp->excmd.cp = tp->lb;
125        gp->excmd.clen = tp->len;
126        F_INIT(&gp->excmd, E_VISEARCH);
127
128        /*
129         * XXX
130         * Warn if the search wraps.  This is a pretty special case, but it's
131         * nice feature that wasn't in the original implementations of ex/vi.
132         * (It was added at some point to System V's version.)  This message
133         * is only displayed if there are no keys in the queue. The problem is
134         * the command is going to succeed, and the message is informational,
135         * not an error.  If a macro displays it repeatedly, e.g., the pattern
136         * only occurs once in the file and wrapscan is set, you lose big.  For
137         * example, if the macro does something like:
138         *
139         *      :map K /pattern/^MjK
140         *
141         * Each search will display the message, but the following "/pattern/"
142         * will immediately overwrite it, with strange results.  The System V
143         * vi displays the "wrapped" message multiple times, but because it's
144         * overwritten each time, it's not as noticeable.  As we don't discard
145         * messages, it's a real problem for us.
146         */
147        if (!KEYS_WAITING(sp))
148                F_SET(&gp->excmd, E_SEARCH_WMSG);
149               
150        /* Save the current line/column. */
151        s_lno = sp->lno;
152        s_cno = sp->cno;
153
154        /*
155         * !!!
156         * Historically, vi / and ? commands were full-blown ex addresses,
157         * including ';' delimiters, trailing <blank>'s, multiple search
158         * strings (separated by semi-colons) and, finally, full-blown z
159         * commands after the / and ? search strings.  (If the search was
160         * being used as a motion, the trailing z command was ignored.
161         * Also, we do some argument checking on the z command, to be sure
162         * that it's not some other random command.) For multiple search
163         * strings, leading <blank>'s at the second and subsequent strings
164         * were eaten as well.  This has some (unintended?) side-effects:
165         * the command /ptrn/;3 is legal and results in moving to line 3.
166         * I suppose you could use it to optionally move to line 3...
167         *
168         * !!!
169         * Historically, if any part of the search command failed, the cursor
170         * remained unmodified (even if ; was used).  We have to play games
171         * because the underlying ex parser thinks we're modifying the cursor
172         * as we go, but I think we're compatible with historic practice.
173         *
174         * !!!
175         * Historically, the command "/STRING/;   " failed, apparently it
176         * confused the parser.  We're not that compatible.
177         */
178        cmdp = &gp->excmd;
179        if (ex_range(sp, cmdp, &err))
180                return (1);
181       
182        /*
183         * Remember where any remaining command information is, and clean
184         * up the fake ex command.
185         */
186        cmd = cmdp->cp;
187        len = cmdp->clen;
188        gp->excmd.clen = 0;
189
190        if (err)
191                goto err2;
192
193        /* Copy out the new cursor position and make sure it's okay. */
194        switch (cmdp->addrcnt) {
195        case 1:
196                vp->m_stop = cmdp->addr1;
197                break;
198        case 2:
199                vp->m_stop = cmdp->addr2;
200                break;
201        }
202        if (!db_exist(sp, vp->m_stop.lno)) {
203                ex_badaddr(sp, &fake,
204                    vp->m_stop.lno == 0 ? A_ZERO : A_EOF, NUM_OK);
205                goto err2;
206        }
207
208        /*
209         * !!!
210         * Historic practice is that a trailing 'z' was ignored if it was a
211         * motion command.  Should probably be an error, but not worth the
212         * effort.
213         */
214        if (ISMOTION(vp))
215                return (v_correct(sp, vp, F_ISSET(cmdp, E_DELTA)));
216               
217        /*
218         * !!!
219         * Historically, if it wasn't a motion command, a delta in the search
220         * pattern turns it into a first nonblank movement.
221         */
222        nb = F_ISSET(cmdp, E_DELTA);
223
224        /* Check for the 'z' command. */
225        if (len != 0) {
226                if (*cmd != 'z')
227                        goto err1;
228
229                /* No blanks, just like the z command. */
230                for (t = cmd + 1, tlen = len - 1; tlen > 0; ++t, --tlen)
231                        if (!isdigit(*t))
232                                break;
233                if (tlen &&
234                    (*t == '-' || *t == '.' || *t == '+' || *t == '^')) {
235                        ++t;
236                        --tlen;
237                        type = 1;
238                } else
239                        type = 0;
240                if (tlen)
241                        goto err1;
242
243                /* The z command will do the nonblank for us. */
244                nb = 0;
245
246                /* Default to z+. */
247                if (!type &&
248                    v_event_push(sp, NULL, "+", 1, CH_NOMAP | CH_QUOTED))
249                        return (1);
250
251                /* Push the user's command. */
252                if (v_event_push(sp, NULL, cmd, len, CH_NOMAP | CH_QUOTED))
253                        return (1);
254
255                /* Push line number so get correct z display. */
256                tlen = snprintf(buf,
257                    sizeof(buf), "%lu", (u_long)vp->m_stop.lno);
258                if (v_event_push(sp, NULL, buf, tlen, CH_NOMAP | CH_QUOTED))
259                        return (1);
260                 
261                /* Don't refresh until after 'z' happens. */
262                F_SET(VIP(sp), VIP_S_REFRESH);
263        }
264
265        /* Non-motion commands move to the end of the range. */
266        vp->m_final = vp->m_stop;
267        if (nb) {
268                F_CLR(vp, VM_RCM_MASK);
269                F_SET(vp, VM_RCM_SETFNB);
270        }
271        return (0);
272
273err1:   msgq(sp, M_ERR,
274            "188|Characters after search string, line offset and/or z command");
275err2:   vp->m_final.lno = s_lno;
276        vp->m_final.cno = s_cno;
277        return (1);
278}
279
280/*
281 * v_searchN -- N
282 *      Reverse last search.
283 *
284 * PUBLIC: int v_searchN __P((SCR *, VICMD *));
285 */
286int
287v_searchN(sp, vp)
288        SCR *sp;
289        VICMD *vp;
290{
291        dir_t dir;
292
293        switch (sp->searchdir) {
294        case BACKWARD:
295                dir = FORWARD;
296                break;
297        case FORWARD:
298                dir = BACKWARD;
299                break;
300        default:
301                dir = sp->searchdir;
302                break;
303        }
304        return (v_search(sp, vp, NULL, 0, SEARCH_PARSE, dir));
305}
306
307/*
308 * v_searchn -- n
309 *      Repeat last search.
310 *
311 * PUBLIC: int v_searchn __P((SCR *, VICMD *));
312 */
313int
314v_searchn(sp, vp)
315        SCR *sp;
316        VICMD *vp;
317{
318        return (v_search(sp, vp, NULL, 0, SEARCH_PARSE, sp->searchdir));
319}
320
321/*
322 * v_searchw -- [count]^A
323 *      Search for the word under the cursor.
324 *
325 * PUBLIC: int v_searchw __P((SCR *, VICMD *));
326 */
327int
328v_searchw(sp, vp)
329        SCR *sp;
330        VICMD *vp;
331{
332        size_t blen, len;
333        int rval;
334        char *bp;
335
336        len = VIP(sp)->klen + sizeof(RE_WSTART) + sizeof(RE_WSTOP);
337        GET_SPACE_RET(sp, bp, blen, len);
338        len = snprintf(bp, blen, "%s%s%s", RE_WSTART, VIP(sp)->keyw, RE_WSTOP);
339
340        rval = v_search(sp, vp, bp, len, SEARCH_SET, FORWARD);
341
342        FREE_SPACE(sp, bp, blen);
343        return (rval);
344}
345
346/*
347 * v_search --
348 *      The search commands.
349 */
350static int
351v_search(sp, vp, ptrn, plen, flags, dir)
352        SCR *sp;
353        VICMD *vp;
354        u_int flags;
355        char *ptrn;
356        size_t plen;
357        dir_t dir;
358{
359        /* Display messages. */
360        LF_SET(SEARCH_MSG);
361
362        /* If it's a motion search, offset past end-of-line is okay. */
363        if (ISMOTION(vp))
364                LF_SET(SEARCH_EOL);
365
366        /*
367         * XXX
368         * Warn if the search wraps.  See the comment above, in v_exaddr().
369         */
370        if (!KEYS_WAITING(sp))
371                LF_SET(SEARCH_WMSG);
372               
373        switch (dir) {
374        case BACKWARD:
375                if (b_search(sp,
376                    &vp->m_start, &vp->m_stop, ptrn, plen, NULL, flags))
377                        return (1);
378                break;
379        case FORWARD:
380                if (f_search(sp,
381                    &vp->m_start, &vp->m_stop, ptrn, plen, NULL, flags))
382                        return (1);
383                break;
384        case NOTSET:
385                msgq(sp, M_ERR, "189|No previous search pattern");
386                return (1);
387        default:
388                abort();
389        }
390
391        /* Correct motion commands, otherwise, simply move to the location. */
392        if (ISMOTION(vp)) {
393                if (v_correct(sp, vp, 0))
394                        return(1);
395        } else
396                vp->m_final = vp->m_stop;
397        return (0);
398}
399
400/*
401 * v_correct --
402 *      Handle command with a search as the motion.
403 *
404 * !!!
405 * Historically, commands didn't affect the line searched to/from if the
406 * motion command was a search and the final position was the start/end
407 * of the line.  There were some special cases and vi was not consistent;
408 * it was fairly easy to confuse it.  For example, given the two lines:
409 *
410 *      abcdefghi
411 *      ABCDEFGHI
412 *
413 * placing the cursor on the 'A' and doing y?$ would so confuse it that 'h'
414 * 'k' and put would no longer work correctly.  In any case, we try to do
415 * the right thing, but it's not going to exactly match historic practice.
416 *
417 * PUBLIC: int v_correct __P((SCR *, VICMD *, int));
418 */
419int
420v_correct(sp, vp, isdelta)
421        SCR *sp;
422        VICMD *vp;
423        int isdelta;
424{
425        dir_t dir;
426        MARK m;
427        size_t len;
428
429        /*
430         * !!!
431         * We may have wrapped if wrapscan was set, and we may have returned
432         * to the position where the cursor started.  Historic vi didn't cope
433         * with this well.  Yank wouldn't beep, but the first put after the
434         * yank would move the cursor right one column (without adding any
435         * text) and the second would put a copy of the current line.  The
436         * change and delete commands would beep, but would leave the cursor
437         * on the colon command line.  I believe that there are macros that
438         * depend on delete, at least, failing.  For now, commands that use
439         * search as a motion component fail when the search returns to the
440         * original cursor position.
441         */
442        if (vp->m_start.lno == vp->m_stop.lno &&
443            vp->m_start.cno == vp->m_stop.cno) {
444                msgq(sp, M_BERR, "190|Search wrapped to original position");
445                return (1);
446        }
447
448        /*
449         * !!!
450         * Searches become line mode operations if there was a delta specified
451         * to the search pattern.
452         */
453        if (isdelta)
454                F_SET(vp, VM_LMODE);
455
456        /*
457         * If the motion is in the reverse direction, switch the start and
458         * stop MARK's so that it's in a forward direction.  (There's no
459         * reason for this other than to make the tests below easier.  The
460         * code in vi.c:vi() would have done the switch.)  Both forward
461         * and backward motions can happen for any kind of search command
462         * because of the wrapscan option.
463         */
464        if (vp->m_start.lno > vp->m_stop.lno ||
465            vp->m_start.lno == vp->m_stop.lno &&
466            vp->m_start.cno > vp->m_stop.cno) {
467                m = vp->m_start;
468                vp->m_start = vp->m_stop;
469                vp->m_stop = m;
470                dir = BACKWARD;
471        } else
472                dir = FORWARD;
473
474        /*
475         * BACKWARD:
476         *      Delete and yank commands move to the end of the range.
477         *      Ignore others.
478         *
479         * FORWARD:
480         *      Delete and yank commands don't move.  Ignore others.
481         */
482        vp->m_final = vp->m_start;
483
484        /*
485         * !!!
486         * Delta'd searches don't correct based on column positions.
487         */
488        if (isdelta)
489                return (0);
490
491        /*
492         * !!!
493         * Backward searches starting at column 0, and forward searches ending
494         * at column 0 are corrected to the last column of the previous line.
495         * Otherwise, adjust the starting/ending point to the character before
496         * the current one (this is safe because we know the search had to move
497         * to succeed).
498         *
499         * Searches become line mode operations if they start at the first
500         * nonblank and end at column 0 of another line.
501         */
502        if (vp->m_start.lno < vp->m_stop.lno && vp->m_stop.cno == 0) {
503                if (db_get(sp, --vp->m_stop.lno, DBG_FATAL, NULL, &len))
504                        return (1);
505                vp->m_stop.cno = len ? len - 1 : 0;
506                len = 0;
507                if (nonblank(sp, vp->m_start.lno, &len))
508                        return (1);
509                if (vp->m_start.cno <= len)
510                        F_SET(vp, VM_LMODE);
511        } else
512                --vp->m_stop.cno;
513
514        return (0);
515}
Note: See TracBrowser for help on using the repository browser.