Index: generic/tclIOCmd.c ================================================================== --- generic/tclIOCmd.c +++ generic/tclIOCmd.c @@ -385,11 +385,12 @@ Tcl_Channel chan; /* The channel to read from. */ int newline, i; /* Discard newline at end? */ int toRead; /* How many bytes to read? */ int charactersRead; /* How many characters were read? */ int mode; /* Mode in which channel is opened. */ - Tcl_Obj *resultPtr, *resultDictPtr, *returnOptsPtr, *chanObjPtr; + int exitstatus = TCL_OK; + Tcl_Obj *resultPtr, *chanObjPtr; if ((objc != 2) && (objc != 3)) { Interp *iPtr; argerror: @@ -469,32 +470,24 @@ * Capture error messages put by the driver into the bypass area and * put them into the regular interpreter result. Fall back to the * regular message if nothing was found in the bypass. */ - if (!TclChanCaughtErrorBypass(interp, chan)) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "error reading \"%s\": %s", - TclGetString(chanObjPtr), Tcl_PosixError(interp))); - } - resultDictPtr = Tcl_NewDictObj(); - Tcl_DictObjPut(NULL, resultDictPtr, Tcl_NewStringObj("read", -1) - , resultPtr); - returnOptsPtr = Tcl_NewDictObj(); - Tcl_DictObjPut(NULL, returnOptsPtr, Tcl_NewStringObj("-result", -1) - , resultDictPtr); - TclChannelRelease(chan); - Tcl_DecrRefCount(resultPtr); - Tcl_SetReturnOptions(interp, returnOptsPtr); - return TCL_ERROR; - } - - /* - * If requested, remove the last newline in the channel if at EOF. - */ - - if ((charactersRead > 0) && (newline != 0)) { + if (TclChanCaughtErrorBypass(interp, chan)) { + Tcl_AddObjErrorInfo(interp, Tcl_GetString(Tcl_ObjPrintf( + "\n %s", Tcl_GetString(Tcl_GetObjResult(interp)))),-1); + } else { + Tcl_AddObjErrorInfo(interp, Tcl_GetString(Tcl_ObjPrintf( + "error reading \"%s\": %s", + TclGetString(chanObjPtr), Tcl_PosixError(interp))), -1); + } + exitstatus = TCL_ERROR; + } else if ((charactersRead > 0) && (newline != 0)) { + /* + * If requested, remove the last newline in the channel if at EOF. + */ + const char *result; int length; result = TclGetStringFromObj(resultPtr, &length); if (result[length - 1] == '\n') { @@ -502,11 +495,11 @@ } } Tcl_SetObjResult(interp, resultPtr); TclChannelRelease(chan); Tcl_DecrRefCount(resultPtr); - return TCL_OK; + return exitstatus; } /* *---------------------------------------------------------------------- * Index: tests/io.test ================================================================== --- tests/io.test +++ tests/io.test @@ -1548,41 +1548,41 @@ scan [string index $in end] %c } 160 apply [list {} { - set template { - test io-12.9.@variant@ {ReadChars: multibyte chars split, default (strict)} -body { - set res {} - set f [open $path(test1) w] - fconfigure $f -translation binary - puts -nonewline $f [string repeat a 9]\xC2 - close $f - set f [open $path(test1)] - fconfigure $f -encoding utf-8 @strict@ -buffersize 10 - set status [catch {read $f} cres copts] - set in [dict get $copts -result] - lappend res $in - lappend res $status $cres - set status [catch {read $f} cres copts] - set in [dict get $copts -result] - lappend res $in - lappend res $status $cres - set res - } -cleanup { - catch {close $f} - } -match glob -result {{read aaaaaaaaa} 1\ - {error reading "*": illegal byte sequence}\ - {read {}} 1 {error reading "*": illegal byte sequence}} - } - - # strict encoding may be the default in Tcl 9, but in 8 it is not - foreach variant {encodingstrict} strict {{-strictencoding 1}} { - set script [string map [ - list @variant@ $variant @strict@ $strict] $template] - uplevel 1 $script - } + set template { + test io-12.9.@variant@ {ReadChars: multibyte chars split, default (strict)} -body { + set res {} + set f [open $path(test1) w] + fconfigure $f -translation binary + puts -nonewline $f [string repeat a 9]\xC2 + close $f + set f [open $path(test1)] + fconfigure $f -encoding utf-8 @strict@ -buffersize 10 + set status [catch {read $f} cres copts] + set in [dict get $copts -errorinfo] + lappend res $status $cres + lappend res $in + set status [catch {read $f} cres copts] + set in [dict get $copts -errorinfo] + lappend res $status $cres + lappend res $in + set res + } -cleanup { + catch {close $f} + } -match glob -result {1 aaaaaaaaa\ + {error reading "*"*:*illegal byte sequence*"read $f"}\ + 1 {} {error reading "*"*:*illegal byte sequence*"read $f"}} + } + + # strict encoding may be the default in Tcl 9, but in 8 it is not + foreach variant {encodingstrict} strict {{-strictencoding 1}} { + set script [string map [ + list @variant@ $variant @strict@ $strict] $template] + uplevel 1 $script + } } [namespace current]] test io-12.10 {ReadChars: multibyte chars split} -body { set f [open $path(test1) w] @@ -9078,18 +9078,17 @@ puts -nonewline $f A\x81 flush $f seek $f 0 fconfigure $f -encoding utf-8 -buffering none -eofchar "" -translation lf -strictencoding 1 } -body { - set status [catch {read $f} cres copts] - set d [dict get $copts -result read] + set status [catch {read $f} d copts] binary scan $d H* hd - lappend hd $status $cres + lappend hd $status [dict get $copts -errorinfo] } -cleanup { close $f removeFile io-75.6 -} -match glob -result {41 1 {error reading "*": illegal byte sequence}} +} -match glob -result {41 1 {error reading "*": illegal byte sequence*}} test io-75.7 {invalid utf-8 encoding eof handling (-strictencoding 1)} -setup { set fn [makeFile {} io-75.7] set f [open $fn w+] fconfigure $f -encoding binary @@ -9097,23 +9096,22 @@ puts -nonewline $f A\xA1\x1A flush $f seek $f 0 fconfigure $f -encoding utf-8 -buffering none -eofchar \x1A -translation lf -strictencoding 1 } -body { - set status [catch {read $f} cres copts] - set d [dict get $copts -result read] + set status [catch {read $f} d copts] binary scan $d H* hd lappend hd [eof $f] lappend hd $status - lappend hd $cres + lappend hd [dict get $copts -errorinfo] fconfigure $f -encoding iso8859-1 lappend hd [read $f];# We changed encoding, so now we can read the \xA1 close $f set hd } -cleanup { removeFile io-75.7 -} -match glob -result {41 0 1 {error reading "*": illegal byte sequence} ¡} +} -match glob -result {41 0 1 {error reading "*": illegal byte sequence*} ¡} test io-75.8.incomplete { incomplete uft-8 char after eof char is not an error (-strictencoding 1) } -setup { set hd {} @@ -9196,33 +9194,32 @@ } -cleanup { removeFile io-75.10 } -result 41c0 -test io-75.10_strict {incomplete multibyte encoding read is an error} -setup { +test io-75.10.strict {incomplete multibyte encoding read is an error} -setup { set res {} set fn [makeFile {} io-75.10] set f [open $fn w+] fconfigure $f -encoding binary puts -nonewline $f A\xC0 flush $f seek $f 0 fconfigure $f -encoding utf-8 -strictencoding 1 -buffering none } -body { - set status [catch {read $f} cres copts] - set d [dict get $copts -result read] - binary scan $d H* hd - lappend res $hd $cres - chan configure $f -encoding iso8859-1 - set d [read $f] - binary scan $d H* hd - lappend res $hd - close $f - return $res + set status [catch {read $f} d copts] + binary scan $d H* hd + lappend res $hd [dict get $copts -errorinfo] + chan configure $f -encoding iso8859-1 + set d [read $f] + binary scan $d H* hd + lappend res $hd + close $f + return $res } -cleanup { removeFile io-75.10 -} -match glob -result {41 {error reading "*": illegal byte sequence} c0} +} -match glob -result {41 {error reading "*": illegal byte sequence*} c0} # As utf-8 has a special treatment in multi-byte decoding, also test another # one. test io-75.11 {shiftjis encoding error read results in raw bytes} -setup { @@ -9235,19 +9232,18 @@ flush $f seek $f 0 fconfigure $f -encoding shiftjis -buffering none -eofchar "" \ -translation lf -strictencoding 1 } -body { - set status [catch {read $f} cres copts] - set d [dict get $copts -result read] + set status [catch {read $f} d copts] binary scan $d H* hd lappend hd $status - lappend hd $cres + lappend hd [dict get $copts -errorinfo] } -cleanup { close $f removeFile io-75.11 -} -match glob -result {41 1 {error reading "*": illegal byte sequence}} +} -match glob -result {41 1 {error reading "*": illegal byte sequence*}} test io-75.12 {invalid utf-8 encoding read is an error} -setup { set res {} set fn [makeFile {} io-75.12] @@ -9257,20 +9253,19 @@ flush $f seek $f 0 fconfigure $f -encoding utf-8 -buffering none -eofchar {} -translation lf \ -strictencoding 1 } -body { - set status [catch {read $f} cres copts] - set d [dict get $copts -result read] + set status [catch {read $f} d copts] close $f binary scan $d H* hd - lappend res $hd $status $cres - return $res + lappend res $hd $status [dict get $copts -errorinfo] + return $res } -cleanup { removeFile io-75.12 -} -match glob -result {41 1 {error reading "*": illegal byte sequence}} -test io-75.12_ignore {invalid utf-8 encoding read is ignored} -setup { +} -match glob -result {41 1 {error reading "*": illegal byte sequence*}} +test io-75.12.ignore {invalid utf-8 encoding read is ignored} -setup { set fn [makeFile {} io-75.12] set f [open $fn w+] fconfigure $f -encoding binary puts -nonewline $f A\x81 flush $f @@ -9293,19 +9288,18 @@ puts -nonewline $f "A\x81" flush $f seek $f 0 fconfigure $f -encoding utf-8 -buffering none -eofchar "" -translation lf -strictencoding 1 } -body { - set status [catch {read $f} cres copts] - set d [dict get $copts -result read] + set status [catch {read $f} d copts] binary scan $d H* hd lappend hd $status close $f - lappend hd $cres + lappend hd [dict get $copts -errorinfo] } -cleanup { removeFile io-75.13 -} -match glob -result {41 1 {error reading "*": illegal byte sequence}} +} -match glob -result {41 1 {error reading "*": illegal byte sequence*}} test io-75.14 {invalid utf-8 encoding [gets] coninues in non-strict mode after error} -setup { set res {} set fn [makeFile {} io-75.14] set f [open $fn w+] Index: tests/ioCmd.test ================================================================== --- tests/ioCmd.test +++ tests/ioCmd.test @@ -862,12 +862,15 @@ return a } } set ch [chan create read foo] } -body { - read $ch 1 -} -returnCodes error -cleanup { + set status [catch { + read $ch 1 + } cres copts] + list $status [dict get $copts -errorinfo] +} -cleanup { catch {close $ch} rename foo {} } -match glob -result {*invalid argument*} test iocmd-21.23 {[close] in [gets] segfaults} -setup { proc foo {method chan args} { @@ -1056,20 +1059,22 @@ rename foo {} set res } -result {{read rc* 4096} {read rc* 4096} snarfsnarf} test iocmd-23.2 {chan read, bad data return, to much} -match glob -body { set res {} - proc foo {args} { + proc foo args { oninit; onfinal; track return [string repeat snarf 1000] } set c [chan create {r w} foo] - note [catch {read $c 2} msg]; note $msg + note [catch {read $c 2} msg copts] + note $msg + note [dict get $copts -errorinfo] close $c rename foo {} set res -} -result {{read rc* 4096} 1 {read delivered more than requested}} +} -result {{read rc* 4096} 1 {} {read delivered more than requested*}} test iocmd-23.3 {chan read, for non-readable channel} -match glob -body { set res {} proc foo {args} { oninit; onfinal; track; note MUST_NOT_HAPPEN } @@ -1084,63 +1089,74 @@ proc foo {args} { oninit; onfinal; track return -code error BOOM! } set c [chan create {r w} foo] - note [catch {read $c 2} msg]; note $msg + note [catch {read $c 2} msg copts] + note $msg + note [dict get $copts -errorinfo] close $c rename foo {} set res -} -result {{read rc* 4096} 1 BOOM!} +} -result {{read rc* 4096} 1 {} {BOOM!*}} test iocmd-23.5 {chan read, break return is error} -match glob -body { set res {} proc foo {args} { oninit; onfinal; track return -code break BOOM! } set c [chan create {r w} foo] - note [catch {read $c 2} msg]; note $msg + note [catch {read $c 2} msg copts] + note $msg + note [dict get $copts -errorinfo] close $c rename foo {} set res -} -result {{read rc* 4096} 1 *bad code*} +} -result {{read rc* 4096} 1 {} {chan handler *bad code*}} test iocmd-23.6 {chan read, continue return is error} -match glob -body { set res {} proc foo {args} { oninit; onfinal; track return -code continue BOOM! } set c [chan create {r w} foo] - note [catch {read $c 2} msg]; note $msg + note [catch {read $c 2} msg copts] + note $msg + note [dict get $copts -errorinfo] close $c rename foo {} set res -} -result {{read rc* 4096} 1 *bad code*} +} -result {{read rc* 4096} 1 {} {chan handler *bad code*}} test iocmd-23.7 {chan read, custom return is error} -match glob -body { set res {} proc foo {args} { oninit; onfinal; track return -code 777 BOOM! } set c [chan create {r w} foo] - note [catch {read $c 2} msg]; note $msg + note [catch {read $c 2} msg copts] + note $msg + note [dict get $copts -errorinfo] close $c rename foo {} set res -} -result {{read rc* 4096} 1 *bad code*} +} -result {{read rc* 4096} 1 {} {chan handler *bad code*}} test iocmd-23.8 {chan read, level is squashed} -match glob -body { set res {} proc foo {args} { oninit; onfinal; track return -level 55 -code 777 BOOM! } set c [chan create {r w} foo] - note [catch {read $c 2} msg opt]; note $msg; noteOpts $opt + note [catch {read $c 2} msg opt] + note $msg + noteOpts $opt close $c rename foo {} set res -} -result {{read rc* 4096} 1 *bad code* {-code 1 -level 0 -errorcode NONE -errorline 1 -errorinfo *bad code*subcommand "read"*}} +} -result {{read rc* 4096} 1 {} {-code 1 -level 0 -errorcode NONE\ + -errorline 1 -errorinfo *bad code*subcommand "read"*}} test iocmd-23.9 {chan read, no data means eof} -match glob -setup { set res {} proc foo {args} { oninit; onfinal; track return "" Index: tests/ioTrans.test ================================================================== --- tests/ioTrans.test +++ tests/ioTrans.test @@ -448,16 +448,17 @@ handle.finalize lappend ::res $args return -code error BOOM! } set c [chan push [tempchan] foo] - lappend res [catch {read $c 2} msg] $msg + lappend res [catch {read $c 2} msg copts] $msg + lappend res [dict get $copts -errorinfo] } -cleanup { tempdone rename foo {} } -result {{read rt* {test data -}} 1 BOOM!} +}} 1 {} {BOOM!*}} test iortrans-4.4 {chan read, break return is error} -setup { set res {} } -match glob -body { proc foo {args} { handle.initialize @@ -464,16 +465,17 @@ handle.finalize lappend ::res $args return -code break BOOM! } set c [chan push [tempchan] foo] - lappend res [catch {read $c 2} msg] $msg + lappend res [catch {read $c 2} msg copts] $msg + lappend res [dict get $copts -errorinfo] } -cleanup { tempdone rename foo {} } -result {{read rt* {test data -}} 1 *bad code*} +}} 1 {} {chan handler returned bad code*}} test iortrans-4.5 {chan read, continue return is error} -setup { set res {} } -match glob -body { proc foo {args} { handle.initialize @@ -480,16 +482,17 @@ handle.finalize lappend ::res $args return -code continue BOOM! } set c [chan push [tempchan] foo] - lappend res [catch {read $c 2} msg] $msg + lappend res [catch {read $c 2} msg copts] $msg + lappend res [dict get $copts -errorinfo] } -cleanup { tempdone rename foo {} } -result {{read rt* {test data -}} 1 *bad code*} +}} 1 {} {chan handler returned bad code*}} test iortrans-4.6 {chan read, custom return is error} -setup { set res {} } -match glob -body { proc foo {args} { handle.initialize @@ -496,16 +499,17 @@ handle.finalize lappend ::res $args return -code 777 BOOM! } set c [chan push [tempchan] foo] - lappend res [catch {read $c 2} msg] $msg + lappend res [catch {read $c 2} msg copts] $msg + lappend res [dict get $copts -errorinfo] } -cleanup { tempdone rename foo {} } -result {{read rt* {test data -}} 1 *bad code*} +}} 1 {} {chan handler returned bad code*}} test iortrans-4.7 {chan read, level is squashed} -setup { set res {} } -match glob -body { proc foo {args} { handle.initialize @@ -518,11 +522,12 @@ noteOpts $opt } -cleanup { tempdone rename foo {} } -result {{read rt* {test data -}} 1 *bad code* {-code 1 -level 0 -errorcode NONE -errorline 1 -errorinfo *bad code*subcommand "read"*}} +}} 1 {} {-code 1 -level 0 -errorcode NONE -errorline 1\ + -errorinfo *bad code*subcommand "read"*}} test iortrans-4.8 {chan read, read, bug 2921116} -setup { set res {} } -match glob -body { proc foo {fd args} { handle.initialize Index: tests/zlib.test ================================================================== --- tests/zlib.test +++ tests/zlib.test @@ -21,10 +21,11 @@ # Work around a bug in some versions of zlib; known to manifest on at # least Mac OS X Mountain Lion... testConstraint recentZlib \ [package vsatisfies [zlib::pkgconfig get zlibVersion] 1.2.6] } +interp bgerror {} [list [namespace current]::bgerror] test zlib-1.1 {zlib basics} -constraints zlib -returnCodes error -body { zlib } -result {wrong # args: should be "zlib command arg ?...?"} test zlib-1.2 {zlib basics} -constraints zlib -returnCodes error -body { @@ -699,11 +700,11 @@ } -cleanup { unset -nocomplain total close $srv } -result {eof 500} test zlib-9.9 "bug #2818131 (gzip mismatch)" -constraints zlib -setup { - proc bgerror {s} {set ::total [list error $s]} + proc bgerror {s o} {set ::total [list error $s [dict get $o -errorinfo]]} set srv [socket -myaddr localhost -server {apply {{c a p} { chan configure $c -translation binary -buffering none -blocking 0 zlib push gzip $c puts -nonewline $c [string repeat hello 100] close $c @@ -730,13 +731,13 @@ set ::total } -cleanup { unset -nocomplain total close $srv rename bgerror {} -} -result {error {invalid block type}} +} -match glob -result {error {} invalid\\ block\\ type*} test zlib-9.10 "bug #2818131 (compress mismatch)" -constraints zlib -setup { - proc bgerror {s} {set ::total [list error $s]} + proc bgerror {s o} {set ::total [list error $s [dict get $o -errorinfo]]} set srv [socket -myaddr localhost -server {apply {{c a p} { chan configure $c -translation binary -buffering none -blocking 0 zlib push compress $c puts -nonewline $c [string repeat hello 100] close $c @@ -763,13 +764,13 @@ set ::total } -cleanup { unset -nocomplain total close $srv rename bgerror {} -} -result {error {invalid stored block lengths}} +} -match glob -result {error {} invalid\\ stored\\ block\\ lengths*} test zlib-9.11 "bug #2818131 (deflate mismatch)" -constraints zlib -setup { - proc bgerror {s} {set ::total [list error $s]} + proc bgerror {s o} {set ::total [list error $s [dict get $o -errorinfo]]} set srv [socket -myaddr localhost -server {apply {{c a p} { chan configure $c -translation binary -buffering none -blocking 0 zlib push deflate $c puts -nonewline $c [string repeat hello 100] close $c @@ -796,16 +797,16 @@ set ::total } -cleanup { unset -nocomplain total close $srv rename bgerror {} -} -result {error {incorrect header check}} +} -match glob -result {error {} incorrect\\ header\\ check*} test zlib-10.0 "bug #2818131 (close with null interp)" -constraints { zlib } -setup { - proc bgerror {s} {set ::total [list error $s]} + proc bgerror {s o} {set ::total [list error $s]} set srv [socket -myaddr localhost -server {apply {{c a p} { chan configure $c -translation binary zlib push inflate $c chan event $c readable [list apply {{c} { set d [read $c] @@ -842,11 +843,11 @@ } -returnCodes error \ -result {bad event name "xyzzy": must be readable or writable} test zlib-10.1 "bug #2818131 (mismatch read)" -constraints { zlib } -setup { - proc bgerror {s} {set ::total [list error $s]} + proc bgerror {s o} {set ::total [list error $s [dict get $o -errorinfo]]} proc zlibRead {c} { set d [read $c] if {[eof $c]} { chan event $c readable {} close $c @@ -876,15 +877,15 @@ set ::total } -cleanup { close $srv rename bgerror {} rename zlibRead {} -} -result {error {invalid block type}} +} -match glob -result {error {} {invalid block type*}} test zlib-10.2 "bug #2818131 (mismatch gets)" -constraints { zlib } -setup { - proc bgerror {s} {set ::total [list error $s]} + proc bgerror {s o} {set ::total [list error $s]} proc zlibRead {c} { if {[gets $c line] < 0} { close $c set ::total [list error -1] } elseif {[eof $c]} {