diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h index 694c23b..53f01dc 100644 --- a/winsup/cygwin/fhandler.h +++ b/winsup/cygwin/fhandler.h @@ -1508,14 +1508,12 @@ class fhandler_pty_common: public fhandler_termios copyto (fh); return fh; } - - protected: - BOOL process_opost_output (HANDLE h, const void *ptr, ssize_t& len, bool is_echo); }; class fhandler_pty_slave: public fhandler_pty_common { HANDLE inuse; // used to indicate that a tty is in use + HANDLE from_slave; /* Helper functions for fchmod and fchown. */ bool fch_open_handles (bool chown); @@ -1575,8 +1573,11 @@ class fhandler_pty_master: public fhandler_pty_common HANDLE from_master, to_master; HANDLE echo_r, echo_w; DWORD dwProcessId; // Owner of master handles + int column; /* Current Column */ public: + int need_nl; // Next read should start with \n + HANDLE get_echo_handle () const { return echo_r; } /* Constructor */ fhandler_pty_master (int); diff --git a/winsup/cygwin/fhandler_tty.cc b/winsup/cygwin/fhandler_tty.cc index ccb76d9..91d3958 100644 --- a/winsup/cygwin/fhandler_tty.cc +++ b/winsup/cygwin/fhandler_tty.cc @@ -42,6 +42,7 @@ struct pipe_request { struct pipe_reply { HANDLE from_master; HANDLE to_master; + HANDLE from_slave; DWORD error; }; @@ -145,8 +146,7 @@ fhandler_pty_common::__release_output_mutex (const char *fn, int ln) void fhandler_pty_master::doecho (const void *str, DWORD len) { - ssize_t towrite = len; - if (!process_opost_output (echo_w, str, towrite, true)) + if (!WriteFile (echo_w, str, len, &len, NULL)) termios_printf ("Write to echo pipe failed, %E"); } @@ -228,6 +228,46 @@ fhandler_pty_master::process_slave_output (char *buf, size_t len, int pktmode_on if (len == 0) goto out; + /* Set RLEN to the number of bytes to read from the pipe. */ + rlen = len; + + char *optr; + optr = buf; + if (pktmode_on && buf) + { + *optr++ = TIOCPKT_DATA; + rlen -= 1; + rc++; + } + + if (rlen == 0) + goto out; + + if (need_nl) + { + /* We need to return a left over \n character, resulting from + \r\n conversion. Note that we already checked for FLUSHO and + output_stopped at the time that we read the character, so we + don't check again here. */ + if (buf) + *optr++ = '\n'; + need_nl = 0; + rc++; + goto out; + } + + if (get_ttyp ()->ti.c_oflag & OPOST && get_ttyp ()->ti.c_oflag & ONLCR) + { + /* We are going to expand \n to \r\n, so don't read more than + half of the number of bytes requested. */ + rlen /= 2; + if (rlen == 0) + rlen = 1; + } + + if (rlen > sizeof outbuf) + rlen = sizeof outbuf; + for (;;) { n = echo_cnt = 0; @@ -267,26 +307,6 @@ fhandler_pty_master::process_slave_output (char *buf, size_t len, int pktmode_on flush_to_slave (); } - /* Set RLEN to the number of bytes to read from the pipe. */ - rlen = len; - - char *optr; - optr = buf; - if (pktmode_on && buf) - { - *optr++ = TIOCPKT_DATA; - rlen -= 1; - } - - if (rlen == 0) - { - rc = optr - buf; - goto out; - } - - if (rlen > sizeof outbuf) - rlen = sizeof outbuf; - /* If echo pipe has data (something has been typed or pasted), prefer it over slave output. */ if (echo_cnt > 0) @@ -308,8 +328,58 @@ fhandler_pty_master::process_slave_output (char *buf, size_t len, int pktmode_on if (get_ttyp ()->ti.c_lflag & FLUSHO || !buf) continue; - memcpy (optr, outbuf, n); - optr += n; + if (!(get_ttyp ()->ti.c_oflag & OPOST)) // raw output mode + { + memcpy (optr, outbuf, n); + optr += n; + } + else // post-process output + { + char *iptr = outbuf; + + while (n--) + { + switch (*iptr) + { + case '\r': + if ((get_ttyp ()->ti.c_oflag & ONOCR) && column == 0) + { + iptr++; + continue; + } + if (get_ttyp ()->ti.c_oflag & OCRNL) + *iptr = '\n'; + else + column = 0; + break; + case '\n': + if (get_ttyp ()->ti.c_oflag & ONLCR) + { + *optr++ = '\r'; + column = 0; + } + if (get_ttyp ()->ti.c_oflag & ONLRET) + column = 0; + break; + default: + column++; + break; + } + + /* Don't store data past the end of the user's buffer. This + can happen if the user requests a read of 1 byte when + doing \r\n expansion. */ + if (optr - buf >= (int) len) + { + if (*iptr != '\n' || n != 0) + system_printf ("internal error: %u unexpected characters", n); + need_nl = 1; + break; + } + + *optr++ = *iptr++; + } + } rc = optr - buf; break; @@ -332,7 +402,7 @@ out: /* pty slave stuff */ fhandler_pty_slave::fhandler_pty_slave (int unit) - : fhandler_pty_common (), inuse (NULL) + : fhandler_pty_common (), inuse (NULL), from_slave (NULL) { if (unit >= 0) dev ().parse (DEV_PTYS_MAJOR, unit); @@ -341,11 +411,11 @@ fhandler_pty_slave::fhandler_pty_slave (int unit) int fhandler_pty_slave::open (int flags, mode_t) { - HANDLE pty_owner, from_master_local, to_master_local; + HANDLE pty_owner, from_master_local, to_master_local, from_slave_local; HANDLE *handles[] = { &from_master_local, &input_available_event, &input_mutex, &inuse, - &output_mutex, &to_master_local, &pty_owner, + &output_mutex, &to_master_local, &from_slave_local, &pty_owner, NULL }; @@ -447,6 +517,15 @@ fhandler_pty_slave::open (int flags, mode_t) errmsg = "can't duplicate output, %E"; goto err; } + if (!DuplicateHandle (pty_owner, get_ttyp ()->from_slave (), + GetCurrentProcess (), &from_slave_local, 0, TRUE, + DUPLICATE_SAME_ACCESS)) + { + termios_printf ("can't duplicate from_slave from %u/%p, %E", + get_ttyp ()->master_pid, get_ttyp ()->from_slave ()); + __seterrno (); + goto err_no_msg; + } if (pty_owner != GetCurrentProcess ()) CloseHandle (pty_owner); } @@ -467,7 +546,8 @@ fhandler_pty_slave::open (int flags, mode_t) } from_master_local = repl.from_master; to_master_local = repl.to_master; - if (!from_master_local || !to_master_local) + from_slave_local = repl.from_slave; + if (!from_master_local || !to_master_local || !from_slave_local) { SetLastError (repl.error); errmsg = "error duplicating pipes, %E"; @@ -476,14 +556,18 @@ fhandler_pty_slave::open (int flags, mode_t) } VerifyHandle (from_master_local); VerifyHandle (to_master_local); + VerifyHandle (from_slave_local); termios_printf ("duplicated from_master %p->%p from pty_owner", get_ttyp ()->from_master (), from_master_local); termios_printf ("duplicated to_master %p->%p from pty_owner", get_ttyp ()->to_master (), to_master_local); + termios_printf ("duplicated from_slave %p->%p from pty_owner", + get_ttyp ()->from_master (), from_slave_local); set_io_handle (from_master_local); set_output_handle (to_master_local); + from_slave = from_slave_local; fhandler_console::need_invisible (); set_open_status (); @@ -532,6 +616,8 @@ fhandler_pty_slave::close () termios_printf ("CloseHandle (inuse), %E"); if (!ForceCloseHandle (input_available_event)) termios_printf ("CloseHandle (input_available_event<%p>), %E", input_available_event); + if (!ForceCloseHandle (from_slave)) + termios_printf ("CloseHandle (from_slave), %E"); if ((unsigned) myself->ctty == FHDEV (DEV_PTYS_MAJOR, get_minor ())) fhandler_console::free_console (); /* assumes that we are the last pty closer */ return fhandler_pty_common::close (); @@ -580,6 +666,7 @@ fhandler_pty_slave::init (HANDLE h, DWORD a, mode_t) ssize_t __stdcall fhandler_pty_slave::write (const void *ptr, size_t len) { + DWORD n; ssize_t towrite = len; bg_check_types bg = bg_check (SIGTTOU); @@ -590,19 +677,44 @@ fhandler_pty_slave::write (const void *ptr, size_t len) push_process_state process_state (PID_TTYOU); - if (!process_opost_output (get_output_handle (), ptr, towrite, false)) + while (len) { - DWORD err = GetLastError (); - termios_printf ("WriteFile failed, %E"); - switch (err) + n = MIN (OUT_BUFFER_SIZE, len); + char *buf = (char *)ptr; + ptr = (char *) ptr + n; + len -= n; + + if (tc ()->output_stopped && is_nonblocking ()) + { + if (len < (size_t)towrite) + towrite -= len; + else + { + set_errno(EAGAIN); + towrite = -1; + } + break; + } + + while (tc ()->output_stopped) + cygwait (10); + + BOOL res = WriteFile (get_output_handle (), buf, n, &n, NULL); + if (!res) { - case ERROR_NO_DATA: - err = ERROR_IO_DEVICE; - default: - __seterrno_from_win_error (err); + DWORD err = GetLastError (); + termios_printf ("WriteFile failed, %E"); + switch (err) + { + case ERROR_NO_DATA: + err = ERROR_IO_DEVICE; + default: + __seterrno_from_win_error (err); + } + raise (SIGHUP); /* FIXME: Should this be SIGTTOU? */ + towrite = -1; + break; } - raise (SIGHUP); /* FIXME: Should this be SIGTTOU? */ - towrite = -1; } return towrite; } @@ -868,6 +980,9 @@ fhandler_pty_slave::tcgetattr (struct termios *t) int fhandler_pty_slave::tcsetattr (int, const struct termios *t) { + DWORD n; + while (::bytes_available (n, from_slave) && n) + cygwait (10); acquire_output_mutex (INFINITE); get_ttyp ()->ti = *t; release_output_mutex (); @@ -1140,7 +1255,7 @@ errout: fhandler_pty_master::fhandler_pty_master (int unit) : fhandler_pty_common (), pktmode (0), master_ctl (NULL), master_thread (NULL), from_master (NULL), to_master (NULL), - echo_r (NULL), echo_w (NULL), dwProcessId (0) + echo_r (NULL), echo_w (NULL), dwProcessId (0), column (0), need_nl (0) { if (unit >= 0) dev ().parse (DEV_PTYM_MAJOR, unit); @@ -1521,6 +1636,13 @@ fhandler_pty_master::pty_master_thread () termios_printf ("DuplicateHandle (to_master), %E"); goto reply; } + if (!DuplicateHandle (GetCurrentProcess (), get_io_handle (), + client, &repl.from_slave, + 0, TRUE, DUPLICATE_SAME_ACCESS)) + { + termios_printf ("DuplicateHandle (from_slave), %E"); + goto reply; + } } reply: repl.error = GetLastError (); @@ -1643,6 +1765,7 @@ fhandler_pty_master::setup () t.set_from_master (from_master); t.set_to_master (to_master); + t.set_from_slave (get_io_handle ()); t.winsize.ws_col = 80; t.winsize.ws_row = 25; t.master_pid = myself->pid; @@ -1681,6 +1804,7 @@ fhandler_pty_master::fixup_after_fork (HANDLE parent) { t.set_from_master (arch->from_master); t.set_to_master (arch->to_master); + t.set_from_slave (arch->get_io_handle ()); } arch->dwProcessId = wpid; } @@ -1697,93 +1821,3 @@ fhandler_pty_master::fixup_after_exec () else from_master = to_master = NULL; } - -BOOL -fhandler_pty_common::process_opost_output (HANDLE h, const void *ptr, ssize_t& len, bool is_echo) -{ - ssize_t towrite = len; - BOOL res = TRUE; - while (towrite) - { - if (!is_echo) - { - if (tc ()->output_stopped && is_nonblocking ()) - { - if (towrite < len) - break; - else - { - set_errno(EAGAIN); - len = -1; - return TRUE; - } - } - while (tc ()->output_stopped) - cygwait (10); - } - - if (!(get_ttyp ()->ti.c_oflag & OPOST)) // raw output mode - { - DWORD n = MIN (OUT_BUFFER_SIZE, towrite); - res = WriteFile (h, ptr, n, &n, NULL); - if (!res) - break; - ptr = (char *) ptr + n; - towrite -= n; - } - else // post-process output - { - char outbuf[OUT_BUFFER_SIZE + 1]; - char *buf = (char *)ptr; - DWORD n = 0; - ssize_t rc = 0; - acquire_output_mutex (INFINITE); - while (n < OUT_BUFFER_SIZE && rc < towrite) - { - switch (buf[rc]) - { - case '\r': - if ((get_ttyp ()->ti.c_oflag & ONOCR) - && get_ttyp ()->column == 0) - { - rc++; - continue; - } - if (get_ttyp ()->ti.c_oflag & OCRNL) - { - outbuf[n++] = '\n'; - rc++; - } - else - { - outbuf[n++] = buf[rc++]; - get_ttyp ()->column = 0; - } - break; - case '\n': - if (get_ttyp ()->ti.c_oflag & ONLCR) - { - outbuf[n++] = '\r'; - get_ttyp ()->column = 0; - } - if (get_ttyp ()->ti.c_oflag & ONLRET) - get_ttyp ()->column = 0; - outbuf[n++] = buf[rc++]; - break; - default: - outbuf[n++] = buf[rc++]; - get_ttyp ()->column++; - break; - } - } - release_output_mutex (); - res = WriteFile (h, outbuf, n, &n, NULL); - if (!res) - break; - ptr = (char *) ptr + rc; - towrite -= rc; - } - } - len -= towrite; - return res; -} diff --git a/winsup/cygwin/select.cc b/winsup/cygwin/select.cc index 1706c87..17a32a3 100644 --- a/winsup/cygwin/select.cc +++ b/winsup/cygwin/select.cc @@ -604,6 +604,11 @@ peek_pipe (select_record *s, bool from_select) { fhandler_pty_master *fhm = (fhandler_pty_master *) fh; fhm->flush_to_slave (); + if (fhm->need_nl) + { + gotone = s->read_ready = true; + goto out; + } } break; default: diff --git a/winsup/cygwin/tty.cc b/winsup/cygwin/tty.cc index 3f1077d..af616d6 100644 --- a/winsup/cygwin/tty.cc +++ b/winsup/cygwin/tty.cc @@ -237,7 +237,6 @@ tty::init () was_opened = false; master_pid = 0; is_console = false; - column = 0; } HANDLE diff --git a/winsup/cygwin/tty.h b/winsup/cygwin/tty.h index 27d43f7..c2a387e 100644 --- a/winsup/cygwin/tty.h +++ b/winsup/cygwin/tty.h @@ -92,16 +92,18 @@ public: private: HANDLE _from_master; HANDLE _to_master; + HANDLE _from_slave; public: HANDLE from_master() const { return _from_master; } HANDLE to_master() const { return _to_master; } + HANDLE from_slave() const { return _from_slave; } void set_from_master (HANDLE h) { _from_master = h; } void set_to_master (HANDLE h) { _to_master = h; } + void set_from_slave (HANDLE h) { _from_slave = h; } int read_retval; bool was_opened; /* True if opened at least once. */ - int column; /* Current Column */ void init (); HANDLE open_inuse (ACCESS_MASK access);