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

Revision 14302, 14.2 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.
Line 
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_ex.c        10.42 (Berkeley) 6/28/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 <limits.h>
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include <unistd.h>
26
27#include "../common/common.h"
28#include "vi.h"
29
30static int v_ecl __P((SCR *));
31static int v_ecl_init __P((SCR *));
32static int v_ecl_log __P((SCR *, TEXT *));
33static int v_ex_done __P((SCR *, VICMD *));
34static int v_exec_ex __P((SCR *, VICMD *, EXCMD *));
35
36/*
37 * v_again -- &
38 *      Repeat the previous substitution.
39 *
40 * PUBLIC: int v_again __P((SCR *, VICMD *));
41 */
42int
43v_again(sp, vp)
44        SCR *sp;
45        VICMD *vp;
46{
47        ARGS *ap[2], a;
48        EXCMD cmd;
49
50        ex_cinit(&cmd, C_SUBAGAIN, 2, vp->m_start.lno, vp->m_start.lno, 1, ap);
51        ex_cadd(&cmd, &a, "", 1);
52
53        return (v_exec_ex(sp, vp, &cmd));
54}
55
56/*
57 * v_exmode -- Q
58 *      Switch the editor into EX mode.
59 *
60 * PUBLIC: int v_exmode __P((SCR *, VICMD *));
61 */
62int
63v_exmode(sp, vp)
64        SCR *sp;
65        VICMD *vp;
66{
67        GS *gp;
68
69        gp = sp->gp;
70
71        /* Try and switch screens -- the screen may not permit it. */
72        if (gp->scr_screen(sp, SC_EX)) {
73                msgq(sp, M_ERR,
74                    "207|The Q command requires the ex terminal interface");
75                return (1);
76        }
77        (void)gp->scr_attr(sp, SA_ALTERNATE, 0);
78
79        /* Save the current cursor position. */
80        sp->frp->lno = sp->lno;
81        sp->frp->cno = sp->cno;
82        F_SET(sp->frp, FR_CURSORSET);
83
84        /* Switch to ex mode. */
85        F_CLR(sp, SC_VI | SC_SCR_VI);
86        F_SET(sp, SC_EX);
87
88        /* Move out of the vi screen. */
89        (void)ex_puts(sp, "\n");
90
91        return (0);
92}
93
94/*
95 * v_join -- [count]J
96 *      Join lines together.
97 *
98 * PUBLIC: int v_join __P((SCR *, VICMD *));
99 */
100int
101v_join(sp, vp)
102        SCR *sp;
103        VICMD *vp;
104{
105        EXCMD cmd;
106        int lno;
107
108        /*
109         * YASC.
110         * The general rule is that '#J' joins # lines, counting the current
111         * line.  However, 'J' and '1J' are the same as '2J', i.e. join the
112         * current and next lines.  This doesn't map well into the ex command
113         * (which takes two line numbers), so we handle it here.  Note that
114         * we never test for EOF -- historically going past the end of file
115         * worked just fine.
116         */
117        lno = vp->m_start.lno + 1;
118        if (F_ISSET(vp, VC_C1SET) && vp->count > 2)
119                lno = vp->m_start.lno + (vp->count - 1);
120
121        ex_cinit(&cmd, C_JOIN, 2, vp->m_start.lno, lno, 0, NULL);
122        return (v_exec_ex(sp, vp, &cmd));
123}
124
125/*
126 * v_shiftl -- [count]<motion
127 *      Shift lines left.
128 *
129 * PUBLIC: int v_shiftl __P((SCR *, VICMD *));
130 */
131int
132v_shiftl(sp, vp)
133        SCR *sp;
134        VICMD *vp;
135{
136        ARGS *ap[2], a;
137        EXCMD cmd;
138
139        ex_cinit(&cmd, C_SHIFTL, 2, vp->m_start.lno, vp->m_stop.lno, 0, ap);
140        ex_cadd(&cmd, &a, "<", 1);
141        return (v_exec_ex(sp, vp, &cmd));
142}
143
144/*
145 * v_shiftr -- [count]>motion
146 *      Shift lines right.
147 *
148 * PUBLIC: int v_shiftr __P((SCR *, VICMD *));
149 */
150int
151v_shiftr(sp, vp)
152        SCR *sp;
153        VICMD *vp;
154{
155        ARGS *ap[2], a;
156        EXCMD cmd;
157
158        ex_cinit(&cmd, C_SHIFTR, 2, vp->m_start.lno, vp->m_stop.lno, 0, ap);
159        ex_cadd(&cmd, &a, ">", 1);
160        return (v_exec_ex(sp, vp, &cmd));
161}
162
163/*
164 * v_suspend -- ^Z
165 *      Suspend vi.
166 *
167 * PUBLIC: int v_suspend __P((SCR *, VICMD *));
168 */
169int
170v_suspend(sp, vp)
171        SCR *sp;
172        VICMD *vp;
173{
174        ARGS *ap[2], a;
175        EXCMD cmd;
176
177        ex_cinit(&cmd, C_STOP, 0, OOBLNO, OOBLNO, 0, ap);
178        ex_cadd(&cmd, &a, "suspend", sizeof("suspend") - 1);
179        return (v_exec_ex(sp, vp, &cmd));
180}
181
182/*
183 * v_switch -- ^^
184 *      Switch to the previous file.
185 *
186 * PUBLIC: int v_switch __P((SCR *, VICMD *));
187 */
188int
189v_switch(sp, vp)
190        SCR *sp;
191        VICMD *vp;
192{
193        ARGS *ap[2], a;
194        EXCMD cmd;
195        char *name;
196
197        /*
198         * Try the alternate file name, then the previous file
199         * name.  Use the real name, not the user's current name.
200         */
201        if ((name = sp->alt_name) == NULL) {
202                msgq(sp, M_ERR, "180|No previous file to edit");
203                return (1);
204        }
205
206        /* If autowrite is set, write out the file. */
207        if (file_m1(sp, 0, FS_ALL))
208                return (1);
209
210        ex_cinit(&cmd, C_EDIT, 0, OOBLNO, OOBLNO, 0, ap);
211        ex_cadd(&cmd, &a, name, strlen(name));
212        return (v_exec_ex(sp, vp, &cmd));
213}
214
215/*
216 * v_tagpush -- ^[
217 *      Do a tag search on the cursor keyword.
218 *
219 * PUBLIC: int v_tagpush __P((SCR *, VICMD *));
220 */
221int
222v_tagpush(sp, vp)
223        SCR *sp;
224        VICMD *vp;
225{
226        ARGS *ap[2], a;
227        EXCMD cmd;
228
229        ex_cinit(&cmd, C_TAG, 0, OOBLNO, 0, 0, ap);
230        ex_cadd(&cmd, &a, VIP(sp)->keyw, strlen(VIP(sp)->keyw));
231        return (v_exec_ex(sp, vp, &cmd));
232}
233
234/*
235 * v_tagpop -- ^T
236 *      Pop the tags stack.
237 *
238 * PUBLIC: int v_tagpop __P((SCR *, VICMD *));
239 */
240int
241v_tagpop(sp, vp)
242        SCR *sp;
243        VICMD *vp;
244{
245        EXCMD cmd;
246
247        ex_cinit(&cmd, C_TAGPOP, 0, OOBLNO, 0, 0, NULL);
248        return (v_exec_ex(sp, vp, &cmd));
249}
250
251/*
252 * v_filter -- [count]!motion command(s)
253 *      Run range through shell commands, replacing text.
254 *
255 * PUBLIC: int v_filter __P((SCR *, VICMD *));
256 */
257int
258v_filter(sp, vp)
259        SCR *sp;
260        VICMD *vp;
261{
262        EXCMD cmd;
263        TEXT *tp;
264
265        /*
266         * !!!
267         * Historical vi permitted "!!" in an empty file, and it's handled
268         * as a special case in the ex_bang routine.  Don't modify this setup
269         * without understanding that one.  In particular, note that we're
270         * manipulating the ex argument structures behind ex's back.
271         *
272         * !!!
273         * Historical vi did not permit the '!' command to be associated with
274         * a non-line oriented motion command, in general, although it did
275         * with search commands.  So, !f; and !w would fail, but !/;<CR>
276         * would succeed, even if they all moved to the same location in the
277         * current line.  I don't see any reason to disallow '!' using any of
278         * the possible motion commands.
279         *
280         * !!!
281         * Historical vi ran the last bang command if N or n was used as the
282         * search motion.
283         */
284        if (F_ISSET(vp, VC_ISDOT) ||
285            ISCMD(vp->rkp, 'N') || ISCMD(vp->rkp, 'n')) {
286                ex_cinit(&cmd, C_BANG,
287                    2, vp->m_start.lno, vp->m_stop.lno, 0, NULL);
288                EXP(sp)->argsoff = 0;                   /* XXX */
289
290                if (argv_exp1(sp, &cmd, "!", 1, 1))
291                        return (1);
292                cmd.argc = EXP(sp)->argsoff;            /* XXX */
293                cmd.argv = EXP(sp)->args;               /* XXX */
294                return (v_exec_ex(sp, vp, &cmd));
295        }
296
297        /* Get the command from the user. */
298        if (v_tcmd(sp, vp,
299            '!', TXT_BS | TXT_CR | TXT_ESCAPE | TXT_FILEC | TXT_PROMPT))
300                return (1);
301
302        /*
303         * Check to see if the user changed their mind.
304         *
305         * !!!
306         * Entering <escape> on an empty line was historically an error,
307         * this implementation doesn't bother.
308         */
309        tp = sp->tiq.cqh_first;
310        if (tp->term != TERM_OK) {
311                vp->m_final.lno = sp->lno;
312                vp->m_final.cno = sp->cno;
313                return (0);
314        }
315
316        /* Home the cursor. */
317        vs_home(sp);
318
319        ex_cinit(&cmd, C_BANG, 2, vp->m_start.lno, vp->m_stop.lno, 0, NULL);
320        EXP(sp)->argsoff = 0;                   /* XXX */
321
322        if (argv_exp1(sp, &cmd, tp->lb + 1, tp->len - 1, 1))
323                return (1);
324        cmd.argc = EXP(sp)->argsoff;            /* XXX */
325        cmd.argv = EXP(sp)->args;               /* XXX */
326        return (v_exec_ex(sp, vp, &cmd));
327}
328
329/*
330 * v_event_exec --
331 *      Execute some command(s) based on an event.
332 *
333 * PUBLIC: int v_event_exec __P((SCR *, VICMD *));
334 */
335int
336v_event_exec(sp, vp)
337        SCR *sp;
338        VICMD *vp;
339{
340        EXCMD cmd;
341
342        switch (vp->ev.e_event) {
343        case E_QUIT:
344                ex_cinit(&cmd, C_QUIT, 0, OOBLNO, OOBLNO, 0, NULL);
345                break;
346        case E_WRITE:
347                ex_cinit(&cmd, C_WRITE, 0, OOBLNO, OOBLNO, 0, NULL);
348                break;
349        default:
350                abort();
351        }
352        return (v_exec_ex(sp, vp, &cmd));
353}
354
355/*
356 * v_exec_ex --
357 *      Execute an ex command.
358 */
359static int
360v_exec_ex(sp, vp, exp)
361        SCR *sp;
362        VICMD *vp;
363        EXCMD *exp;
364{
365        int rval;
366
367        rval = exp->cmd->fn(sp, exp);
368        return (v_ex_done(sp, vp) || rval);
369}
370
371/*
372 * v_ex -- :
373 *      Execute a colon command line.
374 *
375 * PUBLIC: int v_ex __P((SCR *, VICMD *));
376 */
377int
378v_ex(sp, vp)
379        SCR *sp;
380        VICMD *vp;
381{
382        GS *gp;
383        TEXT *tp;
384        int do_cedit, do_resolution, ifcontinue;
385
386        gp = sp->gp;
387
388        /*
389         * !!!
390         * If we put out more than a single line of messages, or ex trashes
391         * the screen, the user may continue entering ex commands.  We find
392         * this out when we do the screen/message resolution.  We can't enter
393         * completely into ex mode however, because the user can elect to
394         * return into vi mode by entering any key, i.e. we have to be in raw
395         * mode.
396         */
397        for (do_cedit = do_resolution = 0;;) {
398                /*
399                 * !!!
400                 * There may already be an ex command waiting to run.  If
401                 * so, we continue with it.
402                 */
403                if (!EXCMD_RUNNING(gp)) {
404                        /* Get a command. */
405                        if (v_tcmd(sp, vp, ':',
406                            TXT_BS | TXT_CEDIT | TXT_FILEC | TXT_PROMPT))
407                                return (1);
408                        tp = sp->tiq.cqh_first;
409
410                        /*
411                         * If the user entered a single <esc>, they want to
412                         * edit their colon command history.  If they already
413                         * entered some text, move it into the edit history.
414                         */
415                        if (tp->term == TERM_CEDIT) {
416                                if (tp->len > 1 && v_ecl_log(sp, tp))
417                                        return (1);
418                                do_cedit = 1;
419                                break;
420                        }
421
422                        /* If the user didn't enter anything, return. */
423                        if (tp->term == TERM_BS)
424                                break;
425
426                        /* Log the command. */
427                        if (O_STR(sp, O_CEDIT) != NULL && v_ecl_log(sp, tp))
428                                return (1);
429
430                        /* Push a command on the command stack. */
431                        if (ex_run_str(sp, NULL, tp->lb, tp->len, 0, 1))
432                                return (1);
433                }
434
435                /* Home the cursor. */
436                vs_home(sp);
437
438                /*
439                 * !!!
440                 * If the editor wrote the screen behind curses back, put out
441                 * a <newline> so that we don't overwrite the user's command
442                 * with its output or the next want-to-continue? message.  This
443                 * doesn't belong here, but I can't find another place to put
444                 * it.  See, we resolved the output from the last ex command,
445                 * and the user entered another one.  This is the only place
446                 * where we have control before the ex command writes output.
447                 * We could get control in vs_msg(), but we have no way to know
448                 * if command didn't put out any output when we try and resolve
449                 * this command.  This fixes a bug where combinations of ex
450                 * commands, e.g. ":set<CR>:!date<CR>:set" didn't look right.
451                 */
452                if (F_ISSET(sp, SC_SCR_EXWROTE))
453                        (void)putchar('\n');
454
455                /* Call the ex parser. */
456                (void)ex_cmd(sp);
457
458                /* Flush ex messages. */
459                (void)ex_fflush(sp);
460
461                /* Resolve any messages. */
462                if (vs_ex_resolve(sp, &ifcontinue))
463                        return (1);
464
465                /*
466                 * Continue or return.  If continuing, make sure that we
467                 * eventually do resolution.
468                 */
469                if (!ifcontinue)
470                        break;
471                do_resolution = 1;
472
473                /* If we're continuing, it's a new command. */
474                ++sp->ccnt;
475        }
476
477        /*
478         * If the user previously continued an ex command, we have to do
479         * resolution to clean up the screen.  Don't wait, we already did
480         * that.
481         */
482        if (do_resolution) {
483                F_SET(sp, SC_EX_WAIT_NO);
484                if (vs_ex_resolve(sp, &ifcontinue))
485                        return (1);
486        }
487
488        /* Cleanup from the ex command. */
489        if (v_ex_done(sp, vp))
490                return (1);
491
492        /* The user may want to edit their colon command history. */
493        if (do_cedit)
494                return (v_ecl(sp));
495
496        return (0);
497}
498
499/*
500 * v_ex_done --
501 *      Cleanup from an ex command.
502 */
503static int
504v_ex_done(sp, vp)
505        SCR *sp;
506        VICMD *vp;
507{
508        size_t len;
509
510        /*
511         * The only cursor modifications are real, however, the underlying
512         * line may have changed; don't trust anything.  This code has been
513         * a remarkably fertile place for bugs.  Do a reality check on a
514         * cursor value, and make sure it's okay.  If necessary, change it.
515         * Ex keeps track of the line number, but it cares less about the
516         * column and it may have disappeared.
517         *
518         * Don't trust ANYTHING.
519         *
520         * XXX
521         * Ex will soon have to start handling the column correctly; see
522         * the POSIX 1003.2 standard.
523         */
524        if (db_eget(sp, sp->lno, NULL, &len, NULL)) {
525                sp->lno = 1;
526                sp->cno = 0;
527        } else if (sp->cno >= len)
528                sp->cno = len ? len - 1 : 0;
529
530        vp->m_final.lno = sp->lno;
531        vp->m_final.cno = sp->cno;
532
533        /*
534         * Don't re-adjust the cursor after executing an ex command,
535         * and ex movements are permanent.
536         */
537        F_CLR(vp, VM_RCM_MASK);
538        F_SET(vp, VM_RCM_SET);
539
540        return (0);
541}
542
543/*
544 * v_ecl --
545 *      Start an edit window on the colon command-line commands.
546 */
547static int
548v_ecl(sp)
549        SCR *sp;
550{
551        GS *gp;
552        SCR *new;
553
554        /* Initialize the screen, if necessary. */
555        gp = sp->gp;
556        if (gp->ccl_sp == NULL && v_ecl_init(sp))
557                return (1);
558
559        /* Get a new screen. */
560        if (screen_init(gp, sp, &new))
561                return (1);
562        if (vs_split(sp, new, 1)) {
563                (void)screen_end(new);
564                return (1);
565        }
566
567        /* Attach to the screen. */
568        new->ep = gp->ccl_sp->ep;
569        ++new->ep->refcnt;
570
571        new->frp = gp->ccl_sp->frp;
572        new->frp->flags = sp->frp->flags;
573
574        /* Move the cursor to the end. */
575        (void)db_last(new, &new->lno);
576        if (new->lno == 0)
577                new->lno = 1;
578
579        /* Remember the originating window. */
580        sp->ccl_parent = sp;
581
582        /* It's a special window. */
583        F_SET(new, SC_COMEDIT);
584
585        /* Set up the switch. */
586        sp->nextdisp = new;
587        F_SET(sp, SC_SSWITCH);
588        return (0);
589}
590
591/*
592 * v_ecl_exec --
593 *      Execute a command from a colon command-line window.
594 *
595 * PUBLIC: int v_ecl_exec __P((SCR *));
596 */
597int
598v_ecl_exec(sp)
599        SCR *sp;
600{
601        size_t len;
602        char *p;
603
604        if (db_get(sp, sp->lno, 0, &p, &len) && sp->lno == 1) {
605                v_emsg(sp, NULL, VIM_EMPTY);
606                return (1);
607        }
608        if (len == 0) {
609                msgq(sp, M_BERR, "307|No ex command to execute");
610                return (1);
611        }
612       
613        /* Push the command on the command stack. */
614        if (ex_run_str(sp, NULL, p, len, 0, 0))
615                return (1);
616
617        /* Set up the switch. */
618        sp->nextdisp = sp->ccl_parent;
619        F_SET(sp, SC_EXIT);
620        return (0);
621}
622
623/*
624 * v_ecl_log --
625 *      Log a command into the colon command-line log file.
626 */
627static int
628v_ecl_log(sp, tp)
629        SCR *sp;
630        TEXT *tp;
631{
632        EXF *save_ep;
633        recno_t lno;
634        int rval;
635
636        /* Initialize the screen, if necessary. */
637        if (sp->gp->ccl_sp == NULL && v_ecl_init(sp))
638                return (1);
639
640        /*
641         * Don't log colon command window commands into the colon command
642         * window...
643         */
644        if (sp->ep == sp->gp->ccl_sp->ep)
645                return (0);
646
647        /*
648         * XXX
649         * Swap the current EXF with the colon command file EXF.  This
650         * isn't pretty, but too many routines "know" that sp->ep points
651         * to the current EXF.
652         */
653        save_ep = sp->ep;
654        sp->ep = sp->gp->ccl_sp->ep;
655        if (db_last(sp, &lno)) {
656                sp->ep = save_ep;
657                return (1);
658        }
659        rval = db_append(sp, 0, lno, tp->lb, tp->len);
660        sp->ep = save_ep;
661        return (rval);
662}
663
664/*
665 * v_ecl_init --
666 *      Initialize the colon command-line log file.
667 */
668static int
669v_ecl_init(sp)
670        SCR *sp;
671{
672        FREF *frp;
673        GS *gp;
674
675        gp = sp->gp;
676
677        /* Get a temporary file. */
678        if ((frp = file_add(sp, NULL)) == NULL)
679                return (1);
680
681        /*
682         * XXX
683         * Create a screen -- the file initialization code wants one.
684         */
685        if (screen_init(gp, sp, &gp->ccl_sp))
686                return (1);
687        if (file_init(gp->ccl_sp, frp, NULL, 0)) {
688                (void)screen_end(gp->ccl_sp);
689                return (1);
690        }
691
692        /* The underlying file isn't recoverable. */
693        F_CLR(gp->ccl_sp->ep, F_RCV_ON);
694
695        return (0);
696}
Note: See TracBrowser for help on using the repository browser.