@@ 45,18 45,33 @@ if [ -n "$BASH_VERSION" ]; then
trap '_kh_last_cmd=$(fc -ln -1 2>/dev/null | sed "s/^[[:space:]]*//")' DEBUG
PROMPT_COMMAND='_kh_log "$_kh_last_cmd" "$?"'
else
- # mksh: no DEBUG trap / PROMPT_COMMAND. Log from PS1 each prompt.
- # The leading $(...) captures $? first, so the exit code is accurate,
- # and _kh_prompt emits nothing so the visible prompt is unchanged.
- # Skip the first render: at shell start fc -ln -1 is a stale HISTFILE
- # entry, not a command run this session.
- _kh_first_prompt=1
+ # mksh: no DEBUG trap / PROMPT_COMMAND, and PS1 command substitution
+ # runs in a subshell, so hook state cannot live in shell variables --
+ # it is persisted to a per-PID file instead. We detect that a new
+ # command ran by watching HISTFILE grow, then read the last entry
+ # straight from the file (fc in the PS1 subshell is lagged by one and
+ # would drop the final command of every session).
+ #
+ # mksh HISTFILE record layout: 0xFF, 4-byte big-endian counter,
+ # command bytes, NUL. So split on 0xFF, take the last record, drop the
+ # 4 counter bytes (cut -b5-), strip the trailing NUL.
+ [ -d "$HOME/.cache" ] || mkdir -p "$HOME/.cache"
+ _kh_state="$HOME/.cache/kh_mksh_$$"
_kh_prompt() {
- if [ -n "$_kh_first_prompt" ]; then
- unset _kh_first_prompt
- return
- fi
- _kh_log "$(fc -ln -1 2>/dev/null | sed 's/^[[:space:]]*//')" "$1"
+ typeset ec="$1" sz prev cmd
+ [ -f "$HISTFILE" ] || return
+ sz=$(wc -c < "$HISTFILE" 2>/dev/null)
+ prev=$(cat "$_kh_state" 2>/dev/null)
+ print -r -- "$sz" > "$_kh_state"
+ # first prompt of the session: record baseline, do not log
+ [ -z "$prev" ] && return
+ # only log when a new command was appended to history
+ [ "$sz" -gt "$prev" ] 2>/dev/null || return
+ cmd=$(tail -c 4096 "$HISTFILE" | tr "\377" "\n" | tail -n1 | cut -b5- | tr -d "\0")
+ _kh_log "$cmd" "$ec"
}
+ # leading $(...) captures $? first so the exit code is accurate; the
+ # hook prints nothing so the visible prompt is unchanged
PS1='$(_kh_prompt "$?")'"$PS1"
+ trap 'rm -f "$_kh_state"' EXIT
fi