tests: Add interactive mode tests
[jimtcl.git] / jim-tty.c
blob5d9cee7908ff043c506b56789f9ab3ed2734bed5
1 /*
2 * Support for tty settings using termios
4 * (c) 2016 Steve Bennett <steveb@workware.net.au>
6 */
8 /* termios support is required */
10 #include <jim-tty.h>
11 #include <termios.h>
13 static const struct {
14 unsigned baud;
15 speed_t speed;
16 } baudtable[] = {
17 { 0, B0 },
18 { 50, B50 },
19 { 75, B75 },
20 { 110, B110 },
21 { 134, B134 },
22 { 150, B150 },
23 { 200, B200 },
24 { 300, B300 },
25 { 600, B600 },
26 { 1200, B1200 },
27 { 1800, B1800 },
28 { 2400, B2400 },
29 { 4800, B4800 },
30 { 9600, B9600 },
31 { 19200, B19200 },
32 { 38400, B38400 },
33 { 57600, B57600 },
34 { 115200, B115200 },
35 #ifdef B230400
36 { 230400, B230400 },
37 #endif
38 #ifdef B460800
39 { 460800, B460800 }
40 #endif
43 struct flag_name_map {
44 const char *name;
45 unsigned value;
48 static const struct flag_name_map parity_map[] = {
49 { "none", 0 },
50 { "even", PARENB },
51 { "odd", PARENB | PARODD },
53 static const struct flag_name_map data_size_map[] = {
54 { "5", CS5 },
55 { "6", CS6 },
56 { "7", CS7 },
57 { "8", CS8 },
59 static const struct flag_name_map stop_size_map[] = {
60 { "1", 0 },
61 { "2", CSTOPB },
63 static const struct flag_name_map input_map[] = {
64 { "raw", 0 },
65 { "cooked", ICANON },
67 static const struct flag_name_map output_map[] = {
68 { "raw", 0 },
69 { "cooked", OPOST },
72 static const char * const tty_settings_names[] = {
73 "baud",
74 "data",
75 "handshake",
76 "input",
77 "output",
78 "parity",
79 "stop",
80 "vmin",
81 "vtime",
82 "echo",
83 NULL
86 enum {
87 OPT_BAUD,
88 OPT_DATA,
89 OPT_HANDSHAKE,
90 OPT_INPUT,
91 OPT_OUTPUT,
92 OPT_PARITY,
93 OPT_STOP,
94 OPT_VMIN,
95 OPT_VTIME,
96 OPT_ECHO
100 #define ARRAYSIZE(A) (sizeof(A)/sizeof(*(A)))
103 * Search the flag/name map for an entry with the given name.
104 * (Actually, just matching the first char)
105 * Returns a pointer to the entry if found, or NULL if not.
107 static const struct flag_name_map *flag_name_to_value(const struct flag_name_map *map, int len, const char *name)
109 int i;
111 for (i = 0; i < len; i++) {
112 /* Only need to compare the first character since all names are unique in the first char */
113 if (*name == *map[i].name) {
114 return &map[i];
117 return NULL;
121 * Search the flag/name map for an entry with the matching value.
122 * Returns the corresponding name if found, or NULL if no match.
124 static const char *flag_value_to_name(const struct flag_name_map *map, int len, unsigned value)
126 int i;
128 for (i = 0; i < len; i++) {
129 if (value == map[i].value) {
130 return map[i].name;
133 return NULL;
137 * If 'str2' is not NULL, appends 'str1' and 'str2' to the list.
138 * Otherwise does nothing.
140 static void JimListAddPair(Jim_Interp *interp, Jim_Obj *listObjPtr, const char *str1, const char *str2)
142 if (str2) {
143 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, str1, -1));
144 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, str2, -1));
148 Jim_Obj *Jim_GetTtySettings(Jim_Interp *interp, int fd)
150 struct termios tio;
151 size_t i;
152 const char *p;
153 Jim_Obj *listObjPtr;
154 speed_t speed;
155 int baud;
157 if (tcgetattr(fd, &tio) < 0) {
158 return NULL;
161 listObjPtr = Jim_NewListObj(interp, NULL, 0);
163 p = flag_value_to_name(parity_map, ARRAYSIZE(parity_map), tio.c_cflag & (PARENB | PARODD));
164 JimListAddPair(interp, listObjPtr, "parity", p);
165 p = flag_value_to_name(data_size_map, ARRAYSIZE(data_size_map), tio.c_cflag & CSIZE);
166 JimListAddPair(interp, listObjPtr, "data", p);
167 p = flag_value_to_name(stop_size_map, ARRAYSIZE(stop_size_map), tio.c_cflag & CSTOPB);
168 JimListAddPair(interp, listObjPtr, "stop", p);
169 if (tio.c_iflag & (IXON | IXOFF)) {
170 p = "xonxoff";
172 else if (tio.c_cflag & CRTSCTS) {
173 p = "rtscts";
175 else {
176 p = "none";
178 JimListAddPair(interp, listObjPtr, "handshake", p);
179 p = flag_value_to_name(input_map, ARRAYSIZE(input_map), tio.c_lflag & ICANON);
180 JimListAddPair(interp, listObjPtr, "input", p);
181 p = flag_value_to_name(output_map, ARRAYSIZE(output_map), tio.c_oflag & OPOST);
182 JimListAddPair(interp, listObjPtr, "output", p);
184 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, "vmin", -1));
185 Jim_ListAppendElement(interp, listObjPtr, Jim_NewIntObj(interp, tio.c_cc[VMIN]));
186 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, "vtime", -1));
187 Jim_ListAppendElement(interp, listObjPtr, Jim_NewIntObj(interp, tio.c_cc[VTIME]));
189 speed = cfgetispeed(&tio);
190 baud = 0;
191 for (i = 0; i < sizeof(baudtable) / sizeof(*baudtable); i++) {
192 if (baudtable[i].speed == speed) {
193 baud = baudtable[i].baud;
194 break;
197 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, "baud", -1));
198 Jim_ListAppendElement(interp, listObjPtr, Jim_NewIntObj(interp, baud));
200 return listObjPtr;
203 int Jim_SetTtySettings(Jim_Interp *interp, int fd, Jim_Obj *dictObjPtr)
205 int len = Jim_ListLength(interp, dictObjPtr);
206 int i;
208 struct termios tio;
210 if (tcgetattr(fd, &tio) < 0) {
211 return -1;
214 for (i = 0; i < len; i += 2) {
215 Jim_Obj *nameObj = Jim_ListGetIndex(interp, dictObjPtr, i);
216 Jim_Obj *valueObj = Jim_ListGetIndex(interp, dictObjPtr, i + 1);
217 int opt;
218 const struct flag_name_map *p;
219 long l;
220 size_t j;
222 if (Jim_GetEnum(interp, nameObj, tty_settings_names, &opt, "setting", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
223 return JIM_ERR;
226 switch (opt) {
227 case OPT_BAUD:
228 if (Jim_GetLong(interp, valueObj, &l) != JIM_OK) {
229 goto badvalue;
231 for (j = 0; j < ARRAYSIZE(baudtable); j++) {
232 if (baudtable[j].baud == l) {
233 break;
236 if (j == ARRAYSIZE(baudtable)) {
237 goto badvalue;
239 cfsetospeed(&tio, baudtable[j].speed);
240 cfsetispeed(&tio, baudtable[j].speed);
241 break;
243 case OPT_PARITY:
244 p = flag_name_to_value(parity_map, ARRAYSIZE(parity_map), Jim_String(valueObj));
245 if (p == NULL) {
246 badvalue:
247 Jim_SetResultFormatted(interp, "bad value for %#s: %#s", nameObj, valueObj);
248 return JIM_ERR;
250 tio.c_cflag &= ~(PARENB | PARODD);
251 tio.c_cflag |= p->value;
252 break;
254 case OPT_STOP:
255 p = flag_name_to_value(stop_size_map, ARRAYSIZE(stop_size_map), Jim_String(valueObj));
256 if (p == NULL) {
257 goto badvalue;
259 tio.c_cflag &= ~CSTOPB;
260 tio.c_cflag |= p->value;
261 break;
263 case OPT_DATA:
264 p = flag_name_to_value(data_size_map, ARRAYSIZE(data_size_map), Jim_String(valueObj));
265 if (p == NULL) {
266 goto badvalue;
268 tio.c_cflag &= ~CSIZE;
269 tio.c_cflag |= p->value;
270 break;
272 case OPT_HANDSHAKE:
273 tio.c_iflag &= ~(IXON | IXOFF);
274 tio.c_cflag &= ~(CRTSCTS);
275 if (Jim_CompareStringImmediate(interp, valueObj, "xonxoff")) {
276 tio.c_iflag |= (IXON | IXOFF);
278 else if (Jim_CompareStringImmediate(interp, valueObj, "rtscts")) {
279 tio.c_cflag |= CRTSCTS;
281 else if (!Jim_CompareStringImmediate(interp, valueObj, "none")) {
282 goto badvalue;
284 break;
286 case OPT_VMIN:
287 if (Jim_GetLong(interp, valueObj, &l) != JIM_OK) {
288 goto badvalue;
290 tio.c_cc[VMIN] = l;
291 break;
293 case OPT_VTIME:
294 if (Jim_GetLong(interp, valueObj, &l) != JIM_OK) {
295 goto badvalue;
297 tio.c_cc[VTIME] = l;
298 break;
300 case OPT_OUTPUT:
301 p = flag_name_to_value(output_map, ARRAYSIZE(output_map), Jim_String(valueObj));
302 if (p == NULL) {
303 goto badvalue;
305 tio.c_oflag &= ~OPOST;
306 tio.c_oflag |= p->value;
307 break;
309 case OPT_INPUT:
310 p = flag_name_to_value(input_map, ARRAYSIZE(input_map), Jim_String(valueObj));
311 if (p == NULL) {
312 goto badvalue;
314 if (p->value) {
315 tio.c_lflag |= (ECHO | ECHOE | ECHOK | ECHONL | ICANON | IEXTEN | ISIG | NOFLSH | TOSTOP);
316 tio.c_iflag |= ICRNL;
318 else {
319 tio.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL | ICANON | IEXTEN | ISIG | NOFLSH | TOSTOP);
320 tio.c_iflag &= ~ICRNL;
322 break;
324 case OPT_ECHO:
325 if (Jim_GetLong(interp, valueObj, &l) != JIM_OK) {
326 goto badvalue;
328 if (l) {
329 tio.c_lflag |= ECHO;
331 else {
332 tio.c_lflag &= ~ECHO;
334 break;
339 if (tcsetattr(fd, TCSAFLUSH, &tio) < 0) {
340 return -1;
342 return 0;