mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
Remove the old mch_call_shell
implementation
This commit is contained in:
parent
2dcae28328
commit
57cd2d6614
492
src/os_unix.c
492
src/os_unix.c
@ -87,7 +87,6 @@ typedef union wait waitstatus;
|
||||
#else
|
||||
typedef int waitstatus;
|
||||
#endif
|
||||
static pid_t wait4pid(pid_t, waitstatus *);
|
||||
|
||||
static int RealWaitForChar(int, long, int *);
|
||||
|
||||
@ -1069,497 +1068,6 @@ void mch_new_shellsize()
|
||||
/* Nothing to do. */
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait for process "child" to end.
|
||||
* Return "child" if it exited properly, <= 0 on error.
|
||||
*/
|
||||
static pid_t wait4pid(child, status)
|
||||
pid_t child;
|
||||
waitstatus *status;
|
||||
{
|
||||
pid_t wait_pid = 0;
|
||||
|
||||
while (wait_pid != child) {
|
||||
/* When compiled with Python threads are probably used, in which case
|
||||
* wait() sometimes hangs for no obvious reason. Use waitpid()
|
||||
* instead and loop (like the GUI). Also needed for other interfaces,
|
||||
* they might call system(). */
|
||||
# ifdef __NeXT__
|
||||
wait_pid = wait4(child, status, WNOHANG, (struct rusage *)0);
|
||||
# else
|
||||
wait_pid = waitpid(child, status, WNOHANG);
|
||||
# endif
|
||||
if (wait_pid == 0) {
|
||||
/* Wait for 10 msec before trying again. */
|
||||
os_delay(10L, TRUE);
|
||||
continue;
|
||||
}
|
||||
if (wait_pid <= 0
|
||||
# ifdef ECHILD
|
||||
&& errno == ECHILD
|
||||
# endif
|
||||
)
|
||||
break;
|
||||
}
|
||||
return wait_pid;
|
||||
}
|
||||
|
||||
int mch_call_shell(char_u *cmd, ShellOpts opts, char_u *extra_shell_arg)
|
||||
{
|
||||
int tmode = cur_tmode;
|
||||
|
||||
# define EXEC_FAILED 122 /* Exit code when shell didn't execute. Don't use
|
||||
127, some shells use that already */
|
||||
|
||||
pid_t pid;
|
||||
pid_t wpid = 0;
|
||||
pid_t wait_pid = 0;
|
||||
# ifdef HAVE_UNION_WAIT
|
||||
union wait status;
|
||||
# else
|
||||
int status = -1;
|
||||
# endif
|
||||
int retval = -1;
|
||||
char **argv = NULL;
|
||||
int i;
|
||||
int fd_toshell[2]; /* for pipes */
|
||||
int fd_fromshell[2];
|
||||
int pipe_error = FALSE;
|
||||
char envbuf[50];
|
||||
int did_settmode = FALSE; /* settmode(TMODE_RAW) called */
|
||||
|
||||
out_flush();
|
||||
if (opts & kShellOptCooked)
|
||||
settmode(TMODE_COOK); /* set to normal mode */
|
||||
|
||||
argv = shell_build_argv(cmd, extra_shell_arg);
|
||||
|
||||
if (argv == NULL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
/*
|
||||
* For the GUI, when writing the output into the buffer and when reading
|
||||
* input from the buffer: Try using a pseudo-tty to get the stdin/stdout
|
||||
* of the executed command into the Vim window. Or use a pipe.
|
||||
*/
|
||||
if (opts & (kShellOptRead|kShellOptWrite)) {
|
||||
{
|
||||
pipe_error = (pipe(fd_toshell) < 0);
|
||||
if (!pipe_error) { /* pipe create OK */
|
||||
pipe_error = (pipe(fd_fromshell) < 0);
|
||||
if (pipe_error) { /* pipe create failed */
|
||||
close(fd_toshell[0]);
|
||||
close(fd_toshell[1]);
|
||||
}
|
||||
}
|
||||
if (pipe_error) {
|
||||
MSG_PUTS(_("\nCannot create pipes\n"));
|
||||
out_flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!pipe_error) { /* pty or pipe opened or not used */
|
||||
|
||||
if ((pid = fork()) == -1) { /* maybe we should use vfork() */
|
||||
MSG_PUTS(_("\nCannot fork\n"));
|
||||
if (opts & (kShellOptRead | kShellOptWrite)) {
|
||||
{
|
||||
close(fd_toshell[0]);
|
||||
close(fd_toshell[1]);
|
||||
close(fd_fromshell[0]);
|
||||
close(fd_fromshell[1]);
|
||||
}
|
||||
}
|
||||
} else if (pid == 0) { /* child */
|
||||
signal_stop(); /* handle signals normally */
|
||||
|
||||
if (opts & (kShellOptHideMess | kShellOptExpand)) {
|
||||
int fd;
|
||||
|
||||
/*
|
||||
* Don't want to show any message from the shell. Can't just
|
||||
* close stdout and stderr though, because some systems will
|
||||
* break if you try to write to them after that, so we must
|
||||
* use dup() to replace them with something else -- webb
|
||||
* Connect stdin to /dev/null too, so ":n `cat`" doesn't hang,
|
||||
* waiting for input.
|
||||
*/
|
||||
fd = open("/dev/null", O_RDWR | O_EXTRA, 0);
|
||||
fclose(stdin);
|
||||
fclose(stdout);
|
||||
fclose(stderr);
|
||||
|
||||
/*
|
||||
* If any of these open()'s and dup()'s fail, we just continue
|
||||
* anyway. It's not fatal, and on most systems it will make
|
||||
* no difference at all. On a few it will cause the execvp()
|
||||
* to exit with a non-zero status even when the completion
|
||||
* could be done, which is nothing too serious. If the open()
|
||||
* or dup() failed we'd just do the same thing ourselves
|
||||
* anyway -- webb
|
||||
*/
|
||||
if (fd >= 0) {
|
||||
ignored = dup(fd); /* To replace stdin (fd 0) */
|
||||
ignored = dup(fd); /* To replace stdout (fd 1) */
|
||||
ignored = dup(fd); /* To replace stderr (fd 2) */
|
||||
|
||||
/* Don't need this now that we've duplicated it */
|
||||
close(fd);
|
||||
}
|
||||
} else if (opts & (kShellOptRead|kShellOptWrite)) {
|
||||
|
||||
# ifdef HAVE_SETSID
|
||||
/* Create our own process group, so that the child and all its
|
||||
* children can be kill()ed. Don't do this when using pipes,
|
||||
* because stdin is not a tty, we would lose /dev/tty. */
|
||||
if (p_stmp) {
|
||||
(void)setsid();
|
||||
# if defined(SIGHUP)
|
||||
/* When doing "!xterm&" and 'shell' is bash: the shell
|
||||
* will exit and send SIGHUP to all processes in its
|
||||
* group, killing the just started process. Ignore SIGHUP
|
||||
* to avoid that. (suggested by Simon Schubert)
|
||||
*/
|
||||
signal(SIGHUP, SIG_IGN);
|
||||
# endif
|
||||
}
|
||||
# endif
|
||||
/* Simulate to have a dumb terminal (for now) */
|
||||
os_setenv("TERM", "dumb", 1);
|
||||
sprintf((char *)envbuf, "%ld", Rows);
|
||||
os_setenv("ROWS", (char *)envbuf, 1);
|
||||
sprintf((char *)envbuf, "%ld", Rows);
|
||||
os_setenv("LINES", (char *)envbuf, 1);
|
||||
sprintf((char *)envbuf, "%ld", Columns);
|
||||
os_setenv("COLUMNS", (char *)envbuf, 1);
|
||||
|
||||
/*
|
||||
* stderr is only redirected when using the GUI, so that a
|
||||
* program like gpg can still access the terminal to get a
|
||||
* passphrase using stderr.
|
||||
*/
|
||||
{
|
||||
/* set up stdin for the child */
|
||||
close(fd_toshell[1]);
|
||||
close(0);
|
||||
ignored = dup(fd_toshell[0]);
|
||||
close(fd_toshell[0]);
|
||||
|
||||
/* set up stdout for the child */
|
||||
close(fd_fromshell[0]);
|
||||
close(1);
|
||||
ignored = dup(fd_fromshell[1]);
|
||||
close(fd_fromshell[1]);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* There is no type cast for the argv, because the type may be
|
||||
* different on different machines. This may cause a warning
|
||||
* message with strict compilers, don't worry about it.
|
||||
* Call _exit() instead of exit() to avoid closing the connection
|
||||
* to the X server (esp. with GTK, which uses atexit()).
|
||||
*/
|
||||
execvp(argv[0], argv);
|
||||
_exit(EXEC_FAILED); /* exec failed, return failure code */
|
||||
} else { /* parent */
|
||||
/*
|
||||
* While child is running, ignore terminating signals.
|
||||
* Do catch CTRL-C, so that "got_int" is set.
|
||||
*/
|
||||
signal_reject_deadly();
|
||||
|
||||
/*
|
||||
* For the GUI we redirect stdin, stdout and stderr to our window.
|
||||
* This is also used to pipe stdin/stdout to/from the external
|
||||
* command.
|
||||
*/
|
||||
if (opts & (kShellOptRead|kShellOptWrite)) {
|
||||
# define BUFLEN 100 /* length for buffer, pseudo tty limit is 128 */
|
||||
char_u buffer[BUFLEN + 1];
|
||||
int buffer_off = 0; /* valid bytes in buffer[] */
|
||||
char_u ta_buf[BUFLEN + 1]; /* TypeAHead */
|
||||
int ta_len = 0; /* valid bytes in ta_buf[] */
|
||||
int len;
|
||||
int p_more_save;
|
||||
int old_State;
|
||||
int toshell_fd;
|
||||
int fromshell_fd;
|
||||
garray_T ga;
|
||||
int noread_cnt;
|
||||
# if defined(HAVE_GETTIMEOFDAY) && defined(HAVE_SYS_TIME_H)
|
||||
struct timeval start_tv;
|
||||
# endif
|
||||
|
||||
{
|
||||
close(fd_toshell[0]);
|
||||
close(fd_fromshell[1]);
|
||||
toshell_fd = fd_toshell[1];
|
||||
fromshell_fd = fd_fromshell[0];
|
||||
}
|
||||
|
||||
/*
|
||||
* Write to the child if there are typed characters.
|
||||
* Read from the child if there are characters available.
|
||||
* Repeat the reading a few times if more characters are
|
||||
* available. Need to check for typed keys now and then, but
|
||||
* not too often (delays when no chars are available).
|
||||
* This loop is quit if no characters can be read from the pty
|
||||
* (WaitForChar detected special condition), or there are no
|
||||
* characters available and the child has exited.
|
||||
* Only check if the child has exited when there is no more
|
||||
* output. The child may exit before all the output has
|
||||
* been printed.
|
||||
*
|
||||
* Currently this busy loops!
|
||||
* This can probably dead-lock when the write blocks!
|
||||
*/
|
||||
p_more_save = p_more;
|
||||
p_more = FALSE;
|
||||
old_State = State;
|
||||
State = EXTERNCMD; /* don't redraw at window resize */
|
||||
|
||||
if ((opts & kShellOptWrite) && toshell_fd >= 0) {
|
||||
/* Fork a process that will write the lines to the
|
||||
* external program. */
|
||||
if ((wpid = fork()) == -1) {
|
||||
MSG_PUTS(_("\nCannot fork\n"));
|
||||
} else if (wpid == 0) { /* child */
|
||||
linenr_T lnum = curbuf->b_op_start.lnum;
|
||||
int written = 0;
|
||||
char_u *lp = ml_get(lnum);
|
||||
size_t l;
|
||||
|
||||
close(fromshell_fd);
|
||||
for (;; ) {
|
||||
l = STRLEN(lp + written);
|
||||
if (l == 0)
|
||||
len = 0;
|
||||
else if (lp[written] == NL)
|
||||
/* NL -> NUL translation */
|
||||
len = write(toshell_fd, "", (size_t)1);
|
||||
else {
|
||||
char_u *s = vim_strchr(lp + written, NL);
|
||||
|
||||
len = write(toshell_fd, (char *)lp + written,
|
||||
s == NULL ? l
|
||||
: (size_t)(s - (lp + written)));
|
||||
}
|
||||
if (len == (int)l) {
|
||||
/* Finished a line, add a NL, unless this line
|
||||
* should not have one. */
|
||||
if (lnum != curbuf->b_op_end.lnum
|
||||
|| !curbuf->b_p_bin
|
||||
|| (lnum != curbuf->b_no_eol_lnum
|
||||
&& (lnum !=
|
||||
curbuf->b_ml.ml_line_count
|
||||
|| curbuf->b_p_eol)))
|
||||
ignored = write(toshell_fd, "\n",
|
||||
(size_t)1);
|
||||
++lnum;
|
||||
if (lnum > curbuf->b_op_end.lnum) {
|
||||
/* finished all the lines, close pipe */
|
||||
close(toshell_fd);
|
||||
toshell_fd = -1;
|
||||
break;
|
||||
}
|
||||
lp = ml_get(lnum);
|
||||
written = 0;
|
||||
} else if (len > 0)
|
||||
written += len;
|
||||
}
|
||||
_exit(0);
|
||||
} else { /* parent */
|
||||
close(toshell_fd);
|
||||
toshell_fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (opts & kShellOptRead)
|
||||
ga_init(&ga, 1, BUFLEN);
|
||||
|
||||
noread_cnt = 0;
|
||||
# if defined(HAVE_GETTIMEOFDAY) && defined(HAVE_SYS_TIME_H)
|
||||
gettimeofday(&start_tv, NULL);
|
||||
# endif
|
||||
for (;; ) {
|
||||
len = 0;
|
||||
if (got_int) {
|
||||
/* CTRL-C sends a signal to the child, we ignore it
|
||||
* ourselves */
|
||||
# ifdef HAVE_SETSID
|
||||
kill(-pid, SIGINT);
|
||||
# else
|
||||
kill(0, SIGINT);
|
||||
# endif
|
||||
if (wpid > 0)
|
||||
kill(wpid, SIGINT);
|
||||
got_int = FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the child has any characters to be printed.
|
||||
* Read them and write them to our window. Repeat this as
|
||||
* long as there is something to do, avoid the 10ms wait
|
||||
* for os_inchar(), or sending typeahead characters to
|
||||
* the external process.
|
||||
* TODO: This should handle escape sequences, compatible
|
||||
* to some terminal (vt52?).
|
||||
*/
|
||||
++noread_cnt;
|
||||
while (RealWaitForChar(fromshell_fd, 10L, NULL)) {
|
||||
len = read_eintr(fromshell_fd, buffer
|
||||
+ buffer_off, (size_t)(BUFLEN - buffer_off)
|
||||
);
|
||||
if (len <= 0) /* end of file or error */
|
||||
goto finished;
|
||||
|
||||
noread_cnt = 0;
|
||||
if (opts & kShellOptRead) {
|
||||
/* Do NUL -> NL translation, append NL separated
|
||||
* lines to the current buffer. */
|
||||
for (i = 0; i < len; ++i) {
|
||||
if (buffer[i] == NL)
|
||||
append_ga_line(&ga);
|
||||
else if (buffer[i] == NUL)
|
||||
ga_append(&ga, NL);
|
||||
else
|
||||
ga_append(&ga, buffer[i]);
|
||||
}
|
||||
}
|
||||
|
||||
windgoto(msg_row, msg_col);
|
||||
cursor_on();
|
||||
out_flush();
|
||||
if (got_int)
|
||||
break;
|
||||
|
||||
# if defined(HAVE_GETTIMEOFDAY) && defined(HAVE_SYS_TIME_H)
|
||||
{
|
||||
struct timeval now_tv;
|
||||
long msec;
|
||||
|
||||
/* Avoid that we keep looping here without
|
||||
* checking for a CTRL-C for a long time. Don't
|
||||
* break out too often to avoid losing typeahead. */
|
||||
gettimeofday(&now_tv, NULL);
|
||||
msec = (now_tv.tv_sec - start_tv.tv_sec) * 1000L
|
||||
+ (now_tv.tv_usec - start_tv.tv_usec) / 1000L;
|
||||
if (msec > 2000) {
|
||||
noread_cnt = 5;
|
||||
break;
|
||||
}
|
||||
}
|
||||
# endif
|
||||
}
|
||||
|
||||
/* If we already detected the child has finished break the
|
||||
* loop now. */
|
||||
if (wait_pid == pid)
|
||||
break;
|
||||
|
||||
/*
|
||||
* Check if the child still exists, before checking for
|
||||
* typed characters (otherwise we would lose typeahead).
|
||||
*/
|
||||
# ifdef __NeXT__
|
||||
wait_pid = wait4(pid, &status, WNOHANG, (struct rusage *)0);
|
||||
# else
|
||||
wait_pid = waitpid(pid, &status, WNOHANG);
|
||||
# endif
|
||||
if ((wait_pid == (pid_t)-1 && errno == ECHILD)
|
||||
|| (wait_pid == pid && WIFEXITED(status))) {
|
||||
/* Don't break the loop yet, try reading more
|
||||
* characters from "fromshell_fd" first. When using
|
||||
* pipes there might still be something to read and
|
||||
* then we'll break the loop at the "break" above. */
|
||||
wait_pid = pid;
|
||||
} else
|
||||
wait_pid = 0;
|
||||
|
||||
}
|
||||
finished:
|
||||
p_more = p_more_save;
|
||||
if (opts & kShellOptRead) {
|
||||
if (ga.ga_len > 0) {
|
||||
append_ga_line(&ga);
|
||||
/* remember that the NL was missing */
|
||||
curbuf->b_no_eol_lnum = curwin->w_cursor.lnum;
|
||||
} else
|
||||
curbuf->b_no_eol_lnum = 0;
|
||||
ga_clear(&ga);
|
||||
}
|
||||
|
||||
/*
|
||||
* Give all typeahead that wasn't used back to ui_inchar().
|
||||
*/
|
||||
if (ta_len)
|
||||
ui_inchar_undo(ta_buf, ta_len);
|
||||
State = old_State;
|
||||
if (toshell_fd >= 0)
|
||||
close(toshell_fd);
|
||||
close(fromshell_fd);
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait until our child has exited.
|
||||
* Ignore wait() returning pids of other children and returning
|
||||
* because of some signal like SIGWINCH.
|
||||
* Don't wait if wait_pid was already set above, indicating the
|
||||
* child already exited.
|
||||
*/
|
||||
if (wait_pid != pid)
|
||||
wait_pid = wait4pid(pid, &status);
|
||||
|
||||
|
||||
/* Make sure the child that writes to the external program is
|
||||
* dead. */
|
||||
if (wpid > 0) {
|
||||
kill(wpid, SIGKILL);
|
||||
wait4pid(wpid, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set to raw mode right now, otherwise a CTRL-C after
|
||||
* catch_signals() will kill Vim.
|
||||
*/
|
||||
if (tmode == TMODE_RAW)
|
||||
settmode(TMODE_RAW);
|
||||
did_settmode = TRUE;
|
||||
signal_accept_deadly();
|
||||
|
||||
if (WIFEXITED(status)) {
|
||||
/* LINTED avoid "bitwise operation on signed value" */
|
||||
retval = WEXITSTATUS(status);
|
||||
if (retval != 0 && !emsg_silent) {
|
||||
if (retval == EXEC_FAILED) {
|
||||
MSG_PUTS(_("\nCannot execute shell "));
|
||||
msg_outtrans(p_sh);
|
||||
msg_putchar('\n');
|
||||
} else if (!(opts & kShellOptSilent)) {
|
||||
MSG_PUTS(_("\nshell returned "));
|
||||
msg_outnum((long)retval);
|
||||
msg_putchar('\n');
|
||||
}
|
||||
}
|
||||
} else
|
||||
MSG_PUTS(_("\nCommand terminated\n"));
|
||||
}
|
||||
}
|
||||
shell_free_argv(argv);
|
||||
|
||||
error:
|
||||
if (!did_settmode)
|
||||
if (tmode == TMODE_RAW)
|
||||
settmode(TMODE_RAW); /* set to raw mode */
|
||||
resettitle();
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait "msec" msec until a character is available from file descriptor "fd".
|
||||
* "msec" == 0 will check for characters once.
|
||||
|
@ -40,7 +40,6 @@ int mch_screenmode(char_u *arg);
|
||||
int mch_get_shellsize(void);
|
||||
void mch_set_shellsize(void);
|
||||
void mch_new_shellsize(void);
|
||||
int mch_call_shell(char_u *cmd, ShellOpts opts, char_u *extra_shell_arg);
|
||||
int mch_expand_wildcards(int num_pat, char_u **pat, int *num_file,
|
||||
char_u ***file,
|
||||
int flags);
|
||||
|
Loading…
Reference in New Issue
Block a user