diff --git a/.gitignore b/.gitignore index 662296d..ecbd5a1 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,4 @@ build/ .vscode/ .vim/ *.log -tags \ No newline at end of file +tags diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 8634b5a..0000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "lib/PDCurses"] - path = lib/PDCurses - url = git@github.com:wmcbrine/PDCurses.git diff --git a/LICENSE b/LICENSE deleted file mode 100644 index abf5af7..0000000 --- a/LICENSE +++ /dev/null @@ -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. diff --git a/Makefile b/Makefile index 02282fd..3a7376d 100644 --- a/Makefile +++ b/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_BUILD=build DIR_OBJ=$(DIR_BUILD)/obj DIR_BIN=$(DIR_BUILD)/bin -CFLAGS=-Wall -MMD -pthread -D_XOPEN_SOURCE=700 -D_POSIX_C_SOURCE=199309L -LIB_DEPS= -LIB_OBJS= +TARGET=$(DIR_BIN)/bytenuts + +DEBUG ?= 0 +ifeq ($(DEBUG), 1) + CFLAGS += -DDEBUG +endif SRCS=$(wildcard $(DIR_SRC)/*.c) OBJS=$(addprefix $(DIR_OBJ)/,$(addsuffix .o,$(basename $(SRCS)))) DEPS=$(addprefix $(DIR_OBJ)/,$(addsuffix .d,$(basename $(SRCS)))) -ifeq ($(OS), LINUX) - 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 +.PHONY: all install uninstall clean all: $(TARGET) @@ -43,19 +28,16 @@ install: uninstall: rm -f /usr/local/bin/bytenuts -$(TARGET): $(OBJS) $(LIB_DEPS) +$(TARGET): $(OBJS) @mkdir -p $(dir $@) @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 @mkdir -p $(dir $@) @echo "compile $<" @$(CC) $(CFLAGS) -c $< -o $@ -PDCurses: - cd lib/PDCurses/wincon && $(MAKE) -j CC=x86_64-w64-mingw32-gcc - clean: rm -rf $(DIR_BUILD) diff --git a/README.md b/README.md index a0c3a82..cebd00d 100644 --- a/README.md +++ b/README.md @@ -24,9 +24,21 @@ Bytenuts has been tested and built on x86-64 and 32-bit ARM Ubuntu 18. - 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 Screenshot](Screenshot.png) +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 @@ -68,12 +80,6 @@ Configs get loaded from ${HOME}/.bytenuts/config (if file exists) --escape= Change the default ctrl+b escape character. - ---inter_cmd_to= - Set the intercommand timeout in milliseconds (default is 10ms). - ---time_fmt= - Time format as used by strftime to prepend to every log line. ``` ## Navigation @@ -88,23 +94,19 @@ If the output window reaches the current line of output, then the output will co ## 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 `=`. Here is a sample config: +There are only three supported configs currently. Each config is defined like `=`. Here is a sample config: ``` colors=1 echo=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 - `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`) - `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`. @@ -124,24 +126,14 @@ The help prompt lists the available commands. Commands (lead with ctrl-b): c: print available quick commands 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 x: start XModem upload with 128B payloads X: start XModem upload with 1024B payloads - H: enter/exit hex buffer mode h: view this help 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 You can provide multiple pages of quick commands you can easily load in with `ctrl+b [0-9]` in the files `~/.config/bytenuts/commands`. Bytenuts will load pages starting from `commands1` until a `commands` 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. @@ -154,12 +146,10 @@ On startup, command page 1 is selected. You can select which command page is cur 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..log` and `~/.config/bytenuts/inbuf..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 Check out known bugs in the [issues tab](https://github.com/cookthebook/bytenuts/issues?q=is%3Aissue+is%3Aopen+label%3Abug). ## 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`. diff --git a/Screenshot.png b/Screenshot.png deleted file mode 100644 index 1b9c0a2..0000000 Binary files a/Screenshot.png and /dev/null differ diff --git a/lib/PDCurses b/lib/PDCurses deleted file mode 160000 index 5c62af0..0000000 --- a/lib/PDCurses +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 5c62af03e9a05e3a3ae8c8354c1234b772dcf4b0 diff --git a/src/bstr.c b/src/bstr.c deleted file mode 100644 index b508ad6..0000000 --- a/src/bstr.c +++ /dev/null @@ -1,117 +0,0 @@ -#include -#include -#include -#include - -#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); - } -} diff --git a/src/bstr.h b/src/bstr.h deleted file mode 100644 index b87585f..0000000 --- a/src/bstr.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef _BSTR_H_ -#define _BSTR_H_ - -#include -#include - -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_ */ diff --git a/src/bytenuts.c b/src/bytenuts.c index 7d8af20..54090df 100644 --- a/src/bytenuts.c +++ b/src/bytenuts.c @@ -1,14 +1,11 @@ -#ifdef __MINGW32__ -# include -#else -# include -#endif +#include #include #include #include #include #include #include +#include #include #include "bytenuts.h" @@ -28,15 +25,15 @@ "--colors=<0|1>\n Turn 8-bit ANSI colors off/on.\n\n" \ "--echo=<0|1>\n Turn input echoing off/on.\n\n" \ "--no_crlf=<0|1>\n Choose to send LF and not CRLF on input.\n\n" \ -"--escape=\n Change the default ctrl+b escape character.\n\n" \ -"--inter_cmd_to=\n Set the intercommand timeout in milliseconds (default is 10ms).\n\n" \ -"--time_fmt=\n Time format as used by strftime to prepend to every log line.\n" \ +"--escape=\n Change the default ctrl+b escape character.\n" \ ) static int parse_args(int argc, char **argv); static int load_configs(); static int read_state(); static int load_state(); +static speed_t string_to_speed(const char *speed); +static const char *speed_to_string(speed_t speed); static bytenuts_t bytenuts; @@ -59,23 +56,15 @@ bytenuts_run(int argc, char **argv) } bytenuts.serial_fd = serial_open(bytenuts.config.serial_path, bytenuts.config.baud); - if (bytenuts.serial_fd == SERIAL_INVALID) { - printf( - "Failed to open serial port \"%s\"\r\n", - bytenuts.config.serial_path - ); + if (bytenuts.serial_fd < 0) { return -1; } - printf("Opened \"%s\"\r\n", bytenuts.config.serial_path); - -#ifndef __MINGW32__ /* use pseudo-terminals for testing purposes */ if (!strcmp(bytenuts.config.serial_path, "/dev/ptmx")) { grantpt(bytenuts.serial_fd); unlockpt(bytenuts.serial_fd); } -#endif // Initialize ncurses initscr(); @@ -118,22 +107,20 @@ bytenuts_run(int argc, char **argv) cheerios_start(&bytenuts); ingest_start(&bytenuts); -#ifndef __MINGW32__ if (!strcmp(bytenuts.config.serial_path, "/dev/ptmx")) { char info[128]; snprintf(info, sizeof(info), "Opened PTY port %s", ptsname(bytenuts.serial_fd)); cheerios_info(info); } -#endif if (bytenuts.resume) { load_state(); } else { - cheerios_print( + cheerios_insert( "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 + "To quit press ctrl-b q\r\n" + "To see all commands, press ctrl-b h\r\n", + 82 ); } @@ -166,7 +153,7 @@ bytenuts_kill() delwin(bytenuts.out_win); endwin(); - serial_close(bytenuts.serial_fd); + close(bytenuts.serial_fd); } int @@ -182,7 +169,7 @@ bytenuts_print_stats() cheerios_insert(st_line, strlen(st_line)); sprintf(st_line, "escape: %c\r\n", bytenuts.config.escape); 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)); sprintf(st_line, "config_path: %s\r\n", bytenuts.config.config_path); cheerios_insert(st_line, strlen(st_line)); @@ -190,10 +177,6 @@ bytenuts_print_stats() cheerios_insert(st_line, strlen(st_line)); sprintf(st_line, "serial_path: %s\r\n", bytenuts.config.serial_path); 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; } @@ -275,8 +258,6 @@ bytenuts_set_status(int user, const char *fmt, ...) int bytenuts_update_screen_size() { - char *old_status = strdup(bytenuts.bytenuts_status); - pthread_mutex_lock(&bytenuts.term_lock); wresize(bytenuts.status_win, 1, COLS); @@ -293,10 +274,7 @@ bytenuts_update_screen_size() pthread_mutex_unlock(&bytenuts.term_lock); - bytenuts_set_status(STATUS_BYTENUTS, old_status); cheerios_insert("", 0); - ingest_refresh(); - free(old_status); return 0; } @@ -313,17 +291,10 @@ parse_args(int argc, char **argv) /* setup default options first */ 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 path[] = "/.config/bytenuts/config"; bytenuts.config.config_path = malloc(strlen(home) + strlen(path) + 1); sprintf(bytenuts.config.config_path, "%s%s", home, path); -#endif } for (int i = 1; i < argc - 1; i++) { @@ -337,7 +308,7 @@ parse_args(int argc, char **argv) if (i == argc - 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")) { i++; @@ -381,17 +352,6 @@ parse_args(int argc, char **argv) bytenuts.config.escape = argv[i][9]; 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; } @@ -439,28 +399,6 @@ load_configs() else if (!bytenuts.config_overrides[3] && !memcmp(line, "escape=", 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; @@ -471,14 +409,10 @@ read_state() { struct stat st; char *buf; - char *cwd; + char *cwd = getcwd(NULL, 0); char *home = getenv("HOME"); FILE *fd; - if (!home) - return 0; - - cwd = getcwd(NULL, 0); chdir(home); fd = fopen(".config/bytenuts/inbuf.log", "r"); @@ -539,3 +473,137 @@ load_state() return 0; } + +static speed_t +string_to_speed(const char *speed) +{ + if (!strcmp("50", speed)) + return B50; + else if (!strcmp("75", speed)) + return B75; + else if (!strcmp("110", speed)) + return B110; + else if (!strcmp("134", speed)) + return B134; + else if (!strcmp("150", speed)) + return B150; + else if (!strcmp("200", speed)) + return B200; + else if (!strcmp("300", speed)) + return B300; + else if (!strcmp("600", speed)) + return B600; + else if (!strcmp("1200", speed)) + return B1200; + else if (!strcmp("1800", speed)) + return B1800; + else if (!strcmp("2400", speed)) + return B2400; + else if (!strcmp("4800", speed)) + return B4800; + else if (!strcmp("9600", speed)) + return B9600; + else if (!strcmp("19200", speed)) + return B19200; + else if (!strcmp("38400", speed)) + return B38400; + else if (!strcmp("57600", speed)) + return B57600; + else if (!strcmp("115200", speed)) + return B1152000; + else if (!strcmp("230400", speed)) + return B230400; + else if (!strcmp("460800", speed)) + return B460800; + else if (!strcmp("500000", speed)) + return B500000; + else if (!strcmp("576000", speed)) + return B576000; + else if (!strcmp("921600", speed)) + return B921600; + 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 const char * +speed_to_string(speed_t speed) +{ + if (speed == B50) + return "B50"; + else if (speed == B75) + return "B75"; + else if (speed == B110) + return "B110"; + else if (speed == B134) + return "B134"; + else if (speed == B150) + return "B150"; + else if (speed == B200) + return "B200"; + else if (speed == B300) + return "B300"; + else if (speed == B600) + return "B600"; + else if (speed == B1200) + return "B1200"; + 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 ""; +} diff --git a/src/bytenuts.h b/src/bytenuts.h index 0bb0ce3..a8f470a 100644 --- a/src/bytenuts.h +++ b/src/bytenuts.h @@ -1,15 +1,10 @@ #ifndef _BYTENUTES_H_ #define _BYTENUTES_H_ -#ifdef __MINGW32__ -# include -#else -# include -#endif +#include #include #include #include -#include #include "serial.h" @@ -28,14 +23,10 @@ typedef struct bytenuts_config_struct { int echo; /* echo user input to the output window, default 0 */ int no_crlf; /* only send LF line endings, not CRLF, default 0 */ 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 *log_path; /* path to the log file (if it exists) */ 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; #define CONFIG_DEFAULT (bytenuts_config_t){ \ @@ -43,18 +34,16 @@ typedef struct bytenuts_config_struct { .echo = 0, \ .no_crlf = 0, \ .escape = 'b', \ - .baud = 115200, \ + .baud = B115200, \ .config_path = NULL, \ .log_path = NULL, \ .serial_path = NULL, \ - .inter_cmd_to = 10, \ - .time_fmt = NULL, \ } typedef struct bytenuts_struct { - serial_t serial_fd; + int serial_fd; bytenuts_config_t config; - int config_overrides[6]; + int config_overrides[4]; int resume; bytenuts_state_t state; WINDOW *status_win; diff --git a/src/cheerios.c b/src/cheerios.c index 40f1eff..1527a6d 100644 --- a/src/cheerios.c +++ b/src/cheerios.c @@ -1,8 +1,5 @@ -#ifdef __MINGW32__ -# include -#else -# include -#endif +#include +#include #include #include #include @@ -28,6 +25,7 @@ int cheerios_start(bytenuts_t *bytenuts) { char *home = getenv("HOME"); + char *cwd; memset(&cheerios, 0, sizeof(cheerios_t)); @@ -45,25 +43,11 @@ 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"); - } + cwd = getcwd(NULL, 0); + chdir(home); + cheerios.backup = fopen(".config/bytenuts/outbuf.log", "w"); + chdir(cwd); + free(cwd); pthread_cond_init(&cheerios.cond, NULL); pthread_mutex_init(&cheerios.lock, NULL); @@ -154,28 +138,31 @@ cheerios_stop() { cheerios.running = 0; pthread_join(cheerios.thr, NULL); + return 0; } int -cheerios_input(const char *buf, size_t len) +cheerios_input(const char *line) { - size_t len_left = len; - size_t p = 0; + size_t line_len = strlen(line); pthread_mutex_lock(&cheerios.lock); - while (len_left > 0) { - ssize_t tmp = serial_write(cheerios.ser_fd, &buf[p], len_left); - if (tmp < 0) { - pthread_mutex_unlock(&cheerios.lock); - return -1; - } - - len_left -= tmp; + if (cheerios.config->echo) { + char *line_parsed = calloc(1, line_len + 5 + 1); + sprintf(line_parsed, ">> %s\r\n", line); + insert_buf(&cheerios.lines, line_parsed, strlen(line_parsed)); + free(line_parsed); } - pthread_mutex_unlock(&cheerios.lock); + write(cheerios.ser_fd, line, strlen(line)); + if (cheerios.config->no_crlf) + write(cheerios.ser_fd, "\n", 1); + else + write(cheerios.ser_fd, "\r\n", 2); + + pthread_mutex_unlock(&cheerios.lock); return 0; } @@ -197,29 +184,6 @@ cheerios_info(const char *line) 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 cheerios_insert(const char *buf, size_t len) { @@ -232,18 +196,17 @@ cheerios_insert(const char *buf, size_t len) 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]; snprintf( status_str, sizeof(status_str), - "\r %.02f/%.02fKB (%.01f%%, %d ACK fails)", + "\r %.02f/%.02fKB (%.01f%%)", (sent / 1024.0), (total / 1024.0), - (sent * 100.0 / total), - ack_fails + (sent * 100.0 / total) ); cheerios_insert(status_str, strlen(status_str)); @@ -307,7 +270,6 @@ cheerios_getmaxy() { int y, x; getmaxyx(cheerios.output, y, x); - (void)x; return y; } @@ -316,7 +278,6 @@ cheerios_getmaxx() { int y, x; getmaxyx(cheerios.output, y, x); - (void)y; return x; } @@ -339,6 +300,7 @@ cheerios_thread(void *arg) { char buf[1024]; ssize_t read_ret = 0; + struct pollfd fds; while (cheerios.running) { pthread_mutex_lock(&cheerios.lock); @@ -347,7 +309,17 @@ cheerios_thread(void *arg) 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) { insert_buf(&cheerios.lines, buf, read_ret); } @@ -358,33 +330,8 @@ cheerios_thread(void *arg) if (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 */ + if (cheerios.backup) fclose(cheerios.backup); - rename(cheerios.backup_filename, out_filename); - - free(out_filename); - free(cheerios.backup_filename); - } - pthread_exit(NULL); return NULL; } @@ -559,17 +506,8 @@ handle_color(line_buffer_t *lines, int line_idx, int *pos, int apply) return 0; while (p < line_len) { - int fit_col = (p + 7) <= line_len; - int fit_en = (p + 4) <= line_len; - /* fg or bg color */ - if ( - fit_col && - ( - !memcmp(&line[p], "\e[38;5;", 7) || - !memcmp(&line[p], "\e[48;5;", 7) - ) - ) { + if (!memcmp(&line[p], "\e[38;5;", 7) || !memcmp(&line[p], "\e[48;5;", 7)) { char col_str[4] = { 0 }; short *col = line[p + 2] == '3' ? &fg : &bg; @@ -597,13 +535,13 @@ handle_color(line_buffer_t *lines, int line_idx, int *pos, int apply) p++; } /* enable our color */ - else if (fit_en && !memcmp(&line[p], "\e[1m", 4)) { + else if (!memcmp(&line[p], "\e[1m", 4)) { p += 4; enable = 1; break; } /* disable our color */ - else if (fit_en && !memcmp(&line[p], "\e[0m", 4)) { + else if (!memcmp(&line[p], "\e[0m", 4)) { p += 4; enable = 0; break; @@ -667,22 +605,5 @@ newline(line_buffer_t *lines) lines->line_lens = realloc(lines->line_lens, sizeof(int) * lines->n_lines); lines->line_lens[lines->n_lines - 1] = 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; } diff --git a/src/cheerios.h b/src/cheerios.h index 7e48360..a07a3a0 100644 --- a/src/cheerios.h +++ b/src/cheerios.h @@ -1,13 +1,8 @@ #ifndef _CHEERIOS_H_ #define _CHEERIOS_H_ -#ifdef __MINGW32__ -# include -#else -# include -#endif +#include #include -#include #include #include "bytenuts.h" @@ -37,11 +32,10 @@ typedef struct cheerios_struct { pthread_t thr; WINDOW *output; pthread_mutex_t *term_lock; - serial_t ser_fd; + int ser_fd; line_buffer_t lines; - FILE *log; /* log file which was opened with -l */ - char *backup_filename; /* realpath to the backup outbuf.pid.log */ - FILE *backup; /* backup log file object */ + FILE *log; + FILE *backup; bytenuts_config_t *config; volatile int mode; pthread_cond_t cond; @@ -67,17 +61,14 @@ int cheerios_gofwd(int lines); /* stop the thread and release memory */ int cheerios_stop(); -/* send over a line of user input to the output */ -int cheerios_input(const char *buf, size_t len); +/* send over a line of user input to the output + * do not terminate with a newline! */ +int cheerios_input(const char *line); /* output a line only to the terminal for info/prompt purposes * do not terminate with a newline! */ 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 */ int cheerios_insert(const char *buf, size_t len); diff --git a/src/ingest.c b/src/ingest.c index e417ee2..3b48079 100644 --- a/src/ingest.c +++ b/src/ingest.c @@ -1,9 +1,5 @@ #include -#ifdef __MINGW32__ -# include -#else -# include -#endif +#include #include #include #include @@ -12,12 +8,10 @@ #include #include #include -#include #include "bytenuts.h" #include "cheerios.h" #include "ingest.h" -#include "timer_math.h" static void *ingest_thread(void *arg); @@ -25,20 +19,12 @@ static ingest_t ingest; static int mode_normal(int ch); static int mode_xmodem(int block_sz); -static int mode_hex(int ch); static int handle_functions(int ch); -static int print_stats(void); -static int auto_complete(void); -static int read_cmd_page(const char *home_dir, int idx); -static void update_cmd_pg_status(void); -static void inter_command_wait(void); - -#ifndef KEY_CUP -# define KEY_CUP (566) -#endif -#ifndef KEY_CDOWN -# define KEY_CDOWN (525) -#endif +static int inbuf_load(); +static int print_stats(); +static int auto_complete(); +static int read_cmd_page(int idx); +static void update_cmd_pg_status(); int ingest_start(bytenuts_t *bytenuts) @@ -51,39 +37,22 @@ ingest_start(bytenuts_t *bytenuts) ingest.term_lock = &bytenuts->term_lock; ingest.config = &bytenuts->config; 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"); if (HOME) { + char *cwd = getcwd(NULL, 0); int idx = 0; - int name_len; - pid_t pid; - while (!read_cmd_page(HOME, idx)) { + chdir(HOME); + + while (!read_cmd_page(idx)) { idx++; } - /* ensure that a process has a unique log */ - pid = getpid(); - 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(".config/bytenuts/inbuf.log", "w"); - ingest.history_fd = fopen(ingest.history_filename, "w"); + chdir(cwd); + free(cwd); } if (ingest.cmd_pgs_n > 0) { @@ -91,10 +60,6 @@ ingest_start(bytenuts_t *bytenuts) } 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; if (pthread_create(&ingest.thr, NULL, ingest_thread, NULL)) { return -1; @@ -108,79 +73,9 @@ ingest_stop() { ingest.running = 0; pthread_join(ingest.thr, NULL); - 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); + if (ingest.history_fd) + fclose(ingest.history_fd); return 0; } @@ -223,6 +118,7 @@ ingest_set_history(char **history, int history_len) static void * ingest_thread(void *arg) { + int esc_flag; int ch; memset(ingest.inbuf, 0, sizeof(ingest.inbuf)); @@ -231,6 +127,8 @@ ingest_thread(void *arg) bytenuts_set_status(STATUS_INGEST, "normal"); while (ingest.running) { + esc_flag = 0; + pthread_mutex_lock(ingest.term_lock); ch = wgetch(ingest.input); pthread_mutex_unlock(ingest.term_lock); @@ -293,7 +191,7 @@ ingest_thread(void *arg) memcpy(ingest.inbuf, toload, toload_len); ingest.inpos = toload_len; ingest.inlen = toload_len; - ingest_refresh(); + inbuf_load(); break; } case 'c': @@ -387,29 +285,18 @@ ingest_thread(void *arg) ingest.mode = INGEST_MODE_XMODEM1K; bytenuts_set_status(STATUS_INGEST, "xmodem1k"); 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': - cheerios_print( - "Commands (lead with ctrl-%c):\r\n" + cheerios_insert( + "Commands (lead with ctrl-b):\r\n" " c: print available quick commands\r\n" " 0-9: load the given quick command (0 is 10)\r\n" " p: select a different quick commands page\r\n" " i: view info/stats\r\n" " x: start XModem upload with 128B 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" " q: quit Bytenuts\r\n", - ingest.config->escape + 313 ); bytenuts_set_status(STATUS_INGEST, "normal"); should_continue = 1; @@ -437,9 +324,6 @@ ingest_thread(void *arg) case INGEST_MODE_XMODEM1K: mode_xmodem(1024); break; - case INGEST_MODE_HEX: - mode_hex(ch); - break; default: break; } @@ -447,34 +331,6 @@ ingest_thread(void *arg) 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); return NULL; } @@ -486,22 +342,9 @@ mode_normal(int ch) return 0; switch (ch) { - case '\n': { /* send inputs */ - size_t inlen = strlen(ingest.inbuf); - const char *ending; - - 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); + case '\n': /* send inputs */ + if (cheerios_input(ingest.inbuf)) { + break; } /* store in history */ @@ -520,10 +363,9 @@ mode_normal(int ch) memset(ingest.inbuf, 0, sizeof(ingest.inbuf)); ingest.inpos = 0; ingest.inlen = 0; - ingest_refresh(); + inbuf_load(); break; - } case KEY_UP: /* load in history */ if (ingest.history_pos == 0) { /* top of history */ break; @@ -536,7 +378,7 @@ mode_normal(int ch) strcpy(ingest.inbuf, ingest.history[ingest.history_pos]); ingest.inlen = strlen(ingest.inbuf); ingest.inpos = ingest.inlen; - ingest_refresh(); + inbuf_load(); break; case KEY_DOWN: /* load in history */ @@ -556,7 +398,7 @@ mode_normal(int ch) ingest.inpos = ingest.inlen; } - ingest_refresh(); + inbuf_load(); break; default: @@ -579,7 +421,7 @@ mode_normal(int ch) ingest.inlen--; } - ingest_refresh(); + inbuf_load(); break; } @@ -600,7 +442,7 @@ mode_xmodem(int block_sz) if (ingest.prepend) free(ingest.prepend); ingest.prepend = strdup("Give me a path (ctrl-c to stop): "); - ingest_refresh(); + inbuf_load(); while (ingest.running) { int ch; @@ -616,8 +458,6 @@ mode_xmodem(int block_sz) switch (ch) { case '\n': - bstr_history_new_entry(ingest.xmodem_hist, ingest.inbuf); - pthread_mutex_lock(ingest.term_lock); werase(ingest.input); wmove(ingest.input, 0, 0); @@ -634,36 +474,8 @@ mode_xmodem(int block_sz) break; case '\t': /* tab complete */ auto_complete(); - ingest_refresh(); + inbuf_load(); 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: { char tmp[1024]; @@ -684,7 +496,7 @@ mode_xmodem(int block_sz) ingest.inlen--; } - ingest_refresh(); + inbuf_load(); break; } @@ -699,10 +511,8 @@ mode_xmodem(int block_sz) free(ingest.prepend); ingest.prepend = NULL; - bstr_history_unset_pos(ingest.xmodem_hist); - bytenuts_set_status(STATUS_INGEST, "normal"); - ingest_refresh(); + inbuf_load(); return 0; } } @@ -710,90 +520,6 @@ mode_xmodem(int block_sz) 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 handle_functions(int ch) { @@ -814,10 +540,10 @@ handle_functions(int ch) case KEY_SEND: /* go to front of log */ cheerios_gofwd(-1); return 1; - case KEY_CUP: /* ctrl + up arrow */ + case 566: /* ctrl + up arrow */ cheerios_goback(1); return 1; - case KEY_CDOWN: /* ctrl + down arrow */ + case 525: /* ctrl + down arrow */ cheerios_gofwd(1); return 1; case KEY_RESIZE: @@ -831,7 +557,7 @@ handle_functions(int ch) ingest.inpos--; ingest.inlen--; ingest.inbuf[ingest.inlen] = '\0'; - ingest_refresh(); + inbuf_load(); return 1; } @@ -843,7 +569,7 @@ handle_functions(int ch) ingest.inlen--; memcpy(&ingest.inbuf[ingest.inpos], tmp, tmp_len); ingest.inbuf[ingest.inlen] = '\0'; - ingest_refresh(); + inbuf_load(); return 1; } @@ -857,7 +583,7 @@ handle_functions(int ch) } else if (ingest.inpos == ingest.inlen - 1) { ingest.inbuf[ingest.inpos] = '\0'; ingest.inlen--; - ingest_refresh(); + inbuf_load(); return 1; } @@ -866,7 +592,7 @@ handle_functions(int ch) ingest.inlen--; memcpy(&ingest.inbuf[ingest.inpos], tmp, tmp_len); ingest.inbuf[ingest.inlen] = '\0'; - ingest_refresh(); + inbuf_load(); return 1; } @@ -874,21 +600,21 @@ handle_functions(int ch) if (ingest.inpos == 0) return 1; ingest.inpos--; - ingest_refresh(); + inbuf_load(); return 1; case KEY_RIGHT: if (ingest.inpos == ingest.inlen) return 1; ingest.inpos++; - ingest_refresh(); + inbuf_load(); return 1; case KEY_END: ingest.inpos = ingest.inlen; - ingest_refresh(); + inbuf_load(); return 1; case KEY_HOME: ingest.inpos = 0; - ingest_refresh(); + inbuf_load(); return 1; #if 0 case KEY_MOUSE: @@ -911,6 +637,80 @@ handle_functions(int ch) } } +static int +inbuf_load() +{ + 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; +} + static int print_stats() { @@ -1042,17 +842,13 @@ auto_complete_cleanup: } static int -read_cmd_page(const char *home_dir, int idx) +read_cmd_page(int idx) { char fname[128]; FILE *fd; char line[1024]; - snprintf( - fname, sizeof(fname), - "%s/.config/bytenuts/commands%d", - home_dir, idx+1 - ); + snprintf(fname, sizeof(fname), ".config/bytenuts/commands%d", idx+1); fd = fopen(fname, "r"); if (!fd) @@ -1091,19 +887,3 @@ update_cmd_pg_status() 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); -} diff --git a/src/ingest.h b/src/ingest.h index bb64216..d069827 100644 --- a/src/ingest.h +++ b/src/ingest.h @@ -1,23 +1,16 @@ #ifndef _INGEST_H_ #define _INGEST_H_ -#ifdef __MINGW32__ -# include -#else -# include -#endif +#include #include #include -#include -#include "bstr.h" #include "bytenuts.h" enum ingest_mode_enum { INGEST_MODE_NORMAL = 0, INGEST_MODE_XMODEM, INGEST_MODE_XMODEM1K, - INGEST_MODE_HEX, }; typedef struct ingest_struct { @@ -30,7 +23,6 @@ typedef struct ingest_struct { int inpos; /* cursor position within the inbuf itself */ int inlen; /* length of the input buffer */ char **history; /* array of strings representing the command history */ - char *history_filename; /* realpath of inbuf.pid.log */ FILE *history_fd; char tmp_history[4096]; /* copy of inbuf before we load in history */ int history_len; @@ -46,8 +38,6 @@ typedef struct ingest_struct { int cmd_pgs_n; /* what command page is currently selected */ int cmd_pg_cur; - struct timespec cmd_ts; /* last command sent timestamp */ - bstr_history_handle xmodem_hist; /* xmodem filename transfer history */ } ingest_t; /* startup the input window thread */ @@ -56,9 +46,6 @@ int ingest_start(bytenuts_t *bytenuts); /* stop the input window thread */ 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); diff --git a/src/main.c b/src/main.c index 6e1e64b..b75957c 100644 --- a/src/main.c +++ b/src/main.c @@ -4,20 +4,16 @@ #include "bytenuts.h" -#ifndef __MINGW32__ static void sigint_handler(int s); -#endif int main(int argc, char **argv) { -#ifndef __MINGW32__ struct sigaction act; act.sa_handler = sigint_handler; sigemptyset(&act.sa_mask); act.sa_flags = 0; sigaction(SIGINT, &act, NULL); -#endif if (bytenuts_run(argc, argv)) { bytenuts_stop(); @@ -27,11 +23,9 @@ main(int argc, char **argv) exit(0); } -#ifndef __MINGW32__ static void sigint_handler(int s) { bytenuts_kill(); exit(1); } -#endif diff --git a/src/paths.c b/src/paths.c deleted file mode 100644 index 7c4de57..0000000 --- a/src/paths.c +++ /dev/null @@ -1,99 +0,0 @@ -#include -#include -#include -#include - -#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); - } -} diff --git a/src/paths.h b/src/paths.h deleted file mode 100644 index 179fafe..0000000 --- a/src/paths.h +++ /dev/null @@ -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 file */ -char *paths_command_file(int idx); - -/* Generate a logfile path of the form ~/.config/bytenuts/[.].log - * PID only inserted if >0 */ -char *paths_logfile(const char *prefix, long long pid); - -#endif /* _PATHS_H_ */ diff --git a/src/serial.c b/src/serial.c index 1368584..364fcbe 100644 --- a/src/serial.c +++ b/src/serial.c @@ -2,158 +2,28 @@ #include #include #include +#include #include -#include -#include #include "serial.h" -#ifdef __MINGW32__ -# include - -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 -serial_close(serial_t serial) +serial_open(const char *path, speed_t bps) { - return (CloseHandle(serial) != 0); -} - -#else -# include -# include - -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; struct termios otio; struct termios ntio; fd = open(path, O_RDWR | O_NOCTTY | O_NDELAY); if (fd == -1) { - return SERIAL_INVALID; + return -1; } tcgetattr(fd, &otio); tcgetattr(fd, &ntio); - cfsetispeed(&ntio, speed); - cfsetospeed(&ntio, speed); + cfsetispeed(&ntio, bps); + cfsetospeed(&ntio, bps); /* https://www.cmrr.umn.edu/~strupp/serial.html */ /* 8N1 */ @@ -187,107 +57,3 @@ serial_open(const char *path, long bps) 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__ */ diff --git a/src/serial.h b/src/serial.h index eff7835..9478298 100644 --- a/src/serial.h +++ b/src/serial.h @@ -1,31 +1,8 @@ #ifndef _SERIAL_H_ #define _SERIAL_H_ -#ifdef __MINGW32__ -# include -typedef HANDLE serial_t; -#else -typedef int serial_t; -#endif +#include -#define SERIAL_INVALID (serial_t)(-1) - -/* 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); +int serial_open(const char *path, speed_t bps); #endif /* _SERIAL_H_ */ diff --git a/src/timer_math.c b/src/timer_math.c deleted file mode 100644 index bce2efd..0000000 --- a/src/timer_math.c +++ /dev/null @@ -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); -} diff --git a/src/timer_math.h b/src/timer_math.h deleted file mode 100644 index 99cdcd9..0000000 --- a/src/timer_math.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef _TIMER_MATH_H_ -#define _TIMER_MATH_H_ - -#include -#include - -/* 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_ */ diff --git a/src/xmodem.c b/src/xmodem.c index 3752184..e004422 100644 --- a/src/xmodem.c +++ b/src/xmodem.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -7,7 +8,7 @@ #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); 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 xmodem_send( - serial_t dest_fd, + int dest_fd, int in_fd, size_t 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; @@ -29,7 +30,6 @@ xmodem_send( uint8_t packet_num; int retries; int ret = 0; - int ack_fails = 0; if (block_sz != 128 && block_sz != 1024) { return -1; @@ -105,7 +105,7 @@ xmodem_send( 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; } @@ -115,17 +115,14 @@ xmodem_send( if (recv_char == XMODEM_ACK) { break; - } else if (recv_char == XMODEM_CAN) { - return XMODEM_ERR_CANCEL; } retries++; - ack_fails++; } sent += read_len; packet_num++; /* expect overflow */ - status_cb(sent, sz, ack_fails); + status_cb(sent, sz); } end_of_transmission: @@ -137,7 +134,7 @@ end_of_transmission: return XMODEM_ERR_TIMEOUT; } - serial_write(dest_fd, &(uint8_t){XMODEM_EOT}, 1); + write(dest_fd, &(uint8_t){XMODEM_EOT}, 1); if ( !_xmodem_wait(dest_fd, &ack_char, XMODEM_TIMEOUT) && @@ -153,12 +150,20 @@ end_of_transmission: } 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; } + read(fd, ret, 1); + return 0; } diff --git a/src/xmodem.h b/src/xmodem.h index c9a44ca..ecc0092 100644 --- a/src/xmodem.h +++ b/src/xmodem.h @@ -30,11 +30,11 @@ * length of `block_sz`. */ int xmodem_send( - serial_t dest_fd, + int dest_fd, int in_fd, size_t 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