ocaml/otherlibs/unix/termios.c

304 lines
6.9 KiB
C

#include <mlvalues.h>
#include <alloc.h>
#include "unix.h"
#ifdef HAS_TERMIOS
#include <termios.h>
#include <errno.h>
static struct termios terminal_status;
enum { Bool, Enum, Speed, Char, End };
enum { Input, Output };
#define iflags ((long)(&terminal_status.c_iflag))
#define oflags ((long)(&terminal_status.c_oflag))
#define cflags ((long)(&terminal_status.c_cflag))
#define lflags ((long)(&terminal_status.c_lflag))
#define cc(n) ((long)(&terminal_status.c_cc[n]))
/* Number of fields in the terminal_io record field. Cf. unix.mli */
#define NFIELDS 51
/* Structure of the terminal_io record. Cf. unix.mli */
static long terminal_io_descr[] = {
/* Input modes */
Bool, iflags, IGNBRK,
Bool, iflags, BRKINT,
Bool, iflags, IGNPAR,
Bool, iflags, PARMRK,
Bool, iflags, INPCK,
Bool, iflags, ISTRIP,
Bool, iflags, INLCR,
Bool, iflags, IGNCR,
Bool, iflags, ICRNL,
Bool, iflags, IXON,
Bool, iflags, IXOFF,
/* Output modes */
Bool, oflags, OPOST,
Bool, oflags, OLCUC,
Bool, oflags, ONLCR,
Bool, oflags, OCRNL,
Bool, oflags, ONOCR,
Bool, oflags, ONLRET,
Bool, oflags, OFILL,
Bool, oflags, OFDEL,
Enum, oflags, 0, 2, NLDLY, NL0, NL1,
Enum, oflags, 0, 2, CRDLY, CR0, CR1,
Enum, oflags, 0, 4, TABDLY, TAB0, TAB1, TAB2, TAB3,
Enum, oflags, 0, 2, BSDLY, BS0, BS1,
Enum, oflags, 0, 2, VTDLY, VT0, VT1,
Enum, oflags, 0, 2, FFDLY, FF0, FF1,
/* Control modes */
Speed, Output,
Speed, Input,
Enum, cflags, 5, 4, CSIZE, CS5, CS6, CS7, CS8,
Enum, cflags, 1, 2, CSTOPB, 0, CSTOPB,
Bool, cflags, CREAD,
Bool, cflags, PARENB,
Bool, cflags, PARODD,
Bool, cflags, HUPCL,
Bool, cflags, CLOCAL,
/* Local modes */
Bool, lflags, ISIG,
Bool, lflags, ICANON,
Bool, lflags, NOFLSH,
Bool, lflags, ECHO,
Bool, lflags, ECHOE,
Bool, lflags, ECHOK,
Bool, lflags, ECHONL,
/* Control characters */
Char, cc(VINTR),
Char, cc(VQUIT),
Char, cc(VERASE),
Char, cc(VKILL),
Char, cc(VEOF),
Char, cc(VEOL),
Char, cc(VMIN),
Char, cc(VTIME),
Char, cc(VSTART),
Char, cc(VSTOP),
End
};
#undef iflags
#undef oflags
#undef cflags
#undef lflags
#undef cc
struct speedtable_entry ;
static struct {
speed_t speed;
int baud;
} speedtable[] = {
B0, 0,
B50, 50,
B75, 75,
B110, 110,
B134, 134,
B150, 150,
B300, 300,
B600, 600,
B1200, 1200,
B1800, 1800,
B2400, 2400,
B4800, 4800,
B9600, 9600,
B19200, 19200,
B38400, 38400
};
#define NSPEEDS (sizeof(speedtable) / sizeof(speedtable[0]))
static void encode_terminal_status(dst)
value * dst;
{
long * pc;
int i;
for(pc = terminal_io_descr; *pc != End; dst++) {
switch(*pc++) {
case Bool:
{ int * src = (int *) (*pc++);
int msk = *pc++;
*dst = Val_bool(*src & msk);
break; }
case Enum:
{ int * src = (int *) (*pc++);
int ofs = *pc++;
int num = *pc++;
int msk = *pc++;
for (i = 0; i < num; i++) {
if ((*src & msk) == pc[i]) {
*dst = Val_int(i + ofs);
break;
}
}
pc += num;
break; }
case Speed:
{ int which = *pc++;
speed_t speed;
switch (which) {
case Output:
speed = cfgetospeed(&terminal_status); break;
case Input:
speed = cfgetispeed(&terminal_status); break;
}
for (i = 0; i < NSPEEDS; i++) {
if (speed == speedtable[i].speed) {
*dst = Val_int(speedtable[i].baud);
break;
}
}
break; }
case Char:
{ unsigned char * src = (unsigned char *) (*pc++);
*dst = Val_int(*src);
break; }
}
}
}
static void decode_terminal_status(src)
value * src;
{
long * pc;
int i;
for (pc = terminal_io_descr; *pc != End; src++) {
switch(*pc++) {
case Bool:
{ int * dst = (int *) (*pc++);
int msk = *pc++;
if (Tag_val(*src) != 0)
*dst |= msk;
else
*dst &= ~msk;
break; }
case Enum:
{ int * dst = (int *) (*pc++);
int ofs = *pc++;
int num = *pc++;
int msk = *pc++;
i = Int_val(*src) - ofs;
if (i >= 0 && i < num) {
*dst = (*dst & ~msk) | pc[i];
} else {
unix_error(EINVAL, "tcsetattr", Nothing);
}
pc += num;
break; }
case Speed:
{ int which = *pc++;
int baud = Int_val(*src);
int res;
for (i = 0; i < NSPEEDS; i++) {
if (baud == speedtable[i].baud) {
switch (which) {
case Output:
res = cfsetospeed(&terminal_status, speedtable[i].speed); break;
case Input:
res = cfsetispeed(&terminal_status, speedtable[i].speed); break;
}
if (res == -1) uerror("tcsetattr", Nothing);
goto ok;
}
}
unix_error(EINVAL, "tcsetattr", Nothing);
ok:
break; }
case Char:
{ unsigned char * dst = (unsigned char *) (*pc++);
*dst = Int_val(*src);
break; }
}
}
}
value unix_tcgetattr(fd)
value fd;
{
value res;
if (tcgetattr(Int_val(fd), &terminal_status) == -1)
uerror("tcgetattr", Nothing);
res = alloc_tuple(NFIELDS);
encode_terminal_status(&Field(res, 0));
return res;
}
static int when_flag_table[] = {
TCSANOW, TCSADRAIN, TCSAFLUSH
};
value unix_tcsetattr(fd, when, arg)
value fd, when, arg;
{
if (tcgetattr(Int_val(fd), &terminal_status) == -1)
uerror("tcsetattr", Nothing);
decode_terminal_status(&Field(arg, 0));
if (tcsetattr(Int_val(fd),
when_flag_table[Tag_val(when)],
&terminal_status) == -1)
uerror("tcsetattr", Nothing);
return Val_unit;
}
value unix_tcsendbreak(fd, delay)
value fd, delay;
{
if (tcsendbreak(Int_val(fd), Int_val(delay)) == -1)
uerror("tcsendbreak", Nothing);
return Val_unit;
}
value unix_tcdrain(fd)
value fd;
{
if (tcdrain(Int_val(fd)) == -1) uerror("tcdrain", Nothing);
return Val_unit;
}
static int queue_flag_table[] = {
TCIFLUSH, TCOFLUSH, TCIOFLUSH
};
value unix_tcflush(fd, queue)
value fd, queue;
{
if (tcflush(Int_val(fd), queue_flag_table[Tag_val(queue)]) == -1)
uerror("tcflush", Nothing);
return Val_unit;
}
static int action_flag_table[] = {
TCOOFF, TCOON, TCIOFF, TCION
};
value unix_tcflow(fd, action)
value fd, action;
{
if (tcflow(Int_val(fd), action_flag_table[Tag_val(action)]) == -1)
uerror("tcflow", Nothing);
return Val_unit;
}
#else
value unix_tcgetattr() { invalid_argument("tcgetattr not implemented"); }
value unix_tcsetattr() { invalid_argument("tcsetattr not implemented"); }
value unix_tcsendbreak() { invalid_argument("tcsendbreak not implemented"); }
value unix_tcdrain() { invalid_argument("tcdrain not implemented"); }
value unix_tcflush() { invalid_argument("tcflush not implemented"); }
value unix_tcflow() { invalid_argument("tcflow not implemented"); }
#endif