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