Compare commits

...

349 commits
v2.6 ... master

Author SHA1 Message Date
Jakob Haufe
6fb3a64ba2 Fix license in meson.build
- Make license here match LICENSE
- According to meson docs, it should not be an array
2026-01-22 12:41:01 +01:00
aiotter
3af4c5591e Fix redundant output on macOS 2025-08-07 17:18:29 +02:00
John Barbero Unenge
cce94b9d92 Add --complete-profiles to help printout and man pages 2025-06-17 16:30:31 +02:00
ii8
86f48a2fb6 Overhaul Lua API
Lua API moved into a tio library table and names adjusted to Lua stdlib style.
Regex in expect() replaced with Lua patterns so binary data can be handled.
New tio.alwaysecho variable allows enabling and disabling echo to console.
Read and write functions now manage complex retry and timeout logic internally,
giving the user a simple "nil if fail" API like the rest of Lua.
exit() was removed, os.exit() already exists in the Lua standard library.
2025-06-14 15:09:21 +02:00
Martin Lund
381c0b7823 Update codeql to v3 2025-06-14 07:03:17 +02:00
Martin Lund
8f33cff6ea Disable compiler warning on unused result 2025-05-31 19:43:11 +02:00
Keith Barratt
9d00cd3492 Fix device description-Linux
This commit only effects Linux.

The description field of the `device_list`, populated by
`tty_search_for_serial_devices()`, was either incorrect or less than ideal
for CDC ACM virtual com ports. For instance:
    (i) Some devices incorrectly have the description field populated by
    the 'product' property of USB hub they are connected via.
    (ii) Other devices have description fields populated with the interface,
    e.g. CDC, when there is a 'product' property available that would give a
    clearer description.

To solve these issues, we first prioritise searching for the 'product' property
of the device over the 'interface' property. We also look for the
'product' property in an additional directory.
2025-05-30 17:20:06 +02:00
Martin Lund
3e0b2d861d Fix Ubuntu workflow 2025-05-30 17:18:21 +02:00
ii8
a1217af4c6 Fix string truncation bug in scripting api 2025-05-25 21:13:43 +02:00
Martin Lund
58bf5c5008 Update tio man page 2025-05-25 19:46:18 +02:00
Maximilian Seesslen
7e61a34df3 Added timestamp format "epoch-usec"
This timestamp format will print the seconds since epoch along with
subdivision in microseconds.

Example:
 [1748009585.087083] tio v3.9-8-g2fb788f-dirty
 [1748009585.087156] Press ctrl-t q to quit
 [1748009585.087683] Connected to /dev/ttyUSB0
2025-05-23 16:36:39 +02:00
Hideaki Tai
2fb788f817 fix: lua script stops output if it includes null terminate 2025-05-06 17:28:53 +02:00
Martin Lund
f887756a71 meson: Enable compiler warnings on unused result and global shadows 2025-04-29 17:44:08 +02:00
Robert Lipe
5d915134a3 Fix --auto new and --auto latest on MacOS. (redo)
Git is being dumb about
67c071633d This PR is identical to that one and will supercede it.

Fix --auto new and --auto latest on MacOS.

'device_list' was both a global (eww!) and a local inside
tty_search_for_serial_devices(). The local got set and
returned, so it looked sane, but the caller used the global
instead of the return value of the function it had just
called, meaning (global) device_list was NULL while
(ignored, local) device_list held a perfectly lovely
linked list.

Tested:
tio --auto new waits for a new device to appare and connects
tio --latest will connect to the most recently attached device
  which, in most worlds, is the most recently enumerated USB
  device, conveniently skipping all the bluetooth nonsense.
  If the lone USB device is disconnected, it then connects to
  one of those, meaning you really do have to restart tio.
2025-04-29 17:05:24 +02:00
Robert Lipe
7516dff802 Add missing build piece. 2025-04-24 17:55:26 +02:00
Robert Lipe
03ef931fb2 - Implemented getPropertyString(), getDeviceLocation(), tty_search_for_serial_devices()
for MacOS
- Added error handling and memory management
- Improved code readability and consistency
- Updated coding style to match project conventions

- Added robust error checking for CoreFoundation property retrieval
- Implemented more defensive memory allocation and type checking
- Switched to using callout device key for more reliable device discovery
- Added single-line block bracing consistent with project style
- Improved comments and code formatting

- Used `kIOCalloutDeviceKey` instead of `kIODialinDeviceKey` for device path retrieval
- Enhanced type checking for CoreFoundation objects
- Simplified memory management and error handling
- Added additional logging and error reporting

- Verified functionality on MacOS 10.11 and 10.15. Tested with ESP32-P4 and ESP32-BOX

Resolves potential device discovery and memory management issues in the MacOS serial device detection code.
2025-04-24 17:55:26 +02:00
Martin Lund
437881f0ed Update AUTHORS 2025-04-23 08:17:11 +02:00
David Ordnung
c736b1e353 Input ICRCRNL mapping to avoid using INLCRNL with ICRNL 2025-04-23 08:09:22 +02:00
Martin Lund
d682e98a66 codeql: Build using ubuntu-22.04 2025-04-16 10:47:39 +02:00
Martin Lund
bdfe87e1cb Update date 2025-04-13 13:31:06 +02:00
Martin Lund
5c2ced1093 Update NEWS 2025-04-13 13:27:50 +02:00
Martin Lund
f87f470415 Fix pattern matching memory corruption 2025-04-13 13:25:25 +02:00
Martin Lund
2e86718973 Add typos.toml 2025-04-13 08:28:01 +02:00
Martin Lund
013aebcc05 Update NEWS 2025-04-12 09:02:22 +02:00
Martin Lund
b33045189f Update plain text man page 2025-04-12 08:57:25 +02:00
Martin Lund
600c3d7563 Update version date 2025-04-12 08:55:40 +02:00
Martin Lund
ebce2d4ee9 Bump version 2025-04-12 08:55:11 +02:00
Martin Lund
16b7aee42f Update NEWS 2025-04-12 08:54:50 +02:00
Martin Lund
d33e275ca3 Update AUTHORS 2025-03-23 07:04:47 +01:00
Samuel Holland
da4074c9a5 Don't add null characters to the expect buffer
They prevent regexec() from seeing the remainder of the buffer.
2025-03-23 07:02:56 +01:00
Martin Lund
f716d2ccdd Update TODO 2025-03-13 15:44:12 +01:00
V
7567e08227 Disable stdout buffering globally
This makes it possible to pipe output to other programs cleanly.
2025-03-11 20:46:14 +01:00
Martin Lund
6aca9ffee5 Update AUTHORS 2025-03-10 16:17:56 +01:00
Lubov66
f5740dbf31 docs: edited the license date 2025-03-10 16:12:53 +01:00
Lubov66
d163afc6b1 Update README.md 2025-03-10 11:37:32 +01:00
Lubov66
1b60dd1ae7 Update README.md 2025-03-10 11:07:03 +01:00
Martin Lund
795ef28f79 Update TODO 2025-02-25 16:16:24 +01:00
Martin Lund
8e155c9276 Update TODO 2025-02-23 16:26:28 +01:00
Martin Lund
6831ad0eae Fix parsing of timestamp options 2025-02-15 20:27:53 +01:00
Martin Lund
8f7bf2fd2c codeql: Upgrade to upload-artifact@v4 2025-02-08 02:58:02 +01:00
Martin Lund
f389f11669 Update plaintext man page 2025-01-26 16:41:24 +01:00
Martin Lund
37994b3cc5 Add character mapping examples 2025-01-25 15:09:07 +01:00
Jakob Haufe
27f8f2c4e6 Manpage: Fix backslash encoding
Literal backslash needs to be written as \e.
2024-12-01 14:32:56 +01:00
Martin Lund
01e637cdf4 Update NEWS 2024-11-30 12:40:48 +01:00
Martin Lund
1b2a0ea130 Update version date 2024-11-30 12:12:09 +01:00
Martin Lund
b8135ea639 Rename git version to simply version 2024-11-30 11:37:27 +01:00
Martin Lund
c49faa7337 Clean up lua API
Rename modem_send() to send()
Rename send to write()
2024-11-30 11:09:43 +01:00
Martin Lund
4511d74a9e Update AUTHORS 2024-11-07 22:17:26 +01:00
Keith Hill
afd82f7ac4 + Add system timestamps to lua read() and new lua read_line() per global options
+ Add missing timestamp-format epoch
+ Update send_ to use fsync and tcdrain like normal tty_sync does
+ Rework read_line to save partial line at timeout
+ Simplified read_line to reduce cyclomatic complexity

+ renamed example files read.lua and read_line.lua
+ moved #define READ_LINE_SIZE to top of file
+ renamed g_linebuf to linebuf, and moved it into read_line as a static variable
2024-11-07 21:45:06 +01:00
Martin Lund
db3f109c7d Zero initialize buffer in read_string() 2024-11-07 18:05:32 +01:00
Martin Lund
ab678e6c88 Use version from git 2024-10-25 19:35:13 +02:00
Martin Lund
330e99381e Fix memory leak in base62_encode() 2024-10-25 18:26:59 +02:00
Martin Lund
7e314b2cc3 Update TODO 2024-10-17 18:53:36 +02:00
Martin Lund
4fb034858a Update AUTHORS 2024-10-15 17:22:34 +02:00
Martin Lund
d494b9d3ac Update README 2024-09-25 20:51:40 +02:00
konosubakonoakua
4034d0ad51 Update readme.md
Update readme.md issue part

Update readme.md issue part
2024-09-25 17:34:06 +02:00
Martin Lund
9fec689117 Fix name declaration conflict with socket send() 2024-09-15 05:57:31 +02:00
Martin Lund
6c4b92270e Add clang-format spec 2024-09-07 09:31:31 +02:00
Martin Lund
a22b270749 Bump version 2024-08-31 09:23:19 +02:00
Martin Lund
9f27ce5899 Update version date 2024-08-31 09:09:34 +02:00
Martin Lund
27f9b9f2e8 Update NEWS 2024-08-31 09:06:28 +02:00
Martin Lund
2e7da862c8 Cleanup 2024-08-24 13:21:41 +02:00
Martin Lund
bb2b4e30b2 Update AUTHORS 2024-08-24 12:37:12 +02:00
Steve Marple
f47467271f Add "epoch" timestamp option
Add an option that prints the timestamp as the number of seconds since
the Unix epoch.
2024-08-24 12:35:30 +02:00
Martin Lund
cdc773100c Update AUTHORS 2024-08-19 20:40:05 +02:00
Tomka Gergely
a3b67d3eb6 The log-directory options is not read from the configuration file. 2024-08-19 20:37:18 +02:00
Martin Lund
ef12ed62df Remove unnecessary sync in line input mode
This caused a problem for some highly timing sensitive modem read-eval-print
loops because the input line and line termination characters (cr/nl) would be
shifted out on the UART with too big delay inbetween because of two
syncs.
2024-08-06 20:48:43 +02:00
Martin Lund
2f6b3796f2 Bump version 2024-07-25 00:12:23 +02:00
Martin Lund
6163bc392b Fix socket send call on platforms without MSG_NOSIGNAL
To fix build issue encountered on MacOS Catalina but may apply to other
platforms.
2024-07-20 08:25:49 +02:00
Martin Lund
475bc29cc8 Update plain text man page 2024-07-19 09:39:41 +02:00
Martin Lund
c4f5269c83 Update version date 2024-07-19 09:39:13 +02:00
Martin Lund
13ad59ac12 Update README 2024-07-19 09:37:57 +02:00
Martin Lund
2259244eb2 Update NEWS 2024-07-19 09:27:23 +02:00
Martin Lund
725423c50c Add configuration file include directive
To include the contents of another configuration file simply do e.g.:

[include raspberrypi.conf]

Also, included file can include other files which can include other
files etc.

This feature is useful for managing many configuration files and sharing
configuration files with others.
2024-07-19 08:49:49 +02:00
Martin Lund
14963032c3 Update TODO 2024-07-15 20:05:28 +02:00
Martin Lund
e1fe232254 Fix shadow variable 2024-07-13 18:36:03 +02:00
Martin Lund
9cafcbcab5 Update README 2024-07-13 17:17:14 +02:00
Martin Lund
f4076258f1 Update man page 2024-07-13 17:09:26 +02:00
Martin Lund
866b5bcb30 Mention how to list key commands in help output 2024-07-13 16:49:19 +02:00
Martin Lund
289bbfd393 Update AUTHORS 2024-07-13 15:14:24 +02:00
Heinrich Schuchardt
68a64ac554 Print correct 'Done' timestamp for X- and Y-modem transfers
Closes: #268

Call tio_printf() after completing xymodem_send().

Signed-off-by: Heinrich Schuchardt <heinrich.schuchardt@canonical.com>
2024-07-13 15:10:39 +02:00
Martin Lund
0a95da00f1 Typo fixes 2024-07-11 17:00:37 +02:00
Martin Lund
32d97683f8 Update TODO 2024-07-10 13:27:12 +02:00
Martin Lund
c3654486c7 Fix hex output mode when using normal input mode
In this combination of modes the input character was not forwarded to
the tty device. This fix makes sure it is forwarded.
2024-07-10 13:11:28 +02:00
Martin Lund
b5184012c4 Update AUTHORS 2024-07-10 01:48:13 +02:00
Robert Lipe
fc0c6f61d2 Recompute listing_device_name_length_max for MacOS case, too. 2024-07-10 01:46:32 +02:00
Martin Lund
53bb2a6ad1 Fix uptime on MacOS
On MacOS the birth time is apparently not available so we use
modification time instead.
2024-07-09 22:12:42 +02:00
Martin Lund
da9f7a6540 Improve warning upon failing connect
Add device path to warning when connect fails.
2024-07-09 21:06:22 +02:00
Martin Lund
2a1dae6336 Fix crashy search_reset() on macOS 2024-07-09 16:44:10 +02:00
Martin Lund
ada2db0739 Clean up shadow variable 2024-07-02 19:20:59 +02:00
Martin Lund
14ee38a0d9 Clean up readline code 2024-07-02 19:13:14 +02:00
Martin Lund
2c700a90b0 Code cleanup 2024-07-02 19:11:40 +02:00
Martin Lund
da04c2c444 Improve listing of long device names 2024-07-02 17:41:49 +02:00
Martin Lund
5f70b75e90 Fix listing of serial devices on macOS 2024-07-01 23:11:58 +02:00
Martin Lund
02cac07a77 Bump version 2024-06-29 12:33:55 +02:00
Martin Lund
a3a6b5127f Update NEWS 2024-06-29 12:22:35 +02:00
Martin Lund
ef6fa8030e Update version date 2024-06-29 12:19:54 +02:00
Martin Lund
561376696b Clarify input and output direction of map flags 2024-06-29 12:17:57 +02:00
Martin Lund
d34fa1c1ad Rename mapping flag MSB2LSB to IMSB2LSB
This is the correct naming since we are changing the input bit order on
input from the serial device.
2024-06-29 12:14:34 +02:00
Martin Lund
9022f51ea5 Update NEWS 2024-06-28 11:51:40 +02:00
Martin Lund
4723cc3f4e Add OIGNCR mapping flag
Ignores CR on output to serial device.
2024-06-27 20:09:25 +02:00
Martin Lund
8c471105fe Fix line input mode ignoring characters ABCD 2024-06-27 18:54:19 +02:00
Martin Lund
b9902bbd78 Fix tainted print 2024-06-27 18:43:16 +02:00
Jakob Haufe
c5afd86b9a Fix typos 2024-06-15 17:16:23 +02:00
Martin Lund
b756d2e1f1 Bump version 2024-06-15 16:46:20 +02:00
Martin Lund
5c7d81b900 Update version date 2024-06-15 16:21:16 +02:00
Martin Lund
7e9574f98d Update NEWS 2024-06-15 16:20:44 +02:00
Martin Lund
053ae53f19 Update configuration output 2024-06-15 16:00:58 +02:00
Martin Lund
8f77ad5830 Clean up script run interaction text 2024-06-15 15:58:08 +02:00
Martin Lund
be4fc0908f Fix unbounded writes 2024-06-15 15:06:10 +02:00
Martin Lund
6ec2ac19d0 Add history and editing feature to line input mode
Use up and down arrow keys to navigate history.

Use left and right arrow keys to move cursor back and forth.

We try mimic the behaviour of GNU readline which we can not use because
we also need to react to key commands.
2024-06-15 14:31:23 +02:00
Martin Lund
bed34a0b9a Reuse socket address
To avoid having to wait for socket timeout when restarting server.
2024-06-11 15:18:38 +02:00
Martin Lund
134038c1ce Fix line input mode
Fix so that ABCD are no longer ignored.
2024-06-09 13:03:59 +02:00
Martin Lund
003b2e37d4 Make sure ICRNL, IGNCR, INLCR take effect 2024-06-02 23:35:36 +02:00
Martin Lund
8014ef68c0 Cleanup parsing of mapping flags 2024-06-02 14:26:39 +02:00
Vyacheslav Patkov
563c4fa6ea Show current mappings in the configuration printout 2024-06-02 13:22:33 +02:00
Vyacheslav Patkov
d1d6b45e8e Use "ctrl-t m" to change mappings interactively 2024-06-02 13:22:33 +02:00
Vyacheslav Patkov
f148a1413c Prompt for Lua script or shell command in interactive session 2024-06-01 16:23:51 +02:00
Martin Lund
bb3636e2d5 Update TODO 2024-05-30 12:17:00 +02:00
Martin Lund
133789517a Add group write permission to xymodem received file 2024-05-29 09:26:08 +02:00
Martin Lund
7e0bd980f2 Fix missing open() flags in xymodem_receive() 2024-05-29 01:41:14 +02:00
Martin Lund
883acbaa4b Update AUTHORS 2024-05-29 01:24:02 +02:00
Eliot Alan Foss
d10e762a7d Added support to receive XMODEM-CRC files from the connected serial port. 2024-05-29 01:23:00 +02:00
Martin Lund
94e40d82f3 Update README 2024-05-27 12:02:41 +02:00
Martin Lund
9315cf6a55 Update README 2024-05-27 10:10:36 +02:00
Martin Lund
eb9726bbcc Update AUTHORS 2024-05-22 18:54:44 +02:00
Martin Lund
4014fc4b3e Update README 2024-05-17 11:59:10 +02:00
Martin Lund
ccc01433b7 Include correct header for poll() 2024-05-16 19:16:17 +02:00
Martin Lund
ee3687430b Bump version 2024-05-15 09:16:13 +02:00
Martin Lund
6f6038ebcd Update plain text man page 2024-05-15 08:35:45 +02:00
Martin Lund
0921796054 Update version date 2024-05-15 08:35:16 +02:00
Martin Lund
dba4690a88 Update NEWS 2024-05-15 00:13:34 +02:00
Martin Lund
b3aac7b182 Update plain text man page 2024-05-15 00:13:03 +02:00
Martin Lund
5b5248929e Force destructive backspace when using local echo
Only takes effect in normal output mode.
2024-05-14 23:22:05 +02:00
Martin Lund
8f45d6f688 Fix local-echo in configuration file 2024-05-14 22:20:59 +02:00
Martin Lund
37f8b4fd1b Update workflow name 2024-05-13 14:07:14 +02:00
Martin Lund
5f5a8a9cdd Update README 2024-05-13 14:01:44 +02:00
Martin Lund
3f616a47c8 Fix includes 2024-05-13 11:04:09 +02:00
Martin Lund
694524cb6f Run CodeQL on push 2024-05-13 11:01:24 +02:00
Martin Lund
747ac62733 Fix includes 2024-05-13 10:57:27 +02:00
Martin Lund
c76a4d0172 Fix includes 2024-05-12 08:57:59 +02:00
Martin Lund
d2dd9f5a5b Update README 2024-05-11 16:44:12 +02:00
Martin Lund
ae9c8edbca Clean up includes 2024-05-11 10:50:01 +02:00
Martin Lund
f71ffeabb7 Force socket write operation to ignore any signals 2024-05-10 14:29:05 +02:00
Martin Lund
6ebd50ab85 Bump version 2024-05-08 13:37:43 +02:00
Martin Lund
241ff93bf4 Man page cleanup 2024-05-08 13:37:05 +02:00
Martin Lund
2d17624ddb Update version date 2024-05-08 13:05:47 +02:00
Martin Lund
ce70f43113 Update man page 2024-05-08 13:04:08 +02:00
Martin Lund
f825363606 Update NEWS 2024-05-08 13:00:44 +02:00
Martin Lund
86f1b3881d Fix shadow variable 2024-05-08 10:31:32 +02:00
Martin Lund
c2f910ffe3 Bump version 2024-05-07 21:49:23 +02:00
Martin Lund
9066523229 Do not print error when using --list with broken config file 2024-05-07 19:08:10 +02:00
Martin Lund
016c81291e Clean up completion script 2024-05-07 14:48:57 +02:00
Martin Lund
e75e19eb00 Add option '--exec <command>' for running shell command
Runs shell command with I/O redirected to device.
2024-05-07 14:38:31 +02:00
Martin Lund
545d473220 Make sure all error output is directed to stderr 2024-05-07 14:21:43 +02:00
Martin Lund
873bd6973d Fix shadow variables 2024-05-07 09:50:07 +02:00
Martin Lund
9320f54a73 Update plaintext man page 2024-05-07 09:19:51 +02:00
Martin Lund
68e2042fd6 Update man page 2024-05-07 09:12:15 +02:00
Martin Lund
b490233988 Fix build on older GNU/Linux systems without statx 2024-05-05 21:02:28 +02:00
Martin Lund
c2ef2fced5 Add codefactor.io shield 2024-05-04 14:07:55 +02:00
Martin Lund
3a75b098d1 Add new build shields to README 2024-05-04 13:35:40 +02:00
Martin Lund
242a2ea843 Fix line ending in --list output 2024-05-04 13:16:28 +02:00
Martin Lund
ce736c267a Update README 2024-05-03 18:25:17 +02:00
Martin Lund
c88cd3c5f3 Print location of configuratin file in --list output 2024-05-03 18:23:53 +02:00
Martin Lund
59940b3311 Update README 2024-05-03 15:45:39 +02:00
Martin Lund
05785e82b3 Update README 2024-05-03 15:39:52 +02:00
Martin Lund
50253a6a77 Update README 2024-05-03 15:36:53 +02:00
Martin Lund
13f3bedb2f Update README 2024-05-03 15:31:05 +02:00
Martin Lund
6310a9fabc Fix alignment of profile listing 2024-05-03 15:26:55 +02:00
Martin Lund
eb087713a4 Update README 2024-05-03 15:26:11 +02:00
Martin Lund
3e81f36dce Update version date 2024-05-03 14:20:57 +02:00
Martin Lund
fe4e47219e Update NEWS 2024-05-03 14:18:03 +02:00
Martin Lund
ec8f63f06d Improve --list feature on non-linux platform 2024-05-03 14:12:47 +02:00
Martin Lund
5ec33f5d4d Update doc 2024-05-03 11:57:26 +02:00
Martin Lund
adafa00b87 Update NEWS 2024-05-03 11:38:00 +02:00
Martin Lund
31647a934c List available profiles in --list output 2024-05-03 11:35:48 +02:00
Martin Lund
e9c96c5456 Update NEWS 2024-05-03 10:47:01 +02:00
Martin Lund
3b3fca2e8b Always message when saving log file 2024-05-03 10:45:49 +02:00
Martin Lund
a53a4f44de Update NEWS 2024-05-03 10:36:14 +02:00
Martin Lund
60caede5dd Add support for using TID as device in config file 2024-05-03 10:32:33 +02:00
Martin Lund
eae7f8f8d7 Update NEWS 2024-05-03 10:02:44 +02:00
Martin Lund
04dfa682c9 Fix use of invalid flag with regexec() 2024-05-03 09:19:21 +02:00
Martin Lund
62a4a93dec Fix potential buffer overflow in match_and_replace() 2024-05-03 08:39:58 +02:00
Martin Lund
7aa2d3fee2 Fix profile autocompletion 2024-05-03 02:12:12 +02:00
Martin Lund
22bcfdc29f Remove inih dependency from CI builds 2024-05-02 23:44:26 +02:00
Martin Lund
0e9dbcbc77 Replace use of stat() with fstat()
For better security.
2024-05-02 22:30:47 +02:00
Martin Lund
68c78222e1 Fix hexN output mode 2024-05-02 21:50:48 +02:00
Martin Lund
22b4f451ea Update pattern matching example 2024-05-02 20:42:10 +02:00
Martin Lund
15ba034ce5 Fix submenu response when invalid key hit 2024-05-02 19:38:14 +02:00
Martin Lund
17bb6edfd2 Bump version 2024-05-02 18:52:52 +02:00
Martin Lund
65c5a068d8 Replace inih with glib key file parser
After including the use of glib we might as well replace inih
with the glib key file parser.

All configuraiton file parsing has been reworked and also the options
parsing has been cleaned up, resulting in better and stricter
configuration file and option value checks.

Compared to old, configuration files now requires any default
configurations to be put in a group/section named [default].

Configuration file keywords such as "enable", "disable", "on",
"off", "yes", "no", "0", "1" have been retired. Now only "true" and
"false" apply to boolean configuration options. This is done to simplify
things and avoid any confusion.

The pattern option feature has been reworked so now the user can now
access the full match string and any matching subexpression using the
%mN syntax.

For example:

[usb devices]
pattern = usb([0-9]*)
device = /dev/ttyUSB%m1

Then when using tio:
$ tio usb12

   %m0 = 'usb12'  // Full match string
   %m1 = 12       // First match subexpression

Which results in device = /dev/ttyUSB12
2024-05-02 18:35:37 +02:00
Martin Lund
68d3b845b2 Remove CircleCI
Replaced with github workflow CI.
2024-04-30 13:40:07 +02:00
Martin Lund
22f030ebb8 Add github workflow for Ubuntu build 2024-04-30 13:35:46 +02:00
Rui Chen
f5703ff107 remove verbose for meson install and use system glib and pkg-config
Signed-off-by: Rui Chen <rui@chenrui.dev>
2024-04-30 10:02:19 +02:00
Rui Chen
3b77eb35cf fix: add build patch for FNM_EXTMATCH
run into the following build failure

```
cc -Isrc/tio.p -Isrc -I../src -I/opt/homebrew/Cellar/glib/2.80.0_2/include/glib-2.0 -I/opt/homebrew/Cellar/glib/2.80.0_2/lib/glib-2.0/include -I/opt/homebrew/opt/gettext/include -I/opt/homebrew/Cellar/pcre2/10.43/include -I/opt/homebrew/Cellar/inih/58/include -I/opt/homebrew/include/lua -fdiagnostics-color=always -Wall -Winvalid-pch -Wextra -std=gnu99 -O3 -Wno-unused-result -DHAVE_IOSSIOSPEED -MD -MQ src/tio.p/misc.c.o -MF src/tio.p/misc.c.o.d -o src/tio.p/misc.c.o -c ../src/misc.c
../src/misc.c:201:38: error: use of undeclared identifier 'FNM_EXTMATCH'
        if (fnmatch(pattern, string, FNM_EXTMATCH) == 0)
                                     ^
1 error generated.
```

Signed-off-by: Rui Chen <rui@chenrui.dev>
2024-04-30 10:02:19 +02:00
Rui Chen
054326454b feat: add macos workflow
Signed-off-by: Rui Chen <rui@chenrui.dev>
2024-04-30 10:02:19 +02:00
Rui Chen
b763f1289b fix: add macos build patch for fs_get_creation_time
Signed-off-by: Rui Chen <rui@chenrui.dev>
2024-04-29 20:21:51 +02:00
Martin Lund
8ead9337d1 Enable extended pattern matching
So that the exclude options can also work as include using special
pattern syntax.

For example, to only include /dev/ttyUSB* devices simply do:

$ tio --exclude-devices=!(/dev/ttyUSB*) --list

See the man page of fnmatch() for all available extended pattern
options.
2024-04-29 19:36:44 +02:00
Martin Lund
d8fb141bc4 Update lua read() description 2024-04-29 16:47:00 +02:00
Martin Lund
a698799a7d Update man page 2024-04-29 16:22:01 +02:00
Martin Lund
5e3722a10e Update man page 2024-04-29 16:09:37 +02:00
Martin Lund
c16a2a1f94 Update README 2024-04-29 16:05:14 +02:00
Martin Lund
e2960c3f82 Update README 2024-04-29 16:00:12 +02:00
Martin Lund
08404e700f Update NEWS 2024-04-29 15:48:00 +02:00
Martin Lund
6d77201ba0 Simplify lua line manipulation API
Collapses lua high(), low(), toggle(), config_high(), config_low(),
config_apply() into one simple function:

set{<line>=<state>, ...}

Line can be any of DTR, RTS, CTS, DSR, CD, RI.

State is high, low, or toggle.

Example:
script = set{DTR=high, RTS=low}; msleep(100); set{DTR=low, RTS=high}; msleep(100); set{RTS=low}

Notice the use of {} instad of () when calling the set function. This is
required to pass parameters by name in lua.
2024-04-29 15:20:53 +02:00
Martin Lund
eef0a15194 Unshadow variable 2024-04-29 01:49:44 +02:00
Martin Lund
6895c05321 Update README 2024-04-28 18:23:08 +02:00
Martin Lund
c453728ab5 Update README 2024-04-28 17:26:10 +02:00
Martin Lund
273afb73f4 Update README 2024-04-28 17:22:26 +02:00
Martin Lund
84dd4c3685 Update README 2024-04-28 14:47:07 +02:00
Martin Lund
33eae0c30d Update script API 2024-04-28 14:46:41 +02:00
Martin Lund
9bc93991e7 Update NEWS 2024-04-27 23:51:01 +02:00
Martin Lund
ed70a587ec Update NEWS 2024-04-27 19:30:24 +02:00
Martin Lund
fe2973522e Fix shadow variable 2024-04-27 19:11:37 +02:00
Martin Lund
f5f62ee02d Disable DEC Special Graphics at exit if vt100
If a vt100 terminal receives the Shift In character '\016' it will
enable the 7 bit DEC Special Graphics character set used for line drawing.

For most users this can happen due to line noise from the tty device and
will likely mess up your terminal even after tio exits.

To better handle this we want to make sure that tio disables this mode
by sending the Shift Out character '\017' at exit.

This mechanism will only activate if environment variable TERM assumes
value "vt100".
2024-04-27 17:32:48 +02:00
Martin Lund
cd160250a6 Cleanup 2024-04-27 17:13:06 +02:00
Martin Lund
588ac3e8ac Update TODO 2024-04-27 15:45:34 +02:00
Martin Lund
76de9b890e Update README 2024-04-27 15:33:22 +02:00
Martin Lund
42ff234204 Add hexN output mode
Adds support for hexN mode where N is a number in the range 1 to 4096
which defines how many hex values will be printed before a line break.

