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