xref: /original-bsd/usr.bin/ex/ex_unix.c (revision 3705696b)
1 /*-
2  * Copyright (c) 1980, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * %sccs.include.proprietary.c%
6  */
7 
8 #ifndef lint
9 static char sccsid[] = "@(#)ex_unix.c	8.1 (Berkeley) 06/09/93";
10 #endif /* not lint */
11 
12 #include "ex.h"
13 #include "ex_temp.h"
14 #include "ex_tty.h"
15 #include "ex_vis.h"
16 #include <sys/wait.h>
17 
18 /*
19  * Unix escapes, filtering
20  */
21 
22 /*
23  * First part of a shell escape,
24  * parse the line, expanding # and % and ! and printing if implied.
25  */
26 unix0(warn)
27 	bool warn;
28 {
29 	register char *up, *fp;
30 	register short c;
31 	char printub, puxb[UXBSIZE + sizeof (int)];
32 
33 	printub = 0;
34 	CP(puxb, uxb);
35 	c = ex_getchar();
36 	if (c == '\n' || c == EOF)
37 		error("Incomplete shell escape command@- use 'shell' to get a shell");
38 	up = uxb;
39 	do {
40 		switch (c) {
41 
42 		case '\\':
43 			if (any(peekchar(), "%#!"))
44 				c = ex_getchar();
45 		default:
46 			if (up >= &uxb[UXBSIZE]) {
47 tunix:
48 				uxb[0] = 0;
49 				error("Command too long");
50 			}
51 			*up++ = c;
52 			break;
53 
54 		case '!':
55 			fp = puxb;
56 			if (*fp == 0) {
57 				uxb[0] = 0;
58 				error("No previous command@to substitute for !");
59 			}
60 			printub++;
61 			while (*fp) {
62 				if (up >= &uxb[UXBSIZE])
63 					goto tunix;
64 				*up++ = *fp++;
65 			}
66 			break;
67 
68 		case '#':
69 			fp = altfile;
70 			if (*fp == 0) {
71 				uxb[0] = 0;
72 				error("No alternate filename@to substitute for #");
73 			}
74 			goto uexp;
75 
76 		case '%':
77 			fp = savedfile;
78 			if (*fp == 0) {
79 				uxb[0] = 0;
80 				error("No filename@to substitute for %%");
81 			}
82 uexp:
83 			printub++;
84 			while (*fp) {
85 				if (up >= &uxb[UXBSIZE])
86 					goto tunix;
87 				*up++ = *fp++ | QUOTE;
88 			}
89 			break;
90 		}
91 		c = ex_getchar();
92 	} while (c == '"' || c == '|' || !endcmd(c));
93 	if (c == EOF)
94 		ungetchar(c);
95 	*up = 0;
96 	if (!inopen)
97 		resetflav();
98 	if (warn)
99 		ckaw();
100 	if (warn && hush == 0 && chng && xchng != chng && value(WARN) && dol > zero) {
101 		xchng = chng;
102 		vnfl();
103 		ex_printf(mesg("[No write]|[No write since last change]"));
104 		noonl();
105 		flush();
106 	} else
107 		warn = 0;
108 	if (printub) {
109 		if (uxb[0] == 0)
110 			error("No previous command@to repeat");
111 		if (inopen) {
112 			splitw++;
113 			vclean();
114 			vgoto(WECHO, 0);
115 		}
116 		if (warn)
117 			vnfl();
118 		if (hush == 0)
119 			lprintf("!%s", uxb);
120 		if (inopen && Outchar != termchar) {
121 			vclreol();
122 			vgoto(WECHO, 0);
123 		} else
124 			putnl();
125 		flush();
126 	}
127 }
128 
129 /*
130  * Do the real work for execution of a shell escape.
131  * Mode is like the number passed to open system calls
132  * and indicates filtering.  If input is implied, newstdin
133  * must have been setup already.
134  */
135 ttymode
136 unixex(opt, up, newstdin, mode)
137 	char *opt, *up;
138 	int newstdin, mode;
139 {
140 	int pvec[2];
141 	ttymode f;
142 
143 	signal(SIGINT, SIG_IGN);
144 #ifdef SIGTSTP
145 	if (dosusp)
146 		signal(SIGTSTP, SIG_DFL);
147 #endif
148 	if (inopen)
149 		f = setty(normf);
150 	if ((mode & 1) && pipe(pvec) < 0) {
151 		/* Newstdin should be io so it will be closed */
152 		if (inopen)
153 			ignore(setty(f));
154 		error("Can't make pipe for filter");
155 	}
156 #ifndef VFORK
157 	pid = fork();
158 #else
159 	pid = vfork();
160 #endif
161 	if (pid < 0) {
162 		if (mode & 1) {
163 			close(pvec[0]);
164 			close(pvec[1]);
165 		}
166 		setrupt();
167 		error("No more processes");
168 	}
169 	if (pid == 0) {
170 		if (up) {
171 			register char *cp = up;
172 			while (*cp)
173 				*cp++ &= TRIM;
174 		}
175 		if (mode & 2) {
176 			close(0);
177 			dup(newstdin);
178 			close(newstdin);
179 		}
180 		if (mode & 1) {
181 			close(pvec[0]);
182 			close(1);
183 			dup(pvec[1]);
184 			if (inopen) {
185 				close(2);
186 				dup(1);
187 			}
188 			close(pvec[1]);
189 		}
190 		if (io)
191 			close(io);
192 		if (tfile)
193 			close(tfile);
194 #ifdef EXSTRINGS
195 		close(erfile);
196 #endif
197 		signal(SIGHUP, oldhup);
198 		signal(SIGQUIT, oldquit);
199 		if (ruptible)
200 			signal(SIGINT, SIG_DFL);
201 		execl(svalue(SHELL), "sh", opt, up, (char *) 0);
202 		ex_printf("No %s!\n", svalue(SHELL));
203 		error(NOSTR);
204 	}
205 	if (mode & 1) {
206 		io = pvec[0];
207 		close(pvec[1]);
208 	}
209 	if (newstdin)
210 		close(newstdin);
211 	return (f);
212 }
213 
214 /*
215  * Wait for the command to complete.
216  * F is for restoration of tty mode if from open/visual.
217  * C flags suppression of printing.
218  */
219 unixwt(c, f)
220 	bool c;
221 	ttymode f;
222 {
223 
224 	waitfor();
225 #ifdef SIGTSTP
226 	if (dosusp)
227 		signal(SIGTSTP, onsusp);
228 #endif
229 	if (inopen)
230 		ignore(setty(f));
231 	setrupt();
232 	if (!inopen && c && hush == 0) {
233 		ex_printf("!\n");
234 		flush();
235 		termreset();
236 		gettmode();
237 	}
238 }
239 
240 /*
241  * Setup a pipeline for the filtration implied by mode
242  * which is like a open number.  If input is required to
243  * the filter, then a child editor is created to write it.
244  * If output is catch it from io which is created by unixex.
245  */
246 filter(mode)
247 	register int mode;
248 {
249 	static int pvec[2];
250 	ttymode f;	/* mjm: was register */
251 	register int lines = lineDOL();
252 	struct stat statb;
253 
254 	mode++;
255 	if (mode & 2) {
256 		signal(SIGINT, SIG_IGN);
257 		if (pipe(pvec) < 0)
258 			error("Can't make pipe");
259 		pid = fork();
260 		io = pvec[0];
261 		if (pid < 0) {
262 			setrupt();
263 			close(pvec[1]);
264 			error("No more processes");
265 		}
266 		if (pid == 0) {
267 			setrupt();
268 			io = pvec[1];
269 			close(pvec[0]);
270 			putfile(1);
271 			ex_exit(0);
272 		}
273 		close(pvec[1]);
274 		io = pvec[0];
275 		setrupt();
276 	}
277 	f = unixex("-c", uxb, (mode & 2) ? pvec[0] : 0, mode);
278 	if (mode == 3) {
279 		ex_delete(0);
280 		addr2 = addr1 - 1;
281 	}
282 	if (mode & 1) {
283 		if(FIXUNDO)
284 			undap1 = undap2 = addr2+1;
285 		if (fstat(io, &statb) < 0)
286 			bsize = LBSIZE;
287 		else {
288 			bsize = statb.st_blksize;
289 			if (bsize <= 0)
290 				bsize = LBSIZE;
291 		}
292 		ignore(append(getfile, addr2));
293 #ifdef TRACE
294 		if (trace)
295 			vudump("after append in filter");
296 #endif
297 	}
298 	close(io);
299 	io = -1;
300 	unixwt(!inopen, f);
301 	netchHAD(lines);
302 }
303 
304 /*
305  * Set up to do a recover, getting io to be a pipe from
306  * the recover process.
307  */
308 recover()
309 {
310 	static int pvec[2];
311 
312 	if (pipe(pvec) < 0)
313 		error(" Can't make pipe for recovery");
314 	pid = fork();
315 	io = pvec[0];
316 	if (pid < 0) {
317 		close(pvec[1]);
318 		error(" Can't fork to execute recovery");
319 	}
320 	if (pid == 0) {
321 		close(2);
322 		dup(1);
323 		close(1);
324 		dup(pvec[1]);
325 	        close(pvec[1]);
326 		execl(_PATH_EXRECOVER, "exrecover", svalue(DIRECTORY),
327 		    file, (char *) 0);
328 		close(1);
329 		dup(2);
330 		error(" No recovery routine");
331 	}
332 	close(pvec[1]);
333 }
334 
335 /*
336  * Wait for the process (pid an external) to complete.
337  */
338 waitfor()
339 {
340 	union wait stat, pstat;
341 	int wpid;
342 
343 	pstat.w_status = 0;
344 	do {
345 		wpid = wait((int *)&stat);
346 		if (wpid == pid) {
347 			pstat = stat;
348 			rpid = wpid;
349 		}
350 	} while (wpid != -1);
351 
352 	if (WIFEXITED(pstat))
353 		status = pstat.w_retcode;
354 	else {
355 		ex_printf("%d: terminated abnormally: %s ",
356 		    pid, sys_siglist[pstat.w_termsig]);
357 		if (pstat.w_coredump)
358 			ex_printf("(core dumped) ");
359 		if (!inopen)
360 			ex_printf("\r\n");
361 		status = pstat.w_termsig;
362 	}
363 }
364 
365 /*
366  * The end of a recover operation.  If the process
367  * exits non-zero, force not edited; otherwise force
368  * a write.
369  */
370 revocer()
371 {
372 
373 	waitfor();
374 	if (pid == rpid && status != 0)
375 		edited = 0;
376 	else
377 		change();
378 }
379