diff options
Diffstat (limited to 'src/os_unix.c')
-rw-r--r-- | src/os_unix.c | 492 |
1 files changed, 0 insertions, 492 deletions
diff --git a/src/os_unix.c b/src/os_unix.c index 3f95f7bdc8..7df61f012f 100644 --- a/src/os_unix.c +++ b/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 *); @@ -1070,497 +1069,6 @@ void mch_new_shellsize() } /* - * 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. * "msec" == -1 will block until a character is available. |