Liam Beguin <liambeguin@gmail.com>
Peter Collingbourne <pcc@google.com>
g0mb4 <gomba007@gmail.com>
+ZeroMemoryEx on GitHub
Thanks to everyone who has contributed to this project.
-=== tio v1.40 ===
+=== tio v1.42 ===
+
+
+
+Changes since tio v1.41:
+
+ * Update man page
+
+ZeroMemoryEx:
+
+ * Handle malloc failure
+
+Sylvain LAFRASSE:
+
+ * Add missing 'string.h' include.
+
+
+
+Changes since tio v1.40:
+
+ * Rename --hex-mode to --hexadecimal
+
+ * Enable buffered writing
+
+ Read block of bytes from input and process same block for output. This
+ will speed things up by reducing I/O overhead.
+
+ * Enable buffered reading
+
+ Read block of bytes from input and process byte by byte for output. This
+ will speed things up by reducing I/O overhead.
+
+ * Refactoring
+
+ * Cleanup stdout flushing
+
+ Flushing is not needed since we disabled buffering of stdout.
+
+ * Simplify stdout_configure() code
+
+ * Simplify stdin_configure() code
+
+ * Update man page
+
+ * Update README
### 1.1 Motivation
-To make a simpler serial device tool for talking with TTY devices with less
-focus on classic terminal/modem features and more focus on the needs of
+To make a simpler serial device tool for talking with serial TTY devices with
+less focus on classic terminal/modem features and more focus on the needs of
embedded developers and hackers.
tio was originally created to replace
[screen](https://www.gnu.org/software/screen) for connecting to serial devices
when used in combination with [tmux](https://tmux.github.io).
-## 2. Usage
-
-### 2.1 Command-line
+## 2. Features
+
+ * Easily connect to serial TTY devices
+ * Automatic connect
+ * Support for arbitrary baud rates
+ * List available serial devices
+ * Show RX/TX statistics
+ * Toggle serial lines
+ * Local echo support
+ * Remap special characters (nl, cr-nl, bs, etc.)
+ * Line timestamps
+ * Support for delayed output
+ * Hexadecimal mode
+ * Log to file
+ * Autogeneration of log filename
+ * Configuration file support
+ * Activate sub-configurations by name or pattern
+ * Redirect I/O to socket for scripting or TTY sharing
+ * Pipe input and/or output
+ * Bash completion
+ * Color support
+ * Man page documentation
+
+## 3. Usage
+
+### 3.1 Command-line
The command-line interface is straightforward as reflected in the output from
'tio --help':
```
- Usage: tio [<options>] <tty-device|config>
+ Usage: tio [<options>] <tty-device|sub-config>
+
+ Connect to tty-device directly or via sub-configuration.
Options:
-b, --baudrate <bps> Baud rate (default: 115200)
-t, --timestamp Enable line timestamp
--timestamp-format <format> Set timestamp format (default: 24hour)
-L, --list-devices List available serial devices
- -l, --log Enable logging to file
+ -l, --log Enable log to file
--log-file <filename> Set log filename
--log-strip Strip control characters and escape sequences
-m, --map <flags> Map special characters
-c, --color 0..255|none|list Colorize tio text (default: 15)
-S, --socket <socket> Redirect I/O to socket
- -x, --hex Enable hexadecimal mode
+ -x, --hexadecimal Enable hexadecimal mode
-v, --version Display version
-h, --help Display help
- Options may be set via configuration file.
+ Options and sub-configurations may be set via configuration file.
In session, press ctrl-t q to quit.
tio features full bash autocompletion.
-### 2.2 Key commands
+### 3.2 Key commands
Various in session key commands are supported. When tio is started, press
ctrl-t ? to list the available key commands.
[20:19:12.041] ctrl-t v Show version
```
-### 2.3 Configuration file
+### 3.3 Configuration file
Options can be set via the configuration file first found in any of the
following locations in the order listed:
```
-## 3. Installation
+## 4. Installation
+
+### 4.1 Installation using package manager
-### 3.1 Installation using package manager
Packages for various GNU/Linux distributions are available. Please consult your
package manager tool to find and install tio.
-### 3.2 Installation using snap
+If you would like to see tio included in your favorite distribution, please
+reach out to their package maintainers team.
+
+### 4.2 Installation using snap
Install latest stable version:
```
$ snap install tio --edge
```
-### 3.3 Installation from source
+### 4.3 Installation from source
The latest source releases can be found [here](https://github.com/tio/tio/releases).
how to build stuff using meson.
-## 4. Contributing
+## 5. Contributing
tio is open source. If you want to help out with the project please feel free
to join in.
[![Donate](images/paypal.png)](https://www.paypal.me/lundmar)
-## 5. Support
+## 6. Support
Submit bug reports via GitHub: https://github.com/tio/tio/issues
-## 6. Website
+## 7. Website
Visit [tio.github.io](https://tio.github.io)
-## 7. License
+## 8. License
tio is GPLv2+. See LICENSE file for more details.
-## 8. Authors
+## 9. Authors
Created by Martin Lund \<martin.lund@keep-it-simple.com>
* Improve error/warning messaging when parsing configuration file
- * Add support for piping to tio
.SH "SYNOPSIS"
.PP
.B tio
-.RI "[" <options> "] " "<tty-device|config>"
+.RI "[" <options> "] " "<tty-device|sub-config>"
.SH "DESCRIPTION"
.PP
.TP
.BR \-l ", " \-\-log
-Enable logging to file. If no filename is provided the filename will be automatically generated.
+Enable log to file. If no filename is provided the filename will be automatically generated.
.TP
.BR " \-\-log-file \fI<filename>
.RE
.TP
-.BR \-x ", " \-\-hex\-mode
+.BR \-x ", " \-\-hexadecimal
Enable hexadecimal mode.
.TP
Bytes can be sent in this mode by typing the \fBtwo-character hexadecimal\fR representation of the value, e.g.: to send \fI0xA\fR you must type \fI0a\fR or \fI0A\fR.
-.SH "CONFIGURATION"
+.SH "CONFIGURATION FILE"
.PP
.TP 16n
-Options can be set via a configuration file using the INI format. tio uses the configuration file first found in the following locations in the order listed: $XDG_CONFIG_HOME/tio/tiorc, $HOME/.config/tio/tiorc, $HOME/.tiorc
+Options can be set via configuration file using the INI format. tio uses the configuration file first found in the following locations in the order listed:
+.PP
+.I $XDG_CONFIG_HOME/tio/tiorc
+.PP
+.I $HOME/.config/tio/tiorc
+.PP
+.I $HOME/.tiorc
.TP
-Config sections can be used to group settings.
+Labels can be used to group settings into named sub-configurations which can be activated from the command-line when starting tio.
.TP
.TP
-tio will try to match the user input to a config section by name or by pattern to get the tty and other options.
+tio will try to match the user input to a sub-configuration by name or by pattern to get the tty and other options.
.TP
-Options without any config section name sets the default options.
+Options without any label change the default options.
+
+.TP
+Any options set via command-line will override options set in the configuraqtion file.
.TP
The following configuration file options are available:
.IP "\fBpattern"
Pattern matching user input. This pattern can be an extended regular expression with a single group.
.IP "\fBtty"
-tty device to open. If tty contains a "%s" it will be substituted with the first group match.
+tty device to open. If it contains a "%s" it is substituted with the first group match.
.IP "\fBbaudrate"
Set baud rate
.IP "\fBdatabits"
.IP "\fBno-autoconnect"
Disable automatic connect
.IP "\fBlog"
-Enable logging to file
+Enable log to file
.IP "\fBlog-file"
Set log filename
.IP "\fBlog-strip"
Map special characters on input or output
.IP "\fBcolor"
Colorize tio text using ANSI color code ranging from 0 to 255
-.IP "\fBhex-mode"
+.IP "\fBhexadecimal"
Enable hexadecimal mode
.IP "\fBsocket"
Set socket to redirect I/O to
-.SH "CONFIGURATION EXAMPLES"
+.SH "CONFIGURATION FILE EXAMPLES"
.TP
-A typical config section used as a short-hand would be defined as such:
+To change the default configuration simply set options like so:
+
+.RS
+.nf
+.eo
+# Defaults
+baudrate = 115200
+databits = 8
+parity = none
+stopbits = 1
+color = 10
+.ec
+.fi
+.RE
+
+.TP
+Named sub-configurations can be added via labels:
.RS
.nf
.eo
[ftdi]
-baudrate=115200
-tty=/dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0
-color=11
+baudrate = 115200
+tty = /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0
+color = 11
.ec
.fi
.RE
.TP
-With this config section defined in the configuration file the following commands would be equivalent:
+Activate the sub-configuration by name:
$ tio ftdi
+.TP
+Which is equivalent to:
+
$ tio -b 115200 -c 11 /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0
.TP
-A config section can also be matched by its pattern which supports regular expressions:
+A sub-configuration can also be activated by its pattern which supports regular expressions:
.RS
.nf
.eo
[usb device]
-pattern=usb([0-9]*)
-baudrate=115200
-tty=/dev/ttyUSB%s
+pattern = usb([0-9]*)
+baudrate = 115200
+tty = /dev/ttyUSB%s
.ec
.fi
.RE
.TP
-Making the following commands equivalent:
+Activate the sub-configuration by pattern match:
$ tio usb12
+.TP
+Which is equivalent to:
+
$ tio -b 115200 /dev/ttyUSB12
+.TP
+It is also possible to combine use of sub-configuration and command-line options. For example:
+
+$ tio -l -t usb12
+
.SH "EXAMPLES"
.TP
-Typical use is without options. For example:
+Typical use is without options:
$ tio /dev/ttyUSB0
.TP
$ tio \-b 115200 \-d 8 \-f none \-s 1 \-p none /dev/ttyUSB0
.TP
-It is recommended to connect serial tty devices by ID. For example:
+It is recommended to connect serial tty devices by ID:
$ tio /dev/serial/by\-id/usb\-FTDI_TTL232R-3V3_FTGQVXBL\-if00\-port0
.PP
Using serial devices by ID ensures that tio automatically reconnects to the
correct serial device if the device is disconnected and then reconnected.
.TP
-To listen and redirect serial device I/O to network socket on port 4444 do:
+Redirect serial device I/O to Unix file socket for scripting:
+
+$ tio -S unix:/tmp/tmux-socket0 /dev/ttyUSB0
+
+.TP
+Then, to issue a command via the file socket simply do:
+
+$ echo "ls -la" | nc -UN /tmp/tmux-socket0 > /dev/null
+
+.TP
+Or use the expect command to script an interaction:
+
+.RS
+.nf
+.eo
+#!/usr/bin/expect -f
+
+set timeout -1
+log_user 0
+
+spawn nc -UN /tmp/tio-socket0
+set uart $spawn_id
+
+send -i $uart "date\n"
+expect -i $uart "prompt> "
+send -i $uart "ls -la\n"
+expect -i $uart "prompt> "
+.ec
+.fi
+.RE
+
+.TP
+Redirect device I/O to network file socket for remote tty sharing:
+
+$ tio --socket inet:4444 /dev/ttyUSB0
+
+.TP
+
+Then, use netcat to connect to the shared tty session over network (assuming tio is hosted on IP 10.0.0.42):
+
+$ nc -N 10.0.0.42 4444
+
+.TP
+Pipe data from file to the serial device:
-$ tio -S inet:4444 /dev/ttyUSB0
+$ cat data.bin | tio /dev/serial/by\-id/usb\-FTDI_TTL232R-3V3_FTGQVXBL\-if00\-port0
.SH "WEBSITE"
.PP
project('tio', 'c',
- version : '1.40',
+ version : '1.42',
license : [ 'GPL-2'],
meson_version : '>= 0.53.2',
default_options : [ 'warning_level=2', 'buildtype=release', 'c_std=gnu99' ]
)
# The tag date of the project_version(), update when the version bumps.
-version_date = '2022-06-17'
+version_date = '2022-07-04'
# Test for dynamic baudrate configuration interface
compiler = meson.get_compiler('c')
-L --list-devices \
-c --color \
-S --socket \
- -x --hex-mode \
+ -x --hexadecimal \
-v --version \
-h --help"
COMPREPLY=( $(compgen -W "unix: inet: inet6:" -- ${cur}) )
return 0
;;
- -x | --hex-mode)
+ -x | --hexadecimal)
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
+
#define _GNU_SOURCE
+
#include "config.h"
#include <sys/types.h>
#include <sys/stat.h>
option.local_echo = false;
}
}
- else if (!strcmp(name, "hex-mode"))
+ else if (!strcmp(name, "hexadecimal"))
{
if (!strcmp(value, "enable"))
{
int ret;
c = malloc(sizeof(struct config_t));
+ if (!c)
+ {
+ fprintf(stderr, "Error: Insufficient memory allocation");
+ exit(EXIT_FAILURE);
+ }
memset(c, 0, sizeof(struct config_t));
// Find config file
* 02110-1301, USA.
*/
+#define __STDC_WANT_LIB_EXT2__ 1 // To access vasprintf
+
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
+#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <time.h>
static FILE *fp;
static bool log_error = false;
+static char file_buffer[BUFSIZ];
static char *date_time(void)
{
option.log_filename = automatic_filename;
}
+ // Open log file in append write mode
fp = fopen(filename, "a+");
-
if (fp == NULL)
{
log_error = true;
exit(EXIT_FAILURE);
}
- setvbuf(fp, NULL, _IONBF, 0);
+
+ // Enable full buffering
+ setvbuf(fp, file_buffer, _IOFBF, BUFSIZ);
}
bool log_strip(char c)
return strip;
}
-void log_write(char c)
+void log_printf(const char *format, ...)
+{
+ char *line;
+
+ va_list(args);
+ va_start(args, format);
+ vasprintf(&line, format, args);
+ va_end(args);
+
+ fwrite(line, strlen(line), 1, fp);
+
+ free(line);
+}
+
+void log_putc(char c)
{
if (fp != NULL)
{
#pragma once
void log_open(const char *filename);
-void log_write(char c);
+void log_printf(const char *format, ...);
+void log_putc(char c);
void log_close(void);
void log_exit(void);
*/
#pragma once
+
#define UNUSED(expr) do { (void)(expr); } while (0)
char * current_time(void);
void print_help(char *argv[])
{
- printf("Usage: %s [<options>] <tty-device|config>\n", argv[0]);
+ printf("Usage: %s [<options>] <tty-device|sub-config>\n", argv[0]);
+ printf("\n");
+ printf("Connect to tty-device directly or via sub-configuration.\n");
printf("\n");
printf("Options:\n");
printf(" -b, --baudrate <bps> Baud rate (default: 115200)\n");
printf(" -m, --map <flags> Map special characters\n");
printf(" -c, --color 0..255|none|list Colorize tio text (default: 15)\n");
printf(" -S, --socket <socket> Redirect I/O to socket\n");
- printf(" -x, --hex-mode Enable hexadecimal mode\n");
+ printf(" -x, --hexadecimal Enable hexadecimal mode\n");
printf(" -v, --version Display version\n");
printf(" -h, --help Display help\n");
printf("\n");
- printf("Options may be set via configuration file.\n");
+ printf("Options and sub-configurations may be set via configuration file.\n");
printf("\n");
printf("In session, press ctrl-t q to quit.\n");
printf("\n");
{"socket", required_argument, 0, 'S' },
{"map", required_argument, 0, 'm' },
{"color", required_argument, 0, 'c' },
- {"hex-mode", no_argument, 0, 'x' },
+ {"hexadecimal", no_argument, 0, 'x' },
{"version", no_argument, 0, 'v' },
{"help", no_argument, 0, 'h' },
{0, 0, 0, 0 }
void print_hex(char c)
{
printf("%02x ", (unsigned char) c);
-
- fflush(stdout);
}
void print_normal(char c)
{
putchar(c);
- fflush(stdout);
}
void print_init_ansi_formatting()
fprintf (stdout, "\r" format "\r\n", ## args); \
else \
fprintf (stdout, "\r%s" format ANSI_RESET "\r\n", ansi_format, ## args); \
- fflush(stdout); \
}
#define ansi_error_printf(format, args...) \
fprintf (stdout, format, ## args); \
else \
fprintf (stdout, "%s" format ANSI_RESET, ansi_format, ## args); \
- fflush(stdout); \
}
#define warning_printf(format, args...) \
fprintf (stdout, "\r[%s] Warning: " format "\r\n", current_time(), ## args); \
else \
ansi_printf("[%s] Warning: " format, current_time(), ## args); \
- fflush(stdout); \
}
#define tio_printf(format, args...) \
* 02110-1301, USA.
*/
-#include "socket.h"
-#include "options.h"
-#include "print.h"
-
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <unistd.h>
+#include <string.h>
+
+#include "socket.h"
+#include "options.h"
+#include "print.h"
#define MAX_SOCKET_CLIENTS 16
#define SOCKET_PORT_DEFAULT 3333
static bool map_o_del_bs = false;
static char hex_chars[2];
static unsigned char hex_char_index = 0;
+static char tty_buffer[BUFSIZ*2];
+static size_t tty_buffer_count = 0;
+static char *tty_buffer_write_ptr = tty_buffer;
static void optional_local_echo(char c)
{
if (!option.local_echo)
+ {
return;
+ }
print(c);
- fflush(stdout);
if (option.log)
- log_write(c);
+ {
+ log_putc(c);
+ }
}
inline static bool is_valid_hex(char c)
inline static unsigned char char_to_nibble(char c)
{
- if(c >= '0' && c <= '9'){
+ if(c >= '0' && c <= '9')
+ {
return c - '0';
- } else if(c >= 'a' && c <= 'f'){
+ }
+ else if (c >= 'a' && c <= 'f')
+ {
return c - 'a' + 10;
- } else if(c >= 'A' && c <= 'F'){
+ }
+ else if (c >= 'A' && c <= 'F')
+ {
return c - 'A' + 10;
- } else {
+ }
+ else
+ {
return 0;
}
}
+void tty_flush(int fd)
+{
+ ssize_t count;
+
+ do
+ {
+ count = write(fd, tty_buffer, tty_buffer_count);
+ if (count < 0)
+ {
+ // Error
+ debug_printf("Write error while flushing tty buffer (%s)", strerror(errno));
+ break;
+ }
+ tty_buffer_count -= count;
+ }
+ while (tty_buffer_count > 0);
+
+ // Reset
+ tty_buffer_write_ptr = tty_buffer;
+ tty_buffer_count = 0;
+}
+
+ssize_t tty_write(int fd, const void *buffer, size_t count)
+{
+ ssize_t bytes_written = 0;
+
+ if (option.output_delay)
+ {
+ // Write byte by byte with output delay
+ for (size_t i=0; i<count; i++)
+ {
+ ssize_t retval = write(fd, buffer, 1);
+ if (retval < 0)
+ {
+ // Error
+ debug_printf("Write error (%s)", strerror(errno));
+ break;
+ }
+ bytes_written += retval;
+ fsync(fd);
+ delay(option.output_delay);
+ }
+ }
+ else
+ {
+ // Flush tty buffer if too full
+ if ((tty_buffer_count + count) > BUFSIZ)
+ {
+ tty_flush(fd);
+ }
+
+ // Copy bytes to tty write buffer
+ memcpy(tty_buffer_write_ptr, buffer, count);
+ tty_buffer_write_ptr += count;
+ tty_buffer_count += count;
+ bytes_written = count;
+ }
+
+ return bytes_written;
+}
+
static void output_hex(char c)
{
hex_chars[hex_char_index++] = c;
optional_local_echo(hex_value);
- ssize_t status = write(fd, &hex_value, 1);
+ ssize_t status = tty_write(fd, &hex_value, 1);
if (status < 0)
{
warning_printf("Could not write to tty device");
- } else
+ }
+ else
{
tx_total++;
}
-
- fsync(fd);
}
}
case KEY_L:
/* Clear screen using ANSI/VT100 escape code */
printf("\033c");
- fflush(stdout);
break;
case KEY_Q:
}
}
+void stdin_restore(void)
+{
+ tcsetattr(STDIN_FILENO, TCSANOW, &stdin_old);
+}
+
void stdin_configure(void)
{
int status;
memcpy(&stdin_new, &stdin_old, sizeof(stdin_old));
/* Reconfigure stdin (RAW configuration) */
- stdin_new.c_iflag &= ~(ICRNL); // Do not translate CR -> NL on input
- stdin_new.c_oflag &= ~(OPOST);
- stdin_new.c_lflag &= ~(ECHO|ICANON|ISIG|ECHOE|ECHOK|ECHONL);
+ cfmakeraw(&stdin_new);
/* Control characters */
stdin_new.c_cc[VTIME] = 0; /* Inter-character timer unused */
atexit(&stdin_restore);
}
-void stdin_restore(void)
+void stdout_restore(void)
{
- tcsetattr(STDIN_FILENO, TCSANOW, &stdin_old);
+ tcsetattr(STDOUT_FILENO, TCSANOW, &stdout_old);
}
void stdout_configure(void)
/* Disable line buffering in stdout. This is necessary if we
* want things like local echo to work correctly. */
- setbuf(stdout, NULL);
+ setvbuf(stdout, NULL, _IONBF, 0);
/* Save current stdout settings */
if (tcgetattr(STDOUT_FILENO, &stdout_old) < 0)
memcpy(&stdout_new, &stdout_old, sizeof(stdout_old));
/* Reconfigure stdout (RAW configuration) */
- stdout_new.c_oflag &= ~(OPOST);
- stdout_new.c_lflag &= ~(ECHO|ICANON|ISIG|ECHOE|ECHOK|ECHONL);
+ cfmakeraw(&stdout_new);
/* Control characters */
stdout_new.c_cc[VTIME] = 0; /* Inter-character timer unused */
atexit(&stdout_restore);
}
-void stdout_restore(void)
-{
- tcsetattr(STDOUT_FILENO, TCSANOW, &stdout_old);
-}
-
void tty_configure(void)
{
bool token_found = true;
tio.c_cflag &= ~PARODD;
}
else if (strcmp("none", option.parity) == 0)
+ {
tio.c_cflag &= ~PARENB;
+ }
else
{
error_printf("Invalid parity");
while (token_found == true)
{
if (token == NULL)
+ {
token = strtok(buffer,",");
+ }
else
+ {
token = strtok(NULL, ",");
+ }
if (token != NULL)
{
if (strcmp(token,"INLCR") == 0)
+ {
tio.c_iflag |= INLCR;
+ }
else if (strcmp(token,"IGNCR") == 0)
+ {
tio.c_iflag |= IGNCR;
+ }
else if (strcmp(token,"ICRNL") == 0)
+ {
tio.c_iflag |= ICRNL;
+ }
else if (strcmp(token,"OCRNL") == 0)
+ {
map_o_cr_nl = true;
+ }
else if (strcmp(token,"ODELBS") == 0)
+ {
map_o_del_bs = true;
+ }
else if (strcmp(token,"INLCRNL") == 0)
+ {
map_i_nl_crnl = true;
+ }
else if (strcmp(token, "ONLCRNL") == 0)
+ {
map_o_nl_crnl = true;
+ }
else
{
printf("Error: Unknown mapping flag %s\n", token);
}
}
else
+ {
token_found = false;
+ }
}
free(buffer);
}
tv.tv_sec = 0;
tv.tv_usec = 1;
first = false;
- } else
+ }
+ else
{
/* Wait up to 1 second */
tv.tv_sec = 1;
previous_char = input_char;
}
socket_handle_input(&rdfs, NULL);
- } else if (status == -1)
+ }
+ else if (status == -1)
{
error_printf("select() failed (%s)", strerror(errno));
exit(EXIT_FAILURE);
/* Test for accessible device file */
status = access(option.tty_device, R_OK);
- if (status == 0) {
+ if (status == 0)
+ {
last_errno = 0;
return;
}
tcsetattr(fd, TCSANOW, &tio_old);
if (connected)
+ {
tty_disconnect();
+ }
+}
+
+void forward_to_tty(int fd, char output_char)
+{
+ int status;
+
+ /* Map output character */
+ if ((output_char == 127) && (map_o_del_bs))
+ {
+ output_char = '\b';
+ }
+ if ((output_char == '\r') && (map_o_cr_nl))
+ {
+ output_char = '\n';
+ }
+
+ /* Map newline character */
+ if ((output_char == '\n' || output_char == '\r') && (map_o_nl_crnl))
+ {
+ const char *crlf = "\r\n";
+
+ optional_local_echo(crlf[0]);
+ optional_local_echo(crlf[1]);
+ status = tty_write(fd, crlf, 2);
+ if (status < 0)
+ {
+ warning_printf("Could not write to tty device");
+ }
+
+ tx_total += 2;
+ }
+ else
+ {
+ if (print_mode == HEX)
+ {
+ output_hex(output_char);
+ }
+ else
+ {
+ /* Send output to tty device */
+ optional_local_echo(output_char);
+ status = tty_write(fd, &output_char, 1);
+ if (status < 0)
+ {
+ warning_printf("Could not write to tty device");
+ }
+
+ /* Update transmit statistics */
+ tx_total++;
+ }
+ }
}
int tty_connect(void)
fd_set rdfs; /* Read file descriptor set */
int maxfd; /* Maximum file descriptor used */
char input_char, output_char;
+ char input_buffer[BUFSIZ];
static char previous_char = 0;
static bool first = true;
int status;
- bool next_timestamp = false;
+ bool next_timestamp = false;
char* now = NULL;
/* Open tty device */
-#ifdef __APPLE__
- fd = open(option.tty_device, O_RDWR | O_NOCTTY | O_NONBLOCK );
-#else
- fd = open(option.tty_device, O_RDWR | O_NOCTTY);
-#endif
+ fd = open(option.tty_device, O_RDWR | O_NOCTTY | O_NONBLOCK);
if (fd < 0)
{
error_printf_silent("Could not open tty device (%s)", strerror(errno));
print_tainted = false;
if (option.timestamp)
+ {
next_timestamp = true;
+ }
/* Manage print output mode */
if (option.hex_mode)
/* Save current port settings */
if (tcgetattr(fd, &tio_old) < 0)
+ {
goto error_tcgetattr;
+ }
#ifdef HAVE_IOSSIOSPEED
if (!standard_baudrate)
if (FD_ISSET(fd, &rdfs))
{
/* Input from tty device ready */
- if (read(fd, &input_char, 1) > 0)
+ ssize_t bytes_read = read(fd, input_buffer, BUFSIZ);
+ if (bytes_read <= 0)
{
- /* Update receive statistics */
- rx_total++;
+ /* Error reading - device is likely unplugged */
+ error_printf_silent("Could not read from tty device");
+ goto error_read;
+ }
- /* Print timestamp on new line, if desired. */
+ /* Update receive statistics */
+ rx_total += bytes_read;
+
+ /* Process input byte by byte */
+ for (int i=0; i<bytes_read; i++)
+ {
+ input_char = input_buffer[i];
+
+ /* Print timestamp on new line if enabled */
if (next_timestamp && input_char != '\n' && input_char != '\r')
{
now = current_time();
ansi_printf_raw("[%s] ", now);
if (option.log)
{
- log_write('[');
- while (*now != '\0')
- {
- log_write(*now);
- ++now;
- }
- log_write(']');
- log_write(' ');
+ log_printf("[%s] ", now);
}
next_timestamp = false;
}
print('\r');
print('\n');
if (option.timestamp)
+ {
next_timestamp = true;
- } else
+ }
+ }
+ else
{
/* Print received tty character to stdout */
print(input_char);
}
- fflush(stdout);
/* Write to log */
if (option.log)
- log_write(input_char);
+ {
+ log_putc(input_char);
+ }
socket_write(input_char);
print_tainted = true;
if (input_char == '\n' && option.timestamp)
+ {
next_timestamp = true;
- } else
- {
- /* Error reading - device is likely unplugged */
- error_printf_silent("Could not read from tty device");
- goto error_read;
+ }
}
}
- if (FD_ISSET(STDIN_FILENO, &rdfs))
+ else if (FD_ISSET(STDIN_FILENO, &rdfs))
{
- forward = true;
-
/* Input from stdin ready */
- status = read(STDIN_FILENO, &input_char, 1);
- if (status <= 0)
+ ssize_t bytes_read = read(STDIN_FILENO, input_buffer, BUFSIZ);
+ if (bytes_read <= 0)
{
error_printf_silent("Could not read from stdin");
goto error_read;
}
- /* Forward input to output */
- output_char = input_char;
-
- if (interactive_mode)
+ /* Process input byte by byte */
+ for (int i=0; i<bytes_read; i++)
{
- /* Do not forward ctrl-t key */
- if (input_char == KEY_CTRL_T)
- forward = false;
+ input_char = input_buffer[i];
- /* Handle commands */
- handle_command_sequence(input_char, previous_char, &output_char, &forward);
+ /* Forward input to output */
+ output_char = input_char;
+ forward = true;
- /* Save previous key */
- previous_char = input_char;
+ if (interactive_mode)
+ {
+ /* Do not forward ctrl-t key */
+ if (input_char == KEY_CTRL_T)
+ {
+ forward = false;
+ }
+
+ /* Handle commands */
+ handle_command_sequence(input_char, previous_char, &output_char, &forward);
+
+ /* Save previous key */
+ previous_char = input_char;
+
+ if (print_mode == HEX)
+ {
+ if (!is_valid_hex(input_char))
+ {
+ warning_printf("Invalid hex character: '%d' (0x%02x)", input_char, input_char);
+ forward = false;
+ }
+ }
+ }
+
+ if (forward)
+ {
+ forward_to_tty(fd, output_char);
+ }
}
+
+ tty_flush(fd);
}
else
{
forward = socket_handle_input(&rdfs, &output_char);
- }
- if (forward)
- {
- if (print_mode == HEX)
+ if (forward)
{
- if (!is_valid_hex(input_char))
- {
- warning_printf("Invalid hex character: '%d' (0x%02x)", input_char, input_char);
- continue;
- }
+ forward_to_tty(fd, output_char);
}
- /* Map output character */
- if ((output_char == 127) && (map_o_del_bs))
- output_char = '\b';
- if ((output_char == '\r') && (map_o_cr_nl))
- output_char = '\n';
-
- /* Map newline character */
- if ((output_char == '\n' || output_char == '\r') && (map_o_nl_crnl)) {
- const char *crlf = "\r\n";
-
- optional_local_echo(crlf[0]);
- optional_local_echo(crlf[1]);
- status = write(fd, crlf, 2);
- if (status < 0)
- warning_printf("Could not write to tty device");
-
- tx_total += 2;
- delay(option.output_delay);
- } else
- {
- if (print_mode == HEX)
- {
- output_hex(output_char);
- }
- else
- {
- /* Send output to tty device */
- optional_local_echo(output_char);
- status = write(fd, &output_char, 1);
- if (status < 0)
- warning_printf("Could not write to tty device");
- fsync(fd);
-
- /* Update transmit statistics */
- tx_total++;
- }
-
- /* Insert output delay */
- delay(option.output_delay);
- }
+ tty_flush(fd);
}
- } else if (status == -1)
+ }
+ else if (status == -1)
{
error_printf("Error: select() failed (%s)", strerror(errno));
exit(EXIT_FAILURE);
#define NORMAL 0
#define HEX 1
+extern bool interactive_mode;
+
void stdout_configure(void);
-void stdout_restore(void);
void stdin_configure(void);
-void stdin_restore(void);
void tty_configure(void);
int tty_connect(void);
void tty_wait_for_device(void);
void list_serial_devices(void);
-
-extern bool interactive_mode;