]> git.sur5r.net Git - tio/commitdiff
New upstream version 1.40 upstream/1.40
authorJakob Haufe <jakob@haufe.it>
Sat, 18 Jun 2022 23:21:19 +0000 (01:21 +0200)
committerJakob Haufe <jakob@haufe.it>
Sat, 18 Jun 2022 23:21:19 +0000 (01:21 +0200)
20 files changed:
.github/FUNDING.yml [new file with mode: 0644]
AUTHORS
ChangeLog
README.md
TODO [new file with mode: 0644]
images/tio-demo.gif
man/tio.1.in
meson.build
src/bash-completion/tio.in
src/configfile.c
src/configfile.h
src/log.c
src/main.c
src/options.c
src/options.h
src/print.c
src/print.h
src/socket.c
src/tty.c
src/tty.h

diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644 (file)
index 0000000..2cfa94a
--- /dev/null
@@ -0,0 +1 @@
+custom: ["https://www.paypal.me/lundmar"]
diff --git a/AUTHORS b/AUTHORS
index 65f22e5369e053b768c517e1d569630e4f016003..5795305ae729d30f9e4e117c4cd6826289907f0e 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -33,5 +33,6 @@ attila-v <attila_v@index.hu>
 Yin Fengwei <fengwei.yin@intel.com>
 Liam Beguin <liambeguin@gmail.com>
 Peter Collingbourne <pcc@google.com>
+g0mb4 <gomba007@gmail.com>
 
 Thanks to everyone who has contributed to this project.
index 26b1f3e66ceb06f5d31605aaebb0ed58ec01dfda..c0cad3cb5681e91a5745957dab54f4b6691c9d2a 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,177 @@
-=== tio v1.38 ===
+=== tio v1.40 ===
+
+
+
+Changes since tio v1.39:
+
+ * Add config support for log-strip
+
+ * Add config support for hex-mode
+
+ * Rename --hex to --hex-mode
+
+ * Fix completion for -e, --local-echo
+
+ * Ignore newlines in hex output
+
+ * Fix newline in warning_printf()
+
+ * Fix ansi_printf_raw() in no color mode
+
+ * Enter non-interactive mode when piping to tio
+
+   Add support for a non interactive mode which allows other application to
+   pipe data to tio which then forwards the data to the connected serial
+   device.
+
+   Non ineractive means that tio does not react to interactive key commands
+   in the incoming stream. This allows users to pipe binary data directly
+   to the connected serial device.
+
+   Example use:
+
+   $ cat commands.txt | tio /dev/ttyUSB0
+
+ * Also strip backspace from log
+
+   To make log strip feature consistent so that we remove all unprintable
+   control characters and escape sequences.
+
+ * Socket code cleanup
+
+ * Cleanup man page
+
+ * Rename --log-filename to --log-file
+
+Yin Fengwei:
+
+ * Allow strip escape sequence characters from log file
+
+   The log without escape key stripped is like:
+
+    ^M[12:47:17] ACRN:\>
+    ^M[12:47:17] ACRN:\>lasdfjklsdjf
+    ^M
+    ^M[12:47:18] Error: Invalid command.
+    ^M[12:47:19] ACRN:\>
+    ^M[12:47:26] ACRN:\>
+    ^M[12:47:26] ACRN:\>sdafkljsdkaljfklsadjflksdjafjsda^H ^H^H...
+    ^M
+    ^M[12:47:31] Error: Invalid command.
+
+   After strip escape key, the log is like:
+
+    [12:49:18] ACRN:\>
+    [12:49:19] ACRN:\>
+    [12:49:19] ACRN:\>ls
+
+    [12:49:19] Error: Invalid command.
+    [12:49:19] ACRN:\>
+    [12:49:19] ACRN:\>dfaslhj
+
+    [12:49:24] Error: Invalid command.
+
+   Beside escape key, it also handle backspace key as well.
+
+
+
+Changes since tio v1.38:
+
+ * Improve key command response for local echo and timestamp
+
+ * Fix invalid hex character error message
+
+ * Make sure only matched config section is parsed
+
+ * Add support for "disable" keyword in config file
+
+ * Unify error message formating
+
+ * Cleanup list devices code
+
+ * Fix command-line tty-device|config parsing
+
+   Allow user to add options on both sides of the provided config argument.
+
+   For example:
+
+    $ tio -b 9600 am64-evm -e
+
+   Before, tio only allowed adding arguments after the config argument.
+
+   Implemented as simple as possible by introducing two stage option parsing.
+
+ * Update bash completion
+
+ * Add support for IPv4 and IPv6 network sockets
+
+   Add support for IPv4 and IPv6 network sockets via socket syntax
+   "inet:<port>" and "inet6:<port>" respectively.
+
+   For example, to listen and redirect serial device I/O to a host bound
+   IPv4 socket simply do:
+
+    $ tio /dev/ttyUSB0 --socket inet:4444
+
+   To connect do e.g.:
+
+    $ nc 127.0.0.1 4444
+
+   Likewise, for IPv6 do:
+
+    $ tio /dev/ttyUSB0 --socket inet6:4444
+
+   To connect do e.g.:
+
+    $ nc ::1 4444
+
+   If port is 0 or no port is provided default port 3333 is used.
+
+ * Fix tio deleting unix socket file
+
+   If tio has a unix file socket open, a second tio instance of tio may
+   delete the socket file. This change fixes so that it will not be deleted
+   and tio will instead error and complain about conflicting socket file.
+
+ * Rework color option
+
+   Rework the color option to support setting ANSI color code values
+   ranging from 0..255 or "none" for no color or "list" to print a list of
+   available ANSI colors codes.
+
+   Also, disables color when piping.
+
+ * Remove print of hex mode status at startup
+
+ * Remove newline option in hex mode
+
+ * Fix configfile memory leaks
+
+ * Remove command-line option inconsistencies
+
+   Optional arguments, as parsed by the getopt_long mechanism, are
+   inherently inconsistent with how you define required arguments.
+
+   To avoid confusion we decide to avoid this inconsistency by replacing
+   optional options with additional options with required argmuments.
+
+ * Replace '1' with 'enable' in config files
+
+ * Convert errors to warnings
+
+g0mb4:
+
+ * Extended hexadecimal mode.
+
+   While in hex mode (ctrl-t h) you can output hexadecimal values.
+   E.g.: to send 0x0A you have to type 0A (always 2 characters).
+
+   Added option -x, --hex to start in hexadecimal mode.
+
+   Added option --newline-in-hex to interpret newline characters in hex mode.
+   This is disabled by default, because, in my opinion, hex stream is
+   fundamentally different from text, so a "new line" is meaningless in this
+   context.
 
 
 
