summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCorinna Vinschen <vinschen@redhat.com>2008-03-04 22:30:21 +0000
committerCorinna Vinschen <vinschen@redhat.com>2008-03-04 22:30:21 +0000
commit20859e2c44b71da4bd0878acf500ffa392452969 (patch)
tree183277638deaa4e57701f517ac3f26eadc8a25e9
parent20e2effe2f4ccfea1dae967a65ba69afb3fa8596 (diff)
downloadgdb-20859e2c44b71da4bd0878acf500ffa392452969.tar.gz
* dtable.cc (dtable::init_std_file_from_handle): Set access to
read/write if handle is connected to a tty or console. * fhandler_dsp.cc (fhandler_dev_dsp::dup): Set open flags correctly after duplicating from archetype. * fhandler_tty.cc (fhandler_tty_slave::dup): Ditto. (fhandler_pty_master::dup): Ditto.
-rw-r--r--winsup/cygwin/ChangeLog9
-rw-r--r--winsup/cygwin/dtable.cc4
-rw-r--r--winsup/cygwin/fhandler_dsp.cc1375
-rw-r--r--winsup/cygwin/fhandler_tty.cc1495
4 files changed, 2882 insertions, 1 deletions
diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog
index b02bab1814d..181c224cdf0 100644
--- a/winsup/cygwin/ChangeLog
+++ b/winsup/cygwin/ChangeLog
@@ -1,3 +1,12 @@
+2008-03-04 Corinna Vinschen <corinna@vinschen.de>
+
+ * dtable.cc (dtable::init_std_file_from_handle): Set access to
+ read/write if handle is connected to a tty or console.
+ * fhandler_dsp.cc (fhandler_dev_dsp::dup): Set open flags correctly
+ after duplicating from archetype.
+ * fhandler_tty.cc (fhandler_tty_slave::dup): Ditto.
+ (fhandler_pty_master::dup): Ditto.
+
2008-03-01 Christopher Faylor <me+cygwin@cgf.cx>
* cygtls.h (_cygtls::handle_threadlist_exception): Eliminate.
diff --git a/winsup/cygwin/dtable.cc b/winsup/cygwin/dtable.cc
index 08180eb448e..17c333c0cc4 100644
--- a/winsup/cygwin/dtable.cc
+++ b/winsup/cygwin/dtable.cc
@@ -336,7 +336,9 @@ dtable::init_std_file_from_handle (int fd, HANDLE handle)
}
DWORD access;
- if (fd == 0)
+ if (dev == FH_TTY || dev == FH_CONSOLE)
+ access = GENERIC_READ | GENERIC_WRITE;
+ else if (fd == 0)
access = GENERIC_READ;
else
access = GENERIC_WRITE; /* Should be rdwr for stderr but not sure that's
diff --git a/winsup/cygwin/fhandler_dsp.cc b/winsup/cygwin/fhandler_dsp.cc
new file mode 100644
index 00000000000..7a74dd79074
--- /dev/null
+++ b/winsup/cygwin/fhandler_dsp.cc
@@ -0,0 +1,1375 @@
+/* Fhandler_dev_dsp: code to emulate OSS sound model /dev/dsp
+
+ Copyright 2001, 2002, 2003, 2004 Red Hat, Inc
+
+ Written by Andy Younger (andy@snoogie.demon.co.uk)
+ Extended by Gerd Spalink (Gerd.Spalink@t-online.de)
+ to support recording from the audio input
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#include "winsup.h"
+#include <stdio.h>
+#include <windows.h>
+#include <sys/soundcard.h>
+#include <mmsystem.h>
+#include "cygerrno.h"
+#include "security.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "cygheap.h"
+
+/*------------------------------------------------------------------------
+ Simple encapsulation of the win32 audio device.
+
+ Implementation Notes
+ 1. Audio structures are malloced just before the first read or
+ write to /dev/dsp. The actual buffer size is determined at that time,
+ such that one buffer holds about 125ms of audio data.
+ At the time of this writing, 12 buffers are allocated,
+ so that up to 1.5 seconds can be buffered within Win32.
+ The buffer size can be queried with the ioctl SNDCTL_DSP_GETBLKSIZE,
+ but for this implementation only returns meaningful results if
+ sampling rate, number of channels and number of bits per sample
+ are not changed afterwards.
+ The audio structures are freed when the device is reset or closed,
+ and they are not passed to exec'ed processes.
+ The dev_ member is cleared after a fork. This forces the child
+ to reopen the audio device._
+
+ 2. Every open call creates a new instance of the handler. After a
+ successful open, every subsequent open from the same process
+ to the device fails with EBUSY.
+ The structures are shared between duped handles, but not with
+ children. They only inherit the settings from the parent.
+ */
+
+class fhandler_dev_dsp::Audio
+{ // This class contains functionality common to Audio_in and Audio_out
+ public:
+ Audio ();
+ ~Audio ();
+
+ class queue;
+
+ bool isvalid ();
+ void setconvert (int format);
+ void convert_none (unsigned char *buffer, int size_bytes) { }
+ void convert_U8_S8 (unsigned char *buffer, int size_bytes);
+ void convert_S16LE_U16LE (unsigned char *buffer, int size_bytes);
+ void convert_S16LE_U16BE (unsigned char *buffer, int size_bytes);
+ void convert_S16LE_S16BE (unsigned char *buffer, int size_bytes);
+ void fillFormat (WAVEFORMATEX * format,
+ int rate, int bits, int channels);
+ unsigned blockSize (int rate, int bits, int channels);
+ void (fhandler_dev_dsp::Audio::*convert_)
+ (unsigned char *buffer, int size_bytes);
+
+ enum { MAX_BLOCKS = 12 };
+ int bufferIndex_; // offset into pHdr_->lpData
+ WAVEHDR *pHdr_; // data to be filled by write
+ WAVEHDR wavehdr_[MAX_BLOCKS];
+ char *bigwavebuffer_; // audio samples only
+ // Member variables below must be locked
+ queue *Qisr2app_; // blocks passed from wave callback
+};
+
+class fhandler_dev_dsp::Audio::queue
+{ // non-blocking fixed size queues for buffer management
+ public:
+ queue (int depth = 4);
+ ~queue ();
+
+ bool send (WAVEHDR *); // queue an item, returns true if successful
+ bool recv (WAVEHDR **); // retrieve an item, returns true if successful
+ void reset ();
+ int query (); // return number of items queued
+ inline void lock () { EnterCriticalSection (&lock_); }
+ inline void unlock () { LeaveCriticalSection (&lock_); }
+ inline void dellock () { debug_printf ("Deleting Critical Section"); DeleteCriticalSection (&lock_); }
+ bool isvalid () { return storage_; }
+ private:
+ CRITICAL_SECTION lock_;
+ int head_;
+ int tail_;
+ int depth_;
+ WAVEHDR **storage_;
+};
+
+static void CALLBACK waveOut_callback (HWAVEOUT hWave, UINT msg, DWORD instance,
+ DWORD param1, DWORD param2);
+
+class fhandler_dev_dsp::Audio_out: public Audio
+{
+ public:
+ void fork_fixup (HANDLE parent);
+ bool query (int rate, int bits, int channels);
+ bool start ();
+ void stop (bool immediately = false);
+ bool write (const char *pSampleData, int nBytes);
+ void buf_info (audio_buf_info *p, int rate, int bits, int channels);
+ void callback_sampledone (WAVEHDR *pHdr);
+ bool parsewav (const char *&pData, int &nBytes,
+ int rate, int bits, int channels);
+
+ private:
+ void init (unsigned blockSize);
+ void waitforallsent ();
+ void waitforspace ();
+ bool sendcurrent ();
+
+ enum { MAX_BLOCKS = 12 };
+ HWAVEOUT dev_; // The wave device
+ /* Private copies of audiofreq_, audiobits_, audiochannels_,
+ possibly set from wave file */
+ int freq_;
+ int bits_;
+ int channels_;
+};
+
+static void CALLBACK waveIn_callback (HWAVEIN hWave, UINT msg, DWORD instance,
+ DWORD param1, DWORD param2);
+
+class fhandler_dev_dsp::Audio_in: public Audio
+{
+public:
+ void fork_fixup (HANDLE parent);
+ bool query (int rate, int bits, int channels);
+ bool start (int rate, int bits, int channels);
+ void stop ();
+ bool read (char *pSampleData, int &nBytes);
+ void buf_info (audio_buf_info *p, int rate, int bits, int channels);
+ void callback_blockfull (WAVEHDR *pHdr);
+
+private:
+ bool init (unsigned blockSize);
+ bool queueblock (WAVEHDR *pHdr);
+ void waitfordata (); // blocks until we have a good pHdr_
+
+ HWAVEIN dev_;
+};
+
+/* --------------------------------------------------------------------
+ Implementation */
+
+// Simple fixed length FIFO queue implementation for audio buffer management
+fhandler_dev_dsp::Audio::queue::queue (int depth)
+{
+ // allow space for one extra object in the queue
+ // so we can distinguish full and empty status
+ depth_ = depth;
+ storage_ = new WAVEHDR *[depth_ + 1];
+}
+
+fhandler_dev_dsp::Audio::queue::~queue ()
+{
+ delete[] storage_;
+}
+
+void
+fhandler_dev_dsp::Audio::queue::reset ()
+ {
+ /* When starting, after reset and after fork */
+ head_ = tail_ = 0;
+ debug_printf ("InitializeCriticalSection");
+ memset (&lock_, 0, sizeof (lock_));
+ InitializeCriticalSection (&lock_);
+ }
+
+bool
+fhandler_dev_dsp::Audio::queue::send (WAVEHDR *x)
+{
+ bool res = false;
+ lock ();
+ if (query () == depth_)
+ system_printf ("Queue overflow");
+ else
+ {
+ storage_[tail_] = x;
+ if (++tail_ > depth_)
+ tail_ = 0;
+ res = true;
+ }
+ unlock ();
+ return res;
+}
+
+bool
+fhandler_dev_dsp::Audio::queue::recv (WAVEHDR **x)
+{
+ bool res = false;
+ lock ();
+ if (query () != 0)
+ {
+ *x = storage_[head_];
+ if (++head_ > depth_)
+ head_ = 0;
+ res = true;
+ }
+ unlock ();
+ return res;
+}
+
+int
+fhandler_dev_dsp::Audio::queue::query ()
+{
+ int n = tail_ - head_;
+ if (n < 0)
+ n += depth_ + 1;
+ return n;
+}
+
+// Audio class implements functionality need for both read and write
+fhandler_dev_dsp::Audio::Audio ()
+{
+ bigwavebuffer_ = NULL;
+ Qisr2app_ = new queue (MAX_BLOCKS);
+ convert_ = &fhandler_dev_dsp::Audio::convert_none;
+}
+
+fhandler_dev_dsp::Audio::~Audio ()
+{
+ debug_printf("");
+ delete Qisr2app_;
+ delete[] bigwavebuffer_;
+}
+
+inline bool
+fhandler_dev_dsp::Audio::isvalid ()
+{
+ return bigwavebuffer_ && Qisr2app_ && Qisr2app_->isvalid ();
+}
+
+void
+fhandler_dev_dsp::Audio::setconvert (int format)
+{
+ switch (format)
+ {
+ case AFMT_S8:
+ convert_ = &fhandler_dev_dsp::Audio::convert_U8_S8;
+ debug_printf ("U8_S8");
+ break;
+ case AFMT_U16_LE:
+ convert_ = &fhandler_dev_dsp::Audio::convert_S16LE_U16LE;
+ debug_printf ("S16LE_U16LE");
+ break;
+ case AFMT_U16_BE:
+ convert_ = &fhandler_dev_dsp::Audio::convert_S16LE_U16BE;
+ debug_printf ("S16LE_U16BE");
+ break;
+ case AFMT_S16_BE:
+ convert_ = &fhandler_dev_dsp::Audio::convert_S16LE_S16BE;
+ debug_printf ("S16LE_S16BE");
+ break;
+ default:
+ convert_ = &fhandler_dev_dsp::Audio::convert_none;
+ debug_printf ("none");
+ }
+}
+
+void
+fhandler_dev_dsp::Audio::convert_U8_S8 (unsigned char *buffer,
+ int size_bytes)
+{
+ while (size_bytes-- > 0)
+ {
+ *buffer ^= (unsigned char)0x80;
+ buffer++;
+ }
+}
+
+void
+fhandler_dev_dsp::Audio::convert_S16LE_U16BE (unsigned char *buffer,
+ int size_bytes)
+{
+ int size_samples = size_bytes / 2;
+ unsigned char hi, lo;
+ while (size_samples-- > 0)
+ {
+ hi = buffer[0];
+ lo = buffer[1];
+ *buffer++ = lo;
+ *buffer++ = hi ^ (unsigned char)0x80;
+ }
+}
+
+void
+fhandler_dev_dsp::Audio::convert_S16LE_U16LE (unsigned char *buffer,
+ int size_bytes)
+{
+ int size_samples = size_bytes / 2;
+ while (size_samples-- > 0)
+ {
+ buffer++;
+ *buffer ^= (unsigned char)0x80;
+ buffer++;
+ }
+}
+
+void
+fhandler_dev_dsp::Audio::convert_S16LE_S16BE (unsigned char *buffer,
+ int size_bytes)
+{
+ int size_samples = size_bytes / 2;
+ unsigned char hi, lo;
+ while (size_samples-- > 0)
+ {
+ hi = buffer[0];
+ lo = buffer[1];
+ *buffer++ = lo;
+ *buffer++ = hi;
+ }
+}
+
+void
+fhandler_dev_dsp::Audio::fillFormat (WAVEFORMATEX * format,
+ int rate, int bits, int channels)
+{
+ memset (format, 0, sizeof (*format));
+ format->wFormatTag = WAVE_FORMAT_PCM;
+ format->wBitsPerSample = bits;
+ format->nChannels = channels;
+ format->nSamplesPerSec = rate;
+ format->nAvgBytesPerSec = format->nSamplesPerSec * format->nChannels
+ * (bits / 8);
+ format->nBlockAlign = format->nChannels * (bits / 8);
+}
+
+// calculate a good block size
+unsigned
+fhandler_dev_dsp::Audio::blockSize (int rate, int bits, int channels)
+{
+ unsigned blockSize;
+ blockSize = ((bits / 8) * channels * rate) / 8; // approx 125ms per block
+ // round up to multiple of 64
+ blockSize += 0x3f;
+ blockSize &= ~0x3f;
+ return blockSize;
+}
+
+//=======================================================================
+void
+fhandler_dev_dsp::Audio_out::fork_fixup (HANDLE parent)
+{
+ /* Null dev_.
+ It will be necessary to reset the queue, open the device
+ and create a lock when writing */
+ debug_printf ("parent=0x%08x", parent);
+ dev_ = NULL;
+}
+
+
+bool
+fhandler_dev_dsp::Audio_out::query (int rate, int bits, int channels)
+{
+ WAVEFORMATEX format;
+ MMRESULT rc;
+
+ fillFormat (&format, rate, bits, channels);
+ rc = waveOutOpen (NULL, WAVE_MAPPER, &format, 0L, 0L, WAVE_FORMAT_QUERY);
+ debug_printf ("%d = waveOutOpen (freq=%d bits=%d channels=%d)", rc, rate, bits, channels);
+ return (rc == MMSYSERR_NOERROR);
+}
+
+bool
+fhandler_dev_dsp::Audio_out::start ()
+{
+ WAVEFORMATEX format;
+ MMRESULT rc;
+ unsigned bSize = blockSize (freq_, bits_, channels_);
+
+ if (dev_)
+ return true;
+
+ /* In case of fork bigwavebuffer may already exist */
+ if (!bigwavebuffer_)
+ bigwavebuffer_ = new char[MAX_BLOCKS * bSize];
+
+ if (!isvalid ())
+ return false;
+
+ fillFormat (&format, freq_, bits_, channels_);
+ rc = waveOutOpen (&dev_, WAVE_MAPPER, &format, (DWORD) waveOut_callback,
+ (DWORD) this, CALLBACK_FUNCTION);
+ if (rc == MMSYSERR_NOERROR)
+ init (bSize);
+
+ debug_printf ("%d = waveOutOpen (freq=%d bits=%d channels=%d)", rc, freq_, bits_, channels_);
+
+ return (rc == MMSYSERR_NOERROR);
+}
+
+void
+fhandler_dev_dsp::Audio_out::stop (bool immediately)
+{
+ MMRESULT rc;
+ WAVEHDR *pHdr;
+
+ debug_printf ("dev_=%08x", (int)dev_);
+ if (dev_)
+ {
+ if (!immediately)
+ {
+ sendcurrent (); // force out last block whatever size..
+ waitforallsent (); // block till finished..
+ }
+
+ rc = waveOutReset (dev_);
+ debug_printf ("%d = waveOutReset ()", rc);
+ while (Qisr2app_->recv (&pHdr))
+ {
+ rc = waveOutUnprepareHeader (dev_, pHdr, sizeof (WAVEHDR));
+ debug_printf ("%d = waveOutUnprepareHeader (0x%08x)", rc, pHdr);
+ }
+
+ rc = waveOutClose (dev_);
+ debug_printf ("%d = waveOutClose ()", rc);
+
+ Qisr2app_->dellock ();
+ }
+}
+
+void
+fhandler_dev_dsp::Audio_out::init (unsigned blockSize)
+{
+ int i;
+
+ // internally queue all of our buffer for later use by write
+ Qisr2app_->reset ();
+ for (i = 0; i < MAX_BLOCKS; i++)
+ {
+ wavehdr_[i].lpData = &bigwavebuffer_[i * blockSize];
+ wavehdr_[i].dwUser = (int) blockSize;
+ wavehdr_[i].dwFlags = 0;
+ if (!Qisr2app_->send (&wavehdr_[i]))
+ {
+ system_printf ("Internal Error i=%d", i);
+ break; // should not happen
+ }
+ }
+ pHdr_ = NULL;
+}
+
+bool
+fhandler_dev_dsp::Audio_out::write (const char *pSampleData, int nBytes)
+{
+ while (nBytes != 0)
+ { // Block if all blocks used until at least one is free
+ waitforspace ();
+
+ int sizeleft = (int)pHdr_->dwUser - bufferIndex_;
+ if (nBytes < sizeleft)
+ { // all data fits into the current block, with some space left
+ memcpy (&pHdr_->lpData[bufferIndex_], pSampleData, nBytes);
+ bufferIndex_ += nBytes;
+ break;
+ }
+ else
+ { // data will fill up the current block
+ memcpy (&pHdr_->lpData[bufferIndex_], pSampleData, sizeleft);
+ bufferIndex_ += sizeleft;
+ sendcurrent ();
+ pSampleData += sizeleft;
+ nBytes -= sizeleft;
+ }
+ }
+ return true;
+}
+
+void
+fhandler_dev_dsp::Audio_out::buf_info (audio_buf_info *p,
+ int rate, int bits, int channels)
+{
+ p->fragstotal = MAX_BLOCKS;
+ if (this && dev_)
+ {
+ /* If the device is running we use the internal values,
+ possibly set from the wave file. */
+ p->fragsize = blockSize (freq_, bits_, channels_);
+ p->fragments = Qisr2app_->query ();
+ if (pHdr_ != NULL)
+ p->bytes = (int)pHdr_->dwUser - bufferIndex_
+ + p->fragsize * p->fragments;
+ else
+ p->bytes = p->fragsize * p->fragments;
+ }
+ else
+ {
+ p->fragsize = blockSize (rate, bits, channels);
+ p->fragments = MAX_BLOCKS;
+ p->bytes = p->fragsize * p->fragments;
+ }
+}
+
+/* This is called on an interupt so use locking.. Note Qisr2app_
+ is used so we should wrap all references to it in locks. */
+inline void
+fhandler_dev_dsp::Audio_out::callback_sampledone (WAVEHDR *pHdr)
+{
+ Qisr2app_->send (pHdr);
+}
+
+void
+fhandler_dev_dsp::Audio_out::waitforspace ()
+{
+ WAVEHDR *pHdr;
+ MMRESULT rc = WAVERR_STILLPLAYING;
+
+ if (pHdr_ != NULL)
+ return;
+ while (!Qisr2app_->recv (&pHdr))
+ {
+ debug_printf ("100ms");
+ Sleep (100);
+ }
+ if (pHdr->dwFlags)
+ {
+ /* Errors are ignored here. They will probbaly cause a failure
+ in the subsequent PrepareHeader */
+ rc = waveOutUnprepareHeader (dev_, pHdr, sizeof (WAVEHDR));
+ debug_printf ("%d = waveOutUnprepareHeader (0x%08x)", rc, pHdr);
+ }
+ pHdr_ = pHdr;
+ bufferIndex_ = 0;
+}
+
+void
+fhandler_dev_dsp::Audio_out::waitforallsent ()
+{
+ while (Qisr2app_->query () != MAX_BLOCKS)
+ {
+ debug_printf ("%d blocks in Qisr2app", Qisr2app_->query ());
+ Sleep (100);
+ }
+}
+
+// send the block described by pHdr_ and bufferIndex_ to wave device
+bool
+fhandler_dev_dsp::Audio_out::sendcurrent ()
+{
+ WAVEHDR *pHdr = pHdr_;
+ MMRESULT rc;
+ debug_printf ("pHdr=0x%08x bytes=%d", pHdr, bufferIndex_);
+
+ if (pHdr_ == NULL)
+ return false;
+ pHdr_ = NULL;
+
+ // Sample buffer conversion
+ (this->*convert_) ((unsigned char *)pHdr->lpData, bufferIndex_);
+
+ // Send internal buffer out to the soundcard
+ pHdr->dwBufferLength = bufferIndex_;
+ rc = waveOutPrepareHeader (dev_, pHdr, sizeof (WAVEHDR));
+ debug_printf ("%d = waveOutPrepareHeader (0x%08x)", rc, pHdr);
+ if (rc == MMSYSERR_NOERROR)
+ {
+ rc = waveOutWrite (dev_, pHdr, sizeof (WAVEHDR));
+ debug_printf ("%d = waveOutWrite (0x%08x)", rc, pHdr);
+ }
+ if (rc == MMSYSERR_NOERROR)
+ return true;
+
+ /* FIXME: Should we return an error instead ?*/
+ pHdr->dwFlags = 0; /* avoid calling UnprepareHeader again */
+ Qisr2app_->send (pHdr);
+ return false;
+}
+
+//------------------------------------------------------------------------
+// Call back routine
+static void CALLBACK
+waveOut_callback (HWAVEOUT hWave, UINT msg, DWORD instance, DWORD param1,
+ DWORD param2)
+{
+ if (msg == WOM_DONE)
+ {
+ fhandler_dev_dsp::Audio_out *ptr =
+ (fhandler_dev_dsp::Audio_out *) instance;
+ ptr->callback_sampledone ((WAVEHDR *) param1);
+ }
+}
+
+//------------------------------------------------------------------------
+// wav file detection..
+#pragma pack(1)
+struct wavchunk
+{
+ char id[4];
+ unsigned int len;
+};
+struct wavformat
+{
+ unsigned short wFormatTag;
+ unsigned short wChannels;
+ unsigned int dwSamplesPerSec;
+ unsigned int dwAvgBytesPerSec;
+ unsigned short wBlockAlign;
+ unsigned short wBitsPerSample;
+};
+#pragma pack()
+
+bool
+fhandler_dev_dsp::Audio_out::parsewav (const char * &pData, int &nBytes,
+ int dev_freq, int dev_bits, int dev_channels)
+{
+ int len;
+ const char *end = pData + nBytes;
+ const char *pDat;
+ int skip = 0;
+
+ /* Start with default values from the device handler */
+ freq_ = dev_freq;
+ bits_ = dev_bits;
+ channels_ = dev_channels;
+ setconvert (bits_ == 8 ? AFMT_U8 : AFMT_S16_LE);
+
+ // Check alignment first: A lot of the code below depends on it
+ if (((int)pData & 0x3) != 0)
+ return false;
+ if (!(pData[0] == 'R' && pData[1] == 'I'
+ && pData[2] == 'F' && pData[3] == 'F'))
+ return false;
+ if (!(pData[8] == 'W' && pData[9] == 'A'
+ && pData[10] == 'V' && pData[11] == 'E'))
+ return false;
+
+ len = *(int *) &pData[4];
+ len -= 12;
+ pDat = pData + 12;
+ skip = 12;
+ while ((len > 0) && (pDat + sizeof (wavchunk) < end))
+ { /* We recognize two kinds of wavchunk:
+ "fmt " for the PCM parameters (only PCM supported here)
+ "data" for the start of PCM data */
+ wavchunk * pChunk = (wavchunk *) pDat;
+ int blklen = pChunk-> len;
+ if (pChunk->id[0] == 'f' && pChunk->id[1] == 'm'
+ && pChunk->id[2] == 't' && pChunk->id[3] == ' ')
+ {
+ wavformat *format = (wavformat *) (pChunk + 1);
+ if ((char *) (format + 1) >= end)
+ return false;
+ // We have found the parameter chunk
+ if (format->wFormatTag == 0x0001)
+ { // Micr*s*ft PCM; check if parameters work with our device
+ if (query (format->dwSamplesPerSec, format->wBitsPerSample,
+ format->wChannels))
+ { // return the parameters we found
+ freq_ = format->dwSamplesPerSec;
+ bits_ = format->wBitsPerSample;
+ channels_ = format->wChannels;
+ }
+ }
+ }
+ else
+ {
+ if (pChunk->id[0] == 'd' && pChunk->id[1] == 'a'
+ && pChunk->id[2] == 't' && pChunk->id[3] == 'a')
+ { // throw away all the header & not output it to the soundcard.
+ skip += sizeof (wavchunk);
+ debug_printf ("Discard %d bytes wave header", skip);
+ pData += skip;
+ nBytes -= skip;
+ setconvert (bits_ == 8 ? AFMT_U8 : AFMT_S16_LE);
+ return true;
+ }
+ }
+ pDat += blklen + sizeof (wavchunk);
+ skip += blklen + sizeof (wavchunk);
+ len -= blklen + sizeof (wavchunk);
+ }
+ return false;
+}
+
+/* ========================================================================
+ Buffering concept for Audio_in:
+ On the first read, we queue all blocks of our bigwavebuffer
+ for reception and start the wave-in device.
+ We manage queues of pointers to WAVEHDR
+ When a block has been filled, the callback puts the corresponding
+ WAVEHDR pointer into a queue.
+ The function read() blocks (polled, sigh) until at least one good buffer
+ has arrived, then the data is copied into the buffer provided to read().
+ After a buffer has been fully used by read(), it is queued again
+ to the wave-in device immediately.
+ The function read() iterates until all data requested has been
+ received, there is no way to interrupt it */
+
+void
+fhandler_dev_dsp::Audio_in::fork_fixup (HANDLE parent)
+{
+ /* Null dev_.
+ It will be necessary to reset the queue, open the device
+ and create a lock when reading */
+ debug_printf ("parent=0x%08x", parent);
+ dev_ = NULL;
+}
+
+bool
+fhandler_dev_dsp::Audio_in::query (int rate, int bits, int channels)
+{
+ WAVEFORMATEX format;
+ MMRESULT rc;
+
+ fillFormat (&format, rate, bits, channels);
+ rc = waveInOpen (NULL, WAVE_MAPPER, &format, 0L, 0L, WAVE_FORMAT_QUERY);
+ debug_printf ("%d = waveInOpen (freq=%d bits=%d channels=%d)", rc, rate, bits, channels);
+ return (rc == MMSYSERR_NOERROR);
+}
+
+bool
+fhandler_dev_dsp::Audio_in::start (int rate, int bits, int channels)
+{
+ WAVEFORMATEX format;
+ MMRESULT rc;
+ unsigned bSize = blockSize (rate, bits, channels);
+
+ if (dev_)
+ return true;
+
+ /* In case of fork bigwavebuffer may already exist */
+ if (!bigwavebuffer_)
+ bigwavebuffer_ = new char[MAX_BLOCKS * bSize];
+
+ if (!isvalid ())
+ return false;
+
+ fillFormat (&format, rate, bits, channels);
+ rc = waveInOpen (&dev_, WAVE_MAPPER, &format, (DWORD) waveIn_callback,
+ (DWORD) this, CALLBACK_FUNCTION);
+ debug_printf ("%d = waveInOpen (rate=%d bits=%d channels=%d)", rc, rate, bits, channels);
+
+ if (rc == MMSYSERR_NOERROR)
+ {
+ if (!init (bSize))
+ return false;
+ }
+ return (rc == MMSYSERR_NOERROR);
+}
+
+void
+fhandler_dev_dsp::Audio_in::stop ()
+{
+ MMRESULT rc;
+ WAVEHDR *pHdr;
+
+ debug_printf ("dev_=%08x", (int)dev_);
+ if (dev_)
+ {
+ /* Note that waveInReset calls our callback for all incomplete buffers.
+ Since all the win32 wave functions appear to use a common lock,
+ we must not call into the wave API from the callback.
+ Otherwise we end up in a deadlock. */
+ rc = waveInReset (dev_);
+ debug_printf ("%d = waveInReset ()", rc);
+
+ while (Qisr2app_->recv (&pHdr))
+ {
+ rc = waveInUnprepareHeader (dev_, pHdr, sizeof (WAVEHDR));
+ debug_printf ("%d = waveInUnprepareHeader (0x%08x)", rc, pHdr);
+ }
+
+ rc = waveInClose (dev_);
+ debug_printf ("%d = waveInClose ()", rc);
+
+ Qisr2app_->dellock ();
+ }
+}
+
+bool
+fhandler_dev_dsp::Audio_in::queueblock (WAVEHDR *pHdr)
+{
+ MMRESULT rc;
+ rc = waveInPrepareHeader (dev_, pHdr, sizeof (WAVEHDR));
+ debug_printf ("%d = waveInPrepareHeader (0x%08x)", rc, pHdr);
+ if (rc == MMSYSERR_NOERROR)
+ {
+ rc = waveInAddBuffer (dev_, pHdr, sizeof (WAVEHDR));
+ debug_printf ("%d = waveInAddBuffer (0x%08x)", rc, pHdr);
+ }
+ if (rc == MMSYSERR_NOERROR)
+ return true;
+
+ /* FIXME: Should the calling function return an error instead ?*/
+ pHdr->dwFlags = 0; /* avoid calling UnprepareHeader again */
+ pHdr->dwBytesRecorded = 0; /* no data will have been read */
+ Qisr2app_->send (pHdr);
+ return false;
+}
+
+bool
+fhandler_dev_dsp::Audio_in::init (unsigned blockSize)
+{
+ MMRESULT rc;
+ int i;
+
+ // try to queue all of our buffer for reception
+ Qisr2app_->reset ();
+ for (i = 0; i < MAX_BLOCKS; i++)
+ {
+ wavehdr_[i].lpData = &bigwavebuffer_[i * blockSize];
+ wavehdr_[i].dwBufferLength = blockSize;
+ wavehdr_[i].dwFlags = 0;
+ if (!queueblock (&wavehdr_[i]))
+ break;
+ }
+ pHdr_ = NULL;
+ rc = waveInStart (dev_);
+ debug_printf ("%d = waveInStart (), queued=%d", rc, i);
+ return (rc == MMSYSERR_NOERROR);
+}
+
+bool
+fhandler_dev_dsp::Audio_in::read (char *pSampleData, int &nBytes)
+{
+ int bytes_to_read = nBytes;
+ nBytes = 0;
+ debug_printf ("pSampleData=%08x nBytes=%d", pSampleData, bytes_to_read);
+ while (bytes_to_read != 0)
+ { // Block till next sound has been read
+ waitfordata ();
+
+ // Handle gathering our blocks into smaller or larger buffer
+ int sizeleft = pHdr_->dwBytesRecorded - bufferIndex_;
+ if (bytes_to_read < sizeleft)
+ { // The current buffer holds more data than requested
+ memcpy (pSampleData, &pHdr_->lpData[bufferIndex_], bytes_to_read);
+ (this->*convert_) ((unsigned char *)pSampleData, bytes_to_read);
+ nBytes += bytes_to_read;
+ bufferIndex_ += bytes_to_read;
+ debug_printf ("got %d", bytes_to_read);
+ break; // done; use remaining data in next call to read
+ }
+ else
+ { // not enough or exact amount in the current buffer
+ if (sizeleft)
+ { // use up what we have
+ memcpy (pSampleData, &pHdr_->lpData[bufferIndex_], sizeleft);
+ (this->*convert_) ((unsigned char *)pSampleData, sizeleft);
+ nBytes += sizeleft;
+ bytes_to_read -= sizeleft;
+ pSampleData += sizeleft;
+ debug_printf ("got %d", sizeleft);
+ }
+ queueblock (pHdr_); // re-queue this block to ISR
+ pHdr_ = NULL; // need to wait for a new block
+ // if more samples are needed, we need a new block now
+ }
+ }
+ debug_printf ("end nBytes=%d", nBytes);
+ return true;
+}
+
+void
+fhandler_dev_dsp::Audio_in::waitfordata ()
+{
+ WAVEHDR *pHdr;
+ MMRESULT rc;
+
+ if (pHdr_ != NULL)
+ return;
+ while (!Qisr2app_->recv (&pHdr))
+ {
+ debug_printf ("100ms");
+ Sleep (100);
+ }
+ if (pHdr->dwFlags) /* Zero if queued following error in queueblock */
+ {
+ /* Errors are ignored here. They will probbaly cause a failure
+ in the subsequent PrepareHeader */
+ rc = waveInUnprepareHeader (dev_, pHdr, sizeof (WAVEHDR));
+ debug_printf ("%d = waveInUnprepareHeader (0x%08x)", rc, pHdr);
+ }
+ pHdr_ = pHdr;
+ bufferIndex_ = 0;
+}
+
+void
+fhandler_dev_dsp::Audio_in::buf_info (audio_buf_info *p,
+ int rate, int bits, int channels)
+{
+ p->fragstotal = MAX_BLOCKS;
+ p->fragsize = blockSize (rate, bits, channels);
+ if (this && dev_)
+ {
+ p->fragments = Qisr2app_->query ();
+ if (pHdr_ != NULL)
+ p->bytes = pHdr_->dwBytesRecorded - bufferIndex_
+ + p->fragsize * p->fragments;
+ else
+ p->bytes = p->fragsize * p->fragments;
+ }
+ else
+ {
+ p->fragments = 0;
+ p->bytes = 0;
+ }
+}
+
+inline void
+fhandler_dev_dsp::Audio_in::callback_blockfull (WAVEHDR *pHdr)
+{
+ Qisr2app_->send (pHdr);
+}
+
+static void CALLBACK
+waveIn_callback (HWAVEIN hWave, UINT msg, DWORD instance, DWORD param1,
+ DWORD param2)
+{
+ if (msg == WIM_DATA)
+ {
+ fhandler_dev_dsp::Audio_in *ptr =
+ (fhandler_dev_dsp::Audio_in *) instance;
+ ptr->callback_blockfull ((WAVEHDR *) param1);
+ }
+}
+
+
+/* ------------------------------------------------------------------------
+ /dev/dsp handler
+ ------------------------------------------------------------------------ */
+fhandler_dev_dsp::fhandler_dev_dsp ():
+ fhandler_base ()
+{
+ debug_printf ("0x%08x", (int)this);
+ audio_in_ = NULL;
+ audio_out_ = NULL;
+}
+
+int
+fhandler_dev_dsp::open (int flags, mode_t mode)
+{
+ if (cygheap->fdtab.find_archetype (dev ()))
+ {
+ set_errno (EBUSY);
+ return 0;
+ }
+ int err = 0;
+ UINT num_in = 0, num_out = 0;
+ set_flags ((flags & ~O_TEXT) | O_BINARY);
+ // Work out initial sample format & frequency, /dev/dsp defaults
+ audioformat_ = AFMT_U8;
+ audiofreq_ = 8000;
+ audiobits_ = 8;
+ audiochannels_ = 1;
+ switch (flags & O_ACCMODE)
+ {
+ case O_RDWR:
+ if ((num_in = waveInGetNumDevs ()) == 0)
+ err = ENXIO;
+ /* Fall through */
+ case O_WRONLY:
+ if ((num_out = waveOutGetNumDevs ()) == 0)
+ err = ENXIO;
+ break;
+ case O_RDONLY:
+ if ((num_in = waveInGetNumDevs ()) == 0)
+ err = ENXIO;
+ break;
+ default:
+ err = EINVAL;
+ }
+
+ if (!err)
+ {
+ set_open_status ();
+ need_fork_fixup (true);
+ nohandle (true);
+
+ // FIXME: Do this better someday
+ fhandler_dev_dsp *arch = (fhandler_dev_dsp *) cmalloc (HEAP_ARCHETYPES, sizeof (*this));
+ archetype = arch;
+ *((fhandler_dev_dsp **) cygheap->fdtab.add_archetype ()) = arch;
+ *arch = *this;
+ archetype->usecount = 1;
+ }
+ else
+ set_errno (err);
+
+ debug_printf ("ACCMODE=0x%08x audio_in=%d audio_out=%d, err=%d",
+ flags & O_ACCMODE, num_in, num_out, err);
+ return !err;
+}
+
+#define IS_WRITE() ((get_flags() & O_ACCMODE) != O_RDONLY)
+#define IS_READ() ((get_flags() & O_ACCMODE) != O_WRONLY)
+
+int
+fhandler_dev_dsp::write (const void *ptr, size_t len)
+{
+ debug_printf ("ptr=%08x len=%d", ptr, len);
+ if ((fhandler_dev_dsp *) archetype != this)
+ return ((fhandler_dev_dsp *)archetype)->write(ptr, len);
+
+ int len_s = len;
+ const char *ptr_s = static_cast <const char *> (ptr);
+
+ if (!audio_out_)
+ if (IS_WRITE ())
+ {
+ debug_printf ("Allocating");
+ if (!(audio_out_ = new Audio_out))
+ return -1;
+
+ /* check for wave file & get parameters & skip header if possible. */
+
+ if (audio_out_->parsewav (ptr_s, len_s,
+ audiofreq_, audiobits_, audiochannels_))
+ debug_printf ("=> ptr_s=%08x len_s=%d", ptr_s, len_s);
+ }
+ else
+ {
+ set_errno (EBADF); // device was opened for read?
+ return -1;
+ }
+
+ /* Open audio device properly with callbacks.
+ Private parameters were set in call to parsewav.
+ This is a no-op when there are successive writes in the same process */
+ if (!audio_out_->start ())
+ {
+ set_errno (EIO);
+ return -1;
+ }
+
+ audio_out_->write (ptr_s, len_s);
+ return len;
+}
+
+void __stdcall
+fhandler_dev_dsp::read (void *ptr, size_t& len)
+{
+ debug_printf ("ptr=%08x len=%d", ptr, len);
+ if ((fhandler_dev_dsp *) archetype != this)
+ return ((fhandler_dev_dsp *)archetype)->read(ptr, len);
+
+ if (!audio_in_)
+ if (IS_READ ())
+ {
+ debug_printf ("Allocating");
+ if (!(audio_in_ = new Audio_in))
+ {
+ len = (size_t)-1;
+ return;
+ }
+ audio_in_->setconvert (audioformat_);
+ }
+ else
+ {
+ len = (size_t)-1;
+ set_errno (EBADF); // device was opened for write?
+ return;
+ }
+
+ /* Open audio device properly with callbacks.
+ This is a noop when there are successive reads in the same process */
+ if (!audio_in_->start (audiofreq_, audiobits_, audiochannels_))
+ {
+ len = (size_t)-1;
+ set_errno (EIO);
+ return;
+ }
+
+ audio_in_->read ((char *)ptr, (int&)len);
+}
+
+_off64_t
+fhandler_dev_dsp::lseek (_off64_t offset, int whence)
+{
+ return 0;
+}
+
+void
+fhandler_dev_dsp::close_audio_in ()
+{
+ if (audio_in_)
+ {
+ audio_in_->stop ();
+ delete audio_in_;
+ audio_in_ = NULL;
+ }
+}
+
+void
+fhandler_dev_dsp::close_audio_out (bool immediately)
+{
+ if (audio_out_)
+ {
+ audio_out_->stop (immediately);
+ delete audio_out_;
+ audio_out_ = NULL;
+ }
+}
+
+int
+fhandler_dev_dsp::close ()
+{
+ debug_printf ("audio_in=%08x audio_out=%08x",
+ (int)audio_in_, (int)audio_out_);
+ if (!hExeced)
+ {
+ if ((fhandler_dev_dsp *) archetype != this)
+ return ((fhandler_dev_dsp *) archetype)->close ();
+
+ if (--usecount == 0)
+ {
+ close_audio_in ();
+ close_audio_out (exit_state != ES_NOT_EXITING);
+ }
+ }
+ return 0;
+}
+
+int
+fhandler_dev_dsp::dup (fhandler_base * child)
+{
+ debug_printf ("");
+ child->archetype = archetype;
+ child->set_flags (get_flags ());
+ archetype->usecount++;
+ return 0;
+}
+
+int
+fhandler_dev_dsp::ioctl (unsigned int cmd, void *ptr)
+{
+ debug_printf ("audio_in=%08x audio_out=%08x",
+ (int)audio_in_, (int)audio_out_);
+ if ((fhandler_dev_dsp *) archetype != this)
+ return ((fhandler_dev_dsp *)archetype)->ioctl(cmd, ptr);
+
+ int *intptr = (int *) ptr;
+ switch (cmd)
+ {
+#define CASE(a) case a : debug_printf ("/dev/dsp: ioctl %s", #a);
+
+ CASE (SNDCTL_DSP_RESET)
+ close_audio_in ();
+ close_audio_out (true);
+ return 0;
+ break;
+
+ CASE (SNDCTL_DSP_GETBLKSIZE)
+ /* This is valid even if audio_X is NULL */
+ if (IS_WRITE ())
+ {
+ *intptr = audio_out_->blockSize (audiofreq_,
+ audiobits_,
+ audiochannels_);
+ }
+ else
+ { // I am very sure that IS_READ is valid
+ *intptr = audio_in_->blockSize (audiofreq_,
+ audiobits_,
+ audiochannels_);
+ }
+ return 0;
+
+ CASE (SNDCTL_DSP_SETFMT)
+ {
+ int nBits;
+ switch (*intptr)
+ {
+ case AFMT_QUERY:
+ *intptr = audioformat_;
+ return 0;
+ break;
+ case AFMT_U16_BE:
+ case AFMT_U16_LE:
+ case AFMT_S16_BE:
+ case AFMT_S16_LE:
+ nBits = 16;
+ break;
+ case AFMT_U8:
+ case AFMT_S8:
+ nBits = 8;
+ break;
+ default:
+ nBits = 0;
+ }
+ if (nBits && IS_WRITE ())
+ {
+ close_audio_out ();
+ if (audio_out_->query (audiofreq_, nBits, audiochannels_))
+ {
+ audiobits_ = nBits;
+ audioformat_ = *intptr;
+ }
+ else
+ {
+ *intptr = audiobits_;
+ return -1;
+ }
+ }
+ if (nBits && IS_READ ())
+ {
+ close_audio_in ();
+ if (audio_in_->query (audiofreq_, nBits, audiochannels_))
+ {
+ audiobits_ = nBits;
+ audioformat_ = *intptr;
+ }
+ else
+ {
+ *intptr = audiobits_;
+ return -1;
+ }
+ }
+ return 0;
+ }
+
+ CASE (SNDCTL_DSP_SPEED)
+ if (IS_WRITE ())
+ {
+ close_audio_out ();
+ if (audio_out_->query (*intptr, audiobits_, audiochannels_))
+ audiofreq_ = *intptr;
+ else
+ {
+ *intptr = audiofreq_;
+ return -1;
+ }
+ }
+ if (IS_READ ())
+ {
+ close_audio_in ();
+ if (audio_in_->query (*intptr, audiobits_, audiochannels_))
+ audiofreq_ = *intptr;
+ else
+ {
+ *intptr = audiofreq_;
+ return -1;
+ }
+ }
+ return 0;
+
+ CASE (SNDCTL_DSP_STEREO)
+ {
+ int nChannels = *intptr + 1;
+ int res = ioctl (SNDCTL_DSP_CHANNELS, &nChannels);
+ *intptr = nChannels - 1;
+ return res;
+ }
+
+ CASE (SNDCTL_DSP_CHANNELS)
+ {
+ int nChannels = *intptr;
+
+ if (IS_WRITE ())
+ {
+ close_audio_out ();
+ if (audio_out_->query (audiofreq_, audiobits_, nChannels))
+ audiochannels_ = nChannels;
+ else
+ {
+ *intptr = audiochannels_;
+ return -1;
+ }
+ }
+ if (IS_READ ())
+ {
+ close_audio_in ();
+ if (audio_in_->query (audiofreq_, audiobits_, nChannels))
+ audiochannels_ = nChannels;
+ else
+ {
+ *intptr = audiochannels_;
+ return -1;
+ }
+ }
+ return 0;
+ }
+
+ CASE (SNDCTL_DSP_GETOSPACE)
+ {
+ if (!IS_WRITE ())
+ {
+ set_errno(EBADF);
+ return -1;
+ }
+ audio_buf_info *p = (audio_buf_info *) ptr;
+ audio_out_->buf_info (p, audiofreq_, audiobits_, audiochannels_);
+ debug_printf ("ptr=%p frags=%d fragsize=%d bytes=%d",
+ ptr, p->fragments, p->fragsize, p->bytes);
+ return 0;
+ }
+
+ CASE (SNDCTL_DSP_GETISPACE)
+ {
+ if (!IS_READ ())
+ {
+ set_errno(EBADF);
+ return -1;
+ }
+ audio_buf_info *p = (audio_buf_info *) ptr;
+ audio_in_->buf_info (p, audiofreq_, audiobits_, audiochannels_);
+ debug_printf ("ptr=%p frags=%d fragsize=%d bytes=%d",
+ ptr, p->fragments, p->fragsize, p->bytes);
+ return 0;
+ }
+
+ CASE (SNDCTL_DSP_SETFRAGMENT)
+ // Fake!! esound & mikmod require this on non PowerPC platforms.
+ //
+ return 0;
+
+ CASE (SNDCTL_DSP_GETFMTS)
+ *intptr = AFMT_S16_LE | AFMT_U8; // only native formats returned here
+ return 0;
+
+ CASE (SNDCTL_DSP_GETCAPS)
+ *intptr = DSP_CAP_BATCH | DSP_CAP_DUPLEX;
+ return 0;
+
+ CASE (SNDCTL_DSP_POST)
+ CASE (SNDCTL_DSP_SYNC)
+ // Stop audio out device
+ close_audio_out ();
+ // Stop audio in device
+ close_audio_in ();
+ return 0;
+
+ default:
+ debug_printf ("/dev/dsp: ioctl 0x%08x not handled yet! FIXME:", cmd);
+ break;
+
+#undef CASE
+ };
+ set_errno (EINVAL);
+ return -1;
+}
+
+void
+fhandler_dev_dsp::fixup_after_fork (HANDLE parent)
+{ // called from new child process
+ debug_printf ("audio_in=%08x audio_out=%08x",
+ (int)audio_in_, (int)audio_out_);
+ if (archetype != this)
+ return ((fhandler_dev_dsp *)archetype)->fixup_after_fork (parent);
+
+ if (audio_in_)
+ audio_in_ ->fork_fixup (parent);
+ if (audio_out_)
+ audio_out_->fork_fixup (parent);
+}
+
+void
+fhandler_dev_dsp::fixup_after_exec ()
+{
+ debug_printf ("audio_in=%08x audio_out=%08x, close_on_exec %d",
+ (int) audio_in_, (int) audio_out_, close_on_exec ());
+ if (!close_on_exec ())
+ {
+ if (archetype != this)
+ return ((fhandler_dev_dsp *) archetype)->fixup_after_exec ();
+
+ audio_in_ = NULL;
+ audio_out_ = NULL;
+ }
+}
diff --git a/winsup/cygwin/fhandler_tty.cc b/winsup/cygwin/fhandler_tty.cc
new file mode 100644
index 00000000000..316e30959f7
--- /dev/null
+++ b/winsup/cygwin/fhandler_tty.cc
@@ -0,0 +1,1495 @@
+/* fhandler_tty.cc
+
+ Copyright 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
+ 2006 Red Hat, Inc.
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#include "winsup.h"
+#include <wingdi.h>
+#include <winuser.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <limits.h>
+#include <cygwin/kd.h>
+#include "cygerrno.h"
+#include "security.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "sigproc.h"
+#include "pinfo.h"
+#include "cygheap.h"
+#include "shared_info.h"
+#include "cygserver.h"
+#include "cygthread.h"
+#include "child_info.h"
+
+/* tty master stuff */
+
+fhandler_tty_master NO_COPY *tty_master;
+
+static DWORD WINAPI process_input (void *); // Input queue thread
+static DWORD WINAPI process_output (void *); // Output queue thread
+static DWORD WINAPI process_ioctl (void *); // Ioctl requests thread
+
+fhandler_tty_master::fhandler_tty_master ()
+ : fhandler_pty_master (), console (NULL)
+{
+}
+
+int
+fhandler_tty_slave::get_unit ()
+{
+ return dev () == FH_TTY ? myself->ctty : dev ().minor;
+}
+
+void
+fhandler_tty_master::set_winsize (bool sendSIGWINCH)
+{
+ winsize w;
+ console->ioctl (TIOCGWINSZ, &w);
+ get_ttyp ()->winsize = w;
+ if (sendSIGWINCH)
+ tc->kill_pgrp (SIGWINCH);
+}
+
+int
+fhandler_tty_master::init ()
+{
+ termios_printf ("Creating master for tty%d", get_unit ());
+
+ if (init_console ())
+ {
+ termios_printf ("can't create fhandler");
+ return -1;
+ }
+
+ termios ti;
+ memset (&ti, 0, sizeof (ti));
+ console->tcsetattr (0, &ti);
+
+ if (!setup (false))
+ return 1;
+
+ set_winsize (false);
+
+ set_close_on_exec (true);
+
+ cygthread *h;
+ h = new cygthread (process_input, 0, cygself, "ttyin");
+ h->SetThreadPriority (THREAD_PRIORITY_HIGHEST);
+ h->zap_h ();
+
+ h = new cygthread (process_ioctl, 0, cygself, "ttyioctl");
+ h->SetThreadPriority (THREAD_PRIORITY_HIGHEST);
+ h->zap_h ();
+
+ h = new cygthread (process_output, 0, cygself, "ttyout");
+ h->SetThreadPriority (THREAD_PRIORITY_HIGHEST);
+ h->zap_h ();
+
+ return 0;
+}
+
+#ifdef DEBUGGING
+static class mutex_stack
+{
+public:
+ const char *fn;
+ int ln;
+ const char *tname;
+} ostack[100];
+
+static int osi;
+#endif /*DEBUGGING*/
+
+DWORD
+fhandler_tty_common::__acquire_output_mutex (const char *fn, int ln,
+ DWORD ms)
+{
+ if (strace.active ())
+ strace.prntf (_STRACE_TERMIOS, fn, "(%d): tty output_mutex: waiting %d ms", ln, ms);
+ DWORD res = WaitForSingleObject (output_mutex, ms);
+ if (res == WAIT_OBJECT_0)
+ {
+#ifndef DEBUGGING
+ if (strace.active ())
+ strace.prntf (_STRACE_TERMIOS, fn, "(%d): tty output_mutex: acquired", ln, res);
+#else
+ ostack[osi].fn = fn;
+ ostack[osi].ln = ln;
+ ostack[osi].tname = cygthread::name ();
+ termios_printf ("acquired for %s:%d, osi %d", fn, ln, osi);
+ osi++;
+#endif
+ }
+ return res;
+}
+
+void
+fhandler_tty_common::__release_output_mutex (const char *fn, int ln)
+{
+ if (ReleaseMutex (output_mutex))
+ {
+#ifndef DEBUGGING
+ if (strace.active ())
+ strace.prntf (_STRACE_TERMIOS, fn, "(%d): tty output_mutex released", ln);
+#else
+ if (osi > 0)
+ osi--;
+ termios_printf ("released at %s:%d, osi %d", fn, ln, osi);
+ termios_printf (" for %s:%d (%s)", ostack[osi].fn, ostack[osi].ln, ostack[osi].tname);
+ ostack[osi].ln = -ln;
+#endif
+ }
+#ifdef DEBUGGING
+ else if (osi > 0)
+ {
+ system_printf ("couldn't release output mutex but we seem to own it, %E");
+ try_to_debug ();
+ }
+#endif
+}
+
+/* Process tty input. */
+
+void
+fhandler_pty_master::doecho (const void *str, DWORD len)
+{
+ acquire_output_mutex (INFINITE);
+ if (!WriteFile (to_master, str, len, &len, NULL))
+ termios_printf ("Write to %p failed, %E", to_master);
+// WaitForSingleObject (output_done_event, INFINITE);
+ release_output_mutex ();
+}
+
+int
+fhandler_pty_master::accept_input ()
+{
+ DWORD bytes_left;
+ int ret = 1;
+
+ WaitForSingleObject (input_mutex, INFINITE);
+
+ bytes_left = eat_readahead (-1);
+
+ if (!bytes_left)
+ {
+ termios_printf ("sending EOF to slave");
+ get_ttyp ()->read_retval = 0;
+ }
+ else
+ {
+ char *p = rabuf;
+ DWORD rc;
+ DWORD written = 0;
+
+ termios_printf ("about to write %d chars to slave", bytes_left);
+ rc = WriteFile (get_output_handle (), p, bytes_left, &written, NULL);
+ if (!rc)
+ {
+ debug_printf ("error writing to pipe %E");
+ get_ttyp ()->read_retval = -1;
+ ret = -1;
+ }
+ else
+ {
+ get_ttyp ()->read_retval = 1;
+ p += written;
+ bytes_left -= written;
+ if (bytes_left > 0)
+ {
+ debug_printf ("to_slave pipe is full");
+ puts_readahead (p, bytes_left);
+ ret = 0;
+ }
+ }
+ }
+
+ SetEvent (input_available_event);
+ ReleaseMutex (input_mutex);
+ return ret;
+}
+
+static DWORD WINAPI
+process_input (void *)
+{
+ char rawbuf[INP_BUFFER_SIZE];
+
+ while (1)
+ {
+ size_t nraw = INP_BUFFER_SIZE;
+ tty_master->console->read ((void *) rawbuf, nraw);
+ if (tty_master->line_edit (rawbuf, nraw, tty_master->get_ttyp ()->ti)
+ == line_edit_signalled)
+ tty_master->console->eat_readahead (-1);
+ }
+}
+
+bool
+fhandler_pty_master::hit_eof ()
+{
+ if (get_ttyp ()->was_opened && !get_ttyp ()->slave_alive ())
+ {
+ /* We have the only remaining open handle to this pty, and
+ the slave pty has been opened at least once. We treat
+ this as EOF. */
+ termios_printf ("all other handles closed");
+ return 1;
+ }
+ return 0;
+}
+
+/* Process tty output requests */
+
+int
+fhandler_pty_master::process_slave_output (char *buf, size_t len, int pktmode_on)
+{
+ size_t rlen;
+ char outbuf[OUT_BUFFER_SIZE + 1];
+ DWORD n;
+ int column = 0;
+ int rc = 0;
+
+ if (len == 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)
+ buf[0] = '\n';
+ need_nl = 0;
+ rc = 1;
+ goto out;
+ }
+
+
+ for (;;)
+ {
+ /* Set RLEN to the number of bytes to read from the pipe. */
+ rlen = len;
+ 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;
+
+ HANDLE handle = get_io_handle ();
+
+ n = 0; // get_readahead_into_buffer (outbuf, len);
+ if (!n)
+ {
+ /* Doing a busy wait like this is quite inefficient, but nothing
+ else seems to work completely. Windows should provide some sort
+ of overlapped I/O for pipes, or something, but it doesn't. */
+ while (1)
+ {
+ if (!PeekNamedPipe (handle, NULL, 0, NULL, &n, NULL))
+ goto err;
+ if (n > 0)
+ break;
+ if (hit_eof ())
+ goto out;
+ /* DISCARD (FLUSHO) and tcflush can finish here. */
+ if (n == 0 && (get_ttyp ()->ti.c_lflag & FLUSHO || !buf))
+ goto out;
+ if (n == 0 && is_nonblocking ())
+ {
+ set_errno (EAGAIN);
+ rc = -1;
+ break;
+ }
+
+ Sleep (10);
+ }
+
+ if (ReadFile (handle, outbuf, rlen, &n, NULL) == FALSE)
+ goto err;
+ }
+
+ termios_printf ("bytes read %u", n);
+ get_ttyp ()->write_error = 0;
+ if (output_done_event != NULL)
+ SetEvent (output_done_event);
+
+ if (get_ttyp ()->ti.c_lflag & FLUSHO || !buf)
+ continue;
+
+ char *optr;
+ optr = buf;
+ if (pktmode_on)
+ *optr++ = TIOCPKT_DATA;
+
+ if (!(get_ttyp ()->ti.c_oflag & OPOST)) // post-process output
+ {
+ memcpy (optr, outbuf, n);
+ optr += n;
+ }
+ else // raw output mode
+ {
+ 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: %d unexpected characters", n);
+ need_nl = 1;
+ break;
+ }
+
+ *optr++ = *iptr++;
+ }
+ }
+ rc = optr - buf;
+ break;
+
+ err:
+ if (GetLastError () == ERROR_BROKEN_PIPE)
+ rc = 0;
+ else
+ {
+ __seterrno ();
+ rc = -1;
+ }
+ break;
+ }
+
+out:
+ termios_printf ("returning %d", rc);
+ return rc;
+}
+
+static DWORD WINAPI
+process_output (void *)
+{
+ char buf[OUT_BUFFER_SIZE * 2];
+
+ for (;;)
+ {
+ int n = tty_master->process_slave_output (buf, OUT_BUFFER_SIZE, 0);
+ if (n <= 0)
+ {
+ if (n < 0)
+ termios_printf ("ReadFile %E");
+ ExitThread (0);
+ }
+ n = tty_master->console->write ((void *) buf, (size_t) n);
+ tty_master->get_ttyp ()->write_error = n == -1 ? get_errno () : 0;
+ }
+}
+
+
+/* Process tty ioctl requests */
+
+static DWORD WINAPI
+process_ioctl (void *)
+{
+ while (1)
+ {
+ WaitForSingleObject (tty_master->ioctl_request_event, INFINITE);
+ termios_printf ("ioctl() request");
+ tty *ttyp = tty_master->get_ttyp ();
+ ttyp->ioctl_retval =
+ tty_master->console->ioctl (ttyp->cmd,
+ (ttyp->cmd == KDSKBMETA)
+ ? (void *) ttyp->arg.value
+ : (void *) &ttyp->arg);
+ SetEvent (tty_master->ioctl_done_event);
+ }
+}
+
+/**********************************************************************/
+/* Tty slave stuff */
+
+fhandler_tty_slave::fhandler_tty_slave ()
+ : fhandler_tty_common (), inuse (NULL)
+{
+ uninterruptible_io (true);
+}
+
+/* FIXME: This function needs to close handles when it has
+ a failing condition. */
+int
+fhandler_tty_slave::open (int flags, mode_t)
+{
+ if (get_device () == FH_TTY)
+ dev().tty_to_real_device ();
+ fhandler_tty_slave *arch = (fhandler_tty_slave *) cygheap->fdtab.find_archetype (pc.dev);
+ if (arch)
+ {
+ *this = *(fhandler_tty_slave *) arch;
+ termios_printf ("copied fhandler_tty_slave archetype");
+ set_flags ((flags & ~O_TEXT) | O_BINARY);
+ cygheap->manage_console_count ("fhandler_tty_slave::open<arch>", 1);
+ goto out;
+ }
+
+ tcinit (cygwin_shared->tty[get_unit ()]);
+
+ cygwin_shared->tty.attach (get_unit ());
+
+ set_flags ((flags & ~O_TEXT) | O_BINARY);
+ /* Create synchronisation events */
+ char buf[CYG_MAX_PATH];
+
+ /* output_done_event may or may not exist. It will exist if the tty
+ was opened by fhandler_tty_master::init, normally called at
+ startup if use_tty is non-zero. It will not exist if this is a
+ pty opened by fhandler_pty_master::open. In the former case, tty
+ output is handled by a separate thread which controls output. */
+ shared_name (buf, OUTPUT_DONE_EVENT, get_unit ());
+ output_done_event = OpenEvent (EVENT_ALL_ACCESS, TRUE, buf);
+
+ if (!(output_mutex = get_ttyp ()->open_output_mutex ()))
+ {
+ termios_printf ("open output mutex failed, %E");
+ __seterrno ();
+ return 0;
+ }
+ if (!(input_mutex = get_ttyp ()->open_input_mutex ()))
+ {
+ termios_printf ("open input mutex failed, %E");
+ __seterrno ();
+ return 0;
+ }
+ shared_name (buf, INPUT_AVAILABLE_EVENT, get_unit ());
+ if (!(input_available_event = OpenEvent (EVENT_ALL_ACCESS, TRUE, buf)))
+ {
+ termios_printf ("open input event failed, %E");
+ __seterrno ();
+ return 0;
+ }
+
+ /* The ioctl events may or may not exist. See output_done_event,
+ above. */
+ shared_name (buf, IOCTL_REQUEST_EVENT, get_unit ());
+ ioctl_request_event = OpenEvent (EVENT_ALL_ACCESS, TRUE, buf);
+ shared_name (buf, IOCTL_DONE_EVENT, get_unit ());
+ ioctl_done_event = OpenEvent (EVENT_ALL_ACCESS, TRUE, buf);
+
+ /* FIXME: Needs a method to eliminate tty races */
+ {
+ acquire_output_mutex (500);
+ inuse = get_ttyp ()->create_inuse (TTY_SLAVE_ALIVE);
+ get_ttyp ()->was_opened = true;
+ release_output_mutex ();
+ }
+
+ if (!get_ttyp ()->from_master || !get_ttyp ()->to_master)
+ {
+ termios_printf ("tty handles have been closed");
+ set_errno (EACCES);
+ return 0;
+ }
+
+ HANDLE from_master_local;
+ HANDLE to_master_local;
+ from_master_local = to_master_local = NULL;
+
+#ifdef USE_SERVER
+ if (!wincap.has_security ()
+ || cygserver_running == CYGSERVER_UNAVAIL
+ || !cygserver_attach_tty (&from_master_local, &to_master_local))
+#endif
+ {
+ if (get_ttyp ()->master_pid < 0)
+ {
+ set_errno (EAGAIN);
+ termios_printf ("*** master is closed");
+ return 0;
+ }
+ pinfo p (get_ttyp ()->master_pid);
+ if (!p)
+ {
+ set_errno (EAGAIN);
+ termios_printf ("*** couldn't find tty master");
+ return 0;
+ }
+#ifdef USE_SERVER
+ termios_printf ("cannot dup handles via server. using old method.");
+#endif
+ HANDLE tty_owner = OpenProcess (PROCESS_DUP_HANDLE, FALSE,
+ p->dwProcessId);
+ if (tty_owner == NULL)
+ {
+ termios_printf ("can't open tty (%d) handle process %d",
+ get_unit (), get_ttyp ()->master_pid);
+ __seterrno ();
+ return 0;
+ }
+
+ if (!DuplicateHandle (tty_owner, get_ttyp ()->from_master,
+ hMainProc, &from_master_local, 0, TRUE,
+ DUPLICATE_SAME_ACCESS))
+ {
+ termios_printf ("can't duplicate input, %E");
+ __seterrno ();
+ return 0;
+ }
+
+ VerifyHandle (from_master_local);
+ if (!DuplicateHandle (tty_owner, get_ttyp ()->to_master,
+ hMainProc, &to_master_local, 0, TRUE,
+ DUPLICATE_SAME_ACCESS))
+ {
+ termios_printf ("can't duplicate output, %E");
+ __seterrno ();
+ return 0;
+ }
+ VerifyHandle (to_master_local);
+ CloseHandle (tty_owner);
+ }
+
+ termios_printf ("duplicated from_master %p->%p from tty_owner",
+ get_ttyp ()->from_master, from_master_local);
+ termios_printf ("duplicated to_master %p->%p from tty_owner",
+ get_ttyp ()->to_master, to_master_local);
+
+ set_io_handle (from_master_local);
+ set_output_handle (to_master_local);
+
+ set_open_status ();
+ if (cygheap->manage_console_count ("fhandler_tty_slave::open", 1) == 1
+ && !output_done_event)
+ fhandler_console::need_invisible ();
+
+ // FIXME: Do this better someday
+ arch = (fhandler_tty_slave *) cmalloc (HEAP_ARCHETYPES, sizeof (*this));
+ *((fhandler_tty_slave **) cygheap->fdtab.add_archetype ()) = arch;
+ archetype = arch;
+ *arch = *this;
+
+out:
+ usecount = 0;
+ arch->usecount++;
+ report_tty_counts (this, "opened", "");
+ myself->set_ctty (get_ttyp (), flags, arch);
+
+ return 1;
+}
+
+int
+fhandler_tty_slave::close ()
+{
+ /* This used to always call fhandler_tty_common::close when hExeced but that
+ caused multiple closes of the handles associated with this tty. Since
+ close_all_files is not called until after the cygwin process has synced
+ or before a non-cygwin process has exited, it should be safe to just
+ close this normally. cgf 2006-05-20 */
+ cygheap->manage_console_count ("fhandler_tty_slave::close", -1);
+
+ archetype->usecount--;
+ report_tty_counts (this, "closed", "");
+
+ if (archetype->usecount)
+ {
+#ifdef DEBUGGING
+ if (archetype->usecount < 0)
+ system_printf ("error: usecount %d", archetype->usecount);
+#endif
+ termios_printf ("just returning because archetype usecount is != 0");
+ return 0;
+ }
+
+ termios_printf ("closing last open %s handle", ttyname ());
+ if (inuse && !CloseHandle (inuse))
+ termios_printf ("CloseHandle (inuse), %E");
+ return fhandler_tty_common::close ();
+}
+
+int
+fhandler_tty_slave::cygserver_attach_tty (LPHANDLE from_master_ptr,
+ LPHANDLE to_master_ptr)
+{
+#ifndef USE_SERVER
+ return 0;
+#else
+ if (!from_master_ptr || !to_master_ptr)
+ return 0;
+
+ client_request_attach_tty req ((DWORD) get_ttyp ()->master_pid,
+ (HANDLE) get_ttyp ()->from_master,
+ (HANDLE) get_ttyp ()->to_master);
+
+ if (req.make_request () == -1 || req.error_code ())
+ return 0;
+
+ *from_master_ptr = req.from_master ();
+ *to_master_ptr = req.to_master ();
+
+ return 1;
+#endif
+}
+
+void
+fhandler_tty_slave::init (HANDLE, DWORD a, mode_t)
+{
+ int flags = 0;
+
+ a &= GENERIC_READ | GENERIC_WRITE;
+ if (a == GENERIC_READ)
+ flags = O_RDONLY;
+ if (a == GENERIC_WRITE)
+ flags = O_WRONLY;
+ if (a == (GENERIC_READ | GENERIC_WRITE))
+ flags = O_RDWR;
+
+ open (flags);
+}
+
+int
+fhandler_tty_slave::write (const void *ptr, size_t len)
+{
+ DWORD n, towrite = len;
+
+ termios_printf ("tty%d, write(%x, %d)", get_unit (), ptr, len);
+
+ acquire_output_mutex (INFINITE);
+
+ while (len)
+ {
+ n = min (OUT_BUFFER_SIZE, len);
+ char *buf = (char *)ptr;
+ ptr = (char *) ptr + n;
+ len -= n;
+
+ /* Previous write may have set write_error to != 0. Check it here.
+ This is less than optimal, but the alternative slows down tty
+ writes enormously. */
+ if (get_ttyp ()->write_error)
+ {
+ set_errno (get_ttyp ()->write_error);
+ towrite = (DWORD) -1;
+ break;
+ }
+
+ if (WriteFile (get_output_handle (), buf, n, &n, NULL) == FALSE)
+ {
+ 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 = (DWORD) -1;
+ break;
+ }
+
+ if (output_done_event != NULL)
+ {
+ DWORD rc;
+ DWORD x = n * 1000;
+ rc = WaitForSingleObject (output_done_event, x);
+ termios_printf ("waited %d ms for output_done_event, WFSO %d", x, rc);
+ }
+ }
+ release_output_mutex ();
+ return towrite;
+}
+
+void __stdcall
+fhandler_tty_slave::read (void *ptr, size_t& len)
+{
+ int totalread = 0;
+ int vmin = 0;
+ int vtime = 0; /* Initialized to prevent -Wuninitialized warning */
+ size_t readlen;
+ DWORD bytes_in_pipe;
+ char buf[INP_BUFFER_SIZE];
+ char peek_buf[INP_BUFFER_SIZE];
+ DWORD time_to_wait;
+ DWORD rc;
+ HANDLE w4[2];
+
+ termios_printf ("read(%x, %d) handle %p", ptr, len, get_handle ());
+
+ if (!ptr) /* Indicating tcflush(). */
+ time_to_wait = 0;
+ else if ((get_ttyp ()->ti.c_lflag & ICANON))
+ time_to_wait = INFINITE;
+ else
+ {
+ vmin = get_ttyp ()->ti.c_cc[VMIN];
+ if (vmin > INP_BUFFER_SIZE)
+ vmin = INP_BUFFER_SIZE;
+ vtime = get_ttyp ()->ti.c_cc[VTIME];
+ if (vmin < 0)
+ vmin = 0;
+ if (vtime < 0)
+ vtime = 0;
+ if (!vmin && !vtime)
+ time_to_wait = 0;
+ else
+ time_to_wait = !vtime ? INFINITE : 100 * vtime;
+ }
+
+ w4[0] = signal_arrived;
+ w4[1] = input_available_event;
+
+ DWORD waiter = time_to_wait;
+ while (len)
+ {
+ rc = WaitForMultipleObjects (2, w4, FALSE, waiter);
+
+ if (rc == WAIT_TIMEOUT)
+ {
+ termios_printf ("wait timed out, waiter %u", waiter);
+ break;
+ }
+
+ if (rc == WAIT_FAILED)
+ {
+ termios_printf ("wait for input event failed, %E");
+ break;
+ }
+
+ if (rc == WAIT_OBJECT_0)
+ {
+ /* if we've received signal after successfully reading some data,
+ just return all data successfully read */
+ if (totalread > 0)
+ break;
+ set_sig_errno (EINTR);
+ len = (size_t) -1;
+ return;
+ }
+
+ rc = WaitForSingleObject (input_mutex, 1000);
+ if (rc == WAIT_FAILED)
+ {
+ termios_printf ("wait for input mutex failed, %E");
+ break;
+ }
+ else if (rc == WAIT_TIMEOUT)
+ {
+ termios_printf ("failed to acquire input mutex after input event arrived");
+ break;
+ }
+ if (!PeekNamedPipe (get_handle (), peek_buf, sizeof (peek_buf), &bytes_in_pipe, NULL, NULL))
+ {
+ termios_printf ("PeekNamedPipe failed, %E");
+ raise (SIGHUP);
+ bytes_in_pipe = 0;
+ }
+
+ /* On first peek determine no. of bytes to flush. */
+ if (!ptr && len == UINT_MAX)
+ len = (size_t) bytes_in_pipe;
+
+ if (ptr && !bytes_in_pipe && !vmin && !time_to_wait)
+ {
+ ReleaseMutex (input_mutex);
+ len = (size_t) bytes_in_pipe;
+ return;
+ }
+
+ readlen = min (bytes_in_pipe, min (len, sizeof (buf)));
+
+ if (ptr && vmin && readlen > (unsigned) vmin)
+ readlen = vmin;
+
+ DWORD n = 0;
+ if (readlen)
+ {
+ termios_printf ("reading %d bytes (vtime %d)", readlen, vtime);
+ if (ReadFile (get_handle (), buf, readlen, &n, NULL) == FALSE)
+ {
+ termios_printf ("read failed, %E");
+ raise (SIGHUP);
+ }
+ /* MSDN states that 5th prameter can be used to determine total
+ number of bytes in pipe, but for some reason this number doesn't
+ change after successful read. So we have to peek into the pipe
+ again to see if input is still available */
+ if (!PeekNamedPipe (get_handle (), peek_buf, 1, &bytes_in_pipe, NULL, NULL))
+ {
+ termios_printf ("PeekNamedPipe failed, %E");
+ raise (SIGHUP);
+ bytes_in_pipe = 0;
+ }
+ if (n)
+ {
+ len -= n;
+ totalread += n;
+ if (ptr)
+ {
+ memcpy (ptr, buf, n);
+ ptr = (char *) ptr + n;
+ }
+ }
+ }
+
+ if (!bytes_in_pipe)
+ ResetEvent (input_available_event);
+
+ ReleaseMutex (input_mutex);
+
+ if (!ptr)
+ {
+ if (!bytes_in_pipe)
+ break;
+ continue;
+ }
+
+ if (get_ttyp ()->read_retval < 0) // read error
+ {
+ set_errno (-get_ttyp ()->read_retval);
+ totalread = -1;
+ break;
+ }
+ if (get_ttyp ()->read_retval == 0) //EOF
+ {
+ termios_printf ("saw EOF");
+ break;
+ }
+ if (get_ttyp ()->ti.c_lflag & ICANON || is_nonblocking ())
+ break;
+ if (vmin && totalread >= vmin)
+ break;
+
+ /* vmin == 0 && vtime == 0:
+ * we've already read all input, if any, so return immediately
+ * vmin == 0 && vtime > 0:
+ * we've waited for input 10*vtime ms in WFSO(input_available_event),
+ * no matter whether any input arrived, we shouldn't wait any longer,
+ * so return immediately
+ * vmin > 0 && vtime == 0:
+ * here, totalread < vmin, so continue waiting until more data
+ * arrive
+ * vmin > 0 && vtime > 0:
+ * similar to the previous here, totalread < vmin, and timer
+ * hadn't expired -- WFSO(input_available_event) != WAIT_TIMEOUT,
+ * so "restart timer" and wait until more data arrive
+ */
+
+ if (vmin == 0)
+ break;
+
+ if (n)
+ waiter = time_to_wait;
+ }
+ termios_printf ("%d=read(%x, %d)", totalread, ptr, len);
+ len = (size_t) totalread;
+}
+
+int
+fhandler_tty_slave::dup (fhandler_base *child)
+{
+ fhandler_tty_slave *arch = (fhandler_tty_slave *) archetype;
+ *(fhandler_tty_slave *) child = *arch;
+ child->set_flags (get_flags ());
+ child->usecount = 0;
+ arch->usecount++;
+ cygheap->manage_console_count ("fhandler_tty_slave::dup", 1);
+ report_tty_counts (child, "duped", "");
+ return 0;
+}
+
+int
+fhandler_pty_master::dup (fhandler_base *child)
+{
+ fhandler_tty_master *arch = (fhandler_tty_master *) archetype;
+ *(fhandler_tty_master *) child = *arch;
+ child->set_flags (get_flags ());
+ child->usecount = 0;
+ arch->usecount++;
+ report_tty_counts (child, "duped master", "");
+ return 0;
+}
+
+int
+fhandler_tty_slave::tcgetattr (struct termios *t)
+{
+ *t = get_ttyp ()->ti;
+ return 0;
+}
+
+int
+fhandler_tty_slave::tcsetattr (int, const struct termios *t)
+{
+ acquire_output_mutex (INFINITE);
+ get_ttyp ()->ti = *t;
+ release_output_mutex ();
+ return 0;
+}
+
+int
+fhandler_tty_slave::tcflush (int queue)
+{
+ int ret = 0;
+
+ termios_printf ("tcflush(%d) handle %p", queue, get_handle ());
+
+ if (queue == TCIFLUSH || queue == TCIOFLUSH)
+ {
+ size_t len = UINT_MAX;
+ read (NULL, len);
+ ret = ((int) len) >= 0 ? 0 : -1;
+ }
+ if (queue == TCOFLUSH || queue == TCIOFLUSH)
+ {
+ /* do nothing for now. */
+ }
+
+ termios_printf ("%d=tcflush(%d)", ret, queue);
+ return ret;
+}
+
+int
+fhandler_tty_slave::ioctl (unsigned int cmd, void *arg)
+{
+ termios_printf ("ioctl (%x)", cmd);
+
+ if (myself->pgid && get_ttyp ()->getpgid () != myself->pgid
+ && myself->ctty == get_unit () && (get_ttyp ()->ti.c_lflag & TOSTOP))
+ {
+ /* background process */
+ termios_printf ("bg ioctl pgid %d, tpgid %d, %s", myself->pgid,
+ get_ttyp ()->getpgid (), myctty ());
+ raise (SIGTTOU);
+ }
+
+ int retval;
+ switch (cmd)
+ {
+ case TIOCGWINSZ:
+ case TIOCSWINSZ:
+ case TIOCLINUX:
+ case KDGKBMETA:
+ case KDSKBMETA:
+ break;
+ case FIONBIO:
+ set_nonblocking (*(int *) arg);
+ retval = 0;
+ goto out;
+ default:
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ acquire_output_mutex (INFINITE);
+
+ get_ttyp ()->cmd = cmd;
+ get_ttyp ()->ioctl_retval = 0;
+ int val;
+ switch (cmd)
+ {
+ case TIOCGWINSZ:
+ get_ttyp ()->arg.winsize = get_ttyp ()->winsize;
+ if (ioctl_request_event)
+ SetEvent (ioctl_request_event);
+ *(struct winsize *) arg = get_ttyp ()->arg.winsize;
+ if (ioctl_done_event)
+ WaitForSingleObject (ioctl_done_event, INFINITE);
+ get_ttyp ()->winsize = get_ttyp ()->arg.winsize;
+ break;
+ case TIOCSWINSZ:
+ if (get_ttyp ()->winsize.ws_row != ((struct winsize *) arg)->ws_row
+ || get_ttyp ()->winsize.ws_col != ((struct winsize *) arg)->ws_col)
+ {
+ get_ttyp ()->arg.winsize = *(struct winsize *) arg;
+ if (ioctl_request_event)
+ {
+ get_ttyp ()->ioctl_retval = -EINVAL;
+ SetEvent (ioctl_request_event);
+ }
+ else
+ {
+ get_ttyp ()->winsize = *(struct winsize *) arg;
+ killsys (-get_ttyp ()->getpgid (), SIGWINCH);
+ }
+ if (ioctl_done_event)
+ WaitForSingleObject (ioctl_done_event, INFINITE);
+ }
+ break;
+ case TIOCLINUX:
+ val = *(unsigned char *) arg;
+ if (val != 6 || !ioctl_request_event || !ioctl_done_event)
+ get_ttyp ()->ioctl_retval = -EINVAL;
+ else
+ {
+ get_ttyp ()->arg.value = val;
+ SetEvent (ioctl_request_event);
+ WaitForSingleObject (ioctl_done_event, INFINITE);
+ *(unsigned char *) arg = get_ttyp ()->arg.value & 0xFF;
+ }
+ break;
+ case KDGKBMETA:
+ if (ioctl_request_event)
+ {
+ SetEvent (ioctl_request_event);
+ if (ioctl_done_event)
+ WaitForSingleObject (ioctl_done_event, INFINITE);
+ *(int *) arg = get_ttyp ()->arg.value;
+ }
+ else
+ get_ttyp ()->ioctl_retval = -EINVAL;
+ break;
+ case KDSKBMETA:
+ if (ioctl_request_event)
+ {
+ get_ttyp ()->arg.value = (int) arg;
+ SetEvent (ioctl_request_event);
+ if (ioctl_done_event)
+ WaitForSingleObject (ioctl_done_event, INFINITE);
+ }
+ else
+ get_ttyp ()->ioctl_retval = -EINVAL;
+ break;
+ }
+
+ release_output_mutex ();
+ retval = get_ttyp ()->ioctl_retval;
+ if (retval < 0)
+ {
+ set_errno (-retval);
+ retval = -1;
+ }
+
+out:
+ termios_printf ("%d = ioctl (%x)", retval, cmd);
+ return retval;
+}
+
+/*******************************************************
+ fhandler_pty_master
+*/
+fhandler_pty_master::fhandler_pty_master ()
+ : fhandler_tty_common (), pktmode (0), need_nl (0), dwProcessId (0)
+{
+}
+
+int
+fhandler_pty_master::open (int flags, mode_t)
+{
+ int ntty;
+ ntty = cygwin_shared->tty.allocate (false);
+ if (ntty < 0)
+ return 0;
+
+ dev().devn = FHDEV (DEV_TTYM_MAJOR, ntty);
+ if (!setup (true))
+ {
+ lock_ttys::release ();
+ return 0;
+ }
+ lock_ttys::release ();
+ set_flags ((flags & ~O_TEXT) | O_BINARY);
+ set_open_status ();
+ //
+ // FIXME: Do this better someday
+ fhandler_pty_master *arch = (fhandler_tty_master *) cmalloc (HEAP_ARCHETYPES, sizeof (*this));
+ *((fhandler_pty_master **) cygheap->fdtab.add_archetype ()) = arch;
+ archetype = arch;
+ *arch = *this;
+ arch->dwProcessId = GetCurrentProcessId ();
+
+ usecount = 0;
+ arch->usecount++;
+ char buf[sizeof ("opened pty master for ttyNNNNNNNNNNN")];
+ __small_sprintf (buf, "opened pty master for tty%d", get_unit ());
+ report_tty_counts (this, buf, "");
+ return 1;
+}
+
+_off64_t
+fhandler_tty_common::lseek (_off64_t, int)
+{
+ set_errno (ESPIPE);
+ return -1;
+}
+
+int
+fhandler_tty_common::close ()
+{
+ termios_printf ("tty%d <%p,%p> closing", get_unit (), get_handle (), get_output_handle ());
+ if (output_done_event && !CloseHandle (output_done_event))
+ termios_printf ("CloseHandle (output_done_event), %E");
+ if (ioctl_done_event && !CloseHandle (ioctl_done_event))
+ termios_printf ("CloseHandle (ioctl_done_event), %E");
+ if (ioctl_request_event && !CloseHandle (ioctl_request_event))
+ termios_printf ("CloseHandle (ioctl_request_event), %E");
+ if (!ForceCloseHandle (input_mutex))
+ termios_printf ("CloseHandle (input_mutex<%p>), %E", input_mutex);
+ if (!ForceCloseHandle (output_mutex))
+ termios_printf ("CloseHandle (output_mutex<%p>), %E", output_mutex);
+ if (!ForceCloseHandle1 (get_handle (), from_pty))
+ termios_printf ("CloseHandle (get_handle ()<%p>), %E", get_handle ());
+ if (!ForceCloseHandle1 (get_output_handle (), to_pty))
+ termios_printf ("CloseHandle (get_output_handle ()<%p>), %E", get_output_handle ());
+
+ if (!ForceCloseHandle (input_available_event))
+ termios_printf ("CloseHandle (input_available_event<%p>), %E", input_available_event);
+
+ return 0;
+}
+
+int
+fhandler_pty_master::close ()
+{
+#if 0
+ while (accept_input () > 0)
+ continue;
+#endif
+ archetype->usecount--;
+ report_tty_counts (this, "closing master", "");
+
+ if (archetype->usecount)
+ {
+#ifdef DEBUGGING
+ if (archetype->usecount < 0)
+ system_printf ("error: usecount %d", archetype->usecount);
+#endif
+ termios_printf ("just returning because archetype usecount is != 0");
+ return 0;
+ }
+
+ fhandler_tty_master *arch = (fhandler_tty_master *) archetype;
+ termios_printf ("closing from_master(%p)/to_master(%p) since we own them(%d)",
+ arch->from_master, arch->to_master, arch->dwProcessId);
+ if (!ForceCloseHandle (arch->from_master))
+ termios_printf ("error closing from_master %p, %E", arch->from_master);
+ if (!ForceCloseHandle (arch->to_master))
+ termios_printf ("error closing from_master %p, %E", arch->to_master);
+ fhandler_tty_common::close ();
+
+ if (hExeced || get_ttyp ()->master_pid != myself->pid)
+ termios_printf ("not clearing: %d, master_pid %d", hExeced, get_ttyp ()->master_pid);
+ else
+ get_ttyp ()->set_master_closed ();
+
+ return 0;
+}
+
+int
+fhandler_pty_master::write (const void *ptr, size_t len)
+{
+ int i;
+ char *p = (char *) ptr;
+ termios ti = tc->ti;
+
+ for (i = 0; i < (int) len; i++)
+ {
+ line_edit_status status = line_edit (p++, 1, ti);
+ if (status > line_edit_signalled)
+ {
+ if (status != line_edit_pipe_full)
+ i = -1;
+ break;
+ }
+ }
+ return i;
+}
+
+void __stdcall
+fhandler_pty_master::read (void *ptr, size_t& len)
+{
+ len = (size_t) process_slave_output ((char *) ptr, len, pktmode);
+}
+
+int
+fhandler_pty_master::tcgetattr (struct termios *t)
+{
+ *t = cygwin_shared->tty[get_unit ()]->ti;
+ return 0;
+}
+
+int
+fhandler_pty_master::tcsetattr (int, const struct termios *t)
+{
+ cygwin_shared->tty[get_unit ()]->ti = *t;
+ return 0;
+}
+
+int
+fhandler_pty_master::tcflush (int queue)
+{
+ int ret = 0;
+
+ termios_printf ("tcflush(%d) handle %p", queue, get_handle ());
+
+ if (queue == TCIFLUSH || queue == TCIOFLUSH)
+ ret = process_slave_output (NULL, OUT_BUFFER_SIZE, 0);
+ else if (queue == TCIFLUSH || queue == TCIOFLUSH)
+ {
+ /* do nothing for now. */
+ }
+
+ termios_printf ("%d=tcflush(%d)", ret, queue);
+ return ret;
+}
+
+int
+fhandler_pty_master::ioctl (unsigned int cmd, void *arg)
+{
+ switch (cmd)
+ {
+ case TIOCPKT:
+ pktmode = *(int *) arg;
+ break;
+ case TIOCGWINSZ:
+ *(struct winsize *) arg = get_ttyp ()->winsize;
+ break;
+ case TIOCSWINSZ:
+ if (get_ttyp ()->winsize.ws_row != ((struct winsize *) arg)->ws_row
+ || get_ttyp ()->winsize.ws_col != ((struct winsize *) arg)->ws_col)
+ {
+ get_ttyp ()->winsize = *(struct winsize *) arg;
+ killsys (-get_ttyp ()->getpgid (), SIGWINCH);
+ }
+ break;
+ case FIONBIO:
+ set_nonblocking (*(int *) arg);
+ break;
+ default:
+ set_errno (EINVAL);
+ return -1;
+ }
+ return 0;
+}
+
+char *
+fhandler_pty_master::ptsname ()
+{
+ static char buf[32];
+
+ __small_sprintf (buf, "/dev/tty%d", get_unit ());
+ return buf;
+}
+
+void
+fhandler_tty_common::set_close_on_exec (bool val)
+{
+ // Cygwin processes will handle this specially on exec.
+ close_on_exec (val);
+}
+
+void
+fhandler_tty_slave::fixup_after_fork (HANDLE parent)
+{
+ // fork_fixup (parent, inuse, "inuse");
+ // fhandler_tty_common::fixup_after_fork (parent);
+ report_tty_counts (this, "inherited", "");
+}
+
+void
+fhandler_tty_slave::fixup_after_exec ()
+{
+ if (!close_on_exec ())
+ fixup_after_fork (NULL);
+}
+
+int
+fhandler_tty_master::init_console ()
+{
+ console = (fhandler_console *) build_fh_dev (*console_dev, "/dev/ttym");
+ if (console == NULL)
+ return -1;
+
+ console->init (INVALID_HANDLE_VALUE, GENERIC_READ | GENERIC_WRITE, O_BINARY);
+ cygheap->manage_console_count ("fhandler_tty_master::init_console", -1, true);
+ console->uninterruptible_io (true);
+ return 0;
+}
+
+#define close_maybe(h) \
+ do { \
+ if (h) \
+ CloseHandle (h); \
+ } while (0)
+
+bool
+fhandler_pty_master::setup (bool ispty)
+{
+ tty& t = *cygwin_shared->tty[get_unit ()];
+
+ tcinit (&t, true); /* Set termios information. Force initialization. */
+
+ const char *errstr = NULL;
+ DWORD pipe_mode = PIPE_NOWAIT;
+
+ /* Create communication pipes */
+
+ /* FIXME: should this be sec_none_nih? */
+ if (!CreatePipe (&from_master, &get_output_handle (), &sec_all, 128 * 1024))
+ {
+ errstr = "input pipe";
+ goto err;
+ }
+
+ if (!CreatePipe (&get_io_handle (), &to_master, &sec_all, 128 * 1024))
+ {
+ errstr = "output pipe";
+ goto err;
+ }
+
+ if (!SetNamedPipeHandleState (get_output_handle (), &pipe_mode, NULL, NULL))
+ termios_printf ("can't set output_handle(%p) to non-blocking mode",
+ get_output_handle ());
+
+ need_nl = 0;
+
+ /* We do not open allow the others to open us (for handle duplication)
+ but rely on cygheap->inherited_ctty for descendant processes.
+ In the future the cygserver may allow access by others. */
+
+#ifdef USE_SERVER
+ if (wincap.has_security ())
+ {
+ if (cygserver_running == CYGSERVER_UNKNOWN)
+ cygserver_init ();
+ }
+#endif
+
+ /* Create synchronisation events */
+
+ if (!ispty)
+ {
+ if (!(output_done_event = t.get_event (errstr = OUTPUT_DONE_EVENT)))
+ goto err;
+ if (!(ioctl_done_event = t.get_event (errstr = IOCTL_DONE_EVENT)))
+ goto err;
+ if (!(ioctl_request_event = t.get_event (errstr = IOCTL_REQUEST_EVENT)))
+ goto err;
+ }
+
+ if (!(input_available_event = t.get_event (errstr = INPUT_AVAILABLE_EVENT, TRUE)))
+ goto err;
+
+ char buf[CYG_MAX_PATH];
+ errstr = shared_name (buf, OUTPUT_MUTEX, t.ntty);
+ if (!(output_mutex = CreateMutex (&sec_all, FALSE, buf)))
+ goto err;
+
+ errstr = shared_name (buf, INPUT_MUTEX, t.ntty);
+ if (!(input_mutex = CreateMutex (&sec_all, FALSE, buf)))
+ goto err;
+
+ if (!DuplicateHandle (hMainProc, from_master, hMainProc, &from_master, 0, false,
+ DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE))
+ {
+ errstr = "non-inheritable from_master";
+ goto err;
+ }
+
+ if (!DuplicateHandle (hMainProc, to_master, hMainProc, &to_master, 0, false,
+ DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE))
+ {
+ errstr = "non-inheritable to_master";
+ goto err;
+ }
+
+ t.from_master = from_master;
+ t.to_master = to_master;
+ // /* screws up tty master */ ProtectHandle1INH (output_mutex, output_mutex);
+ // /* screws up tty master */ ProtectHandle1INH (input_mutex, input_mutex);
+ t.winsize.ws_col = 80;
+ t.winsize.ws_row = 25;
+ t.master_pid = myself->pid;
+
+ termios_printf ("tty%d opened - from_slave %p, to_slave %p", t.ntty,
+ get_io_handle (), get_output_handle ());
+ return true;
+
+err:
+ __seterrno ();
+ close_maybe (get_io_handle ());
+ close_maybe (get_output_handle ());
+ close_maybe (output_done_event);
+ close_maybe (ioctl_done_event);
+ close_maybe (ioctl_request_event);
+ close_maybe (input_available_event);
+ close_maybe (output_mutex);
+ close_maybe (input_mutex);
+ close_maybe (from_master);
+ close_maybe (to_master);
+ termios_printf ("tty%d open failed - failed to create %s", errstr);
+ return false;
+}
+
+void
+fhandler_pty_master::fixup_after_fork (HANDLE parent)
+{
+ DWORD wpid = GetCurrentProcessId ();
+ fhandler_tty_master *arch = (fhandler_tty_master *) archetype;
+ if (arch->dwProcessId != wpid)
+ {
+ tty& t = *get_ttyp ();
+ if (!DuplicateHandle (parent, arch->from_master, hMainProc,
+ &arch->from_master, 0, false, DUPLICATE_SAME_ACCESS))
+ system_printf ("couldn't duplicate from_parent(%p), %E", arch->from_master);
+ if (!DuplicateHandle (parent, arch->to_master, hMainProc,
+ &arch->to_master, 0, false, DUPLICATE_SAME_ACCESS))
+ system_printf ("couldn't duplicate to_parent(%p), %E", arch->from_master);
+ if (myself->pid == t.master_pid)
+ {
+ t.from_master = arch->from_master;
+ t.to_master = arch->to_master;
+ }
+ arch->dwProcessId = wpid;
+ }
+ from_master = arch->from_master;
+ to_master = arch->to_master;
+ report_tty_counts (this, "inherited master", "");
+}
+
+void
+fhandler_pty_master::fixup_after_exec ()
+{
+ if (!close_on_exec ())
+ fixup_after_fork (spawn_info->parent);
+ else
+ from_master = to_master = NULL;
+}