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