file: Better support for trailing slashes in pathnames
[jimtcl.git] / jim-tty.c
blob0ccab7b079266a4537d7446a29abd4861967b4ef
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 NULL
85 enum {
86 OPT_BAUD,
87 OPT_DATA,
88 OPT_HANDSHAKE,
89 OPT_INPUT,
90 OPT_OUTPUT,
91 OPT_PARITY,
92 OPT_STOP,
93 OPT_VMIN,
94 OPT_VTIME
98 #define ARRAYSIZE(A) (sizeof(A)/sizeof(*(A)))
101 * Search the flag/name map for an entry with the given name.
102 * (Actually, just matching the first char)
103 * Returns a pointer to the entry if found, or NULL if not.
105 static const struct flag_name_map *flag_name_to_value(const struct flag_name_map *map, int len, const char *name)
107 int i;
109 for (i = 0; i < len; i++) {
110 /* Only need to compare the first character since all names are unique in the first char */
111 if (*name == *map[i].name) {
112 return &map[i];
115 return NULL;
119 * Search the flag/name map for an entry with the matching value.
120 * Returns the corresponding name if found, or NULL if no match.
122 static const char *flag_value_to_name(const struct flag_name_map *map, int len, unsigned value)
124 int i;
126 for (i = 0; i < len; i++) {
127 if (value == map[i].value) {
128 return map[i].name;
131 return NULL;
135 * If 'str2' is not NULL, appends 'str1' and 'str2' to the list.
136 * Otherwise does nothing.
138 static void JimListAddPair(Jim_Interp *interp, Jim_Obj *listObjPtr, const char *str1, const char *str2)
140 if (str2) {
141 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, str1, -1));
142 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, str2, -1));
146 Jim_Obj *Jim_GetTtySettings(Jim_Interp *interp, int fd)
148 struct termios tio;
149 size_t i;
150 const char *p;
151 Jim_Obj *listObjPtr;
152 speed_t speed;
153 int baud;
155 if (tcgetattr(fd, &tio) < 0) {
156 return NULL;
159 listObjPtr = Jim_NewListObj(interp, NULL, 0);
161 p = flag_value_to_name(parity_map, ARRAYSIZE(parity_map), tio.c_cflag & (PARENB | PARODD));
162 JimListAddPair(interp, listObjPtr, "parity", p);
163 p = flag_value_to_name(data_size_map, ARRAYSIZE(data_size_map), tio.c_cflag & CSIZE);
164 JimListAddPair(interp, listObjPtr, "data", p);
165 p = flag_value_to_name(stop_size_map, ARRAYSIZE(stop_size_map), tio.c_cflag & CSTOPB);
166 JimListAddPair(interp, listObjPtr, "stop", p);
167 if (tio.c_iflag & (IXON | IXOFF)) {
168 p = "xonxoff";
170 else if (tio.c_cflag & CRTSCTS) {
171 p = "rtscts";
173 else {
174 p = "none";
176 JimListAddPair(interp, listObjPtr, "handshake", p);
177 p = flag_value_to_name(input_map, ARRAYSIZE(input_map), tio.c_lflag & ICANON);
178 JimListAddPair(interp, listObjPtr, "input", p);
179 p = flag_value_to_name(output_map, ARRAYSIZE(output_map), tio.c_oflag & OPOST);
180 JimListAddPair(interp, listObjPtr, "output", p);
182 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, "vmin", -1));
183 Jim_ListAppendElement(interp, listObjPtr, Jim_NewIntObj(interp, tio.c_cc[VMIN]));
184 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, "vtime", -1));
185 Jim_ListAppendElement(interp, listObjPtr, Jim_NewIntObj(interp, tio.c_cc[VTIME]));
187 speed = cfgetispeed(&tio);
188 baud = 0;
189 for (i = 0; i < sizeof(baudtable) / sizeof(*baudtable); i++) {
190 if (baudtable[i].speed == speed) {
191 baud = baudtable[i].baud;
192 break;
195 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, "baud", -1));
196 Jim_ListAppendElement(interp, listObjPtr, Jim_NewIntObj(interp, baud));
198 return listObjPtr;
201 int Jim_SetTtySettings(Jim_Interp *interp, int fd, Jim_Obj *dictObjPtr)
203 int len = Jim_ListLength(interp, dictObjPtr);
204 int i;
206 struct termios tio;
208 if (tcgetattr(fd, &tio) < 0) {
209 return -1;
212 for (i = 0; i < len; i += 2) {
213 Jim_Obj *nameObj = Jim_ListGetIndex(interp, dictObjPtr, i);
214 Jim_Obj *valueObj = Jim_ListGetIndex(interp, dictObjPtr, i + 1);
215 int opt;
216 const struct flag_name_map *p;
217 long l;
218 size_t j;
220 if (Jim_GetEnum(interp, nameObj, tty_settings_names, &opt, "setting", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
221 return JIM_ERR;
224 switch (opt) {
225 case OPT_BAUD:
226 if (Jim_GetLong(interp, valueObj, &l) != JIM_OK) {
227 goto badvalue;
229 for (j = 0; j < ARRAYSIZE(baudtable); j++) {
230 if (baudtable[j].baud == l) {
231 break;
234 if (j == ARRAYSIZE(baudtable)) {
235 goto badvalue;
237 cfsetospeed(&tio, baudtable[j].speed);
238 cfsetispeed(&tio, baudtable[j].speed);
239 break;
241 case OPT_PARITY:
242 p = flag_name_to_value(parity_map, ARRAYSIZE(parity_map), Jim_String(valueObj));
243 if (p == NULL) {
244 badvalue:
245 Jim_SetResultFormatted(interp, "bad value for %#s: %#s", nameObj, valueObj);
246 return JIM_ERR;
248 tio.c_cflag &= ~(PARENB | PARODD);
249 tio.c_cflag |= p->value;
250 break;
252 case OPT_STOP:
253 p = flag_name_to_value(stop_size_map, ARRAYSIZE(stop_size_map), Jim_String(valueObj));
254 if (p == NULL) {
255 goto badvalue;
257 tio.c_cflag &= ~CSTOPB;
258 tio.c_cflag |= p->value;
259 break;
261 case OPT_DATA:
262 p = flag_name_to_value(data_size_map, ARRAYSIZE(data_size_map), Jim_String(valueObj));
263 if (p == NULL) {
264 goto badvalue;
266 tio.c_cflag &= ~CSIZE;
267 tio.c_cflag |= p->value;
268 break;
270 case OPT_HANDSHAKE:
271 tio.c_iflag &= ~(IXON | IXOFF);
272 tio.c_cflag &= ~(CRTSCTS);
273 if (Jim_CompareStringImmediate(interp, valueObj, "xonxoff")) {
274 tio.c_iflag |= (IXON | IXOFF);
276 else if (Jim_CompareStringImmediate(interp, valueObj, "rtscts")) {
277 tio.c_cflag |= CRTSCTS;
279 else if (!Jim_CompareStringImmediate(interp, valueObj, "none")) {
280 goto badvalue;
282 break;
284 case OPT_VMIN:
285 if (Jim_GetLong(interp, valueObj, &l) != JIM_OK) {
286 goto badvalue;
288 tio.c_cc[VMIN] = l;
289 break;
291 case OPT_VTIME:
292 if (Jim_GetLong(interp, valueObj, &l) != JIM_OK) {
293 goto badvalue;
295 tio.c_cc[VTIME] = l;
296 break;
298 case OPT_OUTPUT:
299 p = flag_name_to_value(output_map, ARRAYSIZE(output_map), Jim_String(valueObj));
300 if (p == NULL) {
301 goto badvalue;
303 tio.c_oflag &= ~OPOST;
304 tio.c_oflag |= p->value;
305 break;
307 case OPT_INPUT:
308 p = flag_name_to_value(input_map, ARRAYSIZE(input_map), Jim_String(valueObj));
309 if (p == NULL) {
310 goto badvalue;
312 if (p->value) {
313 tio.c_lflag |= (ECHO | ECHOE | ECHOK | ECHONL | ICANON | IEXTEN | ISIG | NOFLSH | TOSTOP);
314 tio.c_iflag |= ICRNL;
316 else {
317 tio.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL | ICANON | IEXTEN | ISIG | NOFLSH | TOSTOP);
318 tio.c_iflag &= ~ICRNL;
320 break;
325 if (tcsetattr(fd, TCSAFLUSH, &tio) < 0) {
326 return -1;
328 return 0;