I am trying to use a TCL program to read TCL modulefiles and translate them into another language. This has worked quite well until now. For reasons too complicated explain I have to treat "puts stderr" differently in different parts of the modulefile. I am asking for help in trying to figure out a way to do this.
Below is an extremely abbreviated modulefile called "modfile". This "modfile" is translated or "sourced" by a second tcl program.
proc ModulesHelp { } {
puts stderr "(1) This is a help message"
}
puts stderr "(2) Here in modfile"
The puts statement inside ModulesHelp has to be treated differently from the second puts statement. Note that any solution CAN NOT CHANGE "modfile". That file is not under my control.
Here is my attempt at a solution:
#!/usr/bin/env tclsh
proc myPuts { stream msg } {
global putMode
puts stdout "putMode: $putMode" # <====== HERE 1
puts stdout $msg
}
proc report { message } {
puts stderr "$message"
}
proc execute-modulefile { m } {
global MODFILE putMode
set putMode "normal"
set slave "__mod"
interp create $slave
interp alias $slave puts {} myPuts
interp alias $slave report {} report
interp eval $slave {global putMode }
interp eval $slave [list "set" "putMode" $putMode]
interp eval $slave [list "set" "m" $m]
set errorVal [interp eval $slave {
set sourceFailed [catch {source $m } errorMsg]
if {[info procs "ModulesHelp"] == "ModulesHelp" } {
set putMode "InHelp" # <======= HERE 2
ModulesHelp
}
if {$sourceFailed} {
report $errorMsg
return 1
}
}]
interp delete $slave
return $errorVal
}
eval execute-modulefile $argv
To run this I do: $ ./try.tcl modfile where obviously the above script is "try.tcl" and the modulefile is "modfile". I am running this on a linux system with tcl 8.4.
What I would like to have happen is that at the line labelled "HERE 2" I like to somehow change the global variable of "putMode" from "normal" to "InHelp" so that I can change the behavior at the line labelled "HERE 1". No matter what I have tried to do I can not change the value of putMode at "HERE 1" by doing something at "HERE 2". The puts statement at "HERE1 always says "normal".
Using a global variable seems like the easiest solution but if someone could show me how to use namespaces or some other technique, I'll be happy with that as well.
Thanks for any insight.
I greatly appreciate the time that others have looked at my question. I am trying to use the proposed solution and I'm not quite seeing it. Here is my new attempt at a solution (This doesn't work at all). Can someone suggest how I modify this code to change "putMode" to inHelp where "HERE 2" is? Also is there something special that needs to go where "HERE 1" is?
#!/usr/bin/env tclsh
proc myPuts { stream msg } {
global putMode
puts stdout "putMode: $putMode" # <=== HERE 1
puts stdout $msg
}
proc report { message } {
puts stderr "$message"
}
proc PutModeTrace {childInterp operation realPutMode} {
# Alias the main array element for the purposes of this procedure
upvar \#0 PutMode($childInterp) realPutMode
if {$operation eq "read"} {
interp eval $childInterp [list set putMode $realPutMode]
} elseif {$operation eq "write"} {
set realPutMode [interp eval $childInterp {set putMode}]
}
}
proc execute-modulefile { m } {
global MODFILE putMode
set putMode "normal"
set slave [interp create]
interp alias $slave puts {} myPuts
interp alias $slave report {} report
interp eval $slave {global putMode }
interp eval $slave [list "set" "putMode" $putMode]
interp eval $slave [list "set" "m" $m]
interp eval $slave [list "set" "slave" $slave]
interp eval $slave {trace add variable putMode {read write} PutModeTrace}
interp alias $slave PutModeTrace {} PutModeTrace $slave
set errorVal [interp eval $slave {
set sourceFailed [catch {source $m } errorMsg]
if {[info procs "ModulesHelp"] == "ModulesHelp" } {
set start "help(\[\["
set end "\]\])"
PutModeTrace $slave "write" "inHelp" # <=== HERE 2
puts stdout $start
ModulesHelp
puts stdout $end
}
if {$sourceFailed} {
report $errorMsg
return 1
}
}]
interp delete $slave
return $errorVal
}
eval execute-modulefile $argv