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