diff options
author | Kaz Kylheku <kaz@kylheku.com> | 2016-05-18 06:18:54 -0700 |
---|---|---|
committer | Kaz Kylheku <kaz@kylheku.com> | 2016-05-18 06:18:54 -0700 |
commit | 6ca427f995a76ed9c5bae2ab86f864ea5ae4d376 (patch) | |
tree | 144ba53e9636aec6418c37225a74359bc20ac626 | |
parent | d2bc824b4f91f6aa462e27f8b5dde6dee1cc031f (diff) | |
download | txr-6ca427f995a76ed9c5bae2ab86f864ea5ae4d376.tar.gz txr-6ca427f995a76ed9c5bae2ab86f864ea5ae4d376.tar.bz2 txr-6ca427f995a76ed9c5bae2ab86f864ea5ae4d376.zip |
Adding termios support.
* Makefile (termios.o): New object file.
* lib.c (init): Call termios_init.
* lisplib.c (termios_set_entries, termios_instantiate): New
functions.
(lisplib_init): Register new functions in autoload table.
* share/txr/stdlib/termios.tl: New file.
* termios.c, termios.h: New files.
* txr.1: Documented termios.
-rw-r--r-- | Makefile | 1 | ||||
-rw-r--r-- | lib.c | 4 | ||||
-rw-r--r-- | lisplib.c | 28 | ||||
-rw-r--r-- | share/txr/stdlib/termios.tl | 78 | ||||
-rw-r--r-- | termios.c | 560 | ||||
-rw-r--r-- | termios.h | 27 | ||||
-rw-r--r-- | txr.1 | 710 |
7 files changed, 1407 insertions, 1 deletions
@@ -53,6 +53,7 @@ OBJS-$(have_glob) += glob.o OBJS-$(have_ftw) += ftw.o OBJS-$(have_posix_sigs) += signal.o OBJS-$(have_sockets) += socket.o +OBJS-$(have_termios) += termios.o OBJS-$(have_termios) += linenoise/linenoise.o EXTRA_OBJS-$(add_win_res) += win/txr.res @@ -63,6 +63,7 @@ #include "syslog.h" #include "glob.h" #include "ftw.h" +#include "termios.h" #include "cadr.h" #include "struct.h" #include "txr.h" @@ -9394,6 +9395,9 @@ void init(const wchar_t *pn, mem_t *(*oom)(mem_t *, size_t), #if HAVE_FTW ftw_init(); #endif +#if HAVE_TERMIOS + termios_init(); +#endif cadr_init(); time_init(); @@ -281,7 +281,6 @@ static val yield_instantiate(val set_fun) } #if HAVE_SOCKETS - static val sock_set_entries(val dlt, val fun) { val name[] = { @@ -313,6 +312,30 @@ static val sock_instantiate(val set_fun) #endif +#if HAVE_TERMIOS + +static val termios_set_entries(val dlt, val fun) +{ + val name[] = { + lit("set-iflags"), lit("set-oflags"), lit("set-cflags"), lit("set-lflags"), + lit("clear-iflags"), lit("clear-oflags"), lit("clear-cflags"), lit("clear-lflags"), + lit("go-raw"), lit("go-cbreak"), lit("go-canon"), + lit("string-encode"), lit("string-decode"), nil + }; + set_dlt_entries(dlt, name, fun); + return nil; +} + +static val termios_instantiate(val set_fun) +{ + funcall1(set_fun, nil); + load(format(nil, lit("~a/termios.tl"), stdlib_path, nao)); + sock_load_init(); + return nil; +} + +#endif + val dlt_register(val dlt, val (*instantiate)(val), val (*set_entries)(val, val)) @@ -339,6 +362,9 @@ void lisplib_init(void) #if HAVE_SOCKETS dlt_register(dl_table, sock_instantiate, sock_set_entries); #endif +#if HAVE_TERMIOS + dlt_register(dl_table, termios_instantiate, termios_set_entries); +#endif } val lisplib_try_load(val sym) diff --git a/share/txr/stdlib/termios.tl b/share/txr/stdlib/termios.tl new file mode 100644 index 00000000..84a6d006 --- /dev/null +++ b/share/txr/stdlib/termios.tl @@ -0,0 +1,78 @@ +;; Copyright 2016 +;; Kaz Kylheku <kaz@kylheku.com> +;; Vancouver, Canada +;; All rights reserved. +;; +;; Redistribution of this software in source and binary forms, with or without +;; modification, is permitted provided that the following two conditions are met. +;; +;; Use of this software in any manner constitutes agreement with the disclaimer +;; which follows the two conditions. +;; +;; 1. Redistributions of source code must retain the above copyright +;; notice, this list of conditions and the following disclaimer. +;; 2. Redistributions in binary form must reproduce the above copyright +;; notice, this list of conditions and the following disclaimer in +;; the documentation and/or other materials provided with the +;; distribution. +;; +;; THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED +;; WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +;; MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE +;; COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DAMAGES, HOWEVER CAUSED, +;; AND UNDER ANY THEORY OF LIABILITY, ARISING IN ANY WAY OUT OF THE USE OF THIS +;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +(defmeth termios set-iflags (tio . values) + (set tio.iflag (logior tio.iflag . values))) + +(defmeth termios set-oflags (tio . values) + (set tio.oflag (logior tio.oflag . values))) + +(defmeth termios set-cflags (tio . values) + (set tio.cflag (logior tio.cflag . values))) + +(defmeth termios set-lflags (tio . values) + (set tio.lflag (logior tio.lflag . values))) + +(defmeth termios clear-iflags (tio . values) + (set tio.iflag (logand tio.iflag (lognot (logior . values))))) + +(defmeth termios clear-oflags (tio . values) + (set tio.oflag (logand tio.oflag (lognot (logior . values))))) + +(defmeth termios clear-cflags (tio . values) + (set tio.cflag (logand tio.cflag (lognot (logior . values))))) + +(defmeth termios clear-lflags (tio . values) + (set tio.lflag (logand tio.lflag (lognot (logior . values))))) + +(defmeth termios go-raw (tio) + tio.(clear-iflags ignbrk brkint parmrk istrip inlcr igncr icrnl ixon) + tio.(clear-oflags opost) + tio.(clear-cflags csize parenb) + tio.(clear-lflags echo echonl icanon isig) + (if (boundp 'iexten) + tio.(clear-lflags iexten)) + tio.(set-cflags cs8) + (set tio.[cc vmin] 1) + (set tio.[cc vtime] 0)) + +(defmeth termios go-cbreak (tio) + tio.(clear-iflags icrnl) + tio.(clear-lflags icanon) + tio.(set-lfags isig) + (set tio.[cc vmin] 1) + (set tio.[cc vtime] 0)) + +(defmeth termios string-encode (tio) + (let ((*print-base* 16)) + tio.(sys:encode-speeds) + (downcase-str `@{tio.iflag}:@{tio.oflag}:@{tio.cflag}:@{tio.lflag}:\ + @{(list-vec tio.cc) ":"}`))) + +(defmeth termios string-decode (tio string) + (let ((vals (mapcar (op int-str @1 16) (split-str string ":")))) + (lset tio.iflag tio.oflag tio.cflag tio.lflag vals) + (set tio.cc (vec-list (cddddr vals))) + tio.(sys:decode-speeds))) diff --git a/termios.c b/termios.c new file mode 100644 index 00000000..cda32a55 --- /dev/null +++ b/termios.c @@ -0,0 +1,560 @@ +/* Copyright 2016 + * Kaz Kylheku <kaz@kylheku.com> + * Vancouver, Canada + * All rights reserved. + * + * Redistribution of this software in source and binary forms, with or without + * modification, is permitted provided that the following two conditions are met. + * + * Use of this software in any manner constitutes agreement with the disclaimer + * which follows the two conditions. + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DAMAGES, HOWEVER CAUSED, + * AND UNDER ANY THEORY OF LIABILITY, ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <stdarg.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <wchar.h> +#include <signal.h> +#include <dirent.h> +#include <errno.h> +#include "config.h" +#include <termios.h> +#include ALLOCA_H +#include "lib.h" +#include "gc.h" +#include "args.h" +#include "eval.h" +#include "signal.h" +#include "unwind.h" +#include "stream.h" +#include "struct.h" +#include "termios.h" + +val termios_s, iflag_s, oflag_s, cflag_s, lflag_s, cc_s, ispeed_s, ospeed_s; + +static cnum termios_speed_to_baud(speed_t code) +{ + switch (code) { + case B0: return 0; + case B50: return 50; + case B75: return 75; + case B110: return 110; + case B134: return 134; + case B150: return 150; + case B200: return 200; + case B300: return 300; + case B600: return 600; + case B1200: return 1200; + case B1800: return 1800; + case B2400: return 2400; + case B4800: return 4800; + case B9600: return 9600; + case B19200: return 19200; + case B38400: return 38400; +#ifdef B57600 + case B57600: return 57600; +#endif +#ifdef B115200 + case B115200: return 115200; +#endif +#ifdef B230400 + case B230400: return 230400; +#endif +#ifdef B460800 + case B460800: return 460800; +#endif +#ifdef B500000 + case B500000: return 500000; +#endif +#ifdef B576000 + case B576000: return 576000; +#endif +#ifdef B921600 + case B921600: return 921600; +#endif +#ifdef B1000000 + case B1000000: return 1000000; +#endif +#ifdef B1152000 + case B1152000: return 1152000; +#endif +#ifdef B1500000 + case B1500000: return 1500000; +#endif +#ifdef B2000000 + case B2000000: return 2000000; +#endif +#ifdef B2500000 + case B2500000: return 2500000; +#endif +#ifdef B3000000 + case B3000000: return 3000000; +#endif +#ifdef B3500000 + case B3500000: return 3500000; +#endif +#ifdef B4000000 + case B4000000: return 4000000; +#endif + default: return 9600; + } +} + +static speed_t termios_baud_to_speed(cnum baud) +{ + switch (baud) { + case 0: return B0; + case 50: return B50; + case 75: return B75; + case 110: return B110; + case 134: return B134; + case 150: return B150; + case 200: return B200; + case 300: return B300; + case 600: return B600; + case 1200: return B1200; + case 1800: return B1800; + case 2400: return B2400; + case 4800: return B4800; + case 9600: return B9600; + case 19200: return B19200; + case 38400: return B38400; +#ifdef B57600 + case 57600: return B57600; +#endif +#ifdef B115200 + case 115200: return B115200; +#endif +#ifdef B230400 + case 230400: return B230400; +#endif +#ifdef B460800 + case 460800: return B460800; +#endif +#ifdef B500000 + case 500000: return B500000; +#endif +#ifdef B576000 + case 576000: return B576000; +#endif +#ifdef B921600 + case 921600: return B921600; +#endif +#ifdef B1000000 + case 1000000: return B1000000; +#endif +#ifdef B1152000 + case 1152000: return B1152000; +#endif +#ifdef B1500000 + case 1500000: return B1500000; +#endif +#ifdef B2000000 + case 2000000: return B2000000; +#endif +#ifdef B2500000 + case 2500000: return B2500000; +#endif +#ifdef B3000000 + case 3000000: return B3000000; +#endif +#ifdef B3500000 + case 3500000: return B3500000; +#endif +#ifdef B4000000 + case 4000000: return B4000000; +#endif + default: + uw_throwf(error_s, lit("unsupported tty speed: ~a"), num(baud), nao); + } +} + +static val termios_unpack(struct termios *in) +{ + args_decl(args, ARGS_MIN); + val out = make_struct(termios_s, nil, args); + int i, cc_sz = (int) (sizeof in->c_cc / sizeof in->c_cc[0]); + val cc = vector(num_fast(cc_sz), nil); + + slotset(out, iflag_s, num(in->c_iflag)); + slotset(out, oflag_s, num(in->c_oflag)); + slotset(out, cflag_s, num(in->c_cflag)); + slotset(out, lflag_s, num(in->c_lflag)); + slotset(out, cc_s, cc); + slotset(out, ispeed_s, num(termios_speed_to_baud(cfgetispeed(in)))); + slotset(out, ospeed_s, num(termios_speed_to_baud(cfgetospeed(in)))); + + for (i = 0; i < cc_sz; i++) + set(vecref_l(cc, num_fast(i)), num(in->c_cc[i])); + + return out; +} + +static void termios_pack(struct termios *out, val in) +{ + int i, cc_sz = (int) (sizeof out->c_cc / sizeof out->c_cc[0]); + val cc = slot(in, cc_s); + + out->c_iflag = c_num(slot(in, iflag_s)); + out->c_oflag = c_num(slot(in, oflag_s)); + out->c_cflag = c_num(slot(in, cflag_s)); + out->c_lflag = c_num(slot(in, lflag_s)); + + cfsetispeed(out, termios_baud_to_speed(c_num(slot(in, ispeed_s)))); + cfsetospeed(out, termios_baud_to_speed(c_num(slot(in, ospeed_s)))); + + for (i = 0; i < cc_sz; i++) { + val ch = vecref(cc, num_fast(i)); + cnum c = c_num(ch); + + out->c_cc[i] = c; + + if (out->c_cc[i] != c) + uw_throwf(system_error_s, lit("value ~s is out of range for c_cc[~a] " + "of struct termios"), + ch, num_fast(i), nao); + } +} + +static val get_fd(val stream) +{ + val fd; + + if (missingp(stream)) + fd = stream_fd(std_input); + else if (integerp(stream)) + fd = stream; + else + fd = stream_fd(stream); + + return fd; +} + +static val tcgetattr_wrap(val stream) +{ + struct termios tio; + int res; + val fd = get_fd(stream); + + res = tcgetattr(c_num(fd), &tio); + + if (res < 0) + uw_throwf(system_error_s, lit("tcgetattr failed: ~d/~s"), + num(errno), string_utf8(strerror(errno)), nao); + + return termios_unpack(&tio); +} + +static val tcsetattr_wrap(val termios, val actions, val stream) +{ + struct termios tio; + int res; + val fd = get_fd(stream); + + actions = default_arg(actions, num(TCSADRAIN)); + + res = tcgetattr(c_num(fd), &tio); + + if (res < 0) + uw_throwf(system_error_s, lit("tcgetattr failed: ~d/~s"), + num(errno), string_utf8(strerror(errno)), nao); + + termios_pack(&tio, termios); + + res = tcsetattr(c_num(fd), c_num(actions), &tio); + + if (res < 0) + uw_throwf(system_error_s, lit("tcsetattr failed: ~d/~s"), + num(errno), string_utf8(strerror(errno)), nao); + + return termios; +} + +static val tcsendbreak_wrap(val duration, val stream) +{ + val fd = get_fd(stream); + int res = tcsendbreak(c_num(fd), if3(missingp(duration), + 500, c_num(duration))); + + if (res < 0) + uw_throwf(system_error_s, lit("tcsendbreak failed: ~d/~s"), + num(errno), string_utf8(strerror(errno)), nao); + + return t; +} + +static val tcdrain_wrap(val stream) +{ + val fd = get_fd(stream); + int res = tcdrain(c_num(fd)); + + if (res < 0) + uw_throwf(system_error_s, lit("tcdrain failed: ~d/~s"), + num(errno), string_utf8(strerror(errno)), nao); + + return t; +} + +static val tcflush_wrap(val queue, val stream) +{ + val fd = get_fd(stream); + int res = tcflush(c_num(fd), c_num(queue)); + + if (res < 0) + uw_throwf(system_error_s, lit("tcflush failed: ~d/~s"), + num(errno), string_utf8(strerror(errno)), nao); + + return t; +} + +static val tcflow_wrap(val action, val stream) +{ + val fd = get_fd(stream); + int res = tcflow(c_num(fd), c_num(action)); + + if (res < 0) + uw_throwf(system_error_s, lit("tcflow failed: ~d/~s"), + num(errno), string_utf8(strerror(errno)), nao); + + return t; +} + +static val encode_speeds(val termios) +{ + struct termios tio = { 0 }; + + tio.c_iflag = c_num(slot(termios, iflag_s)); + tio.c_cflag = c_num(slot(termios, cflag_s)); + cfsetispeed(&tio, termios_baud_to_speed(c_num(slot(termios, ispeed_s)))); + cfsetospeed(&tio, termios_baud_to_speed(c_num(slot(termios, ospeed_s)))); + slotset(termios, iflag_s, num(tio.c_iflag)); + slotset(termios, cflag_s, num(tio.c_cflag)); + + return termios; +} + +static val decode_speeds(val termios) +{ + struct termios tio = { 0 }; + + tio.c_cflag = c_num(slot(termios, cflag_s)); + tio.c_iflag = c_num(slot(termios, iflag_s)); + slotset(termios, ispeed_s, num(termios_speed_to_baud(cfgetispeed(&tio)))); + slotset(termios, ospeed_s, num(termios_speed_to_baud(cfgetospeed(&tio)))); + + return termios; +} + +void termios_init(void) +{ + val termios_t; + + termios_s = intern(lit("termios"), user_package); + iflag_s = intern(lit("iflag"), user_package); + oflag_s = intern(lit("oflag"), user_package); + cflag_s = intern(lit("cflag"), user_package); + lflag_s = intern(lit("lflag"), user_package); + cc_s = intern(lit("cc"), user_package); + ispeed_s = intern(lit("ispeed"), user_package); + ospeed_s = intern(lit("ospeed"), user_package); + + termios_t = make_struct_type(termios_s, nil, nil, + list(iflag_s, oflag_s, cflag_s, lflag_s, + cc_s, ispeed_s, ospeed_s, nao), + nil, nil, nil, nil); + + reg_fun(intern(lit("tcgetattr"), user_package), func_n1o(tcgetattr_wrap, 0)); + reg_fun(intern(lit("tcsetattr"), user_package), func_n3o(tcsetattr_wrap, 1)); + reg_fun(intern(lit("tcsendbreak"), user_package), func_n2o(tcsendbreak_wrap, 0)); + reg_fun(intern(lit("tcdrain"), user_package), func_n1(tcdrain_wrap)); + reg_fun(intern(lit("tcflush"), user_package), func_n2(tcflush_wrap)); + reg_fun(intern(lit("tcflow"), user_package), func_n2(tcflow_wrap)); + static_slot_ensure(termios_t, intern(lit("encode-speeds"), system_package), + func_n1(encode_speeds), nil); + static_slot_ensure(termios_t, intern(lit("decode-speeds"), system_package), + func_n1(decode_speeds), nil); + + /* cc array indexes */ + reg_varl(intern(lit("vintr"), user_package), num_fast(VINTR)); + reg_varl(intern(lit("vquit"), user_package), num_fast(VQUIT)); + reg_varl(intern(lit("verase"), user_package), num_fast(VERASE)); + reg_varl(intern(lit("vkill"), user_package), num_fast(VKILL)); + reg_varl(intern(lit("veof"), user_package), num_fast(VEOF)); + reg_varl(intern(lit("vtime"), user_package), num_fast(VTIME)); + reg_varl(intern(lit("vmin"), user_package), num_fast(VMIN)); +#ifdef VSWTC + reg_varl(intern(lit("vswtc"), user_package), num_fast(VSWTC)); +#endif + reg_varl(intern(lit("vstart"), user_package), num_fast(VSTART)); + reg_varl(intern(lit("vstop"), user_package), num_fast(VSTOP)); + reg_varl(intern(lit("vsusp"), user_package), num_fast(VSUSP)); + reg_varl(intern(lit("veol"), user_package), num_fast(VEOL)); +#ifdef VREPRINT + reg_varl(intern(lit("vreprint"), user_package), num_fast(VREPRINT)); +#endif +#ifdef VDISCARD + reg_varl(intern(lit("vdiscard"), user_package), num_fast(VDISCARD)); +#endif +#ifdef VWERASE + reg_varl(intern(lit("vwerase"), user_package), num_fast(VWERASE)); +#endif +#ifdef VLNEXT + reg_varl(intern(lit("vlnext"), user_package), num_fast(VLNEXT)); +#endif +#ifdef VEOL2 + reg_varl(intern(lit("veol2"), user_package), num_fast(VEOL2)); +#endif + /* iflag bits */ + reg_varl(intern(lit("ignbrk"), user_package), num_fast(IGNBRK)); + reg_varl(intern(lit("brkint"), user_package), num_fast(BRKINT)); + reg_varl(intern(lit("ignpar"), user_package), num_fast(IGNPAR)); + reg_varl(intern(lit("parmrk"), user_package), num_fast(PARMRK)); + reg_varl(intern(lit("inpck"), user_package), num_fast(INPCK)); + reg_varl(intern(lit("istrip"), user_package), num_fast(ISTRIP)); + reg_varl(intern(lit("inlcr"), user_package), num_fast(INLCR)); + reg_varl(intern(lit("igncr"), user_package), num_fast(IGNCR)); + reg_varl(intern(lit("icrnl"), user_package), num_fast(ICRNL)); +#ifdef IUCLC + reg_varl(intern(lit("iuclc"), user_package), num_fast(IUCLC)); +#endif + reg_varl(intern(lit("ixon"), user_package), num_fast(IXON)); + reg_varl(intern(lit("ixany"), user_package), num_fast(IXANY)); + reg_varl(intern(lit("ixoff"), user_package), num_fast(IXOFF)); +#ifdef IMAXBEL + reg_varl(intern(lit("imaxbel"), user_package), num_fast(IMAXBEL)); +#endif +#ifdef IUTF8 + reg_varl(intern(lit("iutf8"), user_package), num_fast(IUTF8)); +#endif + /* oflag bits */ + reg_varl(intern(lit("opost"), user_package), num_fast(OPOST)); +#ifdef OLCUC + reg_varl(intern(lit("olcuc"), user_package), num_fast(OLCUC)); +#endif + reg_varl(intern(lit("onlcr"), user_package), num_fast(ONLCR)); + reg_varl(intern(lit("ocrnl"), user_package), num_fast(OCRNL)); + reg_varl(intern(lit("onocr"), user_package), num_fast(ONOCR)); + reg_varl(intern(lit("onlret"), user_package), num_fast(ONLRET)); + reg_varl(intern(lit("ofill"), user_package), num_fast(OFILL)); +#ifdef OFDEL + reg_varl(intern(lit("ofdel"), user_package), num_fast(OFDEL)); +#endif + reg_varl(intern(lit("vtdly"), user_package), num_fast(VTDLY)); + reg_varl(intern(lit("vt0"), user_package), num_fast(VT0)); + reg_varl(intern(lit("vt1"), user_package), num_fast(VT1)); +#ifdef NLDLY + reg_varl(intern(lit("nldly"), user_package), num_fast(NLDLY)); + reg_varl(intern(lit("nl0"), user_package), num_fast(NL0)); + reg_varl(intern(lit("nl1"), user_package), num_fast(NL1)); +#endif +#ifdef CRDLY + reg_varl(intern(lit("crdly"), user_package), num_fast(CRDLY)); + reg_varl(intern(lit("cr0"), user_package), num_fast(CR0)); + reg_varl(intern(lit("cr1"), user_package), num_fast(CR1)); + reg_varl(intern(lit("cr2"), user_package), num_fast(CR2)); + reg_varl(intern(lit("cr3"), user_package), num_fast(CR3)); +#endif +#ifdef TABDLY + reg_varl(intern(lit("tabdly"), user_package), num_fast(TABDLY)); + reg_varl(intern(lit("tab0"), user_package), num_fast(TAB0)); + reg_varl(intern(lit("tab1"), user_package), num_fast(TAB1)); + reg_varl(intern(lit("tab2"), user_package), num_fast(TAB2)); + reg_varl(intern(lit("tab3"), user_package), num_fast(TAB3)); +#endif +#ifdef BSDLY + reg_varl(intern(lit("bsdly"), user_package), num_fast(BSDLY)); + reg_varl(intern(lit("bs0"), user_package), num_fast(BS0)); + reg_varl(intern(lit("bs1"), user_package), num_fast(BS1)); +#endif +#ifdef FFDLY + reg_varl(intern(lit("ffdly"), user_package), num_fast(FFDLY)); + reg_varl(intern(lit("ff0"), user_package), num_fast(FF0)); + reg_varl(intern(lit("ff1"), user_package), num_fast(FF1)); +#endif + /* cflag bits */ + reg_varl(intern(lit("csize"), user_package), num_fast(CSIZE)); + reg_varl(intern(lit("cs5"), user_package), num_fast(CS5)); + reg_varl(intern(lit("cs6"), user_package), num_fast(CS6)); + reg_varl(intern(lit("cs7"), user_package), num_fast(CS7)); + reg_varl(intern(lit("cs8"), user_package), num_fast(CS8)); + reg_varl(intern(lit("cstopb"), user_package), num_fast(CSTOPB)); + reg_varl(intern(lit("cread"), user_package), num_fast(CREAD)); + reg_varl(intern(lit("parenb"), user_package), num_fast(PARENB)); + reg_varl(intern(lit("parodd"), user_package), num_fast(PARODD)); + reg_varl(intern(lit("hupcl"), user_package), num_fast(HUPCL)); + reg_varl(intern(lit("clocal"), user_package), num_fast(CLOCAL)); +#ifdef CBAUD + reg_varl(intern(lit("cbaud"), user_package), num_fast(CBAUD)); +#endif +#ifdef CBAUDEX + reg_varl(intern(lit("cbaudex"), user_package), num_fast(CBAUDEX)); +#endif +#ifdef CMSPAR + reg_varl(intern(lit("cmspar"), user_package), num_fast(CMSPAR)); +#endif +#ifdef CRTSCTS + reg_varl(intern(lit("crtscts"), user_package), num_fast(CRTSCTS)); +#endif + /* lflag bits */ + reg_varl(intern(lit("isig"), user_package), num_fast(ISIG)); + reg_varl(intern(lit("icanon"), user_package), num_fast(ICANON)); + reg_varl(intern(lit("echo"), user_package), num_fast(ECHO)); + reg_varl(intern(lit("echoe"), user_package), num_fast(ECHOE)); + reg_varl(intern(lit("echok"), user_package), num_fast(ECHOK)); + reg_varl(intern(lit("echonl"), user_package), num_fast(ECHONL)); + reg_varl(intern(lit("noflsh"), user_package), num_fast(NOFLSH)); + reg_varl(intern(lit("tostop"), user_package), num_fast(TOSTOP)); +#ifdef IEXTEN + reg_varl(intern(lit("iexten"), user_package), num_fast(IEXTEN)); +#endif +#ifdef XCASE + reg_varl(intern(lit("xcase"), user_package), num_fast(XCASE)); +#endif +#ifdef ECHOCTL + reg_varl(intern(lit("echoctl"), user_package), num_fast(ECHOCTL)); +#endif +#ifdef ECHOPRT + reg_varl(intern(lit("echoprt"), user_package), num_fast(ECHOPRT)); +#endif +#ifdef ECHOKE + reg_varl(intern(lit("echoke"), user_package), num_fast(ECHOKE)); +#endif +#ifdef FLUSHO + reg_varl(intern(lit("flusho"), user_package), num_fast(FLUSHO)); +#endif +#ifdef PENDIN + reg_varl(intern(lit("pendin"), user_package), num_fast(PENDIN)); +#endif +#ifdef EXTPROC + reg_varl(intern(lit("extproc"), user_package), num_fast(EXTPROC)); +#endif + /* tcflow */ + reg_varl(intern(lit("tcooff"), user_package), num_fast(TCOOFF)); + reg_varl(intern(lit("tcoon"), user_package), num_fast(TCOON)); + reg_varl(intern(lit("tcioff"), user_package), num_fast(TCIOFF)); + reg_varl(intern(lit("tcion"), user_package), num_fast(TCION)); + /* tcflush */ + reg_varl(intern(lit("tciflush"), user_package), num_fast(TCIFLUSH)); + reg_varl(intern(lit("tcoflush"), user_package), num_fast(TCOFLUSH)); + reg_varl(intern(lit("tcioflush"), user_package), num_fast(TCIOFLUSH)); + /* tcsetattr */ + reg_varl(intern(lit("tcsanow"), user_package), num_fast(TCSANOW)); + reg_varl(intern(lit("tcsadrain"), user_package), num_fast(TCSADRAIN)); + reg_varl(intern(lit("tcsaflush"), user_package), num_fast(TCSAFLUSH)); +} diff --git a/termios.h b/termios.h new file mode 100644 index 00000000..df8fb83d --- /dev/null +++ b/termios.h @@ -0,0 +1,27 @@ +/* Copyright 2016 + * Kaz Kylheku <kaz@kylheku.com> + * Vancouver, Canada + * All rights reserved. + * + * Redistribution of this software in source and binary forms, with or without + * modification, is permitted provided that the following two conditions are met. + * + * Use of this software in any manner constitutes agreement with the disclaimer + * which follows the two conditions. + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DAMAGES, HOWEVER CAUSED, + * AND UNDER ANY THEORY OF LIABILITY, ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +void termios_init(void); @@ -39486,6 +39486,716 @@ excluding the contiguous all-zero bits in the least significant position: how many times the address can be shifted to the right before a 1 appears in the least significant bit. +.SS* Unix Terminal Control + +\*(TX provides access to the terminal control "termios" interfaces defined by +POSIX, and some of the extensions to it in Linux. By using termios, programs +can control serial devices, consoles and virtual terminals. Terminal control +in POSIX revolves around a C language structure called +.codn "struct termios" . +This is mirrored in a \*(TL structure also called +.codn termios . + +Like-named \*(TL functions are provided which correspond to the C functions +.codn tcgetattr , +.codn tcsetattr , +.codn tcsendbreak , +.codn tcdrain , +.code tcflush +and +.codn tcflow . + +These have somewhat different argument conventions. The TTY device is specified last, +so that it can conveniently default to the +.code *stdin* +stream. A TTY device can be specified as either a stream object or a numeric +file descriptor. + +The functions +.codn cfgetispeed , +.codn cfgetospeed , +.code cfsetispeed +and +.code cfsetospeed +are not provided, because they are unnecessary. Device speed (informally, "baud rate") +is specified directly as a integer value in the +.code termios +structure. The \*(TL termios functions automatically convert between integer +values and the speed constants (like +.codn B38400 ) +used by the C API. + +All of the various termios-related constants are provided, including some non-standard +ones. They appear in lower case. For instance +.code IGNBRK +and +.code PARENB +are simply known as the predefined \*(TL variables +.code ignbrk +and +.codn parenb . + +.coNP Structure @ termios +.synb +.mets (defstruct termios nil +.mets \ \ iflag oflag cflag lflag +.mets \ \ cc ispeed ospeed) +.syne +.desc +The +.code termios +structure represents the kernel level terminal device configuration. +It holds hardware related setting such as serial line speed, parity +and handshaking. It also holds software settings like translations, +and settings affecting input behaviors. The structure closely corresponds +to the C language +.code termios +structure which exists in the POSIX API. + +The +.codn iflag , +.codn oflag , +.code cflag +and +.code lflag +slots correspond to the +.codn c_iflag , +.codn c_oflag , +.code c_cflag +and +.code c_lflag +members of the C structure. They hold integer values representing +bit fields. + +The +.code cc +slot corresponds to the +.code c_cc +member of the C structure. Whereas the +C structure's +.code c_cc +member is an array of the C type +.codn cc_t , +the +.code cc +slot is a vector of integers, whose values must have the same range as the +.code cc_t +type. + +.coNP Variables @, ignbrk @, brkint @, ignpar @, parmrk @, inpck @, istrip @, inlcr @, igncr @, icrnl @, iuclc @, ixon @, ixany @, ixoff @ imaxbel and @ iutf8 +.desc +These variables specify bitmask values for the +.code iflag +slot of the +.code termios +structure. They correspond to the C language preprocessor symbols +.codn IGNBRK , +.codn BRKINT , +.code IGNPAR +and so forth. + +The +.code imaxbel +and +.code iutf8 +variables are specific to Linux and may not be present. +Portable code should test for their presence with +.codn boundp . + +The +.code iuclc +variable is a legacy feature not found on all systems. + +Note: the +.code termios +methods +.code set-iflags +and +.code clear-iflags +provide a convenient means for setting and clearing combinations of +these flags. + +.coNP Variables @, opost @, olcuc @, onlcr @, ocrnl @, onocr @, onlret @, ofill @, ofdel @, vtdly @, vt0 @, vt1 @, nldly @, nl0 @, nl1 @, crdly @, cr0 @, cr1 @, cr2 @, cr3 @, tabdly @, tab0 @, tab1 @, tab2 @, tab3 @, bsdly @, bs0 @, bs1 @, ffdly @ ff0 and @ ff1 +.desc +These variables specify bitmask values for the +.code oflag +slot of the +.code termios +structure. They correspond to the C language preprocessor symbols +.codn OPOST , +.codn OLCUC , +.code ONLCR +and so forth. + +The variable +.code ofdel +is Linux-specific. Portable programs should test for its presence using +.codn boundp . + +The +.code olcuc +variable is a legacy feature not found on all systems. + +Likewise, whether the following groups of symbols are present is +platform-specific: +.codn nldly , +.code nl0 +and +.codn nl1 ; +.codn crdly , +.codn cr0 , +.codn cr1 , +.code cr2 +and +.codn cr3 ; +.codn tabdly , +.codn tab0 , +.codn tab1 , +.code tab2 +and +.codn tab3 ; +.codn bsdly , +.code bs0 +and +.codn bs1 ; +and +.codn ffdly , +.code ff0 +and +.codn ff1 . + +Note: the +.code termios +methods +.code set-oflags +and +.code clear-oflags +provide a convenient means for setting and clearing combinations of +these flags. + +.coNP Variables @, csize @, cs5 @, cs6 @, cs7 @, cs8 @, cstopb @, cread @, parenb @, parodd @, hupcl @, clocal @, cbaud @, cbaudex @ cmspar and @ crtscts +.desc +These variables specify bitmask values for the +.code cflag +slot of the +.code termios +structure. They correspond to the C language preprocessor symbols +.codn CSIZE , +.codn CS5 , +.code CS6 +and so forth. + +The following are present on Linux, and may not be available +on other platforms. Portable code should test for them using +.codn boundp : +.codn cbaud , +.codn cbaudex , +.code cmspar +and +.codn crtscts . + +Note: the +.code termios +methods +.code set-cflags +and +.code clear-cflags +provide a convenient means for setting and clearing combinations of +these flags. + +.coNP Variables @, isig @, icanon @, echo @, echoe @, echok @, echonl @, noflsh @, tostop @, iexten @, xcase @, echoctl @, echoprt @, echoke @, flusho @ pendin and @ extproc +.desc +These variables specify bitmask values for the +.code lflag +slot of the +.code termios +structure. They correspond to the C language preprocessor symbols +.codn ISIG , +.codn ICANON , +.code ECHO +and so forth. + +The following are present on Linux, and may not be available +on other platforms. Portable code should test for them using +.codn boundp : +.codn iexten , +.codn xcase , +.codn echoctl , +.codn echoprt , +.codn echoke , +.codn flusho , +.code pendin +and +.codn extproc . + +Note: the +.code termios +methods +.code set-lflags +and +.code clear-lflags +provide a convenient means for setting and clearing combinations of +these flags. + +.coNP Variables @, vintr @, vquit @, verase @, vkill @, veof @, vtime @, vmin @, vswtc @, vstart @, vstop @, vsusp @, veol @, vreprint @, vdiscard @, vwerase @ vlnext and @ veol2 +.desc +These variables specify integer offsets into the vector stored in the +.code cc +slot of the +.code termios +structure. They correspond to the C language preprocessor symbols +.codn VINTR , +.codn VQUIT , +.code VERASE +and so forth. + +The following are present on Linux, and may not be available +on other platforms. Portable code should test for them using +.codn boundp : +.codn vswtc , +.codn vreprint , +.codn vdiscard , +.code vlnext +and +.codn veol2 . + +.coNP Variables @, tcooff @, tcoon @ tcioff and @ tcion +.desc +These variables hold integer values suitable as the +.meta action +argument of the +.code tcflow +function. They correspond to the C language preprocessor symbols +.codn TCOOFF , +.codn TCOON , +.code TCIOFF +and +.codn TCION . + +.coNP Variables @, tciflush @ tcoflush and @ tcioflush +.desc +These variables hold integer values suitable as the +.meta queue +argument of the +.code tcflush +function. They correspond to the C language preprocessor symbols +.codn TCIFLUSH , +.code TCOFLUSH +and +.codn TCIOFLUSH . + +.coNP Variables @, tcsanow @ tcsadrain and @ tcsaflush +.desc +These variables hold integer values suitable as the +.meta actions +argument of the +.code tcsetattr +function. They correspond to the C language preprocessor symbols +.codn TCSANOW , +.code TCSADRAIN +and +.codn TCSAFLUSH . + +.coNP Functions @ tcgetattr and @ tcsetattr +.synb +.mets (tcgetattr <> [ device ]) +.mets (tcsetattr < termios < actions <> [ device ]) +.syne +.desc +The +.code tcgetattr +and +.code tcsetattr +functions, respectively, retrieve and install the configuration +of the terminal driver associated with the specified device. + +These functions are wrappers for the like-named POSIX C library functions, +but with different argument conventions, and operating using +a \*(TL structure. + +The +.code tcgetattr +function, if successful, returns a new instance of the +.code termios +structure. + +The +.code tcsetattr +function requires an instance of a +.code termios +structure as an argument to its +.meta termios +parameter. + +A program may alter the settings of a terminal device by +retrieving them using +.codn tcgetattr , +manipulating the structure returned by this function, and +then using +.code tcsetattr +to install the modified structure into the device. + +The +.meta actions +argument of +.code tcsetattr +may be given as the value of one of the variables +.codn tcsanow , +.code tcsadrain +or +.codn tcsaflush . +If it is omitted, the default is +.codn tcsadrain . + +If an argument is given for +.meta device +it must be either a stream, or an integer file descriptor. +In either case, it is expected to be associated with a +terminal (TTY) device. + +If the argument is omitted, it defaults to the stream currently +stored in the +.code *stdin* +stream special variable, expected to be associated with +a terminal device. + +.TP* Notes: + +The C +.code termios +structure usually does not have members for representing the input +and output speed. \*(TL does not use such members, in any case, even +if they are present. The speeds are encoded in the +.code cc_iflag +and +.code cc_lflag +bitmasks. When retrieving the settings, the +.code tcgetattr +function uses the POSIX functions +.code cfgetispeed +and +.code cfgetospeed +to retrieve the speed values from the C structure. These values +are installed as the +.code ispeed +and +.code ospeed +slots of the Lisp structure. A reverse conversion takes place +when setting are installed using +.codn tcsetattr : +the speed values are taken from the slots, and installed into +the C structure using +.code cfsetispeed +and +.code cfsetospeed +before the structure is passed to the C +.code tcsetattr +function. + +On Linux, TTY devices do not have a separate input and output speed. +The C +.code termios +structure stores only one speed which is taken as both the input +and output speed, with a special exception. The input speed may be +programmed as zero. In that case, it is independently represented. +speed may be programmed as zero. + +.coNP Function @ tcsendbreak +.synb +.mets (tcsendbreak >> [ duration <> [ device ]]) +.syne +.desc +The +.code tcsendbreak +function generates a break signal on serial devices. The +.meta duration +parameter specifies the length of the break signal in milliseconds. +If the argument is omitted, the value 500 is used. + +The +.meta device +parameter is exactly the same as that of the +.code tcsetattr +function. + +.coNP Function @ tcdrain +.synb +.mets (tcdrain <> [ device ]) +.syne +.desc +The +.code tcdrain +function waits until all queued output on a terminal +device has been transmitted. It is a direct wrapper +for the like-named POSIX C function. + +The +.meta device +parameter is exactly the same as that of the +.code tcsetattr +function. + +.coNP Function @ tcflush +.synb +.mets (tcflush < queue <> [ device ]) +.syne +.desc +The +.code tcflush +function discards either untransmitted output data, +or received and yet unread input data, depending on the +value of the +.meta queue +argument. It is a direct wrapper for the like-named +POSIX C function. + +The +.meta queue +argument should be the value of one of the variables +.codn tciflush , +.code tcoflush +and +.codn tcioflush , +which specify the flushing of input data, output +data or both. + +The +.meta device +parameter is exactly the same as that of the +.code tcsetattr +function. + +.coNP Function @ tcflow +.synb +.mets (tcflow < action <> [ device ]) +.syne +.desc +The +.code tcflow +function provides bi-directional flow control on the +specified terminal device. It is a direct wrapper +for the like-named POSIX C function. + +The +.meta action +argument should be the value of one of the +variables +.codn tcooff , +.codn tcoon , +.code tcioff +and +.codn tcion . + +The +.meta device +parameter is exactly the same as that of the +.code tcsetattr +function. + +.coNP Methods @, set-iflags @, set-oflags @, set-cflags @, set-lflags @, clear-iflags @, clear-oflags @ clear-cflags and @ clear-lflags +.synb +.mets << termios .(set-iflags << flags *) +.mets << termios .(set-oflags << flags *) +.mets << termios .(set-cflags << flags *) +.mets << termios .(set-lflags << flags *) +.mets << termios .(clear-iflags << flags *) +.mets << termios .(clear-oflags << flags *) +.mets << termios .(clear-cflags << flags *) +.mets << termios .(clear-lflags << flags *) +.syne +.desc +These methods of the +.code termios +structure set or clear multiple flags of the four bitmask flag fields. + +The +.meta flags +arguments specify zero or more integer values. These values +are combined together bitwise, as if by the +.code logior +function to form a single effective mask. +If there are no +.meta flags +arguments, then the effective mask is zero. + +The +.code set-iflags +method sets, in the +.code iflag +member of the +.meta termios +structure, all of the bits which +are set in the effective mask. That is to say, +the effective mask is combined with the value in +.code iflag +by a +.code logior +operation, and the result is stored back into +.codn iflag . +Similarly, the +.codn set-oflags , +.code set-cflags +and +.code set-lflags +methods operate on the +.codn oflag , +.code cflag +and +.code lflag +slots of the structure. + +The +.code clear-iflags +method clears, in the +.code iflag +member of the +.meta termios +structure, all of the bits which are +set in the effective mask. That is to say, +the effective mask is bitwise inverted as if +by the +.code lognot +function, and then combined with the +existing value of the +.code iflag +slot using +.codn logand . +The resulting value is stored back into the +.code iflag +slot. +Similarly, the +.codn clear-oflags , +.code clear-cflags +and +.code clear-lflags +methods operate on the +.codn oflag , +.code cflag +and +.code lflag +slots of the structure. + +Note: the methods +.codn go-raw , +.code go-cbreak +and +.code go-canon +are provided for changing the settings to raw, "cbreak" and canonical mode. +These methods should be preferred to directly manipulating the flag and +.code cc +slots. + +.TP* Example + +In this example, +.code tio +is assumed to be a variable holding an instance of a +.code termios +struct: + +.cblk + ;; clear the ignbrk, brkint, and various other flags: + tio.(clear-iflags ignbrk brkint parmrk istrip inlcr igncr icrnl ixon) + + ;; set the csize and parenb flags: + tio.(set-cflags csize parenb) +.cble + +.coNP Methods @ go-raw and @ go-cbreak +.synb +.mets << termios .(go-raw) +.mets << termios .(go-cbreak) +.syne +.desc +The +.code go-raw +and +.code go-cbreak +methods of the +.code termios +structure manipulate the flag slots, as well as certain elements +of the +.code cc +slot, in order to prepare the terminal settings for, respectively, +"raw" and "cbreak" mode, described below. + +Note that manipulating the +.code termios +structure doesn't actually put these settings into effect in +the terminal device; the settings represented by the structure must +be installed into the device using +.codn tcsetattr . +There is no way to reverse the effect of these methods. +To precisely restore the previous terminal settings, the program +should retain a copy of the original +.code termios +structure. + +"Raw" mode refers to a configuration of the terminal device driver in which input +and output is passed transparently and without accumulation, conversion or +interpretation. Input isn't buffered into lines; as soon as a single byte is +received, it is available to the program. No special input characters such as +commands for generating an interrupt or process suspend request are processed +by the terminal driver; all characters are treated as input data. Input isn't +echoed; the only output which takes place is that generated by program +output requests to the device. + +"Cbreak" mode is named after a concept and function in the "curses" terminal +control library. It refers to a configuration of the terminal device driver +which is less transparent than "raw" mode. Input isn't buffered into lines, +and line editing commands are ordinary input characters, allowing +character-at-a-time input. However, most input translations are preserved, +except that the conversion of CR characters to NL is disabled. The +signal-generating characters are processed in this mode. This latter feature of +the configuration is the likely inspiration for the word "cbreak". Unless +otherwise configured, the interrupt character corresponds to the Ctrl-C key, +and "break" is another term for an interactive interruption. + +.coNP Methods @ string-encode and @ string-decode +.synb +.mets << termios .(string-encode) +.mets << termios .(string-decode << string ) +.syne +.desc +The +.code string-encode +method converts the terminal state stored in a +.code termios +structure into a textual format, returning that representation +as a character string. + +The +.code string-decode +method parses the character representation produced by +.code string-encode +and populates the +.meta termios +structure with the settings are encoded in that string. + +If a string is passed to +.code string-decode +which wasn't produced by +.codn string-encode , +the behavior is unspecified. An exception may or may not be +thrown, and the contents of +.meta termios +may or may not be affected. + +Note: the textual representation produced by +.code string-encode +is intended to be identical to that produced by the +.code -g +option of the GNU Coreutils version of the +.code stty +utility, on the same platform. That is to say, the output of +.code "stty -g" +may be used as input into +.codn string-decode , +and the output of +.code string-encode +may be used as an argument to +.codn stty . + .SS* Web Programming Support .coNP Functions @ url-encode and @ url-decode |