From 5a1feb809191e236cadd2884a5f57ad26cd213a3 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 22 Jul 2017 18:04:08 +0200 Subject: patch 8.0.0744: terminal window does not use a pty Problem: A terminal window uses pipes instead of a pty. Solution: Add pty support. --- src/channel.c | 21 ++++++-- src/os_unix.c | 132 ++++++++++++++++++++++++++++--------------------- src/os_win32.c | 2 +- src/proto/os_unix.pro | 2 +- src/proto/os_win32.pro | 2 +- src/structs.h | 1 + src/terminal.c | 4 +- src/version.c | 2 + 8 files changed, 102 insertions(+), 64 deletions(-) diff --git a/src/channel.c b/src/channel.c index 141bc5f37..7af19b041 100644 --- a/src/channel.c +++ b/src/channel.c @@ -1013,7 +1013,16 @@ ch_close_part(channel_T *channel, ch_part_T part) if (part == PART_SOCK) sock_close(*fd); else - fd_close(*fd); + { + /* When using a pty the same FD is set on multiple parts, only + * close it when the last reference is closed. */ + if ((part == PART_IN || channel->ch_part[PART_IN].ch_fd != *fd) + && (part == PART_OUT + || channel->ch_part[PART_OUT].ch_fd != *fd) + && (part == PART_ERR + || channel->ch_part[PART_ERR].ch_fd != *fd)) + fd_close(*fd); + } *fd = INVALID_FD; channel->ch_to_be_closed &= ~(1 << part); @@ -4280,6 +4289,12 @@ get_job_options(typval_T *tv, jobopt_T *opt, int supported) opt->jo_io_name[part] = get_tv_string_buf_chk(item, opt->jo_io_name_buf[part]); } + else if (STRCMP(hi->hi_key, "pty") == 0) + { + if (!(supported & JO_MODE)) + break; + opt->jo_pty = get_tv_number(item); + } else if (STRCMP(hi->hi_key, "in_buf") == 0 || STRCMP(hi->hi_key, "out_buf") == 0 || STRCMP(hi->hi_key, "err_buf") == 0) @@ -5074,10 +5089,10 @@ job_start(typval_T *argvars, jobopt_T *opt_arg) ch_logs(NULL, "Starting job: %s", (char *)ga.ga_data); ga_clear(&ga); } - mch_start_job(argv, job, &opt); + mch_job_start(argv, job, &opt); #else ch_logs(NULL, "Starting job: %s", (char *)cmd); - mch_start_job((char *)cmd, job, &opt); + mch_job_start((char *)cmd, job, &opt); #endif /* If the channel is reading from a buffer, write lines now. */ diff --git a/src/os_unix.c b/src/os_unix.c index ad70465b0..156168fcc 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -4096,6 +4096,32 @@ set_default_child_environment(void) { set_child_environment(Rows, Columns, "dumb"); } +#endif + +#if defined(FEAT_GUI) || defined(FEAT_JOB_CHANNEL) + static void +open_pty(int *pty_master_fd, int *pty_slave_fd) +{ + char *tty_name; + + *pty_master_fd = OpenPTY(&tty_name); /* open pty */ + if (*pty_master_fd >= 0) + { + /* Leaving out O_NOCTTY may lead to waitpid() always returning + * 0 on Mac OS X 10.7 thereby causing freezes. Let's assume + * adding O_NOCTTY always works when defined. */ +#ifdef O_NOCTTY + *pty_slave_fd = open(tty_name, O_RDWR | O_NOCTTY | O_EXTRA, 0); +#else + *pty_slave_fd = open(tty_name, O_RDWR | O_EXTRA, 0); +#endif + if (*pty_slave_fd < 0) + { + close(*pty_master_fd); + *pty_master_fd = -1; + } + } +} #endif int @@ -4206,7 +4232,6 @@ mch_call_shell( int pty_master_fd = -1; /* for pty's */ # ifdef FEAT_GUI int pty_slave_fd = -1; - char *tty_name; # endif int fd_toshell[2]; /* for pipes */ int fd_fromshell[2]; @@ -4269,25 +4294,7 @@ mch_call_shell( * If the slave can't be opened, close the master pty. */ if (p_guipty && !(options & (SHELL_READ|SHELL_WRITE))) - { - pty_master_fd = OpenPTY(&tty_name); /* open pty */ - if (pty_master_fd >= 0) - { - /* Leaving out O_NOCTTY may lead to waitpid() always returning - * 0 on Mac OS X 10.7 thereby causing freezes. Let's assume - * adding O_NOCTTY always works when defined. */ -#ifdef O_NOCTTY - pty_slave_fd = open(tty_name, O_RDWR | O_NOCTTY | O_EXTRA, 0); -#else - pty_slave_fd = open(tty_name, O_RDWR | O_EXTRA, 0); -#endif - if (pty_slave_fd < 0) - { - close(pty_master_fd); - pty_master_fd = -1; - } - } - } + open_pty(&pty_master_fd, &pty_slave_fd); /* * If not opening a pty or it didn't work, try using pipes. */ @@ -5100,12 +5107,14 @@ error: #if defined(FEAT_JOB_CHANNEL) || defined(PROTO) void -mch_start_job(char **argv, job_T *job, jobopt_T *options) +mch_job_start(char **argv, job_T *job, jobopt_T *options) { pid_t pid; int fd_in[2]; /* for stdin */ int fd_out[2]; /* for stdout */ int fd_err[2]; /* for stderr */ + int pty_master_fd = -1; + int pty_slave_fd = -1; channel_T *channel = NULL; int use_null_for_in = options->jo_io[PART_IN] == JIO_NULL; int use_null_for_out = options->jo_io[PART_OUT] == JIO_NULL; @@ -5128,6 +5137,9 @@ mch_start_job(char **argv, job_T *job, jobopt_T *options) fd_err[0] = -1; fd_err[1] = -1; + if (options->jo_pty) + open_pty(&pty_master_fd, &pty_slave_fd); + /* TODO: without the channel feature connect the child to /dev/null? */ /* Open pipes for stdin, stdout, stderr. */ if (use_file_for_in) @@ -5141,7 +5153,7 @@ mch_start_job(char **argv, job_T *job, jobopt_T *options) goto failed; } } - else if (!use_null_for_in && pipe(fd_in) < 0) + else if (!use_null_for_in && pty_master_fd < 0 && pipe(fd_in) < 0) goto failed; if (use_file_for_out) @@ -5155,7 +5167,7 @@ mch_start_job(char **argv, job_T *job, jobopt_T *options) goto failed; } } - else if (!use_null_for_out && pipe(fd_out) < 0) + else if (!use_null_for_out && pty_master_fd < 0 && pipe(fd_out) < 0) goto failed; if (use_file_for_err) @@ -5169,7 +5181,8 @@ mch_start_job(char **argv, job_T *job, jobopt_T *options) goto failed; } } - else if (!use_out_for_err && !use_null_for_err && pipe(fd_err) < 0) + else if (!use_out_for_err && !use_null_for_err + && pty_master_fd < 0 && pipe(fd_err) < 0) goto failed; if (!use_null_for_in || !use_null_for_out || !use_null_for_err) @@ -5224,54 +5237,53 @@ mch_start_job(char **argv, job_T *job, jobopt_T *options) null_fd = open("/dev/null", O_RDWR | O_EXTRA, 0); /* set up stdin for the child */ + close(0); if (use_null_for_in && null_fd >= 0) - { - close(0); ignored = dup(null_fd); - } + else if (fd_in[0] < 0) + ignored = dup(pty_slave_fd); else - { - if (!use_file_for_in) - close(fd_in[1]); - close(0); ignored = dup(fd_in[0]); - close(fd_in[0]); - } /* set up stderr for the child */ + close(2); if (use_null_for_err && null_fd >= 0) { - close(2); ignored = dup(null_fd); stderr_works = FALSE; } else if (use_out_for_err) - { - close(2); ignored = dup(fd_out[1]); - } + else if (fd_err[1] < 0) + ignored = dup(pty_slave_fd); else - { - if (!use_file_for_err) - close(fd_err[0]); - close(2); ignored = dup(fd_err[1]); - close(fd_err[1]); - } /* set up stdout for the child */ + close(1); if (use_null_for_out && null_fd >= 0) - { - close(1); ignored = dup(null_fd); - } + else if (fd_out[1] < 0) + ignored = dup(pty_slave_fd); else - { - if (!use_file_for_out) - close(fd_out[0]); - close(1); ignored = dup(fd_out[1]); + + if (fd_in[0] >= 0) + close(fd_in[0]); + if (fd_in[1] >= 0) + close(fd_in[1]); + if (fd_out[0] >= 0) + close(fd_out[0]); + if (fd_out[1] >= 0) close(fd_out[1]); + if (fd_err[0] >= 0) + close(fd_err[0]); + if (fd_err[1] >= 0) + close(fd_err[1]); + if (pty_master_fd >= 0) + { + close(pty_master_fd); /* not used */ + close(pty_slave_fd); /* duped above */ } if (null_fd >= 0) @@ -5296,7 +5308,9 @@ mch_start_job(char **argv, job_T *job, jobopt_T *options) job->jv_status = JOB_STARTED; job->jv_channel = channel; /* ch_refcount was set above */ - /* child stdin, stdout and stderr */ + if (pty_master_fd >= 0) + close(pty_slave_fd); /* duped above */ + /* close child stdin, stdout and stderr */ if (!use_file_for_in && fd_in[0] >= 0) close(fd_in[0]); if (!use_file_for_out && fd_out[1] >= 0) @@ -5306,12 +5320,12 @@ mch_start_job(char **argv, job_T *job, jobopt_T *options) if (channel != NULL) { channel_set_pipes(channel, - use_file_for_in || use_null_for_in - ? INVALID_FD : fd_in[1], - use_file_for_out || use_null_for_out - ? INVALID_FD : fd_out[0], - use_out_for_err || use_file_for_err || use_null_for_err - ? INVALID_FD : fd_err[0]); + use_file_for_in || use_null_for_in + ? INVALID_FD : fd_in[1] < 0 ? pty_master_fd : fd_in[1], + use_file_for_out || use_null_for_out + ? INVALID_FD : fd_out[0] < 0 ? pty_master_fd : fd_out[0], + use_out_for_err || use_file_for_err || use_null_for_err + ? INVALID_FD : fd_err[0] < 0 ? pty_master_fd : fd_err[0]); channel_set_job(channel, job, options); } @@ -5332,6 +5346,10 @@ failed: close(fd_err[0]); if (fd_err[1] >= 0) close(fd_err[1]); + if (pty_master_fd >= 0) + close(pty_master_fd); + if (pty_slave_fd >= 0) + close(pty_slave_fd); } char * diff --git a/src/os_win32.c b/src/os_win32.c index f2fd808e9..8a38bcc56 100644 --- a/src/os_win32.c +++ b/src/os_win32.c @@ -4964,7 +4964,7 @@ job_io_file_open( } void -mch_start_job(char *cmd, job_T *job, jobopt_T *options) +mch_job_start(char *cmd, job_T *job, jobopt_T *options) { STARTUPINFO si; PROCESS_INFORMATION pi; diff --git a/src/proto/os_unix.pro b/src/proto/os_unix.pro index 700b69ef2..11191c95b 100644 --- a/src/proto/os_unix.pro +++ b/src/proto/os_unix.pro @@ -57,7 +57,7 @@ void mch_set_shellsize(void); void mch_new_shellsize(void); int mch_parse_cmd(char_u *cmd, int use_shcf, char ***argv, int *argc); int mch_call_shell(char_u *cmd, int options); -void mch_start_job(char **argv, job_T *job, jobopt_T *options); +void mch_job_start(char **argv, job_T *job, jobopt_T *options); char *mch_job_status(job_T *job); job_T *mch_detect_ended_job(job_T *job_list); int mch_stop_job(job_T *job, char_u *how); diff --git a/src/proto/os_win32.pro b/src/proto/os_win32.pro index ca671464b..6c5dc4fc0 100644 --- a/src/proto/os_win32.pro +++ b/src/proto/os_win32.pro @@ -41,7 +41,7 @@ void mch_set_shellsize(void); void mch_new_shellsize(void); void mch_set_winsize_now(void); int mch_call_shell(char_u *cmd, int options); -void mch_start_job(char *cmd, job_T *job, jobopt_T *options); +void mch_job_start(char *cmd, job_T *job, jobopt_T *options); char *mch_job_status(job_T *job); job_T *mch_detect_ended_job(job_T *job_list); int mch_stop_job(job_T *job, char_u *how); diff --git a/src/structs.h b/src/structs.h index 5a7f28b79..44df35629 100644 --- a/src/structs.h +++ b/src/structs.h @@ -1705,6 +1705,7 @@ typedef struct char_u jo_io_name_buf[4][NUMBUFLEN]; char_u *jo_io_name[4]; /* not allocated! */ int jo_io_buf[4]; + int jo_pty; int jo_modifiable[4]; int jo_message[4]; channel_T *jo_channel; diff --git a/src/terminal.c b/src/terminal.c index df40866ba..0d974eddb 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -34,7 +34,6 @@ * TODO: * - When 'termsize' is set and dragging the separator the terminal gets messed * up. - * - Use a pty for I/O with the job. * - set buffer options to be scratch, hidden, nomodifiable, etc. * - set buffer name to command, add (1) to avoid duplicates. * - If [command] is not given the 'shell' option is used. @@ -52,6 +51,8 @@ * - add test for giving error for invalid 'termsize' value. * - support minimal size when 'termsize' is "rows*cols". * - support minimal size when 'termsize' is empty? + * - implement "term" for job_start(): more job options when starting a + * terminal. * - implement ":buf {term-buf-name}" * - implement term_list() list of buffers with a terminal * - implement term_getsize(buf) @@ -673,6 +674,7 @@ setup_job_options(jobopt_T *opt, int rows, int cols) opt->jo_set |= JO_OUT_IO + (JO_OUT_IO << (PART_ERR - PART_OUT)); opt->jo_io_buf[PART_OUT] = curbuf->b_fnum; opt->jo_io_buf[PART_ERR] = curbuf->b_fnum; + opt->jo_pty = TRUE; opt->jo_set |= JO_OUT_BUF + (JO_OUT_BUF << (PART_ERR - PART_OUT)); opt->jo_term_rows = rows; opt->jo_term_cols = cols; diff --git a/src/version.c b/src/version.c index da56b9a31..31dc8d682 100644 --- a/src/version.c +++ b/src/version.c @@ -769,6 +769,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 744, /**/ 743, /**/ -- cgit v1.2.1