echo x - Makefile
sed '/^X/s///' > Makefile << '/'
XCC=exec cc
XCFLAGS= -c -O -D_MINIX -D_POSIX_SOURCE -m
X
Xall:	
X	@$(CC) $(CFLAGS) *.c
X
Xclean:	
X	@rm -rf *.o *.s *.bak
X
/
echo x - NOTES
sed '/^X/s///' > NOTES << '/'
XLine control
X------------
XThe line control functions are not really supported by old Minix tty
Xdrivers, and the fake versions in this implementation can not do much
Xto get around this.  It may be best to leave the following modules out
Xof the library:
X
X    tcdrain.$O
X    tcflow.$O
X    tcflush.$O
X    tcsendbreak.$O
X
XThen the linker will complain about missing routines, and you can look at
Xthese routines to decide whether they are good enough for a particular
Xapplication.  tcflush() works in some cases.
X
XNone of these routines is POSIX as POSIX doesn't specify ENOSYS as a valid
Xreturn value for any of them.
/
echo x - cfgetispeed.c
sed '/^X/s///' > cfgetispeed.c << '/'
X#include <termios.h>
X
X#undef cfgetispeed
X
Xspeed_t cfgetispeed(termios_p)
X_CONST struct termios *termios_p;
X{
X    return termios_p->_c_ispeed;
X}
/
echo x - cfgetospeed.c
sed '/^X/s///' > cfgetospeed.c << '/'
X#include <termios.h>
X
X#undef cfgetospeed
X
Xspeed_t cfgetospeed(termios_p)
X_CONST struct termios *termios_p;
X{
X    return termios_p->_c_ospeed;
X}
/
echo x - cfsetispeed.c
sed '/^X/s///' > cfsetispeed.c << '/'
X#include <termios.h>
X
X#undef cfsetispeed
X
Xint cfsetispeed(termios_p, speed)
Xstruct termios *termios_p;
Xspeed_t speed;
X{
X    termios_p->_c_ispeed = speed;
X    return 0;
X}
/
echo x - cfsetospeed.c
sed '/^X/s///' > cfsetospeed.c << '/'
X#include <termios.h>
X
X#undef cfsetospeed
X
Xint cfsetospeed(termios_p, speed)
Xstruct termios *termios_p;
Xspeed_t speed;
X{
X    termios_p->_c_ospeed = speed;
X    return 0;
X}
/
echo x - tcdrain.c
sed '/^X/s///' > tcdrain.c << '/'
X#include <errno.h>
X#include <termios.h>
X
Xint tcdrain(filedes)
Xint filedes;
X{
X    /* There is no way to wait for all output to be sent. */
X    errno = ENOSYS;
X    return -1;
X}
/
echo x - tcflow.c
sed '/^X/s///' > tcflow.c << '/'
X#include <errno.h>
X#include <termios.h>
X
Xint tcflow(filedes, act)
Xint filedes;
Xint act;
X{
X    /* There is no way to do flow control.  Just writing a start or stop
X     * character is not enough.  There are a lot of reasons for this,
X     * including: the start or stop character should be sent immediately
X     * and not after all the pending output has been sent.  So we only
X     * check for valid parameters here.
X     */
X    errno = act == TCOOFF || act == TCOON || act == TCIOFF || act == TCION
X		? ENOSYS : EINVAL;
X    return -1;
X}
/
echo x - tcflush.c
sed '/^X/s///' > tcflush.c << '/'
X#include <errno.h>
X#include <termios.h>
X
Xint tcflush(filedes, queue_selector)
Xint filedes;
Xint queue_selector;
X{
X    /* Only flushing both input and output is supported by the old drivers.
X     * But we still check for valid parameters.
X     */
X    if (queue_selector == TCIFLUSH)
X	errno = ENOSYS;
X    else if (queue_selector == TCOFLUSH)
X	errno = ENOSYS;
X    else if (queue_selector == TCIOFLUSH)
X    {
X#ifdef TIOCFLUSH
X	return ioctl(filedes, TIOCFLUSH, (struct sgttyb *)0) < 0
X		? -1 : 0;
X#else
X	errno = ENOSYS;
X#endif
X    }
X    else
X	errno = EINVAL;
X    return -1;
X}
/
echo x - tcsendbreak.c
sed '/^X/s///' > tcsendbreak.c << '/'
X#include <errno.h>
X#include <termios.h>
X
Xint tcsendbreak(filedes, duration)
Xint filedes;
Xint duration;
X{
X    /* There is no way to generate a break condition. */
X    errno = ENOSYS;
X    return -1;
X}
/
echo x - termios.c
sed '/^X/s///' > termios.c << '/'
X/* termios.c - fake termios interface using sgtty interface */
X
X/* Force all Minix special symbols to be visible here. */
X#undef  _POSIX_SOURCE		/* in case it is defined elsewhere */
X#undef  _MINIX			/* ditto */
X#define _POSIX_SOURCE	1
X#define _MINIX		1
X
X#include <errno.h>
X#include <termios.h>
X#include <sys/types.h>
X#include <unistd.h>
X
X/* Redefine some of the termios.h names with 'T_' prefixed to the name.
X * We will undefine them soon to avoid clashes with <sgtty.h>.
X */
X#define T_ECHO		0000001
X#define T_XTABS		0006000
X#define T_CRMOD		0000020
X
X#if !(T_ECHO == ECHO && T_XTABS == XTABS && T_CRMOD == CRMOD)
X#include "error, guessed wrong values for ECHO, XTABS or CRMOD in termios.h"
X#endif
X
X/* Undefine everything that clashes with sgtty.h. */
X#undef B0
X#undef B50
X#undef B75
X#undef B110
X#undef B134
X#undef B150
X#undef B200
X#undef B300
X#undef B600
X#undef B1200
X#undef B1800
X#undef B2400
X#undef B4800
X#undef B9600
X#undef B19200
X#undef B28800
X#undef B38400
X#undef B57600
X#undef B115200
X#undef ECHO
X#undef XTABS
X#undef CRMOD
X
X#include <sgtty.h>
X
X#if !(T_XTABS == XTABS && T_CRMOD == CRMOD)
X#include "error, guessed wrong values for XTABS or CRMOD in sgtty.h"
X#endif
X
X/* There is an error in <sgtty.h>.  It defines sg_ispeed and sg_ospeed as
X * char values.  At the same time it defines some values for them which they
X * could never give back when char is a signed type (B19200 is defined as 192).
X * Furthermore ACK has some problem with casting chars to unsigned chars.
X * It just ignores such a cast and
X *    char c = 192;
X *    int ic = (unsigned char)c;
X * won't work as expected (ic != 192).
X * The following is a workaround.
X */
X#ifdef __ACK__
X#define UCHAR(c)	((c) & 0xFF)
X#else
X#define UCHAR(c)	((unsigned char) (c))
X#endif
X
X/* The following is a hack for CRMOD setting.
X * Some programs activate OPOST because they want writing '\n' to position
X * the cursor to the beginning of the next line (this behaviour is not
X * guaranteed, because the effect of OPOST is implementation defined).  Some
X * programs clear ICRNL because they want to read CR as CR and not as LF.
X * It is impossible to satisfy both requirements (grumble), and this
X * implementation has an external variable __tios_crmod that specifies which
X * requirement has priority.  There are 3 possibilities:
X *
X *  __tios_crmod == _TC_COPY_CRMOD	copy CRMOD from c_oflag
X *  __tios_crmod == _TC_ALWAYS_CRMOD	set CRMOD for OPOST
X *  __tios_crmod == _TC_NEVER_CRMOD	set CRMOD for ICRNL only
X *
X * Here is the default method for the termios emulation if not specified
X * elsewhere.  I hope a program that requires an output of '\n' to act in
X * the usual way won't change any of the bits in c_oflag and so an active
X * CRMOD (from tcgetattr) will be kept.
X */
X#ifndef DFLT_TIOS_CRMOD
X#define DFLT_TIOS_CRMOD _TC_COPY_CRMOD
X#endif
X
Xint __tios_crmod = DFLT_TIOS_CRMOD;
X
X/* Various hacks are used for IXON control.  Turning off IXON is supposed to
X * stop output flow control, but there is no way to stop flow control in
X * cooked and cbreak modes using the sgtty interface.  The best we can do is
X * is to set the start and stop characters to an unusual character (the start
X * character will actually never be seen but any other character will restart
X * output).  Then it is difficult to restore the control characters.  We only
X * handle characters 0 to 0x1F, by ORing in 0xE0.  The mask might have to be
X * changed to avoid national characters (it can also be 0x80, 0xA0 or 0xC0).
X */
X#define HIDDEN_TC(tc)	(UCHAR(tc) >= HIDE_TC(0) && UCHAR(tc) <= HIDE_TC(0x1F))
X#define HIDE_TC(tc)	(UCHAR(tc) | HIDE_TC_MASK)
X#define HIDE_TC_MASK	0xE0
X#define UNHIDE_TC(tc)	(UCHAR(tc) & ~HIDE_TC_MASK)
X
X#if (_POSIX_VDISABLE <= 0x80 \
X     || _POSIX_VDISABLE >= HIDE_TC_MASK \
X	&& _POSIX_VDISABLE <= HIDE_TC_MASK + 0x1F)
X#include "warning, you should change _POSIX_VDISABLE in <unistd.h>"
X#endif
X
Xstatic _PROTOTYPE( int tc_to_sg_speed, (speed_t speed) );
Xstatic _PROTOTYPE( speed_t sg_to_tc_speed, (int speed) );
X
X/* Don't bother with the low speeds - Minix does not support them.  Add
X * support for higher speeds (speeds are now easy and don't need defines
X * because they are not encoded).
X */
X
Xstatic speed_t sg_to_tc_speed(speed)
Xint speed;
X{
X    /* The speed encodings in sgtty.h and termios.h are different.  Both are
X     * inflexible.  Minix doesn't really support B0 but we map it through
X     * anyway.  It doesn't support B50, B75 or B134.
X     */
X    switch (speed)
X    {
X	case B0: return 0;
X	case B110: return 110;
X	case B200: return 200;
X	case B300: return 300;
X	case B600: return 600;
X	case B1200: return 1200;
X	case B1800: return 1800;
X	case B2400: return 2400;
X	case B4800: return 4800;
X	case B9600: return 9600;
X	case B19200: return 19200;
X#ifdef B28800
X	case B28800: return 28800;
X#endif
X#ifdef B38400
X	case B38400: return 38400;
X#endif
X#ifdef B57600
X	case B57600: return 57600;
X#endif
X#ifdef B115200
X	case B115200: return 115200;
X#endif
X	default: return (speed_t) -1;
X    }
X}
X
Xstatic int tc_to_sg_speed(speed)
Xspeed_t speed;
X{
X    /* Don't use a switch here in case the compiler is 16-bit and doesn't
X     * properly support longs (speed_t's) in switches.  It turns out the
X     * switch is larger and slower for most compilers anyway!
X     */
X    if (speed == 0) return 0;
X    if (speed == 110) return B110;
X    if (speed == 200) return B200;
X    if (speed == 300) return B300;
X    if (speed == 600) return B600;
X    if (speed == 1200) return B1200;
X    if (speed == 1800) return B1800;
X    if (speed == 2400) return B2400;
X    if (speed == 4800) return B4800;
X    if (speed == 9600) return B9600;
X    if (speed == 19200) return B19200;
X#ifdef B28800
X    if (speed == 28800) return B28800;
X#endif
X#ifdef B38400
X    if (speed == 38400) return B38400;
X#endif
X#ifdef B57600
X    if (speed == 57600) return B57600;
X#endif
X#ifdef B115200
X    if (speed == 115200) return B115200;
X#endif
X    return -1;
X}
X
Xint tcgetattr(filedes, termios_p)
Xint filedes;
Xstruct termios *termios_p;
X{
X    struct sgttyb sgbuf;
X    struct tchars tcbuf;
X
X    if (ioctl(filedes, TIOCGETP, &sgbuf) < 0
X	|| ioctl(filedes, TIOCGETC, (struct sgttyb *) &tcbuf) < 0)
X	return -1;
X
X    /* Minix input flags:
X     *   BRKINT:  forced off (break is not recognized)
X     *   IGNBRK:  forced on (break is not recognized)
X     *   ICRNL:   set if CRMOD is set and not RAW (CRMOD also controls output)
X     *   IGNCR:   forced off (ignoring CR's is not supported)
X     *   INLCR:   forced off (mapping NL's to CR's is not supported)
X     *   ISTRIP:  forced off (should be off for consoles, on for rs232 no RAW)
X     *   IXOFF:   forced off (rs232 uses CTS instead of XON/XOFF)
X     *   IXON:    forced on if not RAW
X     *   PARMRK:  forced off (no '\377', '\0', X sequence on errors)
X     * ? IGNPAR:  forced off (input with parity/framing errors is kept)
X     * ? INPCK:   forced off (input parity checking is not supported)
X     */
X    termios_p->c_iflag = IGNBRK;
X    if (!(sgbuf.sg_flags & RAW))
X    {
X	if (!HIDDEN_TC(tcbuf.t_startc) || !HIDDEN_TC(tcbuf.t_stopc))
X	    termios_p->c_iflag |= IXON;
X	if (sgbuf.sg_flags & CRMOD)
X	    termios_p->c_iflag |= ICRNL;
X    }
X
X    /* Minix output flags:
X     *   OPOST:   set if CRMOD or XTABS is set
X     *   XTABS:   copied from sg_flags
X     *   CRMOD:	  copied from sg_flags
X     */
X    termios_p->c_oflag = sgbuf.sg_flags & (CRMOD | XTABS);
X    if (termios_p->c_oflag)
X	termios_p->c_oflag |= OPOST;
X
X    /* Minix local flags:
X     *   ECHO:    set if ECHO is set
X     *   ECHOE:   set if ECHO is set (echo ERASE as error-correcting backspace)
X     *   ECHOK:   set if ECHO is set ('\n' echoed after KILL char)
X     *   ECHONL:  forced off ('\n' not echoed when ECHO isn't set)
X     *   ICANON:  set if neither CBREAK nor RAW
X     *   IEXTEN:  forced off
X     *   ISIG:    set if not RAW
X     *   NOFLSH:  forced off (input/output queues are always flushed)
X     *   TOSTOP:  forced off (no job control)
X     */
X    termios_p->c_lflag = 0;
X    if (sgbuf.sg_flags & ECHO)
X	termios_p->c_lflag |= T_ECHO | ECHOE | ECHOK;
X    if (!(sgbuf.sg_flags & RAW))
X    {
X	termios_p->c_lflag |= ISIG;
X	if (!(sgbuf.sg_flags & CBREAK))
X	    termios_p->c_lflag |= ICANON;
X    }
X
X    /* Minix control flags:
X     *   CLOCAL:  forced on (ignore modem status lines - not quite right)
X     *   CREAD:   forced on (receiver is always enabled)
X     *   CSIZE:   CS5-CS8 correspond directly to BITS5-BITS8
X     *   CSTOPB:  set for B110 (driver will generate 2 stop-bits than)
X     *   HUPCL:   forced off
X     *   PARENB:  set if EVENP or ODDP is set
X     *   PARODD:  set if ODDP is set
X     */
X    termios_p->c_cflag = CLOCAL | CREAD;
X    switch (sgbuf.sg_flags & BITS8)
X    {
X	case BITS5: termios_p->c_cflag |= CS5; break;
X	case BITS6: termios_p->c_cflag |= CS6; break;
X	case BITS7: termios_p->c_cflag |= CS7; break;
X	case BITS8: termios_p->c_cflag |= CS8; break;
X    }
X    if (sgbuf.sg_flags & ODDP)
X	termios_p->c_cflag |= PARENB | PARODD;
X    if (sgbuf.sg_flags & EVENP)
X	termios_p->c_cflag |= PARENB;
X    if (sgbuf.sg_ispeed == B110)
X	termios_p->c_cflag |= CSTOPB;
X
X    /* Minix may give back different input and output baud rates,
X     * but only the input baud rate is valid for both.
X     * As our termios emulation will fail, if input baud rate differs
X     * from output baud rate, force them to be equal.
X     * Otherwise it would be very surprising not to be able to set
X     * the terminal back to the state returned by tcgetattr :).
X     */
X    termios_p->_c_ospeed =
X    termios_p->_c_ispeed = sg_to_tc_speed(UCHAR(sgbuf.sg_ispeed));
X
X    /* Minix control characters correspond directly except VSUSP and the
X     * important VMIN and VTIME are not really supported.
X     */
X    termios_p->c_cc[VEOF] = tcbuf.t_eofc;
X    termios_p->c_cc[VEOL] = tcbuf.t_brkc;
X    termios_p->c_cc[VERASE] = sgbuf.sg_erase;
X    termios_p->c_cc[VINTR] = tcbuf.t_intrc;
X    termios_p->c_cc[VKILL] = sgbuf.sg_kill;
X    termios_p->c_cc[VQUIT] = tcbuf.t_quitc;
X    if (HIDDEN_TC(tcbuf.t_startc) && HIDDEN_TC(tcbuf.t_stopc))
X    {
X	termios_p->c_cc[VSTART] = UNHIDE_TC(tcbuf.t_startc);
X	termios_p->c_cc[VSTOP] = UNHIDE_TC(tcbuf.t_stopc);
X    }
X    else
X    {
X	termios_p->c_cc[VSTART] = tcbuf.t_startc;
X	termios_p->c_cc[VSTOP] = tcbuf.t_stopc;
X    }
X    termios_p->c_cc[VMIN] = 1;
X    termios_p->c_cc[VTIME] = 0;
X    termios_p->c_cc[VSUSP] = 0;
X
X    return 0;
X}
X
Xint tcsetattr(filedes, opt_actions, termios_p)
Xint filedes;
Xint opt_actions;
X_CONST struct termios *termios_p;
X{
X    struct sgttyb sgbuf;
X    struct tchars tcbuf;
X    int sg_ispeed;
X    int sg_ospeed;
X
X    /* Posix 1003.1-1988 page 135 says:
X     *   Attempts to set unsupported baud rates shall be ignored, and it is
X     *   implementation-defined whether an error is returned by any or all of
X     *   cfsetispeed(), cfsetospeed(), or tcsetattr().  This refers both to
X     *   changes to baud rates not supported by the hardware, and to changes
X     *   setting the input and output baud rates to different values if the
X     *   hardware does not support it.
X     *
X     * In this implementation, tcsetattr may return an error but the cfset
X     * functions do not.  The driver will use the input speed for both input
X     * and output if split speeds are not supported (no error).
X     */
X    sg_ospeed = tc_to_sg_speed(termios_p->_c_ospeed);
X    if (termios_p->_c_ispeed == 0)
X	sg_ispeed = sg_ospeed;
X    else
X	sg_ispeed = tc_to_sg_speed(termios_p->_c_ispeed);
X    if (sg_ispeed == -1 || sg_ospeed == -1)
X    {
X	errno = EINVAL;
X	return -1;
X    }
X    sgbuf.sg_ispeed = sg_ispeed;
X    sgbuf.sg_ospeed = sg_ospeed;
X    sgbuf.sg_flags = 0;
X
X    /* I don't know what should happen with requests that are not supported by
X     * old Minix drivers and therefore cannot be emulated.
X     * Returning an error may confuse the application (the values aren't really
X     * invalid or unsupported by the hardware, they just cannot be satisfied
X     * by the driver).  Not returning an error might be even worse because the
X     * driver will act different to what the application requires it to act
X     * after successfully setting the attributes as specified.
X     * Settings that cannot be emulated fully include:
X     *   _c_ospeed != 110 && c_cflag & CSTOPB
X     *   _c_ospeed == 110 && ! c_cflag & CSTOPB
X     *   (c_cc[VMIN] != 1 || c_cc[VTIME] != 0) && ! c_lflag & ICANON
X     *   c_lflag & ICANON && ! c_lflag & ISIG
X     * For the moment I just ignore these conflicts.  BTW `good' programs will
X     * set values with tcsetattr and then check again with tcgetattr.
X     */
X
X    if (termios_p->c_oflag & OPOST)
X    {
X	if ((__tios_crmod == _TC_COPY_CRMOD && (termios_p->c_oflag & CRMOD))
X	    || __tios_crmod == _TC_ALWAYS_CRMOD)
X		sgbuf.sg_flags |= CRMOD;
X
X	if (termios_p->c_oflag & XTABS)
X		sgbuf.sg_flags |= XTABS;
X    }
X
X    if (termios_p->c_iflag & ICRNL)
X	/* We couldn't do it better :-(. */
X	sgbuf.sg_flags |= CRMOD;
X
X    if (termios_p->c_lflag & T_ECHO)
X	sgbuf.sg_flags |= ECHO;
X    if (!(termios_p->c_lflag & ICANON))
X    {
X	if (termios_p->c_lflag & ISIG)
X	     sgbuf.sg_flags |= CBREAK;
X	else
X	     sgbuf.sg_flags |= RAW;
X    }
X
X    switch (termios_p->c_cflag & CSIZE)
X    {
X	case CS5: sgbuf.sg_flags |= BITS5; break;
X	case CS6: sgbuf.sg_flags |= BITS6; break;
X	case CS7: sgbuf.sg_flags |= BITS7; break;
X	case CS8: sgbuf.sg_flags |= BITS8; break;
X    }
X    if (termios_p->c_cflag & PARENB)
X    {
X	if (termios_p->c_cflag & PARODD)
X	    sgbuf.sg_flags |= ODDP;
X	else
X	    sgbuf.sg_flags |= EVENP;
X    }
X
X    sgbuf.sg_erase = termios_p->c_cc[VERASE];
X    sgbuf.sg_kill = termios_p->c_cc[VKILL];
X    tcbuf.t_intrc = termios_p->c_cc[VINTR];
X    tcbuf.t_quitc = termios_p->c_cc[VQUIT];
X    if (termios_p->c_iflag & IXON)
X    {
X	tcbuf.t_startc = termios_p->c_cc[VSTART];
X	tcbuf.t_stopc = termios_p->c_cc[VSTOP];
X    }
X    else
X    {
X	tcbuf.t_startc = HIDE_TC(termios_p->c_cc[VSTART]);
X	tcbuf.t_stopc = HIDE_TC(termios_p->c_cc[VSTOP]);
X    }
X    tcbuf.t_eofc = termios_p->c_cc[VEOF];
X    tcbuf.t_brkc = termios_p->c_cc[VEOL];
X
X    /* Now we have prepared the new settings we must decide when to change
X     * them.  POSIX specifies 3 different optional actions:
X     *   setting them now,
X     *   wait for all output processed and then set them,
X     *   wait for all output processed, discard all input and then set them.
X     * The last 2 cannot be handled with old Minix tty drivers because
X     * there is no way to wait for all output to be processed and there is
X     * only a request to flush both input and output.  Nevertheless I have
X     * decided to flush input and output when flushing of input is requested.
X     * Wait for output is simply ignored.
X     */
X#ifdef TIOCFLUSH
X    if (opt_actions == TCSAFLUSH)
X	ioctl(filedes, TIOCFLUSH, (struct sgttyb *)0);
X#endif
X
X    return ioctl(filedes, TIOCSETP, &sgbuf) < 0 ||
X	   ioctl(filedes, TIOCSETC, (struct sgttyb *) &tcbuf) < 0 ?
X		-1 : 0;
X}
/
