2 * tio - a simple TTY terminal I/O application
4 * Copyright (c) 2014-2016 Martin Lund
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
27 #include <sys/types.h>
29 #include <sys/param.h>
37 #include "tio/print.h"
38 #include "tio/options.h"
41 #include "tio/error.h"
43 static struct termios new_stdout, old_stdout, old_tio;
44 static long rx_total = 0, tx_total = 0;
45 static bool connected = false;
46 static bool tainted = false;
49 #define tio_printf(format, args...) \
51 if (tainted) putchar('\n'); \
52 color_printf("[tio %s] " format, current_time(), ## args); \
56 void handle_command_sequence(char input_char, char previous_char, char *output_char, bool *forward)
61 /* Ignore unused arguments */
62 if (output_char == NULL)
63 output_char = &unused_char;
66 forward = &unused_bool;
68 /* Handle escape key commands */
69 if (previous_char == KEY_CTRL_T)
74 tio_printf("Key commands:");
75 tio_printf(" ctrl-t ? List available key commands");
76 tio_printf(" ctrl-t i Show settings information");
77 tio_printf(" ctrl-t q Quit");
78 tio_printf(" ctrl-t s Show statistics");
79 tio_printf(" ctrl-t t Send ctrl-t key code");
83 tio_printf("Settings information:");
84 tio_printf(" TTY device: %s", option.tty_device);
85 tio_printf(" Baudrate: %d", option.baudrate);
86 tio_printf(" Databits: %d", option.databits);
87 tio_printf(" Flow: %s", option.flow);
88 tio_printf(" Stopbits: %d", option.stopbits);
89 tio_printf(" Parity: %s", option.parity);
90 tio_printf(" Output delay: %d", option.output_delay);
92 tio_printf(" Log file: %s", option.log_filename);
96 /* Exit upon ctrl-t q sequence */
99 /* Send ctrl-t key code upon ctrl-t t sequence */
100 *output_char = KEY_CTRL_T;
103 /* Show tx/rx statistics upon ctrl-t s sequence */
104 tio_printf("Statistics:");
105 tio_printf(" Sent %ld bytes, received %ld bytes", tx_total, rx_total);
109 /* Ignore unknown ctrl-t escaped keys */
116 void wait_for_tty_device(void)
121 static char input_char, previous_char = 0;
122 static bool first = true;
124 /* Loop until device pops up */
129 /* Don't wait first time */
135 /* Wait up to 1 second */
141 FD_SET(STDIN_FILENO, &rdfs);
143 /* Block until input becomes available or timeout */
144 status = select(STDIN_FILENO + 1, &rdfs, NULL, NULL, &tv);
147 /* Input from stdin ready */
149 /* Read one character */
150 status = read(STDIN_FILENO, &input_char, 1);
153 error_printf("Could not read from stdin");
157 /* Handle commands */
158 handle_command_sequence(input_char, previous_char, NULL, NULL);
160 previous_char = input_char;
162 } else if (status == -1)
164 error_printf("select() failed (%s)", strerror(errno));
168 /* Test for accessible device file */
169 if (access(option.tty_device, R_OK) == 0)
174 void configure_stdout(void)
176 /* Save current stdout settings */
177 if (tcgetattr(STDOUT_FILENO, &old_stdout) < 0)
179 error_printf("Saving current stdio settings failed");
183 /* Prepare new stdout settings */
184 bzero(&new_stdout, sizeof(new_stdout));
186 /* Control, input, output, local modes for stdout */
187 new_stdout.c_cflag = 0;
188 new_stdout.c_iflag = 0;
189 new_stdout.c_oflag = 0;
190 new_stdout.c_lflag = 0;
192 /* Control characters */
193 new_stdout.c_cc[VTIME] = 0; /* Inter-character timer unused */
194 new_stdout.c_cc[VMIN] = 1; /* Blocking read until 1 character received */
196 /* Activate new stdout settings */
197 tcsetattr(STDOUT_FILENO, TCSANOW, &new_stdout);
198 tcsetattr(STDOUT_FILENO, TCSAFLUSH, &new_stdout);
200 /* Print launch hints */
201 tio_printf("tio v%s", VERSION);
202 tio_printf("Press ctrl-t + q to quit");
204 /* Make sure we restore old stdout settings on exit */
205 atexit(&restore_stdout);
208 void restore_stdout(void)
210 tcsetattr(STDOUT_FILENO, TCSANOW, &old_stdout);
211 tcsetattr(STDOUT_FILENO, TCSAFLUSH, &old_stdout);
214 void disconnect_tty(void)
218 tio_printf("Disconnected");
225 void restore_tty(void)
227 tcsetattr(fd, TCSANOW, &old_tio);
228 tcsetattr(fd, TCSAFLUSH, &old_tio);
234 int connect_tty(void)
236 fd_set rdfs; /* Read file descriptor set */
237 int maxfd; /* Maximum file descriptor used */
238 char input_char, output_char;
239 static char previous_char = 0;
240 static bool first = true;
243 /* Open tty device */
244 fd = open(option.tty_device, O_RDWR | O_NOCTTY );
247 error_printf_silent("Could not open tty device (%s)", strerror(errno));
251 /* Make sure device is of tty type */
254 error_printf_silent("Not a tty device");
258 /* Lock device file */
259 status = flock(fd, LOCK_EX | LOCK_NB);
260 if ((status == -1) && (errno == EWOULDBLOCK))
262 error_printf("Device file is locked by another process");
266 /* Flush stale I/O data (if any) */
267 tcflush(fd, TCIOFLUSH);
269 /* Print connect status */
270 tio_printf("Connected");
274 /* Save current port settings */
275 if (tcgetattr(fd, &old_tio) < 0)
276 goto error_tcgetattr;
278 /* Make sure we restore tty settings on exit */
281 atexit(&restore_tty);
285 /* Control, input, output, local modes for tty device */
286 option.tio.c_cflag |= CLOCAL | CREAD;
287 option.tio.c_oflag = 0;
288 option.tio.c_lflag = 0;
290 /* Control characters */
291 option.tio.c_cc[VTIME] = 0; /* Inter-character timer unused */
292 option.tio.c_cc[VMIN] = 1; /* Blocking read until 1 character received */
294 /* Activate new port settings */
295 tcsetattr(fd, TCSANOW, &option.tio);
296 tcsetattr(fd, TCSAFLUSH, &option.tio);
298 maxfd = MAX(fd, STDIN_FILENO) + 1; /* Maximum bit entry (fd) to test */
305 FD_SET(STDIN_FILENO, &rdfs);
307 /* Block until input becomes available */
308 status = select(maxfd, &rdfs, NULL, NULL, NULL);
311 if (FD_ISSET(fd, &rdfs))
313 /* Input from tty device ready */
314 if (read(fd, &input_char, 1) > 0)
316 /* Update receive statistics */
319 /* Print received tty character to stdout */
325 log_write(input_char);
331 /* Error reading - device is likely unplugged */
332 error_printf_silent("Could not read from tty device");
336 if (FD_ISSET(STDIN_FILENO, &rdfs))
340 /* Input from stdin ready */
341 status = read(STDIN_FILENO, &input_char, 1);
344 error_printf_silent("Could not read from stdin");
348 /* Forward input to output except ctrl-t key */
349 output_char = input_char;
350 if (input_char == KEY_CTRL_T)
353 /* Handle commands */
354 handle_command_sequence(input_char, previous_char, &output_char, &forward);
358 /* Send output to tty device */
359 status = write(fd, &output_char, 1);
361 warning_printf("Could not write to tty device");
363 /* Update transmit statistics */
366 /* Insert output delay */
367 delay(option.output_delay);
370 /* Save previous key */
371 previous_char = input_char;
374 } else if (status == -1)
376 error_printf("Error: select() failed (%s)", strerror(errno));