In short, it defines the width of the hex output.

In this mode, if timestamps are enabled they will be added to each hex
line.
2024-04-27 15:25:34 +02:00
Martin Lund
4113a072c2 Make sure to reset tainted state 2024-04-27 14:41:08 +02:00
Martin Lund
232cbee697 Rename sub-config to profile
Because better naming.
2024-04-27 09:13:33 +02:00
Martin Lund
1b77ed783b Update README 2024-04-27 02:30:22 +02:00
Martin Lund
41b8e4f99c Update README 2024-04-27 02:03:12 +02:00
Martin Lund
c61d56935b Fix excludes pattern matching 2024-04-27 01:31:43 +02:00
Martin Lund
6e779a0520 Use lua io.write() instead of print()
io.write() gives better output control as print() is hardcoded to always
print a newline.
2024-04-27 00:48:38 +02:00
Martin Lund
d8fbd607d4 Update README 2024-04-26 22:58:24 +02:00
Martin Lund
01f3c391f0 Update README 2024-04-26 22:54:25 +02:00
Martin Lund
5bbdf3b9f8 Update README 2024-04-26 22:38:55 +02:00
Martin Lund
b4741de50c Bump version 2024-04-26 22:28:27 +02:00
Martin Lund
d19ba1c492 Add new ways to manage serial devices
* Rename --list-devices to --list

 * Rename --no-autoconnect to --no-reconnect

 * Switch -l and -L options

   * -l now lists available serial devices

   * -L enables log to file

 * Add option --auto-connect <strategy>

   * Supported strategies:

     * "new" - Waits to connect first new appearing serial device

     * "latest" - Connects to latest registered serial device

     * "direct" - Connect directly to specified serial device (default)

 * Add options to exclude serial devices from auto connect strategy by
   pattern

   * Supported exclude options:

     * --exclude-devices <pattern>

       Example: '--exclude-devices "/dev/ttyUSB2,/dev/ttyS?"'

     * --exclude-drivers <pattern>

       Example: '--exclude-drivers "cdc_acm"'

     * --exclude-tids <pattern>

       Example: '--exclude-tids "yW07,bCC2"'

     * Patterns support '*' and '?'

 * Connect to same port/device combination via unique topology ID (TID)

   * Topology ID is a 4 digit base62 encoded hash of a device topology
     string coming from the Linux kernel. This means that whenever you
     plug in the same e.g. USB serial port device to the same USB hub
     port connected via the exact same hub topology all the way to your
     computer, you will get the same unique TID.

   * Useful for stable reconnections when serial device has no serial
     device by ID

   * For now, only tested on Linux.

 * Reworked and improved listing of serial devices to show serial devices:

   * By device

     * Including TID, uptime, driver, and description.

     * Sorted by uptime (newest device listed last)

   * By unique topology ID

   * By ID

   * By path

 * Add script interface 'list = tty_search()' for searching for serial
   devices.
2024-04-26 22:19:22 +02:00
Martin Lund
ae76f8f58d Clean up timestamp enum definition 2024-04-20 15:02:43 +02:00
Martin Lund
b05f38abd0 Add missing options to show configuration 2024-04-20 14:51:45 +02:00
Martin Lund
51bfa68bdd Text cleanup 2024-04-20 14:03:38 +02:00
Martin Lund
fa4207ddfd Update description of mute option 2024-04-19 20:14:22 +02:00
Martin Lund
3ca6a66d9b Add lua read_string() function 2024-04-19 17:10:36 +02:00
Martin Lund
f1c5394570 Don't forget to log output in lua expect() 2024-04-19 16:57:27 +02:00
Martin Lund
0105346a11 Cleanup 2024-04-19 16:56:30 +02:00
Martin Lund
96fafc5fb4 Generalize automatic login example for Linux 2024-04-19 14:44:54 +02:00
Davis C
29546bb13a Updated login example with new expect logic 2024-04-19 14:41:30 +02:00
Martin Lund
b0e9fa02e8 Fix log output in hex output mode 2024-04-18 18:55:08 +02:00
Martin Lund
f257b7fba5 Update README 2024-04-18 16:23:41 +02:00
Martin Lund
6fff4939e4 Add timeout based timestamps in hex output mode
This change reintroduces timestamping in hex output mode but based on
timeout instead of new lines which made no sense. This means that
timestamps will only be printed when timeout time has elapsed with no
output activity from serial device.

Adds option --timestamp-timeout <ms> for setting the timeout value in
milliseconds.

Defaults to 200 ms.
2024-04-18 15:52:45 +02:00
Martin Lund
a8e0d2693d Do not echo CR in line input and hex output mode 2024-04-18 14:44:39 +02:00
Martin Lund
c440da2ea8 Improve switched messages 2024-04-18 13:35:09 +02:00
Martin Lund
f1144ca5cc Cleanup 2024-04-18 13:29:06 +02:00
Martin Lund
3cc2d90fda Extend lua expect() to also return matched string 2024-04-17 23:38:21 +02:00
Martin Lund
48c9e8a9a9 Update AUTHORS 2024-04-17 18:30:53 +02:00
Martin Lund
f4c4387e05 Add automatic login script example 2024-04-17 18:27:46 +02:00
Martin Lund
a1987b61b4 Organize examples directory 2024-04-17 18:24:33 +02:00
Martin Lund
f8924182d3 Update TODO 2024-04-17 18:15:51 +02:00
Davis C
d0e95c5fba Reset buffer size at start of expect 2024-04-17 18:13:00 +02:00
Davis C
1cefb7b6bc Revert "Added reset_buffer()"
This reverts commit ee56d1280d.
2024-04-17 18:13:00 +02:00
Davis C
794c5202f4 Added reset_buffer() 2024-04-17 18:13:00 +02:00
Davis C
98653566a8 Return 1 when expect matches 2024-04-17 18:13:00 +02:00
Martin Lund
a605533213 Fix local echo in line mode 2024-04-17 16:17:20 +02:00
Martin Lund
1e20948d83 Fix line input mode
Do not forward input characters to tty device before a line is input via
carriage return.
2024-04-16 19:38:45 +02:00
Martin Lund
4801816357 Introduce basic line input mode 2024-04-16 17:42:34 +02:00
Martin Lund
d60363a64c Cleanup global variable name shadowing 2024-04-15 11:21:36 +02:00
Martin Lund
5c45150f58 Bump version 2024-04-14 18:43:58 +02:00
Martin Lund
d3993da6d4 Update AUTHORS 2024-04-14 18:05:01 +02:00
Martin Lund
00f3ea9b7f Update NEWS 2024-04-14 18:04:51 +02:00
Martin Lund
e8114ca0a4 Update version date 2024-04-14 18:04:31 +02:00
Martin Lund
de0a7c547f Rework resolve_config_file() 2024-04-14 12:52:28 +02:00
Martin Lund
76a7a56e85 Rework line_pulse_duration_option_parse()
Introduce proper sscanf() checks.
2024-04-14 11:43:23 +02:00
Martin Lund
9744fcafcf Rework rs485_parse_config()
Introduce proper sscanf() checks.
2024-04-14 11:30:39 +02:00
Martin Lund
c4878a90d7 Clean up file descriptor name shadowing 2024-04-14 10:42:22 +02:00
Martin Lund
ae461dc296 Add missing header guard 2024-04-14 10:23:43 +02:00
Martin Lund
10eedd4ad2 Update README 2024-04-14 02:32:25 +02:00
Martin Lund
fa41771e65 Update README 2024-04-14 01:50:50 +02:00
Martin Lund
a1a4dc4642 Update plain text man page 2024-04-14 01:42:43 +02:00
Martin Lund
7dbb806311 Upgrade inih subproject 2024-04-14 01:08:37 +02:00
Martin Lund
97537853a8 Remove options --response-wait, --response-timeout
Remove options and rework input handling so it is possible to do the
same thing but via script which is much more flexible.

These options were always a bit of a hardcoded solution. With the new
script expect feature we can wait for any type of response.

For example, pipe command to serial device and wait for line response within 1 second:

$ echo "*IDN?" | tio /dev/ttyACM0 --script "expect('\r\n', 1000)" --mute
2024-04-13 23:18:25 +02:00
Martin Lund
e1e3e298bf Add lua exit(code) 2024-04-13 16:46:00 +02:00
Martin Lund
c5dac4fd33 pdate README 2024-04-13 15:48:32 +02:00
Martin Lund
51300cc4f0 Add timeout feature to expect() 2024-04-13 15:30:14 +02:00
Martin Lund
3ad090caf7 Update README 2024-04-13 00:56:41 +02:00
Martin Lund
fba73f98db Update README 2024-04-13 00:11:44 +02:00
Martin Lund
2db87ede53 Update README 2024-04-12 22:21:35 +02:00
Martin Lund
fca76a017d Fix text alignment 2024-04-12 21:15:36 +02:00
Martin Lund
7915c1a445 Update README 2024-04-12 18:51:17 +02:00
Martin Lund
6c75ec553d Update README 2024-04-12 18:36:31 +02:00
Martin Lund
fc54df1f22 Add lua expect(string)
Add simple expect functionality.

The expect(string) function will wait for input from the tty device and
only return when there is a string match. Regular expressions are
supported.

Example:

script = expect('password:'); send('my_password\n')
2024-04-12 18:20:33 +02:00
Martin Lund
0afae5d3ee Update text 2024-04-12 13:03:51 +02:00
Martin Lund
e028544cd0 Update plain text man page 2024-04-12 00:24:01 +02:00
Martin Lund
5eb649278a Clean up man page 2024-04-12 00:23:04 +02:00
Martin Lund
418a43d96e Add lua send(string) 2024-04-12 00:08:45 +02:00
Martin Lund
00c8124a0a Add lua modem_send(file,protocol) 2024-04-11 21:20:50 +02:00
Martin Lund
78f96bd32c Fix xymodem error print outs 2024-04-10 20:13:44 +02:00
Martin Lund
d8fb1ab0ca Rework x/y-modem transfer command
Remove ctrl-t X optin and instead introduce submenu to ctrl-t x option
for picking which xmodem protocol to use.
2024-04-10 19:56:21 +02:00
Martin Lund
a208c9908a Update README 2024-04-10 15:05:29 +02:00
Martin Lund
2b6a79b9f0 Cleanup options 2024-04-10 15:04:47 +02:00
Martin Lund
2fff4d36d0 Add independent input and output mode
Replaces -x, --hexadecimal option with --intput-mode and --output-mode
so it is possible to select hex or normal mode for both input and output
independently.

To obtain same behaviour as -x, --hexadecimal use the following
configuration:

input-mode = hex
output-mode = hex
2024-04-10 14:40:18 +02:00
HiFiPhile
fd6a246908 Add manpage. 2024-04-09 14:58:17 +02:00
HiFiPhile
901e00bba3 Poll on serial port read instead of delay. 2024-04-09 14:58:17 +02:00
Mengsk
eea46a2005 Add Xmodem-CRC support. 2024-04-09 14:58:17 +02:00
Martin Lund
2ee1f5c224 Update README 2024-04-07 12:40:47 +02:00
Martin Lund
3e50191107 Add tty line configuration script API
On some platforms calling high()/low() to switch line states result in
costly system calls whick makes it impossible to swith two or more tty
lines simultaneously.

To help solve this timing issue we introduce a tty line state
configuration API which can be used instead of using
high()/low().

Using config_low(line) and config_high(line) one can set up a new line
state configuration for multiple lines and then use config_apply() to
finally apply the configuration. This will result in only one system
call to instruct the serial port drive to switch all the configured line
states which should help ensure that the lines are switched
simultaneously.

Example:

script = config_high(DTR); config_low(RTS); config_apply()
2024-04-06 09:34:25 +02:00
Martin Lund
4369d5b66f Add ONULBRK mapping flag
Add ONULBRK mapping to map nul (zero) to send break signal on output.

This is useful if one needs to e.g. send the break signal to the tty
device when connected via socket.
2024-04-05 13:55:51 +02:00
Martin Lund
70913fe120 Add --log-directory option
For specifying directory path in which to save automatically named log
files.
2024-04-04 12:31:55 +02:00
Martin Lund
83f826349b Update codeql config 2024-04-02 03:52:54 +02:00
Martin Lund
00f57c9992 Update circleCI 2024-04-01 15:55:03 +02:00
Martin Lund
0becfa3274 Add Lua scripting feature
Add support for running Lua scripts that can manipulate the tty control
lines. Script is activated automatically on connect or manually via in
session key command.

The Lua scripting feature opens up for many posibilities in the future
such as adding expect like functionality to easily and programatically
interact with the connected device.
2024-04-01 15:37:40 +02:00
Martin Lund
6fee8514f4 Invert line states to reflect true electrical level 2024-03-30 16:31:45 +01:00
Martin Lund
10255d52d0 Update AUTHORS 2024-03-24 11:21:42 +01:00
Mingjie Shen
6720da3b88 Check return values of sscanf()
Failing to check that a call to 'sscanf' actually writes to an output
variable can lead to unexpected behavior at reading time.
2024-03-24 11:03:51 +01:00
Jakob Haufe
ed4ac0c797 Support NO_COLOR env variable as per no-color.org 2024-03-06 13:38:59 +01:00
Martin Lund
d45c9b1a22 Update TODO 2024-03-01 12:28:51 +01:00
Martin Lund
91459c2490 Update AUTHORS 2024-02-19 16:51:24 +01:00
Martin Lund
593f9495f4 Add support for disabling prefix key handling
To disable prefix key input handing simply set prefix-ctrl-key to
'none'.

Based on original patch from Sebastian Krahmer.
2024-02-19 16:51:02 +01:00
Martin Lund
6c520090c6 Add meson man pages install option
Defaults to installing man pages.
2024-02-13 13:49:29 +01:00
Martin Lund
67640d4a00 Update AUTHORS 2024-02-12 22:25:07 +01:00
Fredrik Svedberg
58c9489b92 Add map FF to ESC-c on input
Added map of form feed to ESC-c on input for terminals that
do not clear screen on ^L but do on ESC-c.
2024-02-12 22:22:20 +01:00
Brian
553b67e406
Add CodeQL Workflow for Code Security Analysis (#220)
* Add CodeQL Workflow for Code Security Analysis

Add CodeQL Workflow for Code Security Analysis

This pull request introduces a CodeQL workflow to enhance the security analysis of our repository. CodeQL is a powerful static analysis tool that helps identify and mitigate security vulnerabilities in our codebase. By integrating this workflow into our GitHub Actions, we can proactively identify and address potential issues before they become security threats.

We added a new CodeQL workflow file (.github/workflows/codeql.yml) that
- Runs on every push and pull request to the main branch.
- Excludes queries with a high false positive rate or low-severity findings.
- Does not display results for third-party code, focusing only on our own codebase.

Testing:
To validate the functionality of this workflow, we have run several test scans on the codebase and reviewed the results. The workflow successfully compiles the project, identifies issues, and provides actionable insights while reducing noise by excluding certain queries and third-party code.

Deployment:
Once this pull request is merged, the CodeQL workflow will be active and automatically run on every push and pull request to the main branch. To view the results of these code scans, please follow these steps:
1. Under the repository name, click on the Security tab.
2. In the left sidebar, click Code scanning alerts.

Additional Information:
- You can further customize the workflow to adapt to your specific needs by modifying the workflow file.
- For more information on CodeQL and how to interpret its results, refer to the GitHub documentation and the CodeQL documentation.

Signed-off-by: Brian <bayuan@purdue.edu>

* Add CodeQL Workflow for Code Security Analysis

Add CodeQL Workflow for Code Security Analysis

This pull request introduces a CodeQL workflow to enhance the security analysis of our repository. CodeQL is a powerful static analysis tool that helps identify and mitigate security vulnerabilities in our codebase. By integrating this workflow into our GitHub Actions, we can proactively identify and address potential issues before they become security threats.

We added a new CodeQL workflow file (.github/workflows/codeql.yml) that
- Runs on every pull request (functionality to run on every push to main branches is included as a comment for convenience).
- Runs daily.
- Excludes queries with a high false positive rate or low-severity findings.
- Does not display results for git submodules, focusing only on our own codebase.

Testing:
To validate the functionality of this workflow, we have run several test scans on the codebase and reviewed the results. The workflow successfully compiles the project, identifies issues, and provides actionable insights while reducing noise by excluding certain queries and third-party code.

Deployment:
Once this pull request is merged, the CodeQL workflow will be active and automatically run on every push and pull request to the main branch. To view the results of these code scans, please follow these steps:
1. Under the repository name, click on the Security tab.
2. In the left sidebar, click Code scanning alerts.

Additional Information:
- You can further customize the workflow to adapt to your specific needs by modifying the workflow file.
- For more information on CodeQL and how to interpret its results, refer to the GitHub documentation and the CodeQL documentation (https://codeql.github.com/ and https://codeql.github.com/docs/).

Signed-off-by: Brian <bayuan@purdue.edu>

* Add CodeQL Workflow for Code Security Analysis

Add CodeQL Workflow for Code Security Analysis

This pull request introduces a CodeQL workflow to enhance the security analysis of our repository. CodeQL is a powerful static analysis tool that helps identify and mitigate security vulnerabilities in our codebase. By integrating this workflow into our GitHub Actions, we can proactively identify and address potential issues before they become security threats.

We added a new CodeQL workflow file (.github/workflows/codeql.yml) that
- Runs on every pull request (functionality to run on every push to main branches is included as a comment for convenience).
- Runs daily.
- Excludes queries with a high false positive rate or low-severity findings.
- Does not display results for git submodules, focusing only on our own codebase.

Testing:
To validate the functionality of this workflow, we have run several test scans on the codebase and reviewed the results. The workflow successfully compiles the project, identifies issues, and provides actionable insights while reducing noise by excluding certain queries and third-party code.

Deployment:
Once this pull request is merged, the CodeQL workflow will be active and automatically run on every push and pull request to the main branch. To view the results of these code scans, please follow these steps:
1. Under the repository name, click on the Security tab.
2. In the left sidebar, click Code scanning alerts.

Additional Information:
- You can further customize the workflow to adapt to your specific needs by modifying the workflow file.
- For more information on CodeQL and how to interpret its results, refer to the GitHub documentation and the CodeQL documentation (https://codeql.github.com/ and https://codeql.github.com/docs/).

Signed-off-by: Brian <bayuan@purdue.edu>

* Add CodeQL Workflow for Code Security Analysis

Add CodeQL Workflow for Code Security Analysis

This pull request introduces a CodeQL workflow to enhance the security analysis of our repository. CodeQL is a powerful static analysis tool that helps identify and mitigate security vulnerabilities in our codebase. By integrating this workflow into our GitHub Actions, we can proactively identify and address potential issues before they become security threats.

We added a new CodeQL workflow file (.github/workflows/codeql.yml) that
- Runs on every pull request (functionality to run on every push to main branches is included as a comment for convenience).
- Runs daily.
- Excludes queries with a high false positive rate or low-severity findings.
- Does not display results for git submodules, focusing only on our own codebase.

Testing:
To validate the functionality of this workflow, we have run several test scans on the codebase and reviewed the results. The workflow successfully compiles the project, identifies issues, and provides actionable insights while reducing noise by excluding certain queries and third-party code.

Deployment:
Once this pull request is merged, the CodeQL workflow will be active and automatically run on every push and pull request to the main branch. To view the results of these code scans, please follow these steps:
1. Under the repository name, click on the Security tab.
2. In the left sidebar, click Code scanning alerts.

Additional Information:
- You can further customize the workflow to adapt to your specific needs by modifying the workflow file.
- For more information on CodeQL and how to interpret its results, refer to the GitHub documentation and the CodeQL documentation (https://codeql.github.com/ and https://codeql.github.com/docs/).

Signed-off-by: Brian <bayuan@purdue.edu>

* Update codeql-buildscript.sh

---------

Signed-off-by: Brian <bayuan@purdue.edu>
2024-02-07 00:31:35 +01:00
Sylvain LAFRASSE
4269ec835d Fix double call of tty_disconnect() on macOS/Darwin. 2024-01-12 12:11:24 +01:00
Martin Lund
e572255fd2 Fix file descriptor handling on MacOS 2024-01-11 20:36:17 +01:00
Martin Lund
bfefd04b55 Update README 2023-12-10 21:43:11 +01:00
Jakob Haufe
ed66c72ca1
Fix troff warning (#216)
.eo/.ec sections seemingly need explicit empty lines using .sp

Otherwise, troff complains:

troff:<standard input>:535: warning: expected numeric expression, got '\'
troff:<standard input>:538: warning: expected numeric expression, got '\'
troff:<standard input>:541: warning: expected numeric expression, got '\'
2023-12-08 10:43:37 +01:00
Martin Lund
14f598e11e Update README 2023-09-28 12:52:01 +02:00
Martin Lund
7193e6f0c9 Bump version 2023-09-22 16:57:44 +02:00
HiFiPhile
72399c4fe6
CYGWIN: Fix port auto connection. (#211) 2023-09-22 11:53:37 +02:00
Martin Lund
1777206de7 Update plain text man page 2023-09-21 08:47:34 +02:00
Martin Lund
1c32555c2a Update NEWS 2023-09-19 21:48:58 +02:00
Martin Lund
4307e81760 Update AUTHORS 2023-09-19 21:47:55 +02:00
Martin Lund
3d7d9c85b5 Bump version 2023-09-19 21:37:05 +02:00
Martin Lund
02b60e9fb3 Revert "Make quit hint more explicit"
This reverts commit 93e49ab5a2.
2023-09-19 21:21:59 +02:00
Martin Lund
c93922fd36 Update README 2023-09-16 16:10:30 +02:00
Martin Lund
838c110876 Increase header size for ymodem 2023-09-16 15:03:38 +02:00
Martin Lund
a42f3f78d1 Overwrite old stale letters on xmodem filename input
When entering a file name, eg. 'test' it whould output the following
with 2 stale letters of the former input string:

[14:41:58.987] Send file with XMODEM
[14:42:08.015] Sending file 'test'st
[14:42:08.015] Press any key to abort transfer

To avoid this we simply overwrite the 2 stale letters with whitespaces.
2023-09-16 14:51:34 +02:00
Martin Lund
93e49ab5a2 Make quit hint more explicit
To minimize confusion for new users.
2023-09-16 14:13:27 +02:00
Martin Lund
ed0386d2c4 Re-adjust max line size
So it stays within maximum size handled by xmodem.
2023-09-16 13:14:55 +02:00
Martin Lund
63dced047f Update AUTHORS 2023-09-16 12:30:52 +02:00
Martin Lund
cf6e8b963b Clean up whitespaces 2023-09-16 12:27:34 +02:00
Martin Lund
07864a0e78 Increase line buffer size
Just to make sure we accept very long filenames.
2023-09-16 12:25:38 +02:00
Martin Lund
c9c5f03c10 Fix meson source listing 2023-09-16 12:20:34 +02:00
pnrhub
e6ffbd9058
Add xmodem and ymodem file send support (#208)
* Add xmodem and ymodem file send support
---------

Co-authored-by: pnr <pnr@home25.nl>
2023-09-16 12:17:38 +02:00
Martin Lund
812dee8e54
Merge pull request #207 from HiFiPhile/eintr
tty_stdin_input_thread(): write to pipe only if byte_count > 0.
2023-09-14 00:19:39 +02:00
HiFiPhile
d9dc1ff698 tty_stdin_input_thread(): write to pipe only if byte_count > 0. 2023-09-14 00:06:46 +02:00
Martin Lund
c01baca157
Merge pull request #206 from HiFiPhile/eintr
Ignore EINTR error.
2023-09-13 23:30:04 +02:00
HiFiPhile
5c441f22c2 Ignore EINTR error. 2023-09-13 22:45:11 +02:00
Martin Lund
8134cd3486 Update AUTHORS 2023-09-10 13:57:12 +02:00
Martin Lund
46a72d8254
Merge pull request #204 from HiFiPhile/com
CYGWIN: Add support for "COM*" naming.
2023-09-10 13:55:47 +02:00
HiFiPhile
bdca5a27ec CYGWIN: Add support for "COM*" naming. 2023-09-10 13:32:01 +02:00
Martin Lund
3c7c865e59 Update man page 2023-09-07 00:55:52 +02:00
Martin Lund
59cd3a1379 Update README 2023-08-20 23:48:43 +02:00
Martin Lund
98052936b0 Update TODO 2023-08-18 14:20:14 +02:00
Martin Lund
a486ba581b
Merge pull request #200 from weskoerber/fix/log-append-cli
fix: support --log-append in cli options
2023-07-14 09:25:41 +02:00
Wes Koerber
df5379bac5 chore: reorder log-strip and log-append
reorder to maintain consistency with documentation
2023-07-13 20:58:20 -04:00
Wes Koerber
5656381cc3 chore: update readme, bash completion, man page 2023-07-13 20:58:01 -04:00
Wes Koerber
d461751a71 fix: support --log-append in cli options
fixes: #199
2023-07-13 20:49:53 -04:00
62 changed files with 7993 additions and 1628 deletions

View file

@ -1,27 +0,0 @@
# Use the latest 2.1 version of CircleCI pipeline process engine.
# See: https://circleci.com/docs/2.0/configuration-reference
version: 2.1
# Define a job to be invoked later in a workflow.
# See: https://circleci.com/docs/2.0/configuration-reference/#jobs
jobs:
build-tio:
# Specify the execution environment. You can specify an image from Dockerhub or use one of our Convenience Images from CircleCI's Developer Hub.
# See: https://circleci.com/docs/2.0/configuration-reference/#docker-machine-macos-windows-executor
docker:
- image: cimg/base:edge
# Add steps to the job
# See: https://circleci.com/docs/2.0/configuration-reference/#steps
steps:
- checkout
- run: sudo apt-get -qq update
- run: sudo apt-get install -y bash-completion git meson libinih-dev
- run: git clone https://github.com/tio/tio.git
- run: cd tio && meson build --prefix $HOME/test/tio && ninja -C build install
# Invoke jobs via workflows
# See: https://circleci.com/docs/2.0/configuration-reference/#workflows
workflows:
build-tio-workflow:
jobs:
- build-tio

5
.clang-format Normal file
View file

@ -0,0 +1,5 @@
BasedOnStyle: llvm
IndentWidth: 4
AllowShortFunctionsOnASingleLine: None
KeepEmptyLinesAtTheStartOfBlocks: false
BreakBeforeBraces: Allman

View file

@ -0,0 +1,7 @@
#!/usr/bin/env bash
pip3 install meson -U
pip3 install ninja -U
sudo apt-get install -y liblua5.2-dev libglib2.0-dev
meson setup build
meson compile -C build

126
.github/workflows/codeql.yml vendored Normal file
View file

@ -0,0 +1,126 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ "main", "master" ]
schedule:
- cron: '0 0 * * *'
pull_request:
branches: '*'
jobs:
analyze:
name: Analyze
# Runner size impacts CodeQL analysis time. To learn more, please see:
# - https://gh.io/recommended-hardware-resources-for-running-codeql
# - https://gh.io/supported-runners-and-hardware-resources
# - https://gh.io/using-larger-runners
# Consider using larger runners for possible analysis time improvements.
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-22.04' }}
timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }}
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'cpp' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby', 'swift' ]
# Use only 'java' to analyze code written in Java, Kotlin or both
# Use only 'javascript' to analyze code written in JavaScript, TypeScript or both
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
submodules: recursive
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
queries: security-and-quality
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift).
# If this step fails, then you should remove it and run the build manually (see below)
#- name: Autobuild
# uses: github/codeql-action/autobuild@v3
# Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
# If the Autobuild fails above, remove it and uncomment the following three lines.
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
- run: |
./.github/workflows/codeql-buildscript.sh
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
with:
category: "/language:${{matrix.language}}"
upload: false
id: step1
# Filter out rules with low severity or high false positve rate
# Also filter out warnings in third-party code
- name: Filter out unwanted errors and warnings
uses: advanced-security/filter-sarif@v1
with:
patterns: |
-**:cpp/path-injection
-**:cpp/world-writable-file-creation
-**:cpp/poorly-documented-function
-**:cpp/potentially-dangerous-function
-**:cpp/use-of-goto
-**:cpp/integer-multiplication-cast-to-long
-**:cpp/comparison-with-wider-type
-**:cpp/leap-year/*
-**:cpp/ambiguously-signed-bit-field
-**:cpp/suspicious-pointer-scaling
-**:cpp/suspicious-pointer-scaling-void
-**:cpp/unsigned-comparison-zero
-**/cmake*/Modules/**
input: ${{ steps.step1.outputs.sarif-output }}/cpp.sarif
output: ${{ steps.step1.outputs.sarif-output }}/cpp.sarif
- name: Upload CodeQL results to code scanning
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: ${{ steps.step1.outputs.sarif-output }}
category: "/language:${{matrix.language}}"
- name: Upload CodeQL results as an artifact
if: success() || failure()
uses: actions/upload-artifact@v4
with:
name: codeql-results
path: ${{ steps.step1.outputs.sarif-output }}
retention-days: 5
- name: Fail if an error is found
run: |
./.github/workflows/fail_on_error.py \
${{ steps.step1.outputs.sarif-output }}/cpp.sarif

34
.github/workflows/fail_on_error.py vendored Executable file
View file

@ -0,0 +1,34 @@
#!/usr/bin/env python3
import json
import sys
# Return whether SARIF file contains error-level results
def codeql_sarif_contain_error(filename):
with open(filename, 'r') as f:
s = json.load(f)
for run in s.get('runs', []):
rules_metadata = run['tool']['driver']['rules']
if not rules_metadata:
rules_metadata = run['tool']['extensions'][0]['rules']
for res in run.get('results', []):
if 'ruleIndex' in res:
rule_index = res['ruleIndex']
elif 'rule' in res and 'index' in res['rule']:
rule_index = res['rule']['index']
else:
continue
try:
rule_level = rules_metadata[rule_index]['defaultConfiguration']['level']
except IndexError as e:
print(e, rule_index, len(rules_metadata))
else:
if rule_level == 'error':
return True
return False
if __name__ == "__main__":
if codeql_sarif_contain_error(sys.argv[1]):
sys.exit(1)

30
.github/workflows/macos.yml vendored Normal file
View file

@ -0,0 +1,30 @@
name: MacOS build
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
build:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Install dependencies
run: |
brew install meson ninja lua
- name: Build
run: |
meson setup build
meson compile -C build --verbose
meson install -C build

31
.github/workflows/ubuntu.yml vendored Normal file
View file

@ -0,0 +1,31 @@
name: Ubuntu build
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Install dependencies
run: |
sudo apt update
sudo apt install -y bash-completion git meson liblua5.2-dev libglib2.0-dev
- name: Build
run: |
meson setup build --prefix $HOME/opt/tio
meson compile -C build --verbose
meson install -C build

2
.typos.toml Normal file
View file

@ -0,0 +1,2 @@
[default]
extend-ignore-words-re = ["tio"]

21
AUTHORS
View file

@ -45,5 +45,26 @@ Vyacheslav Patkov <slava@patkov.ru>
Bill Hass <billhass@umich.edu> Bill Hass <billhass@umich.edu>
Peter van Dijk <peter@7bits.nl> Peter van Dijk <peter@7bits.nl>
Braden Young <braden@somewearlabs.com> Braden Young <braden@somewearlabs.com>
Wes Koerber <wkoerber@acsd4u.com>
HiFiPhile <admin@hifiphile.com>
Paul Ruizendaal <pnr@planet.nl>
Fredrik Svedberg <fredrik@svedberg.us>
Sebastian <sebastian.krahmer@gmail.com>
Mingjie Shen <shen497@purdue.edu>
Brian <bayuan@purdue.edu>
Davis C <davisclaib@gmail.com>
KhazAkar <damianzrb@zohomail.eu>
Eliot Alan Foss <eliotfoss@gmail.com>
Robert Lipe <robertlipe@gpsbabel.org>
Heinrich Schuchardt <heinrich.schuchardt@canonical.com>
Tomka Gergely <tomkatudor@gmail.com>
Steve Marple <stevemarple@googlemail.com>
konosubakonoakua <ailike_meow@qq.com>
Keith Hill <k_hill@unitronlp.com>
Lubov66 <radolevanja@gmail.com>
V <v@anomalous.eu>
Samuel Holland <samuel@sholland.org>
David Ordnung <david.ordnung@googlemail.com>
Thanks to everyone who has contributed to this project. Thanks to everyone who has contributed to this project.

View file

@ -1,4 +1,4 @@
Copyright (c) 2014-2022 Martin Lund <martin.lund@keep-it-simple.com> Copyright (c) 2014-2025 Martin Lund <martin.lund@keep-it-simple.com>
This program is free software; you can redistribute it and/or This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License modify it under the terms of the GNU General Public License

749
NEWS
View file

@ -1,5 +1,730 @@
=== tio v3.9 (2025-04-13) ===
=== tio v2.6 ===
Changes since tio v3.8 (2024-11-30):
* Fix parsing of timestamp options
* codeql: Upgrade to upload-artifact@v4
* Update plaintext man page
* Add character mapping examples
* Fix pattern matching memory corruption
Samuel Holland:
* Don't add null characters to the expect buffer
They prevent regexec() from seeing the remainder of the buffer.
V:
* Disable stdout buffering globally
This makes it possible to pipe output to other programs cleanly.
Lubov66:
* docs: edited the license date
Jakob Haufe:
* Manpage: Fix backslash encoding
Literal backslash needs to be written as \e.
Changes since tio v3.7 (2024-08-31):
* Rename git version to simply version
* Clean up lua API
Rename modem_send() to send()
Rename send to write()
* Zero initialize buffer in read_string()
* Use version from git
* Fix memory leak in base62_encode()
* Fix name declaration conflict with socket send()
* Add clang-format spec
Keith Hill:
+ Add system timestamps to lua read() and new lua read_line() per global options
+ Add missing timestamp-format epoch
+ Update send_ to use fsync and tcdrain like normal tty_sync does
+ Rework read_line to save partial line at timeout
+ Simplified read_line to reduce cyclomatic complexity
+ renamed example files read.lua and read_line.lua
+ moved #define READ_LINE_SIZE to top of file
+ renamed g_linebuf to linebuf, and moved it into read_line as a static variable
Changes since tio v3.6 (2024-07-19):
* Remove unnecessary sync in line input mode
This caused a problem for some highly timing sensitive modem read-eval-print
loops because the input line and line termination characters (cr/nl) would be
shifted out on the UART with too big delay inbetween because of two
syncs.
* Fix socket send call on platforms without MSG_NOSIGNAL
To fix build issue encountered on MacOS Catalina but may apply to other
platforms.
Steve Marple:
* Add "epoch" timestamp option
Add an option that prints the timestamp as the number of seconds since
the Unix epoch.
Tomka Gergely:
* The log-directory options is not read from the configuration file.
Changes since tio v3.5 (2024-06-29):
* Add configuration file include directive
To include the contents of another configuration file simply do e.g.:
[include raspberrypi.conf]
Also, included file can include other files which can include other
files etc.
This feature is useful for managing many configuration files and sharing
configuration files with others.
* Mention how to list key commands in help output
* Fix hex output mode when using normal input mode
In this combination of modes the input character was not forwarded to
the tty device. This fix makes sure it is forwarded.
* Fix uptime on MacOS
On MacOS the birth time is apparently not available so we use
modification time instead.
* Improve warning upon failing connect
Add device path to warning when connect fails.
* Fix crashy search_reset() on macOS
* Clean up shadow variable
* Clean up readline code
* Improve listing of long device names
* Fix listing of serial devices on macOS
Heinrich Schuchardt:
* Print correct 'Done' timestamp for X- and Y-modem transfers
Call tio_printf() after completing xymodem_send().
Robert Lipe:
* Recompute listing_device_name_length_max for MacOS case, too.
Changes since tio v3.4:
* Clarify input and output direction of map flags
* Rename mapping flag MSB2LSB to IMSB2LSB
This is the correct naming since we are changing the input bit order on
input from the serial device.
* Add OIGNCR mapping flag
Ignores CR on output to serial device.
* Fix line input mode ignoring characters ABCD
* Fix tainted print
Jakob Haufe:
* Fix typos
Changes since tio v3.3:
* Update configuration output
* Clean up script run interaction text
* Fix unbounded writes
* Add history and editing feature to line input mode
Use up and down arrow keys to navigate history.
Use left and right arrow keys to move cursor back and forth.
We try mimic the behaviour of GNU readline which we can not use because
we also need to react to key commands.
* Reuse socket address
To avoid having to wait for socket timeout when restarting server.
* Fix line input mode
Fix so that ABCD are no longer ignored.
* Make sure ICRNL, IGNCR, INLCR take effect
* Include correct header for poll()
* Add group write permission to xymodem received file
* Fix missing open() flags in xymodem_receive()
Vyacheslav Patkov:
* Show current mappings in the configuration printout
* Use "ctrl-t m" to change mappings interactively
* Prompt for Lua script or shell command in interactive session
Eliot Alan Foss:
* Added support to receive XMODEM-CRC files from the connected serial port.
Changes since tio v3.2:
* Force destructive backspace when using local echo
Only takes effect in normal output mode.
* Fix local-echo in configuration file
* Clean up includes
* Force socket write operation to ignore any signals
* Man page cleanup
Changes since tio v3.1:
* Do not print error when using --list with broken config file
* Clean up completion script
* Add option '--exec <command>' for running shell command
Runs shell command with I/O redirected to device.
* Make sure all error output is directed to stderr
* Fix shadow variables
* Update man page
* Fix build on older GNU/Linux systems without statx
* Fix line ending in --list output
* Print location of configuration file in --list output
* Fix alignment of profile listing
Changes since tio v3.0:
* Improve --list feature on non-linux platform
* List available profiles in --list output
* Always message when saving log file
* Add support for using TID as device in config file
* Fix use of invalid flag with regexec()
* Fix potential buffer overflow in match_and_replace()
* Fix profile autocompletion
* Remove inih dependency from CI builds
* Replace use of stat() with fstat()
For better security.
* Fix hexN output mode
* Update pattern matching example
* Fix submenu response when invalid key hit
* Replace inih with glib key file parser
After including the use of glib we might as well replace inih
with the glib key file parser.
All configuration file parsing has been reworked and also the options
parsing has been cleaned up, resulting in better and stricter
configuration file and option value checks.
Compared to old, configuration files now requires any default
configurations to be put in a group/section named [default].
Configuration file keywords such as "enable", "disable", "on",
"off", "yes", "no", "0", "1" have been retired. Now only "true" and
"false" apply to boolean configuration options. This is done to simplify
things and avoid any confusion.
The pattern option feature has been reworked so now the user can now
access the full match string and any matching subexpression using the
%mN syntax.
For example:
[usb devices]
pattern = usb([0-9]*)
device = /dev/ttyUSB%m1
Then when using tio:
$ tio usb12
%m0 = 'usb12' // Full match string
%m1 = 12 // First match subexpression
Which results in device = /dev/ttyUSB12
* Remove CircleCI
Replaced with github workflow CI.
* Add github workflow for Ubuntu build
* Enable extended pattern matching
So that the exclude options can also work as include using special
pattern syntax.
For example, to only include /dev/ttyUSB* devices simply do:
$ tio --exclude-devices=!(/dev/ttyUSB*) --list
See the man page of fnmatch() for all available extended pattern
options.
* Update lua read() description
Rui Chen:
* fix: add build patch for `FNM_EXTMATCH`
* feat: add macOS workflow
* fix: add macOS build patch for `fs_get_creation_time`
Changes since tio v2.8:
* Simplify lua line manipulation API
Collapses lua high(), low(), toggle(), config_high(), config_low(),
config_apply() into one simple function:
set{<line>=<state>, ...}
Line can be any of DTR, RTS, CTS, DSR, CD, RI.
State is high, low, or toggle.
Example:
script = set{DTR=high, RTS=low}; msleep(100); set{DTR=low, RTS=high}; msleep(100); set{RTS=low}
Notice the use of {} instad of () when calling the set function. This is
required to pass parameters by name in lua.
* Disable DEC Special Graphics at exit if vt100
If a vt100 terminal receives the Shift In character '\016' it will
enable the 7 bit DEC Special Graphics character set used for line drawing.
For most users this can happen due to line noise from the tty device and
will likely mess up your terminal even after tio exits.
To better handle this we want to make sure that tio disables this mode
by sending the Shift Out character '\017' at exit.
This mechanism will only activate if environment variable TERM assumes
value "vt100".
* Add hexN output mode
Adds support for hexN mode where N is a number in the range 1 to 4096
which defines how many hex values will be printed before a line break.
In short, it defines the width of the hex output.
In this mode, if timestamps are enabled they will be added to each hex
line.
* Rename sub-config to profile
Because better naming.
* Use lua io.write() instead of print()
io.write() gives better output control as print() is hardcoded to always
print a newline.
* Add new ways to manage serial devices
* Rename --list-devices to --list
* Rename --no-autoconnect to --no-reconnect
* Switch -l and -L options
* -l now lists available serial devices
* -L enables log to file
* Add option --auto-connect <strategy>
* Supported strategies:
* "new" - Waits to connect first new appearing serial device
* "latest" - Connects to latest registered serial device
* "direct" - Connect directly to specified serial device (default)
* Add options to exclude serial devices from auto connect strategy by
pattern
* Supported exclude options:
* --exclude-devices <pattern>
Example: '--exclude-devices "/dev/ttyUSB2,/dev/ttyS?"'
* --exclude-drivers <pattern>
Example: '--exclude-drivers "cdc_acm"'
* --exclude-tids <pattern>
Example: '--exclude-tids "yW07,bCC2"'
* Patterns support '*' and '?'
* Connect to same port/device combination via unique topology ID (TID)
* Topology ID is a 4 digit base62 encoded hash of a device topology
string coming from the Linux kernel. This means that whenever you
plug in the same e.g. USB serial port device to the same USB hub
port connected via the exact same hub topology all the way to your
computer, you will get the same unique TID.
* Useful for stable reconnections when serial device has no serial
device by ID
* For now, only tested on Linux.
* Reworked and improved listing of serial devices to show serial devices:
* By device
* Including TID, uptime, driver, and description.
* Sorted by uptime (newest device listed last)
* By unique topology ID
* By ID
* By path
* Add script interface 'list = tty_search()' for searching for serial
devices.
* Clean up timestamp enum definition
* Add missing options to show configuration
* Update description of mute option
* Add lua read_string() function
* Don't forget to log output in lua expect()
* Generalize automatic login example for Linux
* Fix log output in hex output mode
* Add timeout based timestamps in hex output mode
This change reintroduces timestamping in hex output mode but based on
timeout instead of new lines which made no sense. This means that
timestamps will only be printed when timeout time has elapsed with no
output activity from serial device.
Adds option --timestamp-timeout <ms> for setting the timeout value in
milliseconds.
Defaults to 200 ms.
* Improve switched messages
* Extend lua expect() to also return matched string
* Add automatic login script example
* Organize examples directory
* Introduce basic line input mode
* Cleanup global variable name shadowing
Davis C:
* Updated login example with new expect logic
* Reset buffer size at start of expect
* Return 1 when `expect` matches
Changes since tio v2.7:
* Rework resolve_config_file()
* Rework line_pulse_duration_option_parse()
Introduce proper sscanf() checks.
* Rework rs485_parse_config()
Introduce proper sscanf() checks.
* Clean up file descriptor name shadowing
* Add missing header guard
* Upgrade inih subproject
* Remove options --response-wait, --response-timeout
Remove options and rework input handling so it is possible to do the
same thing but via script which is much more flexible.
These options were always a bit of a hardcoded solution. With the new
script expect feature we can wait for any type of response.
For example, pipe command to serial device and wait for line response within 1 second:
$ echo "*IDN?" | tio /dev/ttyACM0 --script "expect('\r\n', 1000)" --mute
* Add lua exit(code)
* Add timeout feature to expect()
* Add lua expect(string)
Add simple expect functionality.
The expect(string) function will wait for input from the tty device and
only return when there is a string match. Regular expressions are
supported.
Example:
script = expect('password:'); send('my_password\n')
* Add lua send(string)
* Add lua modem_send(file,protocol)
* Fix xymodem error messages
* Rework x/y-modem transfer command
Remove ctrl-t X option and instead introduce submenu to ctrl-t x option
for picking which xmodem protocol to use.
* Update README
* Cleanup options
* Add independent input and output mode
Replaces -x, --hexadecimal option with --input-mode and --output-mode
so it is possible to select hex or normal mode for both input and output
independently.
To obtain same behavior as -x, --hexadecimal use the following
configuration:
input-mode = hex
output-mode = hex
* Fix file descriptor handling on macOS
* Add tty line configuration script API
On some platforms calling high()/low() to switch line states result in
costly system calls which makes it impossible to switch two or more tty
lines simultaneously.
To help solve this timing issue we introduce a tty line state
configuration API which can be used instead of using
high()/low().
Using config_low(line) and config_high(line) one can set up a new line
state configuration for multiple lines and then use config_apply() to
finally apply the configuration. This will result in only one system
call to instruct the serial port drive to switch all the configured line
states which should help ensure that the lines are switched
simultaneously.
Example:
script = config_high(DTR); config_low(RTS); config_apply()
* Add ONULBRK mapping flag
Add ONULBRK mapping to map nul (zero) to send break signal on output.
This is useful if one needs to e.g. send the break signal to the tty
device when connected via socket.
* Add --log-directory option
For specifying directory path in which to save automatically named log
files.
* Add Lua scripting feature
Add support for running Lua scripts that can manipulate the tty control
lines. Script is activated automatically on connect or manually via in
session key command.
The Lua scripting feature opens up for many possibilities in the future
such as adding expect like functionality to easily and programatically
interact with the connected device.
* Invert line states to reflect true electrical level
* Add support for disabling prefix key handling
To disable prefix key input handing simply set prefix-ctrl-key to
'none'.
Based on original patch from Sebastian Krahmer.
* Add meson man pages install option
Defaults to installing man pages.
HiFiPhile:
* Poll on serial port read instead of delay.
* Add Xmodem-CRC support.
* CYGWIN: Fix port auto connection.
Mingjie Shen:
* Check return values of sscanf()
Failing to check that a call to 'sscanf' actually writes to an output
variable can lead to unexpected behavior at reading time.
Jakob Haufe:
* Support NO_COLOR env variable as per no-color.org
* Fix troff warning
.eo/.ec sections seemingly need explicit empty lines using .sp
Otherwise, troff complains:
troff:<standard input>:535: warning: expected numeric expression, got '\'
troff:<standard input>:538: warning: expected numeric expression, got '\'
troff:<standard input>:541: warning: expected numeric expression, got '\'
Fredrik Svedberg:
* Add map FF to ESC-c on input
Added map of form feed to ESC-c on input for terminals that
do not clear screen on ^L but do on ESC-c.
Brian:
* Add CodeQL Workflow for Code Security Analysis
Sylvain LAFRASSE:
* Fix double call of tty_disconnect() on macOS/Darwin.
Changes since tio v2.6:
Paul Ruizendaal:
* Add xmodem and ymodem file send support
HiFiPhile:
* tty_stdin_input_thread(): write to pipe only if byte_count > 0.
* Ignore EINTR error.
* CYGWIN: Add support for "COM*" naming.
Wes Koerber:
* chore: reorder log-strip and log-append
reorder to maintain consistency with documentation
* chore: update readme, bash completion, man page
* fix: support --log-append in cli options
@ -11,7 +736,7 @@ Changes since tio v2.5:
Add --log-append option which makes tio append to any existing log file. Add --log-append option which makes tio append to any existing log file.
This also changes the default behaviour of tio from appending to This also changes the default behavior of tio from appending to
overwriting any existing log file. Now you have to use this new option overwriting any existing log file. Now you have to use this new option
to make tio append. to make tio append.
@ -345,7 +1070,7 @@ Changes since tio v1.46:
Victor Oliveira Victor Oliveira
* add macports install instructions * add MacPorts install instructions
@ -845,7 +1570,7 @@ Changes since tio v1.35:
* Handle SIGHUP * Handle SIGHUP
Handle SIGHUP so that the registered exit handlers are called to restore Handle SIGHUP so that the registered exit handlers are called to restore
the terminal back to its orignal state. the terminal back to its original state.
* Add color configuration support * Add color configuration support
@ -901,10 +1626,10 @@ Changes since tio v1.34:
* Add support for configurable timestamp format * Add support for configurable timestamp format
Also changes default timestamp format from ISO8601 to classic 24-hour Also changes default timestamp format from ISO 8601 to classic 24-hour
format as this is assumed to be the format that most users would prefer. format as this is assumed to be the format that most users would prefer.
And reintroduces strict but optional ISO8601 format. And reintroduces strict but optional ISO 8601 format.
This feature allows to easily add more timestamp formats in the future. This feature allows to easily add more timestamp formats in the future.
@ -1025,10 +1750,10 @@ Sylvain LAFRASSE:
attila-v: attila-v:
* Refine timestamps with milliseconds and ISO-8601 format (#129). * Refine timestamps with milliseconds and ISO 8601 format (#129).
* Show milliseconds too in the timestamp (#114) and log file (#124) * Show milliseconds too in the timestamp (#114) and log file (#124)
* Change timestamp format to ISO-8601. * Change timestamp format to ISO 8601.
Yin Fengwei: Yin Fengwei:
@ -1107,7 +1832,7 @@ Lars Kellogg-Stedman:
George Stark: George Stark:
* dont show line state if ioctl failed * don't show line state if ioctl failed
* add serial lines manual control * add serial lines manual control
@ -1120,9 +1845,9 @@ arichi:
Mariusz Midor: Mariusz Midor:
* Newline: handle booth NL and CR * Newline: handle both NL and CR
Flag ONLCRNL expects code \n after press Enter, but on some systems \r is send instead. Flag ONLCRNL expects code \n after press Enter, but on some systems \r is sent instead.
@ -1614,7 +2339,7 @@ Changes since tio v1.11:
To display the total number of bytes transmitted/received simply perform the To display the total number of bytes transmitted/received simply perform the
'ctrl-t s' command sequence. 'ctrl-t s' command sequence.
This feature can be useful when eg. trying to detect non-printable This feature can be useful when e.g. trying to detect non-printable
characters. characters.
* Further simplification of key handling * Further simplification of key handling

456
README.md
View file

@ -1,18 +1,20 @@
[![tio](images/tio-icon.png)]() [![tio](images/tio-icon.png)]()
# tio - a simple serial device I/O tool # tio - a serial device I/O tool
[![](https://img.shields.io/circleci/build/github/tio/tio)](https://circleci.com/github/tio/tio/tree/master) [![](https://img.shields.io/github/actions/workflow/status/tio/tio/ubuntu.yml?label=Ubuntu)](https://github.com/tio/tio/actions/workflows/ubuntu.yml)
[![](https://img.shields.io/github/actions/workflow/status/tio/tio/macos.yml?label=MacOS)](https://github.com/tio/tio/actions/workflows/macos.yml)
[![](https://github.com/tio/tio/actions/workflows/codeql.yml/badge.svg)](https://github.com/tio/tio/actions/workflows/codeql.yml)
[![](https://img.shields.io/codefactor/grade/github/tio/tio)](https://www.codefactor.io/repository/github/tio/tio)
[![](https://img.shields.io/github/v/release/tio/tio?sort=semver)](https://github.com/tio/tio/releases) [![](https://img.shields.io/github/v/release/tio/tio?sort=semver)](https://github.com/tio/tio/releases)
[![](https://img.shields.io/repology/repositories/tio)](https://repology.org/project/tio/versions) [![](https://img.shields.io/repology/repositories/tio)](https://repology.org/project/tio/versions)
[![](https://img.shields.io/tokei/lines/github/tio/tio)](https://github.com/tio/tio)
## 1. Introduction ## 1. Introduction
tio is a simple serial device tool which features a straightforward tio is a serial device tool which features a straightforward command-line and
command-line and configuration file interface to easily connect to serial TTY configuration file interface to easily connect to serial TTY devices for basic
devices for basic I/O operations. I/O operations.
<p align="center"> <p align="center">
<img src="images/tio-demo.gif"> <img src="images/tio-demo.gif">
@ -20,46 +22,77 @@ devices for basic I/O operations.
### 1.1 Motivation ### 1.1 Motivation
To make a simpler serial device tool for talking with serial TTY devices with To make a simpler serial device tool for working with serial TTY devices with
less focus on classic terminal/modem features and more focus on the needs of less focus on classic terminal/modem features and more focus on the needs of
embedded developers and hackers. embedded developers and hackers.
tio was originally created to replace tio was originally created as an alternative to
[screen](https://www.gnu.org/software/screen) for connecting to serial devices [screen](https://www.gnu.org/software/screen) for connecting to serial devices
when used in combination with [tmux](https://tmux.github.io). when used in combination with [tmux](https://tmux.github.io).
## 2. Features ## 2. Features
* Easily connect to serial TTY devices * Easily connect to serial TTY devices
* Automatic connect and reconnect
* Sensible defaults (115200 8n1) * Sensible defaults (115200 8n1)
* Automatic connection management
* Automatic detection of serial ports
* Automatic reconnect
* Automatically connect to first new appearing serial device
* Automatically connect to latest registered serial device
* Connect to same port/device combination via unique topology ID (TID)
* Useful for reconnecting when serial device has no serial device by ID
* Support for non-standard baud rates * Support for non-standard baud rates
* Support for RS-485 mode
* Support for mark and space parity * Support for mark and space parity
* List available serial devices by ID * X-modem (1K/CRC) and Y-modem file upload
* Support for RS-485 mode
* List available serial devices
* By device
* Including topology ID, uptime, driver, description
* Sorted by uptime (newest device listed last)
* By ID
* By path
* Show RX/TX statistics * Show RX/TX statistics
* Toggle serial lines * Toggle serial lines
* Pulse serial lines with configurable pulse duration * Pulse serial lines with configurable pulse duration
* Local echo support * Local echo support
* Remapping of characters (nl, cr-nl, bs, lowercase to uppercase, etc.) * Remapping of characters (nl, cr-nl, bs, lowercase to uppercase, etc.)
* Line timestamps * Switchable independent input and output
* Support for delayed output per character * Normal mode
* Support for delayed output per line * Hex mode (output supports variable width)
* Hexadecimal mode * Line mode (input only)
* Timestamp support
* Per line in normal output mode
* Output timeout timestamps in hex output mode
* Support for delayed output
* Per character
* Per line
* Log to file * Log to file
* Autogeneration of log filename * Automatic naming of log file (default)
* Configurable directory for saving automatic named log files
* Manual naming of log file
* Overwrite (default) or append to log file
* Strip control characters and escape sequences
* Configuration file support * Configuration file support
* Activate sub-configurations by name or pattern * Support for configuration profiles
* Redirect I/O to UNIX socket or IPv4/v6 network socket for scripting or TTY sharing * Activate configuration profiles by name or pattern
* Support for including other configuration files
* Redirect I/O of shell command to serial device
* Redirect I/O to UNIX socket or IPv4/v6 network socket
* Useful for scripting or TTY sharing
* Pipe input and/or output * Pipe input and/or output
* Support for simple line request/response handling * Bash completion on options, serial device names, and profile names
* Bash completion on options, serial device names, and sub-configuration names * Configurable tio message text color
* Configurable text color * Supports NO_COLOR env variable as per [no-color.org](https://no-color.org)
* Visual or audible alert on connect/disconnect * Visual or audible alert on connect/disconnect
* Remapping of prefix key * Remapping of prefix key
* Lua scripting support for automation
* Run script manually or automatically at connect (once/always/never)
* Simple expect/send like functionality with support for regular expressions
* Manipulate port modem lines (useful for microcontroller reset/boot etc.)
* Send files via x/y-modem protocol
* Search for serial devices
* Man page documentation * Man page documentation
* Binary size less than 80kB * Plays nicely with [tmux](https://tmux.github.io) and similar terminal multiplexers
* Plays nicely with [tmux](https://tmux.github.io)
## 3. Usage ## 3. Usage
@ -69,55 +102,65 @@ For more usage details please see the man page documentation
### 3.1 Command-line ### 3.1 Command-line
The command-line interface is straightforward as reflected in the output from The command-line interface is straightforward as reflected in the output from
'tio --help': ```tio --help```:
``` ```
Usage: tio [<options>] <tty-device|sub-config> Usage: tio [<options>] <tty-device|profile|tid>
Connect to TTY device directly or via sub-configuration. Connect to TTY device directly or via configuration profile or topology ID.
Options: Options:
-b, --baudrate <bps> Baud rate (default: 115200) -b, --baudrate <bps> Baud rate (default: 115200)
-d, --databits 5|6|7|8 Data bits (default: 8) -d, --databits 5|6|7|8 Data bits (default: 8)
-f, --flow hard|soft|none Flow control (default: none) -f, --flow hard|soft|none Flow control (default: none)
-s, --stopbits 1|2 Stop bits (default: 1) -s, --stopbits 1|2 Stop bits (default: 1)
-p, --parity odd|even|none|mark|space Parity (default: none) -p, --parity odd|even|none|mark|space Parity (default: none)
-o, --output-delay <ms> Output character delay (default: 0) -o, --output-delay <ms> Output character delay (default: 0)
-O, --output-line-delay <ms> Output line delay (default: 0) -O, --output-line-delay <ms> Output line delay (default: 0)
--line-pulse-duration <duration> Set line pulse duration --line-pulse-duration <duration> Set line pulse duration
-n, --no-autoconnect Disable automatic connect -a, --auto-connect new|latest|direct Automatic connect strategy (default: direct)
-e, --local-echo Enable local echo --exclude-devices <pattern> Exclude devices by pattern
-t, --timestamp Enable line timestamp --exclude-drivers <pattern> Exclude drivers by pattern
--timestamp-format <format> Set timestamp format (default: 24hour) --exclude-tids <pattern> Exclude topology IDs by pattern
-L, --list-devices List available serial devices -n, --no-reconnect Do not reconnect
-l, --log Enable log to file -e, --local-echo Enable local echo
--log-file <filename> Set log filename --input-mode normal|hex|line Select input mode (default: normal)
--log-strip Strip control characters and escape sequences --output-mode normal|hex|hexN Select output mode (default: normal)
-m, --map <flags> Map characters -t, --timestamp Enable line timestamp
-c, --color 0..255|bold|none|list Colorize tio text (default: bold) --timestamp-format <format> Set timestamp format (default: 24hour)
-S, --socket <socket> Redirect I/O to socket --timestamp-timeout <ms> Set timestamp timeout (default: 200)
-x, --hexadecimal Enable hexadecimal mode -l, --list List available serial devices, TIDs, and profiles
-r, --response-wait Wait for line response then quit -L, --log Enable log to file
--response-timeout <ms> Response timeout (default: 100) --log-file <filename> Set log filename
--rs-485 Enable RS-485 mode --log-directory <path> Set log directory path for automatic named logs
--rs-485-config <config> Set RS-485 configuration --log-append Append to log file
--alert bell|blink|none Alert on connect/disconnect (default: none) --log-strip Strip control characters and escape sequences
-v, --version Display version -m, --map <flags> Map characters
-h, --help Display help -c, --color 0..255|bold|none|list Colorize tio text (default: bold)
-S, --socket <socket> Redirect I/O to socket
--rs-485 Enable RS-485 mode
--rs-485-config <config> Set RS-485 configuration
--alert bell|blink|none Alert on connect/disconnect (default: none)
--mute Mute tio messages
--script <string> Run script from string
--script-file <filename> Run script from file
--script-run once|always|never Run script on connect (default: always)
--exec <command> Execute shell command with I/O redirected to device
-v, --version Display version
-h, --help Display help
Options and sub-configurations may be set via configuration file. Options and profiles may be set via configuration file.
See the man page for more details. In session you can press ctrl-t ? to list available key commands.
See the man page for more details.
``` ```
By default tio automatically connects to the provided TTY device if present. By default tio automatically connects to the provided TTY device. If the device
If the device is not present, it will wait for it to appear and then connect. is not present, tio will wait for it to appear and then connect. If the
If the connection is lost (eg. device is unplugged), it will wait for the connection is lost (e.g. device is unplugged), it will wait for the device to
device to reappear and then reconnect. However, if the `--no-autoconnect` reappear and then reconnect. However, if the `--no-reconnect` option is
option is provided, tio will exit if the device is not present or an provided, tio will exit if the device is not present or an established
established connection is lost. connection is lost.
tio features full bash autocompletion.
#### 3.1.1 Examples #### 3.1.1 Examples
@ -128,46 +171,129 @@ $ tio /dev/ttyUSB0
Which corresponds to the commonly used default options: Which corresponds to the commonly used default options:
``` ```
$ tio -b 115200 -d 8 -f none -s 1 -p none /dev/ttyUSB0 $ tio --baudrate 115200 --databits 8 --flow none --stopbits 1 --parity none /dev/ttyUSB0
```
List available serial devices:
```
$ tio --list
Device TID Uptime [s] Driver Description
----------------- ---- ------------- ---------------- --------------------------
/dev/ttyS4 BaaB 19526.576 port 16550A UART
/dev/ttyS5 eV0Z 19525.845 port 16550A UART
/dev/ttyUSB1 bCC2 1023.274 ftdi_sio TTL232R-3V3
/dev/ttyUSB0 SPpw 978.527 ftdi_sio TTL232RG-VREG3V3
/dev/ttyACM0 i5q4 2.079 cdc_acm ST-Link VCP Ctrl
By-id
--------------------------------------------------------------------------------
/dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTCHUV56-if00-port0
/dev/serial/by-id/usb-FTDI_TTL232RG-VREG3V3_FT1NELUB-if00-port0
/dev/serial/by-id/usb-STMicroelectronics_STLINK-V3_004900343438510234313939-if02
By-path
--------------------------------------------------------------------------------
/dev/serial/by-path/pci-0000:00:14.0-usb-0:8.1.3.1.4:1.0-port0
/dev/serial/by-path/pci-0000:00:14.0-usbv2-0:8.1.3.1.4:1.0-port0
/dev/serial/by-path/pci-0000:00:14.0-usbv2-0:6.4:1.0-port0
/dev/serial/by-path/pci-0000:00:14.0-usb-0:6.4:1.0-port0
/dev/serial/by-path/pci-0000:00:14.0-usbv2-0:6.3:1.2
/dev/serial/by-path/pci-0000:00:14.0-usb-0:6.3:1.2
Configuration profiles (/home/lundmar/.config/tio/config)
--------------------------------------------------------------------------------
rpi3 stm32 esp32 am64-evm
imx8mp-evk nucleo-h743zi2 usb-devices
``` ```
It is recommended to connect serial TTY devices by ID: It is recommended to connect serial TTY devices by ID:
``` ```
$ tio /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0 $ tio /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTCHUV56-if00-port0
``` ```
Using serial devices by ID ensures that tio automatically reconnects to the Note: Using serial devices by ID helps ensure that tio automatically reconnects
correct serial device if it is disconnected and then reconnected. to the same serial device when reattached, even when it enumerates differently.
List available serial devices by ID: If no serial device by ID is available it is recommended to connect via
topology ID (TID):
``` ```
$ tio --list-devices $ tio bCC2
```
Note: The TID is unique and will stay the same as long as your USB serial port
device plugs into the same USB topology (same ports, same hubs, same
connections, etc.). This way it is possible for tio to successfully reconnect
to the same device.
Connect automatically to first new appearing serial device:
```
$ tio --auto-connect new
```
Connect automatically to latest registered serial device:
```
$ tio --auto-connect latest
```
It is possible to use exclude options to affect which serial devices are
involved in the automatic connection strategy:
```
$ tio --auto-connect new --exclude-devices "/dev/ttyACM?,/dev/ttyUSB2"
```
And to exclude drivers by pattern:
```
$ tio --auto-connect new --exclude-drivers "cdc_acm,ftdi_sio"
```
Note: Pattern matching supports '*' and '?'. Use comma separation to define
multiple patterns.
To include drivers by specific pattern simply negate the exclude option:
```
$ tio --auto-connect new --exclude-drivers !("cp2102")
``` ```
Note: One can also use tio shell completion on /dev which will automatically
list all available serial TTY devices.
Log to file with autogenerated filename: Log to file with autogenerated filename:
``` ```
$ tio --log /dev/ttyUSB0 $ tio --log /dev/ttyUSB0
``` ```
Log to file with specific filename:
```
$ tio --log --log-file my-log.txt
```
Enable ISO8601 timestamps per line: Enable ISO8601 timestamps per line:
``` ```
$ tio --timestamp --timestamp-format iso8601 /dev/ttyUSB0 $ tio --timestamp --timestamp-format iso8601 /dev/ttyUSB0
``` ```
Output to hex with width 16:
```
$ tio --output-mode hex16 /dev/ttyUSB0
```
Redirect I/O to IPv4 network socket on port 4242: Redirect I/O to IPv4 network socket on port 4242:
``` ```
$ tio --socket inet:4242 /dev/ttyUSB0 $ tio --socket inet:4242 /dev/ttyUSB0
``` ```
Inject data to the serial device: Map NL to CR-NL on input from device and DEL to BS on output to device:
```
$ tio --map INLCRNL,ODELBS /dev/ttyUSB0
```
Pipe data to the serial device:
``` ```
$ cat data.bin | tio /dev/ttyUSB0 $ cat data.bin | tio /dev/ttyUSB0
``` ```
Send command to serial device and wait for line response: Manipulate modem lines on connect:
``` ```
$ echo "*IDN?" | tio /dev/ttyACM0 --response-wait $ tio --script "tio.set{DTR=high,RTS=low}; tio.msleep(100); tio.set{DTR=toggle,RTS=toggle}" /dev/ttyUSB0
```
Pipe command to serial device and wait for line response within 1 second:
```
$ echo "*IDN?" | tio /dev/ttyACM0 --script "tio.expect('\r\n', 1000)" --mute
KORAD KD3305P V4.2 SN:32475045 KORAD KD3305P V4.2 SN:32475045
``` ```
@ -177,23 +303,29 @@ Various in session key commands are supported. When tio is started, press
ctrl-t ? to list the available key commands. ctrl-t ? to list the available key commands.
``` ```
[20:19:12.040] Key commands: [15:02:53.269] Key commands:
[20:19:12.040] ctrl-t ? List available key commands [15:02:53.269] ctrl-t ? List available key commands
[20:19:12.040] ctrl-t b Send break [15:02:53.269] ctrl-t b Send break
[20:19:12.040] ctrl-t c Show configuration [15:02:53.269] ctrl-t c Show configuration
[20:19:12.040] ctrl-t e Toggle local echo mode [15:02:53.269] ctrl-t e Toggle local echo mode
[20:19:12.040] ctrl-t f Toggle log to file [15:02:53.269] ctrl-t f Toggle log to file
[20:19:12.040] ctrl-t g Toggle serial port line [15:02:53.269] ctrl-t F Flush data I/O buffers
[20:19:12.040] ctrl-t h Toggle hexadecimal mode [15:02:53.269] ctrl-t g Toggle serial port line
[20:19:12.040] ctrl-t l Clear screen [15:02:53.269] ctrl-t i Toggle input mode
[20:19:12.040] ctrl-t L Show line states [15:02:53.269] ctrl-t l Clear screen
[20:19:12.040] ctrl-t p Pulse serial port line [15:02:53.269] ctrl-t L Show line states
[20:19:12.040] ctrl-t q Quit [15:02:53.269] ctrl-t m Change mapping of characters on input or output
[20:19:12.041] ctrl-t s Show statistics [15:02:53.269] ctrl-t o Toggle output mode
[20:19:12.041] ctrl-t t Toggle line timestamp mode [15:02:53.269] ctrl-t p Pulse serial port line
[20:19:12.041] ctrl-t U Toggle conversion to uppercase [15:02:53.269] ctrl-t q Quit
[20:19:12.041] ctrl-t v Show version [15:02:53.269] ctrl-t r Run script
[20:19:12.041] ctrl-t ctrl-t Send ctrl-t character [15:02:53.269] ctrl-t R Execute shell command with I/O redirected to device
[15:02:53.269] ctrl-t s Show statistics
[15:02:53.269] ctrl-t t Toggle line timestamp mode
[15:02:53.269] ctrl-t v Show version
[15:02:53.269] ctrl-t x Send file via Xmodem
[15:02:53.269] ctrl-t y Send file via Ymodem
[15:02:53.269] ctrl-t ctrl-t Send ctrl-t character
``` ```
If needed, the prefix key (ctrl-t) can be remapped via configuration file. If needed, the prefix key (ctrl-t) can be remapped via configuration file.
@ -206,15 +338,17 @@ following locations in the order listed:
- $HOME/.config/tio/config - $HOME/.config/tio/config
- $HOME/.tioconfig - $HOME/.tioconfig
The configuration file supports sub-configurations using named sections which can The configuration file supports profiles using named sections which can be
be activated via the command-line by name or pattern. A sub-configuration activated via the command-line by name or pattern. A profile specifies which
specifies which TTY device to connect to and other options. TTY device to connect to and other options.
### 3.3.1 Example
Example configuration file: Example configuration file:
``` ```
# Defaults [default]
baudrate = 9600 baudrate = 115200
databits = 8 databits = 8
parity = none parity = none
stopbits = 1 stopbits = 1
@ -222,20 +356,31 @@ color = 10
[rpi3] [rpi3]
device = /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0 device = /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0
baudrate = 115200 no-reconnect = true
no-autoconnect = enable log = true
log = enable
log-file = rpi3.log log-file = rpi3.log
line-pulse-duration = DTR=200,RTS=150 line-pulse-duration = DTR=200,RTS=150
color = 11
[svf2]
device = /dev/ttyUSB0
baudrate = 9600
script = tio.expect("login: "); tio.write("root\n"); tio.expect("Password: "); tio.write("root\n")
color = 12 color = 12
[usb devices] [esp32]
pattern = usb([0-9]*) device = /dev/serial/by-id/usb-0403_6014-if00-port0
device = /dev/ttyUSB%s script = tio.set{DTR=high,RTS=low}; tio.msleep(100); tio.set{DTR=low,RTS=high}; tio.msleep(100); tio.set{RTS=low}
script-run = once
color = 13 color = 13
[usb-devices]
pattern = ^usb([0-9]*)
device = /dev/ttyUSB%m1
color = 14
``` ```
To use a specific sub-configuration by name simply start tio like so: To use a specific profile by name simply start tio like so:
``` ```
$ tio rpi3 $ tio rpi3
``` ```
@ -244,7 +389,86 @@ Or by pattern match:
$ tio usb12 $ tio usb12
``` ```
Another more elaborate configuration file example is available [here](example/config). Another more elaborate configuration file example is available [here](examples/config/config).
### 3.4 Lua script API
Tio suppots Lua scripting to easily automate interaction with the tty device.
In addition to the standard Lua API tio makes the following functions
and variables available:
#### `tio.expect(pattern, timeout)`
Waits for the Lua pattern to match or timeout before continuing.
Timeout is in milliseconds, defaults to 0 meaning it will wait forever.
Returns the captures from the pattern or `nil` on timeout.
#### `tio.read(size, timeout)`
Read up to `size` bytes from serial device. If timeout is 0 or not provided it
will wait forever until data is ready to read.
Returns a string up to `size` bytes long on success and `nil` on timeout.
#### `tio.readline(timeout)`
Read line from serial device. If timeout is 0 or not provided it will wait
forever until data is ready to read.
Returns a string on success and `nil` on timeout. On timeout a partially read
line may be returned as a second return value.
#### `tio.write(string)`
Write string to serial device.
Returns the `tio` table.
#### `tio.send(file, protocol)`
Send file using x/y-modem protocol.
Protocol can be any of `XMODEM_1K`, `XMODEM_CRC`, `YMODEM`.
#### `tio.ttysearch()`
Search for serial devices.
Returns a table of number indexed tables, one for each serial device found.
Each of these tables contains the serial device information accessible via the
following string indexed elements "path", "tid", "uptime", "driver",
"description".
Returns `nil` if no serial devices are found.
#### `tio.set{line=state, ...}`
Set state of one or multiple tty modem lines.
Line can be any of `DTR`, `RTS`, `CTS`, `DSR`, `CD`, `RI`
State is `high`, `low`, or `toggle`.
#### `tio.sleep(seconds)`
Sleep for seconds.
#### `tio.msleep(ms)`
Sleep for milliseconds.
#### `tio.alwaysecho`
A boolean value, defaults to `true`.
If `tio.alwaysecho` is `false`, the result of `tio.read`, `tio.readline` or
`tio.expect` will only be returned from the function and not logged or printed.
If `tio.alwaysecho` is set to `true`, reading functions also emit a single
timestamp to stdout and log file per `options.timestamp` and `options.log`.
## 4. Installation ## 4. Installation
@ -284,6 +508,12 @@ $ pacman -S tio
The latest source releases can be found [here](https://github.com/tio/tio/releases). The latest source releases can be found [here](https://github.com/tio/tio/releases).
Before running the install steps make sure you have glib and lua libraries installed. For example:
```
$ sudo apt install libglib2.0-dev liblua5.2-dev
```
Install steps: Install steps:
``` ```
$ meson setup build $ meson setup build
@ -299,18 +529,20 @@ Note: The meson install steps may differ depending on your specific system.
Getting permission access errors trying to open your serial device? Getting permission access errors trying to open your serial device?
Add your user to the group which allows serial device access. For example, to add your user to the 'dialout' group do: Add your user to the group which allows serial device access permanently. For example, to add your user to the 'dialout' group do:
```bash
sudo usermod -a -G dialout <username>
``` ```
$ sudo usermod -a -G dialout <username> Switch to the "dialout" group, temporary but immediately for this session.
```bash
newgrp dialout
``` ```
## 5. Contributing ## 5. Contributing
tio is open source. If you want to help out with the project please feel free This is an open source project - all contributions (bug reports, code, doc,
to join in. ideas, etc.) are welcome.
All contributions (bug reports, code, doc, ideas, etc.) are welcome.
Please use the github issue tracker and pull request features. Please use the github issue tracker and pull request features.
@ -337,6 +569,6 @@ tio is GPLv2+. See LICENSE file for more details.
## 9. Authors ## 9. Authors
Created by Martin Lund \<martin.lund@keep-it-simple.com> Maintained by Martin Lund \<martin.lund@keep-it-simple.com>
See the AUTHORS file for full list of contributors. See the AUTHORS file for full list of contributors.

62
TODO
View file

@ -1,34 +1,54 @@
* Support for interaction using simple autoresponse strings * Add release support for arm and x86 binary tarballs
Add support for simple autoresponse strings in the configuration file. For * Support input and input mapping from lua scripts
example:
autoresponse = expect:'localhost login: ', send:'root\n', * Add option to send file raw (no modem protocol)
expect:'Password: ', send:'abcd1234\n'
When expect line is matched tio will respond by writing the send string. * Add loopback option
When parsing the autoresponse variable make sure matching expect/send pairs Send received serial input back to output (for testing etc.)
else provide warning.
Maybe support regex matching in expect string to make feature more powerful. * Add loopback support between two serial ports
This is mostly a convenience feature. For more powerful scripted interaction Useful for traffic monitoring
users can continue use the socket feature in combination with the expect tool
as described in the man page.
Maybe provide a mechanism to disable autoresponse feature. Maybe by defining * Add mapping feature for printing non-printable characters
maximum match count and/or in session key command to toggle feature.
* Support for running external command * Porting layer to support native win32 builds.
Add key command e.g. 'ctrl-t r' which prompts user to run external command. Some of the work that needs to be done:
The command will be run in a process which stdin/stdout is redirected to the
serial port.
This is the first step towards maybe also adding automatic support for All posix functions need to be platform independent, go though file by file:
x/y/zmodem data transfer protocols by calling external programs such as
rb/sb, rx/sx, rz/sz, etc. termios.h
unistd.h has very limited functions
ENV different in config_file_resolve
errno
sys/ioctl.h
sys/poll.h
socket, may need a new thread
Serial, RS485, character mapping
Communication pipe
Port enumerate, all devices of the same type have the same name (eg. USB
Serial Device for ttyACM) -> which makes regex not meaningful (kind of a
good thing since libtre in Mingw has too much dependencies makes binary too
big)
* Support traditional hex output format such as:
00000000 74 65 73 74 20 74 65 73 74 20 74 65 73 74 20 74 |test test test t|
00000010 65 73 74 64 66 0a 61 0a 66 61 0a 66 0a 61 73 66 |estdf.a.fa.f.asf|
00000020 64 61 64 73 66 61 73 66 64 61 73 64 66 61 64 73 |dadsfasfdasdfads|
00000030 66 0a 61 73 64 66 61 64 73 66 61 73 64 66 61 73 |f.asdfadsfasdfas|
00000040 64 66 0a 61 73 64 66 61 64 73 66 61 73 64 66 61 |df.asdfadsfasdfa|
00000050 73 64 66 66 64 61 73 64 66 0a 0a 31 32 33 31 0a |sdffdasdf..1231.|
00000060 65 32 31 64 73 77 65 64 0a 0a |e21dswed..|
0000006a
* Add support for activity based time stamping in normal output mode
Already supported in hex output mode.
* Allow tio to connect to socket * Allow tio to connect to socket

View file

@ -9,28 +9,30 @@
# $HOME/.config/tio/config # $HOME/.config/tio/config
# $HOME/.tioconfig # $HOME/.tioconfig
# Defaults [default]
baudrate = 115200 baudrate = 115200
databits = 8 databits = 8
flow = none flow = none
stopbits = 1 stopbits = 1
parity = none parity = none
prefix-ctrl-key = t
output-delay = 0 output-delay = 0
output-line-delay = 0 output-line-delay = 0
no-autoconnect = disable auto-connect = direct
hexadecimal = disable no-reconnect = false
timestamp = disable local-echo = false
log = disable input-mode = normal
log-append = disable output-mode = normal
log-strip = disable timestamp = false
local-echo = disable log = false
log-append = false
log-strip = false
color = bold color = bold
rs-485 = disable rs-485 = false
response-wait = disable
alert = none alert = none
script-run = always
prefix-ctrl-key = t
# Sub-configurations # Configuration profiles
[rpi3] [rpi3]
baudrate = 115200 baudrate = 115200
@ -48,18 +50,29 @@ color = 10
[tincan] [tincan]
baudrate = 9600 baudrate = 9600
device = /dev/serial/by-id/usb-TinCanTools_Flyswatter2_FS20000-if00-port0 device = /dev/serial/by-id/usb-TinCanTools_Flyswatter2_FS20000-if00-port0
log = enable log = true
log-file = tincan.log log-file = tincan.log
log-strip = enable log-strip = true
color = 11 color = 11
[usb] [usb-devices]
pattern = usb([0-9]*) pattern = ^usb([0-9]*)
device = /dev/ttyUSB%s device = /dev/ttyUSB%m1
color = 12 color = 12
[rs-485-device] [rs-485-device]
device = /dev/ttyUSB0 device = /dev/ttyUSB0
rs-485 = enable rs-485 = true
rs-485-config = RTS_ON_SEND=1,RTS_AFTER_SEND=1,RTS_DELAY_BEFORE_SEND=60,RTS_DELAY_AFTER_SEND=80,RX_DURING_TX rs-485-config = RTS_ON_SEND=1,RTS_AFTER_SEND=1,RTS_DELAY_BEFORE_SEND=60,RTS_DELAY_AFTER_SEND=80,RX_DURING_TX
color = 13 color = 13
[esp32]
device = /dev/ttyUSB0
color = 14
script = tio.set{DTR=high,RTS=low}; tio.msleep(100); tio.set{DTR=low,RTS=high}; tio.msleep(100); tio.set{RTS=low}
script-run = always
[buspirate]
device = /dev/ttyACM0
map = INLCRNL,ODELBS
color = 15

View file

@ -0,0 +1,28 @@
local logins = {
["foo"] = {
username = "foouser",
password = "foopass",
},
["bar"] = {
username = "baruser",
password = "barpass",
},
["baz"] = {
username = "bazuser",
password = "bazpass",
},
}
local hostname = tio.expect("^(%g+) login:", 10)
if hostname then
local login = logins[hostname]
if (nil ~= login) then
tio.write(login.username .. "\n")
tio.expect("Password:")
tio.write(login.password .. "\n")
else
io.write("\r\nDon't know login info for " .. hostname .. "\r\n")
end
else
io.write("\r\nDidn't find a login prompt\r\n")
end

View file

@ -0,0 +1,5 @@
tio.set{DTR=high, RTS=low}
tio.msleep(100)
tio.set{DTR=low, RTS=high}
tio.msleep(100)
tio.set{RTS=toggle}

14
examples/lua/read.lua Normal file
View file

@ -0,0 +1,14 @@
tio.read(1000, 6000) -- initial config
tio.write("\n")
tio.msleep(100)
tio.read(650, 60) -- main menu
tio.write("S") -- S menu
tio.msleep(30)
tio.read(650, 60)
tio.write("t") -- Parallel Value Table
tio.read(650, 60)
while true do
tio.msleep(1000)
tio.write("t")
tio.read(650, 50) -- repeat PVT forever
end

View file

@ -0,0 +1,15 @@
tio.read(1000, 8000) -- read initial config
tio.write("\n")
tio.read(650, 100) -- main menu
tio.write("S") -- S menu
repeat
str = tio.readline(25)
until str == nil
while true do
tio.write("t") -- query PV table
tio.msleep(880)
repeat
str = tio.readline(60)
tio.msleep(60)
until str == nil
end

View file

@ -0,0 +1,13 @@
io.write("Searching... ")
local device = tio.ttysearch()
io.write("done\r\n")
for i in ipairs(device) do
io.write("\r\n" .. device[i]["path"] .. "\r\n")
io.write(" tid = " .. device[i]["tid"] .. "\r\n")
io.write(" uptime = " .. device[i]["uptime"] .. "\r\n")
io.write(" driver = " .. device[i]["driver"] .. "\r\n")
io.write(" description = " .. device[i]["description"] .. "\r\n")
end

View file

@ -1,18 +1,18 @@
.TH "tio" "1" "@version_date@" "tio @version@" "User Commands" .TH "tio" "1" "@version_date@" "tio @version@" "User Commands"
.SH "NAME" .SH "NAME"
tio \- a simple serial device I/O tool tio \- a serial device I/O tool
.SH "SYNOPSIS" .SH "SYNOPSIS"
.PP .PP
.B tio .B tio
.RI "[" <options> "] " "<tty-device|sub-config>" .RI "[" <options> "] " "<tty-device|profile|tid>"
.SH "DESCRIPTION" .SH "DESCRIPTION"
.PP .PP
\fBtio\fR is a simple serial device tool which features a straightforward \fBtio\fR is a serial device tool which features a straightforward command-line
command-line and configuration file interface to easily connect to serial TTY and configuration file interface to easily connect to serial TTY devices for
devices for basic I/O operations. basic I/O operations.
.SH "OPTIONS" .SH "OPTIONS"
@ -21,19 +21,19 @@ devices for basic I/O operations.
Set baud rate [bps] (default: 115200). Set baud rate [bps] (default: 115200).
.TP .TP
.BR \-d ", " "\-\-databits 5" | 6 | 7 | 8 .BR \-d ", " "\-\-databits " 5 | 6 | 7 | 8
Set data bits (default: 8). Set data bits (default: 8).
.TP .TP
.BR \-f ", " "\-\-flow hard" | soft | none .BR \-f ", " "\-\-flow " hard | soft | none
Set flow control (default: none). Set flow control (default: none).
.TP .TP
.BR \-s ", " "\-\-stopbits 1" | 2 .BR \-s ", " "\-\-stopbits " 1 | 2
Set stop bits (default: 1). Set stop bits (default: 1).
.TP .TP
.BR \-p ", " "\-\-parity odd" | even | none | mark | space .BR \-p ", " "\-\-parity " odd | even | none | mark | space
Set parity (default: none). Set parity (default: none).
@ -80,17 +80,48 @@ The default pulse duration for each line is 100 ms.
.RE .RE
.TP .TP
.BR \-n ", " \-\-no\-autoconnect .BR "\-a, \-\-auto\-connect new|latest|direct"
Disable automatic connect. Automatically connect to serial device according to one of the following
strategies:
By default tio automatically connects to the provided device if present. If the .RS
device is not present, it will wait for it to appear and then connect. If the .TP 10n
connection is lost (eg. device disconnects), it will wait for the device to .IP "\fBnew"
reappear and then reconnect. Automatically connect to first new appearing serial device.
.IP "\fBlatest"
Automatically connect to latest registered serial device.
.IP "\fBdirect"
Connect directly to specified TTY device.
.P
All the listed strategies automatically reconnects according to strategy if
device is not available or connection is lost.
.P
Default value is "direct".
.RE
However, if the \fB\-\-no\-autoconnect\fR option is provided, tio will exit if .TP
the device is not present or an established connection is lost. .BR " \-\-exclude\-devices \fI<pattern>"
Exclude devices by pattern ('*' and '?' supported).
.TP
.BR " \-\-exclude\-drivers \fI<pattern>"
Exclude drivers by pattern ('*' and '?' supported).
.TP
.BR " \-\-exclude\-tids \fI<pattern>"
Exclude topology IDs by pattern ('*' and '?' supported).
.TP
.BR \-n ", " \-\-no\-reconnect
Do not reconnect.
This means that tio will exit if it fails to connect to device or an
established connection is lost.
.TP .TP
.BR \-e ", " "\-\-local\-echo .BR \-e ", " "\-\-local\-echo
@ -103,7 +134,7 @@ Enable local echo.
Enable line timestamp. Enable line timestamp.
.TP .TP
.BR " \-\-timestamp-format \fI<format> .BR " \-\-timestamp\-format \fI<format>"
Set timestamp format to any of the following timestamp formats: Set timestamp format to any of the following timestamp formats:
.RS .RS
@ -117,32 +148,52 @@ Set timestamp format to any of the following timestamp formats:
24-hour format relative to previous timestamp 24-hour format relative to previous timestamp
.IP "\fBiso8601" .IP "\fBiso8601"
ISO8601 format ("YYYY-MM-DDThh:mm:ss.sss") ISO8601 format ("YYYY-MM-DDThh:mm:ss.sss")
.IP "\fBepoch"
Seconds since Unix epoch (1970-01-01)
.IP "\fBepoch-usec"
Seconds since Unix epoch (1970-01-01) with subdivision in microseconds
.PP .PP
Default format is \fB24hour\fR Default format is \fB24hour\fR
.RE .RE
.TP .TP
.BR \-L ", " \-\-list\-devices .BR " \-\-timestamp\-timeout \fI<ms>"
List available serial devices by ID. Set timestamp timeout value in milliseconds.
This value only takes effect in hex output mode where timestamps are only
printed after elapsed timeout time of no output activity from tty device.
Default value is 200.
.TP .TP
.BR \-l ", " \-\-log .BR \-l ", " \-\-list
List available targets (serial devices, TIDs, configuration profiles).
.TP
.BR \-L ", " \-\-log
Enable log to file. Enable log to file.
The filename will be automatically generated using the following format The log file will be automatically named using the following format
tio_DEVICE_YYYY-MM-DDTHH:MM:SS.log. tio_TARGET_YYYY-MM-DDTHH:MM:SS.log. Target being the command line target such
as tty-device, tid, or configuration profile.
The filename can be manually set using the \-\-log-file option. The filename can be manually set using the \-\-log-file option.
.TP .TP
.BR " \-\-log-file \fI<filename> .BR " \-\-log\-file \fI<filename>
Set log filename. Set log filename.
.TP .TP
.BR " \-\-log-append .BR " \-\-log\-directory \fI<path>
Set log directory path in which to save automatically named log files.
.TP
.BR " \-\-log\-append
Append to log file. Append to log file.
@ -154,8 +205,8 @@ Strip control characters and escape sequences from log.
.TP .TP
.BR \-m ", " "\-\-map " \fI<flags> .BR \-m ", " "\-\-map " \fI<flags>
Map (replace, translate) characters on input or output. The following mapping Map (replace, translate) characters on input to the serial device or output
flags are supported: from the serial device. The following mapping flags are supported:
.RS .RS
.TP 12n .TP 12n
@ -163,10 +214,16 @@ flags are supported:
Map CR to NL on input (unless IGNCR is set) Map CR to NL on input (unless IGNCR is set)
.IP "\fBIGNCR" .IP "\fBIGNCR"
Ignore CR on input Ignore CR on input
.IP "\fBIFFESCC"
Map FF to ESC-c on input
.IP "\fBINLCR" .IP "\fBINLCR"
Map NL to CR on input Map NL to CR on input
.IP "\fBINLCRNL" .IP "\fBINLCRNL"
Map NL to CR-NL on input Map NL to CR-NL on input
.IP "\fBICRCRNL"
Map CR to CR-NL on input
.IP "\fBIMSB2LSB"
Map MSB bit order to LSB on input
.IP "\fBOCRNL" .IP "\fBOCRNL"
Map CR to NL on output Map CR to NL on output
.IP "\fBODELBS" .IP "\fBODELBS"
@ -175,19 +232,44 @@ Map DEL to BS on output
Map NL to CR-NL on output Map NL to CR-NL on output
.IP "\fBOLTU" .IP "\fBOLTU"
Map lowercase characters to uppercase on output Map lowercase characters to uppercase on output
.IP "\fBMSB2LSB" .IP "\fBONULBRK"
Map MSB bit order to LSB on output Map nul (zero) to send break signal on output
.IP "\fBOIGNCR"
Ignore CR on output
.P .P
If defining more than one flag, the flags must be comma separated. If defining more than one flag, the flags must be comma separated.
.RE .RE
.TP .TP
.BR \-x ", " \-\-hexadecimal .BR " \-\-input\-mode " normal|hex|line
Enable hexadecimal mode. Set input mode.
In normal mode input characters are sent immediately as they are typed.
In hex input mode bytes can be sent by typing the \fBtwo-character
hexadecimal\fR representation of the 1 byte value, e.g.: to send \fI0xA\fR you
must type \fI0a\fR or \fI0A\fR.
In line input mode input characters are sent when you press enter. The only
editing feature supported in this mode is backspace.
Default value is "normal".
.TP .TP
.BR \-c ", " "\-\-color " \fI0..255|bold|none|list .BR " \-\-output\-mode " normal|hex|hexN
Set output mode.
In hex mode each incoming byte is printed out as a 1 byte hex value.
In hexN mode, N is a number less than or equal to 4096 which defines how many
hex values will be printed before a line break.
Default value is "normal".
.TP
.BR \-c ", " "\-\-color " 0..255|bold|none|list
Colorize tio text using ANSI color code value ranging from 0 to 255 or use Colorize tio text using ANSI color code value ranging from 0 to 255 or use
"none" for no color or use "bold" to apply bold formatting to existing system "none" for no color or use "bold" to apply bold formatting to existing system
@ -225,21 +307,6 @@ If port is 0 or no port is provided default port 3333 is used.
At present there is a hardcoded limit of 16 clients connected at one time. At present there is a hardcoded limit of 16 clients connected at one time.
.RE .RE
.TP
.BR \-r ", " \-\-response-wait
Wait for line response then quit. A line is considered any string terminated
with a NL character. If no line is received tio will quit after response
timeout.
Any tio text is automatically muted when piping a string to tio while in
response mode to make it easy to parse the response.
.TP
.BR " \-\-response\-timeout " \fI<ms>
Set timeout [ms] of line response (default: 100).
.TP .TP
.BR " \-\-rs\-485" .BR " \-\-rs\-485"
@ -277,6 +344,37 @@ will sound the bell twice or blink twice on disconnect.
Default value is "none". Default value is "none".
.TP
.BR "\-\-mute"
Mute tio messages.
.TP
.BR "\-\-script \fI<string>
Run script from string.
.TP
.BR "\-\-script\-file \fI<filename>
Run script from file with filename.
.TP
.BR "\-\-script\-run once|always|never"
Run script on connect once, always, or never.
Default value is "always".
.TP
.BR "\-\-exec \fI<command>
Execute shell command with I/O redirected to device
.TP
.BR "\-\-complete-profiles
Prints profiles (for shell completion)
.TP .TP
.BR \-v ", " \-\-version .BR \-v ", " \-\-version
@ -285,10 +383,10 @@ Display program version.
.BR \-h ", " \-\-help .BR \-h ", " \-\-help
Display help. Display help.
.SH "KEYS" .SH "KEY COMMANDS"
.PP .PP
.TP 16n .TP 16n
In session, all key strokes are forwarded to the serial device except the following key sequence: a prefix key (default: ctrl-t) followed by a command key. These sequences are intercepted as tio commands: In session, all key strokes are forwarded to the serial device except the following key sequence: a prefix key (default: ctrl-t) followed by a command key. These sequences are intercepted as key commands:
.IP "\fBctrl-t ?" .IP "\fBctrl-t ?"
List available key commands List available key commands
.IP "\fBctrl-t b" .IP "\fBctrl-t b"
@ -303,35 +401,105 @@ Toggle log to file
Flush data I/O buffers (discard data written but not transmitted and data received but not read) Flush data I/O buffers (discard data written but not transmitted and data received but not read)
.IP "\fBctrl-t g" .IP "\fBctrl-t g"
Toggle serial port line Toggle serial port line
.IP "\fBctrl-t h" .IP "\fBctrl-t i"
Toggle hexadecimal mode Toggle input mode
.IP "\fBctrl-t l" .IP "\fBctrl-t l"
Clear screen Clear screen
.IP "\fBctrl-t L" .IP "\fBctrl-t L"
Show line states (DTR, RTS, CTS, DSR, DCD, RI) Show line states (DTR, RTS, CTS, DSR, DCD, RI)
.IP "\fBctrl-t m"
Change mapping of characters on input or output
.IP "\fBctrl-t o"
Toggle output mode
.IP "\fBctrl-t p" .IP "\fBctrl-t p"
Pulse serial port line Pulse serial port line
.IP "\fBctrl-t q" .IP "\fBctrl-t q"
Quit Quit
.IP "\fBctrl-t r"
Run script
.IP "\fBctrl-t R"
Execute shell command with I/O redirected to device
.IP "\fBctrl-t s" .IP "\fBctrl-t s"
Show TX/RX statistics Show TX/RX statistics
.IP "\fBctrl-t t" .IP "\fBctrl-t t"
Toggle line timestamp mode Toggle line timestamp mode
.IP "\fBctrl-t U"
Toggle conversion to uppercase on output
.IP "\fBctrl-t v" .IP "\fBctrl-t v"
Show version Show version
.IP "\fBctrl-t x"
Send file using the XMODEM-1K or XMODEM-CRC protocol (prompts for file name and protocol)
.IP "\fBctrl-t y"
Send file using the YMODEM protocol (prompts for file name)
.IP "\fBctrl-t ctrl-t" .IP "\fBctrl-t ctrl-t"
Send ctrl-t character Send ctrl-t character
.SH "HEXADECIMAL MODE" .SH "SCRIPT API"
.PP .PP
In hexadecimal mode each incoming byte is printed out as a hexadecimal value. Tio suppots Lua scripting to easily automate interaction with the tty device.
.PP In addition to the standard Lua API tio makes the following functions
Bytes can be sent in this mode by typing the \fBtwo-character hexadecimal\fR and variables available:
representation of the value, e.g.: to send \fI0xA\fR you must type \fI0a\fR or
\fI0A\fR. .TP 6n
.IP "\fBtio.expect(pattern, timeout)"
Waits for the Lua pattern to match or timeout before continuing.
Timeout is in milliseconds, defaults to 0 meaning it will wait forever.
Returns the captures from the pattern or nil on timeout.
.IP "\fBtio.read(size, timeout)"
Read up to size bytes from serial device. If timeout is 0 or not provided it
will wait forever until data is ready to read.
Returns a string up to size bytes long on success and nil on timeout.
.IP "\fBtio.readline(timeout)"
Read line from serial device. If timeout is 0 or not provided it will wait
forever until data is ready to read.
Returns a string on success and nil on timeout. On timeout a partially read
line may be returned as a second return value.
.IP "\fBtio.write(string)"
Write string to serial device.
Returns the tio table.
.IP "\fBtio.send(file, protocol)"
Send file using x/y-modem protocol.
Protocol can be any of XMODEM_1K, XMODEM_CRC, YMODEM.
.IP "\fBtio.ttysearch()"
Search for serial devices.
Returns a table of number indexed tables, one for each serial device found.
Each of these tables contains the serial device information accessible via the
following string indexed elements "path", "tid", "uptime", "driver",
"description".
Returns nil if no serial devices are found.
.IP "\fBtio.set{line=state, ...}"
Set state of one or multiple tty modem lines.
Line can be any of DTR, RTS, CTS, DSR, CD, RI
State is high, low, or toggle.
.IP "\fBtio.sleep(seconds)"
Sleep for seconds.
.IP "\fBtio.msleep(ms)"
Sleep for milliseconds.
.IP "\fBtio.alwaysecho"
A boolean value, defaults to true.
If tio.alwaysecho is false, the result of tio.read, tio.readline or tio.expect
will only be returned from the function and not logged or printed.
If tio.alwaysecho is set to true, reading functions also emit a single
timestamp to stdout and log file per options.timestamp and options.log.
.SH "CONFIGURATION FILE" .SH "CONFIGURATION FILE"
.PP .PP
@ -347,11 +515,11 @@ listed:
.I $HOME/.tioconfig .I $HOME/.tioconfig
.PP .PP
Labels can be used to group settings into named sub-configurations which can be Labels can be used to group settings into named configuration profiles which
activated from the command-line when starting tio. can be activated from the command-line when starting tio.
.PP .PP
\fBtio\fR will try to match the user input to a sub-configuration by name or by \fBtio\fR will try to match the user input to a configuration profile by name or by
pattern to get the TTY device and other options. pattern to get the TTY device and other options.
.PP .PP
@ -384,12 +552,16 @@ Set output character delay
Set output line delay Set output line delay
.IP "\fBline-pulse-duration" .IP "\fBline-pulse-duration"
Set line pulse duration Set line pulse duration
.IP "\fBno-autoconnect" .IP "\fBno-reconnect"
Disable automatic connect Do not reconnect
.IP "\fBlog" .IP "\fBlog"
Enable log to file Enable log to file
.IP "\fBlog-file" .IP "\fBlog-file"
Set log filename Set log filename
.IP "\fBlog-directory"
Set log directory path in which to save automatically named log files.
.IP "\fBlog-append"
Append to log file
.IP "\fBlog-strip" .IP "\fBlog-strip"
Enable strip of control and escape sequences from log Enable strip of control and escape sequences from log
.IP "\fBlocal-echo" .IP "\fBlocal-echo"
@ -398,26 +570,40 @@ Enable local echo
Enable line timestamp Enable line timestamp
.IP "\fBtimestamp-format" .IP "\fBtimestamp-format"
Set timestamp format Set timestamp format
.IP "\fBtimestamp-timeout"
Set timestamp timeout
.IP "\fBmap" .IP "\fBmap"
Map characters on input or output Map characters on input or output
.IP "\fBcolor" .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 "\fBhexadecimal" .IP "\fBinput-mode"
Enable hexadecimal mode Set input mode
.IP "\fBoutput-mode"
Set output mode
.IP "\fBsocket" .IP "\fBsocket"
Set socket to redirect I/O to Set socket to redirect I/O to
.IP "\fBprefix-ctrl-key" .IP "\fBprefix-ctrl-key"
Set prefix ctrl key (a..z, default: t) Set prefix ctrl key (a..z or 'none', default: t)
.IP "\fBresponse-wait"
Enable wait for line response
.IP "\fBresponse-timeout"
Set line response timeout
.IP "\fBrs-485" .IP "\fBrs-485"
Enable RS-485 mode Enable RS-485 mode
.IP "\fBrs-485-config" .IP "\fBrs-485-config"
Set RS-485 configuration Set RS-485 configuration
.IP "\fBalert" .IP "\fBalert"
Set alert action on connect/disconnect Set alert action on connect/disconnect
.IP "\fBmute"
Mute tio messages
.IP "\fBscript"
Run script from string
.IP "\fBscript-file"
Run script from file
.IP "\fBscript-run"
Run script on connect
.IP "\fBexec"
Execute shell command with I/O redirected to device
.PP
It is possible to include the content of other configuration files using the
include directive like so: "[include <file>]".
.SH "CONFIGURATION FILE EXAMPLES" .SH "CONFIGURATION FILE EXAMPLES"
@ -427,7 +613,7 @@ To change the default configuration simply set options like so:
.RS .RS
.nf .nf
.eo .eo
# Defaults [default]
baudrate = 9600 baudrate = 9600
databits = 8 databits = 8
parity = none parity = none
@ -439,7 +625,7 @@ line-pulse-duration = DTR=200,RTS=400
.RE .RE
.TP .TP
Named sub-configurations can be added via labels: Named configuration profiles can be added via labels:
.RS .RS
.nf .nf
@ -453,7 +639,7 @@ color = 11
.RE .RE
.TP .TP
Activate the sub-configuration by name: Activate the configuration profile by name:
$ tio rpi3 $ tio rpi3
@ -463,31 +649,31 @@ Which is equivalent to:
$ tio -b 115200 -c 11 /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 .TP
A sub-configuration can also be activated by its pattern which supports regular expressions: A configuration profile can also be activated by its pattern which supports regular expressions:
.RS .RS
.nf .nf
.eo .eo
[usb device] [usb-devices]
pattern = usb([0-9]*) pattern = ^usb([0-9]*)
device = /dev/ttyUSB%s device = /dev/ttyUSB%m1
baudrate = 115200 baudrate = 115200
.ec .ec
.fi .fi
.RE .RE
.TP .TP
Activate the sub-configuration by pattern match: Activate the configuration profile by pattern match:
$ tio usb12 $ tio usb12
.TP .TP
Which is equivalent to: Which becomes equivalent to:
$ tio -b 115200 /dev/ttyUSB12 $ tio -b 115200 /dev/ttyUSB12
.TP .TP
It is also possible to combine use of sub-configuration and command-line options. For example: It is also possible to combine use of configuration profile and command-line options. For example:
$ tio -l -t usb12 $ tio -l -t usb12
@ -524,13 +710,13 @@ Or use the expect command to script an interaction:
.nf .nf
.eo .eo
#!/usr/bin/expect -f #!/usr/bin/expect -f
.sp
set timeout -1 set timeout -1
log_user 0 log_user 0
.sp
spawn nc -UN /tmp/tio-socket0 spawn nc -UN /tmp/tio-socket0
set uart $spawn_id set uart $spawn_id
.sp
send -i $uart "date\n" send -i $uart "date\n"
expect -i $uart "prompt> " expect -i $uart "prompt> "
send -i $uart "ls -la\n" send -i $uart "ls -la\n"
@ -539,6 +725,11 @@ expect -i $uart "prompt> "
.fi .fi
.RE .RE
.TP
It is also possible to use tio's own simpler expect/send script functionality to e.g. automate logins:
$ tio --script 'tio.expect("login: "); tio.write("root\\n"); tio.expect("Password: "); tio.write("root\\n")' /dev/ttyUSB0
.TP .TP
Redirect device I/O to network file socket for remote TTY sharing: Redirect device I/O to network file socket for remote TTY sharing:
@ -556,26 +747,35 @@ Pipe command to the serial device:
$ echo "ls -la" | tio /dev/serial/by\-id/usb\-FTDI_TTL232R-3V3_FTGQVXBL\-if00\-port0 $ echo "ls -la" | tio /dev/serial/by\-id/usb\-FTDI_TTL232R-3V3_FTGQVXBL\-if00\-port0
.TP .TP
Pipe command to the serial device and wait for line response (string ending with CR or NL): Pipe command to serial device and wait for line response within 1 second:
$ echo "*IDN?" | tio /dev/ttyACM0 --response-wait $ echo "*IDN?" | tio /dev/ttyACM0 --script "tio.expect('\\r\\n', 1000)" --mute
.TP .TP
In this mode, only the response will be printed.
.TP .TP
Likewise, to pipe data from file to the serial device: Likewise, to pipe data from file to the serial device:
$ cat data.bin | tio /dev/serial/by\-id/usb\-FTDI_TTL232R-3V3_FTGQVXBL\-if00\-port0 $ cat data.bin | tio /dev/serial/by\-id/usb\-FTDI_TTL232R-3V3_FTGQVXBL\-if00\-port0
.TP
Map NL to CR-NL on input from device and DEL to BS on output to device:
$ tio --map INLCRNL,ODELBS /dev/ttyUSB0
.TP .TP
Enable RS-485 mode: Enable RS-485 mode:
$ tio --rs-485 --rs-485-config=RTS_ON_SEND=1,RX_DURING_TX /dev/ttyUSB0 $ tio --rs-485 --rs-485-config=RTS_ON_SEND=1,RX_DURING_TX /dev/ttyUSB0
.TP
Manipulate DTR and RTS lines upon first connect to reset connected microcontroller:
$ tio --script "tio.set{DTR=high,RTS=low}; tio.msleep(100); tio.set{RTS=toggle}" --script-run once /dev/ttyUSB0
.SH "WEBSITE" .SH "WEBSITE"
.PP .PP
Visit https://tio.github.io Visit https://tio.github.io
.SH "AUTHOR" .SH "AUTHOR"
.PP .PP
Created by Martin Lund <martin.lund@keep\-it\-simple.com>. Maintained by Martin Lund <martin.lund@keep\-it\-simple.com>.

View file

@ -1,13 +1,13 @@
tio(1) User Commands tio(1) tio(1) User Commands tio(1)
NAME NAME
tio - a simple serial device I/O tool tio - a serial device I/O tool
SYNOPSIS SYNOPSIS
tio [<options>] <tty-device|sub-config> tio [<options>] <tty-device|profile|tid>
DESCRIPTION DESCRIPTION
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. tio is a 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.
OPTIONS OPTIONS
-b, --baudrate <bps> -b, --baudrate <bps>
@ -62,13 +62,37 @@ OPTIONS
The default pulse duration for each line is 100 ms. The default pulse duration for each line is 100 ms.
-n, --no-autoconnect -a, --auto-connect new|latest|direct
Disable automatic connect. Automatically connect to serial device according to one of the following strategies:
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 disconnects), it will wait for the device to reappear and then reconnect. new Automatically connect to first new appearing serial device.
However, if the --no-autoconnect option is provided, tio will exit if the device is not present or an established connection is lost. latest Automatically connect to latest registered serial device.
direct Connect directly to specified TTY device.
All the listed strategies automatically reconnects according to strategy if device is not available or connection is lost.
Default value is "direct".
--exclude-devices <pattern>
Exclude devices by pattern ('*' and '?' supported).
--exclude-drivers <pattern>
Exclude drivers by pattern ('*' and '?' supported).
--exclude-tids <pattern>
Exclude topology IDs by pattern ('*' and '?' supported).
-n, --no-reconnect
Do not reconnect.
This means that tio will exit if it fails to connect to device or an established connection is lost.
-e, --local-echo -e, --local-echo
@ -90,17 +114,29 @@ OPTIONS
iso8601 ISO8601 format ("YYYY-MM-DDThh:mm:ss.sss") iso8601 ISO8601 format ("YYYY-MM-DDThh:mm:ss.sss")
epoch Seconds since Unix epoch (1970-01-01)
epoch-usec Seconds since Unix epoch (1970-01-01) with subdivision microseconds
Default format is 24hour Default format is 24hour
-L, --list-devices --timestamp-timeout <ms>
List available serial devices by ID. Set timestamp timeout value in milliseconds.
-l, --log This value only takes effect in hex output mode where timestamps are only printed after elapsed timeout time of no output activity from tty device.
Default value is 200.
-l, --list
List available targets (serial devices, TIDs, configuration profiles).
-L, --log
Enable log to file. Enable log to file.
The filename will be automatically generated using the following format tio_DEVICE_YYYY-MM-DDTHH:MM:SS.log. The log file will be automatically named using the following format tio_TARGET_YYYY-MM-DDTHH:MM:SS.log. Target being the command line target such as tty-device, tid, or configuration profile.
The filename can be manually set using the --log-file option. The filename can be manually set using the --log-file option.
@ -108,6 +144,10 @@ OPTIONS
Set log filename. Set log filename.
--log-directory <path>
Set log directory path in which to save automatically named log files.
--log-append --log-append
Append to log file. Append to log file.
@ -118,16 +158,22 @@ OPTIONS
-m, --map <flags> -m, --map <flags>
Map (replace, translate) characters on input or output. The following mapping flags are supported: Map (replace, translate) characters on input to the serial device or output from the serial device. The following mapping flags are supported:
ICRNL Map CR to NL on input (unless IGNCR is set) ICRNL Map CR to NL on input (unless IGNCR is set)
IGNCR Ignore CR on input IGNCR Ignore CR on input
IFFESCC Map FF to ESC-c on input
INLCR Map NL to CR on input INLCR Map NL to CR on input
INLCRNL Map NL to CR-NL on input INLCRNL Map NL to CR-NL on input
ICRCRNL Map CR to CR-NL on input
IMSB2LSB Map MSB bit order to LSB on input
OCRNL Map CR to NL on output OCRNL Map CR to NL on output
ODELBS Map DEL to BS on output ODELBS Map DEL to BS on output
@ -136,13 +182,33 @@ OPTIONS
OLTU Map lowercase characters to uppercase on output OLTU Map lowercase characters to uppercase on output
MSB2LSB Map MSB bit order to LSB on output ONULBRK Map nul (zero) to send break signal on output
OIGNCR Ignore CR on output
If defining more than one flag, the flags must be comma separated. If defining more than one flag, the flags must be comma separated.
-x, --hexadecimal --input-mode normal|hex|line
Enable hexadecimal mode. Set input mode.
In normal mode input characters are sent immediately as they are typed.
In hex input mode bytes can be sent by typing the two-character hexadecimal representation of the 1 byte value, e.g.: to send 0xA you must type 0a or 0A.
In line input mode input characters are sent when you press enter. The only editing feature supported in this mode is backspace.
Default value is "normal".
--output-mode normal|hex|hexN
Set output mode.
In hex mode each incoming byte is printed out as a 1 byte hex value.
In hexN mode, N is a number less than or equal to 4096 which defines how many hex values will be printed before a line break.
Default value is "normal".
-c, --color 0..255|bold|none|list -c, --color 0..255|bold|none|list
@ -156,7 +222,8 @@ OPTIONS
Redirect I/O to socket. 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 ctrl-t sequences are not recognized), and any input from the serial port is multiplexed to the terminal and all connected clients. 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 ctrl-t sequences are not recognized), and any input from the serial port is multi
plexed to the terminal and all connected clients.
Sockets remain open while the serial port is disconnected, and writes will block. Sockets remain open while the serial port is disconnected, and writes will block.
@ -172,16 +239,6 @@ OPTIONS
At present there is a hardcoded limit of 16 clients connected at one time. At present there is a hardcoded limit of 16 clients connected at one time.
-r, --response-wait
Wait for line response then quit. A line is considered any string terminated with a NL character. If no line is received tio will quit after response timeout.
Any tio text is automatically muted when piping a string to tio while in response mode to make it easy to parse the response.
--response-timeout <ms>
Set timeout [ms] of line response (default: 100).
--rs-485 --rs-485
Enable RS-485 mode. Enable RS-485 mode.
@ -210,6 +267,32 @@ OPTIONS
Default value is "none". Default value is "none".
--mute
Mute tio messages.
--script <string>
Run script from string.
--script-file <filename>
Run script from file with filename.
--script-run once|always|never
Run script on connect once, always, or never.
Default value is "always".
--exec <command>
Execute shell command with I/O redirected to device
--complete-profiles
Prints profiles (for shell completion)
-v, --version -v, --version
Display program version. Display program version.
@ -218,8 +301,8 @@ OPTIONS
Display help. Display help.
KEYS KEY COMMANDS
In session, all key strokes are forwarded to the serial device except the following key sequence: a prefix key (default: ctrl-t) followed by a command key. These sequences are intercepted as tio commands: In session, all key strokes are forwarded to the serial device except the following key sequence: a prefix key (default: ctrl-t) followed by a command key. These sequences are intercepted as key commands:
ctrl-t ? List available key commands ctrl-t ? List available key commands
@ -235,30 +318,95 @@ KEYS
ctrl-t g Toggle serial port line ctrl-t g Toggle serial port line
ctrl-t h Toggle hexadecimal mode ctrl-t i Toggle input mode
ctrl-t l Clear screen ctrl-t l Clear screen
ctrl-t L Show line states (DTR, RTS, CTS, DSR, DCD, RI) ctrl-t L Show line states (DTR, RTS, CTS, DSR, DCD, RI)
ctrl-t m Change mapping of characters on input or output
ctrl-t o Toggle output mode
ctrl-t p Pulse serial port line ctrl-t p Pulse serial port line
ctrl-t q Quit ctrl-t q Quit
ctrl-t r Run script
ctrl-t R Execute shell command with I/O redirected to device
ctrl-t s Show TX/RX statistics ctrl-t s Show TX/RX statistics
ctrl-t t Toggle line timestamp mode ctrl-t t Toggle line timestamp mode
ctrl-t U Toggle conversion to uppercase on output
ctrl-t v Show version ctrl-t v Show version
ctrl-t x Send file using the XMODEM-1K or XMODEM-CRC protocol (prompts for file name and protocol)
ctrl-t y Send file using the YMODEM protocol (prompts for file name)
ctrl-t ctrl-t Send ctrl-t character ctrl-t ctrl-t Send ctrl-t character
HEXADECIMAL MODE SCRIPT API
In hexadecimal mode each incoming byte is printed out as a hexadecimal value. Tio suppots Lua scripting to easily automate interaction with the tty device.
Bytes can be sent in this mode by typing the two-character hexadecimal representation of the value, e.g.: to send 0xA you must type 0a or 0A. In addition to the standard Lua API tio makes the following functions available:
expect(string, timeout)
Expect string - waits for string to match or timeout before continuing. Supports regular expressions. Special characters must be escaped with '\\'. Timeout is in milliseconds, defaults to 0 meaning it will wait forever.
Returns 1 on successful match, 0 on timeout, or -1 on error.
On successful match it also returns the match string as second return value.
read(size, timeout)
Read up to size bytes from serial device. If timeout is 0 or not provided it will wait forever until data is ready to read.
Returns number of bytes read on success, 0 on timeout, or -1 on error.
On success, returns read string as second return value. Also emits a single timestamp to stdout and log file per options.timestamp and options.log.
read_line(timeout)
Read line from serial device. If timeout is 0 or not provided it will wait forever until data is ready to read.
Returns number of bytes read on success, 0 on timeout, or -1 on error.
On success, returns the string that was read as second return value. Also emits a single timestamp to stdout and log file per options.timestamp and options.log.
write(string)
Write string to serial device.
Returns number of bytes written on success or -1 on error.
send(file, protocol)
Send file using x/y-modem protocol.
Protocol can be any of XMODEM_1K, XMODEM_CRC, YMODEM.
tty_search()
Search for serial devices.
Returns a table of number indexed tables, one for each serial device found. Each of these tables contains the serial device information accessible via the following string indexed elements "path", "tid", "uptime", "dri
ver", "description".
Returns nil if no serial devices are found.
set{line=state, ...}
Set state of one or multiple tty modem lines.
Line can be any of DTR, RTS, CTS, DSR, CD, RI
State is high, low, or toggle.
sleep(seconds)
Sleep for seconds.
msleep(ms)
Sleep for milliseconds.
exit(code)
Exit with exit code.
CONFIGURATION FILE CONFIGURATION FILE
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: 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:
@ -269,9 +417,9 @@ CONFIGURATION FILE
$HOME/.tioconfig $HOME/.tioconfig
Labels can be used to group settings into named sub-configurations which can be activated from the command-line when starting tio. Labels can be used to group settings into named configuration profiles which can be activated from the command-line when starting tio.
tio will try to match the user input to a sub-configuration by name or by pattern to get the TTY device and other options. tio will try to match the user input to a configuration profile by name or by pattern to get the TTY device and other options.
Options without any label change the default options. Options without any label change the default options.
@ -299,12 +447,16 @@ CONFIGURATION FILE
line-pulse-duration Set line pulse duration line-pulse-duration Set line pulse duration
no-autoconnect Disable automatic connect no-reconnect Do not reconnect
log Enable log to file log Enable log to file
log-file Set log filename log-file Set log filename
log-directory Set log directory path in which to save automatically named log files.
log-append Append to log file
log-strip Enable strip of control and escape sequences from log log-strip Enable strip of control and escape sequences from log
local-echo Enable local echo local-echo Enable local echo
@ -313,19 +465,19 @@ CONFIGURATION FILE
timestamp-format Set timestamp format timestamp-format Set timestamp format
timestamp-timeout Set timestamp timeout
map Map characters on input or output map Map characters on input or output
color Colorize tio text using ANSI color code ranging from 0 to 255 color Colorize tio text using ANSI color code ranging from 0 to 255
hexadecimal Enable hexadecimal mode input-mode Set input mode
output-mode Set output mode
socket Set socket to redirect I/O to socket Set socket to redirect I/O to
prefix-ctrl-key Set prefix ctrl key (a..z, default: t) prefix-ctrl-key Set prefix ctrl key (a..z or 'none', default: t)
response-wait Enable wait for line response
response-timeout Set line response timeout
rs-485 Enable RS-485 mode rs-485 Enable RS-485 mode
@ -333,10 +485,22 @@ CONFIGURATION FILE
alert Set alert action on connect/disconnect alert Set alert action on connect/disconnect
mute Mute tio messages
script Run script from string
script-file Run script from file
script-run Run script on connect
exec Execute shell command with I/O redirected to device
It is possible to include the content of other configuration files using the include directive like so: "[include <file>]".
CONFIGURATION FILE EXAMPLES CONFIGURATION FILE EXAMPLES
To change the default configuration simply set options like so: To change the default configuration simply set options like so:
# Defaults [default]
baudrate = 9600 baudrate = 9600
databits = 8 databits = 8
parity = none parity = none
@ -344,14 +508,14 @@ CONFIGURATION FILE EXAMPLES
color = 10 color = 10
line-pulse-duration = DTR=200,RTS=400 line-pulse-duration = DTR=200,RTS=400
Named sub-configurations can be added via labels: Named configuration profiles can be added via labels:
[rpi3] [rpi3]
device = /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0 device = /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0
baudrate = 115200 baudrate = 115200
color = 11 color = 11
Activate the sub-configuration by name: Activate the configuration profile by name:
$ tio rpi3 $ tio rpi3
@ -359,22 +523,22 @@ CONFIGURATION FILE EXAMPLES
$ tio -b 115200 -c 11 /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
A sub-configuration can also be activated by its pattern which supports regular expressions: A configuration profile can also be activated by its pattern which supports regular expressions:
[usb device] [usb-devices]
pattern = usb([0-9]*) pattern = ^usb([0-9]*)
device = /dev/ttyUSB%s device = /dev/ttyUSB%m1
baudrate = 115200 baudrate = 115200
Activate the sub-configuration by pattern match: Activate the configuration profile by pattern match:
$ tio usb12 $ tio usb12
Which is equivalent to: Which becomes equivalent to:
$ tio -b 115200 /dev/ttyUSB12 $ tio -b 115200 /dev/ttyUSB12
It is also possible to combine use of sub-configuration and command-line options. For example: It is also possible to combine use of configuration profile and command-line options. For example:
$ tio -l -t usb12 $ tio -l -t usb12
@ -416,6 +580,10 @@ EXAMPLES
send -i $uart "ls -la\n" send -i $uart "ls -la\n"
expect -i $uart "prompt> " expect -i $uart "prompt> "
It is also possible to use tio's own simpler expect/send script functionality to e.g. automate logins:
$ tio --script 'expect("login: "); write("root\n"); expect("Password: "); write("root\n")' /dev/ttyUSB0
Redirect device I/O to network file socket for remote TTY sharing: Redirect device I/O to network file socket for remote TTY sharing:
$ tio --socket inet:4444 /dev/ttyUSB0 $ tio --socket inet:4444 /dev/ttyUSB0
@ -428,24 +596,30 @@ EXAMPLES
$ echo "ls -la" | tio /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0 $ echo "ls -la" | tio /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0
Pipe command to the serial device and wait for line response (string ending with CR or NL): Pipe command to serial device and wait for line response within 1 second:
$ echo "*IDN?" | tio /dev/ttyACM0 --response-wait $ echo "*IDN?" | tio /dev/ttyACM0 --script "expect('\r\n', 1000)" --mute
In this mode, only the response will be printed.
Likewise, to pipe data from file to the serial device: Likewise, to pipe data from file to the serial device:
$ cat data.bin | tio /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0 $ cat data.bin | tio /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0
Map NL to CR-NL on input from device and DEL to BS on output to device:
$ tio --map INLCRNL,ODELBS /dev/ttyUSB0
Enable RS-485 mode: Enable RS-485 mode:
$ tio --rs-485 --rs-485-config=RTS_ON_SEND=1,RX_DURING_TX /dev/ttyUSB0 $ tio --rs-485 --rs-485-config=RTS_ON_SEND=1,RX_DURING_TX /dev/ttyUSB0
Manipulate DTR and RTS lines upon first connect to reset connected microcontroller:
$ tio --script "set{DTR=high,RTS=low}; msleep(100); set{RTS=toggle}" --script-run once /dev/ttyUSB0
WEBSITE WEBSITE
Visit https://tio.github.io Visit https://tio.github.io
AUTHOR AUTHOR
Created by Martin Lund <martin.lund@keep-it-simple.com>. Maintained by Martin Lund <martin.lund@keep-it-simple.com>.
tio 2.6 2022-12-17 tio(1) tio 3.9 2025-04-13 tio(1)

View file

@ -1,12 +1,12 @@
project('tio', 'c', project('tio', 'c',
version : '2.6', version : '3.9',
license : [ 'GPL-2'], license : 'GPL-2.0-or-later',
meson_version : '>= 0.53.2', meson_version : '>= 0.53.2',
default_options : [ 'warning_level=2', 'buildtype=release', 'c_std=gnu99' ] default_options : [ 'warning_level=2', 'buildtype=release', 'c_std=gnu99' ]
) )
# The tag date of the project_version(), update when the version bumps. # The tag date of the project_version(), update when the version bumps.
version_date = '2022-12-17' version_date = '2025-04-13'
# Test for dynamic baudrate configuration interface # Test for dynamic baudrate configuration interface
compiler = meson.get_compiler('c') compiler = meson.get_compiler('c')
@ -80,4 +80,8 @@ if host_machine.system() == 'linux'
endif endif
subdir('src') subdir('src')
subdir('man')
install_man_pages = get_option('install_man_pages')
if install_man_pages
subdir('man')
endif

View file

@ -1,3 +1,6 @@
option('bashcompletiondir', option('bashcompletiondir',
type : 'string', type : 'string',
description : 'Directory for bash completion scripts ["no" disables]') description : 'Directory for bash completion scripts ["no" disables]')
option('install_man_pages',
type : 'boolean', value: true,
description : 'Install man pages')

View file

@ -1,5 +1,5 @@
/* /*
* tio - a simple serial terminal I/O tool * tio - a serial device I/O tool
* *
* Copyright (c) 2014-2022 Martin Lund * Copyright (c) 2014-2022 Martin Lund
* *
@ -19,37 +19,10 @@
* 02110-1301, USA. * 02110-1301, USA.
*/ */
#include "config.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include <string.h>
#include "error.h"
#include "print.h"
#include "options.h" #include "options.h"
#include "alert.h"
enum alert_t alert_option_parse(const char *arg)
{
enum alert_t alert = option.alert; // Default
if (arg != NULL)
{
if (strcmp(arg, "none") == 0)
{
return ALERT_NONE;
}
else if (strcmp(arg, "bell") == 0)
{
return ALERT_BELL;
}
else if (strcmp(arg, "blink") == 0)
{
return ALERT_BLINK;
}
}
return alert;
}
void blink_background(void) void blink_background(void)
{ {

View file

@ -1,5 +1,5 @@
/* /*
* tio - a simple serial terminal I/O tool * tio - a serial device I/O tool
* *
* Copyright (c) 2014-2022 Martin Lund * Copyright (c) 2014-2022 Martin Lund
* *
@ -21,14 +21,13 @@
#pragma once #pragma once
enum alert_t typedef enum
{ {
ALERT_NONE, ALERT_NONE,
ALERT_BELL, ALERT_BELL,
ALERT_BLINK, ALERT_BLINK,
ALERT_END, ALERT_END,
}; } alert_t;
enum alert_t alert_option_parse(const char *arg);
void alert_connect(void); void alert_connect(void);
void alert_disconnect(void); void alert_disconnect(void);

View file

@ -18,24 +18,35 @@ _tio()
-o --output-delay \ -o --output-delay \
-o --output-line-delay \ -o --output-line-delay \
--line-pulse-duration \ --line-pulse-duration \
-n --no-autoconnect \ -a --auto-connect \
--exclude-devices \
--exclude-drivers \
--exclude-tids \
-n --no-reconnect \
-e --local-echo \ -e --local-echo \
-l --log \ -l --log \
--log-file \ --log-file \
--log-directory \
--log-append \
--log-strip \ --log-strip \
-m --map \ -m --map \
-t --timestamp \ -t --timestamp \
--timestamp-format \ --timestamp-format \
-L --list-devices \ --timestamp-timeout \
-L --list \
-c --color \ -c --color \
-S --socket \ -S --socket \
-x --hexadecimal \ --input-mode \
-r --response-wait \ --output-mode \
--response-timeout \
--rs-485 \ --rs-485 \
--rs-485-config \ --rs-485-config \
--alert \ --alert \
--mute \ --mute \
--script \
--script-file \
--script-run \
--exec \
--complete-profiles \
-v --version \ -v --version \
-h --help" -h --help"
@ -70,44 +81,16 @@ _tio()
COMPREPLY=( $(compgen -W "1 10 100" -- ${cur}) ) COMPREPLY=( $(compgen -W "1 10 100" -- ${cur}) )
return 0 return 0
;; ;;
--line-pulse-duration) -a | --auto-connect)
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) COMPREPLY=( $(compgen -W "new latest none" -- ${cur}) )
return 0
;;
-n | --no-autoconnect)
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
;;
--log-file)
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
--log-strip)
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0 return 0
;; ;;
-m | --map) -m | --map)
COMPREPLY=( $(compgen -W "ICRNL IGNCR INLCR INLCRNL OCRNL ODELBS ONLCRNL MSB2LSB" -- ${cur}) ) COMPREPLY=( $(compgen -W "ICRNL IGNCR INLCR IFFESCC INLCRNL ICRCRNL IMSB2LSB OCRNL ODELBS ONLCRNL OLTU ONULBRK OIGNCR" -- ${cur}) )
return 0
;;
-t | --timestamp)
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0 return 0
;; ;;
--timestamp-format) --timestamp-format)
COMPREPLY=( $(compgen -W "24hour 24hour-start 24hour-delta iso8601" -- ${cur}) ) COMPREPLY=( $(compgen -W "24hour 24hour-start 24hour-delta iso8601 epoch epoch-usec" -- ${cur}) )
return 0
;;
-L | --list-devices)
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0 return 0
;; ;;
-c | --color) -c | --color)
@ -118,20 +101,12 @@ _tio()
COMPREPLY=( $(compgen -W "unix: inet: inet6:" -- ${cur}) ) COMPREPLY=( $(compgen -W "unix: inet: inet6:" -- ${cur}) )
return 0 return 0
;; ;;
-x | --hexadecimal) --input-mode)
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) COMPREPLY=( $(compgen -W "normal hex line" -- ${cur}) )
return 0 return 0
;; ;;
-r | --response-wait) --output-mode)
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) COMPREPLY=( $(compgen -W "normal hex" -- ${cur}) )
return 0
;;
--response-timeout)
COMPREPLY=( $(compgen -W "1 10 100" -- ${cur}) )
return 0
;;
--rs-485)
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0 return 0
;; ;;
--rs-485-config) --rs-485-config)
@ -142,16 +117,8 @@ _tio()
COMPREPLY=( $(compgen -W "none bell blink" -- ${cur}) ) COMPREPLY=( $(compgen -W "none bell blink" -- ${cur}) )
return 0 return 0
;; ;;
--mute) --script-run)
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) COMPREPLY=( $(compgen -W "once always never" -- ${cur}) )
return 0
;;
-v | --version)
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
-h | --help)
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0 return 0
;; ;;
*) *)
@ -164,14 +131,14 @@ _tio()
;; ;;
esac esac
sub_configs="`tio --complete-sub-configs`" profiles="`tio --complete-profiles`"
if [ -d /dev/serial/by-id ]; then if [ -d /dev/serial/by-id ]; then
ttys=$(printf '%s\n' /dev/tty* /dev/serial/by-id/*) ttys=$(printf '%s\n' /dev/tty* /dev/serial/by-id/*)
else else
ttys=$(printf '%s\n' /dev/tty*) ttys=$(printf '%s\n' /dev/tty*)
fi fi
COMPREPLY=( $(compgen -W "${ttys} ${sub_configs}" -- ${cur}) ) COMPREPLY=( $(compgen -W "${ttys} ${profiles}" -- ${cur}) )
return 0 return 0
} }

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,5 @@
/* /*
* tio - a simple serial terminal I/O tool * tio - a serial device I/O tool
* *
* Copyright (c) 2020 Liam Beguin * Copyright (c) 2020 Liam Beguin
* Copyright (c) 2022 Martin Lund * Copyright (c) 2022 Martin Lund
@ -22,7 +22,17 @@
#pragma once #pragma once
struct config_t
{
char *path;
char *active_group;
char *device;
};
extern struct config_t config;
void config_file_print(void); void config_file_print(void);
void config_file_parse(void); void config_file_parse(void);
void config_exit(void); void config_exit(void);
void config_file_show_sub_configurations(void); void config_file_show_profiles(void);
void config_list_targets(void);

View file

@ -1,5 +1,5 @@
/* /*
* tio - a simple serial terminal I/O tool * tio - a serial device I/O tool
* *
* Copyright (c) 2014-2022 Martin Lund * Copyright (c) 2014-2022 Martin Lund
* *
@ -19,22 +19,18 @@
* 02110-1301, USA. * 02110-1301, USA.
*/ */
#define __STDC_WANT_LIB_EXT2__ 1 // To access vasprintf #define _GNU_SOURCE // To access vasprintf
#include "config.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdbool.h>
#include <string.h>
#include <errno.h>
#include "options.h"
#include "print.h" #include "print.h"
#include "error.h"
#include "timestamp.h"
static char error[2][1000]; static char error[2][1000];
static bool in_session = false; static bool in_session = false;
bool error_normal = true;
void switch_error_output_mode(void)
{
error_normal = false;
}
void error_enter_session_mode(void) void error_enter_session_mode(void)
{ {
@ -93,7 +89,7 @@ void error_exit(void)
/* Print error */ /* Print error */
error_printf_("Error: %s", error[0]); error_printf_("Error: %s", error[0]);
} }
else if ((error[1][0] != 0) && (option.no_autoconnect)) else if ((error[1][0] != 0) && (option.no_reconnect))
{ {
/* Print silent error */ /* Print silent error */
error_printf_("Error: %s", error[1]); error_printf_("Error: %s", error[1]);

View file

@ -1,5 +1,5 @@
/* /*
* tio - a simple serial terminal I/O tool * tio - a serial device I/O tool
* *
* Copyright (c) 2014-2022 Martin Lund * Copyright (c) 2014-2022 Martin Lund
* *
@ -21,6 +21,10 @@
#pragma once #pragma once
#include <stdbool.h>
extern bool error_normal;
#define TIO_SUCCESS 0 #define TIO_SUCCESS 0
#define TIO_ERROR 1 #define TIO_ERROR 1
@ -28,3 +32,4 @@ void tio_error_printf(const char *format, ...);
void tio_error_printf_silent(const char *format, ...); void tio_error_printf_silent(const char *format, ...);
void error_exit(void); void error_exit(void);
void error_enter_session_mode(void); void error_enter_session_mode(void);
void switch_error_output_mode(void);

226
src/fs.c Normal file
View file

@ -0,0 +1,226 @@
/*
* tio - a serial device I/O tool
*
* Copyright (c) 2014-2024 Martin Lund
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
#define _GNU_SOURCE // For statx()
#include "config.h"
#include <dirent.h>
#include <fcntl.h>
#include <regex.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#include <errno.h>
#include <poll.h>
#include <termios.h>
#include "error.h"
#include "print.h"
#include "options.h"
bool fs_dir_exists(const char *path)
{
struct stat st;
if (stat(path, &st) != 0)
{
return false;
}
else if (!S_ISDIR(st.st_mode))
{
return false;
}
return true;
}
// Function to read the content of a file but stripped of newline
ssize_t fs_read_file_stripped(char *buf, size_t bufsiz, const char *format, ...)
{
char filename[PATH_MAX];
int bytes_printed = 0;
va_list args;
va_start(args, format);
bytes_printed = vsnprintf(filename, sizeof(filename), format, args);
va_end(args);
if (bytes_printed < 0)
{
return -1;
}
FILE *file = fopen(filename, "r");
if (!file)
{
return -1;
}
ssize_t length = fread(buf, 1, bufsiz - 1, file);
if (length == -1)
{
fclose(file);
return -1;
}
// Strip any newline
buf[strcspn(buf, "\n")] = 0;
buf[length] = '\0'; // Make sure to null-terminate the string
fclose(file);
return length;
}
bool fs_file_exists(const char *format, ...)
{
char filename[PATH_MAX];
int bytes_printed = 0;
struct stat st;
va_list args;
va_start(args, format);
bytes_printed = vsnprintf(filename, sizeof(filename), format, args);
va_end(args);
if (bytes_printed < 0)
{
return false;
}
return stat(filename, &st) == 0;
}
char* fs_search_directory(const char *dir_path, const char *dirname)
{
struct dirent *entry;
char path[PATH_MAX];
struct stat st;
DIR *dir;
if ((dir = opendir(dir_path)) == NULL)
{
// Error opening directory
return NULL;
}
while ((entry = readdir(dir)) != NULL)
{
snprintf(path, PATH_MAX, "%s/%s", dir_path, entry->d_name);
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
{
continue;
}
if (lstat(path, &st) == -1)
{
// Error getting directory status
closedir(dir);
return NULL;
}
if (S_ISLNK(st.st_mode))
{
// Skip symbolic links
continue;
}
if (S_ISDIR(st.st_mode))
{
// If it's a directory, check if it's the one we're looking for
if (strcmp(entry->d_name, dirname) == 0)
{
char *result = strndup(path, PATH_MAX);
closedir(dir);
return result;
}
else
{
// Recursively search within directories
char* result = fs_search_directory(path, dirname);
if (result != NULL)
{
closedir(dir);
return result;
}
}
}
}
closedir(dir);
return NULL;
}
#if defined(__linux__) && defined(STATX_BTIME)
// Function to return creation time of file
double fs_get_creation_time(const char *path)
{
struct statx stx;
int fd = open(path, O_RDONLY);
if (fd == -1)
{
return -1;
}
if (statx(fd, "", AT_EMPTY_PATH, STATX_ALL, &stx) != 0)
{
close(fd);
return -1;
}
// Close the file
close(fd);
return stx.stx_btime.tv_sec + stx.stx_btime.tv_nsec / 1e9;
}
#elif defined(__APPLE__) || defined(__MACH__)
double fs_get_creation_time(const char *path)
{
struct stat st;
if (stat(path, &st) != 0)
{
return -1;
}
return st.st_mtimespec.tv_sec + st.st_mtimespec.tv_nsec / 1e9;
}
#else
double fs_get_creation_time(const char *path)
{
struct stat st;
if (stat(path, &st) != 0)
{
return -1;
}
return (double) st.st_ctime;
}
#endif

31
src/fs.h Normal file
View file

@ -0,0 +1,31 @@
/*
* tio - a serial device I/O tool
*
* Copyright (c) 2014-2024 Martin Lund
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
#pragma once
#include <stdbool.h>
#include <stdio.h>
bool fs_dir_exists(const char *path);
bool fs_file_exists(const char *format, ...);
char* fs_search_directory(const char *dir_path, const char *dirname);
ssize_t fs_read_file_stripped(char *buf, size_t bufsiz, const char *format, ...);
double fs_get_creation_time(const char *path);

View file

@ -1,5 +1,5 @@
/* /*
* tio - a simple serial terminal I/O tool * tio - a serial device I/O tool
* *
* Copyright (c) 2014-2022 Martin Lund * Copyright (c) 2014-2022 Martin Lund
* *
@ -19,20 +19,12 @@
* 02110-1301, USA. * 02110-1301, USA.
*/ */
#define __STDC_WANT_LIB_EXT2__ 1 // To access vasprintf #define _GNU_SOURCE // To access vasprintf
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <sys/time.h> #include <sys/time.h>
#include <libgen.h> #include <libgen.h>
#include "options.h" #include <errno.h>
#include "print.h" #include "print.h"
#include "error.h" #include "fs.h"
#define IS_ESC_CSI_INTERMEDIATE_CHAR(c) ((c >= 0x20) && (c <= 0x3F)) #define IS_ESC_CSI_INTERMEDIATE_CHAR(c) ((c >= 0x20) && (c <= 0x3F))
#define IS_ESC_END_CHAR(c) ((c >= 0x30) && (c <= 0x7E)) #define IS_ESC_END_CHAR(c) ((c >= 0x30) && (c <= 0x7E))
@ -58,13 +50,41 @@ static char *date_time(void)
int log_open(const char *filename) int log_open(const char *filename)
{ {
static char automatic_filename[400]; char *automatic_filename;
char *dir_plus_automatic_filename;
if (filename == NULL) if (filename == NULL)
{ {
// Generate filename if none provided ("tio_DEVICE_YYYY-MM-DDTHH:MM:SS.log") // Generate filename if none provided
sprintf(automatic_filename, "tio_%s_%s.log", basename((char *)option.tty_device), date_time()); if (option.auto_connect == AUTO_CONNECT_DIRECT)
filename = automatic_filename; {
// File name format ("tio_TARGET_YYYY-MM-DDTHH:MM:SS.log")
asprintf(&automatic_filename, "tio_%s_%s.log", basename((char *)option.target), date_time());
}
else
{
// If using 'new' or 'latest' autoconnect strategy we simply use strategy
// name to name autogenerated log name as device names may vary
asprintf(&automatic_filename, "tio_%s_%s.log",
option_auto_connect_state_to_string(option.auto_connect),
date_time());
}
if (option.log_directory != NULL)
{
if (fs_dir_exists(option.log_directory) == false)
{
tio_error_printf("Log directory not found");
exit(EXIT_FAILURE);
}
asprintf(&dir_plus_automatic_filename, "%s/%s", option.log_directory, automatic_filename);
filename = dir_plus_automatic_filename;
}
else
{
filename = automatic_filename;
}
} }
log_filename = filename; log_filename = filename;
@ -72,12 +92,12 @@ int log_open(const char *filename)
// Open log file // Open log file
if (option.log_append) if (option.log_append)
{ {
// Appends to existing log file // Append to existing log file
fp = fopen(filename, "a+"); fp = fopen(filename, "a+");
} }
else else
{ {
// Truncates existing log file // Truncate existing log file
fp = fopen(filename, "w+"); fp = fopen(filename, "w+");
} }
if (fp == NULL) if (fp == NULL)
@ -177,16 +197,23 @@ void log_putc(char c)
return; return;
} }
if (option.log_strip) if (option.output_mode == OUTPUT_MODE_HEX)
{ {
if (!log_strip(c)) fprintf(fp, "%02x ", (unsigned char) c);
{
fputc(c, fp);
}
} }
else else
{ {
fputc(c, fp); if (option.log_strip)
{
if (log_strip(c) == false)
{
fputc(c, fp);
}
}
else
{
fputc(c, fp);
}
} }
} }
@ -195,6 +222,7 @@ void log_close(void)
if (fp != NULL) if (fp != NULL)
{ {
fclose(fp); fclose(fp);
tio_printf("Saved log to file %s", log_filename);
fp = NULL; fp = NULL;
log_filename = NULL; log_filename = NULL;
} }
@ -202,9 +230,8 @@ void log_close(void)
void log_exit(void) void log_exit(void)
{ {
if (option.log) if ((option.log) && (log_filename != NULL))
{ {
tio_printf("Saved log to file %s", log_filename);
log_close(); log_close();
} }
} }

View file

@ -1,5 +1,5 @@
/* /*
* tio - a simple serial terminal I/O tool * tio - a serial device I/O tool
* *
* Copyright (c) 2014-2022 Martin Lund * Copyright (c) 2014-2022 Martin Lund
* *

View file

@ -1,5 +1,5 @@
/* /*
* tio - a simple serial terminal I/O tool * tio - a serial device I/O tool
* *
* Copyright (c) 2014-2022 Martin Lund * Copyright (c) 2014-2022 Martin Lund
* *
@ -19,10 +19,11 @@
* 02110-1301, USA. * 02110-1301, USA.
*/ */
#include "config.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include "version.h"
#include "config.h"
#include "options.h" #include "options.h"
#include "configfile.h" #include "configfile.h"
#include "tty.h" #include "tty.h"
@ -45,9 +46,9 @@ int main(int argc, char *argv[])
/* Parse command-line options (1st pass) */ /* Parse command-line options (1st pass) */
options_parse(argc, argv); options_parse(argc, argv);
if (option.complete_sub_configs) if (option.complete_profiles)
{ {
config_file_show_sub_configurations(); config_file_show_profiles();
return status; return status;
} }
@ -60,6 +61,10 @@ int main(int argc, char *argv[])
/* Configure tty device */ /* Configure tty device */
tty_configure(); tty_configure();
/* Disable line buffering in stdout. This is necessary if we
* want things like local echo to work correctly. */
setvbuf(stdout, NULL, _IONBF, 0);
/* Configure input terminal */ /* Configure input terminal */
if (isatty(fileno(stdin))) if (isatty(fileno(stdin)))
{ {
@ -69,14 +74,11 @@ int main(int argc, char *argv[])
{ {
// Enter non interactive mode // Enter non interactive mode
interactive_mode = false; interactive_mode = false;
// Mute tio text in response mode
if (option.response_wait)
{
option.mute = true;
}
} }
/* Switch error output format */
switch_error_output_mode();
/* Configure output terminal */ /* Configure output terminal */
if (isatty(fileno(stdout))) if (isatty(fileno(stdout)))
{ {
@ -104,7 +106,7 @@ int main(int argc, char *argv[])
error_enter_session_mode(); error_enter_session_mode();
/* Print launch hints */ /* Print launch hints */
tio_printf("tio v%s", VERSION); tio_printf("tio %s", VERSION);
if (interactive_mode) if (interactive_mode)
{ {
tio_printf("Press ctrl-%c q to quit", option.prefix_key); tio_printf("Press ctrl-%c q to quit", option.prefix_key);
@ -127,8 +129,9 @@ int main(int argc, char *argv[])
tty_input_thread_wait_ready(); tty_input_thread_wait_ready();
/* Connect to tty device */ /* Connect to tty device */
if (option.no_autoconnect) if (option.no_reconnect)
{ {
tty_search();
status = tty_connect(); status = tty_connect();
} }
else else
@ -137,7 +140,7 @@ int main(int argc, char *argv[])
while (true) while (true)
{ {
tty_wait_for_device(); tty_wait_for_device();
status = tty_connect(); tty_connect();
} }
} }

View file

@ -1,5 +1,10 @@
# Generate version header
version_h = vcs_tag(command : ['git', 'describe', '--tags', '--always', '--dirty'],
input : 'version.h.in',
output :'version.h',
replace_string:'@VERSION@')
config_h = configuration_data() config_h = configuration_data()
config_h.set_quoted('VERSION', meson.project_version())
config_h.set('BAUDRATE_CASES', baudrate_cases) config_h.set('BAUDRATE_CASES', baudrate_cases)
configure_file(output: 'config.h', configuration: config_h) configure_file(output: 'config.h', configuration: config_h)
@ -17,17 +22,38 @@ tio_sources = [
'setspeed.c', 'setspeed.c',
'rs485.c', 'rs485.c',
'timestamp.c', 'timestamp.c',
'alert.c' 'alert.c',
'xymodem.c',
'script.c',
'fs.c',
'readline.c',
version_h
] ]
foreach name: ['lua-5.4', 'lua-5.3', 'lua-5.2', 'lua-5.1', 'lua']
lua_dep = dependency(name, version: '>=5.1', required: false)
if lua_dep.found()
break
endif
endforeach
if not lua_dep.found()
error('Lua could not be found!')
endif
tio_dep = [ tio_dep = [
dependency('threads', required: true), dependency('threads', required: true),
dependency('inih', required: true, dependency('glib-2.0', required: true),
fallback : ['libinih', 'inih_dep'], lua_dep
default_options: ['default_library=static', 'distro_install=false'])
] ]
tio_c_args = ['-Wno-unused-result'] if host_machine.system() == 'darwin'
iokit_dep = dependency('appleframeworks', modules: ['IOKit'], required: true)
corefoundation_dep = dependency('appleframeworks', modules: ['CoreFoundation'], required: true)
tio_dep += [iokit_dep, corefoundation_dep]
endif
tio_c_args = ['-Wshadow','-Wno-unused-result']
if enable_setspeed2 if enable_setspeed2
tio_c_args += '-DHAVE_TERMIOS2' tio_c_args += '-DHAVE_TERMIOS2'

View file

@ -1,5 +1,5 @@
/* /*
* tio - a simple serial terminal I/O tool * tio - a serial device I/O tool
* *
* Copyright (c) 2014-2022 Martin Lund * Copyright (c) 2014-2022 Martin Lund
* *
@ -19,16 +19,15 @@
* 02110-1301, USA. * 02110-1301, USA.
*/ */
#include "config.h" #define _GNU_SOURCE // For FNM_EXTMATCH
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include <string.h> #include <string.h>
#include <time.h> #include <poll.h>
#include <sys/wait.h>
#include <fnmatch.h>
#include <regex.h>
#include <errno.h> #include <errno.h>
#include "error.h"
#include "print.h" #include "print.h"
#include "options.h"
void delay(long ms) void delay(long ms)
{ {
@ -45,22 +44,6 @@ void delay(long ms)
nanosleep(&ts, NULL); nanosleep(&ts, NULL);
} }
long string_to_long(char *string)
{
long result;
char *end_token;
errno = 0;
result = strtol(string, &end_token, 10);
if ((errno != 0) || (*end_token != 0))
{
printf("Error: Invalid digit\n");
exit(EXIT_FAILURE);
}
return result;
}
int ctrl_key_code(unsigned char key) int ctrl_key_code(unsigned char key)
{ {
if ((key >= 'a') && (key <= 'z')) if ((key >= 'a') && (key <= 'z'))
@ -70,3 +53,201 @@ int ctrl_key_code(unsigned char key)
return -1; return -1;
} }
bool regex_match(const char *string, const char *pattern)
{
regex_t regex;
int status;
if (regcomp(&regex, pattern, REG_EXTENDED | REG_NOSUB) != 0)
{
// No match
return false;
}
status = regexec(&regex, string, (size_t) 0, NULL, 0);
regfree(&regex);
if (status != 0)
{
// No match
return false;
}
// Match
return true;
}
int read_poll(int fd, void *data, size_t len, int timeout)
{
struct pollfd fds;
int ret = 0;
fds.events = POLLIN;
fds.fd = fd;
/* Wait data available */
ret = poll(&fds, 1, timeout);
if (ret < 0)
{
tio_error_print("%s", strerror(errno));
return ret;
}
else if (ret > 0)
{
if (fds.revents & POLLIN)
{
// Read ready data
return read(fd, data, len);
}
}
/* Timeout */
return ret;
}
// Function to calculate djb2 hash of string
unsigned long djb2_hash(const unsigned char *str)
{
unsigned long hash = 5381;
int c;
while ((c = *str++))
{
hash = ((hash << 5) + hash) + c; // hash * 33 + c
}
return hash;
}
// Function to encode a number to base62
void *base62_encode(unsigned long num, char *output)
{
const char base62_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
if (output == NULL)
{
tio_error_print("Memory allocation failed");
exit(EXIT_FAILURE);
}
for (int i = 0; i < 4; ++i)
{
output[i] = base62_chars[num % 62];
num /= 62;
}
output[4] = '\0';
return output;
}
// Function to return current time
double get_current_time(void)
{
struct timespec current_time_ts;
if (clock_gettime(CLOCK_REALTIME, &current_time_ts) == -1)
{
// Error
return -1;
}
return current_time_ts.tv_sec + current_time_ts.tv_nsec / 1e9;
}
bool match_patterns(const char *string, const char *patterns)
{
char *pattern;
char *patterns_copy;
if ((string == NULL) || (patterns == NULL))
{
return false;
}
patterns_copy = strdup(patterns);
// Tokenize the patterns string using strtok
pattern = strtok(patterns_copy, ",");
while (pattern != NULL)
{
// Check if the string matches the current pattern
#ifdef FNM_EXTMATCH
if (fnmatch(pattern, string, FNM_EXTMATCH) == 0)
#else
if (fnmatch(pattern, string, 0) == 0)
#endif
{
free(patterns_copy);
return true;
}
// Move to the next pattern
pattern = strtok(NULL, ",");
}
free(patterns_copy);
return false;
}
// Function that forks subprocess, redirects its stdin and stdout to the
// specified filedescriptor, and runs command.
int execute_shell_command(int fd, const char *command)
{
pid_t pid;
int status;
// Fork a child process
pid = fork();
if (pid == -1)
{
// Error occurred
tio_error_print("fork() failed (%s)", strerror(errno));
exit(EXIT_FAILURE);
}
else if (pid == 0)
{
// Child process
tio_printf("Executing shell command '%s'", command);
// Redirect stdout and stderr to the file descriptor
if (dup2(fd, STDOUT_FILENO) == -1 || dup2(fd, STDERR_FILENO) == -1)
{
tio_error_print("dup2() failed (%s)", strerror(errno));
exit(EXIT_FAILURE);
}
// Execute the shell command
execl("/bin/sh", "sh", "-c", command, (char *)NULL);
// If execlp() returns, it means an error occurred
perror("execlp");
tio_error_print("execlp() failed (%s)", strerror(errno));
exit(EXIT_FAILURE);
}
else
{
// Parent process
// Wait for the child process to finish
waitpid(pid, &status, 0);
if (WIFEXITED(status))
{
tio_printf("Command exited with status %d", WEXITSTATUS(status));
return WEXITSTATUS(status);
}
else
{
tio_error_printf("Child process exited abnormally\n");
return -1;
}
}
return 0;
}
void clear_line()
{
print("\r\033[K");
}

View file

@ -1,5 +1,5 @@
/* /*
* tio - a simple serial terminal I/O tool * tio - a serial device I/O tool
* *
* Copyright (c) 2014-2022 Martin Lund * Copyright (c) 2014-2022 Martin Lund
* *
@ -21,11 +21,18 @@
#pragma once #pragma once
#include <stdbool.h>
#include <stdio.h>
#define UNUSED(expr) do { (void)(expr); } while (0) #define UNUSED(expr) do { (void)(expr); } while (0)
char * current_time(void);
void delay(long ms); void delay(long ms);
long string_to_long(char *string);
int ctrl_key_code(unsigned char key); int ctrl_key_code(unsigned char key);
void alert_connect(void); bool regex_match(const char *string, const char *pattern);
void alert_disconnect(void); unsigned long djb2_hash(const unsigned char *str);
void *base62_encode(unsigned long num, char *output);
int read_poll(int fd, void *data, size_t len, int timeout);
double get_current_time(void);
bool match_patterns(const char *string, const char *patterns);
int execute_shell_command(int fd, const char *command);
void clear_line();

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,5 @@
/* /*
* tio - a simple serial terminal I/O tool * tio - a serial device I/O tool
* *
* Copyright (c) 2014-2022 Martin Lund * Copyright (c) 2014-2022 Martin Lund
* *
@ -23,51 +23,89 @@
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include <limits.h> #include "script.h"
#include <termios.h>
#include <sys/param.h>
#include "timestamp.h" #include "timestamp.h"
#include "alert.h" #include "alert.h"
#include "tty.h"
typedef enum
{
INPUT_MODE_NORMAL,
INPUT_MODE_HEX,
INPUT_MODE_LINE,
INPUT_MODE_END,
} input_mode_t;
typedef enum
{
OUTPUT_MODE_NORMAL,
OUTPUT_MODE_HEX,
OUTPUT_MODE_END,
} output_mode_t;
/* Options */ /* Options */
struct option_t struct option_t
{ {
const char *tty_device; char *target;
unsigned int baudrate; int baudrate;
int databits; int databits;
char *flow; flow_t flow;
int stopbits; int stopbits;
char *parity; parity_t parity;
int output_delay; int output_delay;
int output_line_delay; int output_line_delay;
unsigned int dtr_pulse_duration; int dtr_pulse_duration;
unsigned int rts_pulse_duration; int rts_pulse_duration;
unsigned int cts_pulse_duration; int cts_pulse_duration;
unsigned int dsr_pulse_duration; int dsr_pulse_duration;
unsigned int dcd_pulse_duration; int dcd_pulse_duration;
unsigned int ri_pulse_duration; int ri_pulse_duration;
bool no_autoconnect; bool no_reconnect;
auto_connect_t auto_connect;
bool log; bool log;
bool log_append; bool log_append;
bool log_strip; bool log_strip;
bool local_echo; bool local_echo;
enum timestamp_t timestamp; timestamp_t timestamp;
const char *log_filename; char *log_filename;
const char *map; char *log_directory;
const char *socket; char *socket;
int color; int color;
bool hex_mode; input_mode_t input_mode;
unsigned char prefix_code; output_mode_t output_mode;
unsigned char prefix_key; char prefix_code;
bool response_wait; char prefix_key;
int response_timeout; bool prefix_enabled;
bool mute; bool mute;
bool rs485; bool rs485;
uint32_t rs485_config_flags; uint32_t rs485_config_flags;
int32_t rs485_delay_rts_before_send; int32_t rs485_delay_rts_before_send;
int32_t rs485_delay_rts_after_send; int32_t rs485_delay_rts_after_send;
enum alert_t alert; alert_t alert;
bool complete_sub_configs; bool complete_profiles;
char *script;
char *script_filename;
script_run_t script_run;
int timestamp_timeout;
char *exclude_devices;
char *exclude_drivers;
char *exclude_tids;
int hex_n_value;
bool vt100;
char *exec;
bool map_i_nl_cr;
bool map_i_cr_nl;
bool map_ign_cr;
bool map_i_ff_escc;
bool map_i_nl_crnl;
bool map_i_cr_crnl;
bool map_o_cr_nl;
bool map_o_nl_crnl;
bool map_o_del_bs;
bool map_o_ltu;
bool map_o_nulbrk;
bool map_i_msb2lsb;
bool map_o_ign_cr;
}; };
extern struct option_t option; extern struct option_t option;
@ -76,4 +114,22 @@ void options_print();
void options_parse(int argc, char *argv[]); void options_parse(int argc, char *argv[]);
void options_parse_final(int argc, char *argv[]); void options_parse_final(int argc, char *argv[]);
void line_pulse_duration_option_parse(const char *arg); int option_string_to_integer(const char *string, int *value, const char *desc, int min, int max);
void option_parse_flow(const char *arg, flow_t *flow);
void option_parse_parity(const char *arg, parity_t *parity);
void option_parse_output_mode(const char *arg, output_mode_t *mode);
void option_parse_input_mode(const char *arg, input_mode_t *mode);
void option_parse_line_pulse_duration(const char *arg);
void option_parse_script_run(const char *arg, script_run_t *script_run);
void option_parse_alert(const char *arg, alert_t *alert);
void option_parse_auto_connect(const char *arg, auto_connect_t *auto_connect);
const char *option_auto_connect_state_to_string(auto_connect_t strategy);
void option_parse_timestamp(const char *arg, timestamp_t *timestamp);
const char* option_timestamp_format_to_string(timestamp_t timestamp);
void option_parse_mappings(const char *map);

View file

@ -1,5 +1,5 @@
/* /*
* tio - a simple serial terminal I/O tool * tio - a serial device I/O tool
* *
* Copyright (c) 2014-2022 Martin Lund * Copyright (c) 2014-2022 Martin Lund
* *
@ -19,11 +19,6 @@
* 02110-1301, USA. * 02110-1301, USA.
*/ */
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include "options.h"
#include "print.h" #include "print.h"
bool print_tainted = false; bool print_tainted = false;
@ -31,11 +26,13 @@ char ansi_format[30];
void print_hex(char c) void print_hex(char c)
{ {
print_tainted = true;
printf("%02x ", (unsigned char) c); printf("%02x ", (unsigned char) c);
} }
void print_normal(char c) void print_normal(char c)
{ {
print_tainted = true;
putchar(c); putchar(c);
} }
@ -73,3 +70,43 @@ void tio_printf_array(const char *array)
} }
tio_printf(""); tio_printf("");
} }
void print_tainted_set()
{
print_tainted = true;
}
void print(const char *format, ...)
{
va_list args;
va_start(args, format);
vprintf(format, args);
fflush(stdout);
va_end(args);
print_tainted = true;
}
void print_padded(char *string, size_t length, char pad_char)
{
size_t padding = 0;
size_t string_length = 0;
size_t i;
string_length = strlen(string);
if (string_length < length)
{
padding += length - string_length;
printf("%s", string);
for (i=0; i<padding; i++)
{
putchar(pad_char);
}
}
else
{
printf("%s", string);
}
}

View file

@ -1,5 +1,5 @@
/* /*
* tio - a simple serial terminal I/O tool * tio - a serial device I/O tool
* *
* Copyright (c) 2014-2022 Martin Lund * Copyright (c) 2014-2022 Martin Lund
* *
@ -23,7 +23,6 @@
#include <stdio.h> #include <stdio.h>
#include <stdbool.h> #include <stdbool.h>
#include "misc.h"
#include "error.h" #include "error.h"
#include "options.h" #include "options.h"
#include "timestamp.h" #include "timestamp.h"
@ -77,6 +76,29 @@ extern char ansi_format[];
fprintf (stdout, "\r[%s] Warning: " format "\r\n", timestamp_current_time(), ## args); \ fprintf (stdout, "\r[%s] Warning: " format "\r\n", timestamp_current_time(), ## args); \
else \ else \
ansi_printf("[%s] Warning: " format, timestamp_current_time(), ## args); \ ansi_printf("[%s] Warning: " format, timestamp_current_time(), ## args); \
print_tainted = false; \
} \
}
#define tio_error_print(format, args...) \
{ \
if (!option.mute) \
{ \
if (print_tainted) \
putchar('\n'); \
if (option.color < 0) { \
if (error_normal) \
fprintf (stderr, "Error: " format "\n", ## args); \
else \
fprintf (stderr, "\r[%s] Error: " format "\r\n", timestamp_current_time(), ## args); \
} \
else { \
if (error_normal) \
{ ansi_error_printf("Error: " format, ## args); }\
else \
{ ansi_error_printf("[%s] Error: " format, timestamp_current_time(), ## args); }\
} \
print_tainted = false; \
} \ } \
} }
@ -112,7 +134,10 @@ extern char ansi_format[];
#define tio_debug_printf_raw(format, args...) #define tio_debug_printf_raw(format, args...)
#endif #endif
void print(const char *format, ...);
void print_hex(char c); void print_hex(char c);
void print_normal(char c); void print_normal(char c);
void print_init_ansi_formatting(void); void print_init_ansi_formatting(void);
void tio_printf_array(const char *array); void tio_printf_array(const char *array);
void print_tainted_set(void);
void print_padded(char *string, size_t length, char pad_char);

276
src/readline.c Normal file
View file

@ -0,0 +1,276 @@
/*
* tio - a serial device I/O tool
*
* Copyright (c) 2014-2024 Martin Lund
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
#include "print.h"
#include "misc.h"
#define RL_LINE_LENGTH_MAX PATH_MAX
#define RL_HISTORY_MAX 1000
static char rl_line[RL_LINE_LENGTH_MAX] = {};
static char *rl_history[RL_HISTORY_MAX];
static int rl_history_count = 0;
static int rl_history_index = 0;
static int rl_line_length = 0;
static int rl_cursor_pos = 0;
static int rl_escape = 0;
static void print_line(const char *string, int cursor_pos)
{
clear_line();
print("%s", string);
print("\r"); // Move the cursor back to the beginning
for (int i = 0; i < cursor_pos; ++i)
{
print("\x1b[C"); // Move the cursor right
}
}
void readline_init(void)
{
rl_history_count = 0;
rl_history_index = 0;
for (int i = 0; i < RL_HISTORY_MAX; ++i)
{
rl_history[i] = NULL;
}
rl_line[0] = 0;
rl_line_length = 0;
rl_cursor_pos = 0;
rl_escape = 0;
}
char * readline_get(void)
{
return rl_line;
}
static void readline_input_char(char input_char)
{
if (rl_line_length < RL_LINE_LENGTH_MAX - 1)
{
memmove(&rl_line[rl_cursor_pos + 1], &rl_line[rl_cursor_pos], rl_line_length - rl_cursor_pos);
rl_line[rl_cursor_pos] = input_char;
rl_line_length++;
rl_cursor_pos++;
rl_line[rl_line_length] = '\0';
print_line(rl_line, rl_cursor_pos);
}
rl_escape = 0;
}
static void readline_input_cr(void)
{
if (rl_line_length > 0)
{
// Save to history
if (rl_history_count < RL_HISTORY_MAX)
{
rl_history[rl_history_count] = strndup(rl_line, rl_line_length);
rl_history_count++;
}
else
{
free(rl_history[0]);
memmove(&rl_history[0], &rl_history[1], (RL_HISTORY_MAX - 1) * sizeof(char*));
rl_history[RL_HISTORY_MAX - 1] = strndup(rl_line, rl_line_length);
}
}
rl_line[rl_line_length] = '\0';
if (option.local_echo == false)
{
clear_line();
}
else
{
print("\r\n");
}
rl_line_length = 0;
rl_cursor_pos = 0;
rl_history_index = rl_history_count;
rl_escape = 0;
}
static void readline_input_bs(void)
{
if (rl_cursor_pos > 0)
{
memmove(&rl_line[rl_cursor_pos - 1], &rl_line[rl_cursor_pos], rl_line_length - rl_cursor_pos);
rl_line_length--;
rl_cursor_pos--;
rl_line[rl_line_length] = '\0';
print_line(rl_line, rl_cursor_pos);
}
rl_escape = 0;
}
static void readline_input_escape(void)
{
rl_escape = 1;
}
static void readline_input_left_bracket(void)
{
if (rl_escape == 1)
{
rl_escape = 2;
}
else
{
rl_escape = 0;
}
}
static void readline_input_A(void)
{
if (rl_escape == 2)
{
// Up arrow
if (rl_history_index > 0)
{
rl_history_index--;
strncpy(rl_line, rl_history[rl_history_index], RL_LINE_LENGTH_MAX-1);
rl_line_length = strlen(rl_line);
rl_cursor_pos = rl_line_length;
print_line(rl_line, rl_cursor_pos);
}
}
else
{
readline_input_char('A');
}
rl_escape = 0;
}
static void readline_input_B(void)
{
if (rl_escape == 2)
{
// Down arrow
if (rl_history_index < rl_history_count - 1)
{
rl_history_index++;
strncpy(rl_line, rl_history[rl_history_index], RL_LINE_LENGTH_MAX-1);
rl_line_length = strlen(rl_line);
rl_cursor_pos = rl_line_length;
print_line(rl_line, rl_cursor_pos);
}
else if (rl_history_index == rl_history_count - 1)
{
rl_history_index++;
rl_line_length = 0;
rl_cursor_pos = 0;
rl_line[rl_line_length] = '\0';
print_line(rl_line, rl_cursor_pos);
}
}
else
{
readline_input_char('B');
}
rl_escape = 0;
}
static void readline_input_C(void)
{
if (rl_escape == 2)
{
// Right arrow
if (rl_cursor_pos < rl_line_length)
{
rl_cursor_pos++;
print("\x1b[C");
}
}
else
{
readline_input_char('C');
}
rl_escape = 0;
}
static void readline_input_D(void)
{
if (rl_escape == 2)
{
// Left arrow
if (rl_cursor_pos > 0)
{
rl_cursor_pos--;
print("\b");
}
}
else
{
readline_input_char('D');
}
rl_escape = 0;
}
void readline_input(char input_char)
{
switch (input_char)
{
case '\r': // Carriage return
readline_input_cr();
break;
case 127: // Backspace
readline_input_bs();
break;
case 27: // Escape
readline_input_escape();
break;
case '[':
readline_input_left_bracket();
break;
case 'A':
readline_input_A();
break;
case 'B':
readline_input_B();
break;
case 'C':
readline_input_C();
break;
case 'D':
readline_input_D();
break;
default:
readline_input_char(input_char);
break;
}
}

26
src/readline.h Normal file
View file

@ -0,0 +1,26 @@
/*
* tio - a serial device I/O tool
*
* Copyright (c) 2014-2024 Martin Lund
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
#pragma once
void readline_init(void);
void readline_input(char input_char);
char * readline_get(void);

View file

@ -1,5 +1,5 @@
/* /*
* tio - a simple serial device I/O tool * tio - a serial device I/O tool
* *
* Copyright (c) 2022 Martin Lund * Copyright (c) 2022 Martin Lund
* *
@ -19,14 +19,13 @@
* 02110-1301, USA. * 02110-1301, USA.
*/ */
#include <stdlib.h> #include <stdio.h>
#include <string.h>
#include <errno.h> #include <errno.h>
#include <string.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <stdbool.h>
#include "options.h" #include "options.h"
#include "print.h" #include "print.h"
#include "error.h" #include "misc.h"
#ifdef HAVE_RS485 #ifdef HAVE_RS485
@ -57,49 +56,60 @@ void rs485_parse_config(const char *arg)
{ {
char keyname[31]; char keyname[31];
unsigned int value; unsigned int value;
sscanf(token, "%30[^=]=%d", keyname, &value); int match_count;
if (!strcmp(keyname, "RTS_ON_SEND")) match_count = sscanf(token, "%30[^=]=%d", keyname, &value);
{
if (value)
{
/* Set logical level for RTS pin equal to 1 when sending */ if (match_count == 2)
option.rs485_config_flags |= SER_RS485_RTS_ON_SEND; {
} if (!strcmp(keyname, "RTS_ON_SEND"))
else
{ {
/* Set logical level for RTS pin equal to 0 when sending */ if (value)
option.rs485_config_flags &= ~(SER_RS485_RTS_ON_SEND); {
/* Set logical level for RTS pin equal to 1 when sending */
option.rs485_config_flags |= SER_RS485_RTS_ON_SEND;
}
else
{
/* Set logical level for RTS pin equal to 0 when sending */
option.rs485_config_flags &= ~(SER_RS485_RTS_ON_SEND);
}
} }
} else if (!strcmp(keyname, "RTS_AFTER_SEND"))
else if (!strcmp(keyname, "RTS_AFTER_SEND"))
{
if (value)
{ {
/* Set logical level for RTS pin equal to 1 after sending */ if (value)
option.rs485_config_flags |= SER_RS485_RTS_AFTER_SEND; {
/* Set logical level for RTS pin equal to 1 after sending */
option.rs485_config_flags |= SER_RS485_RTS_AFTER_SEND;
}
else
{
/* Set logical level for RTS pin equal to 0 after sending */
option.rs485_config_flags &= ~(SER_RS485_RTS_AFTER_SEND);
}
} }
else else if (!strcmp(keyname, "RTS_DELAY_BEFORE_SEND"))
{ {
/* Set logical level for RTS pin equal to 0 after sending */ /* Set RTS delay before send */
option.rs485_config_flags &= ~(SER_RS485_RTS_AFTER_SEND); option.rs485_delay_rts_before_send = value;
}
else if (!strcmp(keyname, "RTS_DELAY_AFTER_SEND"))
{
/* Set RTS delay after send */
option.rs485_delay_rts_after_send = value;
} }
} }
else if (!strcmp(keyname, "RTS_DELAY_BEFORE_SEND")) else if (match_count == 1)
{ {
/* Set RTS delay before send */ if (!strcmp(keyname, "RX_DURING_TX"))
option.rs485_delay_rts_before_send = value; {
/* Receive data even while sending data */
option.rs485_config_flags |= SER_RS485_RX_DURING_TX;
}
} }
else if (!strcmp(keyname, "RTS_DELAY_AFTER_SEND")) else
{ {
/* Set RTS delay after send */ token_found = false;
option.rs485_delay_rts_after_send = value;
}
else if (!strcmp(keyname, "RX_DURING_TX"))
{
/* Receive data even while sending data */
option.rs485_config_flags |= SER_RS485_RX_DURING_TX;
} }
} }
else else
@ -117,7 +127,7 @@ void rs485_print_config(void)
tio_printf(" RTS_AFTER_SEND: %s", (rs485_config.flags & SER_RS485_RTS_AFTER_SEND) ? "high" : "low"); tio_printf(" RTS_AFTER_SEND: %s", (rs485_config.flags & SER_RS485_RTS_AFTER_SEND) ? "high" : "low");
tio_printf(" RTS_DELAY_BEFORE_SEND = %d", rs485_config.delay_rts_before_send); tio_printf(" RTS_DELAY_BEFORE_SEND = %d", rs485_config.delay_rts_before_send);
tio_printf(" RTS_DELAY_AFTER_SEND = %d", rs485_config.delay_rts_after_send); tio_printf(" RTS_DELAY_AFTER_SEND = %d", rs485_config.delay_rts_after_send);
tio_printf(" RX_DURING_TX: %s", (rs485_config.flags & SER_RS485_RX_DURING_TX) ? "enabled" : "disabled"); tio_printf(" RX_DURING_TX: %s", (rs485_config.flags & SER_RS485_RX_DURING_TX) ? "true" : "false");
} }
int rs485_mode_enable(int fd) int rs485_mode_enable(int fd)

View file

@ -1,5 +1,5 @@
/* /*
* tio - a simple serial device I/O tool * tio - a serial device I/O tool
* *
* Copyright (c) 2022 Martin Lund * Copyright (c) 2022 Martin Lund
* *

547
src/script.c Normal file
View file

@ -0,0 +1,547 @@
/*
* tio - a serial device I/O tool
*
* Copyright (c) 2014-2024 Martin Lund
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <lauxlib.h>
#include <lualib.h>
#include <sys/ioctl.h>
#include <ctype.h>
#include "misc.h"
#include "print.h"
#include "options.h"
#include "tty.h"
#include "xymodem.h"
#include "log.h"
#include "script.h"
#include "fs.h"
#include "timestamp.h"
#include "termios.h"
#define MAX_BUFFER_SIZE 2000 // Maximum size of circular buffer
#define READ_LINE_SIZE 4096 // read_line buffer length
static int device_fd;
static char script_init[] =
"tio.set = function(arg)\n"
" local dtr = arg.DTR or -1\n"
" local rts = arg.RTS or -1\n"
" local cts = arg.CTS or -1\n"
" local dsr = arg.DSR or -1\n"
" local cd = arg.CD or -1\n"
" local ri = arg.RI or -1\n"
" tio.line_set(dtr, rts, cts, dsr, cd, ri)\n"
"end\n"
"tio.expect = function(pattern, timeout)\n"
" local str = ''\n"
" while true do\n"
" local c = tio.read(1, timeout)\n"
" if c then\n"
" str = str .. c\n"
" if string.match(str, pattern) then\n"
" return string.match(str, pattern)\n"
" end\n"
" else\n"
" return nil, str\n"
" end\n"
" end\n"
"end\n"
"tio.alwaysecho = true\n"
"setmetatable(tio, tio)\n";
static bool alwaysecho(lua_State *L)
{
bool b;
lua_getglobal(L, "tio");
lua_getfield(L, -1, "alwaysecho");
b = lua_toboolean(L, -1);
lua_pop(L, 2);
return b;
}
static int api_echo(lua_State *L)
{
size_t len = 0;
const char *str = luaL_checklstring(L, 1, &len);
if (option.timestamp)
{
char *pTimeStampNow = timestamp_current_time();
if (pTimeStampNow)
{
tio_printf("%s", str);
if (option.log)
{
log_printf("\n[%s] %s", pTimeStampNow, str);
}
}
} else {
for (size_t i=0; i<len; i++)
{
putchar(str[i]);
if (option.log)
log_putc(str[i]);
}
}
return 0;
}
static void maybe_echo(lua_State *L)
{
if (alwaysecho(L))
{
lua_pushcfunction(L, api_echo);
lua_pushvalue(L, -2);
lua_call(L, 1, 0);
}
}
// lua: tio.sleep(seconds)
static int api_sleep(lua_State *L)
{
long seconds = lua_tointeger(L, 1);
if (seconds < 0)
{
return 0;
}
tio_printf("Sleeping %ld seconds", seconds);
sleep(seconds);
return 0;
}
// lua: tio.msleep(miliseconds)
static int api_msleep(lua_State *L)
{
long mseconds = lua_tointeger(L, 1);
long useconds = mseconds * 1000;
if (useconds < 0)
{
return 0;
}
tio_printf("Sleeping %ld ms", mseconds);
usleep(useconds);
return 0;
}
// lua: tio.line_set(dtr,rts,cts,dsr,cd,ri)
static int line_set(lua_State *L)
{
tty_line_config_t line_config[6] = { };
int dtr = lua_tointeger(L, 1);
int rts = lua_tointeger(L, 2);
int cts = lua_tointeger(L, 3);
int dsr = lua_tointeger(L, 4);
int cd = lua_tointeger(L, 5);
int ri = lua_tointeger(L, 6);
if (dtr != -1)
{
line_config[0].mask = TIOCM_DTR;
line_config[0].value = dtr;
line_config[0].reserved = true;
}
if (rts != -1)
{
line_config[1].mask = TIOCM_RTS;
line_config[1].value = rts;
line_config[1].reserved = true;
}
if (cts != -1)
{
line_config[2].mask = TIOCM_CTS;
line_config[2].value = cts;
line_config[2].reserved = true;
}
if (dsr != -1)
{
line_config[3].mask = TIOCM_DSR;
line_config[3].value = dsr;
line_config[3].reserved = true;
}
if (cd != -1)
{
line_config[4].mask = TIOCM_CD;
line_config[4].value = cd;
line_config[4].reserved = true;
}
if (ri != -1)
{
line_config[5].mask = TIOCM_RI;
line_config[5].value = ri;
line_config[5].reserved = true;
}
tty_line_set(device_fd, line_config);
return 0;
}
// lua: tio.send(file, protocol)
static int api_send(lua_State *L)
{
const char *file = luaL_checkstring(L, 1);
int protocol = luaL_checkinteger(L, 2);
int ret;
if (file == NULL)
{
return 0;
}
switch (protocol)
{
case XMODEM_1K:
tio_printf("Sending file '%s' using XMODEM-1K", file);
ret = xymodem_send(device_fd, file, XMODEM_1K);
tio_printf("%s", ret < 0 ? "Aborted" : "Done");
break;
case XMODEM_CRC:
tio_printf("Sending file '%s' using XMODEM-CRC", file);
ret = xymodem_send(device_fd, file, XMODEM_CRC);
tio_printf("%s", ret < 0 ? "Aborted" : "Done");
break;
case YMODEM:
tio_printf("Sending file '%s' using YMODEM", file);
ret = xymodem_send(device_fd, file, YMODEM);
tio_printf("%s", ret < 0 ? "Aborted" : "Done");
break;
}
return 0;
}
// lua: tio.write(string)
static int api_write(lua_State *L)
{
size_t len = 0;
const char *string = luaL_checklstring(L, 1, &len);
ssize_t ret;
int attempts = 100;
do {
ret = write(device_fd, string, len);
if (ret < 0)
return luaL_error(L, "%s", strerror(errno));
len -= ret;
string += ret;
} while (len > 0 && --attempts);
if (len > 0)
return luaL_error(L, "partial write");
fsync(device_fd); // flush these characters now
tcdrain(device_fd); //ensure we flushed characters to our device
lua_getglobal(L, "tio");
return 1;
}
// lua: tio.read(size, timeout)
static int api_read(lua_State *L)
{
int size = luaL_checkinteger(L, 1);
int timeout = lua_tointeger(L, 2);
if (timeout == 0)
{
timeout = -1; // Wait forever
}
luaL_Buffer buffer;
luaL_buffinit(L, &buffer);
#if LUA_VERSION_NUM >= 502
char *p = luaL_prepbuffsize(&buffer, size);
#else
if (size > LUAL_BUFFERSIZE)
return luaL_error(L, "buffer overflow, max size is: %d", LUAL_BUFFERSIZE);
char *p = luaL_prepbuffer(&buffer);
#endif
ssize_t ret = read_poll(device_fd, p, size, timeout);
if (ret < 0)
return luaL_error(L, "%s", strerror(errno));
luaL_addsize(&buffer, ret);
luaL_pushresult(&buffer);
if (ret == 0)
{
// On timeout return nil instead of an empty string
lua_pop(L, 1);
lua_pushnil(L);
}
else
{
maybe_echo(L);
}
return 1;
}
// lua: string = tio.readline(timeout)
static int api_readline(lua_State *L) {
int timeout = lua_tointeger(L, 1); //ms
luaL_Buffer b;
char ch;
if (timeout == 0)
{
timeout = -1; // Wait forever
}
luaL_buffinit(L, &b);
luaL_prepbuffer(&b);
while (true) {
int ret = read_poll(device_fd, &ch, 1, timeout);
if (ret < 0)
return luaL_error(L, "%s", strerror(errno));
if (ret == 0)
{
luaL_pushresult(&b);
maybe_echo(L);
lua_pushnil(L);
lua_insert(L, -2);
return 2;
}
if (ch == '\n')
{
luaL_pushresult(&b);
maybe_echo(L);
return 1;
}
luaL_addchar(&b, ch);
}
}
// lua: table = tio.ttysearch()
static int api_ttysearch(lua_State *L)
{
UNUSED(L);
GList *iter;
int i = 1;
GList *device_list = tty_search_for_serial_devices();
if (device_list == NULL)
{
return 0;
}
// Create a new table
lua_newtable(L);
// Iterate through found devices
for (iter = device_list; iter != NULL; iter = g_list_next(iter))
{
device_t *device = (device_t *) iter->data;
// Create a new sub-table for each serial device
lua_newtable(L);
// Add elements to the table
lua_pushstring(L, "path");
lua_pushstring(L, device->path);
lua_settable(L, -3);
lua_pushstring(L, "tid");
lua_pushstring(L, device->tid);
lua_settable(L, -3);
lua_pushstring(L, "uptime");
lua_pushnumber(L, device->uptime);
lua_settable(L, -3);
lua_pushstring(L, "driver");
lua_pushstring(L, device->driver);
lua_settable(L, -3);
lua_pushstring(L, "description");
lua_pushstring(L, device->description);
lua_settable(L, -3);
// Set the sub-table as a row in the main table
lua_rawseti(L, -2, i++);
}
// Return table
return 1;
}
static void script_buffer_run(lua_State *L, const char *script_buffer)
{
int error;
error = luaL_loadbuffer(L, script_buffer, strlen(script_buffer), "tio") ||
lua_pcall(L, 0, 0, 0);
if (error)
{
tio_warning_printf("lua: %s\n", lua_tostring(L, -1));
lua_pop(L, 1); /* Pop error message from the stack */
}
}
static void script_file_run(lua_State *L, const char *filename)
{
if (strlen(filename) == 0)
{
tio_warning_printf("Missing script filename\n");
return;
}
if (luaL_dofile(L, filename))
{
tio_warning_printf("lua: %s", lua_tostring(L, -1));
lua_pop(L, 1); /* pop error message from the stack */
return;
}
}
static const struct luaL_Reg tio_lib[] =
{
{ "echo", api_echo},
{ "sleep", api_sleep},
{ "msleep", api_msleep},
{ "line_set", line_set},
{ "send", api_send},
{ "write", api_write},
{ "read", api_read},
{ "readline", api_readline},
{ "ttysearch", api_ttysearch},
{NULL, NULL}
};
static void script_load(lua_State *L)
{
int error;
error = luaL_loadbuffer(L, script_init, strlen(script_init), "tio") || lua_pcall(L, 0, 0, 0);
if (error)
{
tio_error_print("%s\n", lua_tostring(L, -1));
lua_pop(L, 1); // Pop error message from the stack
}
}
static void script_set_global(lua_State *L, const char *name, long value)
{
lua_pushnumber(L, value);
lua_setglobal(L, name);
}
static void script_set_globals(lua_State *L)
{
script_set_global(L, "toggle", 2);
script_set_global(L, "high", 1);
script_set_global(L, "low", 0);
script_set_global(L, "XMODEM_CRC", XMODEM_CRC);
script_set_global(L, "XMODEM_1K", XMODEM_1K);
script_set_global(L, "YMODEM", YMODEM);
}
#if LUA_VERSION_NUM >= 502
static int luaopen_tio(lua_State *L)
{
luaL_newlib(L, tio_lib);
return 1;
}
#endif
void script_run(int fd, const char *script_filename)
{
lua_State *L;
device_fd = fd;
L = luaL_newstate();
luaL_openlibs(L);
#if LUA_VERSION_NUM >= 502
luaL_requiref(L, "tio", luaopen_tio, 1);
#else
luaL_register(L, "tio", tio_lib);
#endif
lua_pop(L, 1);
// Load lua init script
script_load(L);
// Initialize globals
script_set_globals(L);
if (script_filename != NULL)
{
tio_printf("Running script %s", script_filename);
script_file_run(L, script_filename);
}
else if (option.script_filename != NULL)
{
tio_printf("Running script %s", option.script_filename);
script_file_run(L, option.script_filename);
}
else if (option.script != NULL)
{
tio_printf("Running script");
script_buffer_run(L, option.script);
}
lua_close(L);
}
const char *script_run_state_to_string(script_run_t state)
{
switch (state)
{
case SCRIPT_RUN_ONCE:
return "once";
case SCRIPT_RUN_ALWAYS:
return "always";
case SCRIPT_RUN_NEVER:
return "never";
default:
return "Unknown";
}
}

33
src/script.h Normal file
View file

@ -0,0 +1,33 @@
/*
* tio - a serial device I/O tool
*
* Copyright (c) 2014-2024 Martin Lund
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
#pragma once
typedef enum
{
SCRIPT_RUN_ONCE,
SCRIPT_RUN_ALWAYS,
SCRIPT_RUN_NEVER,
SCRIPT_RUN_END,
} script_run_t;
void script_run(int fd, const char *script_filename);
const char *script_run_state_to_string(script_run_t state);

View file

@ -1,5 +1,5 @@
/* /*
* tio - a simple serial terminal I/O tool * tio - a serial device I/O tool
* *
* Copyright (c) 2017-2022 Martin Lund * Copyright (c) 2017-2022 Martin Lund
* *

View file

@ -1,5 +1,5 @@
/* /*
* tio - a simple serial terminal I/O tool * tio - a serial device I/O tool
* *
* Copyright (c) 2022 Martin Lund * Copyright (c) 2022 Martin Lund
* *

View file

@ -1,5 +1,5 @@
/* /*
* tio - a simple serial terminal I/O tool * tio - a serial device I/O tool
* *
* Copyright (c) 2022 Martin Lund * Copyright (c) 2022 Martin Lund
* *

View file

@ -1,5 +1,5 @@
/* /*
* tio - a simple serial terminal I/O tool * tio - a serial device I/O tool
* *
* Copyright (c) 2022 Martin Lund * Copyright (c) 2022 Martin Lund
* *

View file

@ -1,5 +1,5 @@
/* /*
* tio - a simple serial terminal I/O tool * tio - a serial device I/O tool
* *
* Copyright (c) 2014-2022 Martin Lund * Copyright (c) 2014-2022 Martin Lund
* Copyright (c) 2022 Google LLC * Copyright (c) 2022 Google LLC
@ -52,23 +52,23 @@ static const char *socket_filename(void)
static int socket_inet_port(void) static int socket_inet_port(void)
{ {
/* skip 'inet:' */ /* skip 'inet:' */
int port_number = atoi(option.socket + 5); int port = atoi(option.socket + 5);
if (port_number == 0) if (port == 0)
{ {
port_number = SOCKET_PORT_DEFAULT; port = SOCKET_PORT_DEFAULT;
} }
return port_number; return port;
} }
static int socket_inet6_port(void) static int socket_inet6_port(void)
{ {
/* skip 'inet6:' */ /* skip 'inet6:' */
int port_number = atoi(option.socket + 6); int port = atoi(option.socket + 6);
if (port_number == 0) if (port == 0)
{ {
port_number = SOCKET_PORT_DEFAULT; port = SOCKET_PORT_DEFAULT;
} }
return port_number; return port;
} }
static void socket_exit(void) static void socket_exit(void)
@ -83,14 +83,14 @@ static bool socket_stale(const char *path)
{ {
struct sockaddr_un addr; struct sockaddr_un addr;
bool stale = false; bool stale = false;
int sockfd; int sfd;
/* Test if socket file exists */ /* Test if socket file exists */
if (access(path, F_OK) == 0) if (access(path, F_OK) == 0)
{ {
/* Create test socket */ /* Create test socket */
sockfd = socket(AF_UNIX, SOCK_STREAM, 0); sfd = socket(AF_UNIX, SOCK_STREAM, 0);
if (sockfd < 0) if (sfd < 0)
{ {
tio_warning_printf("Failure opening socket (%s)", strerror(errno)); tio_warning_printf("Failure opening socket (%s)", strerror(errno));
return false; return false;
@ -124,6 +124,7 @@ void socket_configure(void)
struct sockaddr_in6 sockaddr_inet6 = {}; struct sockaddr_in6 sockaddr_inet6 = {};
struct sockaddr *sockaddr_p; struct sockaddr *sockaddr_p;
socklen_t socklen; socklen_t socklen;
int optval;
/* Parse socket string */ /* Parse socket string */
@ -225,6 +226,16 @@ void socket_configure(void)
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
#if defined(SO_NOSIGPIPE) && !defined(MSG_NOSIGNAL)
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR | SO_NOSIGPIPE, &optval, sizeof(optval)))
#else
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)))
#endif
{
tio_error_printf("Failed to set socket options (%s)", strerror(errno));
exit(EXIT_FAILURE);
}
/* Bind */ /* Bind */
if (bind(sockfd, sockaddr_p, socklen) < 0) if (bind(sockfd, sockaddr_p, socklen) < 0)
{ {
@ -263,7 +274,12 @@ void socket_write(char input_char)
{ {
if (clientfds[i] != -1) if (clientfds[i] != -1)
{ {
if (write(clientfds[i], &input_char, 1) <= 0)
#if defined(SO_NOSIGPIPE) && !defined(MSG_NOSIGNAL)
if (send(clientfds[i], &input_char, 1, 0) <= 0)
#else
if (send(clientfds[i], &input_char, 1, MSG_NOSIGNAL) <= 0)
#endif
{ {
tio_error_printf_silent("Failed to write to socket (%s)", strerror(errno)); tio_error_printf_silent("Failed to write to socket (%s)", strerror(errno));
close(clientfds[i]); close(clientfds[i]);
@ -343,20 +359,20 @@ bool socket_handle_input(fd_set *rdfs, char *output_char)
} }
/* If INLCR is set, a received NL character shall be translated into a CR character */ /* If INLCR is set, a received NL character shall be translated into a CR character */
if (*output_char == '\n' && map_i_nl_cr) if (*output_char == '\n' && option.map_i_nl_cr)
{ {
*output_char = '\r'; *output_char = '\r';
} }
else if (*output_char == '\r') else if (*output_char == '\r')
{ {
/* If IGNCR is set, a received CR character shall be ignored (not read). */ /* If IGNCR is set, a received CR character shall be ignored (not read). */
if (map_ign_cr) if (option.map_ign_cr)
{ {
return false; return false;
} }
/* If IGNCR is not set and ICRNL is set, a received CR character shall be translated into an NL character. */ /* If IGNCR is not set and ICRNL is set, a received CR character shall be translated into an NL character. */
if (map_i_cr_nl) if (option.map_i_cr_nl)
{ {
*output_char = '\n'; *output_char = '\n';
} }

View file

@ -1,5 +1,5 @@
/* /*
* tio - a simple serial terminal I/O tool * tio - a serial device I/O tool
* *
* Copyright (c) 2014-2022 Martin Lund * Copyright (c) 2014-2022 Martin Lund
* Copyright (c) 2022 Google LLC * Copyright (c) 2022 Google LLC

View file

@ -1,5 +1,5 @@
/* /*
* tio - a simple serial terminal I/O tool * tio - a serial device I/O tool
* *
* Copyright (c) 2014-2022 Martin Lund * Copyright (c) 2014-2022 Martin Lund
* *
@ -22,8 +22,6 @@
#include "config.h" #include "config.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/time.h> #include <sys/time.h>
#include <time.h> #include <time.h>
#include "error.h" #include "error.h"
@ -31,8 +29,6 @@
#include "options.h" #include "options.h"
#include "timestamp.h" #include "timestamp.h"
#define TIME_STRING_SIZE_MAX 24
char *timestamp_current_time(void) char *timestamp_current_time(void)
{ {
static char time_string[TIME_STRING_SIZE_MAX]; static char time_string[TIME_STRING_SIZE_MAX];
@ -78,75 +74,31 @@ char *timestamp_current_time(void)
tm = localtime(&tv.tv_sec); tm = localtime(&tv.tv_sec);
len = strftime(time_string, sizeof(time_string), "%Y-%m-%dT%H:%M:%S", tm); len = strftime(time_string, sizeof(time_string), "%Y-%m-%dT%H:%M:%S", tm);
break; break;
case TIMESTAMP_EPOCH:
case TIMESTAMP_EPOCH_USEC:
// "N.sss" (seconds since Unix epoch, 1970-01-01 00:00:00Z)
tv = tv_now;
tm = localtime(&tv.tv_sec);
len = strftime(time_string, sizeof(time_string), "%s", tm);
break;
default: default:
return NULL; return NULL;
} }
// Append milliseconds to all timestamps // Append millis-/microseconds to all timestamps
if (len) if (len)
{ {
len = snprintf(time_string + len, TIME_STRING_SIZE_MAX - len, ".%03ld", (long)tv.tv_usec / 1000); if ( option.timestamp == TIMESTAMP_EPOCH_USEC )
{
len = snprintf(time_string + len, TIME_STRING_SIZE_MAX - len, ".%06ld", (long)tv.tv_usec);
}
else
{
len = snprintf(time_string + len, TIME_STRING_SIZE_MAX - len, ".%03ld", (long)tv.tv_usec / 1000);
}
} }
// Save previous time value for next run // Save previous time value for next run
tv_previous = tv_now; tv_previous = tv_now;
return (len < TIME_STRING_SIZE_MAX) ? time_string : NULL; return (len < TIME_STRING_SIZE_MAX) ? time_string : NULL;
} }
const char* timestamp_state_to_string(enum timestamp_t timestamp)
{
switch (timestamp)
{
case TIMESTAMP_NONE:
return "disabled";
break;
case TIMESTAMP_24HOUR:
return "24hour";
break;
case TIMESTAMP_24HOUR_START:
return "24hour-start";
break;
case TIMESTAMP_24HOUR_DELTA:
return "24hour-delta";
break;
case TIMESTAMP_ISO8601:
return "iso8601";
break;
default:
return "unknown";
break;
}
}
enum timestamp_t timestamp_option_parse(const char *arg)
{
enum timestamp_t timestamp = TIMESTAMP_24HOUR; // Default
if (arg != NULL)
{
if (strcmp(arg, "24hour") == 0)
{
return TIMESTAMP_24HOUR;
}
else if (strcmp(arg, "24hour-start") == 0)
{
return TIMESTAMP_24HOUR_START;
}
else if (strcmp(arg, "24hour-delta") == 0)
{
return TIMESTAMP_24HOUR_DELTA;
}
else if (strcmp(arg, "iso8601") == 0)
{
return TIMESTAMP_ISO8601;
}
}
return timestamp;
}

View file

@ -1,5 +1,5 @@
/* /*
* tio - a simple serial terminal I/O tool * tio - a serial device I/O tool
* *
* Copyright (c) 2014-2022 Martin Lund * Copyright (c) 2014-2022 Martin Lund
* *
@ -21,16 +21,19 @@
#pragma once #pragma once
enum timestamp_t typedef enum
{ {
TIMESTAMP_NONE, TIMESTAMP_NONE,
TIMESTAMP_24HOUR, TIMESTAMP_24HOUR,
TIMESTAMP_24HOUR_START, TIMESTAMP_24HOUR_START,
TIMESTAMP_24HOUR_DELTA, TIMESTAMP_24HOUR_DELTA,
TIMESTAMP_ISO8601, TIMESTAMP_ISO8601,
TIMESTAMP_EPOCH,
TIMESTAMP_EPOCH_USEC,
TIMESTAMP_END, TIMESTAMP_END,
}; } timestamp_t;
#define TIME_STRING_SIZE_MAX 24
char *timestamp_current_time(void); char *timestamp_current_time(void);
const char* timestamp_state_to_string(enum timestamp_t timestamp);
enum timestamp_t timestamp_option_parse(const char *arg);

2288
src/tty.c

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,5 @@
/* /*
* tio - a simple serial terminal I/O tool * tio - a serial device I/O tool
* *
* Copyright (c) 2014-2022 Martin Lund * Copyright (c) 2014-2022 Martin Lund
* *
@ -22,17 +22,65 @@
#pragma once #pragma once
#include <stdbool.h> #include <stdbool.h>
#include <glib.h>
#define LINE_HIGH true
#define LINE_LOW false
#define TOPOLOGY_ID_SIZE 4
typedef enum
{
FLOW_NONE,
FLOW_HARD,
FLOW_SOFT,
} flow_t;
typedef enum
{
PARITY_NONE,
PARITY_ODD,
PARITY_EVEN,
PARITY_MARK,
PARITY_SPACE,
} parity_t;
typedef enum
{
AUTO_CONNECT_DIRECT,
AUTO_CONNECT_NEW,
AUTO_CONNECT_LATEST,
AUTO_CONNECT_END,
} auto_connect_t;
typedef struct
{
char *tid;
double uptime;
char *path;
char *driver;
char *description;
} device_t;
typedef struct
{
int mask;
int value;
bool reserved;
} tty_line_config_t;
extern const char *device_name;
extern bool interactive_mode; extern bool interactive_mode;
extern bool map_i_nl_cr;
extern bool map_i_cr_nl;
extern bool map_ign_cr;
void stdout_configure(void); void stdout_configure(void);
void stdin_configure(void); void stdin_configure(void);
void tty_configure(void); void tty_configure(void);
void tty_reconfigure(void);
int tty_connect(void); int tty_connect(void);
void tty_wait_for_device(void); void tty_wait_for_device(void);
void list_serial_devices(void); void list_serial_devices(void);
void tty_input_thread_create(void); void tty_input_thread_create(void);
void tty_input_thread_wait_ready(void); void tty_input_thread_wait_ready(void);
void tty_line_set(int fd, tty_line_config_t line_config[]);
void tty_search(void);
GList *tty_search_for_serial_devices(void);

3
src/version.h.in Normal file
View file

@ -0,0 +1,3 @@
#pragma once
#define VERSION "@VERSION@"

739
src/xymodem.c Normal file
View file

@ -0,0 +1,739 @@
/*
* Minimalistic implementation of the xmodem-1k and ymodem sender protocol.
* https://en.wikipedia.org/wiki/XMODEM
* https://en.wikipedia.org/wiki/YMODEM
*
* SPDX-License-Identifier: GPL-2.0-or-later OR MIT-0
*
*/
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <termios.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <poll.h>
#include "xymodem.h"
#include "print.h"
#include "misc.h"
#define SOH 0x01
#define STX 0x02
#define ACK 0x06
#define NAK 0x15
#define CAN 0x18
#define EOT 0x04
#define SOH_STR "\001"
#define ACK_STR "\006"
#define NAK_STR "\025"
#define CAN_STR "\030"
#define EOT_STR "\004"
#define OK 0
#define ERR (-1)
#define ERR_FATAL (-2)
#define USER_CAN (-5)
#define RX_IGNORE 5
#define min(a, b) ((a) < (b) ? (a) : (b))
struct xpacket_1k {
uint8_t type;
uint8_t seq;
uint8_t nseq;
uint8_t data[1024];
uint8_t crc_hi;
uint8_t crc_lo;
} __attribute__((packed));
struct xpacket {
uint8_t type;
uint8_t seq;
uint8_t nseq;
uint8_t data[128];
uint8_t crc_hi;
uint8_t crc_lo;
} __attribute__((packed));
/* See https://en.wikipedia.org/wiki/Computation_of_cyclic_redundancy_checks */
static uint16_t crc16(const uint8_t *data, uint16_t size)
{
uint16_t crc, s;
for (crc = 0; size > 0; size--) {
s = *data++ ^ (crc >> 8);
s ^= (s >> 4);
crc = (crc << 8) ^ s ^ (s << 5) ^ (s << 12);
}
return crc;
}
static int xmodem_1k(int sio, const void *data, size_t len, int seq)
{
struct xpacket_1k packet;
const uint8_t *buf = data;
char resp = 0;
int rc, crc;
/* Drain pending characters from serial line. Insist on the
* last drained character being 'C'.
*/
while(1) {
if (key_hit)
return -1;
rc = read_poll(sio, &resp, 1, 50);
if (rc == 0) {
if (resp == 'C') break;
if (resp == CAN) return ERR;
continue;
}
else if (rc < 0) {
tio_error_print("Read sync from serial failed");
return ERR;
}
}
/* Always work with 1K packets */
packet.seq = seq;
packet.type = STX;
while (len) {
size_t sz, z = 0;
char *from, status;
/* Build next packet, pad with 0 to full seq */
z = min(len, sizeof(packet.data));
memcpy(packet.data, buf, z);
memset(packet.data + z, 0, sizeof(packet.data) - z);
crc = crc16(packet.data, sizeof(packet.data));
packet.crc_hi = crc >> 8;
packet.crc_lo = crc;
packet.nseq = 0xff - packet.seq;
/* Send packet */
from = (char *) &packet;
sz = sizeof(packet);
while (sz) {
if (key_hit)
return ERR;
if ((rc = write(sio, from, sz)) < 0 ) {
if (errno == EWOULDBLOCK) {
usleep(1000);
continue;
}
tio_error_print("Write packet to serial failed");
return ERR;
}
from += rc;
sz -= rc;
}
/* Clear response */
resp = 0;
/* 'lrzsz' does not ACK ymodem's fin packet */
if (seq == 0 && packet.data[0] == 0) resp = ACK;
/* Read receiver response, timeout 1 s */
for(int n=0; n < 20; n++) {
if (key_hit)
return ERR;
rc = read_poll(sio, &resp, 1, 50);
if (rc < 0) {
tio_error_print("Read ack/nak from serial failed");
return ERR;
} else if(rc > 0) {
break;
}
}
/* Update "progress bar" */
switch (resp) {
case NAK: status = 'N'; break;
case ACK: status = '.'; break;
case 'C': status = 'C'; break;
case CAN: status = '!'; return ERR;
default: status = '?';
}
write(STDOUT_FILENO, &status, 1);
/* Move to next block after ACK */
if (resp == ACK) {
packet.seq++;
len -= z;
buf += z;
}
}
/* Send EOT at 1 Hz until ACK or CAN received */
while (seq) {
if (key_hit)
return ERR;
if (write(sio, EOT_STR, 1) < 0) {
tio_error_print("Write EOT to serial failed");
return ERR;
}
write(STDOUT_FILENO, "|", 1);
/* 1s timeout */
rc = read_poll(sio, &resp, 1, 1000);
if (rc < 0) {
tio_error_print("Read from serial failed");
return ERR;
} else if(rc == 0) {
continue;
}
if (resp == ACK || resp == CAN) {
write(STDOUT_FILENO, "\r\n", 2);
return (resp == ACK) ? OK : ERR;
}
}
return 0; /* not reached */
}
static int xmodem(int sio, const void *data, size_t len)
{
struct xpacket packet;
const uint8_t *buf = data;
char resp = 0;
int rc, crc;
/* Drain pending characters from serial line. Insist on the
* last drained character being 'C'.
*/
while(1) {
if (key_hit)
return -1;
rc = read_poll(sio, &resp, 1, 50);
if (rc == 0) {
if (resp == 'C') break;
if (resp == CAN) return ERR;
continue;
}
else if (rc < 0) {
tio_error_print("Read sync from serial failed");
return ERR;
}
}
/* Always work with 128b packets */
packet.seq = 1;
packet.type = SOH;
while (len) {
size_t sz, z = 0;
char *from, status;
/* Build next packet, pad with 0 to full seq */
z = min(len, sizeof(packet.data));
memcpy(packet.data, buf, z);
memset(packet.data + z, 0, sizeof(packet.data) - z);
crc = crc16(packet.data, sizeof(packet.data));
packet.crc_hi = crc >> 8;
packet.crc_lo = crc;
packet.nseq = 0xff - packet.seq;
/* Send packet */
from = (char *) &packet;
sz = sizeof(packet);
while (sz) {
if (key_hit)
return ERR;
if ((rc = write(sio, from, sz)) < 0 ) {
if (errno == EWOULDBLOCK) {
usleep(1000);
continue;
}
tio_error_print("Write packet to serial failed");
return ERR;
}
from += rc;
sz -= rc;
}
/* Clear response */
resp = 0;
/* Read receiver response, timeout 1 s */
for(int n=0; n < 20; n++) {
if (key_hit)
return ERR;
rc = read_poll(sio, &resp, 1, 50);
if (rc < 0) {
tio_error_print("Read ack/nak from serial failed");
return ERR;
} else if(rc > 0) {
break;
}
}
/* Update "progress bar" */
switch (resp) {
case NAK: status = 'N'; break;
case ACK: status = '.'; break;
case 'C': status = 'C'; break;
case CAN: status = '!'; return ERR;
default: status = '?';
}
write(STDOUT_FILENO, &status, 1);
/* Move to next block after ACK */
if (resp == ACK) {
packet.seq++;
len -= z;
buf += z;
}
}
/* Send EOT at 1 Hz until ACK or CAN received */
while (1) {
if (key_hit)
return ERR;
if (write(sio, EOT_STR, 1) < 0) {
tio_error_print("Write EOT to serial failed");
return ERR;
}
write(STDOUT_FILENO, "|", 1);
/* 1s timeout */
rc = read_poll(sio, &resp, 1, 1000);
if (rc < 0) {
tio_error_print("Read from serial failed");
return ERR;
} else if(rc == 0) {
continue;
}
if (resp == ACK || resp == CAN) {
write(STDOUT_FILENO, "\r\n", 2);
return (resp == ACK) ? OK : ERR;
}
}
return 0; /* not reached */
}
int start_receive(int sio)
{
int rc;
struct pollfd fds;
fds.events = POLLIN;
fds.fd = sio;
for (int n = 0; n < 20; n++)
{
/* Send the 'C' byte until the sender of the file responds with
something. The start character will be sent once a second for a number of
seconds. If nothing is received in that time then return false to indicate
that the transfer did not start. */
rc = write(sio, "C", 1);
if (rc < 0) {
if (errno == EWOULDBLOCK) {
usleep(1000);
continue;
}
tio_error_print("Write packet to serial failed");
return ERR;
}
/* Wait until data is available */
rc = poll(&fds, 1, 3000);
if (rc < 0)
{
tio_error_print("%s", strerror(errno));
return rc;
}
else if (rc > 0)
{
if (fds.revents & POLLIN)
{
return rc;
}
}
if (key_hit)
return USER_CAN;
}
return rc;
}
uint16_t update_CRC(uint16_t crc, char data_char)
{
uint8_t data = data_char;
crc = crc ^ ((uint16_t)data << 8);
for (int ix = 0; (ix < 8); ix++)
{
if (crc & 0x8000)
{
crc = (crc << 1) ^ 0x1021;
}
else
{
crc <<= 1;
}
}
return crc;
}
int receive_packet(int sio, struct xpacket packet, int fd)
{
char rxSeq1, rxSeq2 = 0;
char resp = 0;
uint16_t calcCrc = 0;
uint16_t rxCrc = 0;
int rc;
struct pollfd fds;
fds.events = POLLIN;
fds.fd = sio;
/* Read seq bytes*/
rc = read_poll(sio, &rxSeq1, 1, 3000);
if (rc == 0) {
tio_error_print("Timeout waiting for first seq byte");
return ERR;
} else if (rc < 0) {
tio_error_print("Error reading first seq byte")
return ERR_FATAL;
}
rc = read_poll(sio, &rxSeq2, 1, 3000);
if (rc == 0) {
tio_error_print("Timeout waiting for second seq byte");
return ERR;
} else if (rc < 0) {
tio_error_print("Error reading second seq byte")
return ERR_FATAL;
}
if (key_hit)
return USER_CAN;
/* Read packet Data */
for (unsigned ix = 0; (ix < sizeof(packet.data)); ix++)
{
rc = read_poll(sio, &resp, 1, 3000);
/* If the read times out or fails then fail this packet. */
if (rc == 0)
{
tio_error_print("Timeout waiting for next packet char");
rc = write(sio, CAN_STR, 1);
if (rc < 0) {
tio_error_print("Write cancel packet to serial failed");
return ERR_FATAL;
}
return ERR;
} else if (rc < 0) {
tio_error_print("Error reading next packet char")
rc = write(sio, CAN_STR, 1);
if (rc < 0) {
tio_error_print("Write cancel packet to serial failed");
}
return ERR_FATAL;
}
packet.data[ix] = (uint8_t) resp;
calcCrc = update_CRC(calcCrc, resp);
if (key_hit)
return USER_CAN;
}
/* Read CRC */
rc = read_poll(sio, &resp, 1, 3000);
if (rc == 0) {
tio_error_print("Timeout waiting for first CRC byte");
return ERR;
} else if (rc < 0) {
tio_error_print("Error reading first CRC byte")
return ERR_FATAL;
}
uint8_t uresp = resp;
uint16_t uresp16 = uresp;
rxCrc = uresp16 << 8;
rc = read_poll(sio, &resp, 1, 3000);
if (rc == 0) {
tio_error_print("Timeout waiting for second CRC byte");
return ERR;
} else if (rc < 0) {
tio_error_print("Error reading second CRC byte")
return ERR_FATAL;
}
uresp = resp;
uresp16 = uresp;
rxCrc |= uresp16;
if (key_hit)
return USER_CAN;
/* At this point in the code, there should not be anything in the receive buffer
because the sender has just sent a complete packet and is waiting on a response. */
rc = poll(&fds, 1, 10);
if (rc < 0)
{
tio_error_print("%s", strerror(errno));
tio_error_print("Poll check error after packet finish");
rc = write(sio, CAN_STR, 1);
if (rc < 0) {
tio_error_print("Write cancel packet to serial failed");
}
return ERR_FATAL;
}
else if (rc > 0)
{
if (fds.revents & POLLIN)
{
tio_error_print("RX sync error");
char dummy = 0;
/* Drain buffer */
while (read_poll(sio, &dummy, 1, 100) > 0) {}
return ERR;
}
}
uint8_t tester = 0xff;
uint8_t seq1 = rxSeq1;
uint8_t seq2 = rxSeq2;
if ((calcCrc == rxCrc) && (seq1 == packet.seq - 1) && ((seq1 ^ seq2) == tester))
{
/* Resend of previously processed packet. */
rc = write(sio, ACK_STR, 1);
if (rc < 0) {
tio_error_print("Write acknowlegdement packet to serial failed");
return ERR_FATAL;
}
return RX_IGNORE;
}
else if ((calcCrc != rxCrc) || (seq1 != packet.seq) || ((seq1 ^ seq2) != tester))
{
/* Fail if the CRC or sequence number is not correct or if the two received
sequence numbers are not the complement of one another. */
tio_error_print("Bad CRC or sequence number");
tio_debug_printf("CRC read: %u", rxCrc);
tio_debug_printf("CRC calculated: %u", calcCrc);
tio_debug_printf("Seq read: %hhu", rxSeq1);
tio_debug_printf("Seq should be: %hhu", packet.seq);
tio_debug_printf("inv seq: %hhu", rxSeq2);
return ERR;
}
else
{
/* The data is good. Process the packet then ACK it to the sender. */
rc = write(fd, packet.data, sizeof(packet.data));
if (rc < 0)
{
tio_error_print("Problem writing to file");
rc = write(sio, CAN_STR, 1);
if (rc < 0) {
tio_error_print("Write cancel packet to serial failed");
}
return ERR_FATAL;
}
rc = write(sio, ACK_STR, 1);
if (rc < 0)
{
tio_error_print("Write acknowlegdement packet to serial failed");
return ERR_FATAL;
}
}
return OK;
}
int xmodem_receive(int sio, int fd)
{
struct xpacket packet;
char resp = 0;
int rc;
bool complete = false;
char status;
/* Drain pending characters from serial line.*/
while(1) {
if (key_hit)
return -1;
rc = read_poll(sio, &resp, 1, 50);
if (rc == 0) {
if (resp == CAN) return ERR;
break;
}
else if (rc < 0) {
if (rc != USER_CAN) {
tio_error_print("Read sync from serial failed");
}
return ERR;
}
}
/* Always work with 128b packets */
packet.seq = 1;
packet.type = SOH;
/* Start Receive*/
rc = start_receive(sio);
if (rc == 0)
{
tio_error_print("Timeout waiting for transfer to start");
return ERR;
} else if (rc < 0) {
tio_error_print("Error starting XMODEM receive");
return ERR;
}
while (!complete) {
/* Poll for 1 new byte for 3 seconds */
rc = read_poll(sio, &resp, 1, 3000);
if (rc == 0) {
tio_error_print("Timeout waiting for start of next packet");
return ERR;
} else if (rc < 0) {
tio_error_print("Error reading start of next packet")
return ERR;
}
if (key_hit)
return USER_CAN;
switch(resp)
{
case SOH:
/* Start of a packet */
rc = receive_packet(sio, packet, fd);
if (rc == OK) {
packet.seq++;
status = '.';
} else if (rc == ERR) {
rc = write(sio, NAK_STR, 1);
if (rc < 0) {
tio_error_print("Writing not acknowledge packet to serial failed");
return ERR;
}
status = 'N';
} else if (rc == ERR_FATAL) {
tio_error_print("Receive cancelled due to fatal error");
return ERR;
} else if (rc == USER_CAN) {
rc = write(sio, CAN_STR, 1);
if (rc < 0) {
tio_error_print("Writing cancel to serial failed");
return ERR;
}
return USER_CAN;
} else if (rc == RX_IGNORE) {
status = ':';
}
break;
case EOT:
/* End of Transfer */
rc = write(sio, ACK_STR, 1);
if (rc < 0)
{
tio_error_print("Write acknowlegdement packet to serial failed");
return ERR;
}
complete = true;
status = '\0';
write(STDOUT_FILENO, "|\r\n", 3);
break;
case CAN:
/* Cancel from sender */
tio_error_print("Transmission cancelled from sender");
return ERR;
break;
default:
tio_error_print("Unexpected character received waiting for next packet");
return ERR;
break;
}
/* Update "progress bar" */
write(STDOUT_FILENO, &status, 1);
}
return OK;
}
int xymodem_send(int sio, const char *filename, modem_mode_t mode)
{
size_t len;
int rc, fd;
struct stat stat;
const uint8_t *buf;
/* Open file, map into memory */
fd = open(filename, O_RDONLY);
if (fd < 0) {
tio_error_print("Could not open file");
return ERR;
}
fstat(fd, &stat);
len = stat.st_size;
buf = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0);
if (!buf) {
close(fd);
tio_error_print("Could not mmap file");
return ERR;
}
/* Do transfer */
key_hit = 0;
if (mode == XMODEM_1K) {
rc = xmodem_1k(sio, buf, len, 1);
}
else if (mode == XMODEM_CRC) {
rc = xmodem(sio, buf, len);
}
else {
/* Ymodem: hdr + file + fin */
while(1) {
char hdr[1024], *p;
rc = -1;
if (strlen(filename) > 977) break; /* hdr block overrun */
p = stpncpy(hdr, filename, 1024) + 1;
p += sprintf(p, "%ld %lo %o", len, stat.st_mtime, stat.st_mode);
if (xmodem_1k(sio, hdr, p - hdr, 0) < 0) break; /* hdr with metadata */
if (xmodem_1k(sio, buf, len, 1) < 0) break; /* xmodem file */
if (xmodem_1k(sio, "", 1, 0) < 0) break; /* empty hdr = fin */
rc = 0; break;
}
}
key_hit = 0xff;
/* Flush serial and release resources */
tcflush(sio, TCIOFLUSH);
munmap((void *)buf, len);
close(fd);
return rc;
}
int xymodem_receive(int sio, const char *filename, modem_mode_t mode)
{
int rc, fd;
/* Create new file */
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0664);
if (fd < 0) {
tio_error_print("Could not open file");
return ERR;
}
/* Do transfer */
key_hit = 0;
if (mode == XMODEM_1K) {
tio_error_print("Not supported");
rc = -1;
}
else if (mode == XMODEM_CRC) {
rc = xmodem_receive(sio, fd);
}
else {
tio_error_print("Not supported");
rc = -1;
}
key_hit = 0xff;
/* Flush serial and release resources */
tcflush(sio, TCIOFLUSH);
close(fd);
return rc;
}

34
src/xymodem.h Normal file
View file

@ -0,0 +1,34 @@
/*
* tio - a serial device I/O tool
*
* Copyright (c) 2014-2024 Martin Lund
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
#pragma once
typedef enum {
XMODEM_1K,
XMODEM_CRC,
YMODEM,
} modem_mode_t;
extern char key_hit;
int xymodem_send(int sio, const char *filename, modem_mode_t mode);
int xymodem_receive(int sio, const char *filename, modem_mode_t mode);

View file

@ -1,4 +0,0 @@
[wrap-git]
directory=libinih
url=https://github.com/benhoyt/inih.git
revision=r56