119304Speter/*-
219304Speter * Copyright (c) 1992, 1993, 1994
319304Speter *	The Regents of the University of California.  All rights reserved.
419304Speter * Copyright (c) 1992, 1993, 1994, 1995, 1996
519304Speter *	Keith Bostic.  All rights reserved.
619304Speter *
719304Speter * See the LICENSE file for redistribution information.
819304Speter */
919304Speter
1019304Speter#include "config.h"
1119304Speter
1219304Speter#ifndef lint
13254225Speterstatic const char sccsid[] = "$Id: ex_shell.c,v 10.44 2012/07/06 06:51:26 zy Exp $";
1419304Speter#endif /* not lint */
1519304Speter
1619304Speter#include <sys/queue.h>
17254225Speter#include <sys/time.h>
1819304Speter#include <sys/wait.h>
1919304Speter
2019304Speter#include <bitstring.h>
21254225Speter#include <ctype.h>
2219304Speter#include <errno.h>
2319304Speter#include <limits.h>
2419304Speter#include <signal.h>
2519304Speter#include <stdio.h>
2619304Speter#include <stdlib.h>
2719304Speter#include <string.h>
2819304Speter#include <unistd.h>
2919304Speter
3019304Speter#include "../common/common.h"
3119304Speter
3219304Speterstatic const char *sigmsg __P((int));
3319304Speter
3419304Speter/*
3519304Speter * ex_shell -- :sh[ell]
3619304Speter *	Invoke the program named in the SHELL environment variable
3719304Speter *	with the argument -i.
3819304Speter *
3919304Speter * PUBLIC: int ex_shell __P((SCR *, EXCMD *));
4019304Speter */
4119304Speterint
42254225Speterex_shell(SCR *sp, EXCMD *cmdp)
4319304Speter{
4419304Speter	int rval;
45254225Speter	char *buf;
4619304Speter
4719304Speter	/* We'll need a shell. */
4819304Speter	if (opts_empty(sp, O_SHELL, 0))
4919304Speter		return (1);
5019304Speter
5119304Speter	/*
5219304Speter	 * XXX
5319304Speter	 * Assumes all shells use -i.
5419304Speter	 */
55254225Speter	(void)asprintf(&buf, "%s -i", O_STR(sp, O_SHELL));
56254225Speter	if (buf == NULL) {
57254225Speter		msgq(sp, M_SYSERR, NULL);
58254225Speter		return (1);
59254225Speter	}
6019304Speter
6119304Speter	/* Restore the window name. */
6219304Speter	(void)sp->gp->scr_rename(sp, NULL, 0);
6319304Speter
6419304Speter	/* If we're still in a vi screen, move out explicitly. */
6519304Speter	rval = ex_exec_proc(sp, cmdp, buf, NULL, !F_ISSET(sp, SC_SCR_EXWROTE));
66254225Speter	free(buf);
6719304Speter
6819304Speter	/* Set the window name. */
6919304Speter	(void)sp->gp->scr_rename(sp, sp->frp->name, 1);
7019304Speter
7119304Speter	/*
7219304Speter	 * !!!
7319304Speter	 * Historically, vi didn't require a continue message after the
7419304Speter	 * return of the shell.  Match it.
7519304Speter	 */
7619304Speter	F_SET(sp, SC_EX_WAIT_NO);
7719304Speter
7819304Speter	return (rval);
7919304Speter}
8019304Speter
8119304Speter/*
8219304Speter * ex_exec_proc --
8319304Speter *	Run a separate process.
8419304Speter *
8519304Speter * PUBLIC: int ex_exec_proc __P((SCR *, EXCMD *, char *, const char *, int));
8619304Speter */
8719304Speterint
88254225Speterex_exec_proc(SCR *sp, EXCMD *cmdp, char *cmd, const char *msg, int need_newline)
8919304Speter{
9019304Speter	GS *gp;
9119304Speter	const char *name;
9219304Speter	pid_t pid;
9319304Speter
9419304Speter	gp = sp->gp;
9519304Speter
9619304Speter	/* We'll need a shell. */
9719304Speter	if (opts_empty(sp, O_SHELL, 0))
9819304Speter		return (1);
9919304Speter
10019304Speter	/* Enter ex mode. */
10119304Speter	if (F_ISSET(sp, SC_VI)) {
10219304Speter		if (gp->scr_screen(sp, SC_EX)) {
103254225Speter			ex_wemsg(sp, cmdp->cmd->name, EXM_NOCANON);
10419304Speter			return (1);
10519304Speter		}
10619304Speter		(void)gp->scr_attr(sp, SA_ALTERNATE, 0);
10719304Speter		F_SET(sp, SC_SCR_EX | SC_SCR_EXWROTE);
10819304Speter	}
10919304Speter
11019304Speter	/* Put out additional newline, message. */
11119304Speter	if (need_newline)
11219304Speter		(void)ex_puts(sp, "\n");
11319304Speter	if (msg != NULL) {
11419304Speter		(void)ex_puts(sp, msg);
11519304Speter		(void)ex_puts(sp, "\n");
11619304Speter	}
11719304Speter	(void)ex_fflush(sp);
11819304Speter
11919304Speter	switch (pid = vfork()) {
12019304Speter	case -1:			/* Error. */
12119304Speter		msgq(sp, M_SYSERR, "vfork");
12219304Speter		return (1);
12319304Speter	case 0:				/* Utility. */
124254225Speter		if (gp->scr_child)
125254225Speter			gp->scr_child(sp);
12619304Speter		if ((name = strrchr(O_STR(sp, O_SHELL), '/')) == NULL)
12719304Speter			name = O_STR(sp, O_SHELL);
12819304Speter		else
12919304Speter			++name;
130254225Speter		execl(O_STR(sp, O_SHELL), name, "-c", cmd, (char *)NULL);
13119304Speter		msgq_str(sp, M_SYSERR, O_STR(sp, O_SHELL), "execl: %s");
13219304Speter		_exit(127);
13319304Speter		/* NOTREACHED */
13419304Speter	default:			/* Parent. */
13519304Speter		return (proc_wait(sp, (long)pid, cmd, 0, 0));
13619304Speter	}
13719304Speter	/* NOTREACHED */
13819304Speter}
13919304Speter
14019304Speter/*
14119304Speter * proc_wait --
14219304Speter *	Wait for one of the processes.
14319304Speter *
14419304Speter * !!!
14519304Speter * The pid_t type varies in size from a short to a long depending on the
14619304Speter * system.  It has to be cast into something or the standard promotion
14719304Speter * rules get you.  I'm using a long based on the belief that nobody is
14819304Speter * going to make it unsigned and it's unlikely to be a quad.
14919304Speter *
15019304Speter * PUBLIC: int proc_wait __P((SCR *, long, const char *, int, int));
15119304Speter */
15219304Speterint
153254225Speterproc_wait(SCR *sp, long int pid, const char *cmd, int silent, int okpipe)
15419304Speter{
15519304Speter	size_t len;
15619304Speter	int nf, pstat;
15719304Speter	char *p;
15819304Speter
15919304Speter	/* Wait for the utility, ignoring interruptions. */
16019304Speter	for (;;) {
16119304Speter		errno = 0;
16219304Speter		if (waitpid((pid_t)pid, &pstat, 0) != -1)
16319304Speter			break;
16419304Speter		if (errno != EINTR) {
16519304Speter			msgq(sp, M_SYSERR, "waitpid");
16619304Speter			return (1);
16719304Speter		}
16819304Speter	}
16919304Speter
17019304Speter	/*
17119304Speter	 * Display the utility's exit status.  Ignore SIGPIPE from the
17219304Speter	 * parent-writer, as that only means that the utility chose to
17319304Speter	 * exit before reading all of its input.
17419304Speter	 */
17519304Speter	if (WIFSIGNALED(pstat) && (!okpipe || WTERMSIG(pstat) != SIGPIPE)) {
176254225Speter		for (; cmdskip(*cmd); ++cmd);
17719304Speter		p = msg_print(sp, cmd, &nf);
17819304Speter		len = strlen(p);
17919304Speter		msgq(sp, M_ERR, "%.*s%s: received signal: %s%s",
180254225Speter		    (int)MIN(len, 20), p, len > 20 ? " ..." : "",
18119304Speter		    sigmsg(WTERMSIG(pstat)),
18219304Speter		    WCOREDUMP(pstat) ? "; core dumped" : "");
18319304Speter		if (nf)
18419304Speter			FREE_SPACE(sp, p, 0);
18519304Speter		return (1);
18619304Speter	}
18719304Speter
18819304Speter	if (WIFEXITED(pstat) && WEXITSTATUS(pstat)) {
18919304Speter		/*
19019304Speter		 * Remain silent for "normal" errors when doing shell file
19119304Speter		 * name expansions, they almost certainly indicate nothing
19219304Speter		 * more than a failure to match.
19319304Speter		 *
19419304Speter		 * Remain silent for vi read filter errors.  It's historic
19519304Speter		 * practice.
19619304Speter		 */
19719304Speter		if (!silent) {
198254225Speter			for (; cmdskip(*cmd); ++cmd);
19919304Speter			p = msg_print(sp, cmd, &nf);
20019304Speter			len = strlen(p);
20119304Speter			msgq(sp, M_ERR, "%.*s%s: exited with status %d",
202254225Speter			    (int)MIN(len, 20), p, len > 20 ? " ..." : "",
20319304Speter			    WEXITSTATUS(pstat));
20419304Speter			if (nf)
20519304Speter				FREE_SPACE(sp, p, 0);
20619304Speter		}
20719304Speter		return (1);
20819304Speter	}
20919304Speter	return (0);
21019304Speter}
21119304Speter
21219304Speter/*
21319304Speter * sigmsg --
21419304Speter * 	Return a pointer to a message describing a signal.
21519304Speter */
21619304Speterstatic const char *
217254225Spetersigmsg(int signo)
21819304Speter{
21919304Speter	static char buf[40];
220254225Speter	char *message;
22119304Speter
222254225Speter	/* POSIX.1-2008 leaves strsignal(3)'s return value unspecified. */
223254225Speter	if ((message = strsignal(signo)) != NULL)
224254225Speter		return message;
22519304Speter	(void)snprintf(buf, sizeof(buf), "Unknown signal: %d", signo);
22619304Speter	return (buf);
22719304Speter}
228