Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions man/hstr.1
Original file line number Diff line number Diff line change
Expand Up @@ -231,13 +231,21 @@ setopt histignorespace # skip cmds w/ leading space from history
export HSTR_CONFIG=hicolor # get more colors
hstr_no_tiocsti() {
zle -I
{ HSTR_OUT="$( { </dev/tty hstr -- ${BUFFER}; } 2>&1 1>&3 3>&- )"; } 3>&1;
BUFFER="${HSTR_OUT}"
CURSOR=${#BUFFER}
zle redisplay
{ HSTR_OUT="$( { </dev/tty hstr -- ${BUFFER}; echo -n x >&2; } 2>&1 1>&3 3>&-; )"; } 3>&1;
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This snippet uses echo -n for the stderr sentinel; echo option handling is not portable across shells/options. Prefer a reliable primitive like printf %s x >&2 (or zsh’s print -n -- x >&2) in the documentation example as well.

Suggested change
{ HSTR_OUT="$( { </dev/tty hstr -- ${BUFFER}; echo -n x >&2; } 2>&1 1>&3 3>&-; )"; } 3>&1;
{ HSTR_OUT="$( { </dev/tty hstr -- ${BUFFER}; printf %s x >&2; } 2>&1 1>&3 3>&-; )"; } 3>&1;

Copilot uses AI. Check for mistakes.
HSTR_OUT="${HSTR_OUT%x}"
if [[ "${HSTR_OUT}" == *$'\\n' ]]; then
BUFFER="${HSTR_OUT%$'\\n'}"
CURSOR=${#BUFFER}
zle redisplay
zle accept-line
else
BUFFER="${HSTR_OUT}"
CURSOR=${#BUFFER}
zle redisplay
fi
}
zle -N hstr_no_tiocsti
bindkey '\C-r' hstr_no_tiocsti
bindkey '\\C-r' hstr_no_tiocsti
export HSTR_TIOCSTI=n
.sp
.fi
Expand Down
17 changes: 13 additions & 4 deletions src/hstr.c
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,7 @@ void print_bash_install_code(void)
#endif
);
printf("\nexport HSTR_TIOCSTI=n");
// note: due to Bash 'bind -x' limitations, users need to press ENTER twice on row insertion
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The note about Bash needing ENTER twice is currently a C comment, so it will not appear in the generated --show-bash-configuration output. If this is intended for users, emit it as a # NOTE: line in the printed shell snippet (and/or document it in the manpage); otherwise remove the comment to avoid confusion.

Suggested change
// note: due to Bash 'bind -x' limitations, users need to press ENTER twice on row insertion
printf("\n# NOTE: Due to Bash 'bind -x' limitations, you may need to press ENTER twice after inserting a line from hstr");

Copilot uses AI. Check for mistakes.
}

printf("\n\n");
Expand All @@ -485,10 +486,18 @@ void print_zsh_install_code(void)
#endif

"\n zle -I"
"\n { HSTR_OUT=\"$( { </dev/tty hstr -- ${BUFFER}; } 2>&1 1>&3 3>&- )\"; } 3>&1;"
"\n BUFFER=\"${HSTR_OUT}\""
"\n CURSOR=${#BUFFER}"
"\n zle redisplay"
"\n { HSTR_OUT=\"$( { </dev/tty hstr -- ${BUFFER}; echo -n x >&2; } 2>&1 1>&3 3>&-; )\"; } 3>&1;"
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The generated Zsh snippet uses echo -n to emit a sentinel to stderr. echo option handling is not portable and can vary by shell/options; use a more reliable primitive (e.g. printf %s x >&2 or zsh’s print -n -- x >&2) for the sentinel.

Suggested change
"\n { HSTR_OUT=\"$( { </dev/tty hstr -- ${BUFFER}; echo -n x >&2; } 2>&1 1>&3 3>&-; )\"; } 3>&1;"
"\n { HSTR_OUT=\"$( { </dev/tty hstr -- ${BUFFER}; printf %s x >&2; } 2>&1 1>&3 3>&-; )\"; } 3>&1;"

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using echo -n as the sentinel here can be unreliable in zsh depending on options (e.g., BSD_ECHO/SH_ECHO) and could break the trailing-newline detection logic. Consider a more deterministic sentinel emitter so HSTR_OUT always ends with the marker exactly once.

Other Locations
  • man/hstr.1:235
  • test/sh/tiotcsi-function-zsh.sh:105

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎

"\n HSTR_OUT=\"${HSTR_OUT%%x}\""
"\n if [[ \"${HSTR_OUT}\" == *$'\\n' ]]; then"
"\n BUFFER=\"${HSTR_OUT%%$'\\n'}\""
"\n CURSOR=${#BUFFER}"
"\n zle redisplay"
"\n zle accept-line"
"\n else"
"\n BUFFER=\"${HSTR_OUT}\""
"\n CURSOR=${#BUFFER}"
"\n zle redisplay"
"\n fi"
"\n}"

#if defined(__MS_WSL__)
Expand Down
30 changes: 26 additions & 4 deletions test/sh/tiotcsi-function-bash.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
#!/usr/bin/env bash
# This script is used to experiment with Bash functions in order to set
#
# Copyright (C) 2014-2026 Martin Dvorak <martin.dvorak@mindforger.com>
#
# 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.
#

# This script is used to experiment with Bash functions in order to set
# the content of the terminal prompt without TIOCSTI ioctl() call.
#
# IMPORTANT: to use the functions below @ Bash
Expand Down Expand Up @@ -42,7 +58,7 @@ function mysimple {
# HSTR prints out command which user selected after using ^
# ^ is set to READLINE_LINE
# prompt cursor is set to the end of line by setting READLINE_POINT to 1000 (blindly)

{ READLINE_LINE=$(foohstr ${PROMPT_STR_BEFORE}) 2>&1; }
# move cursor to the end of prompt
READLINE_POINT=${#READLINE_LINE}
Expand All @@ -60,7 +76,7 @@ function hstrdebug {
echo "Readline point: '${READLINE_POINT}'"

READLINE_POINT=0

# OBSERVATIONS:
# {hstrout}>&1 ... is VALID expression
# {hstrout} > &1 ... is INVALID expression
Expand Down Expand Up @@ -91,7 +107,7 @@ function hstrdebug {
#

# bind '"\C-r": "\C-a hstr -- \C-j"'
# - bind
# - bind
# ... Bash command which binds key sequence (Ctrl-r) to a command,
# bind INSERTS the command to the terminal
# - "\C-r"
Expand Down Expand Up @@ -195,4 +211,10 @@ function hstrnotiocsti {
if [[ $- =~ .*i.* ]]; then bind -x '"\C-r": "hstrnotiocsti"'; fi
export HSTR_TIOCSTI=n

# NOTE: Due to bash 'bind -x' limitations, commands cannot be auto-executed
# from the bind function. You need to press ENTER twice: once to select
# in HSTR, and once more to execute the command. This is a known bash
# limitation that cannot be worked around. ZSH users can use 'zle accept-line'
# to auto-execute commands.

# eof
39 changes: 31 additions & 8 deletions test/sh/tiotcsi-function-zsh.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,19 @@
#!/usr/bin/env bash
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The script is labeled as a Zsh example and uses ZLE/bindkey, but the shebang specifies Bash. Executing this file directly will fail under Bash; consider changing the shebang to zsh (or removing the shebang if it’s meant to be sourced from an existing zsh session).

Suggested change
#!/usr/bin/env bash
#!/usr/bin/env zsh

Copilot uses AI. Check for mistakes.
#
# Copyright (C) 2014-2026 Martin Dvorak <martin.dvorak@mindforger.com>
#
# 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.
#

# ##################################################################
# EXAMPLE: NOT WORKING version
Expand Down Expand Up @@ -26,16 +41,16 @@ foohstr() {

hstrnotiocsti() {
local word
# we need the WHOLE buffer, not just 0 to cursor: word=${BUFFER[0,CURSOR]}
# we need the WHOLE buffer, not just 0 to cursor: word=${BUFFER[0,CURSOR]}
BUFFER="$(foohstr ${BUFFER})"
CURSOR=${#BUFFER}
# update the display
# update the display
zle redisplay
}

# create ZLE widget ~ readline function
# create ZLE widget ~ readline function
zle -N hstrnotiocsti
# bind widget to keyboard shortcut
# bind widget to keyboard shortcut
bindkey '\C-r' hstrnotiocsti

# ##################################################################
Expand Down Expand Up @@ -87,10 +102,18 @@ export HSTR_TIOCSTI=n

hstr_no_tiocsti() {
zle -I
{ HSTR_OUT="$( { </dev/tty hstr ${BUFFER}; } 2>&1 1>&3 3>&- )"; } 3>&1;
BUFFER="${HSTR_OUT}"
CURSOR=${#BUFFER}
zle redisplay
{ HSTR_OUT="$( { </dev/tty hstr ${BUFFER}; echo -n x >&2; } 2>&1 1>&3 3>&-; )"; } 3>&1;
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This invocation is missing the -- end-of-options marker (unlike the generated config and manpage). If the current BUFFER begins with a dash (e.g. -n), hstr will parse it as an option rather than a search term; add -- before ${BUFFER} here as well.

Copilot uses AI. Check for mistakes.
HSTR_OUT="${HSTR_OUT%x}"
if [[ "${HSTR_OUT}" == *$'\n' ]]; then
BUFFER="${HSTR_OUT%$'\n'}"
CURSOR=${#BUFFER}
zle redisplay
zle accept-line
else
BUFFER="${HSTR_OUT}"
CURSOR=${#BUFFER}
zle redisplay
fi
}
zle -N hstr_no_tiocsti
bindkey '\C-r' hstr_no_tiocsti
Expand Down