Mail Archives: cygwin/1997/06/02/20:34:00
This is a multi-part message in MIME format.
--------------49597C673EEAD2813A29F035
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Following is the tty support code that I've been
working on. The code integrates Sergey Okhapkin's
patch, but no other changes. The files affected are:
fcntl.cc
fhandler.cc
fhandler.h
include/fcntl.h
include/sys/termios.h
termios.cc
The diff file, 0601tty.diff was produced by
restoring the original B18 distribution, applying
Sergey Okhapkin's patch and then diff'ing against
the changed version of the files listed above.
Patch, however, consistently fails to handle
fhandler.cc and fhandler.h (patch seems to be
generally buggy on my machine), so these two
files are included as separate attachments.
My tty projects seem to be working with these
code changes, but they should be considered
untested. No attempt has been made to accomodate
Win95 differences from WinNT. Consider these
changes to be a first attempt at the development
of a robust tty sub-system for eventual inclusion
in cygwin32, and anyone who wishes to test,
debug, correct, or re-implement is more than
welcome to have at it.
The code changes do not implement any part of
"struct termio"/ioctl() features, but rather the
"struct termios" and tcsetsttr() group of
functions. You will need to convert your code
from ioctl() to tc...() to use the new
features.
tcflush() has a previous implementation, which
is unchanged.
The tty features which have an implementation,
and their known limitations are listed:
1. The O_NDELAY flag to open() and fcntl()
causes the COMMTIMEOUTS structure to be
manipulated to put the file-handle in
blocking or non-blocking I/O mode. fcntl()
can be used to block/non-block in this
fashion:
/* non-blocking I/O */
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NDELAY);
/* blocking I/O */
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & ~O_NDELAY);
O_NONBLOCK can be implemented by some
variation of:
#undef O_NONBLOCK
#define O_NONBLOCK O_NDELAY
Sergey Okhapkin has pointed-out that this
use of fcntl() may have an undesireable
side effect if used with the console or
(as yet non-existant) psuedo-ttys. The
fhandler_base class probably needs to be
augmented with an isaserialport() function
to clear this bug. I was afraid to change
the members of this class (I also wanted to
delete or enlarge the ofspeed and ifspeed
data members) thinking that it might break
already compiled programs. Is this correct?
2. tcgetattr() previously handled CBAUD (all
speeds through B38400, except B0), CSIZE,
ONLCR, and VTIME and VMIN for c_cc[].
New support is for B0, CLOCAL, CSTOPB,
PARENB, PARODD, INPCK, IGNPAR, IXON, IXOFF,
CRTSXOFF, CRTSCTS, and VSTART and VSTOP for
c_cc[].
3. tcsetattr() previously handled CBAUD
(except B0), CSIZE, PARENB, PARODD, IGNCR,
ONLCR, and VTIME and VMIN for c_cc[].
New support is for B0, CSTOPB, INPCK,
IGNPAR, IXON, IXOFF, CRTSXOFF, CRTSCTS,
and VSTART and VSTOP for c_cc[].
TCSANOW, TCSADRAIN, and TCSAFLUSH arguments
are implemented through calls to
FlushFileBuffers() and PurgeComm().
B0 should cause DTR to drop via a
call to EscapeCommFunction().
CLOCAL disables hardware flow-control and
DSR detection. CLOCAL does not disable
DTR lead control, which is often needed
for hanging-up a modem, nor does it affect
CD (carrier detect) in any way. This needs
an implementation so that the loss of
carrier generates the equivalent of SIGHUP.
Any ideas?
4. tcsendbreak() is implemented through calls to
SetCommBreak() and ClearCommBreak().
5. tcdrain() is implemented through a call to
FlushFileBuffers().
6. tcflow() is implemented for TCOOFF and TCOON
with a call to EscapeCommFunction(), and for
TCION and TCIOFF with a call to
TransmitCommChar(). The values in c_cc[VSTART]
and c_cc[VSTOP] are honored, if present,
otherwise 0x11 and 0x13 are used, respectively.
Because many of the termios flags are not supported,
a call to tcgetattr() will not return the same values
that the flags in the call to tcsetattr() specified.
Generally speaking, to more fully implement the tty
subsystem, more state needs to be held in the
fhandler_base or fhandler_tty class.
Good luck,
Mike Anderson
mka AT redes DOT int DOT com DOT mk
Guanajuato, GTO, Mexico
--------------49597C673EEAD2813A29F035
Content-Type: text/plain; charset=us-ascii; name="0601tty.diff"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline; filename="0601tty.diff"
*** winsup.sergey/fcntl.cc Sat May 31 23:56:14 1997
--- winsup/fcntl.cc Sun Jun 01 10:09:59 1997
***************
*** 80,113 ****
}
break;
! case F_GETFL:
! res = 0;
! if (this_procinfo ()->hmap[fd].h->get_access () & GENERIC_READ)
! res |= O_RDONLY;
! if (this_procinfo ()->hmap[fd].h->get_access () & GENERIC_WRITE)
! res |= O_WRONLY;
! if (this_procinfo ()->hmap[fd].h->get_access () & GENERIC_ALL)
! res |= O_RDWR;
! goto done;
!
! case F_SETFL:
! {
! int temp = 0;
! va_start (args, cmd);
! arg = va_arg (args, int);
! va_end (args);
!
! if (arg & O_RDONLY)
! temp |= GENERIC_READ;
! if (arg & O_WRONLY)
! temp |= GENERIC_WRITE;
!
! syscall_printf ("fcntl (%d, F_SETFL, %d);\n", arg);
!
! this_procinfo ()->hmap[fd].h->set_access (temp);
! res = 0;
! goto done;
! }
case F_GETLK:
case F_SETLK:
--- 80,235 ----
}
break;
!
!
!
! case F_GETFL:
! { /* scope only */
! int access;
! res = 0;
!
! access = this_procinfo()->hmap[fd].h->get_access();
!
! if( (access & (GENERIC_READ | GENERIC_WRITE)) == GENERIC_READ )
! {
! res |= O_RDONLY;
! }
! else if( (access & (GENERIC_READ | GENERIC_WRITE)) == GENERIC_WRITE )
! {
! res |= O_WRONLY;
! }
! else if( (access & (GENERIC_READ | GENERIC_WRITE)) ==
! (GENERIC_READ | GENERIC_WRITE) )
! {
! res |= O_RDWR;
! }
! else
! {
! /* ugly error - do what here? */
! }
!
! if( access & _FNDELAY )
! res |= O_NDELAY;
! goto done;
!
! } /* scope only */
!
!
!
!
! case F_SETFL:
! { /* scope only */
!
! int temp = 0;
! int access;
! va_start (args, cmd);
! arg = va_arg (args, int);
! va_end (args);
!
!
! /* these guys aren't allowed anymore in Unix so we have to
! get them from fhandler_base */
! /*
! if( (arg & (O_RDONLY | O_WRONLY | O_RDWR)) == O_RDONLY )
! {
! temp |= GENERIC_READ;
! }
! else if( (arg & (O_RDONLY | O_WRONLY | O_RDWR)) == O_WRONLY )
! {
! temp |= GENERIC_WRITE;
! }
! else if( (arg & (O_RDONLY | O_WRONLY | O_RDWR)) == O_RDWR )
! {
! temp |= (GENERIC_READ | GENERIC_WRITE);
! }
! */
! access = this_procinfo()->hmap[fd].h->get_access();
! temp |= (access & (GENERIC_READ | GENERIC_WRITE));
!
! if( arg & O_NDELAY ) /* non-blocking I/O */
! {
! temp |= _FNDELAY;
!
! BOOL isatty = this_procinfo()->hmap[fd].h->is_tty();
! if( isatty )
! {
! HANDLE myhandle = this_procinfo()->hmap[fd].h->get_handle();
! COMMTIMEOUTS to;
!
! memset( &to, 0, sizeof(to) );
!
! /* probably not necessary */
! if( GetCommTimeouts( myhandle, &to ) == 0 )
! {
! small_printf ("GetCommTimeout failed\n");
! __seterrno ();
! return -1;
! }
!
! to.ReadIntervalTimeout = MAXDWORD;
! to.ReadTotalTimeoutMultiplier = 0;
! to.ReadTotalTimeoutConstant = 0;
!
! /* leave the values vmin_ and vtime_ alone for restoral later */
!
! if( SetCommTimeouts( myhandle, &to ) == 0 )
! {
! small_printf ("SetCommTimeout failed\n");
! __seterrno ();
! return -1;
! }
! } /* if a tty */
!
! } /* non-blocking I/O */
! else
! { /* blocking I/O */
!
! /* if non-blocking is set, unset it */
! if( access & _FNDELAY )
! {
! BOOL isatty = this_procinfo()->hmap[fd].h->is_tty();
! if( isatty )
! {
! HANDLE myhandle = this_procinfo()->hmap[fd].h->get_handle();
! COMMTIMEOUTS to;
!
! memset( &to, 0, sizeof(to) );
!
! /* probably not necessary */
! /*
! if( GetCommTimeouts( myhandle, &to ) == 0 )
! {
! small_printf ("GetCommTimeout failed\n");
! __seterrno ();
! return -1;
! }
!
! we have no access to vtime_ or vmin_
! to.ReadIntervalTimeout = 0;
! to.ReadTotalTimeoutMultiplier = 0;
! to.ReadTotalTimeoutConstant = vtime_ * 100;
! */
!
! if( SetCommTimeouts( myhandle, &to ) == 0 )
! {
! small_printf ("SetCommTimeout failed\n");
! __seterrno ();
! return -1;
! }
! } /* if a tty */
! } /* if non-blocking is set */
! } /* blocking I/O */
!
! syscall_printf ("fcntl (%d, F_SETFL, %d);\n", arg);
!
! this_procinfo()->hmap[fd].h->set_access( temp );
! res = 0;
! goto done;
!
! } /* scope only */
!
!
!
case F_GETLK:
case F_SETLK:
*** winsup.sergey/fhandler.cc Sat May 31 23:56:14 1997
--- winsup/fhandler.cc Sun Jun 01 10:09:59 1997
***************
*** 1261,1266 ****
--- 1261,1358 ----
paranoid_printf ("FHANDLER TTY\n");
}
+
+
+
+
+ fhandler_base *
+ fhandler_tty::open( const char *path, int flags, mode_t mode )
+ {
+ fhandler_base *res = NULL;
+ int temp = 0;
+ int access;
+ HANDLE myhandle;
+ COMMTIMEOUTS to;
+
+ syscall_printf( "fhandler_tty::open( %s, 0x%x, 0x%x )\n",
+ path, flags, mode );
+
+ if( (res = fhandler_base::open( path, flags, mode ) ) == NULL )
+ return( NULL );
+
+ memset( &to, 0, sizeof(to) );
+
+ myhandle = fhandler_base::get_handle();
+ access = fhandler_base::get_access();
+ temp |= (access & (GENERIC_READ | GENERIC_WRITE));
+
+ if( flags & O_NDELAY ) /* non-blocking I/O */
+ {
+ temp |= _FNDELAY;
+
+ /* probably not necessary */
+ if( GetCommTimeouts( myhandle, &to ) == 0 )
+ {
+ small_printf ("GetCommTimeout failed\n");
+ __seterrno ();
+ return( NULL );
+ }
+
+ to.ReadIntervalTimeout = MAXDWORD;
+ to.ReadTotalTimeoutMultiplier = 0;
+ to.ReadTotalTimeoutConstant = 0;
+
+ /* leave the values vmin_ and vtime_ alone for restoral later */
+
+ if( SetCommTimeouts( myhandle, &to ) == 0 )
+ {
+ small_printf ("SetCommTimeout failed\n");
+ __seterrno ();
+ return( NULL );
+ }
+
+ } /* non-blocking I/O */
+ else
+ { /* blocking I/O */
+
+ /* if non-blocking is set, unset it */
+ if( access & _FNDELAY )
+ {
+ /* probably not necessary */
+ /*
+ if( GetCommTimeouts( myhandle, &to ) == 0 )
+ {
+ small_printf ("GetCommTimeout failed\n");
+ __seterrno ();
+ return( NULL );
+ }
+
+ we have no access to vtime_ or vmin_
+ to.ReadIntervalTimeout = 0;
+ to.ReadTotalTimeoutMultiplier = 0;
+ to.ReadTotalTimeoutConstant = vtime_ * 100;
+ */
+
+ if( SetCommTimeouts( myhandle, &to ) == 0 )
+ {
+ small_printf ("SetCommTimeout failed\n");
+ __seterrno ();
+ return( NULL );
+ }
+ } /* if non-blocking is set */
+ } /* blocking I/O */
+
+ fhandler_base::set_access( temp );
+
+ syscall_printf( "%p = fhandler_tty::open( %s, 0x%x, 0x%x )\n",
+ res, path, flags, mode );
+ return res;
+ } /* tty open() */
+
+
+
+
+
int
fhandler_tty::fstat (struct stat *buf)
{
***************
*** 1276,1281 ****
--- 1368,1448 ----
return 0;
}
+
+
+
+
+ int
+ fhandler_tty::tcsendbreak( int duration )
+ {
+ unsigned int sleeptime = 350;
+ if( duration > 1 )
+ sleeptime *= duration;
+
+
+ if( SetCommBreak( get_handle() ) == 0 )
+ return( -1 );
+ usleep( sleeptime );
+ if( ClearCommBreak( get_handle() ) == 0 )
+ return( -1 );
+ return( 0 );
+ }
+
+
+
+
+ int
+ fhandler_tty::tcdrain( void )
+ {
+ if( FlushFileBuffers( get_handle() ) == 0 )
+ return( -1 );
+ return( 0 );
+ }
+
+
+
+
+ int
+ fhandler_tty::tcflow( int action )
+ {
+ DCB dcb;
+ DWORD winaction = 0;
+ char xchar;
+
+ switch( action )
+ {
+ case TCOOFF:
+ winaction = SETXOFF;
+ break;
+ case TCOON:
+ winaction = SETXON;
+ break;
+ case TCION:
+ case TCIOFF:
+ if( GetCommState( get_handle(), &dcb ) == 0 )
+ return( -1 );
+ if( action == TCION )
+ xchar = (dcb.XonChar ? dcb.XonChar : 0x11);
+ else
+ xchar = (dcb.XoffChar ? dcb.XoffChar : 0x13);
+ if( TransmitCommChar( get_handle(), xchar ) == 0 )
+ return( -1 );
+ return( 0 );
+ break;
+ default:
+ return( -1 );
+ break;
+ }
+
+ if( EscapeCommFunction( get_handle(), winaction ) == 0 )
+ return( -1 );
+ return( 0 );
+ }
+
+
+
+
+
int
fhandler_tty::tcflush (int queue)
{
***************
*** 1312,1500 ****
return 0;
}
int
! fhandler_tty::tcsetattr (int, const struct termios *t)
{
- int newrate;
- int newsize;
! COMMTIMEOUTS to;
! DCB state;
- switch (t->c_ospeed)
- {
- case B110:
- newrate = CBR_110;
- break;
- case B300:
- newrate = CBR_300;
- break;
- case B600:
- newrate = CBR_600;
- break;
- case B1200:
- newrate = CBR_1200;
- break;
- case B2400:
- newrate = CBR_2400;
- break;
- case B4800:
- newrate = CBR_4800;
- break;
- case B9600:
- newrate = CBR_9600;
- break;
- case B19200:
- newrate = CBR_19200;
- break;
- case B38400:
- newrate = CBR_38400;
- break;
- default:
- termios_printf ("t->c_ospeed was %d\n", t->c_ospeed);
- set_errno ( EINVAL);
- return -1;
- }
! switch (t->c_cflag & CSIZE)
! {
! case CS5:
! newsize = 5;
! break;
! case CS6:
! newsize = 6;
! break;
! case CS7:
! newsize = 7;
! break;
! case CS8:
! newsize = 8;
! break;
! default:
! newsize = 8;
! }
!
! GetCommState (get_handle (), &state);
#if 0
! ds ("First in tcsetattr", &state);
#endif
! state.BaudRate = newrate;
! state.ByteSize = newsize;
! state.fBinary = 1;
! state.fParity = 0;
! state.fOutxCtsFlow = 0; /*!!*/
! state.fOutxDsrFlow = 0; /*!!*/
! state.fDsrSensitivity = 0; /*!!*/
!
! if (t->c_cflag & PARENB)
! state.Parity = (t->c_cflag & PARODD) ? ODDPARITY:EVENPARITY;
! else
! state.Parity = NOPARITY;
#if 0
! ds ("Before SetCommState", &state);
#endif
! SetCommState (get_handle (), &state);
- set_r_binary ((t->c_iflag & IGNCR) ? 0 : 1);
- set_w_binary ((t->c_oflag & ONLCR) ? 0 : 1);
- vtime_ = t->c_cc[VTIME];
- vmin_ = t->c_cc[VMIN];
- memset (&to, 0, sizeof (to));
- to.ReadTotalTimeoutConstant = vtime_ * 100;
- int res = SetCommTimeouts (get_handle (), &to);
- if (!res)
- {
- small_printf ("CommTimeout failed\n");
- __seterrno ();
- return -1;
- }
- // tdump (fd);
- return 0;
- }
int
! fhandler_tty::tcgetattr (struct termios *t)
{
! DCB state;
! int thisspeed;
! int thissize;
- GetCommState (get_handle (), &state);
#if 0
! ds ("In tcgetattr", &state);
#endif
- switch (state.BaudRate)
- {
- case CBR_110:
- thisspeed = B110;
- break;
- case CBR_300:
- thisspeed = B300;
- break;
- case CBR_600:
- thisspeed = B600;
- break;
- case CBR_1200:
- thisspeed = B1200;
- break;
- case CBR_2400:
- thisspeed = B2400;
- break;
- case CBR_4800:
- thisspeed = B4800;
- break;
- case CBR_9600:
- thisspeed = B9600;
- break;
- case CBR_19200:
- thisspeed = B19200;
- break;
- case CBR_38400:
- thisspeed = B38400;
- break;
- default:
- thisspeed = B9600;
- set_errno ( EINVAL);
- }
- switch (state.ByteSize)
- {
- case 5:
- thissize = CS5;
- break;
- case 6:
- thissize = CS6;
- break;
- case 7:
- thissize = CS7;
- break;
- default:
- case 8:
- thissize = CS8;
- break;
- }
! memset (t, 0, sizeof (*t));
- t->c_ospeed = t->c_ispeed = thisspeed;
- t->c_cflag |= thissize;
#if 0 /* IGNCR doesn't work yet */
! if (!get_r_binary ())
! t->c_iflag |= IGNCR;
#endif
! if (!get_w_binary ())
! t->c_oflag |= ONLCR;
- t->c_cc[VTIME] =vtime_;
- t->c_cc[VMIN] = vmin_;
- // tdump (fd);
- return 0;
- }
/**********************************************************************/
/* /dev/null */
--- 1479,1899 ----
return 0;
}
+
+
+
+
+
int
! fhandler_tty::tcsetattr( int action, const struct termios *t )
{
! /* possible actions are:
! TCSANOW - do the change immediately.
! TCSADRAIN - change after flushing output.
! TCSAFLUSH - change after flushing output and discarding input.
! */
!
! BOOL dropDTR = FALSE;
! COMMTIMEOUTS to;
! DCB state;
!
! if( (action == TCSADRAIN) || (action == TCSAFLUSH) )
! FlushFileBuffers( get_handle() );
! if( action == TCSAFLUSH )
! PurgeComm( get_handle(), (PURGE_RXABORT | PURGE_RXCLEAR) );
! /* get default/last comm state */
! GetCommState (get_handle (), &state);
#if 0
! ds ("First in tcsetattr", &state);
#endif
!
!
!
! /* *********** baud rate *********** */
! switch (t->c_ospeed)
! {
! case B0:
! /* drop DTR */
! dropDTR = TRUE;
! state.BaudRate = 0;
! break;
! case B110:
! state.BaudRate = CBR_110;
! break;
! case B300:
! state.BaudRate = CBR_300;
! break;
! case B600:
! state.BaudRate = CBR_600;
! break;
! case B1200:
! state.BaudRate = CBR_1200;
! break;
! case B2400:
! state.BaudRate = CBR_2400;
! break;
! case B4800:
! state.BaudRate = CBR_4800;
! break;
! case B9600:
! state.BaudRate = CBR_9600;
! break;
! case B19200:
! state.BaudRate = CBR_19200;
! break;
! case B38400:
! state.BaudRate = CBR_38400;
! break;
! /*
! case B57600:
! state.BaudRate = CBR_57600;
! break;
! case B115200:
! state.BaudRate = CBR_115200;
! break;
! */
! /* WIN32 also has 14400, 56000, 128000, and 256000 */
! /* Unix also has 230400 */
! default:
! termios_printf ("t->c_ospeed was %d\n", t->c_ospeed);
! set_errno ( EINVAL);
! return -1;
! }
!
! /* *********** byte size *********** */
! switch (t->c_cflag & CSIZE)
! {
! case CS5:
! state.ByteSize = 5;
! break;
! case CS6:
! state.ByteSize = 6;
! break;
! case CS7:
! state.ByteSize = 7;
! break;
! case CS8:
! default:
! state.ByteSize = 8;
! break;
! }
!
!
! /* *********** stop bits *********** */
! if( t->c_cflag & CSTOPB )
! state.StopBits = TWOSTOPBITS;
! else
! state.StopBits = ONESTOPBIT;
!
!
! /* *********** parity *********** */
! if (t->c_cflag & PARENB)
! state.Parity = (t->c_cflag & PARODD) ? ODDPARITY:EVENPARITY;
! else
! state.Parity = NOPARITY;
!
!
! state.fBinary = TRUE; /* binary transfer */
! state.EofChar = 0; /* no end-of-data in binary mode */
! state.fNull = FALSE; /* don't discard nulls in binary mode */
!
!
! /* *********** parity errors *********** */
! /* fParity combines the function of INPCK and NOT IGNPAR */
! if( (t->c_iflag & INPCK) && (! t->c_iflag & IGNPAR) )
! state.fParity = TRUE; /* react to parity errors */
! else
! state.fParity = FALSE; /* ignore parity errors */
!
! /* Unix has no equivalent to these */
! state.fErrorChar = FALSE;
! state.ErrorChar = 0;
!
!
! /* *********** software flow control *********** */
! /* if the remote device interprets any received character as XON (the
! equivalent of IXANY at the remote side), then we must set
! fTXContinueOnXoff to FALSE to not trigger a premature XON,
! otherwise, a TRUE value separates the TX and RX functions. */
! state.fTXContinueOnXoff = TRUE; /* separate TX and RX flow control */
!
! if( t->c_iflag & IXON ) /* transmission flow control */
! {
! state.fOutX = TRUE; /* enable */
! }
! else
! {
! state.fOutX = FALSE; /* disable */
! }
!
! if( t->c_iflag & IXOFF ) /* reception flow control */
! {
! state.fInX = TRUE; /* enable */
! /* XoffLim and XonLim are left at default values */
! }
! else
! {
! state.fInX = FALSE; /* disable */
! /* XoffLim and XonLim are left at default values */
! }
! state.XonChar = (t->c_cc[VSTART] ? t->c_cc[VSTART] : 0x11);
! state.XoffChar = (t->c_cc[VSTOP] ? t->c_cc[VSTOP] : 0x13);
!
!
! /* *********** hardware flow control *********** */
! state.fOutxDsrFlow = FALSE; /* disable DSR flow control */
!
! /* older versions of Unix presumed hardware flow control if
! software flow control is not enabled. Newer Unices seem to
! require explicit setting of hardware flow-control and this
! code reflects that scheme. */
! /* CLOCAL causes all lead control, except DTR, to be ignored */
! /* input flow-control */
! if( (t->c_cflag & CRTSXOFF) && ( ! (t->c_cflag & CLOCAL) ) )
! {
! state.fRtsControl = RTS_CONTROL_HANDSHAKE; /* enable */
! }
! else
! {
! state.fRtsControl = RTS_CONTROL_DISABLE; /* disable */
! }
!
! /* output flow-control */
! if( (t->c_cflag & CRTSCTS) && ( ! (t->c_cflag & CLOCAL) ) )
! {
! state.fOutxCtsFlow = TRUE; /* enable */
! }
! else
! {
! state.fOutxCtsFlow = FALSE; /* disable */
! }
!
!
!
! /* *********** DTR *********** */
! state.fDtrControl = DTR_CONTROL_ENABLE; /* assert DTR when device
! is opened */
!
! /* *********** DSR *********** */
! if( t->c_cflag & CLOCAL )
! state.fDsrSensitivity = FALSE; /* ignored */
! else
! state.fDsrSensitivity = TRUE; /* DSR must be asserted at device */
!
! /* *********** error handling *********** */
! state.fAbortOnError = TRUE; /* read/write operations terminate upon
! error - requires ClearCommError() to
! resume. */
!
!
#if 0
! ds ("Before SetCommState", &state);
#endif
! SetCommState( get_handle(), &state );
!
! set_r_binary ((t->c_iflag & IGNCR) ? 0 : 1);
! set_w_binary ((t->c_oflag & ONLCR) ? 0 : 1);
!
! vtime_ = t->c_cc[VTIME];
! vmin_ = t->c_cc[VMIN];
!
! if( dropDTR == TRUE )
! {
! EscapeCommFunction( get_handle(), CLRDTR );
! }
! else
! {
! /* in case we previously set CLRDTR and setting
! state.fDtrControl = DTR_CONTROL_ENABLE doesn't do
! the trick. This relationship needs to be discovered
! since a program might want to change some
! parameters while DTR is still down. This would require
! setting some state and testing it.
! */
! EscapeCommFunction( get_handle(), SETDTR );
! }
!
! memset (&to, 0, sizeof (to));
!
! to.ReadTotalTimeoutConstant = vtime_ * 100;
!
! int res = SetCommTimeouts( get_handle (), &to );
! if( ! res )
! {
! small_printf ("CommTimeout failed\n");
! __seterrno ();
! return -1;
! }
! // tdump (fd);
! return 0;
! } /* tcsetattr() */
!
int
! fhandler_tty::tcgetattr( struct termios *t )
{
! DCB state;
!
! /* get current comm state */
! GetCommState (get_handle (), &state);
!
! /* just in case */
! memset (t, 0, sizeof (*t));
#if 0
! ds ("In tcgetattr", &state);
#endif
! /* *********** baud rate *********** */
! switch (state.BaudRate)
! {
! case 0: /* B0 - DTR should be dropped */
! t->c_cflag = t->c_ospeed = t->c_ispeed = B0;
! break;
!
! case CBR_110:
! t->c_cflag = t->c_ospeed = t->c_ispeed = B110;
! break;
! case CBR_300:
! t->c_cflag = t->c_ospeed = t->c_ispeed = B300;
! break;
! case CBR_600:
! t->c_cflag = t->c_ospeed = t->c_ispeed = B600;
! break;
! case CBR_1200:
! t->c_cflag = t->c_ospeed = t->c_ispeed = B1200;
! break;
! case CBR_2400:
! t->c_cflag = t->c_ospeed = t->c_ispeed = B2400;
! break;
! case CBR_4800:
! t->c_cflag = t->c_ospeed = t->c_ispeed = B4800;
! break;
! case CBR_9600:
! t->c_cflag = t->c_ospeed = t->c_ispeed = B9600;
! break;
! case CBR_19200:
! t->c_cflag = t->c_ospeed = t->c_ispeed = B19200;
! break;
! case CBR_38400:
! t->c_cflag = t->c_ospeed = t->c_ispeed = B38400;
! break;
! /*
! case CBR_57600:
! t->c_cflag = t->c_ospeed = t->c_ispeed = B57600;
! break;
! case CBR_115200:
! t->c_cflag = t->c_ospeed = t->c_ispeed = B115200;
! break;
! */
! default:
! /* t->c_cflag = t->c_ospeed = t->c_ispeed = B9600; */
! set_errno ( EINVAL);
! }
!
!
! /* *********** byte size *********** */
! switch (state.ByteSize)
! {
! case 5:
! t->c_cflag |= CS5;
! break;
! case 6:
! t->c_cflag |= CS6;
! break;
! case 7:
! t->c_cflag |= CS7;
! break;
! case 8:
! t->c_cflag |= CS8;
! break;
! default:
! break;
! }
!
!
! /* *********** stop bits *********** */
! if( state.StopBits == TWOSTOPBITS )
! t->c_cflag |= CSTOPB;
!
!
! /* *********** parity *********** */
! if( state.Parity == ODDPARITY )
! t->c_cflag |= (PARENB | PARODD);
! if( state.Parity == EVENPARITY )
! t->c_cflag |= PARENB;
!
!
! /* *********** parity errors *********** */
! /* fParity combines the function of INPCK and NOT IGNPAR */
! if( state.fParity == TRUE )
! t->c_iflag |= INPCK;
! else
! t->c_iflag |= IGNPAR; /* not necessarily! */
!
!
! /* *********** software flow control *********** */
! /* transmission flow control */
! if( state.fOutX == TRUE )
! t->c_iflag |= IXON;
!
! /* reception flow control */
! if( state.fInX == TRUE )
! t->c_iflag |= IXOFF;
!
! t->c_cc[VSTART] = ( state.XonChar ? state.XonChar : 0x11 );
! t->c_cc[VSTOP] = ( state.XoffChar ? state.XoffChar : 0x13 );
!
!
! /* *********** hardware flow control *********** */
! /* older versions of Unix presumed hardware flow control if
! software flow control is not enabled. Newer Unices seem to
! require explicit setting of hardware flow-control and this
! code reflects that scheme. */
! /* input flow-control */
! if( state.fRtsControl == RTS_CONTROL_HANDSHAKE )
! t->c_cflag |= CRTSXOFF;
!
! /* output flow-control */
! if( state.fOutxCtsFlow == TRUE )
! t->c_cflag |= CRTSCTS;
!
! /* *********** CLOCAL *********** */
! /* DSR is the only lead toggled ONLY by CLOCAL, so we can use it
! as a flag that CLOCAL was called. This presumes that
! tcsetattr() was called previously; if not, this may give
! a false CLOCAL. */
! if( state.fDsrSensitivity == FALSE )
! t->c_cflag |= CLOCAL;
!
#if 0 /* IGNCR doesn't work yet */
! if (!get_r_binary ())
! t->c_iflag |= IGNCR;
#endif
! if (!get_w_binary ())
! t->c_oflag |= ONLCR;
!
! t->c_cc[VTIME] = vtime_;
! t->c_cc[VMIN] = vmin_;
!
! // tdump (fd);
! return 0;
! } /* tcgetattr() */
!
!
!
/**********************************************************************/
/* /dev/null */
*** winsup.sergey/fhandler.h Sat May 31 23:56:14 1997
--- winsup/fhandler.h Sun Jun 01 10:10:00 1997
***************
*** 110,120 ****
virtual void dump ();
virtual int dup (fhandler_base *child);
- virtual int tcflush (int) { return -1; }
void *operator new (size_t, void *);
virtual void init (HANDLE, int, int, const char *);
virtual int tcsetattr (int a, const struct termios *t) { return -1; }
virtual int tcgetattr (struct termios *t) { return -1; }
virtual int is_tty () { return 0; }
--- 110,124 ----
virtual void dump ();
virtual int dup (fhandler_base *child);
void *operator new (size_t, void *);
virtual void init (HANDLE, int, int, const char *);
+
+ virtual int tcflush (int) { return -1; }
+ virtual int tcsendbreak( int ) { return 0; }
+ virtual int tcdrain( void ) { return 0; }
+ virtual int tcflow( int ) { return 0; }
virtual int tcsetattr (int a, const struct termios *t) { return -1; }
virtual int tcgetattr (struct termios *t) { return -1; }
virtual int is_tty () { return 0; }
***************
*** 200,206 ****
--- 204,214 ----
/* Constructor */
fhandler_tty (const char *name = 0);
+ virtual fhandler_base *open( const char *path, int flags, mode_t mode );
virtual int raw_read (void *ptr, size_t ulen);
+ virtual int tcsendbreak( int );
+ virtual int tcdrain( void );
+ virtual int tcflow( int );
virtual int tcsetattr (int a, const struct termios *t);
virtual int tcgetattr (struct termios *t);
virtual int fstat (struct stat *buf);
*** winsup.sergey/include/sys/termios.h Sat May 31 23:56:14 1997
--- winsup/include/sys/termios.h Sun Jun 01 10:10:00 1997
***************
*** 34,39 ****
--- 34,40 ----
/* iflag bits */
+ /*
#define IGNBRK 0x0001
#define BRKINT 0x0002
#define IGNPAR 0x0004
***************
*** 41,46 ****
--- 42,55 ----
#define ISTRIP 0x0020
#define INLCR 0x0040
#define IGNCR 0x0080
+ */
+ #define IGNBRK 000001
+ #define BRKINT 000002
+ #define IGNPAR 000004
+ #define INPCK 000020
+ #define ISTRIP 000040
+ #define INLCR 000100
+ #define IGNCR 000200
#define ICRNL 000400
#define IXON 002000
#define IXOFF 010000
***************
*** 49,54 ****
--- 58,64 ----
#define IXANY 100000
+ /* oflag bits */
#define OPOST 000001
#define OCRNL 000004
#define ONLCR 000010
***************
*** 57,62 ****
--- 67,73 ----
#define TAB3 014000
+ /* cflag bits */
#define CLOCAL 004000
#define CREAD 000200
#define CSIZE 000060
***************
*** 68,73 ****
--- 79,86 ----
#define HUPCL 002000
#define PARENB 000400
#define PARODD 001000
+ #define CRTSXOFF 010000000000
+ #define CRTSCTS 020000000000
/* lflag bits */
#define ISIG 0x0001
***************
*** 101,106 ****
--- 114,120 ----
#define NCCS 18
+ #define CBAUD 030017
#define B0 000000
#define B50 000001
#define B75 000002
***************
*** 117,122 ****
--- 131,138 ----
#define B9600 000015
#define B19200 000016
#define B38400 000017
+ #define B57600 010000
+ #define B115200 020000
typedef unsigned char cc_t;
typedef unsigned short tcflag_t;
***************
*** 139,144 ****
--- 155,179 ----
#define cfgetispeed(tp) ((tp)->c_ispeed)
#define cfsetospeed(tp,s) (((tp)->c_ospeed = (s)), 0)
#define cfsetispeed(tp,s) (((tp)->c_ispeed = (s)), 0)
+
+ #ifdef NOTCODE
+ /* changes by MKA */
+ #ifdef __cplusplus
+ extern "C" {
+ #endif
+
+ int tcgetattr( int, struct termios * );
+ int tcsetattr( int, int, struct termios * );
+ int tcsendbreak( int, int );
+ int tcdrain( int );
+ int tcflush( int, int );
+ int tcflow( int, int );
+
+ #ifdef __cplusplus
+ }
+ #endif
+ #endif /* NOTCODE */
+
/* Extra stuff to make porting stuff easier. */
*** winsup.sergey/termios.cc Sat May 31 23:56:15 1997
--- winsup/termios.cc Sun Jun 01 10:10:00 1997
***************
*** 23,67 ****
#include "sys/termios.h"
int
! tcsendbreak (int, int )
{
! syscall_printf ("tcsendbreak\n");
! return 0;
}
int
! tcdrain (int)
{
! syscall_printf ("tcdrain\n");
! return 0;
}
int
! tcflush (int fd, int queue)
{
! int res = 0;
! if (NOT_OPEN_FD (fd))
! {
! set_errno ( EBADF);
! res = -1;
! }
! else
! {
! res = u->self->hmap[fd].h->tcflush (queue);
! }
!
! syscall_printf ("%d = tcflush (%d, %d);\n", res, fd,queue);
! return res;
}
int
! tcflow (int, int)
{
! syscall_printf ("tcflow\n");
! return 0;
}
#if 0
static void
tdump (int)
--- 23,100 ----
#include "sys/termios.h"
int
! tcsendbreak( int fd, int duration )
{
! int res = -1;
!
! if( NOT_OPEN_FD( fd ) )
! {
! set_errno ( EBADF );
! }
! else
! {
! res = u->self->hmap[fd].h->tcsendbreak( duration );
! }
! syscall_printf( "%d = tcsendbreak( %d, %d );\n", res, fd, duration );
! return res;
}
int
! tcdrain( int fd )
{
! int res = -1;
!
! if( NOT_OPEN_FD( fd ) )
! {
! set_errno ( EBADF );
! }
! else
! {
! res = u->self->hmap[fd].h->tcdrain();
! }
!
! syscall_printf( "%d = tcdrain( %d );\n", res, fd );
! return res;
}
int
! tcflush( int fd, int queue )
{
! int res = -1;
! if( NOT_OPEN_FD( fd ) )
! {
! set_errno ( EBADF);
! }
! else
! {
! res = u->self->hmap[fd].h->tcflush( queue );
! }
! syscall_printf( "%d = tcflush( %d, %d );\n", res, fd, queue );
! return res;
}
int
! tcflow( int fd, int action )
{
! int res = -1;
!
! if( NOT_OPEN_FD( fd ) )
! {
! set_errno ( EBADF );
! }
! else
! {
! res = u->self->hmap[fd].h->tcflow( action );
! }
!
! syscall_printf( "%d = tcflow( %d, %d );\n", res, fd, action );
! return res;
}
+
+
+
#if 0
static void
tdump (int)
***************
*** 112,150 ****
#endif
int
! tcsetattr (int fd, int a, const struct termios *t)
{
! int res = -1;
! if (NOT_OPEN_FD (fd))
! {
! set_errno ( EBADF);
! }
! else
! {
! res = u->self->hmap[fd].h->tcsetattr (a, t);
! }
! syscall_printf ("%d = tcsetattr (%d, %d, %x);\n", res, fd, a, t);
! return res;
}
int
! tcgetattr (int fd, struct termios *t)
{
! int res = -1;
!
! if (NOT_OPEN_FD (fd))
! {
! set_errno ( EBADF);
! }
! else
! {
! res = u->self->hmap[fd].h->tcgetattr (t);
! }
! syscall_printf ("%d = tcgetattr (%d, %x);\n", res, fd, t);
! return res;
}
int
--- 145,183 ----
#endif
int
! tcsetattr( int fd, int a, const struct termios *t )
{
! int res = -1;
! if( NOT_OPEN_FD( fd ) )
! {
! set_errno ( EBADF );
! }
! else
! {
! res = u->self->hmap[fd].h->tcsetattr( a, t );
! }
! syscall_printf( "%d = tcsetattr( %d, %d, %x );\n", res, fd, a, t );
! return res;
}
int
! tcgetattr( int fd, struct termios *t )
{
! int res = -1;
!
! if( NOT_OPEN_FD( fd ) )
! {
! set_errno( EBADF );
! }
! else
! {
! res = u->self->hmap[fd].h->tcgetattr( t );
! }
! syscall_printf( "%d = tcgetattr( %d, %x );\n", res, fd, t );
! return res;
}
int
--------------49597C673EEAD2813A29F035
Content-Type: text/plain; charset=us-ascii; name="fhandler.cc"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline; filename="fhandler.cc"
/* fhandler.cc: winsup file handling
see console.cc for fhandler_console functions
Copyright 1996, 1997 Cygnus Solutions
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
#include <sys/termios.h>
#include <fcntl.h>
#include <unistd.h>
#include "winsup.h"
static const int CHUNK_SIZE=1024; /* Used for crlf conversions */
uid_t
get_file_owner (char *filename)
{
static BOOL doit = TRUE;
if (doit && is_nt())
{
char psd_buffer[1024];
PSECURITY_DESCRIPTOR psd = (PSECURITY_DESCRIPTOR) psd_buffer;
DWORD requested_length;
PSID psid;
BOOL bOwnerDefaulted = TRUE;
if (!GetFileSecurity (filename, OWNER_SECURITY_INFORMATION,
psd, 1024, &requested_length))
return getuid();
if (!GetSecurityDescriptorOwner (psd, &psid, &bOwnerDefaulted))
return getuid ();
return psid ? get_id_from_sid(psid) : getuid();
}
return getuid();
}
gid_t
get_file_group (char *filename)
{
static BOOL doit = TRUE;
if (doit && is_nt ())
{
char psd_buffer[1024];
PSECURITY_DESCRIPTOR psd = (PSECURITY_DESCRIPTOR) psd_buffer;
DWORD requested_length;
PSID psid;
BOOL bGroupDefaulted = TRUE;
/* obtain the file's group information */
if (!GetFileSecurity (filename, GROUP_SECURITY_INFORMATION, psd,
1024, &requested_length))
return getgid();
/* extract the group sid from the security descriptor */
if(!GetSecurityDescriptorGroup(psd, &psid, &bGroupDefaulted))
return getgid ();
return psid ? get_id_from_sid(psid) : getuid ();
}
return getgid ();
}
void
hinfo::clearout ()
{
h = 0;
}
int
fhandler_make_pipe (int fildes[2])
{
int fdr, fdw;
pinfo *p = this_procinfo ();
fdr = p->hmap.find_unused_handle (0);
if (fdr < 0)
set_errno (ENMFILE);
else
{
fdw = p->hmap.find_unused_handle (fdr+1);
if (fdw < 0)
set_errno ( ENMFILE);
else
{
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof (sa);
sa.lpSecurityDescriptor = 0;
/* When we fork we duplicate all the file handles to be inherited,
therefore all fds must be created as non-inheritable if we're the
parent. We still set the close-on-exec flag to "no" though. */
/* FIXME: This comment is out of date. Gee, what a surprise. */
sa.bInheritHandle = 1;
HANDLE r, w;
if (CreatePipe (&r, &w, &sa, 0))
{
u->self->hmap.build_fhandler ("piper", fdr, r);
u->self->hmap.build_fhandler ("pipew", fdw, w);
((fhandler_base *) p->hmap[fdr].h)->init (r, 1, GENERIC_READ, "piper");
((fhandler_base *) p->hmap[fdw].h)->init (w, 1, GENERIC_WRITE, "pipew");
fildes[0] = fdr;
fildes[1] = fdw;
debug_printf ("0 = pipe (%d) (%d:%d, %d:%d)\n",
fildes,
fdr,
p->hmap[fdr].h->get_handle (),
fdw,
p->hmap[fdw].h->get_handle ());
return 0;
}
else
{
__seterrno ();
}
}
}
syscall_printf ("-1 = pipe (0x%x)\n", fildes);
return -1;
}
/**********************************************************************/
/* fhandler_base */
/* Record the file name.
Filenames are used mostly for debugging messages, and it's hoped that
in cases where the name is really required, the filename wouldn't ever
be too long (e.g. devices or some such).
*/
void
fhandler_base::set_name (const char *p)
{
if (unix_path_name_ != 0)
{
delete [] unix_path_name_;
}
if (p == 0)
{
unix_path_name_ = 0;
return;
}
unix_path_name_ = new char [ strlen(p) + 1 ];
if (unix_path_name_ == 0)
{
small_printf ("fhandler_base::set_name - fatal error. New failed\n");
exit (ENOMEM);
}
strcpy (unix_path_name_, p);
}
/* Normal file i/o handlers. */
/* Cover function to ReadFile to achieve (as much as possible) Posix style
semantics and use of errno. */
int
fhandler_base::raw_read (void *ptr, size_t ulen)
{
DWORD bytes_read;
int len = ulen;
if (!ReadFile (get_handle(), ptr, len, &bytes_read, 0))
{
int errcode;
/* Some errors are not really errors. Detect such cases here. */
errcode = GetLastError ();
switch (errcode)
{
case ERROR_BROKEN_PIPE:
/* This is really EOF. */
bytes_read = 0;
break;
case ERROR_MORE_DATA:
/* `bytes_read' is supposedly valid. */
break;
default:
syscall_printf ("ReadFile %s failed\n", unix_path_name_);
set_errno (EACCES);
return -1;
break;
}
}
return bytes_read;
}
int
fhandler_base::linearize (unsigned char *buf)
{
int len = (sizeof (access_) + sizeof (handle_) + sizeof (w_binary_) +
sizeof (r_binary_) + sizeof (close_exec_p_) +
sizeof (readahead_valid_) + sizeof (readahead_) +
sizeof (append_p_) + sizeof (rpos_) + sizeof (rsize_) +
sizeof (had_eof_) + sizeof (symlink_p_) + sizeof (execable_p_) +
sizeof (namehash_));
if (buf == 0)
return len;
memcpy (buf, (char *) &access_, sizeof (access_));
buf += sizeof (access_);
memcpy (buf, (char *) &handle_, sizeof (handle_));
buf += sizeof (handle_);
memcpy (buf, (char *) &w_binary_, sizeof (w_binary_));
buf += sizeof (w_binary_);
memcpy (buf, (char *) &r_binary_, sizeof (r_binary_));
buf += sizeof (r_binary_);
memcpy (buf, (char *) &close_exec_p_, sizeof (close_exec_p_));
buf += sizeof (close_exec_p_);
memcpy (buf, (char *) &readahead_valid_, sizeof (readahead_valid_));
buf += sizeof (readahead_valid_);
memcpy (buf, (char *) &readahead_, sizeof (readahead_));
buf += sizeof (readahead_);
memcpy (buf, (char *) &append_p_, sizeof (append_p_));
buf += sizeof (append_p_);
memcpy (buf, (char *) &rpos_, sizeof (rpos_));
buf += sizeof (rpos_);
memcpy (buf, (char *) &rsize_, sizeof (rsize_));
buf += sizeof (rsize_);
memcpy (buf, (char *) &had_eof_, sizeof (had_eof_));
buf += sizeof (had_eof_);
memcpy (buf, (char *) &symlink_p_, sizeof (symlink_p_));
buf += sizeof (symlink_p_);
memcpy (buf, (char *) &execable_p_, sizeof (execable_p_));
buf += sizeof (execable_p_);
memcpy (buf, (char *) &namehash_, sizeof (namehash_));
buf += sizeof (namehash_);
return len;
}
int
fhandler_base::de_linearize (const unsigned char *buf)
{
const unsigned char *orig_buf = buf;
memcpy ((char *) &access_, buf, sizeof (access_));
buf += sizeof (access_);
memcpy ((char *) &handle_, buf, sizeof (handle_));
buf += sizeof (handle_);
memcpy ((char *) &w_binary_, buf, sizeof (w_binary_));
buf += sizeof (w_binary_);
memcpy ((char *) &r_binary_, buf, sizeof (r_binary_));
buf += sizeof (r_binary_);
memcpy ((char *) &close_exec_p_, buf, sizeof (close_exec_p_));
buf += sizeof (close_exec_p_);
memcpy ((char *) &readahead_valid_, buf, sizeof (readahead_valid_));
buf += sizeof (readahead_valid_);
memcpy ((char *) &readahead_, buf, sizeof (readahead_));
buf += sizeof (readahead_);
memcpy ((char *) &append_p_, buf, sizeof (append_p_));
buf += sizeof (append_p_);
memcpy ((char *) &rpos_, buf, sizeof (rpos_));
buf += sizeof (rpos_);
memcpy ((char *) &rsize_, buf, sizeof (rsize_));
buf += sizeof (rsize_);
memcpy ((char *) &had_eof_, buf, sizeof (had_eof_));
buf += sizeof (had_eof_);
memcpy ((char *) &symlink_p_, buf, sizeof (symlink_p_));
buf += sizeof (symlink_p_);
memcpy ((char *) &execable_p_, buf, sizeof (execable_p_));
buf += sizeof (execable_p_);
memcpy ((char *) &namehash_, buf, sizeof (namehash_));
buf += sizeof (namehash_);
return buf - orig_buf;
}
/* Cover function to WriteFile to provide Posix interface and semantics
(as much as possible). */
int
fhandler_base::raw_write (const void *ptr, size_t len)
{
DWORD bytes_written;
if (!WriteFile (get_handle(), ptr, len, &bytes_written, 0))
{
__seterrno ();
if (get_errno () == EPIPE)
raise (SIGPIPE);
return -1;
}
return bytes_written;
}
/* Open system call handler function.
Path is now already checked for symlinks*/
fhandler_base *
fhandler_base::open (const char *path, int flags, mode_t mode)
{
fhandler_base * res = NULL;
HANDLE x;
int file_attributes;
int shared;
SECURITY_ATTRIBUTES sa;
int creation_distribution;
syscall_printf ("fhandler_base::open (%s, 0x%x)\n", path, flags);
path_conv win32_path (path, -1);
if (win32_path.error)
{
set_errno (win32_path.error);
goto done;
}
if ((flags & (O_RDONLY | O_WRONLY | O_RDWR)) == O_RDONLY)
{
access_ = GENERIC_READ;
}
else if ((flags & (O_RDONLY | O_WRONLY | O_RDWR)) == O_WRONLY)
{
access_ = GENERIC_WRITE;
}
else
{
access_ = GENERIC_READ | GENERIC_WRITE;
}
/* FIXME: O_EXCL handling? */
if ((flags & O_TRUNC) && ((flags & O_ACCMODE) != O_RDONLY))
{
if (flags & O_CREAT)
{
creation_distribution = CREATE_ALWAYS;
}
else
{
creation_distribution = TRUNCATE_EXISTING;
}
}
else
{
if (flags & O_CREAT)
{
creation_distribution = OPEN_ALWAYS;
}
else
{
creation_distribution = OPEN_EXISTING;
}
}
if ((flags & O_EXCL) && (flags & O_CREAT))
{
creation_distribution = CREATE_NEW;
}
if (flags & O_APPEND)
{
append_p_ = 1;
}
/* FILE_SHARE_DELETE is only supported under NT */
if (windows_95 ())
shared = FILE_SHARE_READ | FILE_SHARE_WRITE;
else
shared = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
sa.nLength = sizeof (sa);
sa.lpSecurityDescriptor = 0;
sa.bInheritHandle = TRUE;
file_attributes = FILE_ATTRIBUTE_NORMAL;
x = CreateFileA (win32_path.get_win32 (), access_, shared,
&sa, creation_distribution,
file_attributes,
0);
syscall_printf ("%d = CreateFileA (%s, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0)\n",
x,
win32_path.get_win32 (), access_, shared,
&sa, creation_distribution,
file_attributes);
if (x == INVALID_HANDLE_VALUE)
{
if (GetLastError () == ERROR_INVALID_HANDLE)
set_errno (ENOENT);
else
__seterrno ();
goto done;
}
if (flags & O_CREAT)
{
set_file_attribute (win32_path.get_win32 (), mode);
}
set_name (path);
namehash_ = hash_path_name (path);
set_handle(x);
readahead_valid_ = 0;
set_close_on_exec(0);
symlink_p_ = 0;
execable_p_ = 0;
rpos_ = 0;
had_eof_ = 0;
rsize_ = -1;
if (win32_path.binary_p || (flags & O_BINARY))
{
set_r_binary (1);
set_w_binary (1);
}
else if (flags & O_TEXT)
{
set_r_binary (0);
set_w_binary (0);
}
else if (u->fmode_ptr && (*(u->fmode_ptr) & O_BINARY))
{
set_r_binary (1);
set_w_binary (1);
syscall_printf ("filemode defaulting to binary.\n");
}
else
{
set_r_binary (0);
set_w_binary (0);
syscall_printf ("filemode defaulting to text.\n");
}
if (flags & O_APPEND)
SetFilePointer (get_handle(), 0, 0, FILE_END);
else
SetFilePointer (get_handle(), 0, 0, FILE_BEGIN);
res = this;
done:
syscall_printf ("%p = fhandler_base::open (%s, 0x%x)\n", res, path, flags);
return res;
}
/* states:
open buffer in binary mode? Just do the read.
open buffer in text mode? Scan buffer for control zs and handle
the first one found. Then scan buffer, converting every \r\n into
an \n. If last char is an \r, look ahead one more char, if \n then
modify \r, if not, remember char.
*/
int
fhandler_base::read (void *ptr, size_t ulen)
{
int len = ulen;
char *ctrlzpos;
int i;
if (len == 0)
return 0;
if (get_r_binary ())
{
int l = raw_read (ptr, len);
if (l <= 0)
return l;
rpos_ += l;
return l;
}
int chars_to_process;
/* We're in text mode */
if (readahead_valid_)
{
/* We have one character to consume from the last read. */
readahead_valid_ = 0;
((char *)ptr)[0] = readahead_;
chars_to_process = raw_read (&(((char *)ptr)[1]), len-1);
if (chars_to_process < 0)
return chars_to_process;
else if(chars_to_process == 0)
{
/* Even if raw_read returned 0 we still have the
readahead char to process */
chars_to_process = 1;
}
else
{
/* We must increment chars_to_process
as we already had the readahead char */
chars_to_process += 1;
}
}
else
{
chars_to_process = raw_read (ptr, len);
if (chars_to_process <= 0)
return chars_to_process;
}
/* If the first character is a control-z we're at virtual EOF. */
if ( ((char *)ptr)[0] == 0x1a )
{
return 0;
}
/* Scan buffer for a control-z and shorten the buffer to that length */
ctrlzpos = (char *)memchr ((char *)ptr, 0x1a, chars_to_process);
if (ctrlzpos)
{
lseek ( (ctrlzpos - ((char *)ptr + chars_to_process)), SEEK_CUR);
chars_to_process = ctrlzpos - (char *)ptr;
}
/* Scan buffer and turn \r\n into \n */
register char *src= (char *)ptr;
register char *dst = (char *)ptr;
register char *end = src + chars_to_process - 1;
/* Read up to the last but one char - the last char needs special handling */
while (src < end)
{
if (src[0] == '\r' && (src[1] == '\n' || src[1] == '\r'))
{
/* Ignore this. */
src++;
}
else
{
*dst++ = *src++;
}
}
/* if last char is a '\r' then read one more to see if we should
translate this one too */
if (*src == '\r')
{
int len;
char c;
len = raw_read (&c, 1);
if (len > 0)
{
if (c == '\n')
{
*dst++ = '\n';
}
else
{
readahead_valid_ = 1;
readahead_ = c;
}
}
}
else
{
*dst++ = *src;
}
chars_to_process = dst - (char *)ptr;
rpos_ += chars_to_process;
if (u->strace_mask & (_STRACE_DEBUG | _STRACE_ALL))
{
char buf[16 * 6 + 1];
char *p = buf;
for (int i = 0; i < chars_to_process && i < 16; ++i)
{
unsigned char c = ((unsigned char *) ptr)[i];
/* >= 33 so space prints in hex */
__small_sprintf (p, c >= 33 && c <= 127 ? " %c" : " 0x%x", c);
p += strlen (p);
}
debug_printf ("read %d bytes (%s%s)\n", chars_to_process, buf, chars_to_process > 16 ? " ..." : "");
}
return chars_to_process;
}
int
fhandler_base::write (const void *ptr, size_t len)
{
int res;
if (append_p_)
SetFilePointer (get_handle(), 0, 0, FILE_END);
if (get_w_binary ())
{
res = raw_write (ptr, len);
}
else
{
/* Keep track of previous \rs, we don't want to turn existing
\r\n's into \r\n\n's */
register int pr = 0;
/* Copy things in chunks */
char buf[CHUNK_SIZE];
for (unsigned int i = 0; i < len; i += sizeof (buf)/2 )
{
register const char *src = (char *)ptr + i;
int todo = MIN(len - i, sizeof (buf) /2);
register const char *end = src + todo;
register char *dst = buf;
while (src < end)
{
if (*src == '\n' && !pr)
{
/* Emit a cr lf here */
*dst ++ = '\r';
*dst ++ = '\n';
}
else if (*src == '\r')
{
*dst ++ = '\r';
pr = 1;
}
else
{
*dst ++ = *src;
pr = 0;
}
src++;
}
int want = dst-buf;
if ((res = raw_write (buf, want)) != want)
{
if (res == -1)
return -1;
/* Tricky... Didn't write everything we wanted.. How can
we work out exactly which chars were sent ? We don't...
This will only happen in pretty nasty circumstances. */
rpos_ += i;
return i;
}
}
/* Done everything, update by the chars that the user sent */
rpos_ += len;
/* Length of file has changed */
rsize_ = -1;
res = len;
debug_printf ("after write, name %s, rpos %d\n", unix_path_name_, rpos_);
}
return res;
}
off_t
fhandler_base::lseek (off_t offset, int whence)
{
off_t res;
/* Seeks on text files is tough, we rewind and read till we get to the
right place. */
readahead_valid_ = 0;
debug_printf ("lseek (%s, %d, %d)\n", unix_path_name_, offset, whence);
#if 0 /* lseek has no business messing about with text-mode stuff */
if (!get_r_binary ())
{
int newplace;
if (whence == 0)
{
newplace = offset;
}
else if (whence ==1)
{
newplace = rpos + offset;
}
else
{
/* Seek from the end of a file.. */
if (rsize == -1)
{
/* Find the size of the file by reading till the end */
char b[CHUNK_SIZE];
while (read (b, sizeof (b)) > 0)
;
rsize = rpos;
}
newplace = rsize + offset;
}
if (rpos > newplace)
{
SetFilePointer (handle, 0, 0, 0);
rpos = 0;
}
/* You can never shrink something more than 50% by turning CRLF into LF,
so we binary chop looking for the right place */
while (rpos < newplace)
{
char b[CHUNK_SIZE];
size_t span = (newplace - rpos) / 2;
if (span == 0)
span = 1;
if (span > sizeof (b))
span = sizeof (b);
debug_printf ("lseek (%s, %d, %d) span %d, rpos %d newplace %d\n",
name, offset, whence,span,rpos, newplace);
read (b, span);
}
debug_printf ("Returning %d\n", newplace);
return newplace;
}
#endif /* end of deleted code dealing with text mode */
DWORD win32_whence = whence == SEEK_SET ? FILE_BEGIN
: (whence == SEEK_CUR ? FILE_CURRENT : FILE_END);
res = SetFilePointer (get_handle(), offset, 0, win32_whence);
if (res == -1)
{
__seterrno ();
}
return res;
}
int
fhandler_base::close ()
{
int res = -1;
syscall_printf ("fhandler_base::close (handle %d)\n",get_handle());
/* int type = GetFileType (handle);*/
int type = 0;
/* Can't really close these things, but pretend we did. */
if (type == FILE_TYPE_CHAR && 0)
res = 0;
else
{
if (!CloseHandle (get_handle()))
{
paranoid_printf ("CloseHandle (%d <%s>) failed\n", get_handle(), unix_path_name_);
__seterrno ();
}
else
{
if (type == FILE_TYPE_DISK)
process_deletion_queue ();
res = 0;
set_handle((HANDLE)-99);
}
}
return res;
}
int
fhandler_base::ioctl (int cmd, void *buf)
{
if (cmd == FIONBIO)
{
syscall_printf ("fhandler.cc: ioctl (FIONBIO,%x)\n",buf);
}
else
{
syscall_printf ("fhandler.cc: ioctl (%x,%x)\n", cmd, buf);
}
set_errno (EINVAL);
return -1;
}
int
fhandler_base::lock(int, struct flock *)
{
set_errno (ENOSYS);
return -1;
}
int
fhandler_base::fstat (struct stat *buf)
{
int res;
BY_HANDLE_FILE_INFORMATION local;
memset (buf, 0, sizeof (*buf));
res = GetFileInformationByHandle (get_handle(), &local);
debug_printf ("%d = GetFileInformationByHandle (%s, %d)\n",
res, unix_path_name_, get_handle());
if (res == 0)
{
/* GetFileInformationByHandle will fail if it's given stdin/out/err
or a pipe*/
if (1)
{
/* We expect these to fail! */
buf->st_mode |= S_IFCHR;
buf->st_blksize = S_BLKSIZE;
buf->st_ino = namehash_;
syscall_printf ("0 = fstat (, 0x%x)\n", buf);
return 0;
}
else
{
__seterrno ();
syscall_printf ("-1 = fstat (, 0x%x)\n", buf);
return -1;
}
}
path_conv win32_path (get_name (), -1);
if (win32_path.error)
{
set_errno (win32_path.error);
return -1;
}
set_errno (0);
buf->st_atime = to_time_t (&local.ftLastAccessTime);
buf->st_mtime = to_time_t (&local.ftLastWriteTime);
buf->st_ctime = to_time_t (&local.ftCreationTime);
buf->st_nlink = local.nNumberOfLinks;
buf->st_dev = local.dwVolumeSerialNumber;
buf->st_size = local.nFileSizeLow;
buf->st_ino = local.nFileIndexLow ^ namehash_;
buf->st_blksize = S_BLKSIZE;
buf->st_blocks = (buf->st_size + S_BLKSIZE-1) / S_BLKSIZE;
buf->st_uid = get_file_owner (win32_path.get_win32 ());
buf->st_gid = get_file_group (win32_path.get_win32 ());
if(get_file_attribute (win32_path.get_win32 (), (int *) &buf->st_mode) > 0)
{
buf->st_mode &= ~S_IFMT;
if (symlink_p_)
buf->st_mode |= S_IFLNK;
else
buf->st_mode |= S_IFREG;
}
else
{
buf->st_mode = 0;
buf->st_mode |= STD_RBITS;
if (! (local.dwFileAttributes & FILE_ATTRIBUTE_READONLY))
buf->st_mode |= STD_WBITS;
/* | S_IWGRP | S_IWOTH; we don't give write to group etc */
if (symlink_p_)
buf->st_mode |= S_IFLNK;
else
switch (GetFileType (get_handle()))
{
case FILE_TYPE_CHAR:
case FILE_TYPE_UNKNOWN:
buf->st_mode |= S_IFCHR;
break;
case FILE_TYPE_DISK:
buf->st_mode |= S_IFREG;
if (execable_p_)
buf->st_mode |= STD_XBITS;
break;
case FILE_TYPE_PIPE:
buf->st_mode |= S_IFSOCK;
break;
}
}
syscall_printf ("0 = fstat (, 0x%x) st_atime=%x st_size=%d, st_mode=0x%x, st_ino=%d, sizeof=%d\n",
buf, buf->st_atime, buf->st_size, buf->st_mode,
(int) buf->st_ino, sizeof (*buf));
return 0;
}
void
fhandler_base::init (HANDLE f, int bin, int a, const char *n)
{
set_handle (f);
set_r_binary (bin);
set_w_binary (bin);
access_ = a;
set_name (n);
debug_printf ("created new fhandler_base for <%s> with handle %d\n", n, f);
}
void
fhandler_base::dump ()
{
paranoid_printf ( "FHANDLER BASE\n");
}
void
fhandler_base::set_handle (HANDLE x)
{
debug_printf ("set handle to %d\n", x);
handle_ = x;
}
int
fhandler_base::dup (fhandler_base *child)
{
debug_printf ("in fhandler_base dup\n");
child->set_close_on_exec(0);
const HANDLE proc = GetCurrentProcess ();
HANDLE nh;
if (!DuplicateHandle (proc, get_handle(), proc, &nh, 0, 1, DUPLICATE_SAME_ACCESS))
{
small_printf ("COPY FOR DUP FAILED, handle in %x %x!!\n", get_handle(), GetLastError ());
__seterrno();
return -1;
}
child->set_handle(nh);
return 0;
}
/*
* normal I/O constructor.
*/
fhandler_base::fhandler_base ( const char *name )
{
w_binary_ = 0;
r_binary_ = 0;
close_exec_p_ = 0;
handle_ = 0;
access_ = 0;
readahead_valid_ = 0;
readahead_ = 0;
append_p_ = 0;
rpos_ = 0;
rsize_ = 0;
had_eof_ = 0;
symlink_p_ = 0;
namehash_ = 0;
execable_p_ = 0;
unix_path_name_ = 0;
set_name(name);
}
/*
* Normal I/O destructor.
*/
fhandler_base::~fhandler_base ()
{
delete [] unix_path_name_;
}
/**********************************************************************/
/* fhandler_disk_file */
fhandler_disk_file::fhandler_disk_file (const char *name) : fhandler_base(name)
{
}
fhandler_base *
fhandler_disk_file::open (const char *path, int flags, mode_t mode)
{
fhandler_base * res = NULL;
int exec_p = 0;
int syml_p = 0;
char real_path[MAX_PATH];
syscall_printf ("fhandler_disk_file::open (%s, 0x%x)\n", path, flags);
/* O_NOSYMLINK is an internal flag for implementing lstat, nothing more. */
if (flags & O_NOSYMLINK)
{
int len = symlink_check (path, NULL, 0, &syml_p, &exec_p);
if (len == -1)
{
syscall_printf ("%p = fhandler_disk_file::open (%s, 0x%x)\n",
res, path, flags);
return res;
}
strcpy (real_path, path);
}
else
{
int rc = symlink_follow (path, real_path, &exec_p);
/* OK if file doesn't exist but the caller passed O_CREAT */
if (rc == -1 && (get_errno () != ENOENT || (flags & O_CREAT) == 0))
{
syscall_printf ("%p = fhandler_disk_file::open (%s, 0x%x)\n",
res, path, flags);
return res;
}
syml_p = 0;
}
/* If necessary, do various other things to see if PATH is a program. */
if (!exec_p)
exec_p = check_execable_p (real_path);
res = this->fhandler_base::open (real_path, flags, mode);
symlink_p_ = syml_p;
execable_p_ = exec_p;
syscall_printf ("%p = fhandler_disk_file::open (%s, 0x%x)\n", res, path, flags);
return res;
}
/*
* FIXME !!!
* The correct way to do this to get POSIX locking
* semantics is to keep a linked list of posix lock
* requests and map them into Win32 locks. The problem
* is that Win32 does not deal correctly with overlapping
* lock requests. Also another pain is that Win95 doesn't do
* non-blocking or non exclusive locks at all. For '95 just
* convert all lock requests into blocking,exclusive locks.
* This shouldn't break many apps but denying all locking
* would.
* For now just convert to Win32 locks and hope for the best.
*/
int
fhandler_disk_file::lock (int cmd, struct flock *fl)
{
DWORD win32_start;
DWORD win32_len;
DWORD win32_upper;
DWORD startpos;
/*
* We don't do getlck calls yet.
*/
if (cmd == F_GETLK)
{
set_errno (ENOSYS);
return -1;
}
/*
* Calculate where in the file to start from,
* then adjust this by fl->l_start.
*/
switch (fl->l_whence)
{
case SEEK_SET:
startpos = 0;
break;
case SEEK_CUR:
if ((startpos = lseek (0, SEEK_CUR)) < 0)
return -1;
break;
case SEEK_END:
{
BY_HANDLE_FILE_INFORMATION finfo;
if (GetFileInformationByHandle (get_handle(), &finfo) == 0)
{
__seterrno ();
return -1;
}
startpos = finfo.nFileSizeLow; /* Nowhere to keep high word */
break;
}
}
/*
* Now the fun starts. Adjust the start and length
* fields until they make sense.
*/
win32_start = startpos + fl->l_start;
if (fl->l_len < 0)
{
win32_start -= fl->l_len;
win32_len = -fl->l_len;
}
else
win32_len = fl->l_len;
if (win32_start < 0)
{
win32_len -= win32_start;
if (win32_len <= 0)
{
/* Failure ! */
set_errno (EINVAL);
return -1;
}
win32_start = 0;
}
/*
* Special case if len == 0 for POSIX means lock
* to the end of the entire file (and all future extensions).
*/
if (win32_len == 0)
{
win32_len = 0xffffffff;
win32_upper = 0xffffffff;
}
else
win32_upper = 0;
BOOL res;
/*
* Win95 only supportes primitive lock call.
*/
if (windows_95 ())
{
if (fl->l_type == F_UNLCK)
res = UnlockFile (get_handle (), win32_start, 0, win32_len,
win32_upper);
else
res = LockFile (get_handle (), win32_start, 0, win32_len, win32_upper);
}
else
{
/* Windows NT */
DWORD lock_flags = (cmd == F_SETLK) ? LOCKFILE_FAIL_IMMEDIATELY : 0;
lock_flags |= (fl->l_type == F_WRLCK) ? LOCKFILE_EXCLUSIVE_LOCK : 0;
OVERLAPPED ov;
ov.Internal = 0;
ov.InternalHigh = 0;
ov.Offset = win32_start;
ov.OffsetHigh = 0;
ov.hEvent = (HANDLE)0;
if (fl->l_type == F_UNLCK)
res = UnlockFileEx (get_handle (), 0, win32_len, win32_upper, &ov);
else
{
res = LockFileEx (get_handle (), lock_flags, 0, win32_len,
win32_upper, &ov);
/* Deal with the fail immediately case. */
/*
* FIXME !! I think this is the right error to check for
* but I must admit I haven't checked....
*/
if ((res == 0) && (lock_flags & LOCKFILE_FAIL_IMMEDIATELY) &&
(GetLastError() == ERROR_LOCK_FAILED))
{
set_errno (EAGAIN);
return -1;
}
}
}
if (res == 0)
{
__seterrno ();
return -1;
}
return 0;
}
/* Perform various heuristics on PATH to see if it's a program. */
int
fhandler_disk_file::check_execable_p (const char *path)
{
int len = strlen (path);
const char *ch = path + (len > 4 ? len - 4 : len);
if (strcasecmp (".bat", ch) == 0
|| strcasecmp (".exe", ch) == 0
|| strcasecmp (".com", ch) == 0)
return 1;
return 0;
}
/**********************************************************************/
/* fhandler_tty */
fhandler_tty::fhandler_tty (const char *name) : fhandler_base (name)
{
vmin_ = 0;
vtime_ = 0;
}
int
fhandler_tty::linearize (unsigned char *buf)
{
int len = sizeof (vmin_) + sizeof (vtime_);
if (buf == 0)
{
len += this->fhandler_base::linearize (buf);
return len;
}
memcpy (buf, (char *) &vmin_, sizeof (vmin_));
buf += sizeof (vmin_);
memcpy (buf, (char *) &vtime_, sizeof (vtime_));
buf += sizeof (vtime_);
len += this->fhandler_base::linearize (buf);
return len;
}
int
fhandler_tty::de_linearize (const unsigned char *buf)
{
const unsigned char *orig_buf = buf;
memcpy ((char *) &vmin_, buf, sizeof (vmin_));
buf += sizeof (vmin_);
memcpy ((char *) &vtime_, buf, sizeof (vtime_));
buf += sizeof (vtime_);
int len = buf - orig_buf;
return (len + this->fhandler_base::de_linearize (buf));
}
int
fhandler_tty::raw_read (void *ptr, size_t ulen)
{
if (vtime_ || vmin_)
{
if (vmin_ == 0)
ulen = 1;
else if (vmin_ < ulen)
ulen = vmin_;
syscall_printf ("timeout len %d\n", ulen);
}
return fhandler_base::raw_read (ptr, ulen);
}
void
fhandler_tty::dump ()
{
paranoid_printf ("FHANDLER TTY\n");
}
fhandler_base *
fhandler_tty::open( const char *path, int flags, mode_t mode )
{
fhandler_base *res = NULL;
int temp = 0;
int access;
HANDLE myhandle;
COMMTIMEOUTS to;
syscall_printf( "fhandler_tty::open( %s, 0x%x, 0x%x )\n",
path, flags, mode );
if( (res = fhandler_base::open( path, flags, mode ) ) == NULL )
return( NULL );
memset( &to, 0, sizeof(to) );
myhandle = fhandler_base::get_handle();
access = fhandler_base::get_access();
temp |= (access & (GENERIC_READ | GENERIC_WRITE));
if( flags & O_NDELAY ) /* non-blocking I/O */
{
temp |= _FNDELAY;
/* probably not necessary */
if( GetCommTimeouts( myhandle, &to ) == 0 )
{
small_printf ("GetCommTimeout failed\n");
__seterrno ();
return( NULL );
}
to.ReadIntervalTimeout = MAXDWORD;
to.ReadTotalTimeoutMultiplier = 0;
to.ReadTotalTimeoutConstant = 0;
/* leave the values vmin_ and vtime_ alone for restoral later */
if( SetCommTimeouts( myhandle, &to ) == 0 )
{
small_printf ("SetCommTimeout failed\n");
__seterrno ();
return( NULL );
}
} /* non-blocking I/O */
else
{ /* blocking I/O */
/* if non-blocking is set, unset it */
if( access & _FNDELAY )
{
/* probably not necessary */
/*
if( GetCommTimeouts( myhandle, &to ) == 0 )
{
small_printf ("GetCommTimeout failed\n");
__seterrno ();
return( NULL );
}
we have no access to vtime_ or vmin_
to.ReadIntervalTimeout = 0;
to.ReadTotalTimeoutMultiplier = 0;
to.ReadTotalTimeoutConstant = vtime_ * 100;
*/
if( SetCommTimeouts( myhandle, &to ) == 0 )
{
small_printf ("SetCommTimeout failed\n");
__seterrno ();
return( NULL );
}
} /* if non-blocking is set */
} /* blocking I/O */
fhandler_base::set_access( temp );
syscall_printf( "%p = fhandler_tty::open( %s, 0x%x, 0x%x )\n",
res, path, flags, mode );
return res;
} /* tty open() */
int
fhandler_tty::fstat (struct stat *buf)
{
memset (buf, 0, sizeof (*buf));
buf->st_mode |= S_IFCHR;
buf->st_blksize = S_BLKSIZE;
buf->st_uid = getuid ();
buf->st_gid = getgid ();
buf->st_mode |= STD_RBITS | STD_WBITS;
buf->st_ino = get_namehash();
syscall_printf ("0 = fhandler_tty:fstat (%x) (mode %x)\n",
buf, buf->st_mode);
return 0;
}
int
fhandler_tty::tcsendbreak( int duration )
{
unsigned int sleeptime = 350;
if( duration > 1 )
sleeptime *= duration;
if( SetCommBreak( get_handle() ) == 0 )
return( -1 );
usleep( sleeptime );
if( ClearCommBreak( get_handle() ) == 0 )
return( -1 );
return( 0 );
}
int
fhandler_tty::tcdrain( void )
{
if( FlushFileBuffers( get_handle() ) == 0 )
return( -1 );
return( 0 );
}
int
fhandler_tty::tcflow( int action )
{
DCB dcb;
DWORD winaction = 0;
char xchar;
switch( action )
{
case TCOOFF:
winaction = SETXOFF;
break;
case TCOON:
winaction = SETXON;
break;
case TCION:
case TCIOFF:
if( GetCommState( get_handle(), &dcb ) == 0 )
return( -1 );
if( action == TCION )
xchar = (dcb.XonChar ? dcb.XonChar : 0x11);
else
xchar = (dcb.XoffChar ? dcb.XoffChar : 0x13);
if( TransmitCommChar( get_handle(), xchar ) == 0 )
return( -1 );
return( 0 );
break;
default:
return( -1 );
break;
}
if( EscapeCommFunction( get_handle(), winaction ) == 0 )
return( -1 );
return( 0 );
}
int
fhandler_tty::tcflush (int queue)
{
if (queue & (TCOFLUSH | TCIOFLUSH))
{
PurgeComm (get_handle(), PURGE_TXABORT | PURGE_TXCLEAR);
}
if (queue & (TCIFLUSH | TCIOFLUSH))
{
/* Input flushing by polling until nothing turns up
(we stop after 1000 chars anyway) */
COMMTIMEOUTS old;
COMMTIMEOUTS tmp;
char b;
DWORD more = 1;
int max = 1000;
PurgeComm (get_handle(), PURGE_RXABORT | PURGE_RXCLEAR);
GetCommTimeouts (get_handle(), &old);
memset (&tmp, 0, sizeof (tmp));
tmp.ReadTotalTimeoutConstant = 100;
SetCommTimeouts (get_handle(), &tmp);
while (max > 0 && more)
{
ReadFile (get_handle(), &b, 1, &more, 0);
if (more)
{
termios_printf ("dropping %d\n", b);
}
max--;
}
SetCommTimeouts (get_handle(), &old);
}
return 0;
}
int
fhandler_tty::tcsetattr( int action, const struct termios *t )
{
/* possible actions are:
TCSANOW - do the change immediately.
TCSADRAIN - change after flushing output.
TCSAFLUSH - change after flushing output and discarding input.
*/
BOOL dropDTR = FALSE;
COMMTIMEOUTS to;
DCB state;
if( (action == TCSADRAIN) || (action == TCSAFLUSH) )
FlushFileBuffers( get_handle() );
if( action == TCSAFLUSH )
PurgeComm( get_handle(), (PURGE_RXABORT | PURGE_RXCLEAR) );
/* get default/last comm state */
GetCommState (get_handle (), &state);
#if 0
ds ("First in tcsetattr", &state);
#endif
/* *********** baud rate *********** */
switch (t->c_ospeed)
{
case B0:
/* drop DTR */
dropDTR = TRUE;
state.BaudRate = 0;
break;
case B110:
state.BaudRate = CBR_110;
break;
case B300:
state.BaudRate = CBR_300;
break;
case B600:
state.BaudRate = CBR_600;
break;
case B1200:
state.BaudRate = CBR_1200;
break;
case B2400:
state.BaudRate = CBR_2400;
break;
case B4800:
state.BaudRate = CBR_4800;
break;
case B9600:
state.BaudRate = CBR_9600;
break;
case B19200:
state.BaudRate = CBR_19200;
break;
case B38400:
state.BaudRate = CBR_38400;
break;
/*
case B57600:
state.BaudRate = CBR_57600;
break;
case B115200:
state.BaudRate = CBR_115200;
break;
*/
/* WIN32 also has 14400, 56000, 128000, and 256000 */
/* Unix also has 230400 */
default:
termios_printf ("t->c_ospeed was %d\n", t->c_ospeed);
set_errno ( EINVAL);
return -1;
}
/* *********** byte size *********** */
switch (t->c_cflag & CSIZE)
{
case CS5:
state.ByteSize = 5;
break;
case CS6:
state.ByteSize = 6;
break;
case CS7:
state.ByteSize = 7;
break;
case CS8:
default:
state.ByteSize = 8;
break;
}
/* *********** stop bits *********** */
if( t->c_cflag & CSTOPB )
state.StopBits = TWOSTOPBITS;
else
state.StopBits = ONESTOPBIT;
/* *********** parity *********** */
if (t->c_cflag & PARENB)
state.Parity = (t->c_cflag & PARODD) ? ODDPARITY:EVENPARITY;
else
state.Parity = NOPARITY;
state.fBinary = TRUE; /* binary transfer */
state.EofChar = 0; /* no end-of-data in binary mode */
state.fNull = FALSE; /* don't discard nulls in binary mode */
/* *********** parity errors *********** */
/* fParity combines the function of INPCK and NOT IGNPAR */
if( (t->c_iflag & INPCK) && (! t->c_iflag & IGNPAR) )
state.fParity = TRUE; /* react to parity errors */
else
state.fParity = FALSE; /* ignore parity errors */
/* Unix has no equivalent to these */
state.fErrorChar = FALSE;
state.ErrorChar = 0;
/* *********** software flow control *********** */
/* if the remote device interprets any received character as XON (the
equivalent of IXANY at the remote side), then we must set
fTXContinueOnXoff to FALSE to not trigger a premature XON,
otherwise, a TRUE value separates the TX and RX functions. */
state.fTXContinueOnXoff = TRUE; /* separate TX and RX flow control */
if( t->c_iflag & IXON ) /* transmission flow control */
{
state.fOutX = TRUE; /* enable */
}
else
{
state.fOutX = FALSE; /* disable */
}
if( t->c_iflag & IXOFF ) /* reception flow control */
{
state.fInX = TRUE; /* enable */
/* XoffLim and XonLim are left at default values */
}
else
{
state.fInX = FALSE; /* disable */
/* XoffLim and XonLim are left at default values */
}
state.XonChar = (t->c_cc[VSTART] ? t->c_cc[VSTART] : 0x11);
state.XoffChar = (t->c_cc[VSTOP] ? t->c_cc[VSTOP] : 0x13);
/* *********** hardware flow control *********** */
state.fOutxDsrFlow = FALSE; /* disable DSR flow control */
/* older versions of Unix presumed hardware flow control if
software flow control is not enabled. Newer Unices seem to
require explicit setting of hardware flow-control and this
code reflects that scheme. */
/* CLOCAL causes all lead control, except DTR, to be ignored */
/* input flow-control */
if( (t->c_cflag & CRTSXOFF) && ( ! (t->c_cflag & CLOCAL) ) )
{
state.fRtsControl = RTS_CONTROL_HANDSHAKE; /* enable */
}
else
{
state.fRtsControl = RTS_CONTROL_DISABLE; /* disable */
}
/* output flow-control */
if( (t->c_cflag & CRTSCTS) && ( ! (t->c_cflag & CLOCAL) ) )
{
state.fOutxCtsFlow = TRUE; /* enable */
}
else
{
state.fOutxCtsFlow = FALSE; /* disable */
}
/* *********** DTR *********** */
state.fDtrControl = DTR_CONTROL_ENABLE; /* assert DTR when device
is opened */
/* *********** DSR *********** */
if( t->c_cflag & CLOCAL )
state.fDsrSensitivity = FALSE; /* ignored */
else
state.fDsrSensitivity = TRUE; /* DSR must be asserted at device */
/* *********** error handling *********** */
state.fAbortOnError = TRUE; /* read/write operations terminate upon
error - requires ClearCommError() to
resume. */
#if 0
ds ("Before SetCommState", &state);
#endif
SetCommState( get_handle(), &state );
set_r_binary ((t->c_iflag & IGNCR) ? 0 : 1);
set_w_binary ((t->c_oflag & ONLCR) ? 0 : 1);
vtime_ = t->c_cc[VTIME];
vmin_ = t->c_cc[VMIN];
if( dropDTR == TRUE )
{
EscapeCommFunction( get_handle(), CLRDTR );
}
else
{
/* in case we previously set CLRDTR and setting
state.fDtrControl = DTR_CONTROL_ENABLE doesn't do
the trick. This relationship needs to be discovered
since a program might want to change some
parameters while DTR is still down. This would require
setting some state and testing it.
*/
EscapeCommFunction( get_handle(), SETDTR );
}
memset (&to, 0, sizeof (to));
to.ReadTotalTimeoutConstant = vtime_ * 100;
int res = SetCommTimeouts( get_handle (), &to );
if( ! res )
{
small_printf ("CommTimeout failed\n");
__seterrno ();
return -1;
}
// tdump (fd);
return 0;
} /* tcsetattr() */
int
fhandler_tty::tcgetattr( struct termios *t )
{
DCB state;
/* get current comm state */
GetCommState (get_handle (), &state);
/* just in case */
memset (t, 0, sizeof (*t));
#if 0
ds ("In tcgetattr", &state);
#endif
/* *********** baud rate *********** */
switch (state.BaudRate)
{
case 0: /* B0 - DTR should be dropped */
t->c_cflag = t->c_ospeed = t->c_ispeed = B0;
break;
case CBR_110:
t->c_cflag = t->c_ospeed = t->c_ispeed = B110;
break;
case CBR_300:
t->c_cflag = t->c_ospeed = t->c_ispeed = B300;
break;
case CBR_600:
t->c_cflag = t->c_ospeed = t->c_ispeed = B600;
break;
case CBR_1200:
t->c_cflag = t->c_ospeed = t->c_ispeed = B1200;
break;
case CBR_2400:
t->c_cflag = t->c_ospeed = t->c_ispeed = B2400;
break;
case CBR_4800:
t->c_cflag = t->c_ospeed = t->c_ispeed = B4800;
break;
case CBR_9600:
t->c_cflag = t->c_ospeed = t->c_ispeed = B9600;
break;
case CBR_19200:
t->c_cflag = t->c_ospeed = t->c_ispeed = B19200;
break;
case CBR_38400:
t->c_cflag = t->c_ospeed = t->c_ispeed = B38400;
break;
/*
case CBR_57600:
t->c_cflag = t->c_ospeed = t->c_ispeed = B57600;
break;
case CBR_115200:
t->c_cflag = t->c_ospeed = t->c_ispeed = B115200;
break;
*/
default:
/* t->c_cflag = t->c_ospeed = t->c_ispeed = B9600; */
set_errno ( EINVAL);
}
/* *********** byte size *********** */
switch (state.ByteSize)
{
case 5:
t->c_cflag |= CS5;
break;
case 6:
t->c_cflag |= CS6;
break;
case 7:
t->c_cflag |= CS7;
break;
case 8:
t->c_cflag |= CS8;
break;
default:
break;
}
/* *********** stop bits *********** */
if( state.StopBits == TWOSTOPBITS )
t->c_cflag |= CSTOPB;
/* *********** parity *********** */
if( state.Parity == ODDPARITY )
t->c_cflag |= (PARENB | PARODD);
if( state.Parity == EVENPARITY )
t->c_cflag |= PARENB;
/* *********** parity errors *********** */
/* fParity combines the function of INPCK and NOT IGNPAR */
if( state.fParity == TRUE )
t->c_iflag |= INPCK;
else
t->c_iflag |= IGNPAR; /* not necessarily! */
/* *********** software flow control *********** */
/* transmission flow control */
if( state.fOutX == TRUE )
t->c_iflag |= IXON;
/* reception flow control */
if( state.fInX == TRUE )
t->c_iflag |= IXOFF;
t->c_cc[VSTART] = ( state.XonChar ? state.XonChar : 0x11 );
t->c_cc[VSTOP] = ( state.XoffChar ? state.XoffChar : 0x13 );
/* *********** hardware flow control *********** */
/* older versions of Unix presumed hardware flow control if
software flow control is not enabled. Newer Unices seem to
require explicit setting of hardware flow-control and this
code reflects that scheme. */
/* input flow-control */
if( state.fRtsControl == RTS_CONTROL_HANDSHAKE )
t->c_cflag |= CRTSXOFF;
/* output flow-control */
if( state.fOutxCtsFlow == TRUE )
t->c_cflag |= CRTSCTS;
/* *********** CLOCAL *********** */
/* DSR is the only lead toggled ONLY by CLOCAL, so we can use it
as a flag that CLOCAL was called. This presumes that
tcsetattr() was called previously; if not, this may give
a false CLOCAL. */
if( state.fDsrSensitivity == FALSE )
t->c_cflag |= CLOCAL;
#if 0 /* IGNCR doesn't work yet */
if (!get_r_binary ())
t->c_iflag |= IGNCR;
#endif
if (!get_w_binary ())
t->c_oflag |= ONLCR;
t->c_cc[VTIME] = vtime_;
t->c_cc[VMIN] = vmin_;
// tdump (fd);
return 0;
} /* tcgetattr() */
/**********************************************************************/
/* /dev/null */
fhandler_dev_null::fhandler_dev_null(const char *name) : fhandler_base (name)
{;}
fhandler_base *
fhandler_dev_null::open (const char *, int, mode_t)
{
set_handle((HANDLE) DEV_NULL_HANDLE_VALUE);
return this;
}
void
fhandler_dev_null::dump ()
{
paranoid_printf ("FHANDLER DEV/NULL\n");
}
int
fhandler_dev_null::close ()
{
return 0;
}
int
fhandler_dev_null::fstat (struct stat *buf)
{
memset (buf, 0, sizeof (*buf));
buf->st_blksize = S_BLKSIZE;
buf->st_dev = 1234;
buf->st_ino = 4567;
return 0;
}
int
fhandler_dev_null::ioctl (int, void *)
{
return -1;
}
int
fhandler_dev_null::read (void *, size_t)
{
return 0;
}
int
fhandler_dev_null::write (const void *, size_t x)
{
syscall_printf ("/dev/null write %d\n",x);
return x;
}
long
fhandler_dev_null::lseek (long, int)
{
return 0;
}
int
fhandler_dev_null::dup (fhandler_base *child)
{
child->set_handle(get_handle());
return 0;
}
/**********************************************************************/
/* fhandler_dev_floppy */
fhandler_dev_floppy::fhandler_dev_floppy (const char *name) : fhandler_base (name)
{;}
fhandler_base *
fhandler_dev_floppy::open (const char *path, int flags, mode_t)
{
/* Always open a floppy existings */
return fhandler_base::open (path, flags & ~O_CREAT);
}
/**********************************************************************/
/* fhandler_dev_tape */
fhandler_dev_tape::fhandler_dev_tape(const char *name) : fhandler_base (name)
{;}
fhandler_base *
fhandler_dev_tape::open (const char *path, int flags, mode_t)
{
/* Always open a tape existings */
return fhandler_base::open (path, flags & ~O_CREAT);
}
/**********************************************************************/
/* fhandler_socket */
fhandler_socket::fhandler_socket (unsigned int s, const char *name) : fhandler_base (name)
{
set_handle ((HANDLE)s);
debug_printf ("socket id %d\n", s);
}
--------------49597C673EEAD2813A29F035
Content-Type: text/plain; charset=us-ascii; name="fhandler.h"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline; filename="fhandler.h"
/* fhandler.h: winsup file handling
Copyright 1996, 1997 Cygnus Solutions
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
#ifndef _FHANDLER_H_
#define _FHANDLER_H_
#include <sys/types.h>
/* Classes
fhandler_base normal I/O
fhandler_dev_floppy
fhandler_disk_file
fhandler_socket
fhandler_tty adds vmin and vtime
fhandler_console (out with ansi control)
fhandler_dev_null not really I/O
fhandler_proc interesting possibility, not implemented yet
*/
class fhandler_base
{
int access_;
HANDLE handle_;
/* Set if in binary write mode. */
char w_binary_;
/* Set if in binary read mode. */
char r_binary_;
char close_exec_p_; /* Non-zero if close-on-exec bit set. */
/* Used for crlf conversion in text files */
char readahead_valid_;
char readahead_;
char append_p_; /* Set if always appending */
int rpos_; /* Used in text reading */
int rsize_;
char had_eof_;
unsigned long namehash_; /* hashed filename, used as inode num */
/* Full unix path name of this file */
char *unix_path_name_;
void set_name (const char *);
protected:
char symlink_p_;
/* Non-zero if this file looked like it would run (i.e. ends in .exe or .bat
or begins with #!. */
char execable_p_;
public:
fhandler_base (const char *name = 0);
~fhandler_base();
/* Non-virtual simple accessor functions. */
void set_handle (HANDLE);
int get_access () { return access_; }
void set_access (int x) { access_ = x; }
int get_w_binary () { return w_binary_; }
int get_r_binary () { return r_binary_; }
set_w_binary (int b) { w_binary_ = b; }
set_r_binary (int b) { r_binary_ = b; }
void set_close_on_exec(char val) { close_exec_p_ = val; }
char get_close_on_exec() { return close_exec_p_; }
const char *get_name () { return unix_path_name_; }
unsigned long get_namehash() { return namehash_; }
/* Potentially overridden virtual functions. */
virtual fhandler_base *open (const char *path, int flags, mode_t mode=0);
virtual int close ();
virtual int fstat (struct stat *buf);
virtual int ioctl (int cmd, void *);
virtual const char * ttyname () { return get_name(); }
virtual int read (void *ptr, size_t len);
virtual int write (const void *ptr, size_t len);
virtual off_t lseek (off_t offset, int whence);
virtual int lock(int, struct flock *);
virtual void dump ();
virtual int dup (fhandler_base *child);
void *operator new (size_t, void *);
virtual void init (HANDLE, int, int, const char *);
virtual int tcflush (int) { return -1; }
virtual int tcsendbreak( int ) { return 0; }
virtual int tcdrain( void ) { return 0; }
virtual int tcflow( int ) { return 0; }
virtual int tcsetattr (int a, const struct termios *t) { return -1; }
virtual int tcgetattr (struct termios *t) { return -1; }
virtual int is_tty () { return 0; }
virtual class fhandler_socket *is_socket () { return 0; }
virtual class fhandler_console *is_console () { return 0; }
virtual int raw_read (void *ptr, size_t ulen);
virtual int raw_write (const void *ptr, size_t ulen);
/* 1 if it's pointless (and broken) to select for read on the handle */
virtual const int always_read_ready () { return 1; } ;
/* 1 if it's pointless (and broken) to select for write on the handle */
virtual const int always_write_ready () { return 1; } ;
/* 1 if it's pointless (and broken) to select for except on the handle */
virtual const int always_except_ready () { return 1; } ;
/* Function to save state of a fhandler_base into memory. */
virtual int linearize(unsigned char *);
/* Function to de-linearize into a fd */
virtual int de_linearize(const unsigned char *);
/* Virtual accessor functions to hide the fact
that some fd's have two handles. */
virtual HANDLE get_handle () const { return handle_; }
virtual HANDLE get_input_handle() const { return handle_; }
virtual HANDLE get_output_handle() const { return handle_; }
};
class fhandler_socket: public fhandler_base
{
public:
fhandler_socket (unsigned int, const char *name = 0);
int get_socket () const { return (int)get_handle(); }
fhandler_socket * is_socket () { return this; }
virtual int write (const void *ptr, size_t len);
virtual int read (void *ptr, size_t len);
virtual int ioctl (int cmd, void *);
virtual int fstat (struct stat *buf);
virtual int close ();
virtual const int always_read_ready () { return 0; }
virtual const int always_write_ready () { return 0; }
virtual const int always_except_ready () { return 0; }
};
class fhandler_dev_floppy: public fhandler_base
{
public:
fhandler_dev_floppy(const char *name = 0);
virtual fhandler_base *open (const char *path, int flags, mode_t mode=0);
};
class fhandler_dev_tape: public fhandler_base
{
public:
fhandler_dev_tape(const char *name = 0);
virtual fhandler_base *open (const char *path, int flags, mode_t mode=0);
};
/* Standard disk file */
class fhandler_disk_file : public fhandler_base
{
private:
int check_execable_p (const char *path);
public:
fhandler_disk_file(const char *name = 0);
virtual fhandler_base *open (const char *path, int flags, mode_t mode=0);
virtual int lock(int, struct flock *);
};
class fhandler_tty: public fhandler_base
{
private:
unsigned int vmin_; /* from termios */
unsigned int vtime_; /* from termios */
public:
/* Constructor */
fhandler_tty (const char *name = 0);
virtual fhandler_base *open( const char *path, int flags, mode_t mode );
virtual int raw_read (void *ptr, size_t ulen);
virtual int tcsendbreak( int );
virtual int tcdrain( void );
virtual int tcflow( int );
virtual int tcsetattr (int a, const struct termios *t);
virtual int tcgetattr (struct termios *t);
virtual int fstat (struct stat *buf);
virtual int tcflush (int);
virtual void dump ();
virtual int is_tty () { return 1; }
/* Function to save state of a fhandler_tty into memory. */
virtual int linearize(unsigned char *);
/* Function to de-linearize into a fd */
virtual int de_linearize(const unsigned char *);
};
/* This is a input and output console handle */
class fhandler_console: public fhandler_tty
{
private:
/* Output state */
// enum {normal, gotesc, gotsquare, gotarg1, gotcommand} state;
#define normal 1
#define gotesc 2
#define gotsquare 3
#define gotarg1 4
#define gotcommand 6
#define MAXARGS 10
int state_;
int args_[MAXARGS];
int nargs_;
/* Extra output handle */
HANDLE output_handle_;
DWORD default_color;
/* Output calls */
void clear_screen (int, int, int, int);
void scroll_screen (int, int, int, int, int, int);
void cursor_set (int x, int y);
void cursor_get (int *x, int *y);
void cursor_rel (int x, int y);
void get_info ();
const unsigned char * write_normal (unsigned const char*, unsigned const char *);
void char_command (char);
int output_tcsetattr (int a, const struct termios *t);
/* Input state */
/* Bits are..
IGNCR ignore carriage return on input - whether we nuke '\r's
the default for this is set by wheter the file is opened
in text or binary mode.
INLCR translate NL to CR on input
IUCLC map uppercase characters to lowercase on input
*/
int iflag_;
int lflag_;
/* Input calls */
int igncr_enabled ();
int input_tcsetattr (int a, const struct termios *t);
public:
fhandler_console (const char *name = 0);
fhandler_console* is_console () { return this; }
virtual fhandler_base *open (const char *path, int flags, mode_t mode=0);
virtual int write (const void *ptr, size_t len);
virtual int read (void *ptr, size_t len);
virtual int close ();
virtual int tcflush (int);
virtual int tcsetattr (int a, const struct termios *t);
virtual int tcgetattr (struct termios *t);
/* Special dup as we must dup two handles */
virtual int dup (fhandler_base *child);
virtual int ioctl (int cmd, void *);
virtual void init (HANDLE, int, int, const char *);
virtual const int always_read_ready () { return 0;}
/* Function to save state of a fhandler_console_out into memory. */
virtual int linearize(unsigned char *);
/* Function to de-linearize into a fd */
virtual int de_linearize(const unsigned char *);
virtual HANDLE get_output_handle() const { return output_handle_; }
};
class fhandler_dev_null: public fhandler_base
{
#define DEV_NULL_HANDLE_VALUE (-2)
public:
fhandler_dev_null (const char *name = 0);
virtual fhandler_base *open (const char *path, int flags, mode_t mode=0);
virtual int close ();
virtual int fstat (struct stat *buf);
virtual int ioctl (int cmd, void *);
virtual int read (void *ptr, size_t len);
virtual int write (const void *ptr, size_t len);
virtual off_t lseek (off_t offset, int whence);
virtual int dup (fhandler_base *child);
virtual void dump ();
};
#if 0
/* You can't do this */
typedef union
{
fhandler_normal normal;
fhandler_dev_null dev_null;
fhandler bare;
fhandler_tty tty;
} fhandler_union;
#else
#define fhandler_union fhandler_console
#endif
uid_t get_file_owner (char *);
gid_t get_file_group (char *);
BOOL get_file_attribute(char *, int *);
BOOL set_file_attribute(char *, int);
#endif /* _FHANDLER_H_ */
--------------49597C673EEAD2813A29F035--
-
For help on using this list (especially unsubscribing), send a message to
"gnu-win32-request AT cygnus DOT com" with one line of text: "help".
- Raw text -