1b8ba871bSPeter Wemm /*-
2b8ba871bSPeter Wemm * Copyright (c) 1992, 1993, 1994
3b8ba871bSPeter Wemm * The Regents of the University of California. All rights reserved.
4b8ba871bSPeter Wemm * Copyright (c) 1992, 1993, 1994, 1995, 1996
5b8ba871bSPeter Wemm * Keith Bostic. All rights reserved.
6b8ba871bSPeter Wemm *
7b8ba871bSPeter Wemm * See the LICENSE file for redistribution information.
8b8ba871bSPeter Wemm */
9b8ba871bSPeter Wemm
10b8ba871bSPeter Wemm #include "config.h"
11b8ba871bSPeter Wemm
12b8ba871bSPeter Wemm #include <sys/types.h>
13b8ba871bSPeter Wemm #include <sys/queue.h>
14b8ba871bSPeter Wemm #include <sys/time.h>
15b8ba871bSPeter Wemm
16b8ba871bSPeter Wemm #include <bitstring.h>
17b8ba871bSPeter Wemm #include <errno.h>
18b8ba871bSPeter Wemm #include <limits.h>
19b8ba871bSPeter Wemm #include <stdio.h>
20b8ba871bSPeter Wemm #include <stdlib.h>
21b8ba871bSPeter Wemm #include <string.h>
22b8ba871bSPeter Wemm #include <unistd.h>
23b8ba871bSPeter Wemm
24b8ba871bSPeter Wemm #include "../common/common.h"
25b8ba871bSPeter Wemm #include "../vi/vi.h"
26b8ba871bSPeter Wemm
27b8ba871bSPeter Wemm /*
28b8ba871bSPeter Wemm * ex_bang -- :[line [,line]] ! command
29b8ba871bSPeter Wemm *
30b8ba871bSPeter Wemm * Pass the rest of the line after the ! character to the program named by
31b8ba871bSPeter Wemm * the O_SHELL option.
32b8ba871bSPeter Wemm *
33b8ba871bSPeter Wemm * Historical vi did NOT do shell expansion on the arguments before passing
34b8ba871bSPeter Wemm * them, only file name expansion. This means that the O_SHELL program got
35b8ba871bSPeter Wemm * "$t" as an argument if that is what the user entered. Also, there's a
36b8ba871bSPeter Wemm * special expansion done for the bang command. Any exclamation points in
37b8ba871bSPeter Wemm * the user's argument are replaced by the last, expanded ! command.
38b8ba871bSPeter Wemm *
39b8ba871bSPeter Wemm * There's some fairly amazing slop in this routine to make the different
40b8ba871bSPeter Wemm * ways of getting here display the right things. It took a long time to
41b8ba871bSPeter Wemm * get it right (wrong?), so be careful.
42b8ba871bSPeter Wemm *
43c271fa92SBaptiste Daroussin * PUBLIC: int ex_bang(SCR *, EXCMD *);
44b8ba871bSPeter Wemm */
45b8ba871bSPeter Wemm int
ex_bang(SCR * sp,EXCMD * cmdp)46f0957ccaSPeter Wemm ex_bang(SCR *sp, EXCMD *cmdp)
47b8ba871bSPeter Wemm {
48b8ba871bSPeter Wemm enum filtertype ftype;
49b8ba871bSPeter Wemm ARGS *ap;
50b8ba871bSPeter Wemm EX_PRIVATE *exp;
51b8ba871bSPeter Wemm MARK rm;
52b8ba871bSPeter Wemm recno_t lno;
53b8ba871bSPeter Wemm int rval;
54b8ba871bSPeter Wemm const char *msg;
55f0957ccaSPeter Wemm char *np;
56f0957ccaSPeter Wemm size_t nlen;
57b8ba871bSPeter Wemm
58b8ba871bSPeter Wemm ap = cmdp->argv[0];
59b8ba871bSPeter Wemm if (ap->len == 0) {
60b8ba871bSPeter Wemm ex_emsg(sp, cmdp->cmd->usage, EXM_USAGE);
61b8ba871bSPeter Wemm return (1);
62b8ba871bSPeter Wemm }
63b8ba871bSPeter Wemm
64b8ba871bSPeter Wemm /* Set the "last bang command" remembered value. */
65b8ba871bSPeter Wemm exp = EXP(sp);
66b8ba871bSPeter Wemm free(exp->lastbcomm);
67f0957ccaSPeter Wemm if ((exp->lastbcomm = v_wstrdup(sp, ap->bp, ap->len)) == NULL) {
68b8ba871bSPeter Wemm msgq(sp, M_SYSERR, NULL);
69b8ba871bSPeter Wemm return (1);
70b8ba871bSPeter Wemm }
71b8ba871bSPeter Wemm
72b8ba871bSPeter Wemm /*
73b8ba871bSPeter Wemm * If the command was modified by the expansion, it was historically
74b8ba871bSPeter Wemm * redisplayed.
75b8ba871bSPeter Wemm */
76b8ba871bSPeter Wemm if (F_ISSET(cmdp, E_MODIFY) && !F_ISSET(sp, SC_EX_SILENT)) {
77b8ba871bSPeter Wemm /*
78b8ba871bSPeter Wemm * Display the command if modified. Historic ex/vi displayed
79b8ba871bSPeter Wemm * the command if it was modified due to file name and/or bang
80b8ba871bSPeter Wemm * expansion. If piping lines in vi, it would be immediately
81b8ba871bSPeter Wemm * overwritten by any error or line change reporting.
82b8ba871bSPeter Wemm */
83b8ba871bSPeter Wemm if (F_ISSET(sp, SC_VI))
84b8ba871bSPeter Wemm vs_update(sp, "!", ap->bp);
85b8ba871bSPeter Wemm else {
86f0957ccaSPeter Wemm (void)ex_printf(sp, "!"WS"\n", ap->bp);
87b8ba871bSPeter Wemm (void)ex_fflush(sp);
88b8ba871bSPeter Wemm }
89b8ba871bSPeter Wemm }
90b8ba871bSPeter Wemm
91b8ba871bSPeter Wemm /*
92b8ba871bSPeter Wemm * If no addresses were specified, run the command. If there's an
93b8ba871bSPeter Wemm * underlying file, it's been modified and autowrite is set, write
94b8ba871bSPeter Wemm * the file back. If the file has been modified, autowrite is not
95b8ba871bSPeter Wemm * set and the warn option is set, tell the user about the file.
96b8ba871bSPeter Wemm */
97b8ba871bSPeter Wemm if (cmdp->addrcnt == 0) {
98b8ba871bSPeter Wemm msg = NULL;
99*755cc40cSBaptiste Daroussin if (sp->ep != NULL && F_ISSET(sp->ep, F_MODIFIED)) {
100b8ba871bSPeter Wemm if (O_ISSET(sp, O_AUTOWRITE)) {
101b8ba871bSPeter Wemm if (file_aw(sp, FS_ALL))
102b8ba871bSPeter Wemm return (0);
103b8ba871bSPeter Wemm } else if (O_ISSET(sp, O_WARN) &&
104b8ba871bSPeter Wemm !F_ISSET(sp, SC_EX_SILENT))
105b8ba871bSPeter Wemm msg = msg_cat(sp,
106b8ba871bSPeter Wemm "303|File modified since last write.",
107b8ba871bSPeter Wemm NULL);
108*755cc40cSBaptiste Daroussin }
109b8ba871bSPeter Wemm
110b8ba871bSPeter Wemm /* If we're still in a vi screen, move out explicitly. */
111f0957ccaSPeter Wemm INT2CHAR(sp, ap->bp, ap->len+1, np, nlen);
112b8ba871bSPeter Wemm (void)ex_exec_proc(sp,
113f0957ccaSPeter Wemm cmdp, np, msg, !F_ISSET(sp, SC_EX | SC_SCR_EXWROTE));
114b8ba871bSPeter Wemm }
115b8ba871bSPeter Wemm
116b8ba871bSPeter Wemm /*
117b8ba871bSPeter Wemm * If addresses were specified, pipe lines from the file through the
118b8ba871bSPeter Wemm * command.
119b8ba871bSPeter Wemm *
120b8ba871bSPeter Wemm * Historically, vi lines were replaced by both the stdout and stderr
121b8ba871bSPeter Wemm * lines of the command, but ex lines by only the stdout lines. This
122b8ba871bSPeter Wemm * makes no sense to me, so nvi makes it consistent for both, and
123b8ba871bSPeter Wemm * matches vi's historic behavior.
124b8ba871bSPeter Wemm */
125b8ba871bSPeter Wemm else {
126b8ba871bSPeter Wemm NEEDFILE(sp, cmdp);
127b8ba871bSPeter Wemm
128b8ba871bSPeter Wemm /* Autoprint is set historically, even if the command fails. */
129b8ba871bSPeter Wemm F_SET(cmdp, E_AUTOPRINT);
130b8ba871bSPeter Wemm
131b8ba871bSPeter Wemm /*
132b8ba871bSPeter Wemm * !!!
133b8ba871bSPeter Wemm * Historical vi permitted "!!" in an empty file. When this
134b8ba871bSPeter Wemm * happens, we arrive here with two addresses of 1,1 and a
135b8ba871bSPeter Wemm * bad attitude. The simple solution is to turn it into a
136b8ba871bSPeter Wemm * FILTER_READ operation, with the exception that stdin isn't
137b8ba871bSPeter Wemm * opened for the utility, and the cursor position isn't the
138b8ba871bSPeter Wemm * same. The only historic glitch (I think) is that we don't
139b8ba871bSPeter Wemm * put an empty line into the default cut buffer, as historic
140b8ba871bSPeter Wemm * vi did. Imagine, if you can, my disappointment.
141b8ba871bSPeter Wemm */
142b8ba871bSPeter Wemm ftype = FILTER_BANG;
143b8ba871bSPeter Wemm if (cmdp->addr1.lno == 1 && cmdp->addr2.lno == 1) {
144b8ba871bSPeter Wemm if (db_last(sp, &lno))
145b8ba871bSPeter Wemm return (1);
146b8ba871bSPeter Wemm if (lno == 0) {
147b8ba871bSPeter Wemm cmdp->addr1.lno = cmdp->addr2.lno = 0;
148b8ba871bSPeter Wemm ftype = FILTER_RBANG;
149b8ba871bSPeter Wemm }
150b8ba871bSPeter Wemm }
151b8ba871bSPeter Wemm rval = ex_filter(sp, cmdp,
152b8ba871bSPeter Wemm &cmdp->addr1, &cmdp->addr2, &rm, ap->bp, ftype);
153b8ba871bSPeter Wemm
154b8ba871bSPeter Wemm /*
155b8ba871bSPeter Wemm * If in vi mode, move to the first nonblank.
156b8ba871bSPeter Wemm *
157b8ba871bSPeter Wemm * !!!
158b8ba871bSPeter Wemm * Historic vi wasn't consistent in this area -- if you used
159b8ba871bSPeter Wemm * a forward motion it moved to the first nonblank, but if you
160b8ba871bSPeter Wemm * did a backward motion it didn't. And, if you followed a
161b8ba871bSPeter Wemm * backward motion with a forward motion, it wouldn't move to
162b8ba871bSPeter Wemm * the nonblank for either. Going to the nonblank generally
163b8ba871bSPeter Wemm * seems more useful and consistent, so we do it.
164b8ba871bSPeter Wemm */
165b8ba871bSPeter Wemm sp->lno = rm.lno;
166b8ba871bSPeter Wemm if (F_ISSET(sp, SC_VI)) {
167b8ba871bSPeter Wemm sp->cno = 0;
168b8ba871bSPeter Wemm (void)nonblank(sp, sp->lno, &sp->cno);
169b8ba871bSPeter Wemm } else
170b8ba871bSPeter Wemm sp->cno = rm.cno;
171b8ba871bSPeter Wemm }
172b8ba871bSPeter Wemm
173b8ba871bSPeter Wemm /* Ex terminates with a bang, even if the command fails. */
174b8ba871bSPeter Wemm if (!F_ISSET(sp, SC_VI) && !F_ISSET(sp, SC_EX_SILENT))
175b8ba871bSPeter Wemm (void)ex_puts(sp, "!\n");
176b8ba871bSPeter Wemm
177110d525eSBaptiste Daroussin /* Apply expandtab to the new text */
178110d525eSBaptiste Daroussin if (O_ISSET(sp, O_EXPANDTAB))
179110d525eSBaptiste Daroussin ex_retab(sp, cmdp);
180110d525eSBaptiste Daroussin
181b8ba871bSPeter Wemm /*
182b8ba871bSPeter Wemm * XXX
183b8ba871bSPeter Wemm * The ! commands never return an error, so that autoprint always
184b8ba871bSPeter Wemm * happens in the ex parser.
185b8ba871bSPeter Wemm */
186b8ba871bSPeter Wemm return (0);
187b8ba871bSPeter Wemm }
188