Attachment "tip287-revised-revised.patch" to
ticket [1586860fff]
added by
cleverly
2006-11-22 12:54:39.
--- doc/chan.n.orig 2006-10-28 22:37:48.000000000 -0600
+++ doc/chan.n 2006-11-21 22:47:33.000000000 -0700
@@ -493,6 +493,16 @@
only those channel names that match it (according to the rules of
\fBstring match\fR) will be returned.
.TP
+\fBchan pending \fImode channelId\fR
+.
+Depending on whether \fImode\fr is "input" or "output", returns the number of
+bytes of input or output (respectively) currently buffered
+internally for \fIchannelId\fr (especially useful in a readable event
+callback to impose application-specific limits on input line lengths to avoid
+a potential denial-of-service attack where a hostile user crafts
+an extremely long line that exceeds the available memory to buffer it).
+Returns -1 if the channel was not opened for the mode in question.
+.TP
\fBchan postevent \fIchannelId eventSpec\fR
.
This subcommand is used by command handlers specified with \fBchan
--- generic/tclBasic.c.orig 2006-10-28 21:55:59.000000000 -0600
+++ generic/tclBasic.c 2006-11-21 19:01:59.000000000 -0700
@@ -485,6 +485,10 @@
Tcl_CreateObjCommand(interp, "::tcl::chan::rPostevent",
TclChanPostEventObjCmd, (ClientData) NULL, NULL);
+ /* TIP #287 */
+ Tcl_CreateObjCommand(interp, "::tcl::chan::Pending",
+ TclChanPendingObjCmd, (ClientData) NULL, NULL);
+
/*
* Register the built-in functions. This is empty now that they are
* implemented as commands in the ::tcl::mathfunc namespace.
--- generic/tclInt.h.orig 2006-10-28 22:08:38.000000000 -0600
+++ generic/tclInt.h 2006-11-21 18:31:37.000000000 -0700
@@ -2286,6 +2286,9 @@
MODULE_SCOPE int Tcl_CdObjCmd(ClientData clientData,
Tcl_Interp *interp, int objc,
Tcl_Obj *CONST objv[]);
+MODULE_SCOPE int TclChanPendingObjCmd(
+ ClientData clientData, Tcl_Interp *interp,
+ int objc, Tcl_Obj *CONST objv[]); /* TIP 287 */
MODULE_SCOPE int TclChanTruncateObjCmd(
ClientData clientData, Tcl_Interp *interp,
int objc, Tcl_Obj *CONST objv[]);
--- generic/tclIOCmd.c.orig 2006-10-28 21:58:27.000000000 -0600
+++ generic/tclIOCmd.c 2006-11-21 22:51:36.000000000 -0700
@@ -1619,6 +1619,74 @@
}
/*
+ *---------------------------------------------------------------------------
+ *
+ * TclChanPendingObjCmd --
+ *
+ * This function is invoked to process the Tcl "chan pending"
+ * command (TIP #287). See the user documentation for details on
+ * what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * Sets interp's result to the number of bytes of buffered input or
+ * output (depending on whether the first argument is "input" or
+ * "output"), or -1 if the channel wasn't opened for that mode.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+ /* ARGSUSED */
+int
+TclChanPendingObjCmd(
+ ClientData unused, /* Not used. */
+ Tcl_Interp *interp, /* Current interpreter. */
+ int objc, /* Number of arguments. */
+ Tcl_Obj *CONST objv[]) /* Argument objects. */
+{
+ Tcl_Channel chan;
+ int index, mode;
+ char *arg;
+ static CONST char *options[] = {"input", "output", (char *) NULL};
+ enum options {PENDING_INPUT, PENDING_OUTPUT};
+
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 1, objv, "mode channelId");
+ return TCL_ERROR;
+ }
+
+ if (Tcl_GetIndexFromObj(interp, objv[1], options, "mode", 0,
+ &index) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ arg = Tcl_GetString(objv[2]);
+ chan = Tcl_GetChannel(interp, arg, &mode);
+ if (chan == NULL) {
+ return TCL_ERROR;
+ }
+
+ switch ((enum options) index) {
+ case PENDING_INPUT:
+ if ((mode & TCL_READABLE) == 0) {
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(-1));
+ } else {
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(Tcl_InputBuffered(chan)));
+ }
+ return TCL_OK;
+ case PENDING_OUTPUT:
+ if ((mode & TCL_WRITABLE) == 0) {
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(-1));
+ } else {
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(Tcl_OutputBuffered(chan)));
+ }
+ return TCL_OK;
+ }
+}
+
+/*
*----------------------------------------------------------------------
*
* Tcl_ChanTruncateObjCmd --
--- library/init.tcl.orig 2006-09-22 12:13:29.000000000 -0600
+++ library/init.tcl 2006-11-21 19:03:59.000000000 -0700
@@ -78,7 +78,9 @@
# Set up the 'chan' ensemble (TIP #208).
namespace eval chan {
# TIP #219. Added methods: create, postevent.
+ # TIP #287. Added method: pending.
namespace ensemble create -command ::chan -map {
+ pending ::tcl::chan::Pending
blocked ::fblocked
close ::close
configure ::fconfigure
--- tests/chan.test.orig 2006-10-29 13:59:35.000000000 -0700
+++ tests/chan.test 2006-11-21 22:35:24.000000000 -0700
@@ -24,7 +24,7 @@
} -returnCodes error -result "wrong # args: should be \"chan subcommand ?argument ...?\""
test chan-1.2 {chan command general syntax} -body {
chan FOOBAR
-} -returnCodes error -result "unknown or ambiguous subcommand \"FOOBAR\": must be blocked, close, configure, copy, create, eof, event, flush, gets, names, postevent, puts, read, seek, tell, or truncate"
+} -returnCodes error -result "unknown or ambiguous subcommand \"FOOBAR\": must be blocked, close, configure, copy, create, eof, event, flush, gets, names, pending, postevent, puts, read, seek, tell, or truncate"
test chan-2.1 {chan command: blocked subcommand} -body {
chan blocked foo bar
@@ -96,6 +96,116 @@
catch {removeFile $file}
}
+# TIP 287: chan pending
+test chan-16.1 {chan command: pending subcommand} -body {
+ chan pending
+} -returnCodes error -result "wrong # args: should be \"chan pending mode channelId\""
+test chan-16.2 {chan command: pending subcommand} -body {
+ chan pending stdin
+} -returnCodes error -result "wrong # args: should be \"chan pending mode channelId\""
+test chan-16.3 {chan command: pending subcommand} -body {
+ chan pending stdin stdout stderr
+} -returnCodes error -result "wrong # args: should be \"chan pending mode channelId\""
+test chan-16.4 {chan command: pending subcommand} -body {
+ chan pending {input output} stdout
+} -returnCodes error -result "bad mode \"input output\": must be input or output"
+test chan-16.5 {chan command: pending input subcommand} -body {
+ chan pending input stdout
+} -result -1
+test chan-16.6 {chan command: pending input subcommand} -body {
+ chan pending input stdin
+} -result 0
+test chan-16.7 {chan command: pending input subcommand} -body {
+ chan pending input FOOBAR
+} -returnCodes error -result "can not find channel named \"FOOBAR\""
+test chan-16.8 {chan command: pending input subcommand} -setup {
+ set file [makeFile {} testAvailable]
+ set f [open $file w+]
+ chan configure $f -translation lf -buffering line
+} -body {
+ chan puts $f foo
+ chan puts $f bar
+ chan puts $f baz
+ chan seek $f 0
+ chan gets $f
+ chan pending input $f
+} -result 8 -cleanup {
+ catch {chan close $f}
+ catch {removeFile $file}
+}
+test chan-16.9 {chan command: pending input subcommand} -setup {
+ proc chan-16.9-accept {sock addr port} {
+ chan configure $sock -blocking 0 -buffering line -buffersize 32
+ chan event $sock readable [list chan-16.9-readable $sock]
+ }
+
+ proc chan-16.9-readable {sock} {
+ set r [chan gets $sock line]
+ set l [string length $line]
+ set e [chan eof $sock]
+ set b [chan blocked $sock]
+ set i [chan pending input $sock]
+
+ lappend ::chan-16.9-data $r $l $e $b $i
+
+ if {$r != -1 || $e || $l || !$b || $i > 128} {
+ set data [read $sock $i]
+ lappend ::chan-16.9-data [string range $data 0 2]
+ lappend ::chan-16.9-data [string range $data end-2 end]
+ set ::chan-16.9-done 1
+ }
+ }
+
+ proc chan-16.9-client {} {
+ chan puts -nonewline $::client ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890
+ chan flush $::client
+ after 100 chan-16.9-client
+ }
+
+ set ::server [socket -server chan-16.9-accept -myaddr 127.0.0.1 0]
+ set ::client [socket 127.0.0.1 [lindex [fconfigure $::server -sockname] 2]]
+ set ::chan-16.9-data [list]
+ set ::chan-16.9-done 0
+} -body {
+ after idle chan-16.9-client
+ vwait ::chan-16.9-done
+ set ::chan-16.9-data
+} -result {-1 0 0 1 36 -1 0 0 1 72 -1 0 0 1 108 -1 0 0 1 144 ABC 890} -cleanup {
+ catch {chan close $client}
+ catch {chan close $server}
+ rename chan-16.9-accept {}
+ rename chan-16.9-readable {}
+ rename chan-16.9-client {}
+ unset -nocomplain ::chan-16.9-data
+ unset -nocomplain ::chan-16.9-done
+ unset -nocomplain ::server
+ unset -nocomplain ::client
+}
+test chan-16.10 {chan command: pending output subcommand} -body {
+ chan pending output stdin
+} -result -1
+test chan-16.11 {chan command: pending output subcommand} -body {
+ chan pending output stdout
+} -result 0
+test chan-16.12 {chan command: pending output subcommand} -body {
+ chan pending output FOOBAR
+} -returnCodes error -result "can not find channel named \"FOOBAR\""
+test chan-16.13 {chan command: pending output subcommand} -setup {
+ set file [makeFile {} testPendingOutput]
+ set f [open $file w+]
+ chan configure $f -translation lf -buffering full -buffersize 1024
+} -body {
+ set result [list]
+ chan puts $f [string repeat x 512]
+ lappend result [chan pending output $f]
+ chan flush $f
+ lappend result [chan pending output $f]
+} -result [list 513 0] -cleanup {
+ unset -nocomplain result
+ catch {chan close $f}
+ catch {removeFile $file}
+}
+
cleanupTests
return
--- tests/ioCmd.test.orig 2006-10-28 23:06:44.000000000 -0600
+++ tests/ioCmd.test 2006-11-21 19:29:42.000000000 -0700
@@ -628,7 +628,7 @@
test iocmd-20.1 {chan, unknown method} {
catch {chan foo} msg
set msg
-} {unknown or ambiguous subcommand "foo": must be blocked, close, configure, copy, create, eof, event, flush, gets, names, postevent, puts, read, seek, tell, or truncate}
+} {unknown or ambiguous subcommand "foo": must be blocked, close, configure, copy, create, eof, event, flush, gets, names, pending, postevent, puts, read, seek, tell, or truncate}
# --- --- --- --------- --------- ---------
# chan create, and method "initalize"