@@ -62,6 +62,9 @@ __bp_inside_preexec=0
6262# Initial PROMPT_COMMAND string that is removed from PROMPT_COMMAND post __bp_install
6363__bp_install_string=$' __bp_trap_string="$(trap -p DEBUG)"\n trap - DEBUG\n __bp_install'
6464
65+ # The command string that is registered to the DEBUG trap.
66+ __bp_trapdebug_string=' __bp_preexec_invoke_exec "$_"'
67+
6568# Fails if any of the given variables are readonly
6669# Reference https://stackoverflow.com/a/4441178
6770__bp_require_not_readonly () {
@@ -143,15 +146,19 @@ __bp_precmd_invoke_cmd() {
143146 return
144147 fi
145148 local __bp_inside_precmd=1
149+ __bp_invoke_precmd_functions " $__bp_last_ret_value " " $__bp_last_argument_prev_command "
150+ }
146151
152+ __bp_invoke_precmd_functions () {
153+ local __lastexit=$1 __lastarg=$2
147154 # Invoke every function defined in our function array.
148155 local precmd_function
149156 for precmd_function in " ${precmd_functions[@]} " ; do
150157
151158 # Only execute this function if it actually exists.
152159 # Test existence of functions with: declare -[Ff]
153160 if type -t " $precmd_function " 1> /dev/null; then
154- __bp_set_ret_value " $__bp_last_ret_value " " $__bp_last_argument_prev_command "
161+ __bp_set_ret_value " $__lastexit " " $__lastarg "
155162 # Quote our function invocation to prevent issues with IFS
156163 " $precmd_function "
157164 fi
@@ -244,32 +251,40 @@ __bp_preexec_invoke_exec() {
244251 return
245252 fi
246253
247- # Invoke every function defined in our function array.
254+ local preexec_ret_value
255+ __bp_invoke_preexec_functions " ${__bp_last_ret_value:- } " " $__bp_last_argument_prev_command " " $this_command "
256+
257+ # Restore the last argument of the last executed command, and set the return
258+ # value of the DEBUG trap to be the return code of the last preexec function
259+ # to return an error.
260+ # If `extdebug` is enabled a non-zero return value from any preexec function
261+ # will cause the user's command not to execute.
262+ # Run `shopt -s extdebug` to enable
263+ __bp_set_ret_value " $preexec_ret_value " " $__bp_last_argument_prev_command "
264+ }
265+
266+ # This function invokes every function defined in our function array. This
267+ # function assigns the last error exit status to the variable
268+ # `preexec_ret_value`. If there is no error, preexec_ret_value is set to `0`.
269+ __bp_invoke_preexec_functions () {
270+ local __lastexit=$1 __lastarg=$2 __this_command=$3
248271 local preexec_function
249272 local preexec_function_ret_value
250- local preexec_ret_value=0
273+ preexec_ret_value=0
251274 for preexec_function in " ${preexec_functions[@]:- } " ; do
252275
253276 # Only execute each function if it actually exists.
254277 # Test existence of function with: declare -[fF]
255278 if type -t " $preexec_function " 1> /dev/null; then
256- __bp_set_ret_value ${__bp_last_ret_value :- }
279+ __bp_set_ret_value " $__lastexit " " $__lastarg "
257280 # Quote our function invocation to prevent issues with IFS
258- " $preexec_function " " $this_command "
281+ " $preexec_function " " $__this_command "
259282 preexec_function_ret_value=" $? "
260283 if [[ " $preexec_function_ret_value " != 0 ]]; then
261284 preexec_ret_value=" $preexec_function_ret_value "
262285 fi
263286 fi
264287 done
265-
266- # Restore the last argument of the last executed command, and set the return
267- # value of the DEBUG trap to be the return code of the last preexec function
268- # to return an error.
269- # If `extdebug` is enabled a non-zero return value from any preexec function
270- # will cause the user's command not to execute.
271- # Run `shopt -s extdebug` to enable
272- __bp_set_ret_value " $preexec_ret_value " " $__bp_last_argument_prev_command "
273288}
274289
275290__bp_install () {
@@ -278,7 +293,7 @@ __bp_install() {
278293 return 1;
279294 fi
280295
281- trap ' __bp_preexec_invoke_exec "$_" ' DEBUG
296+ trap " $__bp_trapdebug_string " DEBUG
282297
283298 # Preserve any prior DEBUG trap as a preexec function
284299 local prior_trap=$( sed " s/[^']*'\(.*\)'[^']*/\1/" <<< " ${__bp_trap_string:-}" )
@@ -319,10 +334,13 @@ __bp_install() {
319334 fi ;
320335 PROMPT_COMMAND+=' __bp_interactive_mode'
321336
322- # Add two functions to our arrays for convenience
323- # of definition.
324- precmd_functions+=(precmd)
325- preexec_functions+=(preexec)
337+ # Add two functions to our arrays for convenience of definition only when
338+ # the functions have not yet added.
339+ if [[ ! ${__bp_installed_convenience_functions-} ]]; then
340+ __bp_installed_convenience_functions=1
341+ precmd_functions+=(precmd)
342+ preexec_functions+=(preexec)
343+ fi
326344
327345 # Invoke our two functions manually that were added to $PROMPT_COMMAND
328346 __bp_precmd_invoke_cmd
@@ -345,6 +363,29 @@ __bp_install_after_session_init() {
345363 PROMPT_COMMAND+=${__bp_install_string}
346364}
347365
366+ __bp_uninstall () {
367+ # Remove __bp_install hook from PROMPT_COMMAND
368+ if [[ ${PROMPT_COMMAND-} == * " $__bp_install_string " * ]]; then
369+ PROMPT_COMMAND=" ${PROMPT_COMMAND// $__bp_install_string [;$'\n']} " # Edge case of appending to PROMPT_COMMAND
370+ PROMPT_COMMAND=" ${PROMPT_COMMAND// $__bp_install_string } "
371+ fi
372+
373+ # Remove precmd hook from PROMPT_COMMAND
374+ PROMPT_COMMAND=${PROMPT_COMMAND/# $' __bp_precmd_invoke_cmd\n ' / $' \n ' }
375+ PROMPT_COMMAND=${PROMPT_COMMAND% $' \n __bp_interactive_mode' }
376+ PROMPT_COMMAND=${PROMPT_COMMAND# $' \n ' }
377+
378+ # Remove preexec hook in the DEBUG trap
379+ local q=" '" Q=" '\''"
380+ if [[ $( trap -p DEBUG) == " trap -- '${__bp_trapdebug_string// $q / $Q } ' DEBUG" ]]; then
381+ if [[ ${__bp_trap_string-} ]]; then
382+ eval -- " $__bp_trap_string "
383+ else
384+ trap - DEBUG
385+ fi
386+ fi
387+ }
388+
348389# Run our install so long as we're not delaying it.
349390if [[ -z " ${__bp_delay_install:- } " ]]; then
350391 __bp_install_after_session_init
0 commit comments