diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 3cd19cb7..718e705b 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -41,6 +41,7 @@ This document provides instructions to the copilot AI assistants and agents. ## Test instructions - Test code is stored in the `tests/src` directory. +- Use `valgrind` to check for memory leaks and memory corruption issues after code changes. - Tests can be run with `cd build && make test`. - Write backend tests for all bugs being fixed. - Use https://github.com/ThrowTheSwitch/Unity framework for testing. diff --git a/.github/workflows/build_ubuntu.yml.SKIP b/.github/workflows/build_ubuntu.yml similarity index 68% rename from .github/workflows/build_ubuntu.yml.SKIP rename to .github/workflows/build_ubuntu.yml index b468ce5b..73ff818b 100644 --- a/.github/workflows/build_ubuntu.yml.SKIP +++ b/.github/workflows/build_ubuntu.yml @@ -4,14 +4,14 @@ on: [push] jobs: build: - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 with: submodules: recursive - - name: Install packages - run: sudo apt-get update && sudo apt install -y build-essential qt5-default qt5-qmake ccache libncursesw5-dev libreadline-dev + - name: Install packages for Ubuntu 24.04 + run: sudo apt-get update && sudo apt install -y build-essential qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools ccache libncursesw5-dev libreadline-dev - name: Build lib and run unit tests using Unity test framework run: cd test && qmake hstr-unit-tests.pro && make clean all && ./hstr-unit-tests diff --git a/Changelog b/Changelog index bfd64add..26c4a826 100644 --- a/Changelog +++ b/Changelog @@ -1,8 +1,8 @@ -2026-01-24 Martin Dvorak +2026-01-26 Martin Dvorak * Released v3.2.0 - Minor release bringing: - - STDIN handling - - Configuration for TIOCSTI enforcement + - HSTR can newly read from pipe / stdin instead of just shell history + - Enforcement of the HSTR configuration in case of TIOCSTI is not available 2023-04-18 Martin Dvorak diff --git a/PKGBUILD b/PKGBUILD index 71cd6a2d..ef73e429 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -4,7 +4,7 @@ # Contributor: Busindre pkgname=hstr -pkgver=3.1 +pkgver=3.2 pkgrel=1 pkgdesc="A command line utility that brings improved BASH command completion from the history. It aims to make completion easier and more efficient than Ctrl-r." arch=('x86_64' diff --git a/README.md b/README.md index ce96ddb3..8a13a401 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![All releases downloads](https://img.shields.io/github/downloads/dvorka/hstr/total.svg)](https://github.com/dvorka/hstr/releases) [![License](https://img.shields.io/github/license/dvorka/hstr?color=%23fe0000)](https://github.com/dvorka/hstr/blob/master/LICENSE) -Easily view, navigate and search your **command history** with shell history suggest box for +Easily view, navigate and search your **command history** with shell history suggest box for [bash](https://www.gnu.org/software/bash/) and [zsh](CONFIGURATION.md#zsh-history-settings). [![Shell History Suggest Box](doc/hstr-v2.gif "Shell History Suggest Box @ YouTube")](http://www.youtube.com/watch?v=sPF29NyXe2U) @@ -27,6 +27,14 @@ or **bookmark** your favorite commands. +## Packages + +
+ + Packaging status + +
+ ## Installation Install: diff --git a/SECURITY.md b/SECURITY.md index db3fdfc5..4ad212cd 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -6,6 +6,7 @@ The following HSTR versions are currently being supported with security updates. | Version | Supported | | -------- | ------------------ | +| 3.2.0 | :white_check_mark: | | 3.1.0 | :white_check_mark: | | 3.0.0 | :white_check_mark: | | 2.6.0 | :white_check_mark: | diff --git a/build/debian/make-deb.sh b/build/debian/make-deb.sh index 434e7e09..f30b4ddb 100755 --- a/build/debian/make-deb.sh +++ b/build/debian/make-deb.sh @@ -147,8 +147,8 @@ then exit 1 fi -export ARG_BAZAAR_MSG="HSTR 3.1.0" -export ARG_VERSION="3.1.0" +export ARG_BAZAAR_MSG="HSTR 3.2.0" +export ARG_VERSION="3.2.0" # Debian releases: https://www.debian.org/releases/ # 6/7/8/9/10: squeeze wheezy jessie stretch buster diff --git a/build/fedora/rpm-from-deb.sh b/build/fedora/rpm-from-deb.sh index 3686d0e5..4feb4b4b 100755 --- a/build/fedora/rpm-from-deb.sh +++ b/build/fedora/rpm-from-deb.sh @@ -20,7 +20,7 @@ # This script is available from http://www.mindforger.com/fedora/fedora-rpm-from-deb.sh # to be easily available in VMs -export MFVERSION="3.1.0" +export MFVERSION="3.2.0" export MFPRJNAME="hstr-${MFVERSION}" export AMD64NAME="hstr_${MFVERSION}-1_amd64" export I386NAME="hstr_${MFVERSION}-1_i386" diff --git a/build/tarball/tarball-build.sh b/build/tarball/tarball-build.sh index 34689462..e3bf7fee 100755 --- a/build/tarball/tarball-build.sh +++ b/build/tarball/tarball-build.sh @@ -20,7 +20,7 @@ export SCRIPT_HOME=`pwd` -export HSTR_VERSION="3.1.0" +export HSTR_VERSION="3.2.0" export NOW=`date +%Y-%m-%d--%H-%M-%S` export GH_RELEASE_DIR=~/p/hstr/release diff --git a/build/ubuntu/launchpad-make-all-releases.sh b/build/ubuntu/launchpad-make-all-releases.sh index 49b82432..a6c2b9a6 100755 --- a/build/ubuntu/launchpad-make-all-releases.sh +++ b/build/ubuntu/launchpad-make-all-releases.sh @@ -163,8 +163,8 @@ then exit 1 fi -export ARG_BAZAAR_MSG="Release 3.1" -export ARG_MAJOR_VERSION=3.1. +export ARG_BAZAAR_MSG="Release 3.2" +export ARG_MAJOR_VERSION=3.2. export ARG_MINOR_VERSION=8 # minor version is incremented for every Ubuntu version # https://wiki.ubuntu.com/Releases diff --git a/doc/USER-DOCUMENTATION.md b/doc/USER-DOCUMENTATION.md index d1d861d0..2343a3ff 100644 --- a/doc/USER-DOCUMENTATION.md +++ b/doc/USER-DOCUMENTATION.md @@ -16,7 +16,7 @@ Table of contents: * Blacklist * Delete last command * Examples - + # Features HSTR is typically started by pressing Ctrl-r after you configure it for this shorcut in your shell. However, @@ -33,23 +33,35 @@ hh ``` It opens a page with a history view (ranking view is default). -Alternatively you can run HSTR in non-interactive mode - +Alternatively you can run HSTR in non-interactive mode - commands are just listed on standard output based on history view: ```bash -hh --non-interactive +hstr --non-interactive ``` Filtering pattern can be optionally specified as well: ```bash -hh -i git +hstr -i git ``` Prints history items containing `git` to standard output and exits. +HSTR can also read text input from a pipe instead of shell +history, allowing you to interactively browse and filter +any text data: + +```bash +ls | hstr +``` + +This opens HSTR with the output of `ls` command, which you +can filter and select from. Any command output can be piped +to HSTR this way. + ## History Filtering When HSTR starts in interative mode, a view of commands is shown. This list of commands can be filtered just by typing @@ -67,13 +79,13 @@ items is highlighted. Once you are done with your choice: ## Favorite Commands -HSTR allows you to store and manage your favorite -commands. +HSTR allows you to store and manage your favorite +commands. A new favorite command can be added from ranking or raw history view by pressing Ctrl-f. -You can check your favorite commands by choosing +You can check your favorite commands by choosing favorite view - rotate views using Ctrl-/ or start HSTR by adding `favorites-view` to `HSTR_CONFIG` environment property. A favorite command can be choosen just @@ -104,8 +116,8 @@ printf "\e[?2004l" # fix broken copy/paste in terminal #TERM ## Blacklist HSTR allows you to specify a set of commands to be skipped from all the views. Blacklist typically contains -frequently used commands whose completion from history -has a little or no value. The default blacklist looks +frequently used commands whose completion from history +has a little or no value. The default blacklist looks like this: ```bash @@ -125,7 +137,7 @@ Tips: * You can skip any command from history just by prefixing it with `SPACE`. For example: ` echo "Skip this from history"` It's a `bash` - option that is configured using + option that is configured using `HISTCONTROL=ignorespace` environment variable. @@ -148,8 +160,8 @@ Get more colors when running HSTR: export HSTR_CONFIG=hicolor ``` -Start HSTR in configured view and filter out history items -containing 'git' (make sure `hh` is defined using +Start HSTR in configured view and filter out history items +containing 'git' (make sure `hh` is defined using [alias](../CONFIGURATION.md#alias)): ```bash @@ -173,3 +185,9 @@ Check HSTR man page: ```bash man hstr ``` + +Browse output of any command interactively: + +```bash +ls -la | hh +``` diff --git a/hstr.pro b/hstr.pro index 32852a6a..beb2d928 100644 --- a/hstr.pro +++ b/hstr.pro @@ -62,6 +62,6 @@ hstrdebug { QMAKE_LINK = gcc # TIOCSTI debugging: -DEFINES += DEBUG_NO_TIOCSTI +# DEFINES += DEBUG_NO_TIOCSTI message(DEFINES of hstr.pro build: $$DEFINES) diff --git a/man/hstr.1 b/man/hstr.1 index 4bfed0f5..bf6c082c 100644 --- a/man/hstr.1 +++ b/man/hstr.1 @@ -271,6 +271,9 @@ bindkey -s "\eC\-r" "\eC\-a hstr \-\- \eC\-j" # bind hstr to Ctrl-r (for Vi mode .TP \fBhstr --insert-in-terminal="git add . && git diff --cached"\fR Insert command in terminal prompt and exit. +.TP +\fBls | hstr\fR + Read a text input from pipe and interactively browse it. .SH AUTHOR Written by Martin Dvorak .SH BUGS diff --git a/pad.xml b/pad.xml index 0dd53dd5..5e54b343 100644 --- a/pad.xml +++ b/pad.xml @@ -51,10 +51,10 @@ HSTR - 3.1.0 - 4 - 18 - 2023 + 3.2.0 + 5 + 9 + 2025 USD diff --git a/src/hashset.c b/src/hashset.c index 43ad91d4..4b87116b 100644 --- a/src/hashset.c +++ b/src/hashset.c @@ -135,7 +135,7 @@ void hashset_destroy(HashSet *hs, const bool freeValues) while(p && p->next) { if(p->key) { free(p->key); - if(freeValues && p->value) free(p->value); + if(freeValues) free(p->value); // free(NULL) is safe per C standard } pp=p; p=p->next; @@ -143,7 +143,7 @@ void hashset_destroy(HashSet *hs, const bool freeValues) } if(p && p->key) { free(p->key); - if(freeValues && p->value) free(p->value); + if(freeValues) free(p->value); free(p); } } diff --git a/src/hstr.c b/src/hstr.c index 12ad9a49..8551408d 100644 --- a/src/hstr.c +++ b/src/hstr.c @@ -215,6 +215,8 @@ static const char* HELP_STRING= // TODO help screen - curses window (tig) static const char* LABEL_HELP= "Type to filter, UP/DOWN move, RET/TAB select, DEL remove, C-f add favorite, C-g cancel"; +static const char* LABEL_HELP_PIPE= + "Type to filter, UP/DOWN move, RET/TAB select, C-g cancel"; #define GETOPT_NO_ARGUMENT 0 #define GETOPT_REQUIRED_ARGUMENT 1 @@ -274,10 +276,14 @@ typedef struct { int promptItems; bool noIoctl; + bool readingFromPipe; + HISTORY_STATE* pipeHistoryState; } Hstr; static Hstr* hstr; +static void free_history_state(HISTORY_STATE* historyState); + bool show_tiocsti_configuration_warning(void) { // If stdin or stderr is not a TTY, don't prompt interactively to avoid invisible hangs @@ -397,6 +403,8 @@ void hstr_init(void) =0; hstr->noIoctl=false; + hstr->readingFromPipe=false; + hstr->pipeHistoryState=NULL; } void hstr_destroy(void) @@ -406,8 +414,12 @@ void hstr_destroy(void) // blacklist is allocated by hstr struct blacklist_destroy(&hstr->blacklist, false); prioritized_history_destroy(hstr->history); - if(hstr->selection) free(hstr->selection); - if(hstr->selectionRegexpMatch) free(hstr->selectionRegexpMatch); + if(hstr->pipeHistoryState) { + free_history_state(hstr->pipeHistoryState); + hstr->pipeHistoryState = NULL; + } + free(hstr->selection); // free(NULL) is safe per C standard + free(hstr->selectionRegexpMatch); free(hstr); } @@ -768,7 +780,8 @@ void print_help_label(void) int cursorY=getcury(stdscr); char screenLine[CMDLINE_LNG]; - snprintf(screenLine, getmaxx(stdscr), "%s", LABEL_HELP); + const char* label = hstr->readingFromPipe ? LABEL_HELP_PIPE : LABEL_HELP; + snprintf(screenLine, getmaxx(stdscr), "%s", label); mvprintw(hstr->promptYHelp, 0, "%s", screenLine); clrtoeol(); refresh(); @@ -1281,7 +1294,8 @@ int remove_from_history_model(char* almostDead) // raw & ranked history is pruned first as its items point to system history lines int systemOccurences=0, rawOccurences=history_mgmt_remove_from_raw(almostDead, hstr->history); history_mgmt_remove_from_ranked(almostDead, hstr->history); - if(rawOccurences) { + // do NOT mutate system history when reading from pipe + if(rawOccurences && !hstr->readingFromPipe) { systemOccurences=history_mgmt_remove_from_system_history(almostDead); } if(systemOccurences!=rawOccurences && hstr->debugLevel>HSTR_DEBUG_LEVEL_NONE) { @@ -1295,6 +1309,11 @@ void hstr_next_view(void) { hstr->view++; hstr->view=hstr->view%3; + // skip favorites view when reading from pipe + if(hstr->readingFromPipe && hstr->view==HSTR_VIEW_FAVORITES) { + hstr->view++; + hstr->view=hstr->view%3; + } } void stdout_history_and_return(void) @@ -1333,7 +1352,7 @@ void hide_notification(void) } } -void loop_to_select(void) +void loop_to_select(bool ttyInit) { signal(SIGINT, signal_callback_handler_ctrl_c); signal(SIGQUIT, signal_callback_handler_ctrl_c); @@ -1344,7 +1363,11 @@ void loop_to_select(void) isSubshellHint=TRUE; } - hstr_curses_start(); + FILE *tty_in = hstr_curses_start(ttyInit); + if(ttyInit && tty_in==NULL) { + hstr_exit(EXIT_FAILURE); + } + // TODO move the code below to hstr_curses color_init_pair(HSTR_COLOR_NORMAL, -1, -1); if(hstr->theme & HSTR_THEME_COLOR) { @@ -1415,7 +1438,7 @@ void loop_to_select(void) // avoids printing of wild chars in search prompt break; case KEY_DC: // DEL - if(selectionCursorPosition!=SELECTION_CURSOR_IN_PROMPT) { + if(!hstr->readingFromPipe && selectionCursorPosition!=SELECTION_CURSOR_IN_PROMPT) { almostDead=getResultFromSelection(selectionCursorPosition, hstr, result); msg=malloc(strlen(almostDead)+1); strcpy(msg, almostDead); @@ -1672,13 +1695,14 @@ void loop_to_select(void) if(!isSubshellHint) { editCommand=TRUE; } else { - // Not setting editCommand to TRUE here, - // because else an unnecessary blank line gets emitted before returning to prompt. + // not setting editCommand to TRUE here, + // because else an unnecessary blank line + // gets emitted before returning to prompt } if(selectionCursorPosition!=SELECTION_CURSOR_IN_PROMPT) { result=getResultFromSelection(selectionCursorPosition, hstr, result); if(hstr->view==HSTR_VIEW_FAVORITES) { - favorites_choose(hstr->favorites,result); + favorites_choose(hstr->favorites, result); } } else { result=pattern; @@ -1721,18 +1745,26 @@ void loop_to_select(void) hstr_curses_stop(hstr->keepPage); if(result!=NULL) { + int fd = tty_in ? fileno(tty_in) : 0; + if(fixCommand) { - fill_terminal_input("fc \"", FALSE); + fill_terminal_input("fc \"", FALSE, fd); } - fill_terminal_input(result, editCommand); + fill_terminal_input(result, editCommand, fd); if(fixCommand) { - fill_terminal_input("\"", FALSE); + fill_terminal_input("\"", FALSE, fd); } if(executeResult) { // TODO w/o TIOCSTI the command is NOT executed, just shown in the prompt - fill_terminal_input("\n", FALSE); + fill_terminal_input("\n", FALSE, fd); } } + + // close the terminal input file opened when HSTR reads history from pipe + if(tty_in) { + fclose(tty_in); + tty_in=NULL; + } } // TODO protection from line overflow (snprinf) @@ -1756,17 +1788,94 @@ void hstr_assemble_cmdline_pattern(int argc, char* argv[], int startIndex) } } +static void free_history_state(HISTORY_STATE* historyState) +{ + if (!historyState) return; + + if (historyState->entries) { + for (int i = 0; i < historyState->length; i++) { + if (historyState->entries[i]) { + free(historyState->entries[i]->line); + free(historyState->entries[i]); + } + } + free(historyState->entries); + } + free(historyState); +} + void hstr_interactive(void) { - hstr->history=prioritized_history_create(hstr->bigKeys, hstr->blacklist.set); + HISTORY_STATE* historyState=NULL; + if(!isatty(fileno(stdin))) { + // load history (or alternative content) from stdin + hstr->readingFromPipe=true; + int MAX_STDIN_ENTRIES = 2000; + + historyState = malloc(sizeof(HISTORY_STATE)); + if (!historyState) { + fprintf(stderr, "Failed to allocate memory for history state\n"); + hstr_exit(EXIT_FAILURE); + } + memset(historyState, 0, sizeof(HISTORY_STATE)); + + historyState->entries = malloc(sizeof(HIST_ENTRY*) * MAX_STDIN_ENTRIES); + if (!historyState->entries) { + fprintf(stderr, "Failed to allocate memory for history entries\n"); + free_history_state(historyState); + hstr_exit(EXIT_FAILURE); + } + + char *line = NULL; + size_t len = 0; + ssize_t read; + historyState->length=0; + while ((read = getline(&line, &len, stdin)) != -1 && historyState->length < MAX_STDIN_ENTRIES) { + if (read > 0 && line[read-1] == '\n') { + line[read-1] = '\0'; + } + + historyState->entries[historyState->length] = malloc(sizeof(HIST_ENTRY)); + if (!historyState->entries[historyState->length]) { + fprintf(stderr, "Failed to allocate memory for history entry\n"); + free(line); + free_history_state(historyState); + hstr_exit(EXIT_FAILURE); + } + + historyState->entries[historyState->length]->line = strdup(line); + if (!historyState->entries[historyState->length]->line) { + fprintf(stderr, "Failed to allocate memory for history line\n"); + free(historyState->entries[historyState->length]); + free(line); + free_history_state(historyState); + hstr_exit(EXIT_FAILURE); + } + + historyState->entries[historyState->length]->timestamp = NULL; + historyState->entries[historyState->length]->data = NULL; + + historyState->length++; + } + free(line); // free(NULL) is safe per C standard + } + + hstr->history=prioritized_history_create(hstr->bigKeys, hstr->blacklist.set, historyState); + + if(hstr->readingFromPipe) { + hstr->pipeHistoryState=historyState; + } + if(hstr->history) { history_mgmt_open(); if(hstr->interactive) { - loop_to_select(); + loop_to_select(hstr->readingFromPipe); } else { stdout_history_and_return(); } - history_mgmt_flush(); + if(!hstr->readingFromPipe) { + history_mgmt_flush(); + } } // else (no history) handled in create() method hstr_exit(EXIT_SUCCESS); @@ -1798,7 +1907,7 @@ void hstr_getopt(int argc, char **argv) hstr_exit(EXIT_SUCCESS); break; case 'i': - fill_terminal_input(optarg, FALSE); + fill_terminal_input(optarg, FALSE, 0); hstr_exit(EXIT_SUCCESS); break; case 'V': diff --git a/src/hstr_curses.c b/src/hstr_curses.c index e7df4940..3b87bebf 100644 --- a/src/hstr_curses.c +++ b/src/hstr_curses.c @@ -1,6 +1,5 @@ /* hstr_curses.c Curses utilities - http://pueblo.sourceforge.net/doc/manual/ansi_color_codes.html Copyright (C) 2014-2026 Martin Dvorak @@ -19,11 +18,33 @@ #include "include/hstr_curses.h" +// http://pueblo.sourceforge.net/doc/manual/ansi_color_codes.html static bool terminalHasColors=FALSE; -void hstr_curses_start(void) -{ - initscr(); +FILE* hstr_curses_start(bool ttyInit) { + FILE *tty_in = NULL; + + // if HSTR is reading input from pipe, then ncurses must be initialized with /dev/tty + if(ttyInit) { + SCREEN *screen; + + tty_in = fopen("/dev/tty", "r+"); + if (!tty_in) { + perror("Could not open /dev/tty prior to curses initialization"); + return NULL; + } + + screen = newterm(NULL, stdout, tty_in); + if (!screen) { + fprintf(stderr, "Error initializing curses when HSTR is reading from pipe"); + fclose(tty_in); + return NULL; + } + set_term(screen); + } else { + initscr(); + } + keypad(stdscr, TRUE); noecho(); nonl(); // prevent carriage return from being mapped to newline @@ -42,6 +63,8 @@ void hstr_curses_start(void) */ set_escdelay(5); #endif + + return tty_in; } bool terminal_has_colors(void) { diff --git a/src/hstr_history.c b/src/hstr_history.c index b45ab36f..e001c32c 100644 --- a/src/hstr_history.c +++ b/src/hstr_history.c @@ -159,13 +159,16 @@ bool history_mgmt_load_history_file(void) return true; } -HistoryItems* prioritized_history_create(int optionBigKeys, HashSet *blacklist) +HistoryItems* prioritized_history_create(int optionBigKeys, HashSet *blacklist, HISTORY_STATE* historyState) { - using_history(); - if(!history_mgmt_load_history_file()) { - return NULL; + if(!historyState) { + using_history(); + if(!history_mgmt_load_history_file()) { + return NULL; + } + + historyState=history_get_history_state(); } - HISTORY_STATE* historyState=history_get_history_state(); if(historyState->length > 0) { HashSet rankmap; @@ -178,7 +181,7 @@ HistoryItems* prioritized_history_create(int optionBigKeys, HashSet *blacklist) RankedHistoryItem *r; RadixItem *radixItem; - HIST_ENTRY **historyList=history_list(); + HIST_ENTRY **historyList=historyState ? historyState->entries : history_list(); char **rawHistory=malloc(sizeof(char*) * historyState->length); int rawOffset=historyState->length-1, rawTimestamps=0; char *line; @@ -258,12 +261,10 @@ HistoryItems* prioritized_history_create(int optionBigKeys, HashSet *blacklist) radixsort_destroy(&rs); // history/readline cleanup, clear_history() called on exit as entries are used by raw view - free(historyState); return prioritizedHistory; } else { // history/readline cleanup, clear_history() called on exit as entries are used by raw view printf("No history - nothing to suggest...\n"); - free(historyState); return NULL; } @@ -406,6 +407,6 @@ int history_mgmt_remove_from_ranked(char *cmd, HistoryItems *history) { void history_mgmt_flush(void) { if(dirty && !is_zsh_parent_shell()) { - fill_terminal_input("history -r\n", false); + fill_terminal_input("history -r\n", false, 0); } } diff --git a/src/hstr_utils.c b/src/hstr_utils.c index 27840b68..6fc97080 100644 --- a/src/hstr_utils.c +++ b/src/hstr_utils.c @@ -157,27 +157,33 @@ void tiocsti(void) } #endif -void fill_terminal_input(char* cmd, bool padding) +void fill_terminal_input(char* cmd, bool padding, int fd) { if(cmd && strlen(cmd)>0) { - if(is_tiocsti) { + if(is_tiocsti) { size_t size = strlen(cmd); unsigned i; char *c; for (i = 0; i < size; i++) { // terminal I/O control, simulate terminal input c=(cmd+i); - ioctl(0, TIOCSTI, c); + ioctl(fd, TIOCSTI, c); } // echo, but don't flush to terminal - if(padding) printf("\n"); + if(padding) { + printf("\n"); + } } else { + // non-TIOCSTI mode: always write to stderr (shell wrapper captures it) fprintf(stderr, "%s", cmd); - if(padding) fprintf(stderr, "%s", "\n"); + if(padding) { + fprintf(stderr, "%s", "\n"); + } } } } + void reverse_char_pointer_array(char **array, unsigned length) { char *temp; diff --git a/src/include/hstr_curses.h b/src/include/hstr_curses.h index 8081a2ba..cdd070bc 100644 --- a/src/include/hstr_curses.h +++ b/src/include/hstr_curses.h @@ -31,7 +31,7 @@ #define color_attr_off(C) if(terminal_has_colors()) { attroff(C); } #define color_init_pair(X, Y, Z) if(terminal_has_colors()) { init_pair(X, Y, Z); } -void hstr_curses_start(void); +FILE* hstr_curses_start(bool ttyInit); bool terminal_has_colors(void); void hstr_curses_stop(bool keepPage); diff --git a/src/include/hstr_history.h b/src/include/hstr_history.h index 4875d4cb..7179c5f7 100644 --- a/src/include/hstr_history.h +++ b/src/include/hstr_history.h @@ -48,7 +48,7 @@ typedef struct { } HistoryItems; char* parse_history_line(char *l); -HistoryItems* prioritized_history_create(int optionBigKeys, HashSet* blacklist); +HistoryItems* prioritized_history_create(int optionBigKeys, HashSet* blacklist, HISTORY_STATE* historyState); void prioritized_history_destroy(HistoryItems* h); void history_mgmt_open(void); diff --git a/src/include/hstr_utils.h b/src/include/hstr_utils.h index ba954e78..53d81812 100644 --- a/src/include/hstr_utils.h +++ b/src/include/hstr_utils.h @@ -89,7 +89,7 @@ bool is_tiocsti_supported(void); #if !defined(__MS_WSL__) && !defined(__CYGWIN__) void tiocsti(void); #endif -void fill_terminal_input(char* cmd, bool padding); +void fill_terminal_input(char* cmd, bool padding, int fd); void reverse_char_pointer_array(char** array, unsigned length); void get_hostname(int bufferSize, char* buffer); char* get_home_file_path(char* filename);