]> git.sur5r.net Git - tio/commitdiff
New upstream version 1.42 upstream/1.42
authorJakob Haufe <sur5r@debian.org>
Tue, 5 Jul 2022 14:32:01 +0000 (16:32 +0200)
committerJakob Haufe <sur5r@debian.org>
Tue, 5 Jul 2022 14:32:01 +0000 (16:32 +0200)
17 files changed:
AUTHORS
ChangeLog
README.md
TODO
man/tio.1.in
meson.build
src/bash-completion/tio.in
src/configfile.c
src/log.c
src/log.h
src/misc.h
src/options.c
src/print.c
src/print.h
src/socket.c
src/tty.c
src/tty.h

diff --git a/AUTHORS b/AUTHORS
index 5795305ae729d30f9e4e117c4cd6826289907f0e..2d546868dbe1f9bbddb43c5aea1649952563c33f 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -34,5 +34,6 @@ Yin Fengwei <fengwei.yin@intel.com>
 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.
index c0cad3cb5681e91a5745957dab54f4b6691c9d2a..46d9e6ae4825aab7054dc67e422d138cd3912f29 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,48 @@
-=== 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
 
 
 
index bf2b5eb12cfa4e7f72851aa5b218ac3b6ba191c4..b7d2455a58e984a23bd3cfa703864f534f040f17 100644 (file)
--- a/README.md
+++ b/README.md
@@ -16,22 +16,47 @@ devices for basic I/O operations.
 
 ### 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)
@@ -45,17 +70,17 @@ The command-line interface is straightforward as reflected in the output from
       -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.
 
@@ -71,7 +96,7 @@ established connection is lost.
 
 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.
@@ -94,7 +119,7 @@ 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:
@@ -139,13 +164,17 @@ $ tio usb12
 ```
 
 
-## 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:
 ```
@@ -156,7 +185,7 @@ Install bleeding edge:
     $ 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).
 
@@ -173,7 +202,7 @@ Note: Please do no try to install from source if you are not familiar with
 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.
@@ -188,22 +217,22 @@ consider making a donation of your choice:
 [![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>
 
diff --git a/TODO b/TODO
index 26c87706d6f98d39537a66c39d6c432844511fce..563ceea3fad636355f64fa921fcf27063598f518 100644 (file)
--- a/TODO
+++ b/TODO
@@ -1,4 +1,3 @@
 
  * Improve error/warning messaging when parsing configuration file
 
- * Add support for piping to tio
index fa73a527d4145bd5d2ea3e7192f5bfae3a19c175..304f3c33f152dc64820bc117de271b0014ba7bf4 100644 (file)
@@ -6,7 +6,7 @@ tio \- a simple serial device I/O tool
 .SH "SYNOPSIS"
 .PP
 .B tio
-.RI "[" <options> "] " "<tty-device|config>"
+.RI "[" <options> "] " "<tty-device|sub-config>"
 
 .SH "DESCRIPTION"
 .PP
@@ -87,7 +87,7 @@ List available serial devices.
 .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>
@@ -124,7 +124,7 @@ If defining more than one flag, the flags must be comma separated.
 .RE
 
 .TP
-.BR \-x ", " \-\-hex\-mode
+.BR \-x ", " \-\-hexadecimal
 
 Enable hexadecimal mode.
 
@@ -207,20 +207,29 @@ In hexadecimal mode each incoming byte is printed out as a hexadecimal value.
 .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:
@@ -229,7 +238,7 @@ 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"
@@ -245,7 +254,7 @@ Set output delay
 .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"
@@ -260,58 +269,85 @@ Set timestamp format
 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
@@ -319,16 +355,59 @@ Which corresponds to the commonly used options:
 
 $ 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
index 80f667bc17dc91f4bafbfbc1d7607fcc37f23b7d..f3d63f01fc4b88219a001ec3618dfc23289eca80 100644 (file)
@@ -1,12 +1,12 @@
 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')
index 842110bb91a876b048309e976b0a3978fd6e54dc..1710ecce6ee69365ec0a74ab8610ef1d22c99033 100644 (file)
@@ -27,7 +27,7 @@ _tio()
           -L --list-devices \
           -c --color \
           -S --socket \
-          -x --hex-mode \
+          -x --hexadecimal \
           -v --version \
           -h --help"
 
@@ -102,7 +102,7 @@ _tio()
             COMPREPLY=( $(compgen -W "unix: inet: inet6:" -- ${cur}) )
             return 0
             ;;
-        -x | --hex-mode)
+        -x | --hexadecimal)
             COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
             return 0
             ;;
