diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 8a195f4..b96d489 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -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}}" diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 90e1a93..a72e3d3 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -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: | diff --git a/AUTHORS b/AUTHORS index 3754640..21f2bad 100644 --- a/AUTHORS +++ b/AUTHORS @@ -64,7 +64,5 @@ Keith Hill Lubov66 V Samuel Holland -David Ordnung - Thanks to everyone who has contributed to this project. diff --git a/README.md b/README.md index e3ab066..f2c2806 100644 --- a/README.md +++ b/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 diff --git a/examples/config/config b/examples/config/config index 7bd141e..01e84dd 100644 --- a/examples/config/config +++ b/examples/config/config @@ -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] diff --git a/examples/lua/automatic-linux-login.lua b/examples/lua/automatic-linux-login.lua index aa7a990..428caa1 100644 --- a/examples/lua/automatic-linux-login.lua +++ b/examples/lua/automatic-linux-login.lua @@ -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 diff --git a/examples/lua/control-lines-test.lua b/examples/lua/control-lines-test.lua index 55d98b5..5b54ab4 100644 --- a/examples/lua/control-lines-test.lua +++ b/examples/lua/control-lines-test.lua @@ -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} diff --git a/examples/lua/read.lua b/examples/lua/read.lua index 6452816..6baa032 100644 --- a/examples/lua/read.lua +++ b/examples/lua/read.lua @@ -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 diff --git a/examples/lua/read_line.lua b/examples/lua/read_line.lua index a844b48..ef6ec20 100644 --- a/examples/lua/read_line.lua +++ b/examples/lua/read_line.lua @@ -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 diff --git a/examples/lua/serial-device-search.lua b/examples/lua/serial-device-search.lua index 77d9dbc..120d650 100644 --- a/examples/lua/serial-device-search.lua +++ b/examples/lua/serial-device-search.lua @@ -1,6 +1,6 @@ io.write("Searching... ") -local device = tio.ttysearch() +local device = tty_search() io.write("done\r\n") diff --git a/man/tio.1.in b/man/tio.1.in index eb0338d..19c36da 100644 --- a/man/tio.1.in +++ b/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 diff --git a/man/tio.1.txt b/man/tio.1.txt index eeac82c..bf44f9d 100644 --- a/man/tio.1.txt +++ b/man/tio.1.txt @@ -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 @@ -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. diff --git a/meson.build b/meson.build index b95b5c0..05f77ce 100644 --- a/meson.build +++ b/meson.build @@ -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' ] ) diff --git a/src/bash-completion/tio.in b/src/bash-completion/tio.in index b3b61cb..d71aceb 100644 --- a/src/bash-completion/tio.in +++ b/src/bash-completion/tio.in @@ -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) diff --git a/src/configfile.c b/src/configfile.c index ca116bf..6330151 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -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); diff --git a/src/meson.build b/src/meson.build index 05168f7..958d4e9 100644 --- a/src/meson.build +++ b/src/meson.build @@ -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' diff --git a/src/options.c b/src/options.c index acff7ce..40bcc23 100644 --- a/src/options.c +++ b/src/options.c @@ -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 Run script from file\n"); printf(" --script-run once|always|never Run script on connect (default: always)\n"); printf(" --exec 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; diff --git a/src/options.h b/src/options.h index c552217..dadf133 100644 --- a/src/options.h +++ b/src/options.h @@ -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; diff --git a/src/script.c b/src/script.c index b69d55b..b471932 100644 --- a/src/script.c +++ b/src/script.c @@ -20,6 +20,7 @@ */ #include +#include #include #include #include @@ -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 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 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); diff --git a/src/timestamp.c b/src/timestamp.c index 9273758..b5ec306 100644 --- a/src/timestamp.c +++ b/src/timestamp.c @@ -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; diff --git a/src/timestamp.h b/src/timestamp.h index 0544544..6a10d98 100644 --- a/src/timestamp.h +++ b/src/timestamp.h @@ -29,7 +29,6 @@ typedef enum TIMESTAMP_24HOUR_DELTA, TIMESTAMP_ISO8601, TIMESTAMP_EPOCH, - TIMESTAMP_EPOCH_USEC, TIMESTAMP_END, } timestamp_t; diff --git a/src/tty.c b/src/tty.c index efda859..c72fd67 100644 --- a/src/tty.c +++ b/src/tty.c @@ -22,15 +22,6 @@ #if defined(__linux__) #include #endif - -#if defined(__APPLE__) || defined(__MACH__) -#include -#include -#include -#include -#include -#endif - #include "version.h" #include "config.h" #include @@ -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; } +