xref: /titanic_51/usr/src/cmd/vi/port/ex_unix.c (revision 8eea8e29cc4374d1ee24c25a07f45af132db3499)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 1995 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 
31 /* Copyright (c) 1979 Regents of the University of California */
32 
33 #pragma ident	"%Z%%M%	%I%	%E% SMI"
34 
35 #include "ex.h"
36 #include "ex_temp.h"
37 #include "ex_tty.h"
38 #include "ex_vis.h"
39 
40 extern char	getchar();
41 /*
42  * Unix escapes, filtering
43  */
44 
45 /*
46  * First part of a shell escape,
47  * parse the line, expanding # and % and ! and printing if implied.
48  */
49 unix0(warn)
50 	bool warn;
51 {
52 	register unsigned char *up, *fp;
53 	register short c;
54 	char	multic[MB_LEN_MAX + 1];
55 	int	len;
56 	wchar_t	wc;
57 	unsigned char printub, puxb[UXBSIZE + sizeof (int)];
58 
59 	printub = 0;
60 	CP(puxb, uxb);
61 	c = peekchar();
62 	if (c == '\n' || c == EOF) {
63 		(void) getchar();
64 		error(value(vi_TERSE) ?
65 gettext("Incomplete shell escape command") :
66 gettext("Incomplete shell escape command - use 'shell' to get a shell"));
67 	}
68 	up = (unsigned char *)uxb;
69 
70 	for (;;) {
71 		if (!isascii(c)) {
72 			if (c == EOF)
73 				break;
74 			if ((len = _mbftowc(multic, &wc, getchar, &peekc)) > 0) {
75 				if ((up + len) >= (unsigned char *)&uxb[UXBSIZE]) {
76 					uxb[0] = 0;
77 					error(gettext("Command too long"));
78 				}
79 				strncpy(up, multic, len);
80 				up += len;
81 				goto loop_check;
82 			}
83 		}
84 
85 		(void) getchar();
86 		switch (c) {
87 
88 		case '\\':
89 			if (any(peekchar(), "%#!"))
90 				c = getchar();
91 		default:
92 			if (up >= (unsigned char *)&uxb[UXBSIZE]) {
93 tunix:
94 				uxb[0] = 0;
95 				error(gettext("Command too long"));
96 			}
97 			*up++ = c;
98 			break;
99 
100 		case '!':
101 			if (up != (unsigned char *)uxb && *puxb != 0) {
102 				fp = puxb;
103 				if (*fp == 0) {
104 					uxb[0] = 0;
105 					error(value(vi_TERSE) ?
106 gettext("No previous command") :
107 gettext("No previous command to substitute for !"));
108 				}
109 				printub++;
110 				while (*fp) {
111 					if (up >= (unsigned char *)&uxb[UXBSIZE])
112 						goto tunix;
113 					*up++ = *fp++;
114 				}
115 			} else if (up == (unsigned char *)uxb) {
116 				/* If up = uxb it means we are on the first
117 				 * character inside the shell command.
118 				 * (i.e., after the ":!")
119 				 *
120 				 * The user has just entered ":!!" which
121 				 * means that though there is only technically
122 				 * one '!' we know he really meant ":!!!". So
123 				 * substitute the last command for him.
124 				 */
125 				fp = puxb;
126 				if (*fp == 0) {
127 					uxb[0] = 0;
128 					error(value(vi_TERSE) ?
129 gettext("No previous command") :
130 gettext("No previous command to substitute for !"));
131 				}
132 				printub++;
133 				while (*fp) {
134 					if (up >= (unsigned char *)&uxb[UXBSIZE])
135 						goto tunix;
136 					*up++ = *fp++;
137 				}
138 			} else {
139 				/*
140 				 * Treat a lone "!" as just a regular character
141 				 * so commands like "mail machine!login" will
142 				 * work as usual (i.e., the user doesn't need
143 				 * to dereference the "!" with "\!").
144 				 */
145 				if (up >= (unsigned char *)&uxb[UXBSIZE]) {
146 					uxb[0] = 0;
147 					error(gettext("Command too long"));
148 				}
149 				*up++ = c;
150 			}
151 			break;
152 
153 		case '#':
154 			fp = (unsigned char *)altfile;
155 			if (*fp == 0) {
156 				uxb[0] = 0;
157 				error(value(vi_TERSE) ?
158 gettext("No alternate filename") :
159 gettext("No alternate filename to substitute for #"));
160 			}
161 			goto uexp;
162 
163 		case '%':
164 			fp = savedfile;
165 			if (*fp == 0) {
166 				uxb[0] = 0;
167 				error(value(vi_TERSE) ?
168 gettext("No filename") :
169 gettext("No filename to substitute for %%"));
170 			}
171 uexp:
172 			printub++;
173 			while (*fp) {
174 				if (up >= (unsigned char *)&uxb[UXBSIZE])
175 					goto tunix;
176 				*up++ = *fp++;
177 			}
178 			break;
179 		}
180 
181 loop_check:
182 		c = peekchar();
183 		if (c == '"' || c == '|' || !endcmd(c)) {
184 			continue;
185 		} else {
186 			(void) getchar();
187 			break;
188 		}
189 	}
190 	if (c == EOF)
191 		ungetchar(c);
192 	*up = 0;
193 	if (!inopen)
194 		resetflav();
195 	if (warn)
196 		ckaw();
197 	if (warn && hush == 0 && chng && xchng != chng && value(vi_WARN) && dol > zero) {
198 		xchng = chng;
199 		vnfl();
200 		printf(mesg(value(vi_TERSE) ? gettext("[No write]") :
201 gettext("[No write since last change]")));
202 		noonl();
203 		flush();
204 	} else
205 		warn = 0;
206 	if (printub) {
207 		if (uxb[0] == 0)
208 			error(value(vi_TERSE) ? gettext("No previous command") :
209 gettext("No previous command to repeat"));
210 		if (inopen) {
211 			splitw++;
212 			vclean();
213 			vgoto(WECHO, 0);
214 		}
215 		if (warn)
216 			vnfl();
217 		if (hush == 0)
218 			lprintf("!%s", uxb);
219 		if (inopen && Outchar != termchar) {
220 			vclreol();
221 			vgoto(WECHO, 0);
222 		} else
223 			putnl();
224 		flush();
225 	}
226 }
227 
228 /*
229  * Do the real work for execution of a shell escape.
230  * Mode is like the number passed to open system calls
231  * and indicates filtering.  If input is implied, newstdin
232  * must have been setup already.
233  */
234 ttymode
235 unixex(opt, up, newstdin, mode)
236 	unsigned char *opt, *up;
237 	int newstdin, mode;
238 {
239 	int pvec[2];
240 	ttymode f;
241 
242 	signal(SIGINT, SIG_IGN);
243 #ifdef SIGTSTP
244 	if (dosusp)
245 		signal(SIGTSTP, SIG_DFL);
246 #endif
247 	if (inopen)
248 		f = setty(normf);
249 	if ((mode & 1) && pipe(pvec) < 0) {
250 		/* Newstdin should be io so it will be closed */
251 		if (inopen)
252 			setty(f);
253 		error(gettext("Can't make pipe for filter"));
254 	}
255 #ifndef VFORK
256 	pid = fork();
257 #else
258 	pid = vfork();
259 #endif
260 	if (pid < 0) {
261 		if (mode & 1) {
262 			close(pvec[0]);
263 			close(pvec[1]);
264 		}
265 		setrupt();
266 		if (inopen)
267 			setty(f);
268 		error(gettext("No more processes"));
269 	}
270 	if (pid == 0) {
271 		if (mode & 2) {
272 			close(0);
273 			dup(newstdin);
274 			close(newstdin);
275 		}
276 		if (mode & 1) {
277 			close(pvec[0]);
278 			close(1);
279 			dup(pvec[1]);
280 			if (inopen) {
281 				close(2);
282 				dup(1);
283 			}
284 			close(pvec[1]);
285 		}
286 		if (io)
287 			close(io);
288 		if (tfile)
289 			close(tfile);
290 		signal(SIGHUP, oldhup);
291 		signal(SIGQUIT, oldquit);
292 		if (ruptible)
293 			signal(SIGINT, SIG_DFL);
294 	 	execlp(svalue(vi_SHELL), svalue(vi_SHELL), opt, up, (char *) 0);
295 		printf(gettext("Invalid SHELL value: %s\n"), svalue(vi_SHELL));
296 		flush();
297 		error(NOSTR);
298 	}
299 	if (mode & 1) {
300 		io = pvec[0];
301 		close(pvec[1]);
302 	}
303 	if (newstdin)
304 		close(newstdin);
305 	return (f);
306 }
307 
308 /*
309  * Wait for the command to complete.
310  * F is for restoration of tty mode if from open/visual.
311  * C flags suppression of printing.
312  */
313 unixwt(c, f)
314 	bool c;
315 	ttymode f;
316 {
317 
318 	waitfor();
319 #ifdef SIGTSTP
320 	if (dosusp)
321 		signal(SIGTSTP, onsusp);
322 #endif
323 	if (inopen)
324 		setty(f);
325 	setrupt();
326 	if (!inopen && c && hush == 0) {
327 		printf("!\n");
328 		flush();
329 		termreset();
330 		gettmode();
331 	}
332 }
333 
334 /*
335  * Setup a pipeline for the filtration implied by mode
336  * which is like a open number.  If input is required to
337  * the filter, then a child editor is created to write it.
338  * If output is catch it from io which is created by unixex.
339  */
340 vi_filter(mode)
341 	register int mode;
342 {
343 	static int pvec[2];
344 	ttymode f;	/* was register */
345 	register int nlines = lineDOL();
346 	int status2;
347 	pid_t pid2 = 0;
348 
349 	mode++;
350 	if (mode & 2) {
351 		signal(SIGINT, SIG_IGN);
352 		signal(SIGPIPE, SIG_IGN);
353 		if (pipe(pvec) < 0)
354 			error(gettext("Can't make pipe"));
355 		pid2 = fork();
356 		io = pvec[0];
357 		if (pid < 0) {
358 			setrupt();
359 			close(pvec[1]);
360 			error(gettext("No more processes"));
361 		}
362 		if (pid2 == 0) {
363 			extern unsigned char tfname[];
364 			setrupt();
365 			io = pvec[1];
366 			close(pvec[0]);
367 
368 			/* To prevent seeking in this process and the
369 				 parent, we must reopen tfile here */
370 			close(tfile);
371 			tfile = open(tfname, 2);
372 
373 			putfile(1);
374 			exit(errcnt);
375 		}
376 		close(pvec[1]);
377 		io = pvec[0];
378 		setrupt();
379 	}
380 	f = unixex("-c", uxb, (mode & 2) ? pvec[0] : 0, mode);
381 	if (mode == 3) {
382 		delete(0);
383 		addr2 = addr1 - 1;
384 	}
385 	if (mode == 1)
386 		deletenone();
387 	if (mode & 1) {
388 		if(FIXUNDO)
389 			undap1 = undap2 = addr2+1;
390 		(void)append(getfile, addr2);
391 #ifdef UNDOTRACE
392 		if (trace)
393 			vudump(gettext("after append in filter"));
394 #endif
395 	}
396 	close(io);
397 	io = -1;
398 	unixwt(!inopen, f);
399 	if (pid2) {
400 		(void)kill(pid2, 9);
401 		do
402 			rpid = waitpid(pid2, &status2, 0);
403 		while (rpid == (pid_t)-1 && errno == EINTR);
404 	}
405 	netchHAD(nlines);
406 }
407 
408 /*
409  * Set up to do a recover, getting io to be a pipe from
410  * the recover process.
411  */
412 recover()
413 {
414 	static int pvec[2];
415 
416 	if (pipe(pvec) < 0)
417 		error(gettext(" Can't make pipe for recovery"));
418 	pid = fork();
419 	io = pvec[0];
420 	if (pid < 0) {
421 		close(pvec[1]);
422 		error(gettext(" Can't fork to execute recovery"));
423 	}
424 	if (pid == 0) {
425 		unsigned char cryptkey[19];
426 		close(2);
427 		dup(1);
428 		close(1);
429 		dup(pvec[1]);
430 	        close(pvec[1]);
431 		if(xflag) {
432 			strcpy(cryptkey, "CrYpTkEy=XXXXXXXXX");
433 			strcpy(cryptkey + 9, key);
434 			if(putenv((char *)cryptkey) != 0)
435 				smerror(gettext(" Cannot copy key to environment"));
436 			execlp(EXRECOVER, "exrecover", "-x", svalue(vi_DIRECTORY), file, (char *) 0);
437 		} else
438 			execlp(EXRECOVER, "exrecover", svalue(vi_DIRECTORY), file, (char *) 0);
439 		close(1);
440 		dup(2);
441 		error(gettext(" No recovery routine"));
442 	}
443 	close(pvec[1]);
444 }
445 
446 /*
447  * Wait for the process (pid an external) to complete.
448  */
449 waitfor()
450 {
451 
452 	do
453 		rpid = waitpid(pid, &status, 0);
454 	while (rpid == (pid_t)-1 && errno != ECHILD);
455 	if ((status & 0377) == 0)
456 		status = (status >> 8) & 0377;
457 	else {
458 		/*
459 		 * TRANSLATION_NOTE
460 		 *	Reference order of arguments must not
461 		 *	be changed using '%digit$', since vi's
462 		 *	printf() does not support it.
463 		 */
464 		printf(gettext("%d: terminated with signal %d"), pid, status & 0177);
465 		if (status & 0200)
466 			printf(gettext(" -- core dumped"));
467 		putchar('\n');
468 	}
469 }
470 
471 /*
472  * The end of a recover operation.  If the process
473  * exits non-zero, force not edited; otherwise force
474  * a write.
475  */
476 revocer()
477 {
478 
479 	waitfor();
480 	if (pid == rpid && status != 0)
481 		edited = 0;
482 	else
483 		change();
484 }
485