index 41c95bcb6912fd09412b6b1ecf30fcbeae9f325c..352169d0e710111c992a287f78c4e09720f14b3b 100644 (file)
@@ -19,7 +19,9 @@
  * 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>
@@ -171,7 +173,7 @@ static int data_handler(void *user, const char *section, const char *name,
                 option.local_echo = false;
             }
         }
-        else if (!strcmp(name, "hex-mode"))
+        else if (!strcmp(name, "hexadecimal"))
         {
             if (!strcmp(value, "enable"))
             {
@@ -318,6 +320,11 @@ void config_file_parse(void)
     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
index 5366503a90796564f6a839689f69131d76c79f8b..b1577ec6604829e2f1f0edc604318eef1eef3998 100644 (file)
--- a/src/log.c
+++ b/src/log.c
  * 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>
@@ -37,6 +40,7 @@
 
 static FILE *fp;
 static bool log_error = false;
+static char file_buffer[BUFSIZ];
 
 static char *date_time(void)
 {
@@ -64,14 +68,16 @@ void log_open(const char *filename)
         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)
@@ -133,7 +139,21 @@ 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)
     {
index 0dec98bab4aa3ab6e366d1747636f3011eb6eada..d2a21881109be04120c0ad8d02bcf8bec1b9cc38 100644 (file)
--- a/src/log.h
+++ b/src/log.h
@@ -22,6 +22,7 @@
 #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);
index 6a544650bcbea2df2bbddeb9cb6d2149bc06b7a9..734cd9b07f86fd056f4170e58aaed026d5bbc32e 100644 (file)
@@ -20,6 +20,7 @@
  */
 
 #pragma once
+
 #define UNUSED(expr) do { (void)(expr); } while (0)
 
 char * current_time(void);
index bc05bc73c36f2c444643482dde90058a4909f079..85782c9cf469aa288a0a494ac8051958699fd297 100644 (file)
@@ -68,7 +68,9 @@ struct option_t option =
 
 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");
@@ -88,11 +90,11 @@ void print_help(char *argv[])
     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");
@@ -199,7 +201,7 @@ void options_parse(int argc, char *argv[])
             {"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                   }
index 7580c317c3ba8c5bdb41e2d661d663ced7399dc2..68e663c1f6c5547e50e7cadeae849880b7884baa 100644 (file)
@@ -30,14 +30,11 @@ char ansi_format[30];
 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()
index 28592fcecc4a2df5c03951ec9803dcb2824efbb5..53c7eea05d86ea3003ee9dfa1e65afa71c23f88a 100644 (file)
@@ -37,7 +37,6 @@ extern char ansi_format[];
     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...) \
@@ -55,7 +54,6 @@ extern char ansi_format[];
     fprintf (stdout, format, ## args); \
   else \
     fprintf (stdout, "%s" format ANSI_RESET, ansi_format, ## args); \
-  fflush(stdout); \
 }
 
 #define warning_printf(format, args...) \
@@ -66,7 +64,6 @@ extern char ansi_format[];
     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...) \
index 801ef3f3651dadcf3bf66ff55bd24d5cce7ed285..13c204b8f473b751603159af40a07e1ef209abfe 100644 (file)
  * 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
index e24da9c2126c6aff9ca26018d4e99653d0e7b64e..f1efa691cf475f520d3ecc00293df180af7f1752 100644 (file)
--- a/src/tty.c
+++ b/src/tty.c
@@ -78,15 +78,21 @@ static bool map_o_nl_crnl = false;
 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)
@@ -96,17 +102,85 @@ 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;
@@ -118,16 +192,15 @@ static void output_hex(char 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);
     }
 }
 
