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