source: trunk/lang/tcl/scripts/exec.tcl @ 3362

Last change on this file since 3362 was 3362, checked in by ldelgass, 11 years ago

Merge nanovis2 branch to trunk

File size: 4.8 KB
Line 
1
2# ----------------------------------------------------------------------
3#  COMPONENT: exec - simple way to exec with streaming output
4#
5#  This utility makes it easy to exec another tool and get streaming
6#  output from stdout and stderr.  Any stderr messages are specially
7#  encoded with the =RAPPTURE-ERROR=> tag, so they show up in red
8#  in the execution output.
9# ======================================================================
10#  AUTHOR:  Michael McLennan, Purdue University
11#  Copyright (c) 2004-2012  HUBzero Foundation, LLC
12#
13#  See the file "license.terms" for information on usage and
14#  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
15# ======================================================================
16package require BLT
17
18namespace eval Rappture { # forward declaration }
19
20# ----------------------------------------------------------------------
21# USAGE: exec <arg> <arg>...
22#
23# This utility acts like the standard Tcl "exec" command, but it also
24# streams stdout and stderr messages while the command is executing,
25# instead of waiting till the end.  Any error messages are prefixed
26# with the special =RAPPTURE-ERROR=> tag, so they are recognized as
27# errors within Rappture.
28# ----------------------------------------------------------------------
29proc Rappture::exec {args} {
30    variable execout
31    variable execctl
32
33    set execout(output) ""
34    set execout(channel) ""
35    set execout(extra) ""
36    set execctl ""
37
38    set status [catch {eval blt::bgexec ::Rappture::execctl \
39        -keepnewline yes \
40        -killsignal SIGTERM \
41        -onoutput {{::Rappture::_exec_out stdout}} \
42        -onerror {{::Rappture::_exec_out stderr}} \
43        $args} result]
44
45    # add any extra stuff pending from the last stdout/stderr change
46    append execout(output) $execout(extra)
47
48    if { $status != 0 } {
49        # We're here because the exec-ed program failed
50        if { $execctl != "" } {
51            foreach { token pid code mesg } $execctl break
52            if { $token == "EXITED" } {
53                # This means that the program exited normally but
54                # returned a non-zero exitcode.  Consider this an
55                # invalid result from the program.  Append the stderr
56                # from the program to the message.
57                set result \
58                    "Program finished: exit code is $code\n\n"
59                append result $execout(error)
60            } elseif { $token == "abort" }  {
61                # The user pressed the abort button.
62                set result "Program terminated by user.\n\n"
63                append result $execout(output)
64            } else {
65                # Abnormal termination
66                set result "Abnormal program termination: $mesg\n\n"
67                append result $execout(output)
68            }
69        }
70        error $result
71    }
72    return $execout(output)
73}
74
75# ----------------------------------------------------------------------
76# USAGE: _exec_out <channel> <message>
77#
78# Called automatically whenever output comes in from the Rappture::exec
79# utility.  Streams the output to stdout and adds it to the "execout"
80# variable, so it can be returned as one large string at the end of
81# the exec.
82# ----------------------------------------------------------------------
83proc Rappture::_exec_out {channel message} {
84    variable execout
85
86    #
87    # If this message is being written to the stderr channel, then
88    # add the =RAPPTURE-ERROR=> prefix to each line.
89    #
90    if {$channel == "stderr"} {
91        append execout(error) $message
92        set newmesg ""
93        foreach line [split $message \n] {
94            append newmesg "=RAPPTURE-ERROR=>$line\n"
95        }
96        set message $newmesg
97    }
98    #
99    # If this message is coming in on the same channel as the
100    # last, then fine, add it on.  But if it's coming in on a
101    # different channel, we must make sure that we're at a good
102    # breakpoint.  If there's not a line break at the end of the
103    # current output, then add the extra stuff onto a buffer
104    # that we will merge in later once we get to a good point.
105    #
106    if {$execout(channel) == ""} {
107        set execout(channel) $channel
108    }
109
110    set ready [expr {[string length $execout(output)] == 0
111        || [string index $execout(output) end] == "\n"}]
112
113    if {$channel != $execout(channel)} {
114        if {$ready} {
115            # changing channels...
116            if {[string length $execout(extra)] > 0} {
117                # any extra stuff on this new channel? put it out now
118                puts -nonewline $execout(extra)
119                append execout(output) $execout(extra)
120                set execout(extra) ""
121            }
122            puts -nonewline $message
123            append execout(output) $message
124            set execout(channel) $channel
125        } else {
126            # not ready to change channels -- keep this for later
127            append execout(extra) $message
128        }
129    } else {
130        # no need to change channels -- keep printing
131        puts -nonewline $message
132        append execout(output) $message
133    }
134}
Note: See TracBrowser for help on using the repository browser.