diff options
Diffstat (limited to 'ex/ex_shell.c')
-rw-r--r-- | ex/ex_shell.c | 378 |
1 files changed, 378 insertions, 0 deletions
diff --git a/ex/ex_shell.c b/ex/ex_shell.c new file mode 100644 index 0000000..9516803 --- /dev/null +++ b/ex/ex_shell.c @@ -0,0 +1,378 @@ +/*- + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1992, 1993, 1994, 1995, 1996 + * Keith Bostic. All rights reserved. + * + * See the LICENSE file for redistribution information. + */ + +#include "config.h" + +#ifndef lint +static const char sccsid[] = "@(#)ex_shell.c 10.38 (Berkeley) 8/19/96"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/queue.h> +#include <sys/wait.h> + +#include <bitstring.h> +#include <errno.h> +#include <limits.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "../common/common.h" + +static const char *sigmsg __P((int)); + +/* + * ex_shell -- :sh[ell] + * Invoke the program named in the SHELL environment variable + * with the argument -i. + * + * PUBLIC: int ex_shell __P((SCR *, EXCMD *)); + */ +int +ex_shell(sp, cmdp) + SCR *sp; + EXCMD *cmdp; +{ + int rval; + char buf[MAXPATHLEN]; + + /* We'll need a shell. */ + if (opts_empty(sp, O_SHELL, 0)) + return (1); + + /* + * XXX + * Assumes all shells use -i. + */ + (void)snprintf(buf, sizeof(buf), "%s -i", O_STR(sp, O_SHELL)); + + /* Restore the window name. */ + (void)sp->gp->scr_rename(sp, NULL, 0); + + /* If we're still in a vi screen, move out explicitly. */ + rval = ex_exec_proc(sp, cmdp, buf, NULL, !F_ISSET(sp, SC_SCR_EXWROTE)); + + /* Set the window name. */ + (void)sp->gp->scr_rename(sp, sp->frp->name, 1); + + /* + * !!! + * Historically, vi didn't require a continue message after the + * return of the shell. Match it. + */ + F_SET(sp, SC_EX_WAIT_NO); + + return (rval); +} + +/* + * ex_exec_proc -- + * Run a separate process. + * + * PUBLIC: int ex_exec_proc __P((SCR *, EXCMD *, char *, const char *, int)); + */ +int +ex_exec_proc(sp, cmdp, cmd, msg, need_newline) + SCR *sp; + EXCMD *cmdp; + char *cmd; + const char *msg; + int need_newline; +{ + GS *gp; + const char *name; + pid_t pid; + + gp = sp->gp; + + /* We'll need a shell. */ + if (opts_empty(sp, O_SHELL, 0)) + return (1); + + /* Enter ex mode. */ + if (F_ISSET(sp, SC_VI)) { + if (gp->scr_screen(sp, SC_EX)) { + ex_emsg(sp, cmdp->cmd->name, EXM_NOCANON); + return (1); + } + (void)gp->scr_attr(sp, SA_ALTERNATE, 0); + F_SET(sp, SC_SCR_EX | SC_SCR_EXWROTE); + } + + /* Put out additional newline, message. */ + if (need_newline) + (void)ex_puts(sp, "\n"); + if (msg != NULL) { + (void)ex_puts(sp, msg); + (void)ex_puts(sp, "\n"); + } + (void)ex_fflush(sp); + + switch (pid = vfork()) { + case -1: /* Error. */ + msgq(sp, M_SYSERR, "vfork"); + return (1); + case 0: /* Utility. */ + if ((name = strrchr(O_STR(sp, O_SHELL), '/')) == NULL) + name = O_STR(sp, O_SHELL); + else + ++name; + execl(O_STR(sp, O_SHELL), name, "-c", cmd, NULL); + msgq_str(sp, M_SYSERR, O_STR(sp, O_SHELL), "execl: %s"); + _exit(127); + /* NOTREACHED */ + default: /* Parent. */ + return (proc_wait(sp, (long)pid, cmd, 0, 0)); + } + /* NOTREACHED */ +} + +/* + * proc_wait -- + * Wait for one of the processes. + * + * !!! + * The pid_t type varies in size from a short to a long depending on the + * system. It has to be cast into something or the standard promotion + * rules get you. I'm using a long based on the belief that nobody is + * going to make it unsigned and it's unlikely to be a quad. + * + * PUBLIC: int proc_wait __P((SCR *, long, const char *, int, int)); + */ +int +proc_wait(sp, pid, cmd, silent, okpipe) + SCR *sp; + long pid; + const char *cmd; + int silent, okpipe; +{ + size_t len; + int nf, pstat; + char *p; + + /* Wait for the utility, ignoring interruptions. */ + for (;;) { + errno = 0; + if (waitpid((pid_t)pid, &pstat, 0) != -1) + break; + if (errno != EINTR) { + msgq(sp, M_SYSERR, "waitpid"); + return (1); + } + } + + /* + * Display the utility's exit status. Ignore SIGPIPE from the + * parent-writer, as that only means that the utility chose to + * exit before reading all of its input. + */ + if (WIFSIGNALED(pstat) && (!okpipe || WTERMSIG(pstat) != SIGPIPE)) { + for (; isblank(*cmd); ++cmd); + p = msg_print(sp, cmd, &nf); + len = strlen(p); + msgq(sp, M_ERR, "%.*s%s: received signal: %s%s", + MIN(len, 20), p, len > 20 ? " ..." : "", + sigmsg(WTERMSIG(pstat)), + WCOREDUMP(pstat) ? "; core dumped" : ""); + if (nf) + FREE_SPACE(sp, p, 0); + return (1); + } + + if (WIFEXITED(pstat) && WEXITSTATUS(pstat)) { + /* + * Remain silent for "normal" errors when doing shell file + * name expansions, they almost certainly indicate nothing + * more than a failure to match. + * + * Remain silent for vi read filter errors. It's historic + * practice. + */ + if (!silent) { + for (; isblank(*cmd); ++cmd); + p = msg_print(sp, cmd, &nf); + len = strlen(p); + msgq(sp, M_ERR, "%.*s%s: exited with status %d", + MIN(len, 20), p, len > 20 ? " ..." : "", + WEXITSTATUS(pstat)); + if (nf) + FREE_SPACE(sp, p, 0); + } + return (1); + } + return (0); +} + +/* + * XXX + * The sys_siglist[] table in the C library has this information, but there's + * no portable way to get to it. (Believe me, I tried.) + */ +typedef struct _sigs { + int number; /* signal number */ + char *message; /* related message */ +} SIGS; + +SIGS const sigs[] = { +#ifdef SIGABRT + SIGABRT, "Abort trap", +#endif +#ifdef SIGALRM + SIGALRM, "Alarm clock", +#endif +#ifdef SIGBUS + SIGBUS, "Bus error", +#endif +#ifdef SIGCLD + SIGCLD, "Child exited or stopped", +#endif +#ifdef SIGCHLD + SIGCHLD, "Child exited", +#endif +#ifdef SIGCONT + SIGCONT, "Continued", +#endif +#ifdef SIGDANGER + SIGDANGER, "System crash imminent", +#endif +#ifdef SIGEMT + SIGEMT, "EMT trap", +#endif +#ifdef SIGFPE + SIGFPE, "Floating point exception", +#endif +#ifdef SIGGRANT + SIGGRANT, "HFT monitor mode granted", +#endif +#ifdef SIGHUP + SIGHUP, "Hangup", +#endif +#ifdef SIGILL + SIGILL, "Illegal instruction", +#endif +#ifdef SIGINFO + SIGINFO, "Information request", +#endif +#ifdef SIGINT + SIGINT, "Interrupt", +#endif +#ifdef SIGIO + SIGIO, "I/O possible", +#endif +#ifdef SIGIOT + SIGIOT, "IOT trap", +#endif +#ifdef SIGKILL + SIGKILL, "Killed", +#endif +#ifdef SIGLOST + SIGLOST, "Record lock", +#endif +#ifdef SIGMIGRATE + SIGMIGRATE, "Migrate process to another CPU", +#endif +#ifdef SIGMSG + SIGMSG, "HFT input data pending", +#endif +#ifdef SIGPIPE + SIGPIPE, "Broken pipe", +#endif +#ifdef SIGPOLL + SIGPOLL, "I/O possible", +#endif +#ifdef SIGPRE + SIGPRE, "Programming error", +#endif +#ifdef SIGPROF + SIGPROF, "Profiling timer expired", +#endif +#ifdef SIGPWR + SIGPWR, "Power failure imminent", +#endif +#ifdef SIGRETRACT + SIGRETRACT, "HFT monitor mode retracted", +#endif +#ifdef SIGQUIT + SIGQUIT, "Quit", +#endif +#ifdef SIGSAK + SIGSAK, "Secure Attention Key", +#endif +#ifdef SIGSEGV + SIGSEGV, "Segmentation fault", +#endif +#ifdef SIGSOUND + SIGSOUND, "HFT sound sequence completed", +#endif +#ifdef SIGSTOP + SIGSTOP, "Suspended (signal)", +#endif +#ifdef SIGSYS + SIGSYS, "Bad system call", +#endif +#ifdef SIGTERM + SIGTERM, "Terminated", +#endif +#ifdef SIGTRAP + SIGTRAP, "Trace/BPT trap", +#endif +#ifdef SIGTSTP + SIGTSTP, "Suspended", +#endif +#ifdef SIGTTIN + SIGTTIN, "Stopped (tty input)", +#endif +#ifdef SIGTTOU + SIGTTOU, "Stopped (tty output)", +#endif +#ifdef SIGURG + SIGURG, "Urgent I/O condition", +#endif +#ifdef SIGUSR1 + SIGUSR1, "User defined signal 1", +#endif +#ifdef SIGUSR2 + SIGUSR2, "User defined signal 2", +#endif +#ifdef SIGVTALRM + SIGVTALRM, "Virtual timer expired", +#endif +#ifdef SIGWINCH + SIGWINCH, "Window size changes", +#endif +#ifdef SIGXCPU + SIGXCPU, "Cputime limit exceeded", +#endif +#ifdef SIGXFSZ + SIGXFSZ, "Filesize limit exceeded", +#endif +}; + +/* + * sigmsg -- + * Return a pointer to a message describing a signal. + */ +static const char * +sigmsg(signo) + int signo; +{ + static char buf[40]; + const SIGS *sigp; + int n; + + for (n = 0, + sigp = &sigs[0]; n < sizeof(sigs) / sizeof(sigs[0]); ++n, ++sigp) + if (sigp->number == signo) + return (sigp->message); + (void)snprintf(buf, sizeof(buf), "Unknown signal: %d", signo); + return (buf); +} |