blob: 45861a7e7bcf0912a0f7f8621132cb2da9a5ffef [file] [log] [blame]
# advice.tcl - Generic advice package.
# Copyright (C) 1998 Cygnus Solutions.
# Written by Tom Tromey <tromey@cygnus.com>.
# Please note that I adapted this from some code I wrote elsewhere,
# for non-Cygnus reasons. Don't complain to me if you see something
# like it somewhere else.
# Internal state.
defarray ADVICE_state
# This is a helper proc that does all the actual work.
proc ADVICE_do {command argList} {
global ADVICE_state
# Run before advice.
if {[info exists ADVICE_state(before,$command)]} {
foreach item $ADVICE_state(before,$command) {
# We purposely let errors in advice go uncaught.
uplevel $item $argList
}
}
# Run the command itself.
set code [catch \
[list uplevel \#0 $ADVICE_state(original,$command) $argList] \
result]
# Run the after advice.
if {[info exists ADVICE_state(after,$command)]} {
foreach item $ADVICE_state(after,$command) {
# We purposely let errors in advice go uncaught.
uplevel $item [list $code $result] $argList
}
}
# Return just as the original command would.
return -code $code $result
}
# Put some advice on a proc or command.
# WHEN says when to run the advice - `before' or `after' the
# advisee is run.
# WHAT is the name of the proc or command to advise.
# ADVISOR is the advice. It is passed the arguments to the advisee
# call as its arguments. In addition, `after' advisors are
# passed the return code and return value of the proc as their
# first and second arguments.
proc advise {when what advisor} {
global ADVICE_state
if {! [info exists ADVICE_state(original,$what)]} {
set newName [gensym]
rename $what $newName
set ADVICE_state(original,$what) $newName
# Create a new proc which just runs our internal command with the
# correct arguments.
uplevel \#0 [list proc $what args \
[format {ADVICE_do %s $args} $what]]
}
lappend ADVICE_state($when,$what) $advisor
}
# Remove some previously-set advice. Note that we could undo the
# `rename' when the last advisor is removed. This adds complexity,
# though, and there isn't much reason to.
proc unadvise {when what advisor} {
global ADVICE_state
if {[info exists ADVICE_state($when,$what)]} {
set newList {}
foreach item $ADVICE_state($when,$what) {
if {[string compare $advisor $item]} {
lappend newList $item
}
}
set ADVICE_state($when,$what) $newList
}
}