Index: library/lib/nx-shell.tcl =================================================================== diff -u -r2a5945f098b50d909d57fb3d3abfaf44608c0ac8 -r3ee0ac91f0a8034ec4739bc2af1016cdf30fbe57 --- library/lib/nx-shell.tcl (.../nx-shell.tcl) (revision 2a5945f098b50d909d57fb3d3abfaf44608c0ac8) +++ library/lib/nx-shell.tcl (.../nx-shell.tcl) (revision 3ee0ac91f0a8034ec4739bc2af1016cdf30fbe57) @@ -16,39 +16,80 @@ return } if {[info complete ${:line}]} { - set script [list catch [string trim ${:line}] [current]::result [current]::opts] - set r [uplevel #0 $script] - if {$r} { - puts stdout [dict get ${:opts} -errorinfo] - unset :opts; - } else { - puts stdout ${:result} - unset :result - } + :evalScript ${:line} unset :line if {![info exists :forever]} { :prompt } } } - :public object method prompt {{chan stdout}} { + :protected object method evalScript {-exit:switch script} { + set script [list catch [string trim $script] [current]::result [current]::opts] + set r [uplevel #0 $script] + if {$r} { + puts stderr [dict get ${:opts} -errorinfo] + if {$exit} { + exit 1 + } else { + unset :opts; + } + } else { + if {${:result} ne ""} { + puts stdout ${:result} + } + unset :result + } + } + + :protected object method prompt {{chan stdout}} { puts -nonewline $chan "% " flush $chan } :public object method run {argc argv} { - if {$argc == 0} { - :prompt stdout - fconfigure stdin -blocking 0 -buffering line - fileevent stdin readable [list [current] onRead] - vwait :forever - fileevent stdin readable {} - exit + if {[catch { + if {$argc == 0} { + # interactive mode + :prompt stdout + fconfigure stdin -blocking 0 -buffering line + fileevent stdin readable [list [current] onRead] + vwait :forever + fileevent stdin readable {} + exit + } else { + # non-interactive modes + :nonInteract {*}$argv + } + } msg]} { + puts stderr "[current] failed unexpectedly with '$msg'" + exit 2 + } + } + + :protected object method nonInteract {-c:switch args} { + if {$c} { + # non-interactive mode: arg command xor stdin + if {[llength $args]} { + # arg command plus argc/argv + set args [lassign $args script] + set ::argv $args + set ::argc [llength $args] + } else { + # stdin + set ::argv "" + set ::argc 0 + set script [gets stdin] + } + :evalScript -exit $script } else { - set ::argv [lassign $argv argv0] + # non-interactive mode: script + set ::argv [lassign $args argv0] incr ::argc -1 - uplevel #0 [list source $argv0] + if {[catch {uplevel #0 [list source $argv0]} msg]} { + puts stderr $msg + exit 1 + } } } } Index: library/xotcl/xotclsh.in =================================================================== diff -u -r2a5945f098b50d909d57fb3d3abfaf44608c0ac8 -r3ee0ac91f0a8034ec4739bc2af1016cdf30fbe57 --- library/xotcl/xotclsh.in (.../xotclsh.in) (revision 2a5945f098b50d909d57fb3d3abfaf44608c0ac8) +++ library/xotcl/xotclsh.in (.../xotclsh.in) (revision 3ee0ac91f0a8034ec4739bc2af1016cdf30fbe57) @@ -8,8 +8,6 @@ exec $INTERP "$0" ${1+"$@"} # -*- tcl -*- -puts [info nameofexecutable] - # # Tiny scripted replacement of a binary xotclsh. This script can be used # as interactive shell for testing or like a regular shell with the #! Index: library/xotcl/xowish.in =================================================================== diff -u -r2a5945f098b50d909d57fb3d3abfaf44608c0ac8 -r3ee0ac91f0a8034ec4739bc2af1016cdf30fbe57 --- library/xotcl/xowish.in (.../xowish.in) (revision 2a5945f098b50d909d57fb3d3abfaf44608c0ac8) +++ library/xotcl/xowish.in (.../xowish.in) (revision 3ee0ac91f0a8034ec4739bc2af1016cdf30fbe57) @@ -8,8 +8,6 @@ exec $INTERP "$0" ${1+"$@"} # -*- tcl -*- -puts [info nameofexecutable] - # # Tiny scripted replacement of a binary xowish. This # script can be used as interactive shell for testing or like a Index: nxsh.in =================================================================== diff -u -r2a5945f098b50d909d57fb3d3abfaf44608c0ac8 -r3ee0ac91f0a8034ec4739bc2af1016cdf30fbe57 --- nxsh.in (.../nxsh.in) (revision 2a5945f098b50d909d57fb3d3abfaf44608c0ac8) +++ nxsh.in (.../nxsh.in) (revision 3ee0ac91f0a8034ec4739bc2af1016cdf30fbe57) @@ -8,8 +8,6 @@ exec $INTERP "$0" ${1+"$@"} # -*- tcl -*- -puts [info nameofexecutable] - # # Tiny scripted replacement of a binary nxsh. This script can be used # as interactive shell for testing or like a regular shell with the #! Index: nxwish.in =================================================================== diff -u -r2a5945f098b50d909d57fb3d3abfaf44608c0ac8 -r3ee0ac91f0a8034ec4739bc2af1016cdf30fbe57 --- nxwish.in (.../nxwish.in) (revision 2a5945f098b50d909d57fb3d3abfaf44608c0ac8) +++ nxwish.in (.../nxwish.in) (revision 3ee0ac91f0a8034ec4739bc2af1016cdf30fbe57) @@ -8,8 +8,6 @@ exec $INTERP "$0" ${1+"$@"} # -*- tcl -*- -puts [info nameofexecutable] - # # Tiny scripted replacement of a binary nxwish (former xowish). This # script can be used as interactive shell for testing or like a Index: tests/shells.test =================================================================== diff -u --- tests/shells.test (revision 0) +++ tests/shells.test (revision 3ee0ac91f0a8034ec4739bc2af1016cdf30fbe57) @@ -0,0 +1,55 @@ +# -*- Tcl -*- +package require nx +package require nx::test + + +## for now, we do not have scripted shells for Windows environments +## without a bash-like shell, so ... + +if {$tcl_platform(platform) eq "windows"} return; + +nx::test case nxsh { + set rootDir [file join {*}[lrange [file split [file normalize [info script]]] 0 end-2]] + set nxsh [file join $rootDir nxsh] + + set run {puts $argc-[join $argv -]} + + ? [list file exists $nxsh] 1 + ? [list file executable $nxsh] 1 + ## stdin is in interactive mode (just a smoke test) + ? [list exec $nxsh << "$run; exit"] "% 0-" + ## stdin is ignored + ? [list exec $nxsh NXSCRIPT.tcl << "$run; exit"] \ + "couldn't read file \"NXSCRIPT.tcl\": no such file or directory" + ## non-interactive mode (-c) + ? [list exec $nxsh -c "$run" NXSCRIPT.tcl] \ + "1-NXSCRIPT.tcl" + ? [list exec $nxsh -c << $run] "0-" + ? [list exec $nxsh -c $run a b c] "3-a-b-c" + + set tmpfile [file join [::nsf::tmpdir] [pid]] + ? [list exec $nxsh $tmpfile] "couldn't read file \"$tmpfile\": no such file or directory" + ? [list exec $nxsh $tmpfile a b c] "couldn't read file \"$tmpfile\": no such file or directory" + + set ch [open $tmpfile w+] + ? [list file exists $tmpfile] 1 + ? [list file writable $tmpfile] 1 + puts $ch $run + catch {close $ch} + ? [list exec $nxsh $tmpfile] "0-" + ? [list exec $nxsh $tmpfile -c "yyy" a b c] "5--c-yyy-a-b-c" + + file delete -force $tmpfile + + ## avoid pollution of global namespace + set tclsh [info nameofexecutable] + set gvars1 [exec $tclsh << {puts [info vars]}] + set gvars2 [exec $nxsh -c << {puts [info vars]}] + ? [list expr [list [lsort $gvars1] eq [lsort $gvars2]]] 1 +} + +# Local variables: +# mode: tcl +# tcl-indent-level: 2 +# indent-tabs-mode: nil +# End: