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);
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)
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. */
{
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
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];
* 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.
*/
#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;
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)
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)
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)
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)
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)
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)
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);
}
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 *
* 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.
* - 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)
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;