OriginalBugID: 1923 Bug
Version: 8.0.5
SubmitDate: '1999-04-22'
LastModified: '1999-11-07'
Severity: MED
Status: UnAssn
Submitter: pat
ChangedBy: hobbs
OS: Windows NT
OSVersion: 4.00.1381
Machine: X86
FixedDate: '2000-10-25'
ClosedDate: '2000-10-25'
Name:Kenneth Atwell
Comments:
As you can see, the line buffer option that is selected to not
functioning correctly.
I believe this is related to "\n" (Unix) versus "\r\n" (DOS) line
returns. The buffer sent to MyChannelOutputProc() does not contain the
full buffer when the output is mulitple lines long. It appears that
buffers with more EOLs are truncated before buffers with less EOLs.
ReproducibleScript:
Compile and link the following C program (I used MS Visual C++ v5). Run
the program. Sorry about the length, but I had to include some (unused)
boilerplate to create the channel.
----
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include "tcl.h"
int MyChannelCloseProc ( ClientData instanceData, Tcl_Interp *interp )
{
return 0;
}
int MyChannelInputProc ( ClientData instanceData, char *buf, int
bufSize, int *errorCodePtr )
{
*errorCodePtr = ENODEV;
return -1;
}
int MyChannelOutputProc ( ClientData instanceData, char *buf, int
toWrite, int *errorCodePtr )
{
/*
* BUG: "buf" does not always end with a '\n', despite the fact that
we
* selected line buffering. This bug can be caught by uncommenting
the
* assert() macro below.
*/
/* assert ( '\n' == buf [ toWrite - 1 ] ); */
char *pszTmp = ( char * ) malloc ( toWrite + 1 );
assert ( pszTmp );
strncpy ( pszTmp, buf, toWrite );
pszTmp [ toWrite ] = '\0';
printf ( "***BEGIN***\n" );
printf ( pszTmp );
printf ( "***END***\n" );
free ( pszTmp );
return toWrite;
}
void MyChannelWatchProc ( ClientData instanceData, int mask )
{
}
int MyChannelGetHandleProc ( ClientData instanceData, int direction,
ClientData *handlePtr )
{
return TCL_ERROR;
}
static Tcl_ChannelType g_CommandWindowChannelType =
{
"my_channel_type", /* name */
NULL, /* block mode proc */
MyChannelCloseProc, /* close proc */
MyChannelInputProc, /* input proc */
MyChannelOutputProc, /* output proc */
NULL, /* seek proc */
NULL, /* set option proc */
NULL, /* get option proc */
MyChannelWatchProc, /* watch proc */
MyChannelGetHandleProc /* get handle proc */
};
static Tcl_Channel g_CommandWindowChannel;
void main ()
{
int result;
Tcl_Interp *interp = Tcl_CreateInterp ();
assert ( interp );
/* Create a new type of I/O channel. */
g_CommandWindowChannel = Tcl_CreateChannel (
&g_CommandWindowChannelType,
"my_channel",
(ClientData) 0,
TCL_WRITABLE );
/* Set STDIN, STDOUT, and STDERR to use this channel. */
Tcl_SetStdChannel ( g_CommandWindowChannel, TCL_STDOUT );
/* Set the channel buffering. Ouput will occur at EOLs. */
result = Tcl_SetChannelOption ( interp, g_CommandWindowChannel,
"-buffering", "line" );
assert ( TCL_OK == result );
/* Test the new channel with a simple puts*/
result = Tcl_Eval ( interp, "puts \&quot;This line prints correctly.\&quot;" );
assert ( TCL_OK == result );
/* This next command results in an error */
result = Tcl_Eval ( interp, "foo" );
assert ( TCL_ERROR == result );
/* Now try to print out $errorInfo */
result = Tcl_Eval ( interp, "puts $errorInfo" );
assert ( TCL_OK == result );
}
ObservedBehavior:
The output of the program is:
***BEGIN***
This line prints correctly.
***END***
***BEGIN***
invalid command name "foo"
while executing
"fo***END***
***BEGIN***
o"
***END***
DesiredBehavior:
Correct output would be:
***BEGIN***
This line prints correctly.
***END***
***BEGIN***
invalid command name "foo"
while executing
"foo"
***END***
Is this a duplicate of #119300 ?
Logged In: YES
user_id=75003
#119300 does not exist (anymore). I believe this is now
#219300, no ?
Logged In: YES
user_id=80530
I believe that is correct.
Logged In: YES
user_id=75003
I have to declare this item as invalid. Here comes the
explanation.
puts <some-data> translates into a call
to "Tcl_PutsObjCmd", which in turn calls "Tcl_WriteObj",
which in turn calls "WriteChars" (in "tclIO.c"). The last
procedure now does several things: EOL translation and
conversion of the internal UTF-8 to the external encoding.
For the first action (EOL-translation) the system goes over
the input, translates and stores the result in a staging
buffer. Whenever the staging buffer is full the result is
handed to the UTF-8 conversion. This stages appends the
result of the conversion to the current buffer in the out
queue, or allocates a new one if the queue was empty.
All of the above can result in breaking apart the data to
write into several chunks, each in its own buffer. So it is
possible for the first buffer to contain some full lines of
the data to write and a partial line, without a closing \n.
Still, the buffer does _contain full lines_ and thus has to
be flushed according to the set buffering policy.
So it is possible that a buffer is written which is not
closed with a \n, even if policy "line" is set. It just has
to contain at least one full line. That a partial line
follows does not detract from the requirement to flush this
buffer. The assumption you assert upon (end of buffer ==
\n) is simply not true.