summaryrefslogtreecommitdiffstats
path: root/otherlibs/unix/termios.c
diff options
context:
space:
mode:
Diffstat (limited to 'otherlibs/unix/termios.c')
-rw-r--r--otherlibs/unix/termios.c303
1 files changed, 303 insertions, 0 deletions
diff --git a/otherlibs/unix/termios.c b/otherlibs/unix/termios.c
new file mode 100644
index 000000000..fdb0fb95d
--- /dev/null
+++ b/otherlibs/unix/termios.c
@@ -0,0 +1,303 @@
+#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
+