Before using Jacl, I was executing a Tcl binary via Runtime.exec. This
provided me with an easy way of grabbing the input/output from the
Process variable. My requirement is to capture the stdout of a
untrusted Tcl script (and not the result). I just happen to put this
into a file or pipe it to another Java class for processing. My
problem is that now that I've moved to Jacl, I don't have a straight
forward getOut/setOut.
I went down the path of creating a custom Channel, but realized that
all the relevant classes and methods are package private and I
couldn't instantiate them. That was enough to stop me from
investigating if that route would really work anyways, the Interp
might just hardcode that kind of thing. So, caveat lector, I'm too
lazy to investigate the writing a customer Channel route.
I've seen other solution that redirect System.out. I'm not
interesting in grabbing System.out because my code in running a larger
system that uses System.out for its own purposes, and I can't have my
code spill over into it. (Which bring up the point, Interp.java has
lots of System.out and that is very annoying.)
My solution was to write "gets" and "puts" since those are the only
ways of outputting or inputting (correct me if I'm wrong). I'm
describing it here to help other users and to provoke some comments.
I based my solution on http://wiki.tcl.tk/8502 to redirect puts. First
I have to push a variable into the Interp:
String filename = outputFile.getCanonicalPath();
interp.eval("set outputfilename \"" + escape(filename) + "\"", TCL.EVAL_GLOBAL);
interp.eval("set outputfile [open \"$outputfilename\" w+]", TCL.EVAL_GLOBAL);
I then run this code:
set altStdOut $outputfile
set altStdErr $outputfile
if ![llength [info command ::tcl::puts]] {
rename puts ::tcl::puts
proc puts args {
set la [llength $args]
if {$la<1 || $la>3} {
error "usage: puts ?-nonewline? ?channel? string"
}
set nl \n
if {[lindex $args 0]=="-nonewline"} {
set nl ""
set args [lrange $args 1 end]
}
if {[llength $args]==1} {
set args [list stdout [join $args]] ;
}
foreach {channel s} $args break
set cmd ::tcl::puts
if {$nl==""} {lappend cmd -nonewline}
if {$channel=="stdout"} {
global altStdOut
set channel $altStdOut
}
if {$channel=="stderr"} {
global altStdErr
set channel $altStdErr
}
lappend cmd $channel $s
catch {eval $cmd}
flush $channel
}
}
It looks for the channel stdout or a default channel (which maps to
stdout) and then I output them to the file, which is the worst part
of this solution. I'd love to see a solution the just lets me set the
OutputStream, from outside the package. For gets, I used the method
from http://wiki.tcl.tk/10794.
I'd like to see Jacl behave like its in a sandbox, to use in a JSR-233
fashion. In which it minimizes it's access to the System class
(System.exit, System.out, System.err). Are there any efforts in this
regard?
justin
|