index fdeacde451d5af26ad0ed71b294e579c8f17318b..bf2b5eb12cfa4e7f72851aa5b218ac3b6ba191c4 100644 (file)
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# tio - a simple serial terminal I/O tool
+# tio - a simple serial device I/O tool
 
 [![CircleCI](https://circleci.com/gh/tio/tio/tree/master.svg?style=shield)](https://circleci.com/gh/tio/tio/tree/master)
 [![tio](https://snapcraft.io/tio/badge.svg)](https://snapcraft.io/tio)
@@ -6,9 +6,9 @@
 
 ## 1. Introduction
 
-tio is a simple serial terminal tool which features a straightforward
-command-line interface to easily connect to TTY devices for basic I/O
-operations.
+tio is a simple serial device tool which features a straightforward
+command-line and configuration file interface to easily connect to serial TTY
+devices for basic I/O operations.
 
 <p align="center">
 <img src="images/tio-demo.gif">
@@ -16,34 +16,44 @@ operations.
 
 ### 1.1 Motivation
 
-To make a simpler serial terminal tool for talking with TTY devices with less
+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
 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
+
 The command-line interface is straightforward as reflected in the output from
 'tio --help':
 ```
     Usage: tio [<options>] <tty-device|config>
 
     Options:
-      -b, --baudrate <bps>        Baud rate (default: 115200)
-      -d, --databits 5|6|7|8      Data bits (default: 8)
-      -f, --flow hard|soft|none   Flow control (default: none)
-      -s, --stopbits 1|2          Stop bits (default: 1)
-      -p, --parity odd|even|none  Parity (default: none)
-      -o, --output-delay <ms>     Output delay (default: 0)
-      -n, --no-autoconnect        Disable automatic connect
-      -e, --local-echo            Enable local echo
-      -t, --timestamp[=<format>]  Enable timestamp (default: 24hour)
-      -L, --list-devices          List available serial devices
-      -l, --log <filename>        Log to file
-      -m, --map <flags>           Map special characters
-      -c, --color <0..255>        Colorize tio text
-      -S, --socket <socket>       Listen on socket
-      -v, --version               Display version
-      -h, --help                  Display help
+      -b, --baudrate <bps>             Baud rate (default: 115200)
+      -d, --databits 5|6|7|8           Data bits (default: 8)
+      -f, --flow hard|soft|none        Flow control (default: none)
+      -s, --stopbits 1|2               Stop bits (default: 1)
+      -p, --parity odd|even|none       Parity (default: none)
+      -o, --output-delay <ms>          Character output delay (default: 0)
+      -n, --no-autoconnect             Disable automatic connect
+      -e, --local-echo                 Enable local echo
+      -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
+          --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
+      -v, --version                    Display version
+      -h, --help                       Display help
 
     Options may be set via configuration file.
 
@@ -52,28 +62,88 @@ The command-line interface is straightforward as reflected in the output from
     See the man page for more details.
 ```
 
-The only option which requires a bit of elaboration is perhaps the
-`--no-autoconnect` option.
+By default tio automatically connects to the provided TTY device if present.
+If the device is not present, it will wait for it to appear and then connect.
+If the connection is lost (eg. device is unplugged), it will wait for the
+device to reappear and then reconnect. However, if the `--no-autoconnect`
+option is provided, tio will exit if the device is not present or an
+established connection is lost.
+
+tio features full bash autocompletion.
+
+### 2.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.040] Key commands:
+[20:19:12.040]  ctrl-t ?   List available key commands
+[20:19:12.040]  ctrl-t b   Send break
+[20:19:12.040]  ctrl-t c   Show configuration
+[20:19:12.040]  ctrl-t d   Toggle DTR line
+[20:19:12.040]  ctrl-t e   Toggle local echo mode
+[20:19:12.040]  ctrl-t h   Toggle hexadecimal mode
+[20:19:12.040]  ctrl-t l   Clear screen
+[20:19:12.040]  ctrl-t L   Show line states
+[20:19:12.040]  ctrl-t q   Quit
+[20:19:12.040]  ctrl-t r   Toggle RTS line
+[20:19:12.041]  ctrl-t s   Show statistics
+[20:19:12.041]  ctrl-t t   Send ctrl-t key code
+[20:19:12.041]  ctrl-t T   Toggle line timestamp mode
+[20:19:12.041]  ctrl-t v   Show version
+```
+
+### 2.3 Configuration file
+
+Options can be set via the configuration file first found in any of the
+following locations in the order listed:
+ - $XDG_CONFIG_HOME/tio/tiorc
+ - $HOME/.config/tio/tiorc
+ - $HOME/.tiorc
 
-By default tio automatically connects to the provided device if present.  If
-the device is not present, it will wait for it to appear and then connect. If
-the connection is lost (eg. device is unplugged), it will wait for the device
-to reappear and then reconnect. However, if the `--no-autoconnect` option is
-provided, tio will exit if the device is not present or an established
-connection is lost.
+The configuration file supports sub-configurations using named sections which can
+be activated via the command-line by name or pattern.
 
-Tio supports various in session key commands. Press ctrl-t ? to list the
-available key commands.
+Example configuration file:
 
-Tio also features full bash autocompletion support and configuration via ~/.tiorc.
+```
+# Defaults
+baudrate = 115200
+databits = 8
+parity = none
+stopbits = 1
+color = 10
+
+[ftdi]
+tty = /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0
+baudrate = 9600
+no-autoconnect = enable
+log = enable
+log-file = ftdi.log
+color = 12
+
+[usb devices]
+pattern = usb([0-9]*)
+tty = /dev/ttyUSB%s
+color = 13
+```
 
-See the tio man page for more details.
+To use a specific sub-configuration by name simply start tio like so:
+```
+$ tio ftdi
+```
+Or by pattern match:
+```
+$ tio usb12
+```
 
 
 ## 3. Installation
 
 ### 3.1 Installation using package manager
-tio comes prepackaged for various GNU/Linux distributions. Please consult your package manager tool to find and install tio.
+Packages for various GNU/Linux distributions are available. Please consult your
+package manager tool to find and install tio.
 
 ### 3.2 Installation using snap
 
@@ -130,7 +200,7 @@ Visit [tio.github.io](https://tio.github.io)
 
 ## 7. License
 
-Tio is GPLv2+. See LICENSE file for more details.
+tio is GPLv2+. See LICENSE file for more details.
 
 
 ## 8. Authors
diff --git a/TODO b/TODO
new file mode 100644 (file)
index 0000000..26c8770
--- /dev/null
+++ b/TODO
@@ -0,0 +1,4 @@
+
+ * Improve error/warning messaging when parsing configuration file
+
+ * Add support for piping to tio
index 2cde63ec6ad20c52251d018416b06a2289d92100..5106a281e32d6ffa0640272a7bf02e241f4f543b 100644 (file)
Binary files a/images/tio-demo.gif and b/images/tio-demo.gif differ
index f129dc6a470824bd873a6a18b04b8b7475f6064e..fa73a527d4145bd5d2ea3e7192f5bfae3a19c175 100644 (file)
@@ -1,7 +1,7 @@
 .TH "tio" "1" "@version_date@" "tio @version@" "User Commands"
 
 .SH "NAME"
-tio \- a simple serial terminal I/O tool
+tio \- a simple serial device I/O tool
 
 .SH "SYNOPSIS"
 .PP
@@ -11,8 +11,9 @@ tio \- a simple serial terminal I/O tool
 .SH "DESCRIPTION"
 .PP
 .B tio
-is a simple serial terminal tool which features a straightforward command-line
-interface to easily connect to serial TTY devices for basic I/O operations.
+is a simple serial device tool which features a straightforward command-line
+and configuration file interface to easily connect to serial TTY devices for
+basic I/O operations.
 
 .SH "OPTIONS"
 
@@ -57,9 +58,14 @@ option is provided, tio will exit if the device is not present or an established
 Enable local echo.
 
 .TP
-.BR \-t ", " \-\-timestamp[=\fI<format>\fR\fB]
+.BR \-t ", " \-\-timestamp
 
-Enable timestamp. Optionally you can specify any of the following timestamp formats:
+Enable line timestamp.
+
+.TP
+.BR "    \-\-timestamp-format \fI<format>
+
+Set timestamp format to any of the following timestamp formats:
 .RS
 .TP 16n
 .IP "\fB24hour"
@@ -79,9 +85,19 @@ Default format is
 List available serial devices.
 
 .TP
-.BR \-l ", " \-\-log[=\fI<filename>\fR\fB]
+.BR \-l ", " \-\-log
 
-Log to file. If no filename is provided the filename will be automatically generated.
+Enable logging to file. If no filename is provided the filename will be automatically generated.
+
+.TP
+.BR "    \-\-log-file \fI<filename>
+
+Set log filename.
+
+.TP
+.BR "    \-\-log-strip
+
+Strip control characters and escape sequences from log.
 
 .TP
 .BR \-m ", " "\-\-map " \fI<flags>
@@ -108,28 +124,43 @@ If defining more than one flag, the flags must be comma separated.
 .RE
 
 .TP
-.BR \-c ", " "\-\-color " \fI<code>
+.BR \-x ", " \-\-hex\-mode
+
+Enable hexadecimal mode.
+
+.TP
+.BR \-c ", " "\-\-color " \fI0..255|none|list
+
+Colorize tio text using ANSI color code value ranging from 0 to 255 or use "none" for no color.
 
-Colorize tio text using ANSI color code ranging from 0 to 255.
+Use "list" to print a list of available ANSI color codes.
 
-If color code is negative a list of available ANSI colors will be printed.
+Default value is 15.
 
 .TP
 .BR \-S ", " "\-\-socket \fI<socket>\fR\fB
 
-Listen on socket. Any input from clients connected to the socket is sent on the serial port as if entered at the terminal where tio is running (except that
+Redirect I/O to socket. Any input from clients connected to the socket is sent on the serial port as if entered at the terminal where tio is running (except that
 .B ctrl-t
 sequences are not recognized), and any input from the serial port is multiplexed to the terminal and all connected clients.
 
 Sockets remain open while the serial port is disconnected, and writes will block.
 
-Two socket types are supported using different prefixes in the socket field:
-
-unix:<filename> - Unix Domain Socket (file)
-
-inet:<IP>:<port> - Internet Socket (network) (NOT YET SUPPORTED)
+Various socket types are supported using the following prefixes in the socket field:
 
+.RS
+.TP 20n
+.IP "\fBunix:<filename>"
+Unix Domain Socket (file)
+.IP "\fBinet:<port>"
+Internet Socket (network)
+.IP "\fBinet6:<port>"
+Internet IPv6 Socket (network)
+.P
+If port is 0 or no port is provided default port 3333 is used.
+.P
 At present there is a hardcoded limit of 16 clients connected at one time.
+.RE
 
 .TP
 .BR \-v ", " \-\-version
@@ -170,6 +201,12 @@ Toggle RTS
 .IP "\fBctrl-t v"
 Show version
 
+.SH "HEXADECIMAL MODE"
+.TP
+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"
 .PP
 .TP 16n
@@ -188,8 +225,9 @@ Options without any config section name sets the default options.
 .TP
 The following configuration file options are available:
 
+.TP 20n
 .IP "\fBpattern"
-pattern matching user input. This pattern can be an extended regular expression with a single group.
+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.
 .IP "\fBbaudrate"
@@ -207,20 +245,25 @@ Set output delay
 .IP "\fBno-autoconnect"
 Disable automatic connect
 .IP "\fBlog"
-Log to file
+Enable logging to file
+.IP "\fBlog-file"
+Set log filename
+.IP "\fBlog-strip"
+Enable strip of control and escape sequences from log
 .IP "\fBlocal-echo"
 Enable local echo
 .IP "\fBtimestamp"
-Prefix each new line with a timestamp
-.IP "\fBlog-filename"
-Set log filename
+Enable line timestamp
+.IP "\fBtimestamp-format"
+Set timestamp format
 .IP "\fBmap"
 Map special characters on input or output
 .IP "\fBcolor"
-Colorize tio text using ANSI color code ranging from 0 to 255.
+Colorize tio text using ANSI color code ranging from 0 to 255
+.IP "\fBhex-mode"
+Enable hexadecimal mode
 .IP "\fBsocket"
-Set socket path (must include
-.BR unix: ).
+Set socket to redirect I/O to
 
 .SH "CONFIGURATION EXAMPLES"
 
@@ -241,9 +284,9 @@ color=11
 .TP
 With this config section defined in the configuration file the following commands would be equivalent:
 
-tio ftdi
+tio ftdi
 
-tio -b 115200 /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0
+$ 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:
@@ -262,26 +305,30 @@ tty=/dev/ttyUSB%s
 .TP
 Making the following commands equivalent:
 
-tio usb12
+tio usb12
 
-tio -b 115200 /dev/ttyUSB12
+tio -b 115200 /dev/ttyUSB12
 
 .SH "EXAMPLES"
 .TP
 Typical use is without options. For example:
 
-tio /dev/ttyUSB0
+tio /dev/ttyUSB0
 .TP
 Which corresponds to the commonly used options:
 
-tio \-b 115200 \-d 8 \-f none \-s 1 \-p none /dev/ttyUSB0
+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:
 
-tio /dev/serial/by\-id/usb\-FTDI_TTL232R-3V3_FTGQVXBL\-if00\-port0
+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:
+
+$ tio -S inet:4444 /dev/ttyUSB0
 
 .SH "WEBSITE"
 .PP
index 63900cf68271b68ffcf53ac998c2db2a38906bc7..80f667bc17dc91f4bafbfbc1d7607fcc37f23b7d 100644 (file)
@@ -1,12 +1,12 @@
 project('tio', 'c',
-    version : '1.38',
+    version : '1.40',
     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-02'
+version_date = '2022-06-17'
 
 # Test for dynamic baudrate configuration interface
 compiler = meson.get_compiler('c')
index 58da18da21bb176ba07aa0ccc6df89ab867029d3..842110bb91a876b048309e976b0a3978fd6e54dc 100644 (file)
@@ -17,12 +17,18 @@ _tio()
           -p --parity \
           -o --output-delay \
           -n --no-autoconnect \
+          -e --local-echo \
           -l --log \
+             --log-file \
+             --log-strip \
           -m --map \
-          -v --version \
           -t --timestamp \
+             --timestamp-format \
           -L --list-devices \
           -c --color \
+          -S --socket \
+          -x --hex-mode \
+          -v --version \
           -h --help"
 
     #  Complete the arguments to the options.
@@ -36,10 +42,6 @@ _tio()
             COMPREPLY=( $(compgen -W "5 6 7 8" -- ${cur}) )
             return 0
             ;;
-        -h | --local-echo)
-            COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
-            return 0
-            ;;
         -f | --flow)
             COMPREPLY=( $(compgen -W "hard soft none" -- ${cur}) )
             return 0
@@ -60,15 +62,31 @@ _tio()
             COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
             return 0
             ;;
+        -e | --local-echo)
+            COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
+            return 0
+            ;;
         -l | --log)
             COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
             return 0
             ;;
+        -l | --log-file)
+            COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
+            return 0
+            ;;
+        -l | --log-strip)
+            COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
+            return 0
+            ;;
         -m | --map)
             COMPREPLY=( $(compgen -W "ICRNL IGNCR INLCR INLCRNL OCRNL ODELBS ONLCRNL" -- ${cur}) )
             return 0
             ;;
         -t | --timestamp)
+            COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
+            return 0
+            ;;
+        --timestamp-format)
             COMPREPLY=( $(compgen -W "24hour 24hour-start iso8601" -- ${cur}) )
             return 0
             ;;
@@ -77,10 +95,14 @@ _tio()
             return 0
             ;;
         -c | --color)
-            COMPREPLY=( $(compgen -W "$(seq 0 255)" -- ${cur}) )
+            COMPREPLY=( $(compgen -W "$(seq 0 255) none list" -- ${cur}) )
             return 0
             ;;
         -S | --socket)
+            COMPREPLY=( $(compgen -W "unix: inet: inet6:" -- ${cur}) )
+            return 0
+            ;;
+        -x | --hex-mode)
             COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
             return 0
             ;;
index ac73c547f3942b19f27a86508f1494b116e0f3ad..41c95bcb6912fd09412b6b1ecf30fcbeae9f325c 100644 (file)
@@ -87,8 +87,8 @@ static int data_handler(void *user, const char *section, const char *name,
 {
     UNUSED(user);
 
-    // If section matches user input or unnamed section
-    if ((!strcmp(section, c->section_name)) || (!strcmp(section, "")))
+    // If section matches current section being parsed
+    if (!strcmp(section, c->section_name))
     {
         // Set configuration parameter if found
         if (!strcmp(name, "tty"))
@@ -124,24 +124,78 @@ static int data_handler(void *user, const char *section, const char *name,
         }
         else if (!strcmp(name, "no-autoconnect"))
         {
-            option.no_autoconnect = atoi(value);
+            if (!strcmp(value, "enable"))
+            {
+                option.no_autoconnect = true;
+            }
+            else if (!strcmp(value, "disable"))
+            {
+                option.no_autoconnect = false;
+            }
         }
         else if (!strcmp(name, "log"))
         {
-            option.log = atoi(value);
+            if (!strcmp(value, "enable"))
+            {
+                option.log = true;
+            }
+            else if (!strcmp(value, "disable"))
+            {
+                option.log = false;
+            }
+        }
+        else if (!strcmp(name, "log-file"))
+        {
+            asprintf(&c->log_filename, "%s", value);
+            option.log_filename = c->log_filename;
+        }
+        else if (!strcmp(name, "log-strip"))
+        {
+            if (!strcmp(value, "enable"))
+            {
+                option.log_strip = true;
+            }
+            else if (!strcmp(value, "disable"))
+            {
+                option.log_strip = false;
+            }
         }
         else if (!strcmp(name, "local-echo"))
         {
-            option.local_echo = atoi(value);
+            if (!strcmp(value, "enable"))
+            {
+                option.local_echo = true;
+            }
+            else if (!strcmp(value, "disable"))
+            {
+                option.local_echo = false;
+            }
+        }
+        else if (!strcmp(name, "hex-mode"))
+        {
+            if (!strcmp(value, "enable"))
+            {
+                option.hex_mode = true;
+            }
+            else if (!strcmp(value, "disable"))
+            {
+                option.hex_mode = false;
+            }
         }
         else if (!strcmp(name, "timestamp"))
         {
-            option.timestamp = timestamp_option_parse(value);
+            if (!strcmp(value, "enable"))
+            {
+                option.timestamp = TIMESTAMP_24HOUR;
+            }
+            else if (!strcmp(value, "disable"))
+            {
+                option.timestamp = TIMESTAMP_NONE;
+            }
         }
-        else if (!strcmp(name, "log-filename"))
+        else if (!strcmp(name, "timestamp-format"))
         {
-            asprintf(&c->log_filename, "%s", value);
-            option.log_filename = c->log_filename;
+            option.timestamp = timestamp_option_parse(value);
         }
         else if (!strcmp(name, "map"))
         {
@@ -150,7 +204,23 @@ static int data_handler(void *user, const char *section, const char *name,
         }
         else if (!strcmp(name, "color"))
         {
+            if (!strcmp(value, "list"))
+            {
+                // Ignore
+                return 0;
+            }
+
+            if (!strcmp(value, "none"))
+            {
+                option.color = -1; // No color
+                return 0;
+            }
+
             option.color = atoi(value);
+            if ((option.color < 0) || (option.color > 255))
+            {
+                option.color = -1; // No color
+            }
         }
         else if (!strcmp(name, "socket"))
         {
@@ -220,25 +290,32 @@ static int resolve_config_file(void)
         return 0;
     }
 
+    free(c->path);
+
     asprintf(&c->path, "%s/.config/tio/tiorc", getenv("HOME"));
     if (!access(c->path, F_OK))
     {
         return 0;
     }
 
+    free(c->path);
+
     asprintf(&c->path, "%s/.tiorc", getenv("HOME"));
     if (!access(c->path, F_OK))
     {
         return 0;
     }
 
+    free(c->path);
+
+    c->path = NULL;
+
     return -EINVAL;
 }
 
-void config_file_parse(const int argc, char *argv[])
+void config_file_parse(void)
 {
     int ret;
-    int i;
 
     c = malloc(sizeof(struct config_t));
     memset(c, 0, sizeof(struct config_t));
@@ -250,14 +327,8 @@ void config_file_parse(const int argc, char *argv[])
         return;
     }
 
-    for (i = 1; i < argc; i++)
-    {
-        if (argv[i][0] != '-')
-        {
-            c->user = argv[i];
-            break;
-        }
-    }
+    // Set user input which may be tty device or sub config
+    c->user = option.tty_device;
 
     if (!c->user)
     {
@@ -272,6 +343,7 @@ void config_file_parse(const int argc, char *argv[])
         fprintf(stderr, "Error: Unable to parse configuration file (%d)", ret);
         exit(EXIT_FAILURE);
     }
+    free(c->section_name);
     c->section_name = NULL;
 
     // Find matching section
@@ -286,13 +358,15 @@ void config_file_parse(const int argc, char *argv[])
         }
     }
 
-    // Parse settings of found section
+    // Parse settings of found section (sub config)
     ret = ini_parse(c->path, data_handler, NULL);
     if (ret < 0)
     {
         fprintf(stderr, "Error: Unable to parse configuration file (%d)", ret);
         exit(EXIT_FAILURE);
     }
+
+    atexit(&config_exit);
 }
 
 void config_exit(void)
@@ -310,14 +384,14 @@ void config_exit(void)
     free(c);
 }
 
-void config_file_print()
+void config_file_print(void)
 {
     if (c->path != NULL)
     {
         tio_printf(" Path: %s", c->path);
         if (c->section_name != NULL)
         {
-            tio_printf(" Active config section: %s", c->section_name);
+            tio_printf(" Active sub-configuration: %s", c->section_name);
         }
     }
 }
index 57ae2a1a7b65ea46fbc1862bd3390dab2049f7b2..8fbea07cb5132ab1f45fb1bdea23a700db8be131 100644 (file)
@@ -38,6 +38,6 @@ struct config_t
        char *map;
 };
 
-void config_file_print();
-void config_file_parse(const int argc, char *argv[]);
+void config_file_print(void);
+void config_file_parse(void);
 void config_exit(void);
index 640f8349c778759b63a2ced74c7c2ddd2f4edb8d..5366503a90796564f6a839689f69131d76c79f8b 100644 (file)
--- a/src/log.c
+++ b/src/log.c
 #include "print.h"
 #include "error.h"
 
+#define IS_ESC_CSI_INTERMEDIATE_CHAR(c) ((c >= 0x20) && (c <= 0x3F))
+#define IS_ESC_END_CHAR(c)              ((c >= 0x30) && (c <= 0x7E))
+#define IS_CTRL_CHAR(c)                 ((c >= 0x00) && (c <= 0x1F))
+
 static FILE *fp;
 static bool log_error = false;
 
@@ -70,11 +74,80 @@ void log_open(const char *filename)
     setvbuf(fp, NULL, _IONBF, 0);
 }
 
+bool log_strip(char c)
+{
+    static char previous_char = 0;
+    static bool esc_sequence = false;
+    bool strip = false;
+
+    /* Detect if character should be stripped or not */
+    switch (c)
+    {
+        case 0xa:
+            /* Line feed / new line */
+            /* Reset ESC sequence just in case something went wrong with the
+             * escape sequence parsing. */
+            esc_sequence = false;
+            break;
+
+        case 0x1b:
+            /* Escape */
+            strip = true;
+            break;
+
+        case 0x5b:
+            /* Left bracket */
+            if (previous_char == 0x1b)
+            {
+                // Start of ESC sequence
+                esc_sequence = true;
+                strip = true;
+            }
+            break;
+
+        default:
+            if (IS_CTRL_CHAR(c))
+            {
+                /* Strip ASCII control characters */
+                strip = true;
+                break;
+            }
+            else
+            if ((esc_sequence) && (IS_ESC_CSI_INTERMEDIATE_CHAR(c)))
+            {
+                strip = true;
+                break;
+            }
+            else
+            if ((esc_sequence) && (IS_ESC_END_CHAR(c)))
+            {
+                esc_sequence = false;
+                strip = true;
+                break;
+            }
+            break;
+    }
+
+    previous_char = c;
+
+    return strip;
+}
+
 void log_write(char c)
 {
     if (fp != NULL)
     {
-        fputc(c, fp);
+        if (option.log_strip)
+        {
+            if (!log_strip(c))
+            {
+                fputc(c, fp);
+            }
+        }
+        else
+        {
+            fputc(c, fp);
+        }
     }
 }
 
index 49837a98a093fac463f07f39c19ca2892d88468f..519e8d962cce14b6f87a1206c78bb485f6820b79 100644 (file)
@@ -42,49 +42,70 @@ int main(int argc, char *argv[])
     /* Add error exit handler */
     atexit(&error_exit);
 
-    /* Parse configuration file */
-    config_file_parse(argc, argv);
-    atexit(&config_exit);
-
-    /* Parse command-line options */
+    /* Parse command-line options (1st pass) */
     options_parse(argc, argv);
 
-    /* List available serial devices */
-    if (option.list_devices)
-    {
-        list_serial_devices();
-        return status;
-    }
+    /* Parse configuration file */
+    config_file_parse();
+
+    /* Parse command-line options (2nd pass) */
+    options_parse_final(argc, argv);
 
     /* Configure tty device */
     tty_configure();
 
     /* Configure input terminal */
-    stdin_configure();
+    if (isatty(fileno(stdin)))
+    {
+       stdin_configure();
+    }
+    else
+    {
+        // Enter non interactive mode
+        interactive_mode = false;
+    }
 
     /* Configure output terminal */
-    stdout_configure();
+    if (isatty(fileno(stdout)))
+    {
+        stdout_configure();
+    }
+    else
+    {
+        // No color when piping
+        option.color = -1;
+    }
 
     /* Add log exit handler */
     atexit(&log_exit);
 
     /* Create log file */
     if (option.log)
+    {
         log_open(option.log_filename);
+    }
 
-    /* Enable ANSI text formatting (colors etc.) */
-    print_enable_ansi_formatting();
+    /* Initialize ANSI text formatting (colors etc.) */
+    print_init_ansi_formatting();
 
     /* Print launch hints */
     tio_printf("tio v%s", VERSION);
-    tio_printf("Press ctrl-t q to quit");
+    if (interactive_mode)
+    {
+        tio_printf("Press ctrl-t q to quit");
+    }
 
     /* Open socket */
-    socket_configure();
+    if (option.socket)
+    {
+        socket_configure();
+    }
 
     /* Connect to tty device */
-    if (option.no_autoconnect)
+    if ((option.no_autoconnect) || (!interactive_mode))
+    {
         status = tty_connect();
+    }
     else
     {
         /* Enter connect loop */
index e02ee52583c3cf3a795097295b3298fbce6fd829..bc05bc73c36f2c444643482dde90058a4909f079 100644 (file)
 #include "error.h"
 #include "misc.h"
 #include "print.h"
+#include "tty.h"
+
+enum opt_t
+{
+    OPT_NONE,
+    OPT_TIMESTAMP_FORMAT,
+    OPT_LOG_FILE,
+    OPT_LOG_STRIP,
+};
 
 /* Default options */
 struct option_t option =
@@ -47,13 +56,14 @@ struct option_t option =
     .output_delay = 0,
     .no_autoconnect = false,
     .log = false,
+    .log_filename = NULL,
+    .log_strip = false,
     .local_echo = false,
     .timestamp = TIMESTAMP_NONE,
-    .list_devices = false,
-    .log_filename = NULL,
     .socket = NULL,
     .map = "",
-    .color = -1,
+    .color = 15,
+    .hex_mode = false,
 };
 
 void print_help(char *argv[])
@@ -61,30 +71,32 @@ void print_help(char *argv[])
     printf("Usage: %s [<options>] <tty-device|config>\n", argv[0]);
     printf("\n");
     printf("Options:\n");
-    printf("  -b, --baudrate <bps>        Baud rate (default: 115200)\n");
-    printf("  -d, --databits 5|6|7|8      Data bits (default: 8)\n");
-    printf("  -f, --flow hard|soft|none   Flow control (default: none)\n");
-    printf("  -s, --stopbits 1|2          Stop bits (default: 1)\n");
-    printf("  -p, --parity odd|even|none  Parity (default: none)\n");
-    printf("  -o, --output-delay <ms>     Output delay (default: 0)\n");
-    printf("  -n, --no-autoconnect        Disable automatic connect\n");
-    printf("  -e, --local-echo            Enable local echo\n");
-    printf("  -t, --timestamp[=<format>]  Enable timestamp (default: 24hour)\n");
-    printf("  -L, --list-devices          List available serial devices\n");
-    printf("  -l, --log[=<filename>]      Log to file\n");
-    printf("  -m, --map <flags>           Map special characters\n");
-    printf("  -c, --color <code>          Colorize tio text\n");
-    printf("  -S, --socket <socket>       Listen on socket\n");
-    printf("  -v, --version               Display version\n");
-    printf("  -h, --help                  Display help\n");
+    printf("  -b, --baudrate <bps>             Baud rate (default: 115200)\n");
+    printf("  -d, --databits 5|6|7|8           Data bits (default: 8)\n");
+    printf("  -f, --flow hard|soft|none        Flow control (default: none)\n");
+    printf("  -s, --stopbits 1|2               Stop bits (default: 1)\n");
+    printf("  -p, --parity odd|even|none       Parity (default: none)\n");
+    printf("  -o, --output-delay <ms>          Output delay (default: 0)\n");
+    printf("  -n, --no-autoconnect             Disable automatic connect\n");
+    printf("  -e, --local-echo                 Enable local echo\n");
+    printf("  -t, --timestamp                  Enable line timestamp\n");
+    printf("      --timestamp-format <format>  Set timestamp format (default: 24hour)\n");
+    printf("  -L, --list-devices               List available serial devices\n");
+    printf("  -l, --log                        Enable log to file\n");
+    printf("      --log-file <filename>        Set log filename\n");
+    printf("      --log-strip                  Strip control characters and escape sequences\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("  -v, --version                    Display version\n");
+    printf("  -h, --help                       Display help\n");
     printf("\n");
     printf("Options may be set via configuration file.\n");
     printf("\n");
     printf("In session, press ctrl-t q to quit.\n");
     printf("\n");
     printf("See the man page for more details.\n");
-    printf("\n");
-
 }
 
 const char* timestamp_token(enum timestamp_t timestamp)
@@ -129,7 +141,7 @@ enum timestamp_t timestamp_option_parse(const char *arg)
         }
         else
         {
-            printf("Warning: Unknown timestamp type, falling back to '24hour' default format\n");
+            warning_printf("Unknown timestamp type, falling back to '24hour' default format");
         }
     }
 
@@ -170,30 +182,34 @@ void options_parse(int argc, char *argv[])
     {
         static struct option long_options[] =
         {
-            {"baudrate",       required_argument, 0, 'b'},
-            {"databits",       required_argument, 0, 'd'},
-            {"flow",           required_argument, 0, 'f'},
-            {"stopbits",       required_argument, 0, 's'},
-            {"parity",         required_argument, 0, 'p'},
-            {"output-delay",   required_argument, 0, 'o'},
-            {"no-autoconnect", no_argument,       0, 'n'},
-            {"local-echo",     no_argument,       0, 'e'},
-            {"timestamp",      optional_argument, 0, 't'},
-            {"list-devices",   no_argument,       0, 'L'},
-            {"log",            optional_argument, 0, 'l'},
-            {"socket",         required_argument, 0, 'S'},
-            {"map",            required_argument, 0, 'm'},
-            {"color",          required_argument, 0, 'c'},
-            {"version",        no_argument,       0, 'v'},
-            {"help",           no_argument,       0, 'h'},
-            {0,                0,                 0,  0 }
+            {"baudrate",         required_argument, 0, 'b'                  },
+            {"databits",         required_argument, 0, 'd'                  },
+            {"flow",             required_argument, 0, 'f'                  },
+            {"stopbits",         required_argument, 0, 's'                  },
+            {"parity",           required_argument, 0, 'p'                  },
+            {"output-delay",     required_argument, 0, 'o'                  },
+            {"no-autoconnect",   no_argument,       0, 'n'                  },
+            {"local-echo",       no_argument,       0, 'e'                  },
+            {"timestamp",        no_argument,       0, 't'                  },
+            {"timestamp-format", required_argument, 0, OPT_TIMESTAMP_FORMAT },
+            {"list-devices",     no_argument,       0, 'L'                  },
+            {"log",              no_argument,       0, 'l'                  },
+            {"log-file",         required_argument, 0, OPT_LOG_FILE         },
+            {"log-strip",        no_argument,       0, OPT_LOG_STRIP        },
+            {"socket",           required_argument, 0, 'S'                  },
+            {"map",              required_argument, 0, 'm'                  },
+            {"color",            required_argument, 0, 'c'                  },
+            {"hex-mode",         no_argument,       0, 'x'                  },
+            {"version",          no_argument,       0, 'v'                  },
+            {"help",             no_argument,       0, 'h'                  },
+            {0,                  0,                 0,  0                   }
         };
 
         /* getopt_long stores the option index here */
         int option_index = 0;
 
         /* Parse argument using getopt_long */
-        c = getopt_long(argc, argv, "b:d:f:s:p:o:net::Ll::S:m:c:vh", long_options, &option_index);
+        c = getopt_long(argc, argv, "b:d:f:s:p:o:netLlS:m:c:xvh", long_options, &option_index);
 
         /* Detect the end of the options */
         if (c == -1)
@@ -244,18 +260,30 @@ void options_parse(int argc, char *argv[])
                 break;
 
             case 't':
+                option.timestamp = TIMESTAMP_24HOUR;
+                break;
+
+            case OPT_TIMESTAMP_FORMAT:
                 option.timestamp = timestamp_option_parse(optarg);
                 break;
 
             case 'L':
-                option.list_devices = true;
+                list_serial_devices();
+                exit(EXIT_SUCCESS);
                 break;
 
             case 'l':
                 option.log = true;
+                break;
+
+            case OPT_LOG_FILE:
                 option.log_filename = optarg;
                 break;
 
+            case OPT_LOG_STRIP:
+                option.log_strip = true;
+                break;
+
             case 'S':
                 option.socket = optarg;
                 break;
@@ -265,13 +293,7 @@ void options_parse(int argc, char *argv[])
                 break;
 
             case 'c':
-                option.color = string_to_long(optarg);
-                if (option.color > 255)
-                {
-                    printf("Error: Invalid color code\n");
-                    exit(EXIT_FAILURE);
-                }
-                if (option.color < 0)
+                if (!strcmp(optarg, "list"))
                 {
                     // Print available color codes
                     printf("Available color codes:\n");
@@ -281,6 +303,23 @@ void options_parse(int argc, char *argv[])
                     }
                     exit(EXIT_SUCCESS);
                 }
+
+                if (!strcmp(optarg, "none"))
+                {
+                    option.color = -1;
+                    break;
+                }
+
+                option.color = string_to_long(optarg);
+                if ((option.color < 0) || (option.color > 255))
+                {
+                    printf("Error: Invalid color code\n");
+                    exit(EXIT_FAILURE);
+                }
+                break;
+
+            case 'x':
+                option.hex_mode = true;
                 break;
 
             case 'v':
@@ -308,11 +347,6 @@ void options_parse(int argc, char *argv[])
         }
     }
 
-    if (option.list_devices)
-    {
-        return;
-    }
-
     /* Assume first non-option is the tty device name */
     if (strcmp(option.tty_device, ""))
            optind++;
@@ -321,7 +355,7 @@ void options_parse(int argc, char *argv[])
 
     if (strlen(option.tty_device) == 0)
     {
-        printf("Error: Missing device name\n");
+        printf("Error: Missing device or config name\n");
         exit(EXIT_FAILURE);
     }
 
@@ -335,3 +369,16 @@ void options_parse(int argc, char *argv[])
         exit(EXIT_FAILURE);
     }
 }
+
+void options_parse_final(int argc, char *argv[])
+{
+    /* Preserve tty device which may have been set by configuration file */
+    const char *tty_device = option.tty_device;
+
+    /* Do 2nd pass to override settings set by configuration file */
+    optind = 1; // Reset option index to restart scanning of argv
+    options_parse(argc, argv);
+
+    /* Restore tty device */
+    option.tty_device = tty_device;
+}
index 98bec82744a512bae530fb92810daef20eb10c5b..cc905bf656f48d5c57071d2caf61e26dcbbd5e5e 100644 (file)
@@ -32,7 +32,9 @@ enum timestamp_t
     TIMESTAMP_24HOUR,
     TIMESTAMP_24HOUR_START,
     TIMESTAMP_ISO8601,
+    TIMESTAMP_END,
 };
+
 const char* timestamp_token(enum timestamp_t timestamp);
 enum timestamp_t timestamp_option_parse(const char *arg);
 
@@ -48,16 +50,18 @@ struct option_t
     int output_delay;
     bool no_autoconnect;
     bool log;
+    bool log_strip;
     bool local_echo;
     enum timestamp_t timestamp;
-    bool list_devices;
     const char *log_filename;
     const char *map;
     const char *socket;
     int color;
+    bool hex_mode;
 };
 
 extern struct option_t option;
 
 void options_print();
 void options_parse(int argc, char *argv[]);
+void options_parse_final(int argc, char *argv[]);
index 88ba8816454be3ed82a8157c2baf8100fcbf6917..7580c317c3ba8c5bdb41e2d661d663ced7399dc2 100644 (file)
@@ -29,14 +29,7 @@ char ansi_format[30];
 
 void print_hex(char c)
 {
-  if ((c == '\n') || (c == '\r'))
-  {
-    printf("%c", c);
-  }
-  else
-  {
-    printf("%02x ", (unsigned char) c);
-  }
+  printf("%02x ", (unsigned char) c);
 
   fflush(stdout);
 }
@@ -47,16 +40,8 @@ void print_normal(char c)
   fflush(stdout);
 }
 
-void print_enable_ansi_formatting()
+void print_init_ansi_formatting()
 {
-  if (option.color < 0)
-  {
-    // Enable bold text
-    sprintf(ansi_format, "\e[1m");
-  }
-  else
-  {
-    // Enable bold text with user defined ANSI color
-    sprintf(ansi_format, "\e[1;38;5;%dm", option.color);
-  }
+  // Set bold text with user defined ANSI color
+  sprintf(ansi_format, "\e[1;38;5;%dm", option.color);
 }
index 1c6b1e58b0be20e68a5694e7e0b91ca79cb14943..28592fcecc4a2df5c03951ec9803dcb2824efbb5 100644 (file)
@@ -24,6 +24,7 @@
 #include <stdbool.h>
 #include "misc.h"
 #include "error.h"
+#include "options.h"
 
 extern bool print_tainted;
 extern char ansi_format[];
@@ -32,25 +33,39 @@ extern char ansi_format[];
 
 #define ansi_printf(format, args...) \
 { \
-  fprintf (stdout, "\r%s" format ANSI_RESET "\r\n", ansi_format, ## args); \
+  if (option.color < 0) \
+    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 (stderr, "\r%s" format ANSI_RESET "\r\n", ansi_format, ## args); \
+  if (option.color < 0) \
+    fprintf (stdout, "\r" format "\r\n", ## args); \
+  else \
+    fprintf (stderr, "\r%s" format ANSI_RESET "\r\n", ansi_format, ## args); \
   fflush(stderr); \
 }
 
 #define ansi_printf_raw(format, args...) \
 { \
-  fprintf (stdout, "%s" format ANSI_RESET, ansi_format, ## args); \
+  if (option.color < 0) \
+    fprintf (stdout, format, ## args); \
+  else \
+    fprintf (stdout, "%s" format ANSI_RESET, ansi_format, ## args); \
   fflush(stdout); \
 }
 
 #define warning_printf(format, args...) \
 { \
-  ansi_printf("[%s] Warning: " format, current_time(), ## args); \
+  if (print_tainted) \
+    putchar('\n'); \
+  if (option.color < 0) \
+    fprintf (stdout, "\r[%s] Warning: " format "\r\n", current_time(), ## args); \
+  else \
+    ansi_printf("[%s] Warning: " format, current_time(), ## args); \
   fflush(stdout); \
 }
 
@@ -88,4 +103,4 @@ extern char ansi_format[];
 
 void print_hex(char c);
 void print_normal(char c);
-void print_enable_ansi_formatting(void);
+void print_init_ansi_formatting(void);
index d2b125ec34c67de1c2f50f5c5fd753ebdc840201..801ef3f3651dadcf3bf66ff55bd24d5cce7ed285 100644 (file)
 #include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <arpa/inet.h>
 #include <sys/socket.h>
 #include <sys/un.h>
+#include <netinet/in.h>
 #include <unistd.h>
 
 #define MAX_SOCKET_CLIENTS 16
+#define SOCKET_PORT_DEFAULT 3333
 
 static int sockfd;
 static int clientfds[MAX_SOCKET_CLIENTS];
+static int socket_family = AF_UNSPEC;
+static int port_number = SOCKET_PORT_DEFAULT;
 
 static const char *socket_filename(void)
 {
@@ -42,58 +47,161 @@ static const char *socket_filename(void)
     return option.socket + 5;
 }
 
+static int socket_inet_port(void)
+{
+    /* skip 'inet:' */
+    int port_number = atoi(option.socket + 5);
+    if (port_number == 0)
+    {
+        port_number = SOCKET_PORT_DEFAULT;
+    }
+    return port_number;
+}
+
+static int socket_inet6_port(void)
+{
+    /* skip 'inet6:' */
+    int port_number = atoi(option.socket + 6);
+    if (port_number == 0)
+    {
+        port_number = SOCKET_PORT_DEFAULT;
+    }
+    return port_number;
+}
+
 static void socket_exit(void)
 {
-    unlink(socket_filename());
+    if (socket_family == AF_UNIX)
+    {
+        unlink(socket_filename());
+    }
 }
 
 void socket_configure(void)
 {
-    if (!option.socket)
+    struct sockaddr_un sockaddr_unix = {};
+    struct sockaddr_in sockaddr_inet = {};
+    struct sockaddr_in6 sockaddr_inet6 = {};
+    struct sockaddr *sockaddr_p;
+    socklen_t socklen;
+
+    /* Parse socket string */
+
+    if (strncmp(option.socket, "unix:", 5) == 0)
     {
-        return;
+        socket_family = AF_UNIX;
+
+        if (strlen(socket_filename()) == 0)
+        {
+            error_printf("Missing socket filename");
+            exit(EXIT_FAILURE);
+        }
+
+        if (strlen(socket_filename()) > sizeof(sockaddr_unix.sun_path) - 1)
+        {
+            error_printf("Socket file path %s too long", option.socket);
+            exit(EXIT_FAILURE);
+        }
     }
 
-    if (strncmp(option.socket, "unix:", 5) != 0)
+    if (strncmp(option.socket, "inet:", 5) == 0)
     {
-        error_printf("%s: Invalid socket scheme, must be 'unix:'", option.socket);
-        exit(EXIT_FAILURE);
+        socket_family = AF_INET;
+
+        port_number = socket_inet_port();
+
+        if (port_number < 0)
+        {
+            error_printf("Invalid port number: %d", port_number);
+            exit(EXIT_FAILURE);
+        }
+    }
+
+    if (strncmp(option.socket, "inet6:", 6) == 0)
+    {
+        socket_family = AF_INET6;
+
+        port_number = socket_inet6_port();
+
+        if (port_number < 0)
+        {
+            error_printf("Invalid port number: %d", port_number);
+            exit(EXIT_FAILURE);
+        }
     }
 
-    struct sockaddr_un sockaddr = {};
-    if (strlen(socket_filename()) > sizeof(sockaddr.sun_path) - 1)
+    if (socket_family == AF_UNSPEC)
     {
-        error_printf("Socket file path %s too long", option.socket);
+        error_printf("%s: Invalid socket scheme, must be prefixed with 'unix:', 'inet:', or 'inet6:'", option.socket);
         exit(EXIT_FAILURE);
     }
  
-    sockaddr.sun_family = AF_UNIX;
-    strncpy(sockaddr.sun_path, socket_filename(), sizeof(sockaddr.sun_path) - 1);
+    /* Configure socket */
+
+    switch (socket_family)
+    {
+        case AF_UNIX:
+            sockaddr_unix.sun_family = AF_UNIX;
+            strncpy(sockaddr_unix.sun_path, socket_filename(), sizeof(sockaddr_unix.sun_path) - 1);
+            sockaddr_p = (struct sockaddr *) &sockaddr_unix;
+            socklen = sizeof(sockaddr_unix);
+            break;
 
-    sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
+        case AF_INET:
+            sockaddr_inet.sin_family = AF_INET;
+            sockaddr_inet.sin_addr.s_addr = INADDR_ANY;
+            sockaddr_inet.sin_port = htons(port_number);
+            sockaddr_p = (struct sockaddr *) &sockaddr_inet;
+            socklen = sizeof(sockaddr_inet);
+            break;
+
+        case AF_INET6:
+            sockaddr_inet6.sin6_family = AF_INET6;
+            sockaddr_inet6.sin6_addr = in6addr_any;
+            sockaddr_inet6.sin6_port = htons(port_number);
+            sockaddr_p = (struct sockaddr *) &sockaddr_inet6;
+            socklen = sizeof(sockaddr_inet6);
+            break;
+
+        default:
+            error_printf("Invalid socket family (%d)", socket_family);
+            exit(EXIT_FAILURE);
+            break;
+    }
+
+    /* Create socket */
+    sockfd = socket(socket_family, SOCK_STREAM, 0);
     if (sockfd < 0)
     {
-        error_printf("Failed to create socket: %s", strerror(errno));
+        error_printf("Failed to create socket (%s)", strerror(errno));
         exit(EXIT_FAILURE);
     }
 
-    unlink(socket_filename());
-    if (bind(sockfd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) < 0)
+    /* Bind */
+    if (bind(sockfd, sockaddr_p, socklen) < 0)
     {
-        error_printf("Failed to bind to socket %s: %s", socket_filename(), strerror(errno));
+        error_printf("Failed to bind to socket (%s)", strerror(errno));
         exit(EXIT_FAILURE);
     }
 
+    /* Listen */
     if (listen(sockfd, MAX_SOCKET_CLIENTS) < 0)
     {
-        error_printf("Failed to listen on socket %s: %s", socket_filename(), strerror(errno));
+        error_printf("Failed to listen on socket (%s)", strerror(errno));
         exit(EXIT_FAILURE);
     }
 
     memset(clientfds, -1, sizeof(clientfds));
     atexit(socket_exit);
 
-    tio_printf("Listening on socket %s", socket_filename());
+    if (socket_family == AF_UNIX)
+    {
+        tio_printf("Listening on socket %s", socket_filename());
+    }
+    else
+    {
+        tio_printf("Listening on socket port %d", port_number);
+    }
 }
 
 void socket_write(char input_char)
@@ -109,7 +217,7 @@ void socket_write(char input_char)
         {
             if (write(clientfds[i], &input_char, 1) <= 0)
             {
-                error_printf_silent("Failed to write to socket: %s", strerror(errno));
+                error_printf_silent("Failed to write to socket (%s)", strerror(errno));
                 close(clientfds[i]);
                 clientfds[i] = -1;
             }
@@ -180,7 +288,7 @@ bool socket_handle_input(fd_set *rdfs, char *output_char)
             }
             if (status < 0)
             {
-                error_printf_silent("Failed to read from socket: %s", strerror(errno));
+                error_printf_silent("Failed to read from socket (%s)", strerror(errno));
                 close(clientfds[i]);
                 clientfds[i] = -1;
                 continue;
index 0105ea86b0897af8f9e1f341df34e5f8084f0b86..e24da9c2126c6aff9ca26018d4e99653d0e7b64e 100644 (file)
--- a/src/tty.c
+++ b/src/tty.c
@@ -63,6 +63,8 @@ extern int iossiospeed(int fd, int baudrate);
 #define PATH_SERIAL_DEVICES "/dev/serial/by-id/"
 #endif
 
+bool interactive_mode = true;
+
 static struct termios tio, tio_old, stdout_new, stdout_old, stdin_new, stdin_old;
 static unsigned long rx_total = 0, tx_total = 0;
 static bool connected = false;
@@ -74,7 +76,60 @@ static bool map_i_nl_crnl = false;
 static bool map_o_cr_nl = false;
 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 void optional_local_echo(char c)
+{
+    if (!option.local_echo)
+        return;
+    print(c);
+    fflush(stdout);
+    if (option.log)
+        log_write(c);
+}
+
+inline static bool is_valid_hex(char c)
+{
+    return ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
+}
+
+inline static unsigned char char_to_nibble(char c)
+{
+    if(c >= '0' && c <= '9'){
+        return c - '0';
+    } else if(c >= 'a' && c <= 'f'){
+        return c - 'a' + 10;
+    } else if(c >= 'A' && c <= 'F'){
+        return c - 'A' + 10;
+    } else {
+        return 0;
+    }
+}
+
+static void output_hex(char c)
+{
+    hex_chars[hex_char_index++] = c;
+
+    if (hex_char_index == 2)
+    {
+        unsigned char hex_value = char_to_nibble(hex_chars[0]) << 4 | (char_to_nibble(hex_chars[1]) & 0x0F);
+        hex_char_index = 0;
+
+        optional_local_echo(hex_value);
+
+        ssize_t status = write(fd, &hex_value, 1);
+        if (status < 0)
+        {
+            warning_printf("Could not write to tty device");
+        } else
+        {
+            tx_total++;
+        }
 
+        fsync(fd);
+    }
+}
 
 static void toggle_line(const char *line_name, int mask)
 {
@@ -82,7 +137,7 @@ static void toggle_line(const char *line_name, int mask)
 
     if (ioctl(fd, TIOCMGET, &state) < 0)
     {
-        error_printf("Could not get line state: %s", strerror(errno));
+        warning_printf("Could not get line state (%s)", strerror(errno));
     }
     else
     {
@@ -97,7 +152,7 @@ static void toggle_line(const char *line_name, int mask)
             tio_printf("set %s to HIGH", line_name);
         }
         if (ioctl(fd, TIOCMSET, &state) < 0)
-            error_printf("Could not set line state: %s", strerror(errno));
+            warning_printf("Could not set line state (%s)", strerror(errno));
     }
 }
 
@@ -143,7 +198,7 @@ void handle_command_sequence(char input_char, char previous_char, char *output_c
             case KEY_SHIFT_L:
                 if (ioctl(fd, TIOCMGET, &state) < 0)
                 {
-                    error_printf("Could not get line state: %s", strerror(errno));
+                    warning_printf("Could not get line state (%s)", strerror(errno));
                     break;
                 }
                 tio_printf("Line states:");
@@ -174,6 +229,7 @@ void handle_command_sequence(char input_char, char previous_char, char *output_c
 
             case KEY_E:
                 option.local_echo = !option.local_echo;
+                tio_printf("Switched local echo %s", option.local_echo ? "on" : "off");
                 break;
 
             case KEY_H:
@@ -216,7 +272,25 @@ void handle_command_sequence(char input_char, char previous_char, char *output_c
                 break;
 
             case KEY_SHIFT_T:
-                option.timestamp = !option.timestamp;
+                option.timestamp += 1;
+                switch (option.timestamp)
+                {
+                    case TIMESTAMP_NONE:
+                        break;
+                    case TIMESTAMP_24HOUR:
+                        tio_printf("Switched to 24hour timestamp mode");
+                        break;
+                    case TIMESTAMP_24HOUR_START:
+                        tio_printf("Switched to 24hour-start timestamp mode");
+                        break;
+                    case TIMESTAMP_ISO8601:
+                        tio_printf("Switched to iso8601 timestamp mode");
+                        break;
+                    case TIMESTAMP_END:
+                        option.timestamp = TIMESTAMP_NONE;
+                        tio_printf("Switched timestamp off");
+                        break;
+                }
                 break;
 
             case KEY_V:
@@ -329,8 +403,8 @@ void tty_configure(void)
     /* Set speed */
     switch (option.baudrate)
     {
-        /* The macro below expands into switch cases autogenerated by the
-         * configure script. Each switch case verifies and configures the baud
+        /* The macro below expands into switch cases autogenerated by meson
+         * configure. Each switch case verifies and configures the baud
          * rate and is of the form:
          *
          * case $baudrate: baudrate = B$baudrate; break;
@@ -585,16 +659,6 @@ void tty_restore(void)
         tty_disconnect();
 }
 
-static void optional_local_echo(char c)
-{
-    if (!option.local_echo)
-        return;
-    print(c);
-    fflush(stdout);
-    if (option.log)
-        log_write(c);
-}
-
 int tty_connect(void)
 {
     fd_set rdfs;           /* Read file descriptor set */
@@ -644,6 +708,18 @@ int tty_connect(void)
     if (option.timestamp)
         next_timestamp = true;
 
+    /* Manage print output mode */
+    if (option.hex_mode)
+    {
+        print = print_hex;
+        print_mode = HEX;
+    }
+    else
+    {
+        print = print_normal;
+        print_mode = NORMAL;
+    }
+
     /* Save current port settings */
     if (tcgetattr(fd, &tio_old) < 0)
         goto error_tcgetattr;
@@ -781,17 +857,21 @@ int tty_connect(void)
                     goto error_read;
                 }
 
-                /* Forward input to output except ctrl-t key */
+                /* Forward input to output */
                 output_char = input_char;
-                if (input_char == KEY_CTRL_T)
-                    forward = false;
 
-                /* Handle commands */
-                handle_command_sequence(input_char, previous_char, &output_char, &forward);
+                if (interactive_mode)
+                {
+                    /* Do not forward ctrl-t key */
+                    if (input_char == KEY_CTRL_T)
+                        forward = false;
 
-                /* Save previous key */
-                previous_char = input_char;
+                    /* Handle commands */
+                    handle_command_sequence(input_char, previous_char, &output_char, &forward);
 
+                    /* Save previous key */
+                    previous_char = input_char;
+                }
             }
             else
             {
@@ -800,6 +880,15 @@ int tty_connect(void)
 
             if (forward)
             {
+                if (print_mode == HEX)
+                {
+                    if (!is_valid_hex(input_char))
+                    {
+                        warning_printf("Invalid hex character: '%d' (0x%02x)", input_char, input_char);
+                        continue;
+                    }
+                }
+
                 /* Map output character */
                 if ((output_char == 127) && (map_o_del_bs))
                     output_char = '\b';
@@ -820,15 +909,22 @@ int tty_connect(void)
                     delay(option.output_delay);
                 } 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++;
+                    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);
index df5e8243116c6d8052b25078b05519f6afbc05af..081ba47004304f4595476e41f2e42b9a3b7934a4 100644 (file)
--- a/src/tty.h
+++ b/src/tty.h
@@ -21,6 +21,8 @@
 
 #pragma once
 
+#include <stdbool.h>
+
 #define KEY_QUESTION 0x3f
 #define KEY_B 0x62
 #define KEY_C 0x63
@@ -48,3 +50,5 @@ void tty_configure(void);
 int tty_connect(void);
 void tty_wait_for_device(void);
 void list_serial_devices(void);
+
+extern bool interactive_mode;