@@ -251,7 +324,6 @@ void handle_command_sequence(char input_char, char previous_char, char *output_c
             case KEY_L:
                 /* Clear screen using ANSI/VT100 escape code */
                 printf("\033c");
-                fflush(stdout);
                 break;
 
             case KEY_Q:
@@ -304,6 +376,11 @@ void handle_command_sequence(char input_char, char previous_char, char *output_c
     }
 }
 
+void stdin_restore(void)
+{
+    tcsetattr(STDIN_FILENO, TCSANOW, &stdin_old);
+}
+
 void stdin_configure(void)
 {
     int status;
@@ -319,9 +396,7 @@ void stdin_configure(void)
     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 */
@@ -339,9 +414,9 @@ void stdin_configure(void)
     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)
@@ -350,7 +425,7 @@ 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)
@@ -363,8 +438,7 @@ void stdout_configure(void)
     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 */
@@ -385,11 +459,6 @@ void stdout_configure(void)
     atexit(&stdout_restore);
 }
 
-void stdout_restore(void)
-{
-    tcsetattr(STDOUT_FILENO, TCSANOW, &stdout_old);
-}
-
 void tty_configure(void)
 {
     bool token_found = true;
@@ -514,7 +583,9 @@ void tty_configure(void)
         tio.c_cflag &= ~PARODD;
     }
     else if (strcmp("none", option.parity) == 0)
+    {
         tio.c_cflag &= ~PARENB;
+    }
     else
     {
         error_printf("Invalid parity");
@@ -535,26 +606,44 @@ void tty_configure(void)
     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);
@@ -562,7 +651,9 @@ void tty_configure(void)
             }
         }
         else
+        {
             token_found = false;
+        }
     }
     free(buffer);
 }
@@ -586,7 +677,8 @@ void tty_wait_for_device(void)
             tv.tv_sec = 0;
             tv.tv_usec = 1;
             first = false;
-        } else
+        }
+        else
         {
             /* Wait up to 1 second */
             tv.tv_sec = 1;
@@ -619,7 +711,8 @@ void tty_wait_for_device(void)
                 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);
@@ -627,7 +720,8 @@ void tty_wait_for_device(void)
 
         /* Test for accessible device file */
         status = access(option.tty_device, R_OK);
-        if (status == 0) {
+        if (status == 0)
+        {
             last_errno = 0;
             return;
         }
@@ -656,7 +750,60 @@ void tty_restore(void)
     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)
@@ -664,18 +811,15 @@ 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));
@@ -706,7 +850,9 @@ int tty_connect(void)
     print_tainted = false;
 
     if (option.timestamp)
+    {
         next_timestamp = true;
+    }
 
     /* Manage print output mode */
     if (option.hex_mode)
@@ -722,7 +868,9 @@ int tty_connect(void)
 
     /* Save current port settings */
     if (tcgetattr(fd, &tio_old) < 0)
+    {
         goto error_tcgetattr;
+    }
 
 #ifdef HAVE_IOSSIOSPEED
     if (!standard_baudrate)
@@ -787,12 +935,23 @@ int tty_connect(void)
             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();
@@ -801,14 +960,7 @@ int tty_connect(void)
                             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;
                         }
@@ -820,117 +972,96 @@ int tty_connect(void)
                         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);
index 081ba47004304f4595476e41f2e42b9a3b7934a4..339dce95f5d989d2bdf3bdd7418a6437beb0cc90 100644 (file)
--- a/src/tty.h
+++ b/src/tty.h
 #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;