mirror of
https://github.com/tio/tio.git
synced 2026-05-01 14:57:59 +02:00
Compare commits
No commits in common. "master" and "v3.9" have entirely different histories.
22 changed files with 512 additions and 685 deletions
10
.github/workflows/codeql.yml
vendored
10
.github/workflows/codeql.yml
vendored
|
|
@ -27,7 +27,7 @@ jobs:
|
|||
# - 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' }}
|
||||
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-20.04' }}
|
||||
timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }}
|
||||
permissions:
|
||||
actions: read
|
||||
|
|
@ -51,7 +51,7 @@ jobs:
|
|||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
|
|
@ -66,7 +66,7 @@ jobs:
|
|||
# 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
|
||||
# uses: github/codeql-action/autobuild@v2
|
||||
|
||||
# ℹ️ 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
|
||||
|
|
@ -78,7 +78,7 @@ jobs:
|
|||
./.github/workflows/codeql-buildscript.sh
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v3
|
||||
uses: github/codeql-action/analyze@v2
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
upload: false
|
||||
|
|
@ -107,7 +107,7 @@ jobs:
|
|||
output: ${{ steps.step1.outputs.sarif-output }}/cpp.sarif
|
||||
|
||||
- name: Upload CodeQL results to code scanning
|
||||
uses: github/codeql-action/upload-sarif@v3
|
||||
uses: github/codeql-action/upload-sarif@v2
|
||||
with:
|
||||
sarif_file: ${{ steps.step1.outputs.sarif-output }}
|
||||
category: "/language:${{matrix.language}}"
|
||||
|
|
|
|||
3
.github/workflows/ubuntu.yml
vendored
3
.github/workflows/ubuntu.yml
vendored
|
|
@ -21,8 +21,7 @@ jobs:
|
|||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install -y bash-completion git meson liblua5.2-dev libglib2.0-dev
|
||||
sudo apt-get install -y bash-completion git meson liblua5.2-dev libglib2.0-dev
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
|
|
|
|||
2
AUTHORS
2
AUTHORS
|
|
@ -64,7 +64,5 @@ 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.
|
||||
|
|
|
|||
104
README.md
104
README.md
|
|
@ -288,12 +288,12 @@ $ cat data.bin | tio /dev/ttyUSB0
|
|||
|
||||
Manipulate modem lines on connect:
|
||||
```
|
||||
$ tio --script "tio.set{DTR=high,RTS=low}; tio.msleep(100); tio.set{DTR=toggle,RTS=toggle}" /dev/ttyUSB0
|
||||
$ tio --script "set{DTR=high,RTS=low}; msleep(100); 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
|
||||
$ echo "*IDN?" | tio /dev/ttyACM0 --script "expect('\r\n', 1000)" --mute
|
||||
KORAD KD3305P V4.2 SN:32475045
|
||||
```
|
||||
|
||||
|
|
@ -365,12 +365,12 @@ color = 11
|
|||
[svf2]
|
||||
device = /dev/ttyUSB0
|
||||
baudrate = 9600
|
||||
script = tio.expect("login: "); tio.write("root\n"); tio.expect("Password: "); tio.write("root\n")
|
||||
script = expect("login: "); write("root\n"); expect("Password: "); write("root\n")
|
||||
color = 12
|
||||
|
||||
[esp32]
|
||||
device = /dev/serial/by-id/usb-0403_6014-if00-port0
|
||||
script = tio.set{DTR=high,RTS=low}; tio.msleep(100); tio.set{DTR=low,RTS=high}; tio.msleep(100); tio.set{RTS=low}
|
||||
script = set{DTR=high,RTS=low}; msleep(100); set{DTR=low,RTS=high}; msleep(100); set{RTS=low}
|
||||
script-run = once
|
||||
color = 13
|
||||
|
||||
|
|
@ -395,80 +395,72 @@ Another more elaborate configuration file example is available [here](examples/c
|
|||
|
||||
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:
|
||||
In addition to the Lua API tio makes the following functions available:
|
||||
|
||||
```
|
||||
expect(string, timeout)
|
||||
Expect string - waits for string to match or timeout before continueing.
|
||||
Supports regular expressions. Special characters must be escaped with '\\'.
|
||||
Timeout is in milliseconds, defaults to 0 meaning it will wait forever.
|
||||
|
||||
#### `tio.expect(pattern, timeout)`
|
||||
Returns 1 on successful match, 0 on timeout, or -1 on error.
|
||||
|
||||
Waits for the Lua pattern to match or timeout before continuing.
|
||||
Timeout is in milliseconds, defaults to 0 meaning it will wait forever.
|
||||
On successful match it also returns the match string as second return value.
|
||||
|
||||
Returns the captures from the pattern or `nil` on timeout.
|
||||
read(size, timeout)
|
||||
Read from serial device. If timeout is 0 or not provided it will wait
|
||||
forever until data is ready to read.
|
||||
|
||||
#### `tio.read(size, timeout)`
|
||||
Returns number of bytes read on success, 0 on timeout, or -1 on error.
|
||||
|
||||
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.
|
||||
On success, returns read string as second return value.
|
||||
|
||||
Returns a string up to `size` bytes long on success and `nil` on timeout.
|
||||
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.
|
||||
|
||||
#### `tio.readline(timeout)`
|
||||
Returns number of bytes read on success, 0 on timeout, or -1 on error.
|
||||
|
||||
Read line from serial device. If timeout is 0 or not provided it will wait
|
||||
forever until data is ready to read.
|
||||
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.
|
||||
|
||||
Returns a string on success and `nil` on timeout. On timeout a partially read
|
||||
line may be returned as a second return value.
|
||||
write(string)
|
||||
Write string to serial device.
|
||||
|
||||
#### `tio.write(string)`
|
||||
Returns number of bytes written on success or -1 on error.
|
||||
|
||||
Write string to serial device.
|
||||
send(file, protocol)
|
||||
Send file using x/y-modem protocol.
|
||||
|
||||
Returns the `tio` table.
|
||||
Protocol can be any of XMODEM_1K, XMODEM_CRC, YMODEM.
|
||||
|
||||
#### `tio.send(file, protocol)`
|
||||
tty_search()
|
||||
Search for serial devices.
|
||||
|
||||
Send file using x/y-modem protocol.
|
||||
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".
|
||||
|
||||
Protocol can be any of `XMODEM_1K`, `XMODEM_CRC`, `YMODEM`.
|
||||
Returns nil if no serial devices are found.
|
||||
|
||||
#### `tio.ttysearch()`
|
||||
set{line=state, ...}
|
||||
Set state of one or multiple tty modem lines.
|
||||
|
||||
Search for serial devices.
|
||||
Line can be any of DTR, RTS, CTS, DSR, CD, RI
|
||||
|
||||
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".
|
||||
State is high, low, or toggle.
|
||||
|
||||
Returns `nil` if no serial devices are found.
|
||||
sleep(seconds)
|
||||
Sleep for seconds.
|
||||
|
||||
#### `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`.
|
||||
msleep(ms)
|
||||
Sleep for miliseconds.
|
||||
|
||||
exit(code)
|
||||
Exit with exit code.
|
||||
```
|
||||
|
||||
## 4. Installation
|
||||
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ 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 = set{DTR=high,RTS=low}; msleep(100); set{DTR=low,RTS=high}; msleep(100); set{RTS=low}
|
||||
script-run = always
|
||||
|
||||
[buspirate]
|
||||
|
|
|
|||
|
|
@ -13,13 +13,14 @@ local logins = {
|
|||
},
|
||||
}
|
||||
|
||||
local hostname = tio.expect("^(%g+) login:", 10)
|
||||
if hostname then
|
||||
local found, match_str = expect("\\w+- login:", 10)
|
||||
if (1 == found) then
|
||||
local hostname = string.match(match_str, "^%w+")
|
||||
local login = logins[hostname]
|
||||
if (nil ~= login) then
|
||||
tio.write(login.username .. "\n")
|
||||
tio.expect("Password:")
|
||||
tio.write(login.password .. "\n")
|
||||
write(login.username .. "\n")
|
||||
expect("Password:")
|
||||
write(login.password .. "\n")
|
||||
else
|
||||
io.write("\r\nDon't know login info for " .. hostname .. "\r\n")
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
tio.set{DTR=high, RTS=low}
|
||||
tio.msleep(100)
|
||||
tio.set{DTR=low, RTS=high}
|
||||
tio.msleep(100)
|
||||
tio.set{RTS=toggle}
|
||||
set{DTR=high, RTS=low}
|
||||
msleep(100)
|
||||
set{DTR=low, RTS=high}
|
||||
msleep(100)
|
||||
set{RTS=toggle}
|
||||
|
|
|
|||
|
|
@ -1,14 +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)
|
||||
read(1000, 6000) -- initial config
|
||||
write("\n")
|
||||
msleep(100)
|
||||
read(650, 60) -- main menu
|
||||
write("S") -- S menu
|
||||
msleep(30)
|
||||
read(650, 60)
|
||||
write("t") -- Parallel Value Table
|
||||
read(650, 60)
|
||||
while true do
|
||||
tio.msleep(1000)
|
||||
tio.write("t")
|
||||
tio.read(650, 50) -- repeat PVT forever
|
||||
msleep(1000)
|
||||
write("t")
|
||||
read(650, 50) -- repeat PVT forever
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,15 +1,17 @@
|
|||
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
|
||||
read(1000, 8000) -- read initial config
|
||||
write("\n")
|
||||
read(650, 100) -- main menu
|
||||
write("S") -- S menu
|
||||
n = 1
|
||||
while n > 0 do -- while not empty, read more
|
||||
n, str = read_line(25)
|
||||
end
|
||||
while true do
|
||||
write("t") -- query PV table
|
||||
msleep(880)
|
||||
n = 1
|
||||
while n > 0 do -- while not empty, read more
|
||||
n, str = read_line(60)
|
||||
msleep(60)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
io.write("Searching... ")
|
||||
|
||||
local device = tio.ttysearch()
|
||||
local device = tty_search()
|
||||
|
||||
io.write("done\r\n")
|
||||
|
||||
|
|
|
|||
70
man/tio.1.in
70
man/tio.1.in
|
|
@ -150,8 +150,6 @@ Set timestamp format to any of the following timestamp formats:
|
|||
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
|
||||
Default format is \fB24hour\fR
|
||||
.RE
|
||||
|
|
@ -220,8 +218,6 @@ Map FF to ESC-c on input
|
|||
Map NL to CR on input
|
||||
.IP "\fBINLCRNL"
|
||||
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"
|
||||
|
|
@ -371,10 +367,6 @@ Default value is "always".
|
|||
|
||||
Execute shell command with I/O redirected to device
|
||||
|
||||
.TP
|
||||
.BR "\-\-complete-profiles
|
||||
|
||||
Prints profiles (for shell completion)
|
||||
.TP
|
||||
.BR \-v ", " \-\-version
|
||||
|
||||
|
|
@ -437,40 +429,49 @@ Send ctrl-t character
|
|||
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:
|
||||
available:
|
||||
|
||||
.TP 6n
|
||||
|
||||
.IP "\fBtio.expect(pattern, timeout)"
|
||||
Waits for the Lua pattern to match or timeout before continuing.
|
||||
.IP "\fBexpect(string, timeout)"
|
||||
Expect string - waits for string to match or timeout before continuing.
|
||||
Supports regular expressions. Special characters must be escaped with '\e\e'.
|
||||
Timeout is in milliseconds, defaults to 0 meaning it will wait forever.
|
||||
|
||||
Returns the captures from the pattern or nil on timeout.
|
||||
Returns 1 on successful match, 0 on timeout, or -1 on error.
|
||||
|
||||
.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.
|
||||
On successful match it also returns the match string as second return value.
|
||||
|
||||
Returns a string up to size bytes long on success and nil on timeout.
|
||||
.IP "\fBread(size, timeout)"
|
||||
Read from serial device. If timeout is 0 or not provided it will wait forever
|
||||
until data is ready to read.
|
||||
|
||||
.IP "\fBtio.readline(timeout)"
|
||||
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.
|
||||
|
||||
.IP "\fBread_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 a string on success and nil on timeout. On timeout a partially read
|
||||
line may be returned as a second return value.
|
||||
Returns number of bytes read on success, 0 on timeout, or -1 on error.
|
||||
|
||||
.IP "\fBtio.write(string)"
|
||||
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.
|
||||
|
||||
.IP "\fBwrite(string)"
|
||||
Write string to serial device.
|
||||
|
||||
Returns the tio table.
|
||||
Returns number of bytes written on success or -1 on error.
|
||||
|
||||
.IP "\fBtio.send(file, protocol)"
|
||||
.IP "\fBsend(file, protocol)"
|
||||
Send file using x/y-modem protocol.
|
||||
|
||||
Protocol can be any of XMODEM_1K, XMODEM_CRC, YMODEM.
|
||||
|
||||
.IP "\fBtio.ttysearch()"
|
||||
.IP "\fBtty_search()"
|
||||
Search for serial devices.
|
||||
|
||||
Returns a table of number indexed tables, one for each serial device found.
|
||||
|
|
@ -480,26 +481,19 @@ following string indexed elements "path", "tid", "uptime", "driver",
|
|||
|
||||
Returns nil if no serial devices are found.
|
||||
|
||||
.IP "\fBtio.set{line=state, ...}"
|
||||
.IP "\fBset{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)"
|
||||
.IP "\fBsleep(seconds)"
|
||||
Sleep for seconds.
|
||||
.IP "\fBtio.msleep(ms)"
|
||||
.IP "\fBmsleep(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.
|
||||
.IP "\fBexit(code)"
|
||||
Exit with exit code.
|
||||
|
||||
.SH "CONFIGURATION FILE"
|
||||
.PP
|
||||
|
|
@ -728,7 +722,7 @@ expect -i $uart "prompt> "
|
|||
.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
|
||||
$ tio --script 'expect("login: "); write("root\\n"); expect("Password: "); write("root\\n")' /dev/ttyUSB0
|
||||
|
||||
.TP
|
||||
Redirect device I/O to network file socket for remote TTY sharing:
|
||||
|
|
@ -749,7 +743,7 @@ $ echo "ls -la" | tio /dev/serial/by\-id/usb\-FTDI_TTL232R-3V3_FTGQVXBL\-if00\-p
|
|||
.TP
|
||||
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
|
||||
$ echo "*IDN?" | tio /dev/ttyACM0 --script "expect('\\r\\n', 1000)" --mute
|
||||
.TP
|
||||
|
||||
.TP
|
||||
|
|
@ -770,7 +764,7 @@ $ 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
|
||||
$ tio --script "set{DTR=high,RTS=low}; msleep(100); set{RTS=toggle}" --script-run once /dev/ttyUSB0
|
||||
|
||||
.SH "WEBSITE"
|
||||
.PP
|
||||
|
|
|
|||
|
|
@ -116,8 +116,6 @@ OPTIONS
|
|||
|
||||
epoch Seconds since Unix epoch (1970-01-01)
|
||||
|
||||
epoch-usec Seconds since Unix epoch (1970-01-01) with subdivision microseconds
|
||||
|
||||
Default format is 24hour
|
||||
|
||||
--timestamp-timeout <ms>
|
||||
|
|
@ -170,8 +168,6 @@ OPTIONS
|
|||
|
||||
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
|
||||
|
|
@ -289,10 +285,6 @@ OPTIONS
|
|||
|
||||
Execute shell command with I/O redirected to device
|
||||
|
||||
--complete-profiles
|
||||
|
||||
Prints profiles (for shell completion)
|
||||
|
||||
-v, --version
|
||||
|
||||
Display program version.
|
||||
|
|
@ -361,7 +353,7 @@ SCRIPT API
|
|||
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.
|
||||
Read 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.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
project('tio', 'c',
|
||||
version : '3.9',
|
||||
license : 'GPL-2.0-or-later',
|
||||
license : [ 'GPL-2'],
|
||||
meson_version : '>= 0.53.2',
|
||||
default_options : [ 'warning_level=2', 'buildtype=release', 'c_std=gnu99' ]
|
||||
)
|
||||
|
|
|
|||
|
|
@ -46,7 +46,6 @@ _tio()
|
|||
--script-file \
|
||||
--script-run \
|
||||
--exec \
|
||||
--complete-profiles \
|
||||
-v --version \
|
||||
-h --help"
|
||||
|
||||
|
|
@ -86,11 +85,11 @@ _tio()
|
|||
return 0
|
||||
;;
|
||||
-m | --map)
|
||||
COMPREPLY=( $(compgen -W "ICRNL IGNCR INLCR IFFESCC INLCRNL ICRCRNL IMSB2LSB OCRNL ODELBS ONLCRNL OLTU ONULBRK OIGNCR" -- ${cur}) )
|
||||
COMPREPLY=( $(compgen -W "ICRNL IGNCR INLCR IFFESCC INLCRNL IMSB2LSB OCRNL ODELBS ONLCRNL OLTU ONULBRK OIGNCR" -- ${cur}) )
|
||||
return 0
|
||||
;;
|
||||
--timestamp-format)
|
||||
COMPREPLY=( $(compgen -W "24hour 24hour-start 24hour-delta iso8601 epoch epoch-usec" -- ${cur}) )
|
||||
COMPREPLY=( $(compgen -W "24hour 24hour-start 24hour-delta iso8601" -- ${cur}) )
|
||||
return 0
|
||||
;;
|
||||
-c | --color)
|
||||
|
|
|
|||
|
|
@ -207,7 +207,7 @@ static void config_parse_keys(GKeyFile *key_file, char *group)
|
|||
config_get_bool(key_file, group, "timestamp", (bool*) &option.timestamp);
|
||||
if (option.timestamp != TIMESTAMP_NONE)
|
||||
{
|
||||
config_get_string(key_file, group, "timestamp-format", &string, "24hour", "24hour-start", "24hour-delta", "iso8601", "epoch", "epoch-usec", NULL);
|
||||
config_get_string(key_file, group, "timestamp-format", &string, "24hour", "24hour-start", "24hour-delta", "iso8601", "epoch", NULL);
|
||||
if (string != NULL)
|
||||
{
|
||||
option_parse_timestamp(string, &option.timestamp);
|
||||
|
|
|
|||
|
|
@ -47,13 +47,7 @@ tio_dep = [
|
|||
lua_dep
|
||||
]
|
||||
|
||||
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']
|
||||
tio_c_args = ['-Wno-unused-result']
|
||||
|
||||
if enable_setspeed2
|
||||
tio_c_args += '-DHAVE_TERMIOS2'
|
||||
|
|
|
|||
|
|
@ -116,7 +116,6 @@ struct option_t option =
|
|||
.map_ign_cr = false,
|
||||
.map_i_ff_escc = false,
|
||||
.map_i_nl_crnl = false,
|
||||
.map_i_cr_crnl = false,
|
||||
.map_o_cr_nl = false,
|
||||
.map_o_nl_crnl = false,
|
||||
.map_o_del_bs = false,
|
||||
|
|
@ -171,7 +170,6 @@ void option_print_help(char *argv[])
|
|||
printf(" --script-file <filename> Run script from file\n");
|
||||
printf(" --script-run once|always|never Run script on connect (default: always)\n");
|
||||
printf(" --exec <command> Execute shell command with I/O redirected to device\n");
|
||||
printf(" --complete-profiles Prints profiles (for shell completion)\n");
|
||||
printf(" -v, --version Display version\n");
|
||||
printf(" -h, --help Display help\n");
|
||||
printf("\n");
|
||||
|
|
@ -401,10 +399,6 @@ const char* option_timestamp_format_to_string(timestamp_t timestamp)
|
|||
return "epoch";
|
||||
break;
|
||||
|
||||
case TIMESTAMP_EPOCH_USEC:
|
||||
return "epoch-usec";
|
||||
break;
|
||||
|
||||
default:
|
||||
return "unknown";
|
||||
break;
|
||||
|
|
@ -435,10 +429,6 @@ void option_parse_timestamp(const char *arg, timestamp_t *timestamp)
|
|||
{
|
||||
*timestamp = TIMESTAMP_EPOCH;
|
||||
}
|
||||
else if (strcmp(arg, "epoch-usec") == 0)
|
||||
{
|
||||
*timestamp = TIMESTAMP_EPOCH_USEC;
|
||||
}
|
||||
else
|
||||
{
|
||||
tio_error_print("Invalid timestamp '%s'", arg);
|
||||
|
|
@ -780,10 +770,6 @@ void option_parse_mappings(const char *map)
|
|||
{
|
||||
option.map_i_nl_crnl = true;
|
||||
}
|
||||
else if (strcmp(token,"ICRCRNL") == 0)
|
||||
{
|
||||
option.map_i_cr_crnl = true;
|
||||
}
|
||||
else if (strcmp(token, "ONLCRNL") == 0)
|
||||
{
|
||||
option.map_o_nl_crnl = true;
|
||||
|
|
|
|||
|
|
@ -98,7 +98,6 @@ struct option_t
|
|||
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;
|
||||
|
|
|
|||
558
src/script.c
558
src/script.c
|
|
@ -20,6 +20,7 @@
|
|||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <regex.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
|
@ -44,87 +45,23 @@
|
|||
#define READ_LINE_SIZE 4096 // read_line buffer length
|
||||
|
||||
static int device_fd;
|
||||
static char circular_buffer[MAX_BUFFER_SIZE];
|
||||
static char match_string[MAX_BUFFER_SIZE];
|
||||
static int buffer_size = 0;
|
||||
|
||||
static char script_init[] =
|
||||
"tio.set = function(arg)\n"
|
||||
"function set(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";
|
||||
" line_set(dtr, rts, cts, dsr, cd, ri)\n"
|
||||
"end\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)
|
||||
// lua: sleep(seconds)
|
||||
static int sleep_(lua_State *L)
|
||||
{
|
||||
long seconds = lua_tointeger(L, 1);
|
||||
|
||||
|
|
@ -140,8 +77,8 @@ static int api_sleep(lua_State *L)
|
|||
return 0;
|
||||
}
|
||||
|
||||
// lua: tio.msleep(miliseconds)
|
||||
static int api_msleep(lua_State *L)
|
||||
// lua: msleep(miliseconds)
|
||||
static int msleep(lua_State *L)
|
||||
{
|
||||
long mseconds = lua_tointeger(L, 1);
|
||||
long useconds = mseconds * 1000;
|
||||
|
|
@ -157,7 +94,7 @@ static int api_msleep(lua_State *L)
|
|||
return 0;
|
||||
}
|
||||
|
||||
// lua: tio.line_set(dtr,rts,cts,dsr,cd,ri)
|
||||
// lua: line_set(dtr,rts,cts,dsr,cd,ri)
|
||||
static int line_set(lua_State *L)
|
||||
{
|
||||
tty_line_config_t line_config[6] = { };
|
||||
|
|
@ -211,11 +148,11 @@ static int line_set(lua_State *L)
|
|||
return 0;
|
||||
}
|
||||
|
||||
// lua: tio.send(file, protocol)
|
||||
static int api_send(lua_State *L)
|
||||
// lua: modem_send(file, protocol)
|
||||
static int modem_send(lua_State *L)
|
||||
{
|
||||
const char *file = luaL_checkstring(L, 1);
|
||||
int protocol = luaL_checkinteger(L, 2);
|
||||
const char *file = lua_tostring(L, 1);
|
||||
int protocol = lua_tointeger(L, 2);
|
||||
int ret;
|
||||
|
||||
if (file == NULL)
|
||||
|
|
@ -247,118 +184,307 @@ static int api_send(lua_State *L)
|
|||
return 0;
|
||||
}
|
||||
|
||||
// lua: tio.write(string)
|
||||
static int api_write(lua_State *L)
|
||||
// lua: send(string)
|
||||
static int write_(lua_State *L)
|
||||
{
|
||||
size_t len = 0;
|
||||
const char *string = luaL_checklstring(L, 1, &len);
|
||||
ssize_t ret;
|
||||
int attempts = 100;
|
||||
const char *string = lua_tostring(L, 1);
|
||||
int ret;
|
||||
|
||||
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");
|
||||
if (string == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = write(device_fd, string, strlen(string));
|
||||
fsync(device_fd); // flush these characters now
|
||||
tcdrain(device_fd); //ensure we flushed characters to our device
|
||||
|
||||
lua_getglobal(L, "tio");
|
||||
lua_pushnumber(L, ret);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// lua: tio.read(size, timeout)
|
||||
static int api_read(lua_State *L)
|
||||
// Function to add a character to the circular expect buffer
|
||||
static void expect_buffer_add(char c)
|
||||
{
|
||||
int size = luaL_checkinteger(L, 1);
|
||||
int timeout = lua_tointeger(L, 2);
|
||||
|
||||
if (timeout == 0)
|
||||
if (!c)
|
||||
{
|
||||
timeout = -1; // Wait forever
|
||||
return;
|
||||
}
|
||||
|
||||
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)
|
||||
if (buffer_size < MAX_BUFFER_SIZE)
|
||||
{
|
||||
// On timeout return nil instead of an empty string
|
||||
lua_pop(L, 1);
|
||||
lua_pushnil(L);
|
||||
circular_buffer[buffer_size++] = c;
|
||||
}
|
||||
else
|
||||
{
|
||||
maybe_echo(L);
|
||||
// Shift the buffer to accommodate the new character
|
||||
memmove(circular_buffer, circular_buffer + 1, MAX_BUFFER_SIZE - 1);
|
||||
circular_buffer[MAX_BUFFER_SIZE - 1] = c;
|
||||
}
|
||||
|
||||
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;
|
||||
// Function to match against the circular expect buffer using regex
|
||||
static bool match_regex(regex_t *regex)
|
||||
{
|
||||
char buffer[MAX_BUFFER_SIZE + 1]; // Temporary buffer for regex matching
|
||||
const char *s = circular_buffer;
|
||||
regmatch_t pmatch[1];
|
||||
regoff_t len;
|
||||
|
||||
memcpy(buffer, circular_buffer, buffer_size);
|
||||
buffer[buffer_size] = '\0'; // Null-terminate the buffer
|
||||
|
||||
// Match against the regex
|
||||
int ret = regexec(regex, buffer, 1, pmatch, 0);
|
||||
if (!ret)
|
||||
{
|
||||
// Match found
|
||||
len = pmatch[0].rm_eo - pmatch[0].rm_so;
|
||||
memcpy(match_string, s + pmatch[0].rm_so, len);
|
||||
match_string[len] = '\0';
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (ret == REG_NOMATCH)
|
||||
{
|
||||
// No match found, do nothing
|
||||
}
|
||||
else
|
||||
{
|
||||
// Error occurred during matching
|
||||
tio_error_print("Regex match failed");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Function to echo a buffer to stdout and to the log
|
||||
// per the option.timestamp and option.log settings
|
||||
static void echo_buffer(char buffer[], ssize_t len)
|
||||
{
|
||||
if (option.timestamp)
|
||||
{
|
||||
char *pTimeStampNow;
|
||||
pTimeStampNow = timestamp_current_time();
|
||||
if (pTimeStampNow)
|
||||
{
|
||||
tio_printf("%s", buffer); //does timestamps for us
|
||||
if (option.log)
|
||||
{
|
||||
log_printf("\n[%s] %s", pTimeStampNow, buffer);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (ssize_t i=0; i<len; i++)
|
||||
{
|
||||
putchar(buffer[i]);
|
||||
|
||||
if (option.log)
|
||||
{
|
||||
log_putc(buffer[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// lua: ret,string = read(size, timeout)
|
||||
static int read_string(lua_State *L)
|
||||
{
|
||||
int size = lua_tointeger(L, 1) + 1; //plus one for null-terminated string
|
||||
int timeout = lua_tointeger(L, 2);
|
||||
int ret = 0;
|
||||
|
||||
char *buffer = calloc(1, size);
|
||||
if (buffer == NULL)
|
||||
{
|
||||
ret = -1; // Error
|
||||
goto error_rs;
|
||||
}
|
||||
|
||||
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);
|
||||
ssize_t bytes_read = read_poll(device_fd, buffer, size, timeout);
|
||||
if (bytes_read < 0)
|
||||
{
|
||||
ret = -1; // Error
|
||||
goto error_rs;
|
||||
}
|
||||
else if (bytes_read == 0)
|
||||
{
|
||||
ret = 0; // Timeout
|
||||
goto error_rs;
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer[bytes_read] = (char)0;
|
||||
}
|
||||
|
||||
echo_buffer(&buffer[0], bytes_read);
|
||||
ret = bytes_read;
|
||||
|
||||
error_rs:
|
||||
lua_pushnumber(L, ret);
|
||||
if (buffer != NULL)
|
||||
{
|
||||
lua_pushstring(L, ret > 0 ? buffer : "");
|
||||
free(buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
lua_pushstring(L, ""); // give empty string to caller
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
|
||||
// lua: table = tio.ttysearch()
|
||||
static int api_ttysearch(lua_State *L)
|
||||
// lua: ret,string = read_line(timeout)
|
||||
static int read_line(lua_State *L)
|
||||
{
|
||||
static char linebuf[READ_LINE_SIZE];
|
||||
int timeout = lua_tointeger(L, 1); //ms
|
||||
int ret = 0;
|
||||
int read_result = 1; //enable reading input from device
|
||||
char ch;
|
||||
int bytes_read = 0;
|
||||
|
||||
linebuf[0] = '\0';
|
||||
if (timeout == 0)
|
||||
{
|
||||
timeout = -1; // Wait forever
|
||||
}
|
||||
|
||||
// loop reading input until a newline seen or timeout
|
||||
while (true)
|
||||
{
|
||||
read_result = read_poll(device_fd, &ch, 1, timeout);
|
||||
if (read_result < 0)
|
||||
{
|
||||
ret = -1; // Error
|
||||
linebuf[bytes_read] = '\0';
|
||||
goto error_rl;
|
||||
}
|
||||
else if (!read_result)
|
||||
{
|
||||
// Timeout returns a non-empty linebuf as a 'line'
|
||||
ret = bytes_read;
|
||||
linebuf[bytes_read] = '\0';
|
||||
break;
|
||||
}
|
||||
else // we got a character, so handle it
|
||||
{
|
||||
if (ch == '\n')
|
||||
{
|
||||
linebuf[bytes_read] = '\0';
|
||||
break;
|
||||
}
|
||||
else if (bytes_read <= (READ_LINE_SIZE-2))
|
||||
{
|
||||
if (isprint(ch)) // store all printable chars
|
||||
{
|
||||
linebuf[bytes_read++] = ch;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bytes_read)
|
||||
{
|
||||
echo_buffer(linebuf, bytes_read);
|
||||
}
|
||||
ret = bytes_read;
|
||||
|
||||
error_rl:
|
||||
lua_pushnumber(L, ret);
|
||||
lua_pushstring(L, linebuf);
|
||||
return 2;
|
||||
}
|
||||
|
||||
// lua: expect(string, timeout)
|
||||
static int expect(lua_State *L)
|
||||
{
|
||||
const char *string = lua_tostring(L, 1);
|
||||
long timeout = lua_tointeger(L, 2);
|
||||
regex_t regex;
|
||||
int ret = 0;
|
||||
char c;
|
||||
|
||||
// Resets buffer to ignore previous `expect` calls
|
||||
buffer_size = 0;
|
||||
match_string[0] = '\0';
|
||||
|
||||
if ((string == NULL) || (timeout < 0))
|
||||
{
|
||||
ret = -1;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (timeout == 0)
|
||||
{
|
||||
// Let poll() wait forever
|
||||
timeout = -1;
|
||||
}
|
||||
|
||||
// Compile the regular expression
|
||||
ret = regcomp(®ex, string, REG_EXTENDED);
|
||||
if (ret)
|
||||
{
|
||||
tio_error_print("Could not compile regex");
|
||||
ret = -1;
|
||||
goto error;
|
||||
}
|
||||
|
||||
// Main loop to read and match
|
||||
while (true)
|
||||
{
|
||||
ssize_t bytes_read = read_poll(device_fd, &c, 1, timeout);
|
||||
if (bytes_read > 0)
|
||||
{
|
||||
putchar(c);
|
||||
expect_buffer_add(c);
|
||||
|
||||
if (option.log)
|
||||
{
|
||||
log_putc(c);
|
||||
}
|
||||
|
||||
// Match against the entire buffer
|
||||
if (match_regex(®ex))
|
||||
{
|
||||
ret = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Timeout or error
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
regfree(®ex);
|
||||
|
||||
error:
|
||||
lua_pushnumber(L, ret);
|
||||
lua_pushstring(L, match_string);
|
||||
return 2;
|
||||
}
|
||||
|
||||
// lua: exit(code)
|
||||
static int exit_(lua_State *L)
|
||||
{
|
||||
long code = lua_tointeger(L, 1);
|
||||
|
||||
exit(code);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// lua: list = tty_search()
|
||||
static int tty_search_(lua_State *L)
|
||||
{
|
||||
UNUSED(L);
|
||||
GList *iter;
|
||||
|
|
@ -424,7 +550,66 @@ static void script_buffer_run(lua_State *L, const char *script_buffer)
|
|||
}
|
||||
}
|
||||
|
||||
static void script_file_run(lua_State *L, const char *filename)
|
||||
static const struct luaL_Reg tio_lib[] =
|
||||
{
|
||||
{ "sleep", sleep_},
|
||||
{ "msleep", msleep},
|
||||
{ "line_set", line_set},
|
||||
{ "send", modem_send},
|
||||
{ "write", write_},
|
||||
{ "read", read_string},
|
||||
{ "read_line", read_line},
|
||||
{ "expect", expect},
|
||||
{ "exit", exit_},
|
||||
{ "tty_search", tty_search_},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
#if !defined LUA_VERSION_NUM || LUA_VERSION_NUM==501
|
||||
/*
|
||||
** Adapted from Lua 5.2.0 (for backwards compatibility)
|
||||
*/
|
||||
static void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup)
|
||||
{
|
||||
luaL_checkstack(L, nup+1, "too many upvalues");
|
||||
for (; l->name != NULL; l++) { /* fill the table with given functions */
|
||||
int i;
|
||||
lua_pushstring(L, l->name);
|
||||
for (i = 0; i < nup; i++) /* copy upvalues to the top */
|
||||
lua_pushvalue(L, -(nup+1));
|
||||
lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */
|
||||
lua_settable(L, -(nup + 3));
|
||||
}
|
||||
lua_pop(L, nup); /* remove upvalues */
|
||||
}
|
||||
#endif
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
int lua_register_tio(lua_State *L)
|
||||
{
|
||||
// Register lxi functions
|
||||
lua_getglobal(L, "_G");
|
||||
luaL_setfuncs(L, tio_lib, 0);
|
||||
lua_pop(L, 1);
|
||||
|
||||
// Load lua init script
|
||||
script_load(L);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void script_file_run(lua_State *L, const char *filename)
|
||||
{
|
||||
if (strlen(filename) == 0)
|
||||
{
|
||||
|
|
@ -440,39 +625,13 @@ static void script_file_run(lua_State *L, const char *filename)
|
|||
}
|
||||
}
|
||||
|
||||
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)
|
||||
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)
|
||||
void script_set_globals(lua_State *L)
|
||||
{
|
||||
script_set_global(L, "toggle", 2);
|
||||
script_set_global(L, "high", 1);
|
||||
|
|
@ -482,14 +641,6 @@ static void script_set_globals(lua_State *L)
|
|||
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;
|
||||
|
|
@ -499,15 +650,8 @@ void script_run(int fd, const char *script_filename)
|
|||
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);
|
||||
// Bind tio functions
|
||||
lua_register_tio(L);
|
||||
|
||||
// Initialize globals
|
||||
script_set_globals(L);
|
||||
|
|
|
|||
|
|
@ -75,7 +75,6 @@ char *timestamp_current_time(void)
|
|||
len = strftime(time_string, sizeof(time_string), "%Y-%m-%dT%H:%M:%S", tm);
|
||||
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);
|
||||
|
|
@ -85,18 +84,12 @@ char *timestamp_current_time(void)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
// Append millis-/microseconds to all timestamps
|
||||
// Append milliseconds to all timestamps
|
||||
if (len)
|
||||
{
|
||||
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);
|
||||
}
|
||||
len = snprintf(time_string + len, TIME_STRING_SIZE_MAX - len, ".%03ld", (long)tv.tv_usec / 1000);
|
||||
}
|
||||
|
||||
// Save previous time value for next run
|
||||
tv_previous = tv_now;
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ typedef enum
|
|||
TIMESTAMP_24HOUR_DELTA,
|
||||
TIMESTAMP_ISO8601,
|
||||
TIMESTAMP_EPOCH,
|
||||
TIMESTAMP_EPOCH_USEC,
|
||||
TIMESTAMP_END,
|
||||
} timestamp_t;
|
||||
|
||||
|
|
|
|||
315
src/tty.c
315
src/tty.c
|
|
@ -22,15 +22,6 @@
|
|||
#if defined(__linux__)
|
||||
#include <linux/serial.h>
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__) || defined(__MACH__)
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <IOKit/IOBSD.h>
|
||||
#include <IOKit/IOKitLib.h>
|
||||
#include <IOKit/serial/IOSerialKeys.h>
|
||||
#include <IOKit/usb/IOUSBLib.h>
|
||||
#endif
|
||||
|
||||
#include "version.h"
|
||||
#include "config.h"
|
||||
#include <stdarg.h>
|
||||
|
|
@ -632,18 +623,16 @@ void tty_output_mode_set(output_mode_t mode)
|
|||
static void mappings_print(void)
|
||||
{
|
||||
if (option.map_i_cr_nl || option.map_ign_cr || option.map_i_ff_escc ||
|
||||
option.map_i_nl_cr || option.map_i_nl_crnl || option.map_i_cr_crnl ||
|
||||
option.map_o_cr_nl || option.map_o_del_bs || option.map_o_nl_crnl ||
|
||||
option.map_o_ltu || option.map_o_nulbrk || option.map_i_msb2lsb ||
|
||||
option.map_o_ign_cr)
|
||||
option.map_i_nl_cr || option.map_i_nl_crnl || option.map_o_cr_nl ||
|
||||
option.map_o_del_bs || option.map_o_nl_crnl || option.map_o_ltu ||
|
||||
option.map_o_nulbrk || option.map_i_msb2lsb || option.map_o_ign_cr)
|
||||
{
|
||||
tio_printf(" Mappings:%s%s%s%s%s%s%s%s%s%s%s%s%s",
|
||||
tio_printf(" Mappings:%s%s%s%s%s%s%s%s%s%s%s%s",
|
||||
option.map_i_cr_nl ? " ICRNL" : "",
|
||||
option.map_ign_cr ? " IGNCR" : "",
|
||||
option.map_i_ff_escc ? " IFFESCC" : "",
|
||||
option.map_i_nl_cr ? " INLCR" : "",
|
||||
option.map_i_nl_crnl ? " INLCRNL" : "",
|
||||
option.map_i_cr_crnl ? " ICRCRNL" : "",
|
||||
option.map_i_msb2lsb ? " IMSB2LSB" : "",
|
||||
option.map_o_cr_nl ? " OCRNL" : "",
|
||||
option.map_o_del_bs ? " ODELBS" : "",
|
||||
|
|
@ -794,34 +783,30 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward)
|
|||
tio_printf("INLCRNL is %s", option.map_i_nl_crnl ? "set" : "unset");
|
||||
break;
|
||||
case KEY_5:
|
||||
option.map_i_cr_crnl = !option.map_i_cr_crnl;
|
||||
tio_printf("ICRCRNL is %s", option.map_i_cr_crnl ? "set" : "unset");
|
||||
break;
|
||||
case KEY_6:
|
||||
option.map_i_msb2lsb = !option.map_i_msb2lsb;
|
||||
tio_printf("IMSB2LSB is %s", option.map_i_msb2lsb ? "set" : "unset");
|
||||
break;
|
||||
case KEY_7:
|
||||
case KEY_6:
|
||||
option.map_o_cr_nl = !option.map_o_cr_nl;
|
||||
tio_printf("OCRNL is %s", option.map_o_cr_nl ? "set" : "unset");
|
||||
break;
|
||||
case KEY_8:
|
||||
case KEY_7:
|
||||
option.map_o_del_bs = !option.map_o_del_bs;
|
||||
tio_printf("ODELBS is %s", option.map_o_del_bs ? "set" : "unset");
|
||||
break;
|
||||
case KEY_9:
|
||||
case KEY_8:
|
||||
option.map_o_nl_crnl = !option.map_o_nl_crnl;
|
||||
tio_printf("ONLCRNL is %s", option.map_o_nl_crnl ? "set" : "unset");
|
||||
break;
|
||||
case KEY_A:
|
||||
case KEY_9:
|
||||
option.map_o_ltu = !option.map_o_ltu;
|
||||
tio_printf("OLTU is %s", option.map_o_ltu ? "set" : "unset");
|
||||
break;
|
||||
case KEY_B:
|
||||
case KEY_A:
|
||||
option.map_o_nulbrk = !option.map_o_nulbrk;
|
||||
tio_printf("ONULBRK is %s", option.map_o_nulbrk ? "set" : "unset");
|
||||
break;
|
||||
case KEY_C:
|
||||
case KEY_B:
|
||||
option.map_o_ign_cr = !option.map_o_ign_cr;
|
||||
tio_printf("OIGNCR is %s", option.map_o_ign_cr ? "set" : "unset");
|
||||
break;
|
||||
|
|
@ -1022,22 +1007,20 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward)
|
|||
tio_printf(" (3) INLCR: %s mapping NL to CR on input",
|
||||
option.map_i_nl_cr ? "Unset" : "Set");
|
||||
tio_printf(" (4) INLCRNL: %s mapping NL to CR-NL on input",
|
||||
option.map_i_nl_crnl ? "Unset" : "Set");
|
||||
tio_printf(" (5) ICRCRNL: %s mapping CR to CR-NL on input",
|
||||
option.map_i_cr_crnl ? "Unset" : "Set");
|
||||
tio_printf(" (6) IMSB2LSB: %s mapping MSB bit order to LSB on input",
|
||||
option.map_i_nl_cr ? "Unset" : "Set");
|
||||
tio_printf(" (5) IMSB2LSB: %s mapping MSB bit order to LSB on input",
|
||||
option.map_i_msb2lsb ? "Unset" : "Set");
|
||||
tio_printf(" (7) OCRNL: %s mapping CR to NL on output",
|
||||
tio_printf(" (6) OCRNL: %s mapping CR to NL on output",
|
||||
option.map_o_cr_nl ? "Unset" : "Set");
|
||||
tio_printf(" (8) ODELBS: %s mapping DEL to BS on output",
|
||||
tio_printf(" (7) ODELBS: %s mapping DEL to BS on output",
|
||||
option.map_o_del_bs ? "Unset" : "Set");
|
||||
tio_printf(" (9) ONLCRNL: %s mapping NL to CR-NL on output",
|
||||
tio_printf(" (8) ONLCRNL: %s mapping NL to CR-NL on output",
|
||||
option.map_o_nl_crnl ? "Unset" : "Set");
|
||||
tio_printf(" (a) OLTU: %s mapping lowercase to uppercase on output",
|
||||
tio_printf(" (9) OLTU: %s mapping lowercase to uppercase on output",
|
||||
option.map_o_ltu ? "Unset" : "Set");
|
||||
tio_printf(" (b) ONULBRK: %s mapping NUL to send break signal on output",
|
||||
tio_printf(" (a) ONULBRK: %s mapping NUL to send break signal on output",
|
||||
option.map_o_nulbrk ? "Unset" : "Set");
|
||||
tio_printf(" (c) OIGNCR: %s ignoring CR on output",
|
||||
tio_printf(" (b) OIGNCR: %s ignoring CR on output",
|
||||
option.map_o_ign_cr ? "Unset" : "Set");
|
||||
|
||||
// Process next input character as sub command
|
||||
|
|
@ -1100,9 +1083,6 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward)
|
|||
case TIMESTAMP_EPOCH:
|
||||
tio_printf("Switched timestamp mode to epoch");
|
||||
break;
|
||||
case TIMESTAMP_EPOCH_USEC:
|
||||
tio_printf("Switched timestamp mode to epoch with subdivision in microseconds");
|
||||
break;
|
||||
case TIMESTAMP_END:
|
||||
option.timestamp = TIMESTAMP_NONE;
|
||||
tio_printf("Switched timestamp mode off");
|
||||
|
|
@ -1785,22 +1765,18 @@ GList *tty_search_for_serial_devices(void)
|
|||
creation_time = fs_get_creation_time(path);
|
||||
double uptime = current_time - creation_time;
|
||||
|
||||
// Read sysfs files to get best possible description
|
||||
// Read sysfs files to get best possible description of the driver
|
||||
char description[50] = {};
|
||||
length = fs_read_file_stripped(description, sizeof(description), "/sys/class/tty/%s/device/../product", entry->d_name);
|
||||
if (length == -1)
|
||||
{
|
||||
length = fs_read_file_stripped(description, sizeof(description), "/sys/class/tty/%s/device/../../product", entry->d_name);
|
||||
}
|
||||
if (length == -1)
|
||||
{
|
||||
length = fs_read_file_stripped(description, sizeof(description), "/sys/class/tty/%s/device/interface", entry->d_name);
|
||||
}
|
||||
length = fs_read_file_stripped(description, sizeof(description), "/sys/class/tty/%s/device/interface", entry->d_name);
|
||||
if (length == -1)
|
||||
{
|
||||
length = fs_read_file_stripped(description, sizeof(description), "/sys/class/tty/%s/device/../interface", entry->d_name);
|
||||
}
|
||||
if (length == -1)
|
||||
{
|
||||
length = fs_read_file_stripped(description, sizeof(description), "/sys/class/tty/%s/device/../../product", entry->d_name);
|
||||
}
|
||||
if (length == -1)
|
||||
{
|
||||
snprintf(description, sizeof(description), "%s", get_serial_port_type(path));
|
||||
}
|
||||
|
|
@ -1857,226 +1833,6 @@ GList *tty_search_for_serial_devices(void)
|
|||
return device_list;
|
||||
}
|
||||
|
||||
#elif defined(__APPLE__) || defined(__MACH__)
|
||||
|
||||
char *getPropertyString(io_object_t device, CFStringRef property)
|
||||
{
|
||||
/* Validate inputs */
|
||||
if (device == IO_OBJECT_NULL || property == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Attempt to get property */
|
||||
CFTypeRef valueRef = IORegistryEntryCreateCFProperty(
|
||||
device, property, kCFAllocatorDefault, 0);
|
||||
if (!valueRef)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Ensure it's a CFString */
|
||||
if (CFGetTypeID(valueRef) != CFStringGetTypeID())
|
||||
{
|
||||
CFRelease(valueRef);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Convert to C string */
|
||||
CFIndex length = CFStringGetLength(valueRef);
|
||||
CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1;
|
||||
char *result = malloc(maxSize);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
CFRelease(valueRef);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool converted = CFStringGetCString(
|
||||
(CFStringRef)valueRef,
|
||||
result,
|
||||
maxSize,
|
||||
kCFStringEncodingUTF8
|
||||
);
|
||||
|
||||
CFRelease(valueRef);
|
||||
|
||||
if (!converted)
|
||||
{
|
||||
free(result);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
char *getDeviceLocation(io_object_t device)
|
||||
{
|
||||
/* Validate device */
|
||||
if (device == IO_OBJECT_NULL)
|
||||
{
|
||||
return strdup("Invalid Device");
|
||||
}
|
||||
|
||||
/* Attempt to get location */
|
||||
io_string_t location = {0};
|
||||
kern_return_t result = IORegistryEntryGetLocationInPlane(
|
||||
device, kIOServicePlane, location);
|
||||
|
||||
if (result != KERN_SUCCESS)
|
||||
{
|
||||
return strdup("Unknown Location");
|
||||
}
|
||||
|
||||
/* Safely copy location */
|
||||
size_t len = strnlen(location, sizeof(io_string_t));
|
||||
char *trimmed_location = calloc(1, len + 1);
|
||||
|
||||
if (!trimmed_location)
|
||||
{
|
||||
return strdup("Memory Error");
|
||||
}
|
||||
|
||||
memcpy(trimmed_location, location, len);
|
||||
return trimmed_location;
|
||||
}
|
||||
|
||||
// for __APPLE__
|
||||
GList *tty_search_for_serial_devices(void)
|
||||
{
|
||||
search_reset();
|
||||
io_iterator_t iter = IO_OBJECT_NULL;
|
||||
CFMutableDictionaryRef matchingDict = NULL;
|
||||
listing_device_name_length_max = 0;
|
||||
|
||||
/* Create matching dictionary for serial devices */
|
||||
if (!(matchingDict = IOServiceMatching(kIOSerialBSDServiceValue)))
|
||||
{
|
||||
tio_error_print("Failed to create matching dictionary for serial devices");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Get matching services */
|
||||
kern_return_t kernResult = IOServiceGetMatchingServices(
|
||||
kIOMainPortDefault, matchingDict, &iter);
|
||||
matchingDict = NULL; /* Dictionary ownership transferred */
|
||||
|
||||
if (kernResult != KERN_SUCCESS)
|
||||
{
|
||||
tio_error_print("IOServiceGetMatchingServices failed: %d", kernResult);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Defensive check for iterator */
|
||||
if (iter == IO_OBJECT_NULL)
|
||||
{
|
||||
tio_error_print("Invalid device iterator");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Iterate through serial devices and collect information */
|
||||
for (io_object_t device; (device = IOIteratorNext(iter));)
|
||||
{
|
||||
char *devicePath = NULL, *locationID = NULL;
|
||||
char *productName = NULL, *vendorName = NULL;
|
||||
char tid[5] = {0};
|
||||
double uptime = 0.0;
|
||||
|
||||
/* Get device path - key determines if we get tty. or cu. */
|
||||
//if (!(devicePath = getPropertyString(device, CFSTR(kIODialinDeviceKey))))
|
||||
if (!(devicePath = getPropertyString(device, CFSTR(kIOCalloutDeviceKey))))
|
||||
{
|
||||
IOObjectRelease(device);
|
||||
continue; /* Skip devices without a path */
|
||||
}
|
||||
|
||||
/* Update length of longest device name string */
|
||||
listing_device_name_length_max =
|
||||
strlen(devicePath) > listing_device_name_length_max
|
||||
? strlen(devicePath)
|
||||
: listing_device_name_length_max;
|
||||
|
||||
/* Calculate uptime */
|
||||
uptime = get_current_time() - fs_get_creation_time(devicePath);
|
||||
|
||||
/* Find USB device (if applicable) */
|
||||
io_object_t usbDevice = IO_OBJECT_NULL;
|
||||
kern_return_t usbResult = IORegistryEntryGetParentEntry(
|
||||
device, kIOServicePlane, &usbDevice);
|
||||
|
||||
/* Traverse up the device tree to find a USB device */
|
||||
while (usbResult == KERN_SUCCESS &&
|
||||
!IOObjectConformsTo(usbDevice, "IOUSBDevice"))
|
||||
{
|
||||
io_object_t oldUsbDevice = usbDevice;
|
||||
usbResult = IORegistryEntryGetParentEntry(
|
||||
usbDevice, kIOServicePlane, &usbDevice);
|
||||
IOObjectRelease(oldUsbDevice);
|
||||
}
|
||||
|
||||
/* If we found a USB device */
|
||||
if (usbResult == KERN_SUCCESS)
|
||||
{
|
||||
locationID = getDeviceLocation(usbDevice);
|
||||
|
||||
unsigned long hash2 = djb2_hash((const unsigned char *)(locationID ?: ""));
|
||||
base62_encode(hash2, tid);
|
||||
|
||||
/* Get product and vendor names */
|
||||
productName = getPropertyString(usbDevice, CFSTR("USB Product Name"));
|
||||
vendorName = getPropertyString(usbDevice, CFSTR("USB Vendor Name"));
|
||||
|
||||
IOObjectRelease(usbDevice);
|
||||
}
|
||||
|
||||
/* Create device structure */
|
||||
device_t *device_info = g_new0(device_t, 1);
|
||||
if (!device_info)
|
||||
{
|
||||
tio_error_print("Memory allocation failed for device_info");
|
||||
free(devicePath);
|
||||
free(locationID);
|
||||
free(productName);
|
||||
free(vendorName);
|
||||
IOObjectRelease(device);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Populate device info */
|
||||
*device_info = (device_t) {
|
||||
.path = devicePath,
|
||||
.tid = g_strdup(tid),
|
||||
.uptime = uptime,
|
||||
.driver = g_strdup(vendorName),
|
||||
.description = g_strdup(productName ?: vendorName ?: "")
|
||||
};
|
||||
|
||||
/* Add to device list */
|
||||
device_list = g_list_append(device_list, device_info);
|
||||
|
||||
/* Clean up */
|
||||
free(locationID);
|
||||
free(productName);
|
||||
free(vendorName);
|
||||
IOObjectRelease(device);
|
||||
}
|
||||
|
||||
/* Clean up iterator */
|
||||
IOObjectRelease(iter);
|
||||
|
||||
/* Check if device list is empty */
|
||||
if (!device_list)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Sort device list by uptime */
|
||||
device_list = g_list_sort(device_list, compare_uptime);
|
||||
|
||||
return device_list;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
GList *tty_search_for_serial_devices(void)
|
||||
|
|
@ -2357,21 +2113,8 @@ void tty_wait_for_device(void)
|
|||
}
|
||||
else if (status == -1)
|
||||
{
|
||||
#if defined(__CYGWIN__)
|
||||
// Happens when port unpluged
|
||||
if (errno == EACCES)
|
||||
{
|
||||
goto error;
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
if (errno == EBADF)
|
||||
{
|
||||
break; // tty_disconnect() will be naturally triggered by atexit()
|
||||
}
|
||||
#else
|
||||
tio_error_printf("select() failed (%s)", strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2841,15 +2584,6 @@ int tty_connect(void)
|
|||
do_timestamp = true;
|
||||
}
|
||||
}
|
||||
else if ((input_char == '\r') && (option.map_i_cr_crnl) && (!option.map_i_msb2lsb))
|
||||
{
|
||||
printchar('\r');
|
||||
printchar('\n');
|
||||
if (option.timestamp)
|
||||
{
|
||||
do_timestamp = true;
|
||||
}
|
||||
}
|
||||
else if ((input_char == '\f') && (option.map_i_ff_escc) && (!option.map_i_msb2lsb))
|
||||
{
|
||||
printchar('\e');
|
||||
|
|
@ -3010,3 +2744,4 @@ error_read:
|
|||
error_open:
|
||||
return TIO_ERROR;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue