Compare commits
24 changed files with 348 additions and 1785 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,5 +1,4 @@
|
||||||
build/
|
build/
|
||||||
.vscode/
|
.vscode/
|
||||||
.vim/
|
|
||||||
*.log
|
*.log
|
||||||
tags
|
tags
|
||||||
3
.gitmodules
vendored
3
.gitmodules
vendored
|
|
@ -1,3 +0,0 @@
|
||||||
[submodule "lib/PDCurses"]
|
|
||||||
path = lib/PDCurses
|
|
||||||
url = git@github.com:wmcbrine/PDCurses.git
|
|
||||||
201
LICENSE
201
LICENSE
|
|
@ -1,201 +0,0 @@
|
||||||
Apache License
|
|
||||||
Version 2.0, January 2004
|
|
||||||
http://www.apache.org/licenses/
|
|
||||||
|
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
||||||
|
|
||||||
1. Definitions.
|
|
||||||
|
|
||||||
"License" shall mean the terms and conditions for use, reproduction,
|
|
||||||
and distribution as defined by Sections 1 through 9 of this document.
|
|
||||||
|
|
||||||
"Licensor" shall mean the copyright owner or entity authorized by
|
|
||||||
the copyright owner that is granting the License.
|
|
||||||
|
|
||||||
"Legal Entity" shall mean the union of the acting entity and all
|
|
||||||
other entities that control, are controlled by, or are under common
|
|
||||||
control with that entity. For the purposes of this definition,
|
|
||||||
"control" means (i) the power, direct or indirect, to cause the
|
|
||||||
direction or management of such entity, whether by contract or
|
|
||||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
||||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
||||||
|
|
||||||
"You" (or "Your") shall mean an individual or Legal Entity
|
|
||||||
exercising permissions granted by this License.
|
|
||||||
|
|
||||||
"Source" form shall mean the preferred form for making modifications,
|
|
||||||
including but not limited to software source code, documentation
|
|
||||||
source, and configuration files.
|
|
||||||
|
|
||||||
"Object" form shall mean any form resulting from mechanical
|
|
||||||
transformation or translation of a Source form, including but
|
|
||||||
not limited to compiled object code, generated documentation,
|
|
||||||
and conversions to other media types.
|
|
||||||
|
|
||||||
"Work" shall mean the work of authorship, whether in Source or
|
|
||||||
Object form, made available under the License, as indicated by a
|
|
||||||
copyright notice that is included in or attached to the work
|
|
||||||
(an example is provided in the Appendix below).
|
|
||||||
|
|
||||||
"Derivative Works" shall mean any work, whether in Source or Object
|
|
||||||
form, that is based on (or derived from) the Work and for which the
|
|
||||||
editorial revisions, annotations, elaborations, or other modifications
|
|
||||||
represent, as a whole, an original work of authorship. For the purposes
|
|
||||||
of this License, Derivative Works shall not include works that remain
|
|
||||||
separable from, or merely link (or bind by name) to the interfaces of,
|
|
||||||
the Work and Derivative Works thereof.
|
|
||||||
|
|
||||||
"Contribution" shall mean any work of authorship, including
|
|
||||||
the original version of the Work and any modifications or additions
|
|
||||||
to that Work or Derivative Works thereof, that is intentionally
|
|
||||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
||||||
or by an individual or Legal Entity authorized to submit on behalf of
|
|
||||||
the copyright owner. For the purposes of this definition, "submitted"
|
|
||||||
means any form of electronic, verbal, or written communication sent
|
|
||||||
to the Licensor or its representatives, including but not limited to
|
|
||||||
communication on electronic mailing lists, source code control systems,
|
|
||||||
and issue tracking systems that are managed by, or on behalf of, the
|
|
||||||
Licensor for the purpose of discussing and improving the Work, but
|
|
||||||
excluding communication that is conspicuously marked or otherwise
|
|
||||||
designated in writing by the copyright owner as "Not a Contribution."
|
|
||||||
|
|
||||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
||||||
on behalf of whom a Contribution has been received by Licensor and
|
|
||||||
subsequently incorporated within the Work.
|
|
||||||
|
|
||||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
copyright license to reproduce, prepare Derivative Works of,
|
|
||||||
publicly display, publicly perform, sublicense, and distribute the
|
|
||||||
Work and such Derivative Works in Source or Object form.
|
|
||||||
|
|
||||||
3. Grant of Patent License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
(except as stated in this section) patent license to make, have made,
|
|
||||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
||||||
where such license applies only to those patent claims licensable
|
|
||||||
by such Contributor that are necessarily infringed by their
|
|
||||||
Contribution(s) alone or by combination of their Contribution(s)
|
|
||||||
with the Work to which such Contribution(s) was submitted. If You
|
|
||||||
institute patent litigation against any entity (including a
|
|
||||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
||||||
or a Contribution incorporated within the Work constitutes direct
|
|
||||||
or contributory patent infringement, then any patent licenses
|
|
||||||
granted to You under this License for that Work shall terminate
|
|
||||||
as of the date such litigation is filed.
|
|
||||||
|
|
||||||
4. Redistribution. You may reproduce and distribute copies of the
|
|
||||||
Work or Derivative Works thereof in any medium, with or without
|
|
||||||
modifications, and in Source or Object form, provided that You
|
|
||||||
meet the following conditions:
|
|
||||||
|
|
||||||
(a) You must give any other recipients of the Work or
|
|
||||||
Derivative Works a copy of this License; and
|
|
||||||
|
|
||||||
(b) You must cause any modified files to carry prominent notices
|
|
||||||
stating that You changed the files; and
|
|
||||||
|
|
||||||
(c) You must retain, in the Source form of any Derivative Works
|
|
||||||
that You distribute, all copyright, patent, trademark, and
|
|
||||||
attribution notices from the Source form of the Work,
|
|
||||||
excluding those notices that do not pertain to any part of
|
|
||||||
the Derivative Works; and
|
|
||||||
|
|
||||||
(d) If the Work includes a "NOTICE" text file as part of its
|
|
||||||
distribution, then any Derivative Works that You distribute must
|
|
||||||
include a readable copy of the attribution notices contained
|
|
||||||
within such NOTICE file, excluding those notices that do not
|
|
||||||
pertain to any part of the Derivative Works, in at least one
|
|
||||||
of the following places: within a NOTICE text file distributed
|
|
||||||
as part of the Derivative Works; within the Source form or
|
|
||||||
documentation, if provided along with the Derivative Works; or,
|
|
||||||
within a display generated by the Derivative Works, if and
|
|
||||||
wherever such third-party notices normally appear. The contents
|
|
||||||
of the NOTICE file are for informational purposes only and
|
|
||||||
do not modify the License. You may add Your own attribution
|
|
||||||
notices within Derivative Works that You distribute, alongside
|
|
||||||
or as an addendum to the NOTICE text from the Work, provided
|
|
||||||
that such additional attribution notices cannot be construed
|
|
||||||
as modifying the License.
|
|
||||||
|
|
||||||
You may add Your own copyright statement to Your modifications and
|
|
||||||
may provide additional or different license terms and conditions
|
|
||||||
for use, reproduction, or distribution of Your modifications, or
|
|
||||||
for any such Derivative Works as a whole, provided Your use,
|
|
||||||
reproduction, and distribution of the Work otherwise complies with
|
|
||||||
the conditions stated in this License.
|
|
||||||
|
|
||||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
||||||
any Contribution intentionally submitted for inclusion in the Work
|
|
||||||
by You to the Licensor shall be under the terms and conditions of
|
|
||||||
this License, without any additional terms or conditions.
|
|
||||||
Notwithstanding the above, nothing herein shall supersede or modify
|
|
||||||
the terms of any separate license agreement you may have executed
|
|
||||||
with Licensor regarding such Contributions.
|
|
||||||
|
|
||||||
6. Trademarks. This License does not grant permission to use the trade
|
|
||||||
names, trademarks, service marks, or product names of the Licensor,
|
|
||||||
except as required for reasonable and customary use in describing the
|
|
||||||
origin of the Work and reproducing the content of the NOTICE file.
|
|
||||||
|
|
||||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
||||||
agreed to in writing, Licensor provides the Work (and each
|
|
||||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
implied, including, without limitation, any warranties or conditions
|
|
||||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
||||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
||||||
appropriateness of using or redistributing the Work and assume any
|
|
||||||
risks associated with Your exercise of permissions under this License.
|
|
||||||
|
|
||||||
8. Limitation of Liability. In no event and under no legal theory,
|
|
||||||
whether in tort (including negligence), contract, or otherwise,
|
|
||||||
unless required by applicable law (such as deliberate and grossly
|
|
||||||
negligent acts) or agreed to in writing, shall any Contributor be
|
|
||||||
liable to You for damages, including any direct, indirect, special,
|
|
||||||
incidental, or consequential damages of any character arising as a
|
|
||||||
result of this License or out of the use or inability to use the
|
|
||||||
Work (including but not limited to damages for loss of goodwill,
|
|
||||||
work stoppage, computer failure or malfunction, or any and all
|
|
||||||
other commercial damages or losses), even if such Contributor
|
|
||||||
has been advised of the possibility of such damages.
|
|
||||||
|
|
||||||
9. Accepting Warranty or Additional Liability. While redistributing
|
|
||||||
the Work or Derivative Works thereof, You may choose to offer,
|
|
||||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
||||||
or other liability obligations and/or rights consistent with this
|
|
||||||
License. However, in accepting such obligations, You may act only
|
|
||||||
on Your own behalf and on Your sole responsibility, not on behalf
|
|
||||||
of any other Contributor, and only if You agree to indemnify,
|
|
||||||
defend, and hold each Contributor harmless for any liability
|
|
||||||
incurred by, or claims asserted against, such Contributor by reason
|
|
||||||
of your accepting any such warranty or additional liability.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
APPENDIX: How to apply the Apache License to your work.
|
|
||||||
|
|
||||||
To apply the Apache License to your work, attach the following
|
|
||||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
||||||
replaced with your own identifying information. (Don't include
|
|
||||||
the brackets!) The text should be enclosed in the appropriate
|
|
||||||
comment syntax for the file format. We also recommend that a
|
|
||||||
file or class name and description of purpose be included on the
|
|
||||||
same "printed page" as the copyright notice for easier
|
|
||||||
identification within third-party archives.
|
|
||||||
|
|
||||||
Copyright 2025 Aaron Cook
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
42
Makefile
42
Makefile
|
|
@ -1,39 +1,24 @@
|
||||||
OS=LINUX
|
CC=clang
|
||||||
|
CFLAGS=-g -Wall -MMD -pthread -D_XOPEN_SOURCE=700 -D_POSIX_C_SOURCE
|
||||||
|
LDFLAGS=-lpthread -lncurses
|
||||||
|
|
||||||
DIR_SRC=src
|
DIR_SRC=src
|
||||||
DIR_BUILD=build
|
DIR_BUILD=build
|
||||||
DIR_OBJ=$(DIR_BUILD)/obj
|
DIR_OBJ=$(DIR_BUILD)/obj
|
||||||
DIR_BIN=$(DIR_BUILD)/bin
|
DIR_BIN=$(DIR_BUILD)/bin
|
||||||
|
|
||||||
CFLAGS=-Wall -MMD -pthread -D_XOPEN_SOURCE=700 -D_POSIX_C_SOURCE=199309L
|
TARGET=$(DIR_BIN)/bytenuts
|
||||||
LIB_DEPS=
|
|
||||||
LIB_OBJS=
|
DEBUG ?= 0
|
||||||
|
ifeq ($(DEBUG), 1)
|
||||||
|
CFLAGS += -DDEBUG
|
||||||
|
endif
|
||||||
|
|
||||||
SRCS=$(wildcard $(DIR_SRC)/*.c)
|
SRCS=$(wildcard $(DIR_SRC)/*.c)
|
||||||
OBJS=$(addprefix $(DIR_OBJ)/,$(addsuffix .o,$(basename $(SRCS))))
|
OBJS=$(addprefix $(DIR_OBJ)/,$(addsuffix .o,$(basename $(SRCS))))
|
||||||
DEPS=$(addprefix $(DIR_OBJ)/,$(addsuffix .d,$(basename $(SRCS))))
|
DEPS=$(addprefix $(DIR_OBJ)/,$(addsuffix .d,$(basename $(SRCS))))
|
||||||
|
|
||||||
ifeq ($(OS), LINUX)
|
.PHONY: all install uninstall clean
|
||||||
CC=clang
|
|
||||||
LDFLAGS=-lncurses -lpthread
|
|
||||||
TARGET=$(DIR_BIN)/bytenuts
|
|
||||||
else
|
|
||||||
CC=x86_64-w64-mingw32-gcc
|
|
||||||
CFLAGS+=-Ilib/PDCurses/wincon/ -Ilib/PDCurses/
|
|
||||||
LDFLAGS=-lpthread -static
|
|
||||||
LIB_DEPS+=PDCurses
|
|
||||||
LIB_OBJS+=lib/PDCurses/wincon/*.o
|
|
||||||
TARGET=$(DIR_BIN)/bytenuts.exe
|
|
||||||
endif
|
|
||||||
|
|
||||||
DEBUG ?= 0
|
|
||||||
ifeq ($(DEBUG), 1)
|
|
||||||
CFLAGS += -g
|
|
||||||
else
|
|
||||||
CFLAGS += -O2
|
|
||||||
endif
|
|
||||||
|
|
||||||
.PHONY: all install uninstall clean PDCurses
|
|
||||||
|
|
||||||
all: $(TARGET)
|
all: $(TARGET)
|
||||||
|
|
||||||
|
|
@ -43,19 +28,16 @@ install:
|
||||||
uninstall:
|
uninstall:
|
||||||
rm -f /usr/local/bin/bytenuts
|
rm -f /usr/local/bin/bytenuts
|
||||||
|
|
||||||
$(TARGET): $(OBJS) $(LIB_DEPS)
|
$(TARGET): $(OBJS)
|
||||||
@mkdir -p $(dir $@)
|
@mkdir -p $(dir $@)
|
||||||
@echo "link and compile for $@"
|
@echo "link and compile for $@"
|
||||||
@$(CC) $(CFLAGS) $(LDFLAGS) -o $(TARGET) $(OBJS) $(LIB_OBJS)
|
@$(CC) $(CFLAGS) $(LDFLAGS) -o $(TARGET) $(OBJS)
|
||||||
|
|
||||||
$(DIR_OBJ)/$(DIR_SRC)/%.o: $(DIR_SRC)/%.c
|
$(DIR_OBJ)/$(DIR_SRC)/%.o: $(DIR_SRC)/%.c
|
||||||
@mkdir -p $(dir $@)
|
@mkdir -p $(dir $@)
|
||||||
@echo "compile $<"
|
@echo "compile $<"
|
||||||
@$(CC) $(CFLAGS) -c $< -o $@
|
@$(CC) $(CFLAGS) -c $< -o $@
|
||||||
|
|
||||||
PDCurses:
|
|
||||||
cd lib/PDCurses/wincon && $(MAKE) -j CC=x86_64-w64-mingw32-gcc
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf $(DIR_BUILD)
|
rm -rf $(DIR_BUILD)
|
||||||
|
|
||||||
|
|
|
||||||
84
README.md
84
README.md
|
|
@ -22,11 +22,22 @@ Bytenuts has been tested and built on x86-64 and 32-bit ARM Ubuntu 18.
|
||||||
- Output history - Use page up/down, home/end, and ctrl + up/down arrow to scroll through the output window
|
- Output history - Use page up/down, home/end, and ctrl + up/down arrow to scroll through the output window
|
||||||
- Output logging - Output can be saved to a log file passed in with the `-l` option
|
- Output logging - Output can be saved to a log file passed in with the `-l` option
|
||||||
- Quick commands - Pages of quick commands are loaded from `~/.config/bytenuts/commands[1-10]`
|
- Quick commands - Pages of quick commands are loaded from `~/.config/bytenuts/commands[1-10]`
|
||||||
- Session resumption - Bytenuts can load the previous instance's commands and serial output
|
|
||||||
|
|
||||||
Sample screenshot running in Windows Terminal and WSL:
|
## Planned Features
|
||||||
|
|
||||||

|
Bytenuts is still very early in development. Here are some features that may get implemented.
|
||||||
|
|
||||||
|
- Other transfer protocols - Maybe Kermit and ZModem
|
||||||
|
|
||||||
|
## Un-Planned Features
|
||||||
|
|
||||||
|
Bytenuts is not supposed to be the be-all end-all application, nor is it supposed to be able to do everything SecureCRT can. Here are some things it probably won't have.
|
||||||
|
|
||||||
|
- Non-serial communications - No SSH, telnet, etc.
|
||||||
|
- Elaborate menuing - Bytenuts functions will be shortcut driven to focus on what's important (the serial communication)
|
||||||
|
- Script loading - Think like how you can load scripts in ZOC on SecureCRT. Write normal Python scripts if you want to do that kind of stuff.
|
||||||
|
- Multi connection support - Open another instance of Bytenuts if you need to
|
||||||
|
- Windows support - No
|
||||||
|
|
||||||
## Launch Options
|
## Launch Options
|
||||||
|
|
||||||
|
|
@ -39,41 +50,12 @@ bytenuts [OPTIONS] <serial path>
|
||||||
|
|
||||||
Configs get loaded from ${HOME}/.bytenuts/config (if file exists)
|
Configs get loaded from ${HOME}/.bytenuts/config (if file exists)
|
||||||
|
|
||||||
OPTIONS
|
OPTIONS
|
||||||
=========
|
|
||||||
|
|
||||||
-h
|
-h: show this help
|
||||||
Show this help.
|
-b <baud>: set a baud rate (default 115200)
|
||||||
|
-l <path>: log all output to the given file
|
||||||
-b <baud>
|
-c <path>: load a config from the given path rather than the default
|
||||||
Set a baud rate (default 115200).
|
|
||||||
|
|
||||||
-l <path>
|
|
||||||
Log all output to the given file.
|
|
||||||
|
|
||||||
-c <path>
|
|
||||||
Load a config from the given path rather than the default.
|
|
||||||
|
|
||||||
-r|--resume
|
|
||||||
Resume the previous instance of bytenuts.
|
|
||||||
|
|
||||||
--colors=<0|1>
|
|
||||||
Turn 8-bit ANSI colors off/on.
|
|
||||||
|
|
||||||
--echo=<0|1>
|
|
||||||
Turn input echoing off/on.
|
|
||||||
|
|
||||||
--no_crlf=<0|1>
|
|
||||||
Choose to send LF and not CRLF on input.
|
|
||||||
|
|
||||||
--escape=<char>
|
|
||||||
Change the default ctrl+b escape character.
|
|
||||||
|
|
||||||
--inter_cmd_to=<ms>
|
|
||||||
Set the intercommand timeout in milliseconds (default is 10ms).
|
|
||||||
|
|
||||||
--time_fmt=<fmt>
|
|
||||||
Time format as used by strftime to prepend to every log line.
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Navigation
|
## Navigation
|
||||||
|
|
@ -88,23 +70,17 @@ If the output window reaches the current line of output, then the output will co
|
||||||
|
|
||||||
## Configuration File
|
## Configuration File
|
||||||
|
|
||||||
Many of the launch options have a corresponding configuration token that can be saved to a config file. Each config is defined like `<name>=<value>`. Here is a sample config:
|
There are only three supported configs currently. Each config is defined like `<name>=<value>`. Here is a sample config:
|
||||||
|
|
||||||
```
|
```
|
||||||
colors=1
|
colors=1
|
||||||
echo=0
|
echo=0
|
||||||
no_crlf=0
|
no_crlf=0
|
||||||
escape=a
|
|
||||||
inter_cmd_to=100
|
|
||||||
time_fmt=%X %m/%d %Z|>
|
|
||||||
```
|
```
|
||||||
|
|
||||||
- `colors` - enable parsing of 8-bit ANSI color codes
|
- `colors` - enable parsing of 8-bit ANSI color codes
|
||||||
- `echo` - echo input to the terminal in app
|
- `echo` - echo input to the terminal in app
|
||||||
- `no_crlf` - just send a line feed (`\n`) for user input rather than carriage return + line feed (`\r\n`)
|
- `no_crlf` - just send a line feed (`\n`) for user input rather than carriage return + line feed (`\r\n`)
|
||||||
- `escape` - change what character is used as an escape sequence for commands (e.g. if set to `escape=a`, Bytenuts can be exited with `ctrl+a, q`)
|
|
||||||
- `inter_cmd_to` - Set a timeout in milliseconds that must be met. Useful for pasting in multiple lines and ensuring a short delay in between the commands.
|
|
||||||
- `time_fmt` - The time format string (see `man 3 strftime`) to be prepended to every line in the log file (will not get printed in the console view)
|
|
||||||
|
|
||||||
Bytenuts looks for the configs at `~/.config/bytenuts/config`.
|
Bytenuts looks for the configs at `~/.config/bytenuts/config`.
|
||||||
|
|
||||||
|
|
@ -124,24 +100,14 @@ The help prompt lists the available commands.
|
||||||
Commands (lead with ctrl-b):
|
Commands (lead with ctrl-b):
|
||||||
c: print available quick commands
|
c: print available quick commands
|
||||||
0-9: load the given quick command (0 is 10)
|
0-9: load the given quick command (0 is 10)
|
||||||
p: select a different quick commands page
|
p, 0-9: select a different quick commands page (0 is 10)
|
||||||
i: view info/stats
|
i: view info/stats
|
||||||
x: start XModem upload with 128B payloads
|
x: start XModem upload with 128B payloads
|
||||||
X: start XModem upload with 1024B payloads
|
X: start XModem upload with 1024B payloads
|
||||||
H: enter/exit hex buffer mode
|
|
||||||
h: view this help
|
h: view this help
|
||||||
q: quit Bytenuts
|
q: quit Bytenuts
|
||||||
```
|
```
|
||||||
|
|
||||||
### Hex Buffer Mode
|
|
||||||
When the `ctrl+b H` command has been issued for the first time, you will enter hex buffer mode. In this mode, the input buffer is interpreted as a hex string and will be converted to its byte equivalent before it gets sent to the target. Example inputs:
|
|
||||||
|
|
||||||
- `746573740d0a` gets sent as `test\r\n`
|
|
||||||
- `d0a` gets sent as `\r\n` as it assumed the leading `0` has been omitted
|
|
||||||
- `74657374xd0a` gets sent as `test` since the `xd` byte contains an invalid hex character
|
|
||||||
|
|
||||||
Hex buffer mode can be turned off with a second `ctrl+b H`.
|
|
||||||
|
|
||||||
## Quick Commands
|
## Quick Commands
|
||||||
|
|
||||||
You can provide multiple pages of quick commands you can easily load in with `ctrl+b [0-9]` in the files `~/.config/bytenuts/commands<idx>`. Bytenuts will load pages starting from `commands1` until a `commands<idx>` no longer exists. The commands should be newline separated. When a command is loaded, the entire contents of the line is loaded into the input buffer.
|
You can provide multiple pages of quick commands you can easily load in with `ctrl+b [0-9]` in the files `~/.config/bytenuts/commands<idx>`. Bytenuts will load pages starting from `commands1` until a `commands<idx>` no longer exists. The commands should be newline separated. When a command is loaded, the entire contents of the line is loaded into the input buffer.
|
||||||
|
|
@ -150,16 +116,10 @@ Bytenuts will only store the first ten commands for each page, so the first 10 l
|
||||||
|
|
||||||
On startup, command page 1 is selected. You can select which command page is currently loaded by doing `ctrl+p, [0-9]` to load the desired page (note that index 0 will correspond to `commands10`).
|
On startup, command page 1 is selected. You can select which command page is currently loaded by doing `ctrl+p, [0-9]` to load the desired page (note that index 0 will correspond to `commands10`).
|
||||||
|
|
||||||
## Session Resumption
|
|
||||||
|
|
||||||
Bytenuts caches the current instance's command list and output buffer in `~/.config/bytenuts/inbuf.log` and `~/.config/bytenuts/outbuf.log` respectively. If launched with the `-r` flag, Bytenuts will load in the input and output history from those files. The `outbuf.log` can also serve as a backup log if one forgot to launch with the `-l` flag.
|
|
||||||
|
|
||||||
While Bytenuts is running, the process will be writing its backup output and input to `~/.config/bytenuts/outbuf.<pid>.log` and `~/.config/bytenuts/inbuf.<pid>.log` and rename those files to the paths without the PID upon exit. This allows for multiple processes to be open without writing to the same log files. Thus, the `-r` flag will load the most recently exited Bytenuts instance.
|
|
||||||
|
|
||||||
## Bugs
|
## Bugs
|
||||||
|
|
||||||
Check out known bugs in the [issues tab](https://github.com/cookthebook/bytenuts/issues?q=is%3Aissue+is%3Aopen+label%3Abug).
|
Check out known bugs in the [issues tab](https://github.com/cookthebook/bytenuts/issues?q=is%3Aissue+is%3Aopen+label%3Abug).
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
Building Bytenuts is very simple. All you need is clang and libncurses (`sudo apt install clang libncurses5-dev`). Run `make` in the Bytenuts root directory to build. You can also install the build (creating a link in `/usr/local/bin` to the `build` directory) by running `sudo make install`.
|
Building Bytenuts is very simple. All you need is clang and libncurses. Run `make` in the Bytenuts root directory to build. You can also install the build (creating a link in `/usr/local/bin` to the `build` directory) by running `sudo make install`.
|
||||||
|
|
|
||||||
BIN
Screenshot.png
BIN
Screenshot.png
Binary file not shown.
|
Before Width: | Height: | Size: 9.1 KiB |
|
|
@ -1 +0,0 @@
|
||||||
Subproject commit 5c62af03e9a05e3a3ae8c8354c1234b772dcf4b0
|
|
||||||
117
src/bstr.c
117
src/bstr.c
|
|
@ -1,117 +0,0 @@
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include "bstr.h"
|
|
||||||
|
|
||||||
typedef struct bstr_history_struct {
|
|
||||||
char **strs; /* strings in our history, in reverse chronological order */
|
|
||||||
int len; /* number of strings in the history */
|
|
||||||
int pos; /* current position index in the history */
|
|
||||||
} bstr_history_t;
|
|
||||||
|
|
||||||
char *
|
|
||||||
bstr_print(char *base, const char *fmt, ...)
|
|
||||||
{
|
|
||||||
char *new_buf;
|
|
||||||
size_t new_len;
|
|
||||||
va_list ap;
|
|
||||||
|
|
||||||
va_start(ap, fmt);
|
|
||||||
new_len = vsnprintf(NULL, 0, fmt, ap);
|
|
||||||
va_end(ap);
|
|
||||||
|
|
||||||
new_buf = calloc(1, new_len + 1);
|
|
||||||
|
|
||||||
va_start(ap, fmt);
|
|
||||||
vsprintf(new_buf, fmt, ap);
|
|
||||||
va_end(ap);
|
|
||||||
|
|
||||||
if (!base) {
|
|
||||||
return new_buf;
|
|
||||||
} else {
|
|
||||||
size_t baselen = strlen(base);
|
|
||||||
|
|
||||||
base = realloc(base, baselen + new_len + 1);
|
|
||||||
memcpy(&base[baselen], new_buf, new_len);
|
|
||||||
base[baselen + new_len] = '\0';
|
|
||||||
|
|
||||||
free(new_buf);
|
|
||||||
return base;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bstr_history_handle
|
|
||||||
bstr_history_create(void)
|
|
||||||
{
|
|
||||||
bstr_history_t *ret = calloc(1, sizeof(bstr_history_t));
|
|
||||||
ret->pos = -1;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *
|
|
||||||
bstr_history_atpos(bstr_history_handle hist)
|
|
||||||
{
|
|
||||||
if ((hist->len == 0) || (hist->pos < 0)) {
|
|
||||||
return NULL;
|
|
||||||
} else {
|
|
||||||
return hist->strs[hist->pos];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
bstr_history_new_entry(bstr_history_handle hist, const char *str)
|
|
||||||
{
|
|
||||||
/* stored in reverse chronological order */
|
|
||||||
hist->len++;
|
|
||||||
hist->strs = realloc(hist->strs, sizeof(char *) * hist->len);
|
|
||||||
hist->strs[hist->len-1] = strdup(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
bstr_history_older(bstr_history_handle hist)
|
|
||||||
{
|
|
||||||
if (hist->len == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (hist->pos == -1) {
|
|
||||||
hist->pos = hist->len - 1;
|
|
||||||
} else {
|
|
||||||
hist->pos--;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hist->pos < 0)
|
|
||||||
hist->pos = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
bstr_history_newer(bstr_history_handle hist)
|
|
||||||
{
|
|
||||||
if ((hist->len == 0) || (hist->pos == -1))
|
|
||||||
return;
|
|
||||||
|
|
||||||
hist->pos++;
|
|
||||||
if (hist->pos >= hist->len)
|
|
||||||
hist->pos = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
bstr_history_unset_pos(bstr_history_handle hist)
|
|
||||||
{
|
|
||||||
hist->pos = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
bstr_history_destroy(bstr_history_handle hist)
|
|
||||||
{
|
|
||||||
if (!hist)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (hist->len > 0) {
|
|
||||||
for (int i = 0; i < hist->len; i++) {
|
|
||||||
free(hist->strs[i]);
|
|
||||||
}
|
|
||||||
free(hist->strs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
40
src/bstr.h
40
src/bstr.h
|
|
@ -1,40 +0,0 @@
|
||||||
#ifndef _BSTR_H_
|
|
||||||
#define _BSTR_H_
|
|
||||||
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
typedef struct bstr_history_struct * bstr_history_handle;
|
|
||||||
|
|
||||||
|
|
||||||
/* printf onto the end of base, reallocating and returning that buffer
|
|
||||||
* can provide NULL for no base */
|
|
||||||
__attribute__((format(printf, 2, 3)))
|
|
||||||
char *bstr_print(char *base, const char *fmt, ...);
|
|
||||||
|
|
||||||
/* Create a new bstr_history struct. The position of the history will start as
|
|
||||||
* unset. */
|
|
||||||
bstr_history_handle bstr_history_create(void);
|
|
||||||
|
|
||||||
/* return the string at the current history position (NULL if empty or position
|
|
||||||
* is not set) */
|
|
||||||
const char *bstr_history_atpos(bstr_history_handle hist);
|
|
||||||
|
|
||||||
/* Put a new entry into the history */
|
|
||||||
void bstr_history_new_entry(bstr_history_handle hist, const char *str);
|
|
||||||
|
|
||||||
/* Move the postition index one entry older. If the history position is unset,
|
|
||||||
* it is set to the newest entry. */
|
|
||||||
void bstr_history_older(bstr_history_handle hist);
|
|
||||||
|
|
||||||
/* Move the position index one entry newer. If the position is at the newest
|
|
||||||
* entry, it becomes unset. If the position is unset, it stays unset. */
|
|
||||||
void bstr_history_newer(bstr_history_handle hist);
|
|
||||||
|
|
||||||
/* Unset the history position index */
|
|
||||||
void bstr_history_unset_pos(bstr_history_handle hist);
|
|
||||||
|
|
||||||
/* destroy/free a bstr_history */
|
|
||||||
void bstr_history_destroy(bstr_history_handle hist);
|
|
||||||
|
|
||||||
#endif /* _BSTR_H_ */
|
|
||||||
313
src/bytenuts.c
313
src/bytenuts.c
|
|
@ -1,14 +1,11 @@
|
||||||
#ifdef __MINGW32__
|
#include <ncurses.h>
|
||||||
# include <curses.h>
|
|
||||||
#else
|
|
||||||
# include <ncurses.h>
|
|
||||||
#endif
|
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <sched.h>
|
#include <sched.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#include <termios.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "bytenuts.h"
|
#include "bytenuts.h"
|
||||||
|
|
@ -19,24 +16,21 @@
|
||||||
"USAGE\n\n" \
|
"USAGE\n\n" \
|
||||||
"bytenuts [OPTIONS] <serial path>\n" \
|
"bytenuts [OPTIONS] <serial path>\n" \
|
||||||
"\nConfigs get loaded from ${HOME}/.bytenuts/config (if file exists)\n" \
|
"\nConfigs get loaded from ${HOME}/.bytenuts/config (if file exists)\n" \
|
||||||
"\n OPTIONS\n=========\n\n" \
|
"\nOPTIONS\n\n" \
|
||||||
"-h\n Show this help.\n\n" \
|
"-h: show this help\n" \
|
||||||
"-b <baud>\n Set a baud rate (default 115200).\n\n" \
|
"-b <baud>: set a baud rate (default 115200)\n" \
|
||||||
"-l <path>\n Log all output to the given file.\n\n" \
|
"-l <path>: log all output to the given file\n" \
|
||||||
"-c <path>\n Load a config from the given path rather than the default.\n\n" \
|
"-c <path>: load a config from the given path rather than the default\n" \
|
||||||
"-r|--resume\n Resume the previous instance of bytenuts.\n\n" \
|
"--colors=<0|1>: turn 8-bit ANSI colors off/on\n" \
|
||||||
"--colors=<0|1>\n Turn 8-bit ANSI colors off/on.\n\n" \
|
"--echo=<0|1>: turn input echoing off/on\n" \
|
||||||
"--echo=<0|1>\n Turn input echoing off/on.\n\n" \
|
"--no_crlf=<0|1>: choose to send LF and not CRLF on input\n" \
|
||||||
"--no_crlf=<0|1>\n Choose to send LF and not CRLF on input.\n\n" \
|
"--escape=<char>: change the default ctrl+b escape character\n" \
|
||||||
"--escape=<char>\n Change the default ctrl+b escape character.\n\n" \
|
|
||||||
"--inter_cmd_to=<ms>\n Set the intercommand timeout in milliseconds (default is 10ms).\n\n" \
|
|
||||||
"--time_fmt=<fmt>\n Time format as used by strftime to prepend to every log line.\n" \
|
|
||||||
)
|
)
|
||||||
|
|
||||||
static int parse_args(int argc, char **argv);
|
static int parse_args(int argc, char **argv);
|
||||||
static int load_configs();
|
static int load_configs();
|
||||||
static int read_state();
|
static speed_t string_to_speed(const char *speed);
|
||||||
static int load_state();
|
static const char *speed_to_string(speed_t speed);
|
||||||
|
|
||||||
static bytenuts_t bytenuts;
|
static bytenuts_t bytenuts;
|
||||||
|
|
||||||
|
|
@ -54,28 +48,16 @@ bytenuts_run(int argc, char **argv)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bytenuts.resume) {
|
|
||||||
read_state();
|
|
||||||
}
|
|
||||||
|
|
||||||
bytenuts.serial_fd = serial_open(bytenuts.config.serial_path, bytenuts.config.baud);
|
bytenuts.serial_fd = serial_open(bytenuts.config.serial_path, bytenuts.config.baud);
|
||||||
if (bytenuts.serial_fd == SERIAL_INVALID) {
|
if (bytenuts.serial_fd < 0) {
|
||||||
printf(
|
|
||||||
"Failed to open serial port \"%s\"\r\n",
|
|
||||||
bytenuts.config.serial_path
|
|
||||||
);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("Opened \"%s\"\r\n", bytenuts.config.serial_path);
|
|
||||||
|
|
||||||
#ifndef __MINGW32__
|
|
||||||
/* use pseudo-terminals for testing purposes */
|
/* use pseudo-terminals for testing purposes */
|
||||||
if (!strcmp(bytenuts.config.serial_path, "/dev/ptmx")) {
|
if (!strcmp(bytenuts.config.serial_path, "/dev/ptmx")) {
|
||||||
grantpt(bytenuts.serial_fd);
|
grantpt(bytenuts.serial_fd);
|
||||||
unlockpt(bytenuts.serial_fd);
|
unlockpt(bytenuts.serial_fd);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
// Initialize ncurses
|
// Initialize ncurses
|
||||||
initscr();
|
initscr();
|
||||||
|
|
@ -118,24 +100,11 @@ bytenuts_run(int argc, char **argv)
|
||||||
cheerios_start(&bytenuts);
|
cheerios_start(&bytenuts);
|
||||||
ingest_start(&bytenuts);
|
ingest_start(&bytenuts);
|
||||||
|
|
||||||
#ifndef __MINGW32__
|
|
||||||
if (!strcmp(bytenuts.config.serial_path, "/dev/ptmx")) {
|
if (!strcmp(bytenuts.config.serial_path, "/dev/ptmx")) {
|
||||||
char info[128];
|
char info[128];
|
||||||
snprintf(info, sizeof(info), "Opened PTY port %s", ptsname(bytenuts.serial_fd));
|
snprintf(info, sizeof(info), "Opened PTY port %s", ptsname(bytenuts.serial_fd));
|
||||||
cheerios_info(info);
|
cheerios_info(info);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
if (bytenuts.resume) {
|
|
||||||
load_state();
|
|
||||||
} else {
|
|
||||||
cheerios_print(
|
|
||||||
"Welcome to Bytenuts\r\n"
|
|
||||||
"To quit press ctrl-%c q\r\n"
|
|
||||||
"To see all commands, press ctrl-%c h\r\n",
|
|
||||||
bytenuts.config.escape, bytenuts.config.escape
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_mutex_lock(&bytenuts.lock);
|
pthread_mutex_lock(&bytenuts.lock);
|
||||||
pthread_cond_wait(&bytenuts.stop_cond, &bytenuts.lock);
|
pthread_cond_wait(&bytenuts.stop_cond, &bytenuts.lock);
|
||||||
|
|
@ -166,7 +135,7 @@ bytenuts_kill()
|
||||||
delwin(bytenuts.out_win);
|
delwin(bytenuts.out_win);
|
||||||
endwin();
|
endwin();
|
||||||
|
|
||||||
serial_close(bytenuts.serial_fd);
|
close(bytenuts.serial_fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
|
|
@ -182,7 +151,7 @@ bytenuts_print_stats()
|
||||||
cheerios_insert(st_line, strlen(st_line));
|
cheerios_insert(st_line, strlen(st_line));
|
||||||
sprintf(st_line, "escape: %c\r\n", bytenuts.config.escape);
|
sprintf(st_line, "escape: %c\r\n", bytenuts.config.escape);
|
||||||
cheerios_insert(st_line, strlen(st_line));
|
cheerios_insert(st_line, strlen(st_line));
|
||||||
sprintf(st_line, "baud: %ld\r\n", bytenuts.config.baud);
|
sprintf(st_line, "baud: %s\r\n", speed_to_string(bytenuts.config.baud));
|
||||||
cheerios_insert(st_line, strlen(st_line));
|
cheerios_insert(st_line, strlen(st_line));
|
||||||
sprintf(st_line, "config_path: %s\r\n", bytenuts.config.config_path);
|
sprintf(st_line, "config_path: %s\r\n", bytenuts.config.config_path);
|
||||||
cheerios_insert(st_line, strlen(st_line));
|
cheerios_insert(st_line, strlen(st_line));
|
||||||
|
|
@ -190,10 +159,6 @@ bytenuts_print_stats()
|
||||||
cheerios_insert(st_line, strlen(st_line));
|
cheerios_insert(st_line, strlen(st_line));
|
||||||
sprintf(st_line, "serial_path: %s\r\n", bytenuts.config.serial_path);
|
sprintf(st_line, "serial_path: %s\r\n", bytenuts.config.serial_path);
|
||||||
cheerios_insert(st_line, strlen(st_line));
|
cheerios_insert(st_line, strlen(st_line));
|
||||||
sprintf(st_line, "inter_cmd_to: %d\r\n", bytenuts.config.inter_cmd_to);
|
|
||||||
cheerios_insert(st_line, strlen(st_line));
|
|
||||||
sprintf(st_line, "time_fmt: %s\r\n", bytenuts.config.time_fmt);
|
|
||||||
cheerios_insert(st_line, strlen(st_line));
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -275,8 +240,6 @@ bytenuts_set_status(int user, const char *fmt, ...)
|
||||||
int
|
int
|
||||||
bytenuts_update_screen_size()
|
bytenuts_update_screen_size()
|
||||||
{
|
{
|
||||||
char *old_status = strdup(bytenuts.bytenuts_status);
|
|
||||||
|
|
||||||
pthread_mutex_lock(&bytenuts.term_lock);
|
pthread_mutex_lock(&bytenuts.term_lock);
|
||||||
|
|
||||||
wresize(bytenuts.status_win, 1, COLS);
|
wresize(bytenuts.status_win, 1, COLS);
|
||||||
|
|
@ -293,10 +256,7 @@ bytenuts_update_screen_size()
|
||||||
|
|
||||||
pthread_mutex_unlock(&bytenuts.term_lock);
|
pthread_mutex_unlock(&bytenuts.term_lock);
|
||||||
|
|
||||||
bytenuts_set_status(STATUS_BYTENUTS, old_status);
|
|
||||||
cheerios_insert("", 0);
|
cheerios_insert("", 0);
|
||||||
ingest_refresh();
|
|
||||||
free(old_status);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -313,17 +273,10 @@ parse_args(int argc, char **argv)
|
||||||
/* setup default options first */
|
/* setup default options first */
|
||||||
bytenuts.config = CONFIG_DEFAULT;
|
bytenuts.config = CONFIG_DEFAULT;
|
||||||
{
|
{
|
||||||
#ifdef __MINGW32__
|
|
||||||
char *home = getenv("HOMEPATH");
|
|
||||||
char path[] = "\\.config\\bytenuts\\config";
|
|
||||||
bytenuts.config.config_path = malloc(strlen(home) + strlen(path) + 4);
|
|
||||||
sprintf(bytenuts.config.config_path, "C:\\%s%s", home, path);
|
|
||||||
#else
|
|
||||||
char *home = getenv("HOME");
|
char *home = getenv("HOME");
|
||||||
char path[] = "/.config/bytenuts/config";
|
char path[] = "/.config/bytenuts/config";
|
||||||
bytenuts.config.config_path = malloc(strlen(home) + strlen(path) + 1);
|
bytenuts.config.config_path = malloc(strlen(home) + strlen(path) + 1);
|
||||||
sprintf(bytenuts.config.config_path, "%s%s", home, path);
|
sprintf(bytenuts.config.config_path, "%s%s", home, path);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 1; i < argc - 1; i++) {
|
for (int i = 1; i < argc - 1; i++) {
|
||||||
|
|
@ -337,7 +290,7 @@ parse_args(int argc, char **argv)
|
||||||
if (i == argc - 1)
|
if (i == argc - 1)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
bytenuts.config.baud = strtol(argv[i], NULL, 10);
|
bytenuts.config.baud = string_to_speed(argv[i]);
|
||||||
}
|
}
|
||||||
else if (!strcmp(argv[i], "-l")) {
|
else if (!strcmp(argv[i], "-l")) {
|
||||||
i++;
|
i++;
|
||||||
|
|
@ -381,20 +334,6 @@ parse_args(int argc, char **argv)
|
||||||
bytenuts.config.escape = argv[i][9];
|
bytenuts.config.escape = argv[i][9];
|
||||||
bytenuts.config_overrides[3] = 1;
|
bytenuts.config_overrides[3] = 1;
|
||||||
}
|
}
|
||||||
else if (arg_len > 15 && !memcmp(argv[i], "--inter_cmd_to=", 15)) {
|
|
||||||
long cmd_to = strtol(&argv[i][15], NULL, 10);
|
|
||||||
if (cmd_to >= 0) {
|
|
||||||
bytenuts.config.inter_cmd_to = cmd_to;
|
|
||||||
bytenuts.config_overrides[4] = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (arg_len > 11 && !memcmp(argv[i], "--time_fmt=", 11)) {
|
|
||||||
bytenuts.config.time_fmt = strdup(&argv[i][11]);
|
|
||||||
bytenuts.config_overrides[5] = 1;
|
|
||||||
}
|
|
||||||
else if (!strcmp(argv[i], "--resume") || !strcmp(argv[i], "-r")) {
|
|
||||||
bytenuts.resume = 1;
|
|
||||||
}
|
|
||||||
else {
|
else {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -439,103 +378,141 @@ load_configs()
|
||||||
else if (!bytenuts.config_overrides[3] && !memcmp(line, "escape=", 7)) {
|
else if (!bytenuts.config_overrides[3] && !memcmp(line, "escape=", 7)) {
|
||||||
bytenuts.config.escape = line[7];
|
bytenuts.config.escape = line[7];
|
||||||
}
|
}
|
||||||
else if (!bytenuts.config_overrides[4] && !memcmp(line, "inter_cmd_to=", 13)) {
|
|
||||||
long cmd_to = strtol(&line[13], NULL, 10);
|
|
||||||
if (cmd_to >= 0) {
|
|
||||||
bytenuts.config.inter_cmd_to = cmd_to;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (!bytenuts.config_overrides[5] && !memcmp(line, "time_fmt=", 9)) {
|
|
||||||
size_t time_fmt_len;
|
|
||||||
bytenuts.config.time_fmt = strdup(&line[9]);
|
|
||||||
|
|
||||||
time_fmt_len = strlen(bytenuts.config.time_fmt);
|
|
||||||
while (
|
|
||||||
time_fmt_len > 0 &&
|
|
||||||
(
|
|
||||||
bytenuts.config.time_fmt[time_fmt_len-1] == '\r' ||
|
|
||||||
bytenuts.config.time_fmt[time_fmt_len-1] == '\n'
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
bytenuts.config.time_fmt[time_fmt_len-1] = 0;
|
|
||||||
time_fmt_len--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static speed_t
|
||||||
read_state()
|
string_to_speed(const char *speed)
|
||||||
{
|
{
|
||||||
struct stat st;
|
if (!strcmp("50", speed))
|
||||||
char *buf;
|
return B50;
|
||||||
char *cwd;
|
else if (!strcmp("75", speed))
|
||||||
char *home = getenv("HOME");
|
return B75;
|
||||||
FILE *fd;
|
else if (!strcmp("110", speed))
|
||||||
|
return B110;
|
||||||
if (!home)
|
else if (!strcmp("134", speed))
|
||||||
return 0;
|
return B134;
|
||||||
|
else if (!strcmp("150", speed))
|
||||||
cwd = getcwd(NULL, 0);
|
return B150;
|
||||||
chdir(home);
|
else if (!strcmp("200", speed))
|
||||||
|
return B200;
|
||||||
fd = fopen(".config/bytenuts/inbuf.log", "r");
|
else if (!strcmp("300", speed))
|
||||||
stat(".config/bytenuts/inbuf.log", &st);
|
return B300;
|
||||||
if (fd && st.st_size > 0) {
|
else if (!strcmp("600", speed))
|
||||||
buf = calloc(1, st.st_size + 1);
|
return B600;
|
||||||
while (fgets(buf, st.st_size+1, fd)) {
|
else if (!strcmp("1200", speed))
|
||||||
bytenuts.state.history_len++;
|
return B1200;
|
||||||
bytenuts.state.history = realloc(
|
else if (!strcmp("1800", speed))
|
||||||
bytenuts.state.history,
|
return B1800;
|
||||||
sizeof(char *) * bytenuts.state.history_len
|
else if (!strcmp("2400", speed))
|
||||||
);
|
return B2400;
|
||||||
bytenuts.state.history[bytenuts.state.history_len-1] = strdup(buf);
|
else if (!strcmp("4800", speed))
|
||||||
}
|
return B4800;
|
||||||
|
else if (!strcmp("9600", speed))
|
||||||
free(buf);
|
return B9600;
|
||||||
}
|
else if (!strcmp("19200", speed))
|
||||||
if (fd)
|
return B19200;
|
||||||
fclose(fd);
|
else if (!strcmp("38400", speed))
|
||||||
|
return B38400;
|
||||||
stat(".config/bytenuts/outbuf.log", &st);
|
else if (!strcmp("57600", speed))
|
||||||
fd = fopen(".config/bytenuts/outbuf.log", "r");
|
return B57600;
|
||||||
|
else if (!strcmp("115200", speed))
|
||||||
if (fd && st.st_size > 0) {
|
return B1152000;
|
||||||
bytenuts.state.buf = malloc(st.st_size);
|
else if (!strcmp("230400", speed))
|
||||||
bytenuts.state.buf_len = st.st_size;
|
return B230400;
|
||||||
fread(bytenuts.state.buf, 1, st.st_size, fd);
|
else if (!strcmp("460800", speed))
|
||||||
}
|
return B460800;
|
||||||
if (fd)
|
else if (!strcmp("500000", speed))
|
||||||
fclose(fd);
|
return B500000;
|
||||||
|
else if (!strcmp("576000", speed))
|
||||||
chdir(cwd);
|
return B576000;
|
||||||
free(cwd);
|
else if (!strcmp("921600", speed))
|
||||||
|
return B921600;
|
||||||
return 0;
|
else if (!strcmp("1000000", speed))
|
||||||
|
return B1000000;
|
||||||
|
else if (!strcmp("1152000", speed))
|
||||||
|
return B1152000;
|
||||||
|
else if (!strcmp("1500000", speed))
|
||||||
|
return B1500000;
|
||||||
|
else if (!strcmp("2000000", speed))
|
||||||
|
return B2000000;
|
||||||
|
else if (!strcmp("2500000", speed))
|
||||||
|
return B2500000;
|
||||||
|
else if (!strcmp("3000000", speed))
|
||||||
|
return B3000000;
|
||||||
|
else if (!strcmp("3500000", speed))
|
||||||
|
return B3500000;
|
||||||
|
else if (!strcmp("4000000", speed))
|
||||||
|
return B4000000;
|
||||||
|
else
|
||||||
|
return B0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static const char *
|
||||||
load_state()
|
speed_to_string(speed_t speed)
|
||||||
{
|
{
|
||||||
if (bytenuts.state.history) {
|
if (speed == B50)
|
||||||
ingest_set_history(bytenuts.state.history, bytenuts.state.history_len);
|
return "B50";
|
||||||
|
else if (speed == B75)
|
||||||
for (int i = 0; i < bytenuts.state.history_len; i++) {
|
return "B75";
|
||||||
free(bytenuts.state.history[i]);
|
else if (speed == B110)
|
||||||
}
|
return "B110";
|
||||||
free(bytenuts.state.history);
|
else if (speed == B134)
|
||||||
bytenuts.state.history = NULL;
|
return "B134";
|
||||||
bytenuts.state.history_len = 0;
|
else if (speed == B150)
|
||||||
}
|
return "B150";
|
||||||
|
else if (speed == B200)
|
||||||
if (bytenuts.state.buf) {
|
return "B200";
|
||||||
cheerios_insert(bytenuts.state.buf, bytenuts.state.buf_len);
|
else if (speed == B300)
|
||||||
free(bytenuts.state.buf);
|
return "B300";
|
||||||
bytenuts.state.buf = NULL;
|
else if (speed == B600)
|
||||||
bytenuts.state.buf_len = 0;
|
return "B600";
|
||||||
}
|
else if (speed == B1200)
|
||||||
|
return "B1200";
|
||||||
return 0;
|
else if (speed == B1800)
|
||||||
|
return "B1800";
|
||||||
|
else if (speed == B2400)
|
||||||
|
return "B2400";
|
||||||
|
else if (speed == B4800)
|
||||||
|
return "B4800";
|
||||||
|
else if (speed == B9600)
|
||||||
|
return "B9600";
|
||||||
|
else if (speed == B19200)
|
||||||
|
return "B19200";
|
||||||
|
else if (speed == B38400)
|
||||||
|
return "B38400";
|
||||||
|
else if (speed == B57600)
|
||||||
|
return "B57600";
|
||||||
|
else if (speed == B115200)
|
||||||
|
return "B1152000";
|
||||||
|
else if (speed == B230400)
|
||||||
|
return "B230400";
|
||||||
|
else if (speed == B460800)
|
||||||
|
return "B460800";
|
||||||
|
else if (speed == B500000)
|
||||||
|
return "B500000";
|
||||||
|
else if (speed == B576000)
|
||||||
|
return "B576000";
|
||||||
|
else if (speed == B921600)
|
||||||
|
return "B921600";
|
||||||
|
else if (speed == B1000000)
|
||||||
|
return "B1000000";
|
||||||
|
else if (speed == B1152000)
|
||||||
|
return "B1152000";
|
||||||
|
else if (speed == B1500000)
|
||||||
|
return "B1500000";
|
||||||
|
else if (speed == B2000000)
|
||||||
|
return "B2000000";
|
||||||
|
else if (speed == B2500000)
|
||||||
|
return "B2500000";
|
||||||
|
else if (speed == B3000000)
|
||||||
|
return "B3000000";
|
||||||
|
else if (speed == B3500000)
|
||||||
|
return "B3500000";
|
||||||
|
else if (speed == B4000000)
|
||||||
|
return "B4000000";
|
||||||
|
else
|
||||||
|
return "";
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,41 +1,25 @@
|
||||||
#ifndef _BYTENUTES_H_
|
#ifndef _BYTENUTES_H_
|
||||||
#define _BYTENUTES_H_
|
#define _BYTENUTES_H_
|
||||||
|
|
||||||
#ifdef __MINGW32__
|
#include <ncurses.h>
|
||||||
# include <curses.h>
|
|
||||||
#else
|
|
||||||
# include <ncurses.h>
|
|
||||||
#endif
|
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include "serial.h"
|
#include "serial.h"
|
||||||
|
|
||||||
/* https://en.wikipedia.org/wiki/Control_character#How_control_characters_map_to_keyboards */
|
/* https://en.wikipedia.org/wiki/Control_character#How_control_characters_map_to_keyboards */
|
||||||
#define CTRL(c) ((c)&31)
|
#define CTRL(c) ((c)&31)
|
||||||
|
|
||||||
typedef struct bytenuts_state_struct {
|
|
||||||
char **history; /* commands from previous session */
|
|
||||||
int history_len;
|
|
||||||
char *buf; /* previous buffer */
|
|
||||||
size_t buf_len;
|
|
||||||
} bytenuts_state_t;
|
|
||||||
|
|
||||||
typedef struct bytenuts_config_struct {
|
typedef struct bytenuts_config_struct {
|
||||||
int colors; /* enable terminal colors, default 1 */
|
int colors; /* enable terminal colors, default 1 */
|
||||||
int echo; /* echo user input to the output window, default 0 */
|
int echo; /* echo user input to the output window, default 0 */
|
||||||
int no_crlf; /* only send LF line endings, not CRLF, default 0 */
|
int no_crlf; /* only send LF line endings, not CRLF, default 0 */
|
||||||
char escape; /* which character is used for escape sequence */
|
char escape; /* which character is used for escape sequence */
|
||||||
long baud; /* baud rate */
|
speed_t baud; /* baud rate */
|
||||||
char *config_path; /* config file path */
|
char *config_path; /* config file path */
|
||||||
char *log_path; /* path to the log file (if it exists) */
|
char *log_path; /* path to the log file (if it exists) */
|
||||||
char *serial_path; /* path to the target serial device */
|
char *serial_path; /* path to the target serial device */
|
||||||
uint32_t inter_cmd_to; /* inter command timeout in ms */
|
|
||||||
/* time format to be prepended to all log lines in the output file only,
|
|
||||||
* NULL for no time prepended */
|
|
||||||
char *time_fmt;
|
|
||||||
} bytenuts_config_t;
|
} bytenuts_config_t;
|
||||||
|
|
||||||
#define CONFIG_DEFAULT (bytenuts_config_t){ \
|
#define CONFIG_DEFAULT (bytenuts_config_t){ \
|
||||||
|
|
@ -43,20 +27,16 @@ typedef struct bytenuts_config_struct {
|
||||||
.echo = 0, \
|
.echo = 0, \
|
||||||
.no_crlf = 0, \
|
.no_crlf = 0, \
|
||||||
.escape = 'b', \
|
.escape = 'b', \
|
||||||
.baud = 115200, \
|
.baud = B115200, \
|
||||||
.config_path = NULL, \
|
.config_path = NULL, \
|
||||||
.log_path = NULL, \
|
.log_path = NULL, \
|
||||||
.serial_path = NULL, \
|
.serial_path = NULL, \
|
||||||
.inter_cmd_to = 10, \
|
|
||||||
.time_fmt = NULL, \
|
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct bytenuts_struct {
|
typedef struct bytenuts_struct {
|
||||||
serial_t serial_fd;
|
int serial_fd;
|
||||||
bytenuts_config_t config;
|
bytenuts_config_t config;
|
||||||
int config_overrides[6];
|
int config_overrides[4];
|
||||||
int resume;
|
|
||||||
bytenuts_state_t state;
|
|
||||||
WINDOW *status_win;
|
WINDOW *status_win;
|
||||||
WINDOW *out_win;
|
WINDOW *out_win;
|
||||||
WINDOW *in_win;
|
WINDOW *in_win;
|
||||||
|
|
|
||||||
257
src/cheerios.c
257
src/cheerios.c
|
|
@ -1,8 +1,5 @@
|
||||||
#ifdef __MINGW32__
|
#include <ncurses.h>
|
||||||
# include <curses.h>
|
#include <poll.h>
|
||||||
#else
|
|
||||||
# include <ncurses.h>
|
|
||||||
#endif
|
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <sched.h>
|
#include <sched.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
@ -20,15 +17,13 @@ static cheerios_t cheerios;
|
||||||
static void *cheerios_thread(void *arg);
|
static void *cheerios_thread(void *arg);
|
||||||
static int insert_buf(line_buffer_t *lines, const char *buf, size_t len);
|
static int insert_buf(line_buffer_t *lines, const char *buf, size_t len);
|
||||||
static int write_lines(line_buffer_t *lines);
|
static int write_lines(line_buffer_t *lines);
|
||||||
static int handle_color(line_buffer_t *lines, int line_idx, int *pos, int apply);
|
static int handle_color(line_buffer_t *lines, int line_idx, int *pos);
|
||||||
short curs_color(int fg);
|
short curs_color(int fg);
|
||||||
static int newline(line_buffer_t *lines);
|
static int newline(line_buffer_t *lines);
|
||||||
|
|
||||||
int
|
int
|
||||||
cheerios_start(bytenuts_t *bytenuts)
|
cheerios_start(bytenuts_t *bytenuts)
|
||||||
{
|
{
|
||||||
char *home = getenv("HOME");
|
|
||||||
|
|
||||||
memset(&cheerios, 0, sizeof(cheerios_t));
|
memset(&cheerios, 0, sizeof(cheerios_t));
|
||||||
|
|
||||||
cheerios.output = bytenuts->out_win;
|
cheerios.output = bytenuts->out_win;
|
||||||
|
|
@ -45,26 +40,6 @@ cheerios_start(bytenuts_t *bytenuts)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (home) {
|
|
||||||
pid_t pid;
|
|
||||||
int name_len;
|
|
||||||
|
|
||||||
/* ensure that a process has a unique log */
|
|
||||||
pid = getpid();
|
|
||||||
name_len = snprintf(
|
|
||||||
NULL, 0,
|
|
||||||
"%s/.config/bytenuts/outbuf.%lld.log",
|
|
||||||
home, (long long)pid
|
|
||||||
);
|
|
||||||
cheerios.backup_filename = calloc(1, name_len + 1);
|
|
||||||
snprintf(
|
|
||||||
cheerios.backup_filename, name_len + 1,
|
|
||||||
"%s/.config/bytenuts/outbuf.%lld.log",
|
|
||||||
home, (long long)pid
|
|
||||||
);
|
|
||||||
cheerios.backup = fopen(cheerios.backup_filename, "w");
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_cond_init(&cheerios.cond, NULL);
|
pthread_cond_init(&cheerios.cond, NULL);
|
||||||
pthread_mutex_init(&cheerios.lock, NULL);
|
pthread_mutex_init(&cheerios.lock, NULL);
|
||||||
cheerios.running = 1;
|
cheerios.running = 1;
|
||||||
|
|
@ -154,28 +129,31 @@ cheerios_stop()
|
||||||
{
|
{
|
||||||
cheerios.running = 0;
|
cheerios.running = 0;
|
||||||
pthread_join(cheerios.thr, NULL);
|
pthread_join(cheerios.thr, NULL);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
cheerios_input(const char *buf, size_t len)
|
cheerios_input(const char *line)
|
||||||
{
|
{
|
||||||
size_t len_left = len;
|
size_t line_len = strlen(line);
|
||||||
size_t p = 0;
|
|
||||||
|
|
||||||
pthread_mutex_lock(&cheerios.lock);
|
pthread_mutex_lock(&cheerios.lock);
|
||||||
while (len_left > 0) {
|
|
||||||
ssize_t tmp = serial_write(cheerios.ser_fd, &buf[p], len_left);
|
|
||||||
|
|
||||||
if (tmp < 0) {
|
if (cheerios.config->echo) {
|
||||||
pthread_mutex_unlock(&cheerios.lock);
|
char *line_parsed = calloc(1, line_len + 5 + 1);
|
||||||
return -1;
|
sprintf(line_parsed, ">> %s\r\n", line);
|
||||||
|
insert_buf(&cheerios.lines, line_parsed, strlen(line_parsed));
|
||||||
|
free(line_parsed);
|
||||||
}
|
}
|
||||||
|
|
||||||
len_left -= tmp;
|
write(cheerios.ser_fd, line, strlen(line));
|
||||||
}
|
if (cheerios.config->no_crlf)
|
||||||
pthread_mutex_unlock(&cheerios.lock);
|
write(cheerios.ser_fd, "\n", 1);
|
||||||
|
else
|
||||||
|
write(cheerios.ser_fd, "\r\n", 2);
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&cheerios.lock);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -197,29 +175,6 @@ cheerios_info(const char *line)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
|
||||||
cheerios_print(const char *fmt, ...)
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
char *buf;
|
|
||||||
size_t len;
|
|
||||||
|
|
||||||
va_start(ap, fmt);
|
|
||||||
len = vsnprintf(NULL, 0, fmt, ap);
|
|
||||||
va_end(ap);
|
|
||||||
|
|
||||||
buf = calloc(1, len+1);
|
|
||||||
|
|
||||||
va_start(ap, fmt);
|
|
||||||
vsprintf(buf, fmt, ap);
|
|
||||||
va_end(ap);
|
|
||||||
|
|
||||||
cheerios_insert(buf, len);
|
|
||||||
free(buf);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
int
|
||||||
cheerios_insert(const char *buf, size_t len)
|
cheerios_insert(const char *buf, size_t len)
|
||||||
{
|
{
|
||||||
|
|
@ -232,18 +187,17 @@ cheerios_insert(const char *buf, size_t len)
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
__xmodem_callback(size_t sent, size_t total, int ack_fails)
|
__xmodem_callback(size_t sent, size_t total)
|
||||||
{
|
{
|
||||||
char status_str[1024];
|
char status_str[1024];
|
||||||
|
|
||||||
|
|
||||||
snprintf(
|
snprintf(
|
||||||
status_str, sizeof(status_str),
|
status_str, sizeof(status_str),
|
||||||
"\r %.02f/%.02fKB (%.01f%%, %d ACK fails)",
|
"\r %.02f/%.02fKB (%.01f%%)",
|
||||||
(sent / 1024.0),
|
(sent / 1024.0),
|
||||||
(total / 1024.0),
|
(total / 1024.0),
|
||||||
(sent * 100.0 / total),
|
(sent * 100.0 / total)
|
||||||
ack_fails
|
|
||||||
);
|
);
|
||||||
|
|
||||||
cheerios_insert(status_str, strlen(status_str));
|
cheerios_insert(status_str, strlen(status_str));
|
||||||
|
|
@ -307,7 +261,6 @@ cheerios_getmaxy()
|
||||||
{
|
{
|
||||||
int y, x;
|
int y, x;
|
||||||
getmaxyx(cheerios.output, y, x);
|
getmaxyx(cheerios.output, y, x);
|
||||||
(void)x;
|
|
||||||
return y;
|
return y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -316,7 +269,6 @@ cheerios_getmaxx()
|
||||||
{
|
{
|
||||||
int y, x;
|
int y, x;
|
||||||
getmaxyx(cheerios.output, y, x);
|
getmaxyx(cheerios.output, y, x);
|
||||||
(void)y;
|
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -339,6 +291,7 @@ cheerios_thread(void *arg)
|
||||||
{
|
{
|
||||||
char buf[1024];
|
char buf[1024];
|
||||||
ssize_t read_ret = 0;
|
ssize_t read_ret = 0;
|
||||||
|
struct pollfd fds;
|
||||||
|
|
||||||
while (cheerios.running) {
|
while (cheerios.running) {
|
||||||
pthread_mutex_lock(&cheerios.lock);
|
pthread_mutex_lock(&cheerios.lock);
|
||||||
|
|
@ -347,7 +300,17 @@ cheerios_thread(void *arg)
|
||||||
pthread_cond_wait(&cheerios.cond, &cheerios.lock);
|
pthread_cond_wait(&cheerios.cond, &cheerios.lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
read_ret = serial_read(cheerios.ser_fd, buf, sizeof(buf));
|
fds.fd = cheerios.ser_fd;
|
||||||
|
fds.events = POLLIN;
|
||||||
|
fds.revents = 0;
|
||||||
|
|
||||||
|
if (poll(&fds, 1, 1) == 0) {
|
||||||
|
pthread_mutex_unlock(&cheerios.lock);
|
||||||
|
nanosleep(&(struct timespec){ 0, 100000 }, NULL);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
read_ret = read(cheerios.ser_fd, buf, sizeof(buf));
|
||||||
if (read_ret > 0) {
|
if (read_ret > 0) {
|
||||||
insert_buf(&cheerios.lines, buf, read_ret);
|
insert_buf(&cheerios.lines, buf, read_ret);
|
||||||
}
|
}
|
||||||
|
|
@ -358,33 +321,6 @@ cheerios_thread(void *arg)
|
||||||
|
|
||||||
if (cheerios.log)
|
if (cheerios.log)
|
||||||
fclose(cheerios.log);
|
fclose(cheerios.log);
|
||||||
|
|
||||||
if (cheerios.backup) {
|
|
||||||
char *out_filename;
|
|
||||||
int out_filename_len;
|
|
||||||
char *home = getenv("HOME");
|
|
||||||
|
|
||||||
/* do not chdir as other threads can still be running */
|
|
||||||
out_filename_len = snprintf(
|
|
||||||
NULL, 0,
|
|
||||||
"%s/.config/bytenuts/outbuf.log",
|
|
||||||
home
|
|
||||||
);
|
|
||||||
out_filename = calloc(1, out_filename_len + 1);
|
|
||||||
snprintf(
|
|
||||||
out_filename, out_filename_len + 1,
|
|
||||||
"%s/.config/bytenuts/outbuf.log",
|
|
||||||
home
|
|
||||||
);
|
|
||||||
|
|
||||||
/* move this processes log to the path that can be loaded on resumption */
|
|
||||||
fclose(cheerios.backup);
|
|
||||||
rename(cheerios.backup_filename, out_filename);
|
|
||||||
|
|
||||||
free(out_filename);
|
|
||||||
free(cheerios.backup_filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_exit(NULL);
|
pthread_exit(NULL);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
@ -396,12 +332,8 @@ insert_buf(line_buffer_t *lines, const char *buf, size_t len)
|
||||||
newline(lines);
|
newline(lines);
|
||||||
|
|
||||||
for (size_t i = 0; i < len; i++) {
|
for (size_t i = 0; i < len; i++) {
|
||||||
if (cheerios.mode == CHEERIOS_MODE_NORMAL) {
|
if (cheerios.log && cheerios.mode == CHEERIOS_MODE_NORMAL)
|
||||||
if (cheerios.log)
|
|
||||||
fwrite(&buf[i], 1, 1, cheerios.log);
|
fwrite(&buf[i], 1, 1, cheerios.log);
|
||||||
if (cheerios.backup)
|
|
||||||
fwrite(&buf[i], 1, 1, cheerios.backup);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* line feed starts a new row */
|
/* line feed starts a new row */
|
||||||
if (buf[i] == '\n') {
|
if (buf[i] == '\n') {
|
||||||
|
|
@ -435,96 +367,32 @@ write_lines(line_buffer_t *lines)
|
||||||
int row = lines->bot;
|
int row = lines->bot;
|
||||||
int rows_printed = 0;
|
int rows_printed = 0;
|
||||||
int window_height = getmaxy(cheerios.output);
|
int window_height = getmaxy(cheerios.output);
|
||||||
int window_width = getmaxx(cheerios.output);
|
|
||||||
line_buffer_t lines_wrapped = { 0 };
|
|
||||||
|
|
||||||
if (row < 0)
|
if (row < 0)
|
||||||
row = lines->n_lines - 1;
|
row = lines->n_lines - 1;
|
||||||
|
|
||||||
while (row >= 0 && rows_printed < window_height) {
|
|
||||||
/* we can print the whole line */
|
|
||||||
if (lines->line_lens[row] <= window_width) {
|
|
||||||
lines_wrapped.n_lines++;
|
|
||||||
lines_wrapped.lines = realloc(
|
|
||||||
lines_wrapped.lines,
|
|
||||||
sizeof(uint8_t *) * lines_wrapped.n_lines
|
|
||||||
);
|
|
||||||
lines_wrapped.line_lens = realloc(
|
|
||||||
lines_wrapped.line_lens,
|
|
||||||
sizeof(int) * lines_wrapped.n_lines
|
|
||||||
);
|
|
||||||
|
|
||||||
lines_wrapped.lines[lines_wrapped.n_lines - 1] = lines->lines[row];
|
|
||||||
lines_wrapped.line_lens[lines_wrapped.n_lines - 1] = lines->line_lens[row];
|
|
||||||
|
|
||||||
rows_printed++;
|
|
||||||
}
|
|
||||||
/* we have to split the line */
|
|
||||||
else {
|
|
||||||
int n_split = lines->line_lens[row] / window_width;
|
|
||||||
|
|
||||||
if ((lines->line_lens[row] % window_width) == 0)
|
|
||||||
n_split--;
|
|
||||||
|
|
||||||
lines_wrapped.n_lines += n_split + 1;
|
|
||||||
lines_wrapped.lines = realloc(
|
|
||||||
lines_wrapped.lines,
|
|
||||||
sizeof(uint8_t *) * lines_wrapped.n_lines
|
|
||||||
);
|
|
||||||
lines_wrapped.line_lens = realloc(
|
|
||||||
lines_wrapped.line_lens,
|
|
||||||
sizeof(int) * lines_wrapped.n_lines
|
|
||||||
);
|
|
||||||
|
|
||||||
/* fill out lines that fill the width */
|
|
||||||
for (int i = 0; i < n_split; i++) {
|
|
||||||
lines_wrapped.lines[lines_wrapped.n_lines - 1 - i] =
|
|
||||||
&lines->lines[row][i * window_width];
|
|
||||||
lines_wrapped.line_lens[lines_wrapped.n_lines - 1 - i] =
|
|
||||||
window_width;
|
|
||||||
}
|
|
||||||
/* left over */
|
|
||||||
lines_wrapped.lines[lines_wrapped.n_lines - 1 - n_split] =
|
|
||||||
&lines->lines[row][n_split * window_width];
|
|
||||||
|
|
||||||
if ((lines->line_lens[row] % window_width) == 0) {
|
|
||||||
lines_wrapped.line_lens[lines_wrapped.n_lines - 1 - n_split] = window_width;
|
|
||||||
} else {
|
|
||||||
lines_wrapped.line_lens[lines_wrapped.n_lines - 1 - n_split] =
|
|
||||||
lines->line_lens[row] % window_width;
|
|
||||||
}
|
|
||||||
|
|
||||||
rows_printed += n_split + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
row--;
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_mutex_lock(cheerios.term_lock);
|
pthread_mutex_lock(cheerios.term_lock);
|
||||||
|
|
||||||
curs_set(0);
|
curs_set(0);
|
||||||
werase(cheerios.output);
|
werase(cheerios.output);
|
||||||
|
|
||||||
/* the copying process reverses the order of the rows... */
|
while (row >= 0 && rows_printed < window_height) {
|
||||||
row = 0;
|
|
||||||
rows_printed = 0;
|
|
||||||
while (row < lines_wrapped.n_lines && rows_printed < window_height) {
|
|
||||||
wmove(cheerios.output, window_height - rows_printed - 1, 0);
|
wmove(cheerios.output, window_height - rows_printed - 1, 0);
|
||||||
|
|
||||||
for (int i = 0; i < lines_wrapped.line_lens[row]; i++) {
|
for (int i = 0; i < lines->line_lens[row]; i++) {
|
||||||
handle_color(&lines_wrapped, row, &i, 1);
|
handle_color(lines, row, &i);
|
||||||
|
|
||||||
if (i >= getmaxx(cheerios.output)) {
|
if (i >= getmaxx(cheerios.output)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i < lines_wrapped.line_lens[row]) {
|
if (i < lines->line_lens[row]) {
|
||||||
waddch(cheerios.output, lines_wrapped.lines[row][i]);
|
waddch(cheerios.output, lines->lines[row][i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rows_printed++;
|
rows_printed++;
|
||||||
row++;
|
row--;
|
||||||
}
|
}
|
||||||
|
|
||||||
curs_set(1);
|
curs_set(1);
|
||||||
|
|
@ -538,16 +406,11 @@ write_lines(line_buffer_t *lines)
|
||||||
bytenuts_set_status(STATUS_CHEERIOS, "locked");
|
bytenuts_set_status(STATUS_CHEERIOS, "locked");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lines_wrapped.lines)
|
|
||||||
free(lines_wrapped.lines);
|
|
||||||
if (lines_wrapped.line_lens)
|
|
||||||
free(lines_wrapped.line_lens);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
handle_color(line_buffer_t *lines, int line_idx, int *pos, int apply)
|
handle_color(line_buffer_t *lines, int line_idx, int *pos)
|
||||||
{
|
{
|
||||||
short fg, bg = -1;
|
short fg, bg = -1;
|
||||||
int enable = -1;
|
int enable = -1;
|
||||||
|
|
@ -559,17 +422,8 @@ handle_color(line_buffer_t *lines, int line_idx, int *pos, int apply)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
while (p < line_len) {
|
while (p < line_len) {
|
||||||
int fit_col = (p + 7) <= line_len;
|
|
||||||
int fit_en = (p + 4) <= line_len;
|
|
||||||
|
|
||||||
/* fg or bg color */
|
/* fg or bg color */
|
||||||
if (
|
if (!memcmp(&line[p], "\e[38;5;", 7) || !memcmp(&line[p], "\e[48;5;", 7)) {
|
||||||
fit_col &&
|
|
||||||
(
|
|
||||||
!memcmp(&line[p], "\e[38;5;", 7) ||
|
|
||||||
!memcmp(&line[p], "\e[48;5;", 7)
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
char col_str[4] = { 0 };
|
char col_str[4] = { 0 };
|
||||||
short *col = line[p + 2] == '3' ? &fg : &bg;
|
short *col = line[p + 2] == '3' ? &fg : &bg;
|
||||||
|
|
||||||
|
|
@ -597,13 +451,13 @@ handle_color(line_buffer_t *lines, int line_idx, int *pos, int apply)
|
||||||
p++;
|
p++;
|
||||||
}
|
}
|
||||||
/* enable our color */
|
/* enable our color */
|
||||||
else if (fit_en && !memcmp(&line[p], "\e[1m", 4)) {
|
else if (!memcmp(&line[p], "\e[1m", 4)) {
|
||||||
p += 4;
|
p += 4;
|
||||||
enable = 1;
|
enable = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
/* disable our color */
|
/* disable our color */
|
||||||
else if (fit_en && !memcmp(&line[p], "\e[0m", 4)) {
|
else if (!memcmp(&line[p], "\e[0m", 4)) {
|
||||||
p += 4;
|
p += 4;
|
||||||
enable = 0;
|
enable = 0;
|
||||||
break;
|
break;
|
||||||
|
|
@ -614,11 +468,6 @@ handle_color(line_buffer_t *lines, int line_idx, int *pos, int apply)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!apply) {
|
|
||||||
*pos = p;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (enable == 0) {
|
if (enable == 0) {
|
||||||
for (int i = 0; i < NCOLOR_PAIRS; i++) {
|
for (int i = 0; i < NCOLOR_PAIRS; i++) {
|
||||||
if (lines->enabled_pairs[i]) {
|
if (lines->enabled_pairs[i]) {
|
||||||
|
|
@ -653,6 +502,9 @@ handle_color(line_buffer_t *lines, int line_idx, int *pos, int apply)
|
||||||
lines->enabled_pairs[pair_pos] = 1;
|
lines->enabled_pairs[pair_pos] = 1;
|
||||||
wattron(cheerios.output, COLOR_PAIR(pair_pos + 1));
|
wattron(cheerios.output, COLOR_PAIR(pair_pos + 1));
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
*pos = p;
|
*pos = p;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -667,22 +519,5 @@ newline(line_buffer_t *lines)
|
||||||
lines->line_lens = realloc(lines->line_lens, sizeof(int) * lines->n_lines);
|
lines->line_lens = realloc(lines->line_lens, sizeof(int) * lines->n_lines);
|
||||||
lines->line_lens[lines->n_lines - 1] = 0;
|
lines->line_lens[lines->n_lines - 1] = 0;
|
||||||
lines->pos = 0;
|
lines->pos = 0;
|
||||||
|
|
||||||
if (cheerios.config->time_fmt && (cheerios.log || cheerios.backup)) {
|
|
||||||
char tstr[128];
|
|
||||||
time_t now;
|
|
||||||
struct tm *tinfo;
|
|
||||||
size_t tstr_len;
|
|
||||||
|
|
||||||
time(&now);
|
|
||||||
tinfo = localtime(&now);
|
|
||||||
tstr_len = strftime(tstr, 128, cheerios.config->time_fmt, tinfo);
|
|
||||||
|
|
||||||
if (cheerios.log)
|
|
||||||
fwrite(tstr, 1, tstr_len, cheerios.log);
|
|
||||||
if (cheerios.backup)
|
|
||||||
fwrite(tstr, 1, tstr_len, cheerios.backup);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,8 @@
|
||||||
#ifndef _CHEERIOS_H_
|
#ifndef _CHEERIOS_H_
|
||||||
#define _CHEERIOS_H_
|
#define _CHEERIOS_H_
|
||||||
|
|
||||||
#ifdef __MINGW32__
|
#include <ncurses.h>
|
||||||
# include <curses.h>
|
|
||||||
#else
|
|
||||||
# include <ncurses.h>
|
|
||||||
#endif
|
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "bytenuts.h"
|
#include "bytenuts.h"
|
||||||
|
|
@ -37,11 +32,9 @@ typedef struct cheerios_struct {
|
||||||
pthread_t thr;
|
pthread_t thr;
|
||||||
WINDOW *output;
|
WINDOW *output;
|
||||||
pthread_mutex_t *term_lock;
|
pthread_mutex_t *term_lock;
|
||||||
serial_t ser_fd;
|
int ser_fd;
|
||||||
line_buffer_t lines;
|
line_buffer_t lines;
|
||||||
FILE *log; /* log file which was opened with -l */
|
FILE *log;
|
||||||
char *backup_filename; /* realpath to the backup outbuf.pid.log */
|
|
||||||
FILE *backup; /* backup log file object */
|
|
||||||
bytenuts_config_t *config;
|
bytenuts_config_t *config;
|
||||||
volatile int mode;
|
volatile int mode;
|
||||||
pthread_cond_t cond;
|
pthread_cond_t cond;
|
||||||
|
|
@ -67,17 +60,14 @@ int cheerios_gofwd(int lines);
|
||||||
/* stop the thread and release memory */
|
/* stop the thread and release memory */
|
||||||
int cheerios_stop();
|
int cheerios_stop();
|
||||||
|
|
||||||
/* send over a line of user input to the output */
|
/* send over a line of user input to the output
|
||||||
int cheerios_input(const char *buf, size_t len);
|
* do not terminate with a newline! */
|
||||||
|
int cheerios_input(const char *line);
|
||||||
|
|
||||||
/* output a line only to the terminal for info/prompt purposes
|
/* output a line only to the terminal for info/prompt purposes
|
||||||
* do not terminate with a newline! */
|
* do not terminate with a newline! */
|
||||||
int cheerios_info(const char *line);
|
int cheerios_info(const char *line);
|
||||||
|
|
||||||
/* Like cheerios_insert except using printf formatting */
|
|
||||||
__attribute__((format(printf, 1, 2)))
|
|
||||||
int cheerios_print(const char *fmt, ...);
|
|
||||||
|
|
||||||
/* directly insert a buffer to the output window */
|
/* directly insert a buffer to the output window */
|
||||||
int cheerios_insert(const char *buf, size_t len);
|
int cheerios_insert(const char *buf, size_t len);
|
||||||
|
|
||||||
|
|
|
||||||
465
src/ingest.c
465
src/ingest.c
|
|
@ -1,9 +1,5 @@
|
||||||
#include <libgen.h>
|
#include <libgen.h>
|
||||||
#ifdef __MINGW32__
|
#include <ncurses.h>
|
||||||
# include <curses.h>
|
|
||||||
#else
|
|
||||||
# include <ncurses.h>
|
|
||||||
#endif
|
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <sched.h>
|
#include <sched.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
@ -12,12 +8,10 @@
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <errno.h>
|
|
||||||
|
|
||||||
#include "bytenuts.h"
|
#include "bytenuts.h"
|
||||||
#include "cheerios.h"
|
#include "cheerios.h"
|
||||||
#include "ingest.h"
|
#include "ingest.h"
|
||||||
#include "timer_math.h"
|
|
||||||
|
|
||||||
static void *ingest_thread(void *arg);
|
static void *ingest_thread(void *arg);
|
||||||
|
|
||||||
|
|
@ -25,20 +19,12 @@ static ingest_t ingest;
|
||||||
|
|
||||||
static int mode_normal(int ch);
|
static int mode_normal(int ch);
|
||||||
static int mode_xmodem(int block_sz);
|
static int mode_xmodem(int block_sz);
|
||||||
static int mode_hex(int ch);
|
|
||||||
static int handle_functions(int ch);
|
static int handle_functions(int ch);
|
||||||
static int print_stats(void);
|
static int inbuf_load();
|
||||||
static int auto_complete(void);
|
static int print_stats();
|
||||||
static int read_cmd_page(const char *home_dir, int idx);
|
static int auto_complete();
|
||||||
static void update_cmd_pg_status(void);
|
static int read_cmd_page(int idx);
|
||||||
static void inter_command_wait(void);
|
static void update_cmd_pg_status();
|
||||||
|
|
||||||
#ifndef KEY_CUP
|
|
||||||
# define KEY_CUP (566)
|
|
||||||
#endif
|
|
||||||
#ifndef KEY_CDOWN
|
|
||||||
# define KEY_CDOWN (525)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int
|
int
|
||||||
ingest_start(bytenuts_t *bytenuts)
|
ingest_start(bytenuts_t *bytenuts)
|
||||||
|
|
@ -51,39 +37,20 @@ ingest_start(bytenuts_t *bytenuts)
|
||||||
ingest.term_lock = &bytenuts->term_lock;
|
ingest.term_lock = &bytenuts->term_lock;
|
||||||
ingest.config = &bytenuts->config;
|
ingest.config = &bytenuts->config;
|
||||||
ingest.cmd_pg_cur = -1;
|
ingest.cmd_pg_cur = -1;
|
||||||
ingest.xmodem_hist = bstr_history_create();
|
|
||||||
|
|
||||||
/* ensure character sequences work in all terminals */
|
|
||||||
define_key("\033[1;5A", KEY_CUP);
|
|
||||||
define_key("\033[1;5B", KEY_CDOWN);
|
|
||||||
define_key("\033[1;2H", KEY_SHOME);
|
|
||||||
define_key("\033[1;2F", KEY_SEND);
|
|
||||||
|
|
||||||
HOME = getenv("HOME");
|
HOME = getenv("HOME");
|
||||||
if (HOME) {
|
if (HOME) {
|
||||||
|
char *cwd = getcwd(NULL, 0);
|
||||||
int idx = 0;
|
int idx = 0;
|
||||||
int name_len;
|
|
||||||
pid_t pid;
|
|
||||||
|
|
||||||
while (!read_cmd_page(HOME, idx)) {
|
chdir(HOME);
|
||||||
|
|
||||||
|
while (!read_cmd_page(idx)) {
|
||||||
idx++;
|
idx++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ensure that a process has a unique log */
|
chdir(cwd);
|
||||||
pid = getpid();
|
free(cwd);
|
||||||
name_len = snprintf(
|
|
||||||
NULL, 0,
|
|
||||||
"%s/.config/bytenuts/inbuf.%lld.log",
|
|
||||||
HOME, (long long)pid
|
|
||||||
);
|
|
||||||
ingest.history_filename = calloc(1, name_len + 1);
|
|
||||||
snprintf(
|
|
||||||
ingest.history_filename, name_len + 1,
|
|
||||||
"%s/.config/bytenuts/inbuf.%lld.log",
|
|
||||||
HOME, (long long)pid
|
|
||||||
);
|
|
||||||
|
|
||||||
ingest.history_fd = fopen(ingest.history_filename, "w");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ingest.cmd_pgs_n > 0) {
|
if (ingest.cmd_pgs_n > 0) {
|
||||||
|
|
@ -91,10 +58,6 @@ ingest_start(bytenuts_t *bytenuts)
|
||||||
}
|
}
|
||||||
update_cmd_pg_status();
|
update_cmd_pg_status();
|
||||||
|
|
||||||
clock_gettime(CLOCK_REALTIME, &ingest.cmd_ts);
|
|
||||||
/* ensure first command is not delayed */
|
|
||||||
timer_sub_ms(&ingest.cmd_ts, ingest.config->inter_cmd_to);
|
|
||||||
|
|
||||||
ingest.running = 1;
|
ingest.running = 1;
|
||||||
if (pthread_create(&ingest.thr, NULL, ingest_thread, NULL)) {
|
if (pthread_create(&ingest.thr, NULL, ingest_thread, NULL)) {
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -111,126 +74,26 @@ ingest_stop()
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
|
||||||
ingest_refresh()
|
|
||||||
{
|
|
||||||
static int start = 0;
|
|
||||||
int startx = 0, starty = 0;
|
|
||||||
int cx = -1, cy = -1;
|
|
||||||
int win_len = getmaxx(ingest.input) + 1;
|
|
||||||
|
|
||||||
pthread_mutex_lock(ingest.term_lock);
|
|
||||||
|
|
||||||
werase(ingest.input);
|
|
||||||
wmove(ingest.input, 0, 0);
|
|
||||||
if (ingest.prepend) {
|
|
||||||
wprintw(ingest.input, "%s", ingest.prepend);
|
|
||||||
getyx(ingest.input, starty, startx);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (start > ingest.inpos) {
|
|
||||||
start = ingest.inpos;
|
|
||||||
}
|
|
||||||
/* this is not perfect as some characters are wider than one space, but it
|
|
||||||
* makes the algorithm for printing less redundant */
|
|
||||||
else if (ingest.inpos > (start + win_len)) {
|
|
||||||
start = ingest.inpos - win_len;
|
|
||||||
}
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
int hit_cursor = 0;
|
|
||||||
while (1) {
|
|
||||||
int p = i + start;
|
|
||||||
int iy, ix;
|
|
||||||
i++;
|
|
||||||
|
|
||||||
getyx(ingest.input, iy, ix);
|
|
||||||
|
|
||||||
/* print the cursor at this position */
|
|
||||||
if (p == ingest.inpos) {
|
|
||||||
cy = iy;
|
|
||||||
cx = ix;
|
|
||||||
hit_cursor = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* nothing left to write... */
|
|
||||||
if (p >= ingest.inlen)
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* we want to leave a blank character for printing the cursor at the end of the line */
|
|
||||||
if (ix >= (win_len-2)) {
|
|
||||||
/* we printed the cursor, so we can break */
|
|
||||||
if (hit_cursor)
|
|
||||||
break;
|
|
||||||
/* otherwise, we didn't print the cursor, so start over */
|
|
||||||
i = 0;
|
|
||||||
start++;
|
|
||||||
wmove(ingest.input, starty, startx);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
waddch(ingest.input, ingest.inbuf[p]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cx < 0) {
|
|
||||||
cx = ingest.inlen < win_len ? ingest.inlen : win_len - 1;
|
|
||||||
cy = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
wmove(ingest.input, cy, cx);
|
|
||||||
wrefresh(ingest.input);
|
|
||||||
|
|
||||||
pthread_mutex_unlock(ingest.term_lock);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
ingest_set_history(char **history, int history_len)
|
|
||||||
{
|
|
||||||
if (ingest.history) {
|
|
||||||
for (int i = 0; ingest.history[i] != NULL; i++) {
|
|
||||||
free(ingest.history[i]);
|
|
||||||
}
|
|
||||||
free(ingest.history);
|
|
||||||
}
|
|
||||||
|
|
||||||
ingest.history = calloc(1, sizeof(char *) * (history_len+1));
|
|
||||||
for (int i = 0; i < history_len; i++) {
|
|
||||||
size_t item_len = strlen(history[i]);
|
|
||||||
ingest.history[i] = strdup(history[i]);
|
|
||||||
|
|
||||||
while (
|
|
||||||
ingest.history[i][item_len-1] == '\n' ||
|
|
||||||
ingest.history[i][item_len-1] == '\r'
|
|
||||||
) {
|
|
||||||
item_len--;
|
|
||||||
ingest.history[i][item_len] = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ingest.history_fd) {
|
|
||||||
fwrite(ingest.history[i], 1, item_len, ingest.history_fd);
|
|
||||||
fwrite("\n", 1, 1, ingest.history_fd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ingest.history_len = history_len;
|
|
||||||
ingest.history_pos = ingest.history_len;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *
|
static void *
|
||||||
ingest_thread(void *arg)
|
ingest_thread(void *arg)
|
||||||
{
|
{
|
||||||
|
int esc_flag;
|
||||||
int ch;
|
int ch;
|
||||||
|
|
||||||
memset(ingest.inbuf, 0, sizeof(ingest.inbuf));
|
memset(ingest.inbuf, 0, sizeof(ingest.inbuf));
|
||||||
ingest.inpos = 0;
|
ingest.inpos = 0;
|
||||||
|
|
||||||
|
cheerios_insert(
|
||||||
|
"Welcome to Bytenuts\r\n"
|
||||||
|
"To quit press ctrl-b q\r\n"
|
||||||
|
"To see all commands, press ctrl-b h\r\n",
|
||||||
|
82
|
||||||
|
);
|
||||||
bytenuts_set_status(STATUS_INGEST, "normal");
|
bytenuts_set_status(STATUS_INGEST, "normal");
|
||||||
|
|
||||||
while (ingest.running) {
|
while (ingest.running) {
|
||||||
|
esc_flag = 0;
|
||||||
|
|
||||||
pthread_mutex_lock(ingest.term_lock);
|
pthread_mutex_lock(ingest.term_lock);
|
||||||
ch = wgetch(ingest.input);
|
ch = wgetch(ingest.input);
|
||||||
pthread_mutex_unlock(ingest.term_lock);
|
pthread_mutex_unlock(ingest.term_lock);
|
||||||
|
|
@ -293,7 +156,7 @@ ingest_thread(void *arg)
|
||||||
memcpy(ingest.inbuf, toload, toload_len);
|
memcpy(ingest.inbuf, toload, toload_len);
|
||||||
ingest.inpos = toload_len;
|
ingest.inpos = toload_len;
|
||||||
ingest.inlen = toload_len;
|
ingest.inlen = toload_len;
|
||||||
ingest_refresh();
|
inbuf_load();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'c':
|
case 'c':
|
||||||
|
|
@ -387,29 +250,18 @@ ingest_thread(void *arg)
|
||||||
ingest.mode = INGEST_MODE_XMODEM1K;
|
ingest.mode = INGEST_MODE_XMODEM1K;
|
||||||
bytenuts_set_status(STATUS_INGEST, "xmodem1k");
|
bytenuts_set_status(STATUS_INGEST, "xmodem1k");
|
||||||
break;
|
break;
|
||||||
case 'H':
|
|
||||||
if (ingest.mode == INGEST_MODE_NORMAL) {
|
|
||||||
ingest.mode = INGEST_MODE_HEX;
|
|
||||||
bytenuts_set_status(STATUS_INGEST, "hex");
|
|
||||||
} else {
|
|
||||||
ingest.mode = INGEST_MODE_NORMAL;
|
|
||||||
bytenuts_set_status(STATUS_INGEST, "normal");
|
|
||||||
}
|
|
||||||
should_continue = 1;
|
|
||||||
break;
|
|
||||||
case 'h':
|
case 'h':
|
||||||
cheerios_print(
|
cheerios_insert(
|
||||||
"Commands (lead with ctrl-%c):\r\n"
|
"Commands (lead with ctrl-b):\r\n"
|
||||||
" c: print available quick commands\r\n"
|
" c: print available quick commands\r\n"
|
||||||
" 0-9: load the given quick command (0 is 10)\r\n"
|
" 0-9: load the given quick command (0 is 10)\r\n"
|
||||||
" p: select a different quick commands page\r\n"
|
" p: select a different quick commands page\r\n"
|
||||||
" i: view info/stats\r\n"
|
" i: view info/stats\r\n"
|
||||||
" x: start XModem upload with 128B payloads\r\n"
|
" x: start XModem upload with 128B payloads\r\n"
|
||||||
" X: start XModem upload with 1024B payloads\r\n"
|
" X: start XModem upload with 1024B payloads\r\n"
|
||||||
" H: enter/exit hex buffer mode\r\n"
|
|
||||||
" h: view this help\r\n"
|
" h: view this help\r\n"
|
||||||
" q: quit Bytenuts\r\n",
|
" q: quit Bytenuts\r\n",
|
||||||
ingest.config->escape
|
313
|
||||||
);
|
);
|
||||||
bytenuts_set_status(STATUS_INGEST, "normal");
|
bytenuts_set_status(STATUS_INGEST, "normal");
|
||||||
should_continue = 1;
|
should_continue = 1;
|
||||||
|
|
@ -437,9 +289,6 @@ ingest_thread(void *arg)
|
||||||
case INGEST_MODE_XMODEM1K:
|
case INGEST_MODE_XMODEM1K:
|
||||||
mode_xmodem(1024);
|
mode_xmodem(1024);
|
||||||
break;
|
break;
|
||||||
case INGEST_MODE_HEX:
|
|
||||||
mode_hex(ch);
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -447,34 +296,6 @@ ingest_thread(void *arg)
|
||||||
sched_yield();
|
sched_yield();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ingest.history_fd) {
|
|
||||||
char *in_filename;
|
|
||||||
int in_filename_len;
|
|
||||||
char *home = getenv("HOME");
|
|
||||||
|
|
||||||
/* do not chdir as other threads can still be running */
|
|
||||||
in_filename_len = snprintf(
|
|
||||||
NULL, 0,
|
|
||||||
"%s/.config/bytenuts/inbuf.log",
|
|
||||||
home
|
|
||||||
);
|
|
||||||
in_filename = calloc(1, in_filename_len + 1);
|
|
||||||
snprintf(
|
|
||||||
in_filename, in_filename_len + 1,
|
|
||||||
"%s/.config/bytenuts/inbuf.log",
|
|
||||||
home
|
|
||||||
);
|
|
||||||
|
|
||||||
/* move this processes history to the path that can be loaded on resumption */
|
|
||||||
fclose(ingest.history_fd);
|
|
||||||
rename(ingest.history_filename, in_filename);
|
|
||||||
|
|
||||||
free(in_filename);
|
|
||||||
free(ingest.history_filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
bstr_history_destroy(ingest.xmodem_hist);
|
|
||||||
|
|
||||||
pthread_exit(NULL);
|
pthread_exit(NULL);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
@ -486,22 +307,9 @@ mode_normal(int ch)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
switch (ch) {
|
switch (ch) {
|
||||||
case '\n': { /* send inputs */
|
case '\n': /* send inputs */
|
||||||
size_t inlen = strlen(ingest.inbuf);
|
if (cheerios_input(ingest.inbuf)) {
|
||||||
const char *ending;
|
break;
|
||||||
|
|
||||||
if (ingest.config->no_crlf)
|
|
||||||
ending = "\n";
|
|
||||||
else
|
|
||||||
ending = "\r\n";
|
|
||||||
|
|
||||||
inter_command_wait();
|
|
||||||
cheerios_input(ingest.inbuf, inlen);
|
|
||||||
cheerios_input(ending, strlen(ending));
|
|
||||||
if (ingest.config->echo) {
|
|
||||||
cheerios_insert(">> ", 3);
|
|
||||||
cheerios_insert(ingest.inbuf, inlen);
|
|
||||||
cheerios_insert("\r\n", 2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* store in history */
|
/* store in history */
|
||||||
|
|
@ -511,19 +319,14 @@ mode_normal(int ch)
|
||||||
ingest.history = realloc(ingest.history, ingest.history_len * sizeof(char *));
|
ingest.history = realloc(ingest.history, ingest.history_len * sizeof(char *));
|
||||||
ingest.history[ingest.history_pos] = strdup(ingest.inbuf);
|
ingest.history[ingest.history_pos] = strdup(ingest.inbuf);
|
||||||
ingest.history_pos++;
|
ingest.history_pos++;
|
||||||
if (ingest.history_fd) {
|
|
||||||
fwrite(ingest.inbuf, 1, strlen(ingest.inbuf), ingest.history_fd);
|
|
||||||
fwrite("\n", 1, 1, ingest.history_fd);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(ingest.inbuf, 0, sizeof(ingest.inbuf));
|
memset(ingest.inbuf, 0, sizeof(ingest.inbuf));
|
||||||
ingest.inpos = 0;
|
ingest.inpos = 0;
|
||||||
ingest.inlen = 0;
|
ingest.inlen = 0;
|
||||||
ingest_refresh();
|
inbuf_load();
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case KEY_UP: /* load in history */
|
case KEY_UP: /* load in history */
|
||||||
if (ingest.history_pos == 0) { /* top of history */
|
if (ingest.history_pos == 0) { /* top of history */
|
||||||
break;
|
break;
|
||||||
|
|
@ -532,11 +335,10 @@ mode_normal(int ch)
|
||||||
}
|
}
|
||||||
|
|
||||||
ingest.history_pos--;
|
ingest.history_pos--;
|
||||||
memset(ingest.inbuf, 0, sizeof(ingest.inbuf));
|
|
||||||
strcpy(ingest.inbuf, ingest.history[ingest.history_pos]);
|
strcpy(ingest.inbuf, ingest.history[ingest.history_pos]);
|
||||||
ingest.inlen = strlen(ingest.inbuf);
|
ingest.inlen = strlen(ingest.inbuf);
|
||||||
ingest.inpos = ingest.inlen;
|
ingest.inpos = ingest.inlen;
|
||||||
ingest_refresh();
|
inbuf_load();
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case KEY_DOWN: /* load in history */
|
case KEY_DOWN: /* load in history */
|
||||||
|
|
@ -544,19 +346,17 @@ mode_normal(int ch)
|
||||||
break;
|
break;
|
||||||
} else if (ingest.history_pos == ingest.history_len - 1) { /* load back temp storage */
|
} else if (ingest.history_pos == ingest.history_len - 1) { /* load back temp storage */
|
||||||
ingest.history_pos++;
|
ingest.history_pos++;
|
||||||
memset(ingest.inbuf, 0, sizeof(ingest.inbuf));
|
|
||||||
strcpy(ingest.inbuf, ingest.tmp_history);
|
strcpy(ingest.inbuf, ingest.tmp_history);
|
||||||
ingest.inlen = strlen(ingest.inbuf);
|
ingest.inlen = strlen(ingest.inbuf);
|
||||||
ingest.inpos = ingest.inlen;
|
ingest.inpos = ingest.inlen;
|
||||||
} else {
|
} else {
|
||||||
ingest.history_pos++;
|
ingest.history_pos++;
|
||||||
memset(ingest.inbuf, 0, sizeof(ingest.inbuf));
|
|
||||||
strcpy(ingest.inbuf, ingest.history[ingest.history_pos]);
|
strcpy(ingest.inbuf, ingest.history[ingest.history_pos]);
|
||||||
ingest.inlen = strlen(ingest.inbuf);
|
ingest.inlen = strlen(ingest.inbuf);
|
||||||
ingest.inpos = ingest.inlen;
|
ingest.inpos = ingest.inlen;
|
||||||
}
|
}
|
||||||
|
|
||||||
ingest_refresh();
|
inbuf_load();
|
||||||
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
@ -579,7 +379,7 @@ mode_normal(int ch)
|
||||||
ingest.inlen--;
|
ingest.inlen--;
|
||||||
}
|
}
|
||||||
|
|
||||||
ingest_refresh();
|
inbuf_load();
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -600,7 +400,7 @@ mode_xmodem(int block_sz)
|
||||||
if (ingest.prepend)
|
if (ingest.prepend)
|
||||||
free(ingest.prepend);
|
free(ingest.prepend);
|
||||||
ingest.prepend = strdup("Give me a path (ctrl-c to stop): ");
|
ingest.prepend = strdup("Give me a path (ctrl-c to stop): ");
|
||||||
ingest_refresh();
|
inbuf_load();
|
||||||
|
|
||||||
while (ingest.running) {
|
while (ingest.running) {
|
||||||
int ch;
|
int ch;
|
||||||
|
|
@ -616,8 +416,6 @@ mode_xmodem(int block_sz)
|
||||||
|
|
||||||
switch (ch) {
|
switch (ch) {
|
||||||
case '\n':
|
case '\n':
|
||||||
bstr_history_new_entry(ingest.xmodem_hist, ingest.inbuf);
|
|
||||||
|
|
||||||
pthread_mutex_lock(ingest.term_lock);
|
pthread_mutex_lock(ingest.term_lock);
|
||||||
werase(ingest.input);
|
werase(ingest.input);
|
||||||
wmove(ingest.input, 0, 0);
|
wmove(ingest.input, 0, 0);
|
||||||
|
|
@ -634,36 +432,8 @@ mode_xmodem(int block_sz)
|
||||||
break;
|
break;
|
||||||
case '\t': /* tab complete */
|
case '\t': /* tab complete */
|
||||||
auto_complete();
|
auto_complete();
|
||||||
ingest_refresh();
|
inbuf_load();
|
||||||
break;
|
break;
|
||||||
case KEY_BACKSPACE:
|
|
||||||
case KEY_UP:
|
|
||||||
/* fall-through */
|
|
||||||
case KEY_DOWN:
|
|
||||||
{
|
|
||||||
const char *filename;
|
|
||||||
|
|
||||||
if (ch == KEY_UP) {
|
|
||||||
bstr_history_older(ingest.xmodem_hist);
|
|
||||||
} else {
|
|
||||||
bstr_history_newer(ingest.xmodem_hist);
|
|
||||||
}
|
|
||||||
|
|
||||||
filename = bstr_history_atpos(ingest.xmodem_hist);
|
|
||||||
if (!filename) {
|
|
||||||
memset(ingest.inbuf, 0, sizeof(ingest.inbuf));
|
|
||||||
ingest.inpos = 0;
|
|
||||||
ingest.inlen = 0;
|
|
||||||
ingest_refresh();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
strncpy(ingest.inbuf, filename, sizeof(ingest.inbuf));
|
|
||||||
ingest.inpos = strlen(ingest.inbuf);
|
|
||||||
ingest.inlen = ingest.inpos;
|
|
||||||
ingest_refresh();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
char tmp[1024];
|
char tmp[1024];
|
||||||
|
|
@ -684,7 +454,7 @@ mode_xmodem(int block_sz)
|
||||||
ingest.inlen--;
|
ingest.inlen--;
|
||||||
}
|
}
|
||||||
|
|
||||||
ingest_refresh();
|
inbuf_load();
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -699,10 +469,8 @@ mode_xmodem(int block_sz)
|
||||||
free(ingest.prepend);
|
free(ingest.prepend);
|
||||||
ingest.prepend = NULL;
|
ingest.prepend = NULL;
|
||||||
|
|
||||||
bstr_history_unset_pos(ingest.xmodem_hist);
|
|
||||||
|
|
||||||
bytenuts_set_status(STATUS_INGEST, "normal");
|
bytenuts_set_status(STATUS_INGEST, "normal");
|
||||||
ingest_refresh();
|
inbuf_load();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -710,90 +478,6 @@ mode_xmodem(int block_sz)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
|
||||||
mode_hex(int ch)
|
|
||||||
{
|
|
||||||
if (handle_functions(ch))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
switch (ch) {
|
|
||||||
case '\n': { /* send inputs */
|
|
||||||
char hexbuf[sizeof(ingest.inbuf)/2];
|
|
||||||
size_t inbuflen = strlen(ingest.inbuf);
|
|
||||||
size_t hexlen = 0;
|
|
||||||
/* assume leading 0 on odd length buffers */
|
|
||||||
if (inbuflen % 2 != 0) {
|
|
||||||
memmove(&ingest.inbuf[1], ingest.inbuf, inbuflen);
|
|
||||||
ingest.inbuf[0] = '0';
|
|
||||||
inbuflen++;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < inbuflen; i += 2) {
|
|
||||||
char tmp = ingest.inbuf[i+2];
|
|
||||||
char *inval;
|
|
||||||
long ch;
|
|
||||||
|
|
||||||
ingest.inbuf[i+2] = '\0';
|
|
||||||
ch = strtol(&ingest.inbuf[i], &inval, 16);
|
|
||||||
ingest.inbuf[i+2] = tmp;
|
|
||||||
|
|
||||||
/* strtol could not parse a character */
|
|
||||||
if (inval != &ingest.inbuf[i+2])
|
|
||||||
break;
|
|
||||||
|
|
||||||
hexbuf[i/2] = ch;
|
|
||||||
hexlen++;
|
|
||||||
}
|
|
||||||
|
|
||||||
inter_command_wait();
|
|
||||||
cheerios_input(hexbuf, hexlen);
|
|
||||||
if (ingest.config->echo) {
|
|
||||||
cheerios_insert(">> (hex)\r\n", 10);
|
|
||||||
for (int i = 0; i < hexlen; i++) {
|
|
||||||
if (
|
|
||||||
i > 0 &&
|
|
||||||
i != hexlen-1 &&
|
|
||||||
i % 16 == 0
|
|
||||||
) {
|
|
||||||
cheerios_insert("\r\n", 2);
|
|
||||||
}
|
|
||||||
cheerios_insert(&ingest.inbuf[i*2], 2);
|
|
||||||
cheerios_insert(" ", 1);
|
|
||||||
}
|
|
||||||
cheerios_insert("\r\n", 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* store in history */
|
|
||||||
ingest.history_pos = ingest.history_len;
|
|
||||||
if (ingest.inlen > 0) {
|
|
||||||
ingest.history_len++;
|
|
||||||
ingest.history = realloc(ingest.history, ingest.history_len * sizeof(char *));
|
|
||||||
ingest.history[ingest.history_pos] = strdup(ingest.inbuf);
|
|
||||||
ingest.history_pos++;
|
|
||||||
if (ingest.history_fd) {
|
|
||||||
fwrite(ingest.inbuf, 1, strlen(ingest.inbuf), ingest.history_fd);
|
|
||||||
fwrite("\n", 1, 1, ingest.history_fd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(ingest.inbuf, 0, sizeof(ingest.inbuf));
|
|
||||||
ingest.inpos = 0;
|
|
||||||
ingest.inlen = 0;
|
|
||||||
ingest_refresh();
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case KEY_UP: /* load in history */
|
|
||||||
case KEY_DOWN: /* load in history */
|
|
||||||
default:
|
|
||||||
mode_normal(ch);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
handle_functions(int ch)
|
handle_functions(int ch)
|
||||||
{
|
{
|
||||||
|
|
@ -814,10 +498,10 @@ handle_functions(int ch)
|
||||||
case KEY_SEND: /* go to front of log */
|
case KEY_SEND: /* go to front of log */
|
||||||
cheerios_gofwd(-1);
|
cheerios_gofwd(-1);
|
||||||
return 1;
|
return 1;
|
||||||
case KEY_CUP: /* ctrl + up arrow */
|
case 566: /* ctrl + up arrow */
|
||||||
cheerios_goback(1);
|
cheerios_goback(1);
|
||||||
return 1;
|
return 1;
|
||||||
case KEY_CDOWN: /* ctrl + down arrow */
|
case 525: /* ctrl + down arrow */
|
||||||
cheerios_gofwd(1);
|
cheerios_gofwd(1);
|
||||||
return 1;
|
return 1;
|
||||||
case KEY_RESIZE:
|
case KEY_RESIZE:
|
||||||
|
|
@ -831,7 +515,7 @@ handle_functions(int ch)
|
||||||
ingest.inpos--;
|
ingest.inpos--;
|
||||||
ingest.inlen--;
|
ingest.inlen--;
|
||||||
ingest.inbuf[ingest.inlen] = '\0';
|
ingest.inbuf[ingest.inlen] = '\0';
|
||||||
ingest_refresh();
|
inbuf_load();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -843,7 +527,7 @@ handle_functions(int ch)
|
||||||
ingest.inlen--;
|
ingest.inlen--;
|
||||||
memcpy(&ingest.inbuf[ingest.inpos], tmp, tmp_len);
|
memcpy(&ingest.inbuf[ingest.inpos], tmp, tmp_len);
|
||||||
ingest.inbuf[ingest.inlen] = '\0';
|
ingest.inbuf[ingest.inlen] = '\0';
|
||||||
ingest_refresh();
|
inbuf_load();
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
@ -857,7 +541,7 @@ handle_functions(int ch)
|
||||||
} else if (ingest.inpos == ingest.inlen - 1) {
|
} else if (ingest.inpos == ingest.inlen - 1) {
|
||||||
ingest.inbuf[ingest.inpos] = '\0';
|
ingest.inbuf[ingest.inpos] = '\0';
|
||||||
ingest.inlen--;
|
ingest.inlen--;
|
||||||
ingest_refresh();
|
inbuf_load();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -866,7 +550,7 @@ handle_functions(int ch)
|
||||||
ingest.inlen--;
|
ingest.inlen--;
|
||||||
memcpy(&ingest.inbuf[ingest.inpos], tmp, tmp_len);
|
memcpy(&ingest.inbuf[ingest.inpos], tmp, tmp_len);
|
||||||
ingest.inbuf[ingest.inlen] = '\0';
|
ingest.inbuf[ingest.inlen] = '\0';
|
||||||
ingest_refresh();
|
inbuf_load();
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
@ -874,21 +558,21 @@ handle_functions(int ch)
|
||||||
if (ingest.inpos == 0)
|
if (ingest.inpos == 0)
|
||||||
return 1;
|
return 1;
|
||||||
ingest.inpos--;
|
ingest.inpos--;
|
||||||
ingest_refresh();
|
inbuf_load();
|
||||||
return 1;
|
return 1;
|
||||||
case KEY_RIGHT:
|
case KEY_RIGHT:
|
||||||
if (ingest.inpos == ingest.inlen)
|
if (ingest.inpos == ingest.inlen)
|
||||||
return 1;
|
return 1;
|
||||||
ingest.inpos++;
|
ingest.inpos++;
|
||||||
ingest_refresh();
|
inbuf_load();
|
||||||
return 1;
|
return 1;
|
||||||
case KEY_END:
|
case KEY_END:
|
||||||
ingest.inpos = ingest.inlen;
|
ingest.inpos = ingest.inlen;
|
||||||
ingest_refresh();
|
inbuf_load();
|
||||||
return 1;
|
return 1;
|
||||||
case KEY_HOME:
|
case KEY_HOME:
|
||||||
ingest.inpos = 0;
|
ingest.inpos = 0;
|
||||||
ingest_refresh();
|
inbuf_load();
|
||||||
return 1;
|
return 1;
|
||||||
#if 0
|
#if 0
|
||||||
case KEY_MOUSE:
|
case KEY_MOUSE:
|
||||||
|
|
@ -911,6 +595,37 @@ handle_functions(int ch)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
inbuf_load()
|
||||||
|
{
|
||||||
|
int cx, cy = -1;
|
||||||
|
|
||||||
|
pthread_mutex_lock(ingest.term_lock);
|
||||||
|
|
||||||
|
werase(ingest.input);
|
||||||
|
wmove(ingest.input, 0, 0);
|
||||||
|
if (ingest.prepend)
|
||||||
|
wprintw(ingest.input, "%s", ingest.prepend);
|
||||||
|
|
||||||
|
for (int i = 0; i < ingest.inlen; i++) {
|
||||||
|
if (i == ingest.inpos)
|
||||||
|
getyx(ingest.input, cy, cx);
|
||||||
|
waddch(ingest.input, ingest.inbuf[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cx < 0) {
|
||||||
|
cx = ingest.inlen;
|
||||||
|
cy = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
wmove(ingest.input, cy, cx);
|
||||||
|
wrefresh(ingest.input);
|
||||||
|
|
||||||
|
pthread_mutex_unlock(ingest.term_lock);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
print_stats()
|
print_stats()
|
||||||
{
|
{
|
||||||
|
|
@ -1042,17 +757,13 @@ auto_complete_cleanup:
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
read_cmd_page(const char *home_dir, int idx)
|
read_cmd_page(int idx)
|
||||||
{
|
{
|
||||||
char fname[128];
|
char fname[128];
|
||||||
FILE *fd;
|
FILE *fd;
|
||||||
char line[1024];
|
char line[1024];
|
||||||
|
|
||||||
snprintf(
|
snprintf(fname, sizeof(fname), ".config/bytenuts/commands%d", idx+1);
|
||||||
fname, sizeof(fname),
|
|
||||||
"%s/.config/bytenuts/commands%d",
|
|
||||||
home_dir, idx+1
|
|
||||||
);
|
|
||||||
|
|
||||||
fd = fopen(fname, "r");
|
fd = fopen(fname, "r");
|
||||||
if (!fd)
|
if (!fd)
|
||||||
|
|
@ -1086,24 +797,8 @@ static void
|
||||||
update_cmd_pg_status()
|
update_cmd_pg_status()
|
||||||
{
|
{
|
||||||
if (ingest.cmd_pgs_n > 0) {
|
if (ingest.cmd_pgs_n > 0) {
|
||||||
bytenuts_set_status(STATUS_CMDPAGE, "cmd pg.%d", ingest.cmd_pg_cur+1);
|
bytenuts_set_status(STATUS_CMDPAGE, "pg.%d", ingest.cmd_pg_cur+1);
|
||||||
} else {
|
} else {
|
||||||
bytenuts_set_status(STATUS_CMDPAGE, "n/a");
|
bytenuts_set_status(STATUS_CMDPAGE, "n/a");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
inter_command_wait()
|
|
||||||
{
|
|
||||||
struct timespec target;
|
|
||||||
struct timespec now;
|
|
||||||
|
|
||||||
clock_gettime(CLOCK_REALTIME, &now);
|
|
||||||
memcpy(&target, &ingest.cmd_ts, sizeof(struct timespec));
|
|
||||||
|
|
||||||
timer_add_ms(&target, ingest.config->inter_cmd_to);
|
|
||||||
timer_sub(&target, &now);
|
|
||||||
|
|
||||||
nanosleep(&target, NULL);
|
|
||||||
clock_gettime(CLOCK_REALTIME, &ingest.cmd_ts);
|
|
||||||
}
|
|
||||||
|
|
|
||||||
24
src/ingest.h
24
src/ingest.h
|
|
@ -1,23 +1,15 @@
|
||||||
#ifndef _INGEST_H_
|
#ifndef _INGEST_H_
|
||||||
#define _INGEST_H_
|
#define _INGEST_H_
|
||||||
|
|
||||||
#ifdef __MINGW32__
|
#include <ncurses.h>
|
||||||
# include <curses.h>
|
|
||||||
#else
|
|
||||||
# include <ncurses.h>
|
|
||||||
#endif
|
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <stdio.h>
|
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
#include "bstr.h"
|
|
||||||
#include "bytenuts.h"
|
#include "bytenuts.h"
|
||||||
|
|
||||||
enum ingest_mode_enum {
|
enum ingest_mode_enum {
|
||||||
INGEST_MODE_NORMAL = 0,
|
INGEST_MODE_NORMAL = 0,
|
||||||
INGEST_MODE_XMODEM,
|
INGEST_MODE_XMODEM,
|
||||||
INGEST_MODE_XMODEM1K,
|
INGEST_MODE_XMODEM1K,
|
||||||
INGEST_MODE_HEX,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct ingest_struct {
|
typedef struct ingest_struct {
|
||||||
|
|
@ -26,13 +18,11 @@ typedef struct ingest_struct {
|
||||||
pthread_t thr;
|
pthread_t thr;
|
||||||
volatile int running;
|
volatile int running;
|
||||||
char *prepend; /* string to be prepended in the prompt */
|
char *prepend; /* string to be prepended in the prompt */
|
||||||
char inbuf[4096];
|
char inbuf[1024];
|
||||||
int inpos; /* cursor position within the inbuf itself */
|
int inpos; /* cursor position within the inbuf itself */
|
||||||
int inlen; /* length of the input buffer */
|
int inlen; /* length of the input buffer */
|
||||||
char **history; /* array of strings representing the command history */
|
char **history; /* array of strings representing the command history */
|
||||||
char *history_filename; /* realpath of inbuf.pid.log */
|
char tmp_history[1024]; /* copy of inbuf before we load in history */
|
||||||
FILE *history_fd;
|
|
||||||
char tmp_history[4096]; /* copy of inbuf before we load in history */
|
|
||||||
int history_len;
|
int history_len;
|
||||||
int history_pos; /* where we currently reside in the history */
|
int history_pos; /* where we currently reside in the history */
|
||||||
int mode;
|
int mode;
|
||||||
|
|
@ -46,8 +36,6 @@ typedef struct ingest_struct {
|
||||||
int cmd_pgs_n;
|
int cmd_pgs_n;
|
||||||
/* what command page is currently selected */
|
/* what command page is currently selected */
|
||||||
int cmd_pg_cur;
|
int cmd_pg_cur;
|
||||||
struct timespec cmd_ts; /* last command sent timestamp */
|
|
||||||
bstr_history_handle xmodem_hist; /* xmodem filename transfer history */
|
|
||||||
} ingest_t;
|
} ingest_t;
|
||||||
|
|
||||||
/* startup the input window thread */
|
/* startup the input window thread */
|
||||||
|
|
@ -56,10 +44,4 @@ int ingest_start(bytenuts_t *bytenuts);
|
||||||
/* stop the input window thread */
|
/* stop the input window thread */
|
||||||
int ingest_stop();
|
int ingest_stop();
|
||||||
|
|
||||||
/* refresh the inbuf window */
|
|
||||||
int ingest_refresh();
|
|
||||||
|
|
||||||
/* Set the the command history directly. Values are copied */
|
|
||||||
int ingest_set_history(char **history, int history_len);
|
|
||||||
|
|
||||||
#endif /* _INGEST_H_ */
|
#endif /* _INGEST_H_ */
|
||||||
|
|
|
||||||
|
|
@ -4,20 +4,16 @@
|
||||||
|
|
||||||
#include "bytenuts.h"
|
#include "bytenuts.h"
|
||||||
|
|
||||||
#ifndef __MINGW32__
|
|
||||||
static void sigint_handler(int s);
|
static void sigint_handler(int s);
|
||||||
#endif
|
|
||||||
|
|
||||||
int
|
int
|
||||||
main(int argc, char **argv)
|
main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
#ifndef __MINGW32__
|
|
||||||
struct sigaction act;
|
struct sigaction act;
|
||||||
act.sa_handler = sigint_handler;
|
act.sa_handler = sigint_handler;
|
||||||
sigemptyset(&act.sa_mask);
|
sigemptyset(&act.sa_mask);
|
||||||
act.sa_flags = 0;
|
act.sa_flags = 0;
|
||||||
sigaction(SIGINT, &act, NULL);
|
sigaction(SIGINT, &act, NULL);
|
||||||
#endif
|
|
||||||
|
|
||||||
if (bytenuts_run(argc, argv)) {
|
if (bytenuts_run(argc, argv)) {
|
||||||
bytenuts_stop();
|
bytenuts_stop();
|
||||||
|
|
@ -27,11 +23,9 @@ main(int argc, char **argv)
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef __MINGW32__
|
|
||||||
static void
|
static void
|
||||||
sigint_handler(int s)
|
sigint_handler(int s)
|
||||||
{
|
{
|
||||||
bytenuts_kill();
|
bytenuts_kill();
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
|
||||||
99
src/paths.c
99
src/paths.c
|
|
@ -1,99 +0,0 @@
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <inttypes.h>
|
|
||||||
|
|
||||||
#include "bstr.h"
|
|
||||||
#include "paths.h"
|
|
||||||
|
|
||||||
#if __MINGW32__
|
|
||||||
# define BSTR_LLD "%I64d"
|
|
||||||
#else
|
|
||||||
# define BSTR_LLD "%lld"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
char *
|
|
||||||
paths_append(char *base, const char *suffix)
|
|
||||||
{
|
|
||||||
size_t baselen;
|
|
||||||
const char *sep = "";
|
|
||||||
|
|
||||||
if (!base)
|
|
||||||
return strdup(suffix);
|
|
||||||
|
|
||||||
baselen = strlen(base);
|
|
||||||
|
|
||||||
if (
|
|
||||||
(baselen == 0) ||
|
|
||||||
((base[baselen-1] != '/') && (base[baselen-1] == '\\'))
|
|
||||||
) {
|
|
||||||
/* no separator at end of path */
|
|
||||||
#if __MINGW32__
|
|
||||||
sep = "\\";
|
|
||||||
#else
|
|
||||||
sep = "/";
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
return bstr_print(base, "%s%s", sep, suffix);
|
|
||||||
}
|
|
||||||
|
|
||||||
char *
|
|
||||||
paths_bnconf_dir()
|
|
||||||
{
|
|
||||||
#ifdef __MINGW32__
|
|
||||||
char *home = getenv("HOMEPATH");
|
|
||||||
#else
|
|
||||||
char *home = getenv("HOME");
|
|
||||||
#endif
|
|
||||||
char *ret = NULL;
|
|
||||||
|
|
||||||
if (!home)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
#ifdef __MINGW32__
|
|
||||||
ret = strdup("C:\\");
|
|
||||||
#endif
|
|
||||||
ret = paths_append(ret, home);
|
|
||||||
ret = paths_append(ret, ".config/bytenuts");
|
|
||||||
|
|
||||||
/* TODO free environment variable on Windows? */
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *
|
|
||||||
paths_bnconf_default()
|
|
||||||
{
|
|
||||||
char *ret = paths_bnconf_dir();
|
|
||||||
|
|
||||||
if (!ret)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return paths_append(ret, "config");
|
|
||||||
}
|
|
||||||
|
|
||||||
char *
|
|
||||||
paths_command_file(int idx)
|
|
||||||
{
|
|
||||||
char *ret = paths_bnconf_dir();
|
|
||||||
|
|
||||||
if (!ret)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return bstr_print(ret, "/commands%d", idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
char *
|
|
||||||
paths_logfile(const char *prefix, long long pid)
|
|
||||||
{
|
|
||||||
char *ret = paths_bnconf_dir();
|
|
||||||
|
|
||||||
if (!ret)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if (pid > 0) {
|
|
||||||
return bstr_print(ret, "/%s." BSTR_LLD ".log", prefix, pid);
|
|
||||||
} else {
|
|
||||||
return bstr_print(ret, "/%s.log", prefix);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
20
src/paths.h
20
src/paths.h
|
|
@ -1,20 +0,0 @@
|
||||||
#ifndef _PATHS_H_
|
|
||||||
#define _PATHS_H_
|
|
||||||
|
|
||||||
/* Realloc the base path and add the suffix to it, "/"s handled nicely */
|
|
||||||
char *paths_append(char *base, const char *suffix);
|
|
||||||
|
|
||||||
/* Get the ~/.config/bytenuts directory */
|
|
||||||
char *paths_bnconf_dir();
|
|
||||||
|
|
||||||
/* Get the default bytenuts config filepath */
|
|
||||||
char *paths_bnconf_default();
|
|
||||||
|
|
||||||
/* Generate the path to the command<idx> file */
|
|
||||||
char *paths_command_file(int idx);
|
|
||||||
|
|
||||||
/* Generate a logfile path of the form ~/.config/bytenuts/<prefix>[.<pid>].log
|
|
||||||
* PID only inserted if >0 */
|
|
||||||
char *paths_logfile(const char *prefix, long long pid);
|
|
||||||
|
|
||||||
#endif /* _PATHS_H_ */
|
|
||||||
244
src/serial.c
244
src/serial.c
|
|
@ -2,158 +2,28 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#include <termios.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <time.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include "serial.h"
|
#include "serial.h"
|
||||||
|
|
||||||
#ifdef __MINGW32__
|
|
||||||
# include <windows.h>
|
|
||||||
|
|
||||||
serial_t
|
|
||||||
serial_open(const char *comport, long bps)
|
|
||||||
{
|
|
||||||
/* referred to: https://github.com/waynix/SPinGW/tree/master */
|
|
||||||
HANDLE serial = CreateFile(
|
|
||||||
comport,
|
|
||||||
GENERIC_READ | GENERIC_WRITE,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
OPEN_EXISTING,
|
|
||||||
0,
|
|
||||||
0
|
|
||||||
);
|
|
||||||
DCB serial_params = { 0 };
|
|
||||||
COMMTIMEOUTS timeouts = { 0 };
|
|
||||||
|
|
||||||
if (serial == INVALID_HANDLE_VALUE) {
|
|
||||||
return SERIAL_INVALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
serial_params.DCBlength = sizeof(serial_params);
|
|
||||||
if (!GetCommState(serial, &serial_params)) {
|
|
||||||
return SERIAL_INVALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 8n1 */
|
|
||||||
serial_params.BaudRate = bps;
|
|
||||||
serial_params.ByteSize = 8;
|
|
||||||
serial_params.StopBits = 1;
|
|
||||||
serial_params.Parity = NOPARITY;
|
|
||||||
|
|
||||||
if (!SetCommState(serial, &serial_params)) {
|
|
||||||
return SERIAL_INVALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
timeouts.ReadIntervalTimeout = 0;
|
|
||||||
timeouts.ReadTotalTimeoutConstant = 0;
|
|
||||||
timeouts.ReadTotalTimeoutMultiplier = 0;
|
|
||||||
timeouts.WriteTotalTimeoutConstant = 0;
|
|
||||||
timeouts.WriteTotalTimeoutMultiplier = 0;
|
|
||||||
|
|
||||||
if (!SetCommTimeouts(serial, &timeouts)) {
|
|
||||||
return SERIAL_INVALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
return serial;
|
|
||||||
}
|
|
||||||
|
|
||||||
ssize_t
|
|
||||||
serial_read(serial_t serial, void *buf, size_t len)
|
|
||||||
{
|
|
||||||
DWORD ret = 0;
|
|
||||||
|
|
||||||
if (!ReadFile(serial, buf, len, &ret, NULL)) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
ssize_t
|
|
||||||
serial_read_to(serial_t serial, void *buf, size_t len, unsigned int to_ms)
|
|
||||||
{
|
|
||||||
DWORD ret = -1;
|
|
||||||
size_t p = 0;
|
|
||||||
size_t len_left = len;
|
|
||||||
|
|
||||||
while (to_ms > 0) {
|
|
||||||
DWORD tmp = 0;
|
|
||||||
|
|
||||||
if (!ReadFile(serial, &((uint8_t *)buf)[p], len_left, &tmp, NULL)) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tmp > 0) {
|
|
||||||
if (ret < 0) {
|
|
||||||
ret = tmp;
|
|
||||||
} else {
|
|
||||||
ret += tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
len_left -= tmp;
|
|
||||||
p += tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (len_left == 0) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* sleep 1ms */
|
|
||||||
nanosleep(&(struct timespec){ 0, 1000000 }, NULL);
|
|
||||||
to_ms--;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ret < 0) {
|
|
||||||
ret = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
ssize_t
|
|
||||||
serial_write(serial_t serial, const void *buf, size_t len)
|
|
||||||
{
|
|
||||||
DWORD ret = 0;
|
|
||||||
|
|
||||||
if (!WriteFile(serial, buf, len, &ret, NULL)) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
int
|
||||||
serial_close(serial_t serial)
|
serial_open(const char *path, speed_t bps)
|
||||||
{
|
{
|
||||||
return (CloseHandle(serial) != 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
# include <termios.h>
|
|
||||||
# include <poll.h>
|
|
||||||
|
|
||||||
static speed_t long_to_speed(long bps);
|
|
||||||
|
|
||||||
serial_t
|
|
||||||
serial_open(const char *path, long bps)
|
|
||||||
{
|
|
||||||
speed_t speed = long_to_speed(bps);
|
|
||||||
int fd;
|
int fd;
|
||||||
struct termios otio;
|
struct termios otio;
|
||||||
struct termios ntio;
|
struct termios ntio;
|
||||||
|
|
||||||
fd = open(path, O_RDWR | O_NOCTTY | O_NDELAY);
|
fd = open(path, O_RDWR | O_NOCTTY | O_NDELAY);
|
||||||
if (fd == -1) {
|
if (fd == -1) {
|
||||||
return SERIAL_INVALID;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
tcgetattr(fd, &otio);
|
tcgetattr(fd, &otio);
|
||||||
tcgetattr(fd, &ntio);
|
tcgetattr(fd, &ntio);
|
||||||
|
|
||||||
cfsetispeed(&ntio, speed);
|
cfsetispeed(&ntio, bps);
|
||||||
cfsetospeed(&ntio, speed);
|
cfsetospeed(&ntio, bps);
|
||||||
|
|
||||||
/* https://www.cmrr.umn.edu/~strupp/serial.html */
|
/* https://www.cmrr.umn.edu/~strupp/serial.html */
|
||||||
/* 8N1 */
|
/* 8N1 */
|
||||||
|
|
@ -187,107 +57,3 @@ serial_open(const char *path, long bps)
|
||||||
|
|
||||||
return fd;
|
return fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t
|
|
||||||
serial_read(serial_t serial, void *buf, size_t len)
|
|
||||||
{
|
|
||||||
return serial_read_to(serial, buf, len, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
ssize_t
|
|
||||||
serial_read_to(serial_t serial, void *buf, size_t len, unsigned int to_ms)
|
|
||||||
{
|
|
||||||
struct pollfd fds;
|
|
||||||
fds.fd = serial;
|
|
||||||
fds.events = POLLIN;
|
|
||||||
fds.revents = 0;
|
|
||||||
|
|
||||||
if (poll(&fds, 1, to_ms) == 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return read(serial, buf, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
ssize_t
|
|
||||||
serial_write(serial_t serial, const void *buf, size_t len)
|
|
||||||
{
|
|
||||||
return write(serial, buf, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
serial_close(serial_t serial)
|
|
||||||
{
|
|
||||||
return close(serial);
|
|
||||||
}
|
|
||||||
|
|
||||||
static speed_t
|
|
||||||
long_to_speed(long bps)
|
|
||||||
{
|
|
||||||
switch (bps) {
|
|
||||||
case 50:
|
|
||||||
return B50;
|
|
||||||
case 75:
|
|
||||||
return B75;
|
|
||||||
case 110:
|
|
||||||
return B110;
|
|
||||||
case 134:
|
|
||||||
return B134;
|
|
||||||
case 150:
|
|
||||||
return B150;
|
|
||||||
case 200:
|
|
||||||
return B200;
|
|
||||||
case 300:
|
|
||||||
return B300;
|
|
||||||
case 600:
|
|
||||||
return B600;
|
|
||||||
case 1200:
|
|
||||||
return B1200;
|
|
||||||
case 1800:
|
|
||||||
return B1800;
|
|
||||||
case 2400:
|
|
||||||
return B2400;
|
|
||||||
case 4800:
|
|
||||||
return B4800;
|
|
||||||
case 9600:
|
|
||||||
return B9600;
|
|
||||||
case 19200:
|
|
||||||
return B19200;
|
|
||||||
case 38400:
|
|
||||||
return B38400;
|
|
||||||
case 57600:
|
|
||||||
return B57600;
|
|
||||||
case 115200:
|
|
||||||
return B115200;
|
|
||||||
case 230400:
|
|
||||||
return B230400;
|
|
||||||
case 460800:
|
|
||||||
return B460800;
|
|
||||||
case 500000:
|
|
||||||
return B500000;
|
|
||||||
case 576000:
|
|
||||||
return B576000;
|
|
||||||
case 921600:
|
|
||||||
return B921600;
|
|
||||||
case 1000000:
|
|
||||||
return B1000000;
|
|
||||||
case 1152000:
|
|
||||||
return B1152000;
|
|
||||||
case 1500000:
|
|
||||||
return B1500000;
|
|
||||||
case 2000000:
|
|
||||||
return B2000000;
|
|
||||||
case 2500000:
|
|
||||||
return B2500000;
|
|
||||||
case 3000000:
|
|
||||||
return B3000000;
|
|
||||||
case 3500000:
|
|
||||||
return B3500000;
|
|
||||||
case 4000000:
|
|
||||||
return B4000000;
|
|
||||||
default:
|
|
||||||
return B0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* __MINGW32__ */
|
|
||||||
|
|
|
||||||
27
src/serial.h
27
src/serial.h
|
|
@ -1,31 +1,8 @@
|
||||||
#ifndef _SERIAL_H_
|
#ifndef _SERIAL_H_
|
||||||
#define _SERIAL_H_
|
#define _SERIAL_H_
|
||||||
|
|
||||||
#ifdef __MINGW32__
|
#include <termios.h>
|
||||||
# include <windows.h>
|
|
||||||
typedef HANDLE serial_t;
|
|
||||||
#else
|
|
||||||
typedef int serial_t;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define SERIAL_INVALID (serial_t)(-1)
|
int serial_open(const char *path, speed_t bps);
|
||||||
|
|
||||||
/* Open a serial port with the given speed and return a handle that can be used
|
|
||||||
* in the other serial APIs*/
|
|
||||||
serial_t serial_open(const char *path, long bps);
|
|
||||||
|
|
||||||
/* Read up to len bytes (non-blocking), length read is returned */
|
|
||||||
ssize_t serial_read(serial_t serial, void *buf, size_t len);
|
|
||||||
|
|
||||||
/* Read up to len bytes, or until to_ms milliseconds have elapsed, length read
|
|
||||||
* is returned */
|
|
||||||
ssize_t serial_read_to(serial_t serial, void *buf, size_t len, unsigned int to_ms);
|
|
||||||
|
|
||||||
/* Write len bytes onto the serial port, actual number of bytes written is
|
|
||||||
* returned */
|
|
||||||
ssize_t serial_write(serial_t serial, const void *buf, size_t len);
|
|
||||||
|
|
||||||
/* Close the serial file */
|
|
||||||
int serial_close(serial_t serial);
|
|
||||||
|
|
||||||
#endif /* _SERIAL_H_ */
|
#endif /* _SERIAL_H_ */
|
||||||
|
|
|
||||||
|
|
@ -1,71 +0,0 @@
|
||||||
#include "timer_math.h"
|
|
||||||
|
|
||||||
int
|
|
||||||
timer_cmp(const struct timespec *a, const struct timespec *b)
|
|
||||||
{
|
|
||||||
if (a->tv_sec < b->tv_sec) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
else if (a->tv_sec > b->tv_sec) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
else if (a->tv_nsec < b->tv_nsec) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
else if (a->tv_nsec > b->tv_nsec) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
timer_add(struct timespec *a, const struct timespec *b)
|
|
||||||
{
|
|
||||||
a->tv_sec += b->tv_sec;
|
|
||||||
a->tv_nsec += b->tv_nsec;
|
|
||||||
|
|
||||||
if (a->tv_nsec > 1000000000) {
|
|
||||||
a->tv_sec += 1;
|
|
||||||
a->tv_nsec -= 1000000000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
timer_add_ms(struct timespec *ts, uint32_t ms)
|
|
||||||
{
|
|
||||||
struct timespec tmp = {
|
|
||||||
.tv_sec = ms / 1000,
|
|
||||||
.tv_nsec = (ms % 1000) * 1000000
|
|
||||||
};
|
|
||||||
timer_add(ts, &tmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
timer_sub(struct timespec *a, const struct timespec *b)
|
|
||||||
{
|
|
||||||
if (timer_cmp(a, b) <= 0) {
|
|
||||||
a->tv_sec = 0;
|
|
||||||
a->tv_nsec = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
a->tv_sec -= b->tv_sec;
|
|
||||||
a->tv_nsec -= b->tv_nsec;
|
|
||||||
|
|
||||||
if (a->tv_nsec < 0) {
|
|
||||||
a->tv_nsec += 1000000000;
|
|
||||||
a->tv_sec -= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
timer_sub_ms(struct timespec *ts, uint32_t ms)
|
|
||||||
{
|
|
||||||
struct timespec tmp = {
|
|
||||||
.tv_sec = ms / 1000,
|
|
||||||
.tv_nsec = (ms % 1000) * 1000000
|
|
||||||
};
|
|
||||||
timer_sub(ts, &tmp);
|
|
||||||
}
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
#ifndef _TIMER_MATH_H_
|
|
||||||
#define _TIMER_MATH_H_
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
/* NOTE
|
|
||||||
* All math here is assuming that the second and nanosecond values of the
|
|
||||||
* timespecs are all non-negative, and nanoseconds is in the range [0, 999999999] */
|
|
||||||
|
|
||||||
/* Compare the two timespecs
|
|
||||||
* Return -1 if a < b, 0 if a = b, and 1 if a > b */
|
|
||||||
int timer_cmp(const struct timespec *a, const struct timespec *b);
|
|
||||||
|
|
||||||
/* Add the timespec value of b to a */
|
|
||||||
void timer_add(struct timespec *a, const struct timespec *b);
|
|
||||||
|
|
||||||
/* Add ms milliseconds to the timespec*/
|
|
||||||
void timer_add_ms(struct timespec *ts, uint32_t ms);
|
|
||||||
|
|
||||||
/* Subtract time b from a. If b > a, then a is set to 0 */
|
|
||||||
void timer_sub(struct timespec *a, const struct timespec *b);
|
|
||||||
|
|
||||||
/* Subtract ms milliseconds from ts. If ts would become negative, it is set to 0 */
|
|
||||||
void timer_sub_ms(struct timespec *ts, uint32_t ms);
|
|
||||||
|
|
||||||
#endif /* _TIMER_MATH_H_ */
|
|
||||||
29
src/xmodem.c
29
src/xmodem.c
|
|
@ -1,3 +1,4 @@
|
||||||
|
#include <poll.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
@ -7,7 +8,7 @@
|
||||||
|
|
||||||
#include "xmodem.h"
|
#include "xmodem.h"
|
||||||
|
|
||||||
int _xmodem_wait(serial_t fd, uint8_t *ret, int timeout_ms);
|
int _xmodem_wait(int fd, uint8_t *ret, int timeout_ms);
|
||||||
|
|
||||||
uint16_t _xmodem_crc(const uint8_t *buf, size_t len);
|
uint16_t _xmodem_crc(const uint8_t *buf, size_t len);
|
||||||
uint8_t _xmodem_csum(const uint8_t *buf, size_t len);
|
uint8_t _xmodem_csum(const uint8_t *buf, size_t len);
|
||||||
|
|
@ -15,11 +16,11 @@ void _xmodem_status_print(size_t sent, size_t total);
|
||||||
|
|
||||||
int
|
int
|
||||||
xmodem_send(
|
xmodem_send(
|
||||||
serial_t dest_fd,
|
int dest_fd,
|
||||||
int in_fd,
|
int in_fd,
|
||||||
size_t sz,
|
size_t sz,
|
||||||
int block_sz,
|
int block_sz,
|
||||||
void (*status_cb)(size_t sent, size_t total, int ack_fails)
|
void (*status_cb)(size_t sent, size_t total)
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
uint8_t start_byte;
|
uint8_t start_byte;
|
||||||
|
|
@ -29,7 +30,6 @@ xmodem_send(
|
||||||
uint8_t packet_num;
|
uint8_t packet_num;
|
||||||
int retries;
|
int retries;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
int ack_fails = 0;
|
|
||||||
|
|
||||||
if (block_sz != 128 && block_sz != 1024) {
|
if (block_sz != 128 && block_sz != 1024) {
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -105,7 +105,7 @@ xmodem_send(
|
||||||
goto end_of_transmission;
|
goto end_of_transmission;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (serial_write(dest_fd, buf, buf_sz) != buf_sz){
|
if (write(dest_fd, buf, buf_sz) != buf_sz){
|
||||||
return XMODEM_ERR_WRITE;
|
return XMODEM_ERR_WRITE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -115,17 +115,14 @@ xmodem_send(
|
||||||
|
|
||||||
if (recv_char == XMODEM_ACK) {
|
if (recv_char == XMODEM_ACK) {
|
||||||
break;
|
break;
|
||||||
} else if (recv_char == XMODEM_CAN) {
|
|
||||||
return XMODEM_ERR_CANCEL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
retries++;
|
retries++;
|
||||||
ack_fails++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sent += read_len;
|
sent += read_len;
|
||||||
packet_num++; /* expect overflow */
|
packet_num++; /* expect overflow */
|
||||||
status_cb(sent, sz, ack_fails);
|
status_cb(sent, sz);
|
||||||
}
|
}
|
||||||
|
|
||||||
end_of_transmission:
|
end_of_transmission:
|
||||||
|
|
@ -137,7 +134,7 @@ end_of_transmission:
|
||||||
return XMODEM_ERR_TIMEOUT;
|
return XMODEM_ERR_TIMEOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
serial_write(dest_fd, &(uint8_t){XMODEM_EOT}, 1);
|
write(dest_fd, &(uint8_t){XMODEM_EOT}, 1);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!_xmodem_wait(dest_fd, &ack_char, XMODEM_TIMEOUT) &&
|
!_xmodem_wait(dest_fd, &ack_char, XMODEM_TIMEOUT) &&
|
||||||
|
|
@ -153,12 +150,20 @@ end_of_transmission:
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
_xmodem_wait(serial_t fd, uint8_t *ret, int timeout_ms)
|
_xmodem_wait(int fd, uint8_t *ret, int timeout_ms)
|
||||||
{
|
{
|
||||||
if (serial_read_to(fd, ret, 1, timeout_ms) != 1) {
|
struct pollfd pfd;
|
||||||
|
|
||||||
|
pfd.fd = fd;
|
||||||
|
pfd.events = POLLIN;
|
||||||
|
pfd.revents = 0;
|
||||||
|
|
||||||
|
if (poll(&pfd, 1, timeout_ms) <= 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
read(fd, ret, 1);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,11 +30,11 @@
|
||||||
* length of `block_sz`.
|
* length of `block_sz`.
|
||||||
*/
|
*/
|
||||||
int xmodem_send(
|
int xmodem_send(
|
||||||
serial_t dest_fd,
|
int dest_fd,
|
||||||
int in_fd,
|
int in_fd,
|
||||||
size_t sz,
|
size_t sz,
|
||||||
int block_sz,
|
int block_sz,
|
||||||
void (*status_cb)(size_t sent, size_t total, int ack_fails)
|
void (*status_cb)(size_t sent, size_t total)
|
||||||
);
|
);
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue