Tcl Source Code

Check-in [239fb8a769]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:merge trunk
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | novem
Files: files | file ages | folders
SHA1: 239fb8a769c161357ad6bc7d64d5e1aaa6c0342e
User & Date: jan.nijtmans 2014-05-09 13:50:41
Context
2014-05-16
15:33
merge trunk check-in: 44638ca040 user: dgp tags: novem
2014-05-09
13:50
merge trunk check-in: 239fb8a769 user: jan.nijtmans tags: novem
13:33
Test iocmd-32.1 is not "impossible" but after writing it properly it does segfault trying to use a d... check-in: 0a4dfac63e user: dgp tags: trunk
2014-05-02
06:58
Update Unicode tables to Unicode 7.0 beta check-in: 872972ea94 user: jan.nijtmans tags: novem
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to doc/tclvars.n.

343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
If this variable exists, then the interpreter
was compiled with threads enabled.
.TP
\fBuser\fR
.
This identifies the
current user based on the login information available on the platform.
This comes from the USER or LOGNAME environment variable on Unix,
and the value from GetUserName on Windows.
.TP
\fBwordSize\fR
.
This gives the size of the native-machine word in bytes (strictly, it
is same as the result of evaluating \fIsizeof(long)\fR in C.)
.RE
.TP







|
|







343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
If this variable exists, then the interpreter
was compiled with threads enabled.
.TP
\fBuser\fR
.
This identifies the
current user based on the login information available on the platform.
This value comes from the getuid() and getpwuid() system calls on Unix,
and the value from the GetUserName() system call on Windows.
.TP
\fBwordSize\fR
.
This gives the size of the native-machine word in bytes (strictly, it
is same as the result of evaluating \fIsizeof(long)\fR in C.)
.RE
.TP

Changes to generic/tcl.h.

2225
2226
2227
2228
2229
2230
2231





2232
2233
2234
2235
2236
2237
2238

#include "tclDecls.h"

/*
 * Include platform specific public function declarations that are accessible
 * via the stubs table.
 */






#include "tclPlatDecls.h"

/*
 *----------------------------------------------------------------------------
 * The following declarations either map ckalloc and ckfree to malloc and
 * free, or they map them to functions with all sorts of debugging hooks







>
>
>
>
>







2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243

#include "tclDecls.h"

/*
 * Include platform specific public function declarations that are accessible
 * via the stubs table.
 */

#if defined(BUILD_tcl)
#   undef TCLAPI
#   define TCLAPI MODULE_SCOPE
#endif

#include "tclPlatDecls.h"

/*
 *----------------------------------------------------------------------------
 * The following declarations either map ckalloc and ckfree to malloc and
 * free, or they map them to functions with all sorts of debugging hooks

Changes to generic/tclIO.c.

1
2
3
4
5
6
7
8

9
10
11
12
13
14
15
/*
 * tclIO.c --
 *
 *	This file provides the generic portions (those that are the same on
 *	all platforms and for all channel types) of Tcl's IO facilities.
 *
 * Copyright (c) 1998-2000 Ajuba Solutions
 * Copyright (c) 1995-1997 Sun Microsystems, Inc.

 *
 * See the file "license.terms" for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 */

#include "tclInt.h"
#include "tclIO.h"








>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/*
 * tclIO.c --
 *
 *	This file provides the generic portions (those that are the same on
 *	all platforms and for all channel types) of Tcl's IO facilities.
 *
 * Copyright (c) 1998-2000 Ajuba Solutions
 * Copyright (c) 1995-1997 Sun Microsystems, Inc.
 * Contributions from Don Porter, NIST, 2014. (not subject to US copyright)
 *
 * See the file "license.terms" for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 */

#include "tclInt.h"
#include "tclIO.h"
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197

198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
			    Channel *chanPtr);
static int		CloseChannel(Tcl_Interp *interp, Channel *chanPtr,
			    int errorCode);
static int		CloseChannelPart(Tcl_Interp *interp, Channel *chanPtr,
			    int errorCode, int flags);
static int		CloseWrite(Tcl_Interp *interp, Channel *chanPtr);
static void		CommonGetsCleanup(Channel *chanPtr);
static int		CopyAndTranslateBuffer(ChannelState *statePtr,
			    char *result, int space);
static int		CopyBuffer(Channel *chanPtr, char *result, int space);
static int		CopyData(CopyState *csPtr, int mask);
static void		CopyEventProc(ClientData clientData, int mask);
static void		CreateScriptRecord(Tcl_Interp *interp,
			    Channel *chanPtr, int mask, Tcl_Obj *scriptPtr);
static void		DeleteChannelTable(ClientData clientData,
			    Tcl_Interp *interp);
static void		DeleteScriptRecord(Tcl_Interp *interp,
			    Channel *chanPtr, int mask);
static int		DetachChannel(Tcl_Interp *interp, Tcl_Channel chan);
static void		DiscardInputQueued(ChannelState *statePtr,
			    int discardSavedBuffers);
static void		DiscardOutputQueued(ChannelState *chanPtr);
static int		DoRead(Channel *chanPtr, char *srcPtr, int slen, int allowShortReads);

static int		DoReadChars(Channel *chan, Tcl_Obj *objPtr, int toRead,
			    int appendFlag);
static int		FilterInputBytes(Channel *chanPtr,
			    GetsState *statePtr);
static int		FlushChannel(Tcl_Interp *interp, Channel *chanPtr,
			    int calledFromAsyncFlush);
static int		TclGetsObjBinary(Tcl_Channel chan, Tcl_Obj *objPtr);
static Tcl_Encoding	GetBinaryEncoding();
static void		FreeBinaryEncoding(ClientData clientData);
static Tcl_HashTable *	GetChannelTable(Tcl_Interp *interp);
static int		GetInput(Channel *chanPtr);
static int		HaveVersion(const Tcl_ChannelType *typePtr,
			    Tcl_ChannelTypeVersion minimumVersion);
static void		PeekAhead(Channel *chanPtr, char **dstEndPtr,
			    GetsState *gsPtr);
static int		ReadBytes(ChannelState *statePtr, Tcl_Obj *objPtr,
			    int charsLeft, int *offsetPtr);
static int		ReadChars(ChannelState *statePtr, Tcl_Obj *objPtr,
			    int charsLeft, int *offsetPtr, int *factorPtr);
static void		RecycleBuffer(ChannelState *statePtr,
			    ChannelBuffer *bufPtr, int mustDiscard);
static int		StackSetBlockMode(Channel *chanPtr, int mode);
static int		SetBlockMode(Tcl_Interp *interp, Channel *chanPtr,
			    int mode);
static void		StopCopy(CopyState *csPtr);
static int		TranslateInputEOL(ChannelState *statePtr, char *dst,
			    const char *src, int *dstLenPtr, int *srcLenPtr);
static void		UpdateInterest(Channel *chanPtr);
static int		Write(Channel *chanPtr, const char *src,
			    int srcLen, Tcl_Encoding encoding);
static Tcl_Obj *	FixLevelCode(Tcl_Obj *msg);
static void		SpliceChannel(Tcl_Channel chan);
static void		CutChannel(Tcl_Channel chan);







<
<













|
>
















|

|






|







176
177
178
179
180
181
182


183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
			    Channel *chanPtr);
static int		CloseChannel(Tcl_Interp *interp, Channel *chanPtr,
			    int errorCode);
static int		CloseChannelPart(Tcl_Interp *interp, Channel *chanPtr,
			    int errorCode, int flags);
static int		CloseWrite(Tcl_Interp *interp, Channel *chanPtr);
static void		CommonGetsCleanup(Channel *chanPtr);


static int		CopyBuffer(Channel *chanPtr, char *result, int space);
static int		CopyData(CopyState *csPtr, int mask);
static void		CopyEventProc(ClientData clientData, int mask);
static void		CreateScriptRecord(Tcl_Interp *interp,
			    Channel *chanPtr, int mask, Tcl_Obj *scriptPtr);
static void		DeleteChannelTable(ClientData clientData,
			    Tcl_Interp *interp);
static void		DeleteScriptRecord(Tcl_Interp *interp,
			    Channel *chanPtr, int mask);
static int		DetachChannel(Tcl_Interp *interp, Tcl_Channel chan);
static void		DiscardInputQueued(ChannelState *statePtr,
			    int discardSavedBuffers);
static void		DiscardOutputQueued(ChannelState *chanPtr);
static int		DoRead(Channel *chanPtr, char *dst, int bytesToRead,
			    int allowShortReads);
static int		DoReadChars(Channel *chan, Tcl_Obj *objPtr, int toRead,
			    int appendFlag);
static int		FilterInputBytes(Channel *chanPtr,
			    GetsState *statePtr);
static int		FlushChannel(Tcl_Interp *interp, Channel *chanPtr,
			    int calledFromAsyncFlush);
static int		TclGetsObjBinary(Tcl_Channel chan, Tcl_Obj *objPtr);
static Tcl_Encoding	GetBinaryEncoding();
static void		FreeBinaryEncoding(ClientData clientData);
static Tcl_HashTable *	GetChannelTable(Tcl_Interp *interp);
static int		GetInput(Channel *chanPtr);
static int		HaveVersion(const Tcl_ChannelType *typePtr,
			    Tcl_ChannelTypeVersion minimumVersion);
static void		PeekAhead(Channel *chanPtr, char **dstEndPtr,
			    GetsState *gsPtr);
static int		ReadBytes(ChannelState *statePtr, Tcl_Obj *objPtr,
			    int charsLeft);
static int		ReadChars(ChannelState *statePtr, Tcl_Obj *objPtr,
			    int charsLeft, int *factorPtr);
static void		RecycleBuffer(ChannelState *statePtr,
			    ChannelBuffer *bufPtr, int mustDiscard);
static int		StackSetBlockMode(Channel *chanPtr, int mode);
static int		SetBlockMode(Tcl_Interp *interp, Channel *chanPtr,
			    int mode);
static void		StopCopy(CopyState *csPtr);
static void		TranslateInputEOL(ChannelState *statePtr, char *dst,
			    const char *src, int *dstLenPtr, int *srcLenPtr);
static void		UpdateInterest(Channel *chanPtr);
static int		Write(Channel *chanPtr, const char *src,
			    int srcLen, Tcl_Encoding encoding);
static Tcl_Obj *	FixLevelCode(Tcl_Obj *msg);
static void		SpliceChannel(Tcl_Channel chan);
static void		CutChannel(Tcl_Channel chan);
1743
1744
1745
1746
1747
1748
1749




1750
1751
1752
1753
1754
1755
1756
    if ((mask & TCL_WRITABLE) != 0) {
	CopyState *csPtrR = statePtr->csPtrR;
	CopyState *csPtrW = statePtr->csPtrW;

	statePtr->csPtrR = NULL;
	statePtr->csPtrW = NULL;





	if (Tcl_Flush((Tcl_Channel) prevChanPtr) != TCL_OK) {
	    statePtr->csPtrR = csPtrR;
	    statePtr->csPtrW = csPtrW;
	    if (interp) {
		Tcl_SetObjResult(interp, Tcl_ObjPrintf(
                        "could not flush channel \"%s\"",
			Tcl_GetChannelName(prevChan)));







>
>
>
>







1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
    if ((mask & TCL_WRITABLE) != 0) {
	CopyState *csPtrR = statePtr->csPtrR;
	CopyState *csPtrW = statePtr->csPtrW;

	statePtr->csPtrR = NULL;
	statePtr->csPtrW = NULL;

	/*
	 * TODO: Examine what can go wrong if Tcl_Flush() call disturbs
	 * the stacking state of this channel during its operations.
	 */
	if (Tcl_Flush((Tcl_Channel) prevChanPtr) != TCL_OK) {
	    statePtr->csPtrR = csPtrR;
	    statePtr->csPtrW = csPtrW;
	    if (interp) {
		Tcl_SetObjResult(interp, Tcl_ObjPrintf(
                        "could not flush channel \"%s\"",
			Tcl_GetChannelName(prevChan)));
1774
1775
1776
1777
1778
1779
1780

1781
1782
1783

1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
     * the channel itself. We use the buffers in the channel below the new
     * transformation to hold the data. In the future this allows us to write
     * transformations which pre-read data and push the unused part back when
     * they are going away.
     */

    if (((mask & TCL_READABLE) != 0) && (statePtr->inQueueHead != NULL)) {

	/*
	 * Remark: It is possible that the channel buffers contain data from
	 * some earlier push-backs.

	 */

	statePtr->inQueueTail->nextPtr = prevChanPtr->inQueueHead;
	prevChanPtr->inQueueHead = statePtr->inQueueHead;

	if (prevChanPtr->inQueueTail == NULL) {
	    prevChanPtr->inQueueTail = statePtr->inQueueTail;
	}

	statePtr->inQueueHead = NULL;
	statePtr->inQueueTail = NULL;
    }

    chanPtr = ckalloc(sizeof(Channel));








>

<
|
>


|
|

|
|
<







1778
1779
1780
1781
1782
1783
1784
1785
1786

1787
1788
1789
1790
1791
1792
1793
1794
1795

1796
1797
1798
1799
1800
1801
1802
     * the channel itself. We use the buffers in the channel below the new
     * transformation to hold the data. In the future this allows us to write
     * transformations which pre-read data and push the unused part back when
     * they are going away.
     */

    if (((mask & TCL_READABLE) != 0) && (statePtr->inQueueHead != NULL)) {

	/*

	 * When statePtr->inQueueHead is not NULL, we know
	 * prevChanPtr->inQueueHead must be NULL.
	 */

	assert(prevChanPtr->inQueueHead == NULL);
	assert(prevChanPtr->inQueueTail == NULL);

	prevChanPtr->inQueueHead = statePtr->inQueueHead;
	prevChanPtr->inQueueTail = statePtr->inQueueTail;


	statePtr->inQueueHead = NULL;
	statePtr->inQueueTail = NULL;
    }

    chanPtr = ckalloc(sizeof(Channel));

1872
1873
1874
1875
1876
1877
1878







1879
1880
1881
1882
1883
1884
1885
	/*
	 * Instead of manipulating the per-thread / per-interp list/hashtable
	 * of registered channels we wind down the state of the
	 * transformation, and then restore the state of underlying channel
	 * into the old structure.
	 */








	Channel *downChanPtr = chanPtr->downChanPtr;

	/*
	 * Flush the buffers. This ensures that any data still in them at this
	 * time _is_ handled by the transformation we are unstacking right
	 * now. Restrict this to writable channels. Take care to hide a
	 * possible bg-copy in progress from Tcl_Flush and the







>
>
>
>
>
>
>







1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
	/*
	 * Instead of manipulating the per-thread / per-interp list/hashtable
	 * of registered channels we wind down the state of the
	 * transformation, and then restore the state of underlying channel
	 * into the old structure.
	 */

	/*
	 * TODO: Figure out how to handle the situation where the chan
	 * operations called below by this unstacking operation cause
	 * another unstacking recursively.  In that case the downChanPtr
	 * value we're holding on to will not be the right thing.
	 */

	Channel *downChanPtr = chanPtr->downChanPtr;

	/*
	 * Flush the buffers. This ensures that any data still in them at this
	 * time _is_ handled by the transformation we are unstacking right
	 * now. Restrict this to writable channels. Take care to hide a
	 * possible bg-copy in progress from Tcl_Flush and the
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
	chanPtr->typePtr = NULL;

	/*
	 * AK: Tcl_NotifyChannel may hold a reference to this block of memory
	 */

	Tcl_EventuallyFree(chanPtr, TCL_DYNAMIC);
	UpdateInterest(downChanPtr);

	if (result != 0) {
	    Tcl_SetErrno(result);

	    /*
	     * TIP #219, Tcl Channel Reflection API.
	     * Move error messages put by the driver into the chan/ip bypass







|







1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
	chanPtr->typePtr = NULL;

	/*
	 * AK: Tcl_NotifyChannel may hold a reference to this block of memory
	 */

	Tcl_EventuallyFree(chanPtr, TCL_DYNAMIC);
	UpdateInterest(statePtr->topChanPtr);

	if (result != 0) {
	    Tcl_SetErrno(result);

	    /*
	     * TIP #219, Tcl Channel Reflection API.
	     * Move error messages put by the driver into the chan/ip bypass
2296
2297
2298
2299
2300
2301
2302



2303
2304
2305
2306
2307
2308
2309
    return bufPtr;
}

static void
PreserveChannelBuffer(
    ChannelBuffer *bufPtr)
{



    bufPtr->refCount++;
}

static void
ReleaseChannelBuffer(
    ChannelBuffer *bufPtr)
{







>
>
>







2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
    return bufPtr;
}

static void
PreserveChannelBuffer(
    ChannelBuffer *bufPtr)
{
    if (bufPtr->refCount == 0) {
	Tcl_Panic("Reuse of ChannelBuffer! %p", bufPtr);
    }
    bufPtr->refCount++;
}

static void
ReleaseChannelBuffer(
    ChannelBuffer *bufPtr)
{
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374

    if (mustDiscard) {
	ReleaseChannelBuffer(bufPtr);
	return;
    }

    /*
     * Only save buffers which are at least as big as the requested buffersize
     * for the channel. This is to honor dynamic changes of the buffersize
     * made by the user.
     */

    if ((bufPtr->bufLength - BUFFER_PADDING) < statePtr->bufSize) {
	ReleaseChannelBuffer(bufPtr);
	return;
    }

    /*
     * Only save buffers for the input queue if the channel is readable.
     */







|
|



|







2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388

    if (mustDiscard) {
	ReleaseChannelBuffer(bufPtr);
	return;
    }

    /*
     * Only save buffers which have the requested buffersize for the
     * channel. This is to honor dynamic changes of the buffersize
     * made by the user.
     */

    if ((bufPtr->bufLength - BUFFER_PADDING) != statePtr->bufSize) {
	ReleaseChannelBuffer(bufPtr);
	return;
    }

    /*
     * Only save buffers for the input queue if the channel is readable.
     */
2594
2595
2596
2597
2598
2599
2600

2601
2602
2603
2604
2605
2606
2607
	if (written < 0) {
	    /*
	     * If the last attempt to write was interrupted, simply retry.
	     */

	    if (errorCode == EINTR) {
		errorCode = 0;

		continue;
	    }

	    /*
	     * If the channel is non-blocking and we would have blocked, start
	     * a background flushing handler and break out of the loop.
	     */







>







2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
	if (written < 0) {
	    /*
	     * If the last attempt to write was interrupted, simply retry.
	     */

	    if (errorCode == EINTR) {
		errorCode = 0;
		ReleaseChannelBuffer(bufPtr);
		continue;
	    }

	    /*
	     * If the channel is non-blocking and we would have blocked, start
	     * a background flushing handler and break out of the loop.
	     */
2615
2616
2617
2618
2619
2620
2621

2622
2623
2624
2625
2626
2627
2628
		 */

		if (!GotFlag(statePtr, BG_FLUSH_SCHEDULED) && !TclInExit()) {
		    SetFlag(statePtr, BG_FLUSH_SCHEDULED);
		    UpdateInterest(chanPtr);
		}
		errorCode = 0;

		break;
	    }

	    /*
	     * Decide whether to report the error upwards or defer it.
	     */








>







2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
		 */

		if (!GotFlag(statePtr, BG_FLUSH_SCHEDULED) && !TclInExit()) {
		    SetFlag(statePtr, BG_FLUSH_SCHEDULED);
		    UpdateInterest(chanPtr);
		}
		errorCode = 0;
		ReleaseChannelBuffer(bufPtr);
		break;
	    }

	    /*
	     * Decide whether to report the error upwards or defer it.
	     */

2676
2677
2678
2679
2680
2681
2682

2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697

	    /*
	     * When we get an error we throw away all the output currently
	     * queued.
	     */

	    DiscardOutputQueued(statePtr);

	    continue;
	} else {
	    wroteSome = 1;
	}

	if (!IsBufferEmpty(bufPtr)) {
	    bufPtr->nextRemoved += written;
	}

	/*
	 * If this buffer is now empty, recycle it.
	 */

	if (IsBufferEmpty(bufPtr)) {
	    statePtr->outQueueHead = bufPtr->nextPtr;







>





<
|
<







2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704

2705

2706
2707
2708
2709
2710
2711
2712

	    /*
	     * When we get an error we throw away all the output currently
	     * queued.
	     */

	    DiscardOutputQueued(statePtr);
	    ReleaseChannelBuffer(bufPtr);
	    continue;
	} else {
	    wroteSome = 1;
	}


	bufPtr->nextRemoved += written;


	/*
	 * If this buffer is now empty, recycle it.
	 */

	if (IsBufferEmpty(bufPtr)) {
	    statePtr->outQueueHead = bufPtr->nextPtr;
4010
4011
4012
4013
4014
4015
4016

4017
4018
4019
4020
4021
4022
4023

static int
WillRead(
    Channel *chanPtr)
{
    if (chanPtr->typePtr == NULL) {
	/* Prevent read attempts on a closed channel */

	Tcl_SetErrno(EINVAL);
	return -1;
    }
    if ((chanPtr->typePtr->seekProc != NULL)
            && (Tcl_OutputBuffered((Tcl_Channel) chanPtr) > 0)) {
        if ((chanPtr->state->curOutPtr != NULL)
                && IsBufferReady(chanPtr->state->curOutPtr)) {







>







4025
4026
4027
4028
4029
4030
4031
4032
4033
4034
4035
4036
4037
4038
4039

static int
WillRead(
    Channel *chanPtr)
{
    if (chanPtr->typePtr == NULL) {
	/* Prevent read attempts on a closed channel */
        DiscardInputQueued(chanPtr->state, 0);
	Tcl_SetErrno(EINVAL);
	return -1;
    }
    if ((chanPtr->typePtr->seekProc != NULL)
            && (Tcl_OutputBuffered((Tcl_Channel) chanPtr) > 0)) {
        if ((chanPtr->state->curOutPtr != NULL)
                && IsBufferReady(chanPtr->state->curOutPtr)) {
4156
4157
4158
4159
4160
4161
4162
4163
4164
4165
4166
4167
4168
4169
4170
4171
4172
	    }
	
	    result |= Tcl_UtfToExternal(NULL, encoding, nl, nlLen,
		statePtr->outputEncodingFlags,
		&statePtr->outputEncodingState, dst,
		dstLen + BUFFER_PADDING, &srcRead, &dstWrote, NULL);

	    if (srcRead != nlLen) {
		Tcl_Panic("Can This Happen?");
	    }

	    bufPtr->nextAdded += dstWrote;
	    src++;
	    srcLen--;
	    total += dstWrote;
	    dst += dstWrote;
	    dstLen -= dstWrote;







|
<
<







4172
4173
4174
4175
4176
4177
4178
4179


4180
4181
4182
4183
4184
4185
4186
	    }
	
	    result |= Tcl_UtfToExternal(NULL, encoding, nl, nlLen,
		statePtr->outputEncodingFlags,
		&statePtr->outputEncodingState, dst,
		dstLen + BUFFER_PADDING, &srcRead, &dstWrote, NULL);

	    assert (srcRead == nlLen);



	    bufPtr->nextAdded += dstWrote;
	    src++;
	    srcLen--;
	    total += dstWrote;
	    dst += dstWrote;
	    dstLen -= dstWrote;
4190
4191
4192
4193
4194
4195
4196

4197
4198
4199
4200
4201
4202
4203

	if ((srcLen + saved == 0) && (result == TCL_OK)) {
	    endEncoding = 0;
	}

	if (IsBufferFull(bufPtr)) {
	    if (FlushChannel(NULL, chanPtr, 0) != 0) {

		return -1;
	    }
	    flushed += statePtr->bufSize;
	    if (saved == 0 || src[-1] != '\n') {
		needNlFlush = 0;
	    }
	}







>







4204
4205
4206
4207
4208
4209
4210
4211
4212
4213
4214
4215
4216
4217
4218

	if ((srcLen + saved == 0) && (result == TCL_OK)) {
	    endEncoding = 0;
	}

	if (IsBufferFull(bufPtr)) {
	    if (FlushChannel(NULL, chanPtr, 0) != 0) {
		ReleaseChannelBuffer(bufPtr);
		return -1;
	    }
	    flushed += statePtr->bufSize;
	    if (saved == 0 || src[-1] != '\n') {
		needNlFlush = 0;
	    }
	}
4543
4544
4545
4546
4547
4548
4549
4550


4551
4552


4553
4554
4555
4556
4557
4558
4559
     */

  gotEOL:
    /*
     * Regenerate the top channel, in case it was changed due to
     * self-modifying reflected transforms.
     */
    /*


    chanPtr = statePtr->topChanPtr;
     */



    bufPtr = gs.bufPtr;
    if (bufPtr == NULL) {
	Tcl_Panic("Tcl_GetsObj: gotEOL reached with bufPtr==NULL");
    }
    statePtr->inputEncodingState = gs.state;
    Tcl_ExternalToUtf(NULL, gs.encoding, RemovePoint(bufPtr), gs.rawRead,







|
>
>
|
<
>
>







4558
4559
4560
4561
4562
4563
4564
4565
4566
4567
4568

4569
4570
4571
4572
4573
4574
4575
4576
4577
     */

  gotEOL:
    /*
     * Regenerate the top channel, in case it was changed due to
     * self-modifying reflected transforms.
     */

    if (chanPtr != statePtr->topChanPtr) {
	Tcl_Release(chanPtr);
	chanPtr = statePtr->topChanPtr;

	Tcl_Preserve(chanPtr);
    }

    bufPtr = gs.bufPtr;
    if (bufPtr == NULL) {
	Tcl_Panic("Tcl_GetsObj: gotEOL reached with bufPtr==NULL");
    }
    statePtr->inputEncodingState = gs.state;
    Tcl_ExternalToUtf(NULL, gs.encoding, RemovePoint(bufPtr), gs.rawRead,
4579
4580
4581
4582
4583
4584
4585
4586


4587
4588


4589
4590
4591
4592
4593
4594
4595
     */

  restore:
    /*
     * Regenerate the top channel, in case it was changed due to
     * self-modifying reflected transforms.
     */
    /*


    chanPtr = statePtr->topChanPtr;
     */


    bufPtr = statePtr->inQueueHead;
    if (bufPtr != NULL) {
	bufPtr->nextRemoved = oldRemoved;
	bufPtr = bufPtr->nextPtr;
    }

    for ( ; bufPtr != NULL; bufPtr = bufPtr->nextPtr) {







<
>
>
|
<
>
>







4597
4598
4599
4600
4601
4602
4603

4604
4605
4606

4607
4608
4609
4610
4611
4612
4613
4614
4615
     */

  restore:
    /*
     * Regenerate the top channel, in case it was changed due to
     * self-modifying reflected transforms.
     */

    if (chanPtr != statePtr->topChanPtr) {
	Tcl_Release(chanPtr);
	chanPtr = statePtr->topChanPtr;

	Tcl_Preserve(chanPtr);
    }
    bufPtr = statePtr->inQueueHead;
    if (bufPtr != NULL) {
	bufPtr->nextRemoved = oldRemoved;
	bufPtr = bufPtr->nextPtr;
    }

    for ( ; bufPtr != NULL; bufPtr = bufPtr->nextPtr) {
4621
4622
4623
4624
4625
4626
4627
4628


4629
4630


4631
4632
4633
4634
4635
4636
4637
     */

  done:
    /*
     * Regenerate the top channel, in case it was changed due to
     * self-modifying reflected transforms.
     */
    /*


    chanPtr = statePtr->topChanPtr;
     */


    UpdateInterest(chanPtr);
    Tcl_Release(chanPtr);
    return copiedTotal;
}

/*
 *---------------------------------------------------------------------------







<
>
>
|
<
>
>







4641
4642
4643
4644
4645
4646
4647

4648
4649
4650

4651
4652
4653
4654
4655
4656
4657
4658
4659
     */

  done:
    /*
     * Regenerate the top channel, in case it was changed due to
     * self-modifying reflected transforms.
     */

    if (chanPtr != statePtr->topChanPtr) {
	Tcl_Release(chanPtr);
	chanPtr = statePtr->topChanPtr;

	Tcl_Preserve(chanPtr);
    }
    UpdateInterest(chanPtr);
    Tcl_Release(chanPtr);
    return copiedTotal;
}

/*
 *---------------------------------------------------------------------------
4955
4956
4957
4958
4959
4960
4961
4962
4963
4964
4965
4966
4967
4968
4969
FilterInputBytes(
    Channel *chanPtr,		/* Channel to read. */
    GetsState *gsPtr)		/* Current state of gets operation. */
{
    ChannelState *statePtr = chanPtr->state;
				/* State info for channel */
    ChannelBuffer *bufPtr;
    char *raw, *rawStart, *dst;
    int offset, toRead, dstNeeded, spaceLeft, result, rawLen;
    Tcl_Obj *objPtr;
#define ENCODING_LINESIZE 20	/* Lower bound on how many bytes to convert at
				 * a time. Since we don't know a priori how
				 * many bytes of storage this many source
				 * bytes will use, we actually need at least
				 * ENCODING_LINESIZE * TCL_MAX_UTF bytes of







|







4977
4978
4979
4980
4981
4982
4983
4984
4985
4986
4987
4988
4989
4990
4991
FilterInputBytes(
    Channel *chanPtr,		/* Channel to read. */
    GetsState *gsPtr)		/* Current state of gets operation. */
{
    ChannelState *statePtr = chanPtr->state;
				/* State info for channel */
    ChannelBuffer *bufPtr;
    char *raw, *dst;
    int offset, toRead, dstNeeded, spaceLeft, result, rawLen;
    Tcl_Obj *objPtr;
#define ENCODING_LINESIZE 20	/* Lower bound on how many bytes to convert at
				 * a time. Since we don't know a priori how
				 * many bytes of storage this many source
				 * bytes will use, we actually need at least
				 * ENCODING_LINESIZE * TCL_MAX_UTF bytes of
5017
5018
5019
5020
5021
5022
5023
5024
5025
5026
5027
5028
5029
5030
5031
5032

    /*
     * Convert some of the bytes from the channel buffer to UTF-8. Space in
     * objPtr's string rep is used to hold the UTF-8 characters. Grow the
     * string rep if we need more space.
     */

    rawStart = RemovePoint(bufPtr);
    raw = rawStart;
    rawLen = BytesLeft(bufPtr);

    dst = *gsPtr->dstPtr;
    offset = dst - objPtr->bytes;
    toRead = ENCODING_LINESIZE;
    if (toRead > rawLen) {
	toRead = rawLen;







|
<







5039
5040
5041
5042
5043
5044
5045
5046

5047
5048
5049
5050
5051
5052
5053

    /*
     * Convert some of the bytes from the channel buffer to UTF-8. Space in
     * objPtr's string rep is used to hold the UTF-8 characters. Grow the
     * string rep if we need more space.
     */

    raw = RemovePoint(bufPtr);

    rawLen = BytesLeft(bufPtr);

    dst = *gsPtr->dstPtr;
    offset = dst - objPtr->bytes;
    toRead = ENCODING_LINESIZE;
    if (toRead > rawLen) {
	toRead = rawLen;
5371
5372
5373
5374
5375
5376
5377
5378
5379
5380
5381
5382
5383
5384
5385
5386
5387
5388
5389
5390
5391
5392
5393
5394
5395
5396
5397
5398
5399
5400
5401
5402
5403
5404
5405

5406
5407
5408
5409
5410
5411
5412
5413
5414
5415
5416
5417
5418
5419
5420
5421
5422
5423
5424
5425
5426
5427
5428
5429
5430
5431
5432
5433
5434
5435
5436
5437
5438
5439
5440
	    if (GotFlag(statePtr, CHANNEL_BLOCKED)) {
		if (GotFlag(statePtr, CHANNEL_NONBLOCKING)) {
		    goto done;
		}
		ResetFlag(statePtr, CHANNEL_BLOCKED);
	    }

#ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING
	    /*
	     * [Bug 943274]. Better emulation of non-blocking channels for
	     * channels without BlockModeProc, by keeping track of true
	     * fileevents generated by the OS == Data waiting and reading if
	     * and only if we are sure to have data.
	     */

	    if (GotFlag(statePtr, CHANNEL_NONBLOCKING) &&
		    (Tcl_ChannelBlockModeProc(chanPtr->typePtr) == NULL) &&
		    !GotFlag(statePtr, CHANNEL_HAS_MORE_DATA)) {
		/*
		 * We bypass the driver; it would block as no data is
		 * available.
		 */

		nread = -1;
		result = EWOULDBLOCK;
	    } else
#endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */
	    {
		/*
		 * Now go to the driver to get as much as is possible to fill
		 * the remaining request. Do all the error handling by
		 * ourselves. The code was stolen from 'GetInput' and slightly
		 * adapted (different return value here).
		 *
		 * The case of 'bytesToRead == 0' at this point cannot happen.

		 */

		nread = ChanRead(chanPtr, bufPtr + copied,
			bytesToRead - copied, &result);
	    }

	    if (nread > 0) {
		/*
		 * If we get a short read, signal up that we may be BLOCKED.
		 * We should avoid calling the driver because on some
		 * platforms we will block in the low level reading code even
		 * though the channel is set into nonblocking mode.
		 */

		if (nread < (bytesToRead - copied)) {
		    SetFlag(statePtr, CHANNEL_BLOCKED);
		}

#ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING
		if (nread <= (bytesToRead - copied)) {
		    /*
		     * [Bug 943274] We have read the available data, clear
		     * flag.
		     */

		    ResetFlag(statePtr, CHANNEL_HAS_MORE_DATA);
		}
#endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */
	    } else if (nread == 0) {
		SetFlag(statePtr, CHANNEL_EOF);
		statePtr->inputEncodingFlags |= TCL_ENCODING_END;

	    } else if (nread < 0) {
		if ((result == EWOULDBLOCK) || (result == EAGAIN)) {
		    if (copied > 0) {







<

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
|
|
|
|
|
>
|

|
|
<












<
<
<
<
<
<
<
<
<
<
<







5392
5393
5394
5395
5396
5397
5398

5399




















5400
5401
5402
5403
5404
5405
5406
5407
5408
5409
5410

5411
5412
5413
5414
5415
5416
5417
5418
5419
5420
5421
5422











5423
5424
5425
5426
5427
5428
5429
	    if (GotFlag(statePtr, CHANNEL_BLOCKED)) {
		if (GotFlag(statePtr, CHANNEL_NONBLOCKING)) {
		    goto done;
		}
		ResetFlag(statePtr, CHANNEL_BLOCKED);
	    }


	    /*




















	     * Now go to the driver to get as much as is possible to
	     * fill the remaining request. Do all the error handling by
	     * ourselves. The code was stolen from 'GetInput' and
	     * slightly adapted (different return value here).
	     *
	     * The case of 'bytesToRead == 0' at this point cannot
	     * happen.
	     */

	    nread = ChanRead(chanPtr, bufPtr + copied,
		    bytesToRead - copied, &result);


	    if (nread > 0) {
		/*
		 * If we get a short read, signal up that we may be BLOCKED.
		 * We should avoid calling the driver because on some
		 * platforms we will block in the low level reading code even
		 * though the channel is set into nonblocking mode.
		 */

		if (nread < (bytesToRead - copied)) {
		    SetFlag(statePtr, CHANNEL_BLOCKED);
		}











	    } else if (nread == 0) {
		SetFlag(statePtr, CHANNEL_EOF);
		statePtr->inputEncodingFlags |= TCL_ENCODING_END;

	    } else if (nread < 0) {
		if ((result == EWOULDBLOCK) || (result == EAGAIN)) {
		    if (copied > 0) {
5554
5555
5556
5557
5558
5559
5560
5561
5562

5563
5564
5565
5566
5567
5568
5569
5570
5571
5572
5573




5574
5575
5576
5577
5578
5579
5580
5581
5582
5583


5584
5585
5586
5587
5588
5589
5590
5591
5592
5593
5594
5595
5596
5597
5598
5599
5600
5601
5602
5603
5604
5605
5606
5607
5608
5609
5610
5611
				 * will be appended to the object. Otherwise,
				 * the data will replace the existing contents
				 * of the object. */
{
    ChannelState *statePtr = chanPtr->state;
				/* State info for channel */
    ChannelBuffer *bufPtr;
    int offset, factor, copied, copiedNow, result;
    Tcl_Encoding encoding;

#define UTF_EXPANSION_FACTOR	1024

    /*
     * This operation should occur at the top of a channel stack.
     */

    chanPtr = statePtr->topChanPtr;
    encoding = statePtr->encoding;
    factor = UTF_EXPANSION_FACTOR;
    Tcl_Preserve(chanPtr);





    if (appendFlag == 0) {
	if (encoding == NULL) {
	    Tcl_SetByteArrayLength(objPtr, 0);
	} else {
	    Tcl_SetObjLength(objPtr, 0);

	    /*
	     * We're going to access objPtr->bytes directly, so we must ensure
	     * that this is actually a string object (otherwise it might have
	     * been pure Unicode).


	     */

	    TclGetString(objPtr);
	}
	offset = 0;
    } else {
	if (encoding == NULL) {
	    Tcl_GetByteArrayFromObj(objPtr, &offset);
	} else {
	    TclGetStringFromObj(objPtr, &offset);
	}
    }

    for (copied = 0; (unsigned) toRead > 0; ) {
	copiedNow = -1;
	if (statePtr->inQueueHead != NULL) {
	    if (encoding == NULL) {
		copiedNow = ReadBytes(statePtr, objPtr, toRead, &offset);
	    } else {
		copiedNow = ReadChars(statePtr, objPtr, toRead, &offset,
			&factor);
	    }

	    /*
	     * If the current buffer is empty recycle it.
	     */

	    bufPtr = statePtr->inQueueHead;







|

>











>
>
>
>

|








>
>




<
<
<
<
<
<
<





|
|

|
<







5543
5544
5545
5546
5547
5548
5549
5550
5551
5552
5553
5554
5555
5556
5557
5558
5559
5560
5561
5562
5563
5564
5565
5566
5567
5568
5569
5570
5571
5572
5573
5574
5575
5576
5577
5578
5579
5580
5581
5582
5583







5584
5585
5586
5587
5588
5589
5590
5591
5592

5593
5594
5595
5596
5597
5598
5599
				 * will be appended to the object. Otherwise,
				 * the data will replace the existing contents
				 * of the object. */
{
    ChannelState *statePtr = chanPtr->state;
				/* State info for channel */
    ChannelBuffer *bufPtr;
    int factor, copied, copiedNow, result;
    Tcl_Encoding encoding;
    int binaryMode;
#define UTF_EXPANSION_FACTOR	1024

    /*
     * This operation should occur at the top of a channel stack.
     */

    chanPtr = statePtr->topChanPtr;
    encoding = statePtr->encoding;
    factor = UTF_EXPANSION_FACTOR;
    Tcl_Preserve(chanPtr);

    binaryMode = (encoding == NULL)
	    && (statePtr->inputTranslation == TCL_TRANSLATE_LF) 
	    && (statePtr->inEofChar == '\0');

    if (appendFlag == 0) {
	if (binaryMode) {
	    Tcl_SetByteArrayLength(objPtr, 0);
	} else {
	    Tcl_SetObjLength(objPtr, 0);

	    /*
	     * We're going to access objPtr->bytes directly, so we must ensure
	     * that this is actually a string object (otherwise it might have
	     * been pure Unicode).
	     *
	     * Probably not needed anymore.
	     */

	    TclGetString(objPtr);
	}







    }

    for (copied = 0; (unsigned) toRead > 0; ) {
	copiedNow = -1;
	if (statePtr->inQueueHead != NULL) {
	    if (binaryMode) {
		copiedNow = ReadBytes(statePtr, objPtr, toRead);
	    } else {
		copiedNow = ReadChars(statePtr, objPtr, toRead, &factor);

	    }

	    /*
	     * If the current buffer is empty recycle it.
	     */

	    bufPtr = statePtr->inQueueHead;
5627
5628
5629
5630
5631
5632
5633





5634
5635
5636
5637
5638
5639
5640
5641
5642
5643
5644
5645
5646
5647
5648
5649
5650
5651
5652
5653
5654
5655
5656
5657
5658
5659
5660
5661
5662
5663
5664


5665
5666


5667
5668
5669
5670
5671
5672
5673
5674
5675
5676
5677
5678
5679
5680
5681
5682
5683
5684
5685
5686
5687
5688
5689
5690
5691
5692

5693
5694
5695
5696
5697
5698
5699
5700
5701
5702
5703
5704
5705
5706
5707
5708
5709
5710
5711
5712
5713
5714
5715
5716
5717
5718
5719
5720
5721
5722
5723
5724
5725
5726
5727
5728
5729
5730
5731
5732
5733
5734
5735
5736
5737
5738
5739
5740
5741
5742
5743
5744
5745
5746
5747
5748
5749
5750
5751
5752
5753
5754
5755
5756
5757
5758
5759
5760
5761
5762
5763
5764
5765
5766
5767
5768
5769
5770
5771
5772
5773
5774
5775
5776
5777
	    if (GotFlag(statePtr, CHANNEL_BLOCKED)) {
		if (GotFlag(statePtr, CHANNEL_NONBLOCKING)) {
		    break;
		}
		ResetFlag(statePtr, CHANNEL_BLOCKED);
	    }
	    result = GetInput(chanPtr);





	    if (result != 0) {
		if (result == EAGAIN) {
		    break;
		}
		copied = -1;
		goto done;
	    }
	} else {
	    copied += copiedNow;
	    toRead -= copiedNow;
	}
    }

    ResetFlag(statePtr, CHANNEL_BLOCKED);
    if (encoding == NULL) {
	Tcl_SetByteArrayLength(objPtr, offset);
    } else {
	Tcl_SetObjLength(objPtr, offset);
    }

    /*
     * Update the notifier state so we don't block while there is still data
     * in the buffers.
     */

  done:
    /*
     * Regenerate the top channel, in case it was changed due to
     * self-modifying reflected transforms.
     */
    /*


    chanPtr = statePtr->topChanPtr;
     */


    UpdateInterest(chanPtr);
    Tcl_Release(chanPtr);
    return copied;
}

/*
 *---------------------------------------------------------------------------
 *
 * ReadBytes --
 *
 *	Reads from the channel until the requested number of bytes have been
 *	seen, EOF is seen, or the channel would block. Bytes from the channel
 *	are stored in objPtr as a ByteArray object. EOL and EOF translation
 *	are done.
 *
 *	'bytesToRead' can safely be a very large number because space is only
 *	allocated to hold data read from the channel as needed.
 *
 * Results:
 *	The return value is the number of bytes appended to the object and
 *	*offsetPtr is filled with the total number of bytes in the object
 *	(greater than the return value if there were already bytes in the
 *	object).
 *
 * Side effects:
 *	None.

 *
 *---------------------------------------------------------------------------
 */

static int
ReadBytes(
    ChannelState *statePtr,	/* State of the channel to read. */
    Tcl_Obj *objPtr,		/* Input data is appended to this ByteArray
				 * object. Its length is how much space has
				 * been allocated to hold data, not how many
				 * bytes of data have been stored in the
				 * object. */
    int bytesToRead,		/* Maximum number of bytes to store, or < 0 to
				 * get all available bytes. Bytes are obtained
				 * from the first buffer in the queue - even
				 * if this number is larger than the number of
				 * bytes available in the first buffer, only
				 * the bytes from the first buffer are
				 * returned. */
    int *offsetPtr)		/* On input, contains how many bytes of objPtr
				 * have been used to hold data. On output,
				 * filled with how many bytes are now being
				 * used. */
{
    int toRead, srcLen, offset, length, srcRead, dstWrote;
    ChannelBuffer *bufPtr;
    char *src, *dst;

    offset = *offsetPtr;

    bufPtr = statePtr->inQueueHead;
    src = RemovePoint(bufPtr);
    srcLen = BytesLeft(bufPtr);

    toRead = bytesToRead;
    if ((unsigned) toRead > (unsigned) srcLen) {
	toRead = srcLen;
    }

    dst = (char *) Tcl_GetByteArrayFromObj(objPtr, &length);
    if (toRead > length - offset - 1) {
	/*
	 * Double the existing size of the object or make enough room to hold
	 * all the characters we may get from the source buffer, whichever is
	 * larger.
	 */

	length = offset * 2;
	if (offset < toRead) {
	    length = offset + toRead + 1;
	}
	dst = (char *) Tcl_SetByteArrayLength(objPtr, length);
    }
    dst += offset;

    if (GotFlag(statePtr, INPUT_NEED_NL)) {
	ResetFlag(statePtr, INPUT_NEED_NL);
	if ((srcLen == 0) || (*src != '\n')) {
	    *dst = '\r';
	    *offsetPtr += 1;
	    return 1;
	}
	*dst++ = '\n';
	src++;
	srcLen--;
	toRead--;
    }

    srcRead = srcLen;
    dstWrote = toRead;
    if (TranslateInputEOL(statePtr, dst, src, &dstWrote, &srcRead) != 0) {
	if (dstWrote == 0) {
	    return -1;
	}
    }
    bufPtr->nextRemoved += srcRead;
    *offsetPtr += dstWrote;
    return dstWrote;
}

/*
 *---------------------------------------------------------------------------
 *
 * ReadChars --
 *







>
>
>
>
>














<
<
<
<
<











<
>
>
|
<
>
>



















|
|
<
<


<
>












|






<
<
<
<

<
|
<
<
<
<
<
<
|
<
|
<
<
|
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
<
<
<
<
<
<
<
|
<
|







5615
5616
5617
5618
5619
5620
5621
5622
5623
5624
5625
5626
5627
5628
5629
5630
5631
5632
5633
5634
5635
5636
5637
5638
5639
5640





5641
5642
5643
5644
5645
5646
5647
5648
5649
5650
5651

5652
5653
5654

5655
5656
5657
5658
5659
5660
5661
5662
5663
5664
5665
5666
5667
5668
5669
5670
5671
5672
5673
5674
5675
5676
5677


5678
5679

5680
5681
5682
5683
5684
5685
5686
5687
5688
5689
5690
5691
5692
5693
5694
5695
5696
5697
5698
5699




5700

5701






5702

5703


5704
5705


























5706









5707

5708
5709
5710
5711
5712
5713
5714
5715
	    if (GotFlag(statePtr, CHANNEL_BLOCKED)) {
		if (GotFlag(statePtr, CHANNEL_NONBLOCKING)) {
		    break;
		}
		ResetFlag(statePtr, CHANNEL_BLOCKED);
	    }
	    result = GetInput(chanPtr);
	    if (chanPtr != statePtr->topChanPtr) {
		Tcl_Release(chanPtr);
		chanPtr = statePtr->topChanPtr;
		Tcl_Preserve(chanPtr);
	    }
	    if (result != 0) {
		if (result == EAGAIN) {
		    break;
		}
		copied = -1;
		goto done;
	    }
	} else {
	    copied += copiedNow;
	    toRead -= copiedNow;
	}
    }

    ResetFlag(statePtr, CHANNEL_BLOCKED);






    /*
     * Update the notifier state so we don't block while there is still data
     * in the buffers.
     */

  done:
    /*
     * Regenerate the top channel, in case it was changed due to
     * self-modifying reflected transforms.
     */

    if (chanPtr != statePtr->topChanPtr) {
	Tcl_Release(chanPtr);
	chanPtr = statePtr->topChanPtr;

	Tcl_Preserve(chanPtr);
    }
    UpdateInterest(chanPtr);
    Tcl_Release(chanPtr);
    return copied;
}

/*
 *---------------------------------------------------------------------------
 *
 * ReadBytes --
 *
 *	Reads from the channel until the requested number of bytes have been
 *	seen, EOF is seen, or the channel would block. Bytes from the channel
 *	are stored in objPtr as a ByteArray object. EOL and EOF translation
 *	are done.
 *
 *	'bytesToRead' can safely be a very large number because space is only
 *	allocated to hold data read from the channel as needed.
 *
 * Results:
 *	The return value is the number of bytes appended to the object, or
 *	-1 to indicate that zero bytes were read due to an EOF.


 *
 * Side effects:

 *	The storage of bytes in objPtr can cause (re-)allocation of memory.
 *
 *---------------------------------------------------------------------------
 */

static int
ReadBytes(
    ChannelState *statePtr,	/* State of the channel to read. */
    Tcl_Obj *objPtr,		/* Input data is appended to this ByteArray
				 * object. Its length is how much space has
				 * been allocated to hold data, not how many
				 * bytes of data have been stored in the
				 * object. */
    int bytesToRead)		/* Maximum number of bytes to store, or < 0 to
				 * get all available bytes. Bytes are obtained
				 * from the first buffer in the queue - even
				 * if this number is larger than the number of
				 * bytes available in the first buffer, only
				 * the bytes from the first buffer are
				 * returned. */




{

    ChannelBuffer *bufPtr = statePtr->inQueueHead;






    int srcLen = BytesLeft(bufPtr);

    int toRead = bytesToRead>srcLen || bytesToRead<0 ? srcLen : bytesToRead;



    TclAppendBytesToByteArray(objPtr, (unsigned char *) RemovePoint(bufPtr),


























	    toRead);









    bufPtr->nextRemoved += toRead;

    return toRead;
}

/*
 *---------------------------------------------------------------------------
 *
 * ReadChars --
 *
5807
5808
5809
5810
5811
5812
5813
5814
5815
5816
5817
5818
5819
5820
5821
5822
5823
5824
5825
5826
5827
5828
5829
5830
5831
5832
5833
5834
5835
5836
5837
5838



5839
5840
5841


5842

5843
5844
5845
5846
5847
5848
5849

5850
5851








5852
5853
5854
5855







5856
5857





5858
5859
5860
5861
5862
5863
5864
5865
5866
5867
5868




5869

5870


5871
5872
5873
5874
5875
5876


5877
5878



5879

5880





5881
5882
5883
5884

5885
5886
5887
5888
5889
5890
5891
5892
5893
5894
5895
5896
5897
5898
5899
5900

5901

5902






5903
5904
5905
5906
5907
5908
5909



5910
5911
5912
5913
5914
5915


5916
5917
5918

5919
5920
5921



5922
5923


5924




5925






5926






5927
5928
5929
5930
5931
5932
5933

5934
5935
5936


5937
5938



5939
5940



5941
5942


5943
5944
5945
5946

5947

5948
5949

5950

5951


5952








5953
5954

5955
5956

5957
5958
5959
5960
5961
5962
5963
5964
5965
5966




5967





5968



5969
5970
5971


5972
5973
5974
5975
5976
5977
5978
5979
5980



5981
5982
5983






5984
5985
5986


5987
5988



5989
5990




5991
5992

5993
5994
5995
5996
5997
5998
5999
6000
6001
6002
6003
6004
6005
6006
6007
6008
6009
6010
6011
6012
6013
6014
6015
6016

6017
6018
6019
6020
6021
6022
6023
6024
6025
6026
6027
6028
6029
6030
6031
6032
6033
6034
6035
6036
6037
6038
6039
6040
6041
6042
6043
6044
6045
6046
6047
6048
6049
6050
6051
6052
6053
6054
6055
6056
6057
6058
6059
6060
6061
6062
6063
6064
6065
6066
6067
6068
6069

6070
6071
6072
6073
6074
6075
6076
6077
6078
6079
6080
6081
6082
6083
6084
6085
6086
6087
6088
6089
6090
6091
6092
6093
6094
6095
6096
6097
6098
6099
6100
6101
6102
6103
6104
6105
6106
6107
6108
6109
6110
6111
6112
6113
6114
6115
6116
6117


6118
6119
6120





6121
6122
6123
6124
6125
6126

6127
6128
6129
6130
6131
6132
6133





6134





6135
6136

6137
6138
6139
6140
6141
6142
6143
6144
6145
6146
6147
6148
6149
6150
6151
6152
6153
6154
6155
6156
6157

6158
6159

6160
6161
6162
6163
6164
6165
6166
6167
6168
6169
6170
6171
6172

6173
6174

6175



6176
6177

6178


6179

6180
6181
6182
6183
6184
6185
6186
6187
6188
6189
6190
6191
6192
6193
6194
6195
6196
6197
6198




6199
6200

6201
6202
6203
6204
6205
6206
6207
6208
6209
6210
6211
6212
6213
6214

6215
6216
6217
6218
6219
6220

6221
6222

6223
6224
6225
6226
6227
6228
6229
6230
6231
6232
6233
6234
6235
6236
6237
6238
6239
6240
6241
6242
6243
6244
6245
    int charsToRead,		/* Maximum number of characters to store, or
				 * -1 to get all available characters.
				 * Characters are obtained from the first
				 * buffer in the queue -- even if this number
				 * is larger than the number of characters
				 * available in the first buffer, only the
				 * characters from the first buffer are
				 * returned. */
    int *offsetPtr,		/* On input, contains how many bytes of objPtr
				 * have been used to hold data. On output,
				 * filled with how many bytes are now being
				 * used. */
    int *factorPtr)		/* On input, contains a guess of how many
				 * bytes need to be allocated to hold the
				 * result of converting N source bytes to
				 * UTF-8. On output, contains another guess
				 * based on the data seen so far. */
{
    int toRead, factor, offset, spaceLeft, srcLen, dstNeeded;
    int srcRead, dstWrote, numChars, dstRead;
    ChannelBuffer *bufPtr;
    char *src, *dst;
    Tcl_EncodingState oldState;
    int encEndFlagSuppressed = 0;

    factor = *factorPtr;
    offset = *offsetPtr;

    bufPtr = statePtr->inQueueHead;
    src = RemovePoint(bufPtr);
    srcLen = BytesLeft(bufPtr);




    toRead = charsToRead;
    if ((unsigned) toRead > (unsigned) srcLen) {
	toRead = srcLen;


    }


    /*
     * 'factor' is how much we guess that the bytes in the source buffer will
     * expand when converted to UTF-8 chars. This guess comes from analyzing
     * how many characters were produced by the previous pass.
     */


    dstNeeded = TCL_UTF_MAX - 1 + toRead * factor / UTF_EXPANSION_FACTOR;
    spaceLeft = objPtr->length - offset;









    if (dstNeeded > spaceLeft) {
	/*
	 * Double the existing size of the object or make enough room to hold







	 * all the characters we want from the source buffer, whichever is
	 * larger.





	 */

	int length = offset + ((offset < dstNeeded) ? dstNeeded : offset);

	if (Tcl_AttemptSetObjLength(objPtr, length) == 0) {
	    length = offset + dstNeeded;
	    if (Tcl_AttemptSetObjLength(objPtr, length) == 0) {
		dstNeeded = TCL_UTF_MAX - 1 + toRead;
		length = offset + dstNeeded;
		Tcl_SetObjLength(objPtr, length);
	    }




	}

	spaceLeft = length - offset;


    }
    if (toRead == srcLen) {
	/*
	 * Want to convert the whole buffer in one pass. If we have enough
	 * space, convert it using all available space in object rather than
	 * using the factor.


	 */




	dstNeeded = spaceLeft;

    }





    dst = objPtr->bytes + offset;

    /*
     * [Bug 1462248]: The cause of the crash reported in this bug is this:

     *
     * - ReadChars, called with a single buffer, with a incomplete
     *	 multi-byte character at the end (only the first byte of it).
     * - Encoding translation fails, asks for more data
     * - Data is read, and eof is reached, TCL_ENCODING_END (TEE) is set.
     * - ReadChar is called again, converts the first buffer, but due to TEE
     *	 it does not check for incomplete multi-byte data, and the character
     *	 just after the end of the first buffer is a valid completion of the
     *	 multi-byte header in the actual buffer. The conversion reads more
     *	 characters from the buffer then present. This causes nextRemoved to
     *	 overshoot nextAdded and the next reads compute a negative srcLen,
     *	 cause further translations to fail, causing copying of data into the
     *	 next buffer using bad arguments, causing the mecpy for to eventually
     *	 fail.
     *
     * In the end it is a memory access bug spiraling out of control if the

     * conditions are _just so_. And ultimate cause is that TEE is given to a

     * conversion where it should not. TEE signals that this is the last






     * buffer. Except in our case it is not.
     *
     * My solution is to suppress TEE if the first buffer is not the last. We
     * will eventually need it given that EOF has been reached, but not right
     * now. This is what the new flag "endEncSuppressFlag" is for.
     *
     * The bug in 'Tcl_Utf2UtfProc' where it read from memory behind the



     * actual buffer has been fixed as well, and fixes the problem with the
     * crash too, but this would still allow the generic layer to
     * accidentially break a multi-byte sequence if the conditions are just
     * right, because again the ExternalToUtf would be successful where it
     * should not.
     */



    if ((statePtr->inputEncodingFlags & TCL_ENCODING_END) &&
	    (bufPtr->nextPtr != NULL)) {

	/*
	 * TEE is set for a buffer which is not the last. Squash it for now,
	 * and restore it later, before yielding control to our caller.



	 */



	statePtr->inputEncodingFlags &= ~TCL_ENCODING_END;




	encEndFlagSuppressed = 1;






    }







    oldState = statePtr->inputEncodingState;
    if (GotFlag(statePtr, INPUT_NEED_NL)) {
	/*
	 * We want a '\n' because the last character we saw was '\r'.
	 */


	ResetFlag(statePtr, INPUT_NEED_NL);
	Tcl_ExternalToUtf(NULL, statePtr->encoding, src, srcLen,
		statePtr->inputEncodingFlags, &statePtr->inputEncodingState,


		dst, TCL_UTF_MAX + 1, &srcRead, &dstWrote, &numChars);
	if ((dstWrote > 0) && (*dst == '\n')) {



	    /*
	     * The next char was a '\n'. Consume it and produce a '\n'.



	     */



	    bufPtr->nextRemoved += srcRead;
	} else {
	    /*
	     * The next char was not a '\n'. Produce a '\r'.

	     */


	    *dst = '\r';

	}

	statePtr->inputEncodingFlags &= ~TCL_ENCODING_START;


	*offsetPtr += 1;









	if (encEndFlagSuppressed) {

	    statePtr->inputEncodingFlags |= TCL_ENCODING_END;
	}

	return 1;
    }

    Tcl_ExternalToUtf(NULL, statePtr->encoding, src, srcLen,
	    statePtr->inputEncodingFlags, &statePtr->inputEncodingState, dst,
	    dstNeeded + 1, &srcRead, &dstWrote, &numChars);

    if (encEndFlagSuppressed) {
	statePtr->inputEncodingFlags |= TCL_ENCODING_END;
    }










    if (srcRead == 0) {



	/*
	 * Not enough bytes in src buffer to make a complete char. Copy the
	 * bytes to the next buffer to make a new contiguous string, then tell


	 * the caller to fill the buffer with more bytes.
	 */

	ChannelBuffer *nextPtr;

	nextPtr = bufPtr->nextPtr;
	if (nextPtr == NULL) {
	    if (srcLen > 0) {
		/*



		 * There isn't enough data in the buffers to complete the next
		 * character, so we need to wait for more data before the next
		 * file event can be delivered. [Bug 478856]






		 *
		 * The exception to this is if the input buffer was completely
		 * empty before we tried to convert its contents. Nothing in,


		 * nothing out, and no incomplete character data. The
		 * conversion before the current one was complete.



		 */





		SetFlag(statePtr, CHANNEL_NEED_MORE_DATA);
	    }

	    return -1;
	}

	/*
	 * Space is made at the beginning of the buffer to copy the previous
	 * unused bytes there. Check first if the buffer we are using actually
	 * has enough space at its beginning for the data we are copying.
	 * Because if not we will write over the buffer management
	 * information, especially the 'nextPtr'.
	 *
	 * Note that the BUFFER_PADDING (See AllocChannelBuffer) is used to
	 * prevent exactly this situation. I.e. it should never happen.
	 * Therefore it is ok to panic should it happen despite the
	 * precautions.
	 */

	if (nextPtr->nextRemoved - srcLen < 0) {
	    Tcl_Panic("Buffer Underflow, BUFFER_PADDING not enough");
	}

	nextPtr->nextRemoved -= srcLen;
	memcpy(RemovePoint(nextPtr), src, (size_t) srcLen);
	RecycleBuffer(statePtr, bufPtr, 0);
	statePtr->inQueueHead = nextPtr;

	return ReadChars(statePtr, objPtr, charsToRead, offsetPtr, factorPtr);
    }

    dstRead = dstWrote;
    if (TranslateInputEOL(statePtr, dst, dst, &dstWrote, &dstRead) != 0) {
	/*
	 * Hit EOF char. How many bytes of src correspond to where the EOF was
	 * located in dst? Run the conversion again with an output buffer just
	 * big enough to hold the data so we can get the correct value for
	 * srcRead.
	 */

	if (dstWrote == 0) {
	    return -1;
	}
	statePtr->inputEncodingState = oldState;
	Tcl_ExternalToUtf(NULL, statePtr->encoding, src, srcLen,
		statePtr->inputEncodingFlags, &statePtr->inputEncodingState,
		dst, dstRead + TCL_UTF_MAX, &srcRead, &dstWrote, &numChars);
	TranslateInputEOL(statePtr, dst, dst, &dstWrote, &dstRead);
    }

    /*
     * The number of characters that we got may be less than the number that
     * we started with because "\r\n" sequences may have been turned into just
     * '\n' in dst.
     */

    numChars -= dstRead - dstWrote;

    if ((unsigned) numChars > (unsigned) toRead) {
	/*
	 * Got too many chars.
	 */

	const char *eof = Tcl_UtfAtIndex(dst, toRead);

	statePtr->inputEncodingState = oldState;
	Tcl_ExternalToUtf(NULL, statePtr->encoding, src, srcLen,
		statePtr->inputEncodingFlags, &statePtr->inputEncodingState,
		dst, eof - dst + TCL_UTF_MAX, &srcRead, &dstWrote, &numChars);
	dstRead = dstWrote;
	TranslateInputEOL(statePtr, dst, dst, &dstWrote, &dstRead);
	numChars -= (dstRead - dstWrote);
    }
    statePtr->inputEncodingFlags &= ~TCL_ENCODING_START;

    bufPtr->nextRemoved += srcRead;
    if (dstWrote > srcRead + 1) {
	*factorPtr = dstWrote * UTF_EXPANSION_FACTOR / srcRead;
    }
    *offsetPtr += dstWrote;
    return numChars;

}

/*
 *---------------------------------------------------------------------------
 *
 * TranslateInputEOL --
 *
 *	Perform input EOL and EOF translation on the source buffer, leaving
 *	the translated result in the destination buffer.
 *
 * Results:
 *	The return value is 1 if the EOF character was found when copying
 *	bytes to the destination buffer, 0 otherwise.
 *
 * Side effects:
 *	None.
 *
 *---------------------------------------------------------------------------
 */

static int
TranslateInputEOL(
    ChannelState *statePtr,	/* Channel being read, for EOL translation and
				 * EOF character. */
    char *dstStart,		/* Output buffer filled with chars by applying
				 * appropriate EOL translation to source
				 * characters. */
    const char *srcStart,	/* Source characters. */
    int *dstLenPtr,		/* On entry, the maximum length of output
				 * buffer in bytes; must be <= *srcLenPtr. On
				 * exit, the number of bytes actually used in
				 * output buffer. */
    int *srcLenPtr)		/* On entry, the length of source buffer. On
				 * exit, the number of bytes read from the
				 * source buffer. */
{
    int dstLen, srcLen, inEofChar;
    const char *eof;

    dstLen = *dstLenPtr;

    eof = NULL;
    inEofChar = statePtr->inEofChar;
    if (inEofChar != '\0') {
	/*
	 * Find EOF in translated buffer then compress out the EOL. The source
	 * buffer may be much longer than the destination buffer - we only
	 * want to return EOF if the EOF has been copied to the destination


	 * buffer.
	 */






	const char *src, *srcMax = srcStart + *srcLenPtr;

	for (src = srcStart; src < srcMax; src++) {
	    if (*src == inEofChar) {
		eof = src;
		srcLen = src - srcStart;

		if (srcLen < dstLen) {
		    dstLen = srcLen;
		}
		*srcLenPtr = srcLen;
		break;
	    }
	}





    }





    switch (statePtr->inputTranslation) {
    case TCL_TRANSLATE_LF:

	if (dstStart != srcStart) {
	    memcpy(dstStart, srcStart, (size_t) dstLen);
	}
	srcLen = dstLen;
	break;
    case TCL_TRANSLATE_CR: {
	char *dst, *dstEnd;

	if (dstStart != srcStart) {
	    memcpy(dstStart, srcStart, (size_t) dstLen);
	}
	dstEnd = dstStart + dstLen;
	for (dst = dstStart; dst < dstEnd; dst++) {
	    if (*dst == '\r') {
		*dst = '\n';
	    }
	}
	srcLen = dstLen;
	break;
    }
    case TCL_TRANSLATE_CRLF: {

	char *dst;
	const char *src, *srcEnd, *srcMax;


	dst = dstStart;
	src = srcStart;
	srcEnd = srcStart + dstLen;
	srcMax = srcStart + *srcLenPtr;

	for ( ; src < srcEnd; ) {
	    if (*src == '\r') {
		src++;
		if (src >= srcMax) {
		    SetFlag(statePtr, INPUT_NEED_NL);
		} else if (*src == '\n') {
		    *dst++ = *src++;

		} else {
		    *dst++ = '\r';

		}



	    } else {
		*dst++ = *src++;

	    }


	}

	srcLen = src - srcStart;
	dstLen = dst - dstStart;
	break;
    }
    case TCL_TRANSLATE_AUTO: {
	char *dst;
	const char *src, *srcEnd, *srcMax;

	dst = dstStart;
	src = srcStart;
	srcEnd = srcStart + dstLen;
	srcMax = srcStart + *srcLenPtr;

	if (GotFlag(statePtr, INPUT_SAW_CR) && (src < srcMax)) {
	    if (*src == '\n') {
		src++;
	    }
	    ResetFlag(statePtr, INPUT_SAW_CR);
	}




	for ( ; src < srcEnd; ) {
	    if (*src == '\r') {

		src++;
		if (src >= srcMax) {
		    SetFlag(statePtr, INPUT_SAW_CR);
		} else if (*src == '\n') {
		    if (srcEnd < srcMax) {
			srcEnd++;
		    }
		    src++;
		}
		*dst++ = '\n';
	    } else {
		*dst++ = *src++;
	    }
	}

	srcLen = src - srcStart;
	dstLen = dst - dstStart;
	break;
    }
    default:
	return 0;

    }
    *dstLenPtr = dstLen;


    if ((eof != NULL) && (srcStart + srcLen >= eof)) {
	/*
	 * EOF character was seen in EOL translated range. Leave current file
	 * position pointing at the EOF character, but don't store the EOF
	 * character in the output string.
	 */

	SetFlag(statePtr, CHANNEL_EOF | CHANNEL_STICKY_EOF);
	statePtr->inputEncodingFlags |= TCL_ENCODING_END;
	ResetFlag(statePtr, INPUT_SAW_CR | INPUT_NEED_NL);
	return 1;
    }

    *srcLenPtr = srcLen;
    return 0;
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_Ungets --
 *







|
|
|
|
|






|
<
<
|
|
<
|
<
<
|
|
|
|

>
>
>
|
<
|
>
>
|
>






|
>
|
|
>
>
>
>
>
>
>
>
|
|
|
<
>
>
>
>
>
>
>
|
<
>
>
>
>
>
|

|
|
<
|
<
<
<
<
|
>
>
>
>
|
>
|
>
>
|
<

<
|
|
>
>


>
>
>
|
>
|
>
>
>
>
>
|
|
|
|
>
|
<
|
|
<
|
|
<
<
<
<
<
<
<
|
<
>
|
>
|
>
>
>
>
>
>
|
|
<
<
<
|
<
>
>
>
|
|
<
|
<
|
>
>
|
|
<
>
|
|
<
>
>
>
|

>
>
|
>
>
>
>
|
>
>
>
>
>
>
|
>
>
>
>
>
>
|
|
|
|
<
|

>
|
|
|
>
>
|
|
>
>
>

<
>
>
>


>
>
|
|
|
<
>
|
>
|
|
>
|
>
|
>
>
|
>
>
>
>
>
>
>
>
|
|
>
|
|
>
|
|

<
<
<
|
<
<
|
>
>
>
>

>
>
>
>
>
|
>
>
>
|
<
<
>
>
|


|

<
|
|
|
>
>
>
|
<
<
>
>
>
>
>
>
|
|
|
>
>
|
<
>
>
>
|

>
>
>
>
|
|
>
|
|

|
|
|
|
|
|
|
|
|
|
|
|

|
|
|

|
|
|
|
>
|
|

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|

|
|
|
|
|
|
>




















|








<
|
|




<
|
<
|
|
<
|
|
|
|
<
<
>
>
|
|

>
>
>
>
>
|
|
<
<
|
|
>
|
|
|
<
|
|
|
>
>
>
>
>
|
>
>
>
>
>


>

|

<
<
|
|
|
<
<
|
|
<
<
|


|

<

>
|
<
>

|
|
|
<

|
|
|
|
<
|
|
>

|
>

>
>
>

|
>

>
>

>
|
|



<
|
<
|
<
|
<

|
|
<
<


>
>
>
>
|
|
>
|
|
|
|
<
<
<
|
|
|
<
<
|
<
>
|
|



<
>


>

|








|
<

<
<
<







5745
5746
5747
5748
5749
5750
5751
5752
5753
5754
5755
5756
5757
5758
5759
5760
5761
5762
5763


5764
5765

5766


5767
5768
5769
5770
5771
5772
5773
5774
5775

5776
5777
5778
5779
5780
5781
5782
5783
5784
5785
5786
5787
5788
5789
5790
5791
5792
5793
5794
5795
5796
5797
5798
5799
5800
5801

5802
5803
5804
5805
5806
5807
5808
5809

5810
5811
5812
5813
5814
5815
5816
5817
5818

5819




5820
5821
5822
5823
5824
5825
5826
5827
5828
5829
5830

5831

5832
5833
5834
5835
5836
5837
5838
5839
5840
5841
5842
5843
5844
5845
5846
5847
5848
5849
5850
5851
5852
5853
5854

5855
5856

5857
5858







5859

5860
5861
5862
5863
5864
5865
5866
5867
5868
5869
5870
5871



5872

5873
5874
5875
5876
5877

5878

5879
5880
5881
5882
5883

5884
5885
5886

5887
5888
5889
5890
5891
5892
5893
5894
5895
5896
5897
5898
5899
5900
5901
5902
5903
5904
5905
5906
5907
5908
5909
5910
5911
5912
5913
5914
5915
5916

5917
5918
5919
5920
5921
5922
5923
5924
5925
5926
5927
5928
5929
5930

5931
5932
5933
5934
5935
5936
5937
5938
5939
5940

5941
5942
5943
5944
5945
5946
5947
5948
5949
5950
5951
5952
5953
5954
5955
5956
5957
5958
5959
5960
5961
5962
5963
5964
5965
5966
5967
5968
5969



5970


5971
5972
5973
5974
5975
5976
5977
5978
5979
5980
5981
5982
5983
5984
5985
5986


5987
5988
5989
5990
5991
5992
5993

5994
5995
5996
5997
5998
5999
6000


6001
6002
6003
6004
6005
6006
6007
6008
6009
6010
6011
6012

6013
6014
6015
6016
6017
6018
6019
6020
6021
6022
6023
6024
6025
6026
6027
6028
6029
6030
6031
6032
6033
6034
6035
6036
6037
6038
6039
6040
6041
6042
6043
6044
6045
6046
6047
6048
6049
6050
6051
6052










































6053
6054
6055
6056
6057
6058
6059
6060
6061
6062
6063
6064
6065
6066
6067
6068
6069
6070
6071
6072
6073
6074
6075
6076
6077
6078
6079
6080
6081
6082
6083
6084
6085
6086
6087
6088
6089
6090

6091
6092
6093
6094
6095
6096

6097

6098
6099

6100
6101
6102
6103


6104
6105
6106
6107
6108
6109
6110
6111
6112
6113
6114
6115


6116
6117
6118
6119
6120
6121

6122
6123
6124
6125
6126
6127
6128
6129
6130
6131
6132
6133
6134
6135
6136
6137
6138
6139
6140
6141


6142
6143
6144


6145
6146


6147
6148
6149
6150
6151

6152
6153
6154

6155
6156
6157
6158
6159

6160
6161
6162
6163
6164

6165
6166
6167
6168
6169
6170
6171
6172
6173
6174
6175
6176
6177
6178
6179
6180
6181
6182
6183
6184
6185
6186
6187

6188

6189

6190

6191
6192
6193


6194
6195
6196
6197
6198
6199
6200
6201
6202
6203
6204
6205
6206



6207
6208
6209


6210

6211
6212
6213
6214
6215
6216

6217
6218
6219
6220
6221
6222
6223
6224
6225
6226
6227
6228
6229
6230
6231

6232



6233
6234
6235
6236
6237
6238
6239
    int charsToRead,		/* Maximum number of characters to store, or
				 * -1 to get all available characters.
				 * Characters are obtained from the first
				 * buffer in the queue -- even if this number
				 * is larger than the number of characters
				 * available in the first buffer, only the
				 * characters from the first buffer are
				 * returned. The execption is when there is
				 * not any complete character in the first
				 * buffer.  In that case, a recursive call
				 * effectively obtains chars from the
				 * second buffer. */
    int *factorPtr)		/* On input, contains a guess of how many
				 * bytes need to be allocated to hold the
				 * result of converting N source bytes to
				 * UTF-8. On output, contains another guess
				 * based on the data seen so far. */
{
    Tcl_Encoding encoding = statePtr->encoding? statePtr->encoding


	    : GetBinaryEncoding();
    Tcl_EncodingState savedState = statePtr->inputEncodingState;

    ChannelBuffer *bufPtr = statePtr->inQueueHead;


    int savedIEFlags = statePtr->inputEncodingFlags;
    int savedFlags = statePtr->flags;
    char *dst, *src = RemovePoint(bufPtr);
    int dstLimit, numBytes, srcLen = BytesLeft(bufPtr);

    /*
     * One src byte can yield at most one character.  So when the
     * number of src bytes we plan to read is less than the limit on
     * character count to be read, clearly we will remain within that

     * limit, and we can use the value of "srcLen" as a tighter limit
     * for sizing receiving buffers.
     */

    int toRead = ((unsigned) charsToRead > srcLen) ? srcLen : charsToRead;

    /*
     * 'factor' is how much we guess that the bytes in the source buffer will
     * expand when converted to UTF-8 chars. This guess comes from analyzing
     * how many characters were produced by the previous pass.
     */
    
    int factor = *factorPtr;
    int dstNeeded = TCL_UTF_MAX - 1 + toRead * factor / UTF_EXPANSION_FACTOR;

    (void) TclGetStringFromObj(objPtr, &numBytes);
    Tcl_AppendToObj(objPtr, NULL, dstNeeded);
    if (toRead == srcLen) {
	unsigned int size;
	dst = TclGetStringStorage(objPtr, &size) + numBytes;
	dstNeeded = size - numBytes;
    } else {
	dst = TclGetString(objPtr) + numBytes;
    }

    /*

     * This routine is burdened with satisfying several constraints.
     * It cannot append more than 'charsToRead` chars onto objPtr.
     * This is measured after encoding and translation transformations
     * are completed.  There is no precise number of src bytes that can
     * be associated with the limit.  Yet, when we are done, we must know
     * precisely the number of src bytes that were consumed to produce
     * the appended chars, so that all subsequent bytes are left in
     * the buffers for future read operations.

     *
     * The consequence is that we have no choice but to implement a
     * "trial and error" approach, where in general we may need to
     * perform transformations and copies multiple times to achieve
     * a consistent set of results.  This takes the shape of a loop.
     */

    dstLimit = dstNeeded + 1;
    while (1) {

	int dstDecoded, dstRead, dstWrote, srcRead, numChars;





	/*
	 * Perform the encoding transformation.  Read no more than
	 * srcLen bytes, write no more than dstLimit bytes.
	 */

	int code = Tcl_ExternalToUtf(NULL, encoding, src, srcLen,
		statePtr->inputEncodingFlags & (bufPtr->nextPtr
		? ~0 : ~TCL_ENCODING_END), &statePtr->inputEncodingState,
		dst, dstLimit, &srcRead, &dstDecoded, &numChars);


	/*

	 * Perform the translation transformation in place.  Read no more
	 * than the dstDecoded bytes the encoding transformation actually
	 * produced.  Capture the number of bytes written in dstWrote.
	 * Capture the number of bytes actually consumed in dstRead.
	 */

	dstWrote = dstLimit;
	dstRead = dstDecoded;
	TranslateInputEOL(statePtr, dst, dst, &dstWrote, &dstRead);

	if (dstRead < dstDecoded) {

	    /*
	     * The encoding transformation produced bytes that the
	     * translation transformation did not consume.  Why did
	     * this happen?
	     */

	    if (statePtr->inEofChar && dst[dstRead] == statePtr->inEofChar) {
		/*
		 * 1) There's an eof char set on the channel, and
		 *    we saw it and stopped translating at that point.
		 *

		 * NOTE the bizarre spec of TranslateInputEOL in this case.
		 * Clearly the eof char had to be read in order to account

		 * for the stopping, but the value of dstRead does not
		 * include it.







		 *

		 * Also rather bizarre, our caller can only notice an
		 * EOF condition if we return the value -1 as the number
		 * of chars read.  This forces us to perform a 2-call
		 * dance where the first call can read all the chars
		 * up to the eof char, and the second call is solely
		 * for consuming the encoded eof char then pointed at
		 * by src so that we can return that magic -1 value.
		 * This seems really wasteful, especially since
		 * the first decoding pass of each call is likely to
		 * decode many bytes beyond that eof char that's all we
		 * care about.
		 */





		if (dstRead == 0) {
		    /*
		     * Curious choice in the eof char handling.  We leave
		     * the eof char in the buffer.  So, no need to compute
		     * a proper srcRead value.  At this point, there

		     * are no chars before the eof char in the buffer.

		     */
		    Tcl_SetObjLength(objPtr, numBytes);
		    return -1;
		}


		{
		    /*
		     * There are chars leading the buffer before the eof

		     * char.  Adjust the dstLimit so we go back and read
		     * only those and do not encounter the eof char this
		     * time.
		     */

		    dstLimit = dstRead + TCL_UTF_MAX;
		    statePtr->flags = savedFlags;
		    statePtr->inputEncodingFlags = savedIEFlags;
		    statePtr->inputEncodingState = savedState;
		    continue;
		}
	    }

	    /*
	     * 2) The other way to read fewer bytes than are decoded
	     *    is when the final byte is \r and we're in a CRLF
	     *    translation mode so we cannot decide whether to
	     *	  record \r or \n yet.
	     */

	    assert(dstRead + 1 == dstDecoded);
	    assert(dst[dstRead] == '\r');
	    assert(statePtr->inputTranslation == TCL_TRANSLATE_CRLF);

	    if (dstWrote > 0) {
		/*
		 * There are chars we can read before we hit the bare cr.
		 * Go back with a smaller dstLimit so we get them in the
		 * next pass, compute a matching srcRead, and don't end
		 * up back here in this call.

		 */

		dstLimit = dstRead + TCL_UTF_MAX;
		statePtr->flags = savedFlags;
		statePtr->inputEncodingFlags = savedIEFlags;
		statePtr->inputEncodingState = savedState;
		continue;
	    }

	    assert(dstWrote == 0);
	    assert(dstRead == 0);
	    assert(dstDecoded == 1);

	    /*

	     * We decoded only the bare cr, and we cannot read a
	     * translated char from that alone.  We have to know what's
	     * next.  So why do we only have the one decoded char?
	     */

	    if (code != TCL_OK) {
		char buffer[TCL_UTF_MAX + 2];
		int read, decoded, count;

		/* 

		 * Didn't get everything the buffer could offer
		 */

		statePtr->flags = savedFlags;
		statePtr->inputEncodingFlags = savedIEFlags;
		statePtr->inputEncodingState = savedState;

		Tcl_ExternalToUtf(NULL, encoding, src, srcLen,
		statePtr->inputEncodingFlags & (bufPtr->nextPtr
		? ~0 : ~TCL_ENCODING_END), &statePtr->inputEncodingState,
		buffer, TCL_UTF_MAX + 2, &read, &decoded, &count);

		if (count == 2) {
		    if (buffer[1] == '\n') {
			/* \r\n translate to \n */
			dst[0] = '\n';
			bufPtr->nextRemoved += read;
		    } else {
			dst[0] = '\r';
			bufPtr->nextRemoved += srcRead;
		    }

		    dst[1] = '\0';
		    statePtr->inputEncodingFlags &= ~TCL_ENCODING_START;

		    Tcl_SetObjLength(objPtr, numBytes + 1);
		    return 1;
		}




	    } else if (statePtr->flags & CHANNEL_EOF) {



		/*
		 * The bare \r is the only char and we will never read
		 * a subsequent char to make the determination.
		 */

		dst[0] = '\r';
		bufPtr->nextRemoved = bufPtr->nextAdded;
		Tcl_SetObjLength(objPtr, numBytes + 1);
		return 1;
	    }

	    /* FALL THROUGH - get more data (dstWrote == 0) */
	}

	/* 


	 * The translation transformation can only reduce the number
	 * of chars when it converts \r\n into \n.  The reduction in
	 * the number of chars is the difference in bytes read and written.
	 */

	numChars -= (dstRead - dstWrote);


	if (charsToRead > 0 && numChars > charsToRead) {

	    /* 
	     * We read more chars than allowed.  Reset limits to
	     * prevent that and try again.
	     */



	    dstLimit = Tcl_UtfAtIndex(dst, charsToRead + 1) - dst;
	    statePtr->flags = savedFlags;
	    statePtr->inputEncodingFlags = savedIEFlags;
	    statePtr->inputEncodingState = savedState;
	    continue;
	}

	if (dstWrote == 0) {

	    /*
	     * We were not able to read any chars.  Maybe there were
	     * not enough src bytes to decode into a char.  Maybe

	     * a lone \r could not be translated (crlf mode).  Need
	     * to combine any unused src bytes we have in the first
	     * buffer with subsequent bytes to try again.
	     */

	    ChannelBuffer *nextPtr = bufPtr->nextPtr;

	    if (nextPtr == NULL) {
		if (srcLen > 0) {
		    SetFlag(statePtr, CHANNEL_NEED_MORE_DATA);
		}
		Tcl_SetObjLength(objPtr, numBytes);
		return -1;
	    }

	    /*
	     * Space is made at the beginning of the buffer to copy the
	     * previous unused bytes there. Check first if the buffer we
	     * are using actually has enough space at its beginning for
	     * the data we are copying.  Because if not we will write over
	     * the buffer management information, especially the 'nextPtr'.
	     *
	     * Note that the BUFFER_PADDING (See AllocChannelBuffer) is
	     * used to prevent exactly this situation. I.e. it should never
	     * happen.  Therefore it is ok to panic should it happen despite
	     * the precautions.
	     */

	    if (nextPtr->nextRemoved - srcLen < 0) {
		Tcl_Panic("Buffer Underflow, BUFFER_PADDING not enough");
	    }

	    nextPtr->nextRemoved -= srcLen;
	    memcpy(RemovePoint(nextPtr), src, (size_t) srcLen);
	    RecycleBuffer(statePtr, bufPtr, 0);
	    statePtr->inQueueHead = nextPtr;
	    Tcl_SetObjLength(objPtr, numBytes);
	    return ReadChars(statePtr, objPtr, charsToRead, factorPtr);
	}











































	statePtr->inputEncodingFlags &= ~TCL_ENCODING_START;

	bufPtr->nextRemoved += srcRead;
	if (dstWrote > srcRead + 1) {
	    *factorPtr = dstWrote * UTF_EXPANSION_FACTOR / srcRead;
	}
	Tcl_SetObjLength(objPtr, numBytes + dstWrote);
	return numChars;
    }
}

/*
 *---------------------------------------------------------------------------
 *
 * TranslateInputEOL --
 *
 *	Perform input EOL and EOF translation on the source buffer, leaving
 *	the translated result in the destination buffer.
 *
 * Results:
 *	The return value is 1 if the EOF character was found when copying
 *	bytes to the destination buffer, 0 otherwise.
 *
 * Side effects:
 *	None.
 *
 *---------------------------------------------------------------------------
 */

static void 
TranslateInputEOL(
    ChannelState *statePtr,	/* Channel being read, for EOL translation and
				 * EOF character. */
    char *dstStart,		/* Output buffer filled with chars by applying
				 * appropriate EOL translation to source
				 * characters. */
    const char *srcStart,	/* Source characters. */
    int *dstLenPtr,		/* On entry, the maximum length of output

				 * buffer in bytes. On exit, the number of
				 * bytes actually used in output buffer. */
    int *srcLenPtr)		/* On entry, the length of source buffer. On
				 * exit, the number of bytes read from the
				 * source buffer. */
{

    const char *eof = NULL;

    int dstLen = *dstLenPtr;
    int srcLen = *srcLenPtr;

    int inEofChar = statePtr->inEofChar;

    /*
     * Depending on the translation mode in use, there's no need


     * to scan more srcLen bytes at srcStart than can possibly transform
     * to dstLen bytes.  This keeps the scan for eof char below from
     * being pointlessly long.
     */

    switch (statePtr->inputTranslation) {
    case TCL_TRANSLATE_LF:
    case TCL_TRANSLATE_CR:
	if (srcLen > dstLen) {
	/* In these modes, each src byte become a dst byte. */
	    srcLen = dstLen;
	}


	break;
    default:
	/* In other modes, at most 2 src bytes become a dst byte. */
	if (srcLen > 2 * dstLen) {
	    srcLen = 2 * dstLen;
	}

	break;
    }

    if (inEofChar != '\0') {
	/*
	 * Make sure we do not read past any logical end of channel input
	 * created by the presence of the input eof char.
	 */

	if ((eof = memchr(srcStart, inEofChar, srcLen))) {
	    srcLen = eof - srcStart;
	}
    }

    switch (statePtr->inputTranslation) {
    case TCL_TRANSLATE_LF:
    case TCL_TRANSLATE_CR:
	if (dstStart != srcStart) {
	    memcpy(dstStart, srcStart, (size_t) srcLen);
	}


	if (statePtr->inputTranslation == TCL_TRANSLATE_CR) {
	    char *dst = dstStart;
	    char *dstEnd = dstStart + srcLen;



	    while ((dst = memchr(dst, '\r', dstEnd - dst))) {


		*dst++ = '\n';
	    }
	}
	dstLen = srcLen;
	break;

    case TCL_TRANSLATE_CRLF: {
	const char *crFound, *src = srcStart;
	char *dst = dstStart;

	int lesser = (dstLen < srcLen) ? dstLen : srcLen;

	while ((crFound = memchr(src, '\r', lesser))) {
	    int numBytes = crFound - src;
	    memmove(dst, src, numBytes);


	    dst += numBytes; dstLen -= numBytes;
	    src += numBytes; srcLen -= numBytes;
	    if (srcLen == 1) {
		/* valid src bytes end in \r */

		if (eof) {
		    *dst++ = '\r';
		    src++; srcLen--;
		} else {
		    lesser = 0;
		    break;
		}
	    } else if (src[1] == '\n') {
		*dst++ = '\n';
		src += 2; srcLen -= 2;
	    } else {
		*dst++ = '\r';
		src++; srcLen--;
	    }
	    dstLen--;
	    lesser = (dstLen < srcLen) ? dstLen : srcLen;
	}
	memmove(dst, src, lesser);
	srcLen = src + lesser - srcStart;
	dstLen = dst + lesser - dstStart;
	break;
    }
    case TCL_TRANSLATE_AUTO: {

	const char *crFound, *src = srcStart;

	char *dst = dstStart;

	int lesser;


	if ((statePtr->flags & INPUT_SAW_CR) && srcLen) {
	    if (*src == '\n') { src++; srcLen--; }


	    ResetFlag(statePtr, INPUT_SAW_CR);
	}
	lesser = (dstLen < srcLen) ? dstLen : srcLen;
	while ((crFound = memchr(src, '\r', lesser))) {
	    int numBytes = crFound - src;
	    memmove(dst, src, numBytes);

	    dst[numBytes] = '\n';
	    dst += numBytes + 1; dstLen -= numBytes + 1;
	    src += numBytes + 1; srcLen -= numBytes + 1;
	    if (srcLen == 0) {
		SetFlag(statePtr, INPUT_SAW_CR);
	    } else if (*src == '\n') {



		src++; srcLen--;
	    }
	    lesser = (dstLen < srcLen) ? dstLen : srcLen;


	}

	memmove(dst, src, lesser);
	srcLen = src + lesser - srcStart;
	dstLen = dst + lesser - dstStart;
	break;
    }
    default:

	Tcl_Panic("unknown input translation %d", statePtr->inputTranslation);
    }
    *dstLenPtr = dstLen;
    *srcLenPtr = srcLen;

    if (srcStart + srcLen == eof) {
	/*
	 * EOF character was seen in EOL translated range. Leave current file
	 * position pointing at the EOF character, but don't store the EOF
	 * character in the output string.
	 */

	SetFlag(statePtr, CHANNEL_EOF | CHANNEL_STICKY_EOF);
	statePtr->inputEncodingFlags |= TCL_ENCODING_END;
	ResetFlag(statePtr, INPUT_SAW_CR);

    }



}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_Ungets --
 *
6285
6286
6287
6288
6289
6290
6291
6292
6293
6294
6295
6296
6297
6298
6299
6300
6301
6302
6303
6304
6305
6306
6307
6308
    if (CheckChannelErrors(statePtr, TCL_READABLE) != 0) {
	len = -1;
	goto done;
    }
    statePtr->flags = flags;

    /*
     * If we have encountered a sticky EOF, just punt without storing (sticky
     * EOF is set if we have seen the input eofChar, to prevent reading beyond
     * the eofChar). Otherwise, clear the EOF flags, and clear the BLOCKED
     * bit. We want to discover these conditions anew in each operation.
     */

    if (GotFlag(statePtr, CHANNEL_STICKY_EOF)) {
	goto done;
    }
    ResetFlag(statePtr, CHANNEL_BLOCKED | CHANNEL_EOF);

    bufPtr = AllocChannelBuffer(len);
    memcpy(InsertPoint(bufPtr), str, (size_t) len);
    bufPtr->nextAdded += len;

    if (statePtr->inQueueHead == NULL) {
	bufPtr->nextPtr = NULL;







<
<
|
<


|
<
<
|







6279
6280
6281
6282
6283
6284
6285


6286

6287
6288
6289


6290
6291
6292
6293
6294
6295
6296
6297
    if (CheckChannelErrors(statePtr, TCL_READABLE) != 0) {
	len = -1;
	goto done;
    }
    statePtr->flags = flags;

    /*


     * Clear the EOF flags, and clear the BLOCKED bit.

     */

    ResetFlag(statePtr,


	    CHANNEL_BLOCKED | CHANNEL_STICKY_EOF | CHANNEL_EOF | INPUT_SAW_CR);

    bufPtr = AllocChannelBuffer(len);
    memcpy(InsertPoint(bufPtr), str, (size_t) len);
    bufPtr->nextAdded += len;

    if (statePtr->inQueueHead == NULL) {
	bufPtr->nextPtr = NULL;
6429
6430
6431
6432
6433
6434
6435



6436
6437
6438
6439
6440
6441
6442
/*
 *---------------------------------------------------------------------------
 *
 * GetInput --
 *
 *	Reads input data from a device into a channel buffer.
 *



 * Results:
 *	The return value is the Posix error code if an error occurred while
 *	reading from the file, or 0 otherwise.
 *
 * Side effects:
 *	Reads from the underlying device.
 *







>
>
>







6418
6419
6420
6421
6422
6423
6424
6425
6426
6427
6428
6429
6430
6431
6432
6433
6434
/*
 *---------------------------------------------------------------------------
 *
 * GetInput --
 *
 *	Reads input data from a device into a channel buffer.
 *
 *	IMPORTANT!  This routine is only called on a chanPtr argument
 *	that is the top channel of a stack!
 *
 * Results:
 *	The return value is the Posix error code if an error occurred while
 *	reading from the file, or 0 otherwise.
 *
 * Side effects:
 *	Reads from the underlying device.
 *
6466
6467
6468
6469
6470
6471
6472
6473
6474
6475
6476
6477
6478
6479
6480
6481
6482

6483
6484
6485
6486
6487
6488
6489
    }

    /*
     * First check for more buffers in the pushback area of the topmost
     * channel in the stack and use them. They can be the result of a
     * transformation which went away without reading all the information
     * placed in the area when it was stacked.
     *
     * Two possibilities for the state: No buffers in it, or a single empty
     * buffer. In the latter case we can recycle it now.
     */

    if (chanPtr->inQueueHead != NULL) {
	if (statePtr->inQueueHead != NULL) {
	    RecycleBuffer(statePtr, statePtr->inQueueHead, 0);
	    statePtr->inQueueHead = NULL;
	}


	statePtr->inQueueHead = chanPtr->inQueueHead;
	statePtr->inQueueTail = chanPtr->inQueueTail;
	chanPtr->inQueueHead = NULL;
	chanPtr->inQueueTail = NULL;
	return 0;
    }







<
<
<



<
<
<
|
>







6458
6459
6460
6461
6462
6463
6464



6465
6466
6467



6468
6469
6470
6471
6472
6473
6474
6475
6476
    }

    /*
     * First check for more buffers in the pushback area of the topmost
     * channel in the stack and use them. They can be the result of a
     * transformation which went away without reading all the information
     * placed in the area when it was stacked.



     */

    if (chanPtr->inQueueHead != NULL) {




	assert(statePtr->inQueueHead == NULL);

	statePtr->inQueueHead = chanPtr->inQueueHead;
	statePtr->inQueueTail = chanPtr->inQueueTail;
	chanPtr->inQueueHead = NULL;
	chanPtr->inQueueTail = NULL;
	return 0;
    }
6504
6505
6506
6507
6508
6509
6510
6511
6512
6513
6514
6515
6516
6517
6518
6519
6520
6521
6522
6523
6524
6525
6526
6527
6528
6529
6530
6531
6532
6533
6534
6535
6536
6537
6538
6539
6540

6541
6542
6543
6544
6545
6546
6547
6548
6549
6550

6551
6552
6553
6554
6555
6556
6557
6558
6559
6560
6561
6562
6563
6564
6565
6566
6567
6568
6569
6570
6571
6572
6573
6574
6575
6576
6577
6578
6579
6580
6581
6582
6583
6584
6585
6586
6587
6588
6589
6590
6591
6592
6593
6594
6595
6596
6597
6598
6599
6600
6601
6602
6603
6604
6605
6606
6607
6608
6609
6610
6611
6612
6613
	toRead = SpaceLeft(bufPtr);
    } else {
	bufPtr = statePtr->saveInBufPtr;
	statePtr->saveInBufPtr = NULL;

	/*
	 * Check the actual buffersize against the requested buffersize.
	 * Buffers which are smaller than requested are squashed. This is done
	 * to honor dynamic changes of the buffersize made by the user.
	 */

	if ((bufPtr != NULL)
		&& (bufPtr->bufLength - BUFFER_PADDING < statePtr->bufSize)) {
	    ReleaseChannelBuffer(bufPtr);
	    bufPtr = NULL;
	}

	if (bufPtr == NULL) {
	    bufPtr = AllocChannelBuffer(statePtr->bufSize);
	}
	bufPtr->nextPtr = NULL;

	/*
	 * SF #427196: Use the actual size of the buffer to determine the
	 * number of bytes to read from the channel and not the size for new
	 * buffers. They can be different if the buffersize was changed
	 * between reads.
	 *
	 * Note: This affects performance negatively if the buffersize was
	 * extended but this small buffer is reused for all subsequent reads.
	 * The system never uses buffers with the requested bigger size in
	 * that case. An adjunct patch could try and delete all unused buffers
	 * it encounters and which are smaller than the formally requested
	 * buffersize.
	 */

	toRead = SpaceLeft(bufPtr);


	if (statePtr->inQueueTail == NULL) {
	    statePtr->inQueueHead = bufPtr;
	} else {
	    statePtr->inQueueTail->nextPtr = bufPtr;
	}
	statePtr->inQueueTail = bufPtr;
    }

    /*

     * If EOF is set, we should avoid calling the driver because on some
     * platforms it is impossible to read from a device after EOF.
     */

    if (GotFlag(statePtr, CHANNEL_EOF)) {
	return 0;
    }

#ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING
    /*
     * [Bug 943274]: Better emulation of non-blocking channels for channels
     * without BlockModeProc, by keeping track of true fileevents generated by
     * the OS == Data waiting and reading if and only if we are sure to have
     * data.
     */

    if (GotFlag(statePtr, CHANNEL_NONBLOCKING) &&
	    (Tcl_ChannelBlockModeProc(chanPtr->typePtr) == NULL) &&
	    !GotFlag(statePtr, CHANNEL_HAS_MORE_DATA)) {
	/*
	 * Bypass the driver, it would block, as no data is available
	 */

	nread = -1;
	result = EWOULDBLOCK;
    } else
#endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */
    {
	PreserveChannelBuffer(bufPtr);
	nread = ChanRead(chanPtr, InsertPoint(bufPtr), toRead, &result);
    }

    if (nread > 0) {
	result = 0;
	bufPtr->nextAdded += nread;

	/*
	 * If we get a short read, signal up that we may be BLOCKED. We should
	 * avoid calling the driver because on some platforms we will block in
	 * the low level reading code even though the channel is set into
	 * nonblocking mode.
	 */

	if (nread < toRead) {
	    SetFlag(statePtr, CHANNEL_BLOCKED);
	}

#ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING
	if (nread <= toRead) {
	    /*
	     * [Bug 943274]: We have read the available data, clear flag.
	     */

	    ResetFlag(statePtr, CHANNEL_HAS_MORE_DATA);
	}
#endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */
    } else if (nread == 0) {
	result = 0;
	SetFlag(statePtr, CHANNEL_EOF);
	statePtr->inputEncodingFlags |= TCL_ENCODING_END;
    } else if (nread < 0) {
	if ((result == EWOULDBLOCK) || (result == EAGAIN)) {
	    SetFlag(statePtr, CHANNEL_BLOCKED);







|




|









<
<
<
<
<
<
<
<
<
<
<
<
<
<

>










>








<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
|
<
<














<
<
<
<
<
<
<
<
<
<







6491
6492
6493
6494
6495
6496
6497
6498
6499
6500
6501
6502
6503
6504
6505
6506
6507
6508
6509
6510
6511
6512














6513
6514
6515
6516
6517
6518
6519
6520
6521
6522
6523
6524
6525
6526
6527
6528
6529
6530
6531
6532
6533




















6534
6535


6536
6537
6538
6539
6540
6541
6542
6543
6544
6545
6546
6547
6548
6549










6550
6551
6552
6553
6554
6555
6556
	toRead = SpaceLeft(bufPtr);
    } else {
	bufPtr = statePtr->saveInBufPtr;
	statePtr->saveInBufPtr = NULL;

	/*
	 * Check the actual buffersize against the requested buffersize.
	 * Saved buffers of the wrong size are squashed. This is done
	 * to honor dynamic changes of the buffersize made by the user.
	 */

	if ((bufPtr != NULL)
		&& (bufPtr->bufLength - BUFFER_PADDING != statePtr->bufSize)) {
	    ReleaseChannelBuffer(bufPtr);
	    bufPtr = NULL;
	}

	if (bufPtr == NULL) {
	    bufPtr = AllocChannelBuffer(statePtr->bufSize);
	}
	bufPtr->nextPtr = NULL;















	toRead = SpaceLeft(bufPtr);
	assert(toRead == statePtr->bufSize);

	if (statePtr->inQueueTail == NULL) {
	    statePtr->inQueueHead = bufPtr;
	} else {
	    statePtr->inQueueTail->nextPtr = bufPtr;
	}
	statePtr->inQueueTail = bufPtr;
    }

    /*
     * TODO - consider escape before buffer alloc
     * If EOF is set, we should avoid calling the driver because on some
     * platforms it is impossible to read from a device after EOF.
     */

    if (GotFlag(statePtr, CHANNEL_EOF)) {
	return 0;
    }





















    PreserveChannelBuffer(bufPtr);
    nread = ChanRead(chanPtr, InsertPoint(bufPtr), toRead, &result);


    if (nread > 0) {
	result = 0;
	bufPtr->nextAdded += nread;

	/*
	 * If we get a short read, signal up that we may be BLOCKED. We should
	 * avoid calling the driver because on some platforms we will block in
	 * the low level reading code even though the channel is set into
	 * nonblocking mode.
	 */

	if (nread < toRead) {
	    SetFlag(statePtr, CHANNEL_BLOCKED);
	}










    } else if (nread == 0) {
	result = 0;
	SetFlag(statePtr, CHANNEL_EOF);
	statePtr->inputEncodingFlags |= TCL_ENCODING_END;
    } else if (nread < 0) {
	if ((result == EWOULDBLOCK) || (result == EAGAIN)) {
	    SetFlag(statePtr, CHANNEL_BLOCKED);
7032
7033
7034
7035
7036
7037
7038
7039
7040
7041
7042
7043
7044
7045
7046
7047
7048
7049
7050
7051
7052
7053
7054
    if (BUSY_STATE(statePtr, flags) && ((flags & CHANNEL_RAW_MODE) == 0)) {
	Tcl_SetErrno(EBUSY);
	return -1;
    }

    if (direction == TCL_READABLE) {
	/*
	 * If we have not encountered a sticky EOF, clear the EOF bit (sticky
	 * EOF is set if we have seen the input eofChar, to prevent reading
	 * beyond the eofChar). Also, always clear the BLOCKED bit. We want to
	 * discover these conditions anew in each operation.
	 */

	if (!GotFlag(statePtr, CHANNEL_STICKY_EOF)) {
	    ResetFlag(statePtr, CHANNEL_EOF);
	}
	ResetFlag(statePtr, CHANNEL_BLOCKED | CHANNEL_NEED_MORE_DATA);
    }

    return 0;
}

/*







<
<
|
|


<
<
<







6975
6976
6977
6978
6979
6980
6981


6982
6983
6984
6985



6986
6987
6988
6989
6990
6991
6992
    if (BUSY_STATE(statePtr, flags) && ((flags & CHANNEL_RAW_MODE) == 0)) {
	Tcl_SetErrno(EBUSY);
	return -1;
    }

    if (direction == TCL_READABLE) {
	/*


	 * Clear the BLOCKED bit. We want to discover this condition
	 * anew in each operation.
	 */




	ResetFlag(statePtr, CHANNEL_BLOCKED | CHANNEL_NEED_MORE_DATA);
    }

    return 0;
}

/*
7257
7258
7259
7260
7261
7262
7263




7264
















7265
7266
7267
7268
7269
7270
7271
    if (sz < 1) {
	sz = 1;
    } else if (sz > MAX_CHANNEL_BUFFER_SIZE) {
	sz = MAX_CHANNEL_BUFFER_SIZE;
    }

    statePtr = ((Channel *) chan)->state;




    statePtr->bufSize = sz;
















}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_GetChannelBufferSize --
 *







>
>
>
>

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







7195
7196
7197
7198
7199
7200
7201
7202
7203
7204
7205
7206
7207
7208
7209
7210
7211
7212
7213
7214
7215
7216
7217
7218
7219
7220
7221
7222
7223
7224
7225
7226
7227
7228
7229
    if (sz < 1) {
	sz = 1;
    } else if (sz > MAX_CHANNEL_BUFFER_SIZE) {
	sz = MAX_CHANNEL_BUFFER_SIZE;
    }

    statePtr = ((Channel *) chan)->state;

    if (statePtr->bufSize == sz) {
	return;
    }
    statePtr->bufSize = sz;

    /*
     * If bufsize changes, need to get rid of old utility buffer.
     */

    if (statePtr->saveInBufPtr != NULL) {
	RecycleBuffer(statePtr, statePtr->saveInBufPtr, 1);
	statePtr->saveInBufPtr = NULL;
    }
    if ((statePtr->inQueueHead != NULL)
	    && (statePtr->inQueueHead->nextPtr == NULL)
	    && IsBufferEmpty(statePtr->inQueueHead)) {
	RecycleBuffer(statePtr, statePtr->inQueueHead, 1);
	statePtr->inQueueHead = NULL;
	statePtr->inQueueTail = NULL;
    }
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_GetChannelBufferSize --
 *
7692
7693
7694
7695
7696
7697
7698

7699
7700
7701
7702
7703
7704
7705
    } else if (HaveOpt(7, "-buffersize")) {
	int newBufferSize;

	if (Tcl_GetInt(interp, newValue, &newBufferSize) == TCL_ERROR) {
	    return TCL_ERROR;
	}
	Tcl_SetChannelBufferSize(chan, newBufferSize);

    } else if (HaveOpt(2, "-encoding")) {
	Tcl_Encoding encoding;

	if ((newValue[0] == '\0') || (strcmp(newValue, "binary") == 0)) {
	    encoding = NULL;
	} else {
	    encoding = Tcl_GetEncoding(interp, newValue);







>







7650
7651
7652
7653
7654
7655
7656
7657
7658
7659
7660
7661
7662
7663
7664
    } else if (HaveOpt(7, "-buffersize")) {
	int newBufferSize;

	if (Tcl_GetInt(interp, newValue, &newBufferSize) == TCL_ERROR) {
	    return TCL_ERROR;
	}
	Tcl_SetChannelBufferSize(chan, newBufferSize);
	return TCL_OK;
    } else if (HaveOpt(2, "-encoding")) {
	Tcl_Encoding encoding;

	if ((newValue[0] == '\0') || (strcmp(newValue, "binary") == 0)) {
	    encoding = NULL;
	} else {
	    encoding = Tcl_GetEncoding(interp, newValue);
7723
7724
7725
7726
7727
7728
7729

7730
7731
7732
7733
7734
7735
7736
	statePtr->encoding = encoding;
	statePtr->inputEncodingState = NULL;
	statePtr->inputEncodingFlags = TCL_ENCODING_START;
	statePtr->outputEncodingState = NULL;
	statePtr->outputEncodingFlags = TCL_ENCODING_START;
	ResetFlag(statePtr, CHANNEL_NEED_MORE_DATA);
	UpdateInterest(chanPtr);

    } else if (HaveOpt(2, "-eofchar")) {
	if (Tcl_SplitList(interp, newValue, &argc, &argv) == TCL_ERROR) {
	    return TCL_ERROR;
	}
	if (argc == 0) {
	    statePtr->inEofChar = 0;
	    statePtr->outEofChar = 0;







>







7682
7683
7684
7685
7686
7687
7688
7689
7690
7691
7692
7693
7694
7695
7696
	statePtr->encoding = encoding;
	statePtr->inputEncodingState = NULL;
	statePtr->inputEncodingFlags = TCL_ENCODING_START;
	statePtr->outputEncodingState = NULL;
	statePtr->outputEncodingFlags = TCL_ENCODING_START;
	ResetFlag(statePtr, CHANNEL_NEED_MORE_DATA);
	UpdateInterest(chanPtr);
	return TCL_OK;
    } else if (HaveOpt(2, "-eofchar")) {
	if (Tcl_SplitList(interp, newValue, &argc, &argv) == TCL_ERROR) {
	    return TCL_ERROR;
	}
	if (argc == 0) {
	    statePtr->inEofChar = 0;
	    statePtr->outEofChar = 0;
7883
7884
7885
7886
7887
7888
7889
7890
7891
7892
7893
7894
7895
7896
7897
7898
7899
7900
7901
7902
7903
7904
7905
7906
7907
7908
7909
7910
7911
7912
    } else if (chanPtr->typePtr->setOptionProc != NULL) {
	return chanPtr->typePtr->setOptionProc(chanPtr->instanceData, interp,
		optionName, newValue);
    } else {
	return Tcl_BadChannelOption(interp, optionName, NULL);
    }

    /*
     * If bufsize changes, need to get rid of old utility buffer.
     */

    if (statePtr->saveInBufPtr != NULL) {
	RecycleBuffer(statePtr, statePtr->saveInBufPtr, 1);
	statePtr->saveInBufPtr = NULL;
    }
    if ((statePtr->inQueueHead != NULL)
	    && (statePtr->inQueueHead->nextPtr == NULL)
	    && IsBufferEmpty(statePtr->inQueueHead)) {
	RecycleBuffer(statePtr, statePtr->inQueueHead, 1);
	statePtr->inQueueHead = NULL;
	statePtr->inQueueTail = NULL;
    }

    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * CleanupChannelHandlers --







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







7843
7844
7845
7846
7847
7848
7849
















7850
7851
7852
7853
7854
7855
7856
    } else if (chanPtr->typePtr->setOptionProc != NULL) {
	return chanPtr->typePtr->setOptionProc(chanPtr->instanceData, interp,
		optionName, newValue);
    } else {
	return Tcl_BadChannelOption(interp, optionName, NULL);
    }

















    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * CleanupChannelHandlers --
7990
7991
7992
7993
7994
7995
7996
7997
7998
7999
8000
8001
8002
8003
8004
8005
8006
8007
8008
8009
8010
8011
8012
8013
8014
8015
8016
8017
8018
				/* State info for channel */
    ChannelHandler *chPtr;
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
    NextChannelHandler nh;
    Channel *upChanPtr;
    const Tcl_ChannelType *upTypePtr;

#ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING
    /*
     * [SF Tcl Bug 943274] For a non-blocking channel without blockmodeproc we
     * keep track of actual input coming from the OS so that we can do a
     * credible imitation of non-blocking behaviour.
     */

    if ((mask & TCL_READABLE) &&
	    GotFlag(statePtr, CHANNEL_NONBLOCKING) &&
	    (Tcl_ChannelBlockModeProc(chanPtr->typePtr) == NULL) &&
	    !GotFlag(statePtr, CHANNEL_TIMER_FEV)) {
	SetFlag(statePtr, CHANNEL_HAS_MORE_DATA);
    }
#endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */

    /*
     * In contrast to the other API functions this procedure walks towards the
     * top of a stack and not down from it.
     *
     * The channel calling this procedure is the one who generated the event,
     * and thus does not take part in handling it. IOW, its HandlerProc is not
     * called, instead we begin with the channel above it.







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







7934
7935
7936
7937
7938
7939
7940















7941
7942
7943
7944
7945
7946
7947
				/* State info for channel */
    ChannelHandler *chPtr;
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
    NextChannelHandler nh;
    Channel *upChanPtr;
    const Tcl_ChannelType *upTypePtr;
















    /*
     * In contrast to the other API functions this procedure walks towards the
     * top of a stack and not down from it.
     *
     * The channel calling this procedure is the one who generated the event,
     * and thus does not take part in handling it. IOW, its HandlerProc is not
     * called, instead we begin with the channel above it.
8245
8246
8247
8248
8249
8250
8251
8252
8253
8254
8255
8256
8257
8258
8259
8260
8261
8262
8263
8264
8265
8266
8267
8268
8269
8270
8271
8272
8273
8274
8275
8276
8277
8278
8279
8280
8281
	/*
	 * Restart the timer in case a channel handler reenters the event loop
	 * before UpdateInterest gets called by Tcl_NotifyChannel.
	 */

	statePtr->timer = Tcl_CreateTimerHandler(SYNTHETIC_EVENT_TIME,
                ChannelTimerProc,chanPtr);

#ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING
	/*
	 * Set the TIMER flag to notify the higher levels that the driver
	 * might have no data for us. We do this only if we are in
	 * non-blocking mode and the driver has no BlockModeProc because only
	 * then we really don't know if the driver will block or not. A
	 * similar test is done in "PeekAhead".
	 */

	if (GotFlag(statePtr, CHANNEL_NONBLOCKING) &&
		(Tcl_ChannelBlockModeProc(chanPtr->typePtr) == NULL)) {
	    SetFlag(statePtr, CHANNEL_TIMER_FEV);
	}
#endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */

	Tcl_Preserve(statePtr);
	Tcl_NotifyChannel((Tcl_Channel) chanPtr, TCL_READABLE);

#ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING
	ResetFlag(statePtr, CHANNEL_TIMER_FEV);
#endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */

	Tcl_Release(statePtr);
    } else {
	statePtr->timer = NULL;
	UpdateInterest(chanPtr);
    }
}








<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


<
<
<
<
<







8174
8175
8176
8177
8178
8179
8180
















8181
8182





8183
8184
8185
8186
8187
8188
8189
	/*
	 * Restart the timer in case a channel handler reenters the event loop
	 * before UpdateInterest gets called by Tcl_NotifyChannel.
	 */

	statePtr->timer = Tcl_CreateTimerHandler(SYNTHETIC_EVENT_TIME,
                ChannelTimerProc,chanPtr);
















	Tcl_Preserve(statePtr);
	Tcl_NotifyChannel((Tcl_Channel) chanPtr, TCL_READABLE);





	Tcl_Release(statePtr);
    } else {
	statePtr->timer = NULL;
	UpdateInterest(chanPtr);
    }
}

9195
9196
9197
9198
9199
9200
9201


9202
9203
9204
9205
9206

9207








9208
9209
9210
9211
9212
9213
9214
9215
9216
9217
9218
9219
9220
9221
9222
9223
9224
9225
9226
9227
9228
9229


9230
9231
9232

9233
9234
9235
9236
9237

9238
9239
9240
9241
9242
9243
9244
9245
9246

9247
9248
9249
9250
9251
9252
9253
9254
9255

9256
9257
9258
9259
9260
9261
9262
9263

9264
9265
9266
9267
9268
9269
9270
9271
9272
9273
9274

9275
9276
9277
9278
9279
9280
9281
9282
9283
9284
9285
9286
9287
9288
9289
9290
9291
9292
9293


9294
9295
9296
9297
9298
9299
9300
9301
9302
9303
9304
9305
9306
9307
9308
9309
9310
9311
9312
9313



9314
9315
9316
9317
9318
9319
9320
9321
9322
9323
9324
9325
9326
9327
9328
9329
9330
9331
9332
9333
9334
9335
9336
9337
9338
9339
9340
9341
9342

9343
9344
9345
9346
9347
9348
9349
9350
9351
9352
9353
9354
9355




9356
9357
9358
9359
9360
9361
9362
9363
9364
9365
9366
9367
9368
9369
9370
9371
9372
9373
9374
9375
9376
9377

9378
9379
9380
9381
9382
9383
9384
9385
9386
9387
9388
9389

9390

9391
9392
9393
9394
9395
9396
9397
9398
9399
9400
9401
9402
9403
9404
9405
9406
9407
9408
9409
9410
9411
9412
9413
9414
9415
9416
9417
9418
9419
9420
9421
9422
9423
9424
9425
9426
9427
9428
9429
9430
9431
9432
9433
9434
9435
9436
9437
9438
9439
9440
9441
9442
9443
9444
9445
9446
9447
9448
9449

9450
9451
9452

9453
9454
9455
9456
9457
9458
9459
9460
9461
9462


9463
9464
9465
9466
9467
9468
9469
9470
9471
9472
9473
9474
9475
9476
9477
9478
9479
9480
9481
9482
9483
9484
9485
9486
9487
9488
9489
9490
9491
9492
9493
9494
9495
9496
9497
9498
9499

9500
9501
9502
9503
9504



9505
9506
9507
9508
9509
9510
9511
9512
}

/*
 *----------------------------------------------------------------------
 *
 * DoRead --
 *


 *	Reads a given number of bytes from a channel. No encoding conversions
 *	are applied to the bytes being read.
 *
 * Results:
 *	The number of characters read, or -1 on error. Use Tcl_GetErrno() to

 *	retrieve the error code for the error that occurred.








 *
 * Side effects:
 *	May cause input to be buffered.
 *
 *----------------------------------------------------------------------
 */

static int
DoRead(
    Channel *chanPtr,		/* The channel from which to read. */
    char *bufPtr,		/* Where to store input read. */
    int toRead,			/* Maximum number of bytes to read. */
    int allowShortReads)	/* Allow half-blocking (pipes,sockets) */
{
    ChannelState *statePtr = chanPtr->state;
				/* State info for channel */
    int copied;			/* How many characters were copied into the
				 * result string? */
    int copiedNow;		/* How many characters were copied from the
				 * current input buffer? */
    int result;			/* Of calling GetInput. */



    /*
     * If we have not encountered a sticky EOF, clear the EOF bit. Either way
     * clear the BLOCKED bit. We want to discover these anew during each

     * operation.
     */

    Tcl_Preserve(chanPtr);
    if (!GotFlag(statePtr, CHANNEL_STICKY_EOF)) {

	ResetFlag(statePtr, CHANNEL_EOF);
    }
    ResetFlag(statePtr, CHANNEL_BLOCKED | CHANNEL_NEED_MORE_DATA);

    for (copied = 0; copied < toRead; copied += copiedNow) {
	copiedNow = CopyAndTranslateBuffer(statePtr, bufPtr + copied,
		toRead - copied);
	if (copiedNow == 0) {
	    if (GotFlag(statePtr, CHANNEL_EOF)) {

		goto done;
	    }
	    if (GotFlag(statePtr, CHANNEL_BLOCKED)) {
		if (GotFlag(statePtr, CHANNEL_NONBLOCKING)) {
		    goto done;
		}
		ResetFlag(statePtr, CHANNEL_BLOCKED);
	    }
	    result = GetInput(chanPtr);

	    if (result != 0) {
		if (result != EAGAIN) {
		    copied = -1;
		}
		goto done;
	    }
	} else if (allowShortReads) {
            copied += copiedNow;

            break;
        }
    }

    ResetFlag(statePtr, CHANNEL_BLOCKED);

    /*
     * Update the notifier state so we don't block while there is still data
     * in the buffers.
     */


  done:
    UpdateInterest(chanPtr);
    Tcl_Release(chanPtr);
    return copied;
}

/*
 *----------------------------------------------------------------------
 *
 * CopyAndTranslateBuffer --
 *
 *	Copy at most one buffer of input to the result space, doing eol
 *	translations according to mode in effect currently.
 *
 * Results:
 *	Number of bytes stored in the result buffer (as opposed to the number
 *	of bytes read from the channel). May return zero if no input is
 *	available to be translated.
 *


 * Side effects:
 *	Consumes buffered input. May deallocate one buffer.
 *
 *----------------------------------------------------------------------
 */

static int
CopyAndTranslateBuffer(
    ChannelState *statePtr,	/* Channel state from which to read input. */
    char *result,		/* Where to store the copied input. */
    int space)			/* How many bytes are available in result to
				 * store the copied input? */
{
    ChannelBuffer *bufPtr;	/* The buffer from which to copy bytes. */
    int bytesInBuffer;		/* How many bytes are available to be copied
				 * in the current input buffer? */
    int copied;			/* How many characters were already copied
				 * into the destination space? */
    int i;			/* Iterates over the copied input looking for
				 * the input eofChar. */




    /*
     * If there is no input at all, return zero. The invariant is that either
     * there is no buffer in the queue, or if the first buffer is empty, it is
     * also the last buffer (and thus there is no input in the queue). Note
     * also that if the buffer is empty, we leave it in the queue.
     */

    if (statePtr->inQueueHead == NULL) {
	return 0;
    }
    bufPtr = statePtr->inQueueHead;
    bytesInBuffer = BytesLeft(bufPtr);

    copied = 0;
    switch (statePtr->inputTranslation) {
    case TCL_TRANSLATE_LF:
	if (bytesInBuffer == 0) {
	    return 0;
	}

	/*
	 * Copy the current chunk into the result buffer.
	 */

	if (bytesInBuffer < space) {
	    space = bytesInBuffer;
	}
	memcpy(result, RemovePoint(bufPtr), (size_t) space);

	bufPtr->nextRemoved += space;
	copied = space;
	break;
    case TCL_TRANSLATE_CR: {
	char *end;

	if (bytesInBuffer == 0) {
	    return 0;
	}

	/*
	 * Copy the current chunk into the result buffer, then replace all \r
	 * with \n.




	 */

	if (bytesInBuffer < space) {
	    space = bytesInBuffer;
	}
	memcpy(result, RemovePoint(bufPtr), (size_t) space);
	bufPtr->nextRemoved += space;
	copied = space;

	for (end = result + copied; result < end; result++) {
	    if (*result == '\r') {
		*result = '\n';
	    }
	}
	break;
    }
    case TCL_TRANSLATE_CRLF: {
	char *src, *end, *dst;
	int curByte;

	/*
	 * If there is a held-back "\r" at EOF, produce it now.

	 */

	if (bytesInBuffer == 0) {
	    if ((statePtr->flags & (INPUT_SAW_CR | CHANNEL_EOF)) ==
		    (INPUT_SAW_CR | CHANNEL_EOF)) {
		result[0] = '\r';
		ResetFlag(statePtr, INPUT_SAW_CR);
		return 1;
	    }
	    return 0;
	}


	/*

	 * Copy the current chunk and replace "\r\n" with "\n" (but not
	 * standalone "\r"!).
	 */

	if (bytesInBuffer < space) {
	    space = bytesInBuffer;
	}
	memcpy(result, RemovePoint(bufPtr), (size_t) space);
	bufPtr->nextRemoved += space;
	copied = space;

	end = result + copied;
	dst = result;
	for (src = result; src < end; src++) {
	    curByte = *src;
	    if (curByte == '\n') {
		ResetFlag(statePtr, INPUT_SAW_CR);
	    } else if (GotFlag(statePtr, INPUT_SAW_CR)) {
		ResetFlag(statePtr, INPUT_SAW_CR);
		*dst = '\r';
		dst++;
	    }
	    if (curByte == '\r') {
		SetFlag(statePtr, INPUT_SAW_CR);
	    } else {
		*dst = (char) curByte;
		dst++;
	    }
	}
	copied = dst - result;
	break;
    }
    case TCL_TRANSLATE_AUTO: {
	char *src, *end, *dst;
	int curByte;

	if (bytesInBuffer == 0) {
	    return 0;
	}

	/*
	 * Loop over the current buffer, converting "\r" and "\r\n" to "\n".
	 */

	if (bytesInBuffer < space) {
	    space = bytesInBuffer;
	}
	memcpy(result, RemovePoint(bufPtr), (size_t) space);
	bufPtr->nextRemoved += space;
	copied = space;

	end = result + copied;
	dst = result;
	for (src = result; src < end; src++) {
	    curByte = *src;
	    if (curByte == '\r') {
		SetFlag(statePtr, INPUT_SAW_CR);
		*dst = '\n';
		dst++;

	    } else {
		if ((curByte != '\n') || !GotFlag(statePtr, INPUT_SAW_CR)) {
		    *dst = (char) curByte;

		    dst++;
		}
		ResetFlag(statePtr, INPUT_SAW_CR);
	    }
	}
	copied = dst - result;
	break;
    }
    default:
	Tcl_Panic("unknown eol translation mode");


    }

    /*
     * If an in-stream EOF character is set for this channel, check that the
     * input we copied so far does not contain the EOF char. If it does, copy
     * only up to and excluding that character.
     */

    if (statePtr->inEofChar != 0) {
	for (i = 0; i < copied; i++) {
	    if (result[i] == (char) statePtr->inEofChar) {
		/*
		 * Set sticky EOF so that no further input is presented to the
		 * caller.
		 */

		SetFlag(statePtr, CHANNEL_EOF | CHANNEL_STICKY_EOF);
		statePtr->inputEncodingFlags |= TCL_ENCODING_END;
		copied = i;
		break;
	    }
	}
    }

    /*
     * If the current buffer is empty recycle it.
     */

    if (IsBufferEmpty(bufPtr)) {
	statePtr->inQueueHead = bufPtr->nextPtr;
	if (statePtr->inQueueHead == NULL) {
	    statePtr->inQueueTail = NULL;
	}
	RecycleBuffer(statePtr, bufPtr, 0);
    }

    /*

     * Return the number of characters copied into the result buffer. This may
     * be different from the number of bytes consumed, because of EOL
     * translations.
     */




    return copied;
}

/*
 *----------------------------------------------------------------------
 *
 * CopyBuffer --
 *







>
>
|



|
>
|
>
>
>
>
>
>
>
>










|
|



|
<
<
<
<
<

>
>
|
<
<
>
|
|

<
<
>
|
|
<
|
<
<
<
<
<
>
|
<
<
<
<
<
<
<
<
>
<
<
<
|
<
<
|
<
>
|
|
|
|
<

<
|
|
<

>
|
|
|
<
|
|
<
<
<
<
<
<
<
<
<
<
<
<
|
>
>
|
<
<
<
<
|
<
<
<
<
<
<
|
<
<
<
<
|
<
|
>
>
>
|
|
<
<
<
|
<
|
<
<
|
<
<
|
<
<
<
<
<
|
|
<
<
<
|
<
<
|
|
>
|
<
<
<
<
|
|
<
|
|
|
<
<
>
>
>
>
|

|
<
<
<
<
<
|
<
<
<
<
<
|
|
<
<
<

|
<
>
|

<
|
<
<
|
|
<
|
<
|
>
|
>
|
<
|

<
|
<
|
<
<
|
<
<
<
<
<
<
<
<
<
<
|
|
<
<
<
<
<
<
|
<
<
<
<
<

|
|
|
|
<
<
<
|
<
<
<
<
|
<
|
|
<
<
<
<
|
<
|
>
|
<
<
>
|
<
<
|
|
<
<
|
<
<
>
>
|
|
<
<
<
<
<

|
<
<
<
<
<
<
|
<
|
<
<


<
<
<
<
<

|
|
|
|
|
|
|

<
>
|
<
|
<
|
>
>
>
|







9103
9104
9105
9106
9107
9108
9109
9110
9111
9112
9113
9114
9115
9116
9117
9118
9119
9120
9121
9122
9123
9124
9125
9126
9127
9128
9129
9130
9131
9132
9133
9134
9135
9136
9137
9138
9139
9140
9141
9142





9143
9144
9145
9146


9147
9148
9149
9150


9151
9152
9153

9154





9155
9156








9157



9158


9159

9160
9161
9162
9163
9164

9165

9166
9167

9168
9169
9170
9171
9172

9173
9174












9175
9176
9177
9178




9179






9180




9181

9182
9183
9184
9185
9186
9187



9188

9189


9190


9191





9192
9193



9194


9195
9196
9197
9198




9199
9200

9201
9202
9203


9204
9205
9206
9207
9208
9209
9210





9211





9212
9213



9214
9215

9216
9217
9218

9219


9220
9221

9222

9223
9224
9225
9226
9227

9228
9229

9230

9231


9232










9233
9234






9235





9236
9237
9238
9239
9240



9241




9242

9243
9244




9245

9246
9247
9248


9249
9250


9251
9252


9253


9254
9255
9256
9257





9258
9259






9260

9261


9262
9263





9264
9265
9266
9267
9268
9269
9270
9271
9272

9273
9274

9275

9276
9277
9278
9279
9280
9281
9282
9283
9284
9285
9286
9287
}

/*
 *----------------------------------------------------------------------
 *
 * DoRead --
 *
 *	Stores up to "bytesToRead" bytes in memory pointed to by "dst".
 *	These bytes come from reading the channel "chanPtr" and 
 *	performing the configured translations.  No encoding conversions
 *	are applied to the bytes being read.
 *
 * Results:
 *	The number of bytes actually stored (<= bytesToRead),
 * 	or -1 if there is an error in reading the channel.  Use
 * 	Tcl_GetErrno() to retrieve the error code for the error
 *	that occurred.
 *
 *	The number of bytes stored can be less than the number
 * 	requested when
 *	  - EOF is reached on the channel; or
 *	  - the channel is non-blocking, and we've read all we can
 *	    without blocking.
 *	  - a channel reading error occurs (and we return -1)
 *
 * Side effects:
 *	May cause input to be buffered.
 *
 *----------------------------------------------------------------------
 */

static int
DoRead(
    Channel *chanPtr,		/* The channel from which to read. */
    char *dst,			/* Where to store input read. */
    int bytesToRead,		/* Maximum number of bytes to read. */
    int allowShortReads)	/* Allow half-blocking (pipes,sockets) */
{
    ChannelState *statePtr = chanPtr->state;
    char *p = dst;






    Tcl_Preserve(chanPtr);
    while (bytesToRead) {
	/*


	 * Each pass through the loop is intended to process up to 
	 * one channel buffer.
	 */



	int bytesRead, bytesWritten;
	ChannelBuffer *bufPtr = statePtr->inQueueHead;


	/*





	 * When there's no buffered data to read, and we're at EOF,
	 * escape to the caller.








	 */






	if (statePtr->flags & CHANNEL_EOF

		&& (bufPtr == NULL || IsBufferEmpty(bufPtr))) {
	    break;
	}

	/* If there is no full buffer, attempt to create and/or fill one. */



	while (bufPtr == NULL || !IsBufferFull(bufPtr)) {
	    int code;


	    ResetFlag(statePtr, CHANNEL_BLOCKED);
	moreData:
	    code = GetInput(chanPtr);
	    bufPtr = statePtr->inQueueHead;


	    assert (bufPtr != NULL);













	    if (statePtr->flags & (CHANNEL_EOF|CHANNEL_BLOCKED)) {
		/* Further reads cannot do any more */
		break;




	    }






	    




	    if (code) {

		/* Read error */
		UpdateInterest(chanPtr);
		Tcl_Release(chanPtr);
		return -1;
	    }




	    assert (IsBufferFull(bufPtr));

	}





	assert (bufPtr != NULL);






	bytesRead = BytesLeft(bufPtr);



	bytesWritten = bytesToRead;



	TranslateInputEOL(statePtr, p, RemovePoint(bufPtr),
		&bytesWritten, &bytesRead);
	bufPtr->nextRemoved += bytesRead;




	p += bytesWritten;
	bytesToRead -= bytesWritten;


	if (!IsBufferEmpty(bufPtr)) {
	    /*


	     * Buffer is not empty.  How can that be?
	     *
	     * 0) We stopped early because we got all the bytes
	     *    we were seeking.  That's fine.
	     */

	    if (bytesToRead == 0) {





		UpdateInterest(chanPtr);





		break;
	    }




	    /*

	     * 1) We're @EOF because we saw eof char.
	     */


	    if (statePtr->inEofChar


		    && RemovePoint(bufPtr)[0] == statePtr->inEofChar) {
		UpdateInterest(chanPtr);

		break;

	    }

	    /*
	     * 2) The buffer holds a \r while in CRLF translation,
	     *    followed by the end of the buffer.

	     */


	    assert(statePtr->inputTranslation == TCL_TRANSLATE_CRLF);

	    assert(RemovePoint(bufPtr)[0] == '\r');


	    assert(BytesLeft(bufPtr) == 1);











	    if (bufPtr->nextPtr == NULL) {






		/* There's no more buffered data.... */






		if (statePtr->flags & CHANNEL_EOF) {
		    /* ...and there never will be. */

		    *p++ = '\r';



		    bytesToRead--;




		    bufPtr->nextRemoved++;

		} else if (statePtr->flags & CHANNEL_BLOCKED) {
		    /* ...and we cannot get more now. */




		    SetFlag(statePtr, CHANNEL_NEED_MORE_DATA);

		    UpdateInterest(chanPtr);
		    break;
		} else {


		    /* ... so we need to get some. */
		    goto moreData;


		}
	    }





	    if (bufPtr->nextPtr) {
		/* There's a next buffer.  Shift orphan \r to it. */

		ChannelBuffer *nextPtr = bufPtr->nextPtr;






		nextPtr->nextRemoved -= 1;






		RemovePoint(nextPtr)[0] = '\r';

		bufPtr->nextRemoved++;


	    }
	}






	if (IsBufferEmpty(bufPtr)) {
	    statePtr->inQueueHead = bufPtr->nextPtr;
	    if (statePtr->inQueueHead == NULL) {
		statePtr->inQueueTail = NULL;
	    }
	    RecycleBuffer(statePtr, bufPtr, 0);
	}


	if ((statePtr->flags & CHANNEL_NONBLOCKING || allowShortReads)
		&& statePtr->flags & CHANNEL_BLOCKED) {

	    break;

	}
    }

    Tcl_Release(chanPtr);
    return (int)(p - dst);
}

/*
 *----------------------------------------------------------------------
 *
 * CopyBuffer --
 *
9708
9709
9710
9711
9712
9713
9714

9715
9716
9717


9718
9719
9720
9721
9722
9723
9724
9725
9726
9727
StackSetBlockMode(
    Channel *chanPtr,		/* Channel to modify. */
    int mode)			/* One of TCL_MODE_BLOCKING or
				 * TCL_MODE_NONBLOCKING. */
{
    int result = 0;
    Tcl_DriverBlockModeProc *blockModeProc;


    /*
     * Start at the top of the channel stack


     */

    chanPtr = chanPtr->state->topChanPtr;
    while (chanPtr != NULL) {
	blockModeProc = Tcl_ChannelBlockModeProc(chanPtr->typePtr);
	if (blockModeProc != NULL) {
	    result = blockModeProc(chanPtr->instanceData, mode);
	    if (result != 0) {
		Tcl_SetErrno(result);
		return result;







>



>
>


|







9483
9484
9485
9486
9487
9488
9489
9490
9491
9492
9493
9494
9495
9496
9497
9498
9499
9500
9501
9502
9503
9504
9505
StackSetBlockMode(
    Channel *chanPtr,		/* Channel to modify. */
    int mode)			/* One of TCL_MODE_BLOCKING or
				 * TCL_MODE_NONBLOCKING. */
{
    int result = 0;
    Tcl_DriverBlockModeProc *blockModeProc;
    ChannelState *statePtr = chanPtr->state;

    /*
     * Start at the top of the channel stack
     * TODO: Examine what can go wrong when blockModeProc calls
     * disturb the stacking state of the channel.
     */

    chanPtr = statePtr->topChanPtr;
    while (chanPtr != NULL) {
	blockModeProc = Tcl_ChannelBlockModeProc(chanPtr->typePtr);
	if (blockModeProc != NULL) {
	    result = blockModeProc(chanPtr->instanceData, mode);
	    if (result != 0) {
		Tcl_SetErrno(result);
		return result;
10852
10853
10854
10855
10856
10857
10858

10859
10860
10861
10862
10863
10864
10865
    ChannelState *statePtr;

    if (interp == NULL) {
	return TCL_ERROR;
    }
    if (objPtr->typePtr == &chanObjType) {
	/*

	 * The channel is valid until any call to DetachChannel occurs.
	 * Ensure consistency checks are done.
	 */

	statePtr = GET_CHANNELSTATE(objPtr);
	if (GotFlag(statePtr, CHANNEL_TAINTED|CHANNEL_CLOSED)) {
	    ResetFlag(statePtr, CHANNEL_TAINTED);







>







10630
10631
10632
10633
10634
10635
10636
10637
10638
10639
10640
10641
10642
10643
10644
    ChannelState *statePtr;

    if (interp == NULL) {
	return TCL_ERROR;
    }
    if (objPtr->typePtr == &chanObjType) {
	/*
	 * TODO: TAINT Flag and dup'd channel values?
	 * The channel is valid until any call to DetachChannel occurs.
	 * Ensure consistency checks are done.
	 */

	statePtr = GET_CHANNELSTATE(objPtr);
	if (GotFlag(statePtr, CHANNEL_TAINTED|CHANNEL_CLOSED)) {
	    ResetFlag(statePtr, CHANNEL_TAINTED);
10935
10936
10937
10938
10939
10940
10941
10942
10943
10944
10945
10946
10947
10948
10949
10950
10951
10952
10953
10954
10955
    ChanFlag('R', BUFFER_READY);
    ChanFlag('F', BG_FLUSH_SCHEDULED);
    ChanFlag('c', CHANNEL_CLOSED);
    ChanFlag('E', CHANNEL_EOF);
    ChanFlag('S', CHANNEL_STICKY_EOF);
    ChanFlag('B', CHANNEL_BLOCKED);
    ChanFlag('/', INPUT_SAW_CR);
    ChanFlag('*', INPUT_NEED_NL);
    ChanFlag('D', CHANNEL_DEAD);
    ChanFlag('R', CHANNEL_RAW_MODE);
#ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING
    ChanFlag('T', CHANNEL_TIMER_FEV);
    ChanFlag('H', CHANNEL_HAS_MORE_DATA);
#endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */
    ChanFlag('x', CHANNEL_INCLOSE);

    buf[i] ='\0';

    fprintf(stderr, "%s: %s\n", str, buf);
    return 0;
}







<


<
<
<
<







10714
10715
10716
10717
10718
10719
10720

10721
10722




10723
10724
10725
10726
10727
10728
10729
    ChanFlag('R', BUFFER_READY);
    ChanFlag('F', BG_FLUSH_SCHEDULED);
    ChanFlag('c', CHANNEL_CLOSED);
    ChanFlag('E', CHANNEL_EOF);
    ChanFlag('S', CHANNEL_STICKY_EOF);
    ChanFlag('B', CHANNEL_BLOCKED);
    ChanFlag('/', INPUT_SAW_CR);

    ChanFlag('D', CHANNEL_DEAD);
    ChanFlag('R', CHANNEL_RAW_MODE);




    ChanFlag('x', CHANNEL_INCLOSE);

    buf[i] ='\0';

    fprintf(stderr, "%s: %s\n", str, buf);
    return 0;
}

Changes to generic/tclIO.h.

249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
#define CHANNEL_BLOCKED		(1<<11)	/* EWOULDBLOCK or EAGAIN occurred on
					 * this channel. This bit is cleared
					 * before every input or output
					 * operation. */
#define INPUT_SAW_CR		(1<<12)	/* Channel is in CRLF eol input
					 * translation mode and the last byte
					 * seen was a "\r". */
#define INPUT_NEED_NL		(1<<15)	/* Saw a '\r' at end of last buffer,
					 * and there should be a '\n' at
					 * beginning of next buffer. */
#define CHANNEL_DEAD		(1<<13)	/* The channel has been closed by the
					 * exit handler (on exit) but not
					 * deallocated. When any IO operation
					 * sees this flag on a channel, it
					 * does not call driver level
					 * functions to avoid referring to
					 * deallocated data. */
#define CHANNEL_NEED_MORE_DATA	(1<<14)	/* The last input operation failed
					 * because there was not enough data
					 * to complete the operation. This
					 * flag is set when gets fails to get
					 * a complete line or when read fails
					 * to get a complete character. When
					 * set, file events will not be
					 * delivered for buffered data until
					 * the state of the channel
					 * changes. */
#define CHANNEL_RAW_MODE	(1<<16)	/* When set, notes that the Raw API is
					 * being used. */
#ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING
#define CHANNEL_TIMER_FEV	(1<<17)	/* When set the event we are notified
					 * by is a fileevent generated by a
					 * timer. We don't know if the driver
					 * has more data and should not try to
					 * read from it. If the system needs
					 * more than is in the buffers out
					 * read routines will simulate a short
					 * read (0 characters read) */
#define CHANNEL_HAS_MORE_DATA   (1<<18) /* Set by NotifyChannel for a channel
					 * if and only if the channel is
					 * configured non-blocking, the driver
					 * for said channel has no
					 * blockmodeproc, and data has arrived
					 * for reading at the OS level). A
					 * GetInput will pass reading from the
					 * driver if the channel is
					 * non-blocking, without blockmode
					 * proc and the flag has not been set.
					 * A read will be performed if the
					 * flag is set. This will reset the
					 * flag as well. */
#endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */

#define CHANNEL_INCLOSE		(1<<19)	/* Channel is currently being closed.
					 * Its structures are still live and
					 * usable, but it may not be closed
					 * again from within the close
					 * handler. */
#define CHANNEL_TAINTED		(1<<20)	/* Channel stack structure has changed.







<
<
<



















<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







249
250
251
252
253
254
255



256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274























275
276
277
278
279
280
281
#define CHANNEL_BLOCKED		(1<<11)	/* EWOULDBLOCK or EAGAIN occurred on
					 * this channel. This bit is cleared
					 * before every input or output
					 * operation. */
#define INPUT_SAW_CR		(1<<12)	/* Channel is in CRLF eol input
					 * translation mode and the last byte
					 * seen was a "\r". */



#define CHANNEL_DEAD		(1<<13)	/* The channel has been closed by the
					 * exit handler (on exit) but not
					 * deallocated. When any IO operation
					 * sees this flag on a channel, it
					 * does not call driver level
					 * functions to avoid referring to
					 * deallocated data. */
#define CHANNEL_NEED_MORE_DATA	(1<<14)	/* The last input operation failed
					 * because there was not enough data
					 * to complete the operation. This
					 * flag is set when gets fails to get
					 * a complete line or when read fails
					 * to get a complete character. When
					 * set, file events will not be
					 * delivered for buffered data until
					 * the state of the channel
					 * changes. */
#define CHANNEL_RAW_MODE	(1<<16)	/* When set, notes that the Raw API is
					 * being used. */
























#define CHANNEL_INCLOSE		(1<<19)	/* Channel is currently being closed.
					 * Its structures are still live and
					 * usable, but it may not be closed
					 * again from within the close
					 * handler. */
#define CHANNEL_TAINTED		(1<<20)	/* Channel stack structure has changed.

Changes to generic/tclIOCmd.c.

332
333
334
335
336
337
338
339

340
341
342
343
344
345
346
	    goto done;
	}
	lineLen = -1;
    }
    if (objc == 3) {
	if (Tcl_ObjSetVar2(interp, objv[2], NULL, linePtr,
		TCL_LEAVE_ERR_MSG) == NULL) {
	    return TCL_ERROR;

	}
	Tcl_SetObjResult(interp, Tcl_NewIntObj(lineLen));
    } else {
	Tcl_SetObjResult(interp, linePtr);
    }
  done:
    Tcl_Release(chan);







|
>







332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
	    goto done;
	}
	lineLen = -1;
    }
    if (objc == 3) {
	if (Tcl_ObjSetVar2(interp, objv[2], NULL, linePtr,
		TCL_LEAVE_ERR_MSG) == NULL) {
	    code = TCL_ERROR;
	    goto done;
	}
	Tcl_SetObjResult(interp, Tcl_NewIntObj(lineLen));
    } else {
	Tcl_SetObjResult(interp, linePtr);
    }
  done:
    Tcl_Release(chan);

Changes to generic/tclIOGT.c.

294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319

320
321
322
323
324
325
326
    dataPtr->readIsFlushed = 0;
    dataPtr->flags = 0;
    if (ds.string[0] == '0') {
	dataPtr->flags |= CHANNEL_ASYNC;
    }
    Tcl_DStringFree(&ds);

    dataPtr->self = chan;
    dataPtr->watchMask = 0;
    dataPtr->mode = mode;
    dataPtr->timer = NULL;
    dataPtr->maxRead = 4096;	/* Initial value not relevant. */
    dataPtr->interp = interp;
    dataPtr->command = cmdObjPtr;
    Tcl_IncrRefCount(dataPtr->command);

    ResultInit(&dataPtr->result);

    dataPtr->self = Tcl_StackChannel(interp, &transformChannelType, dataPtr,
	    mode, chan);
    if (dataPtr->self == NULL) {
	Tcl_AppendPrintfToObj(Tcl_GetObjResult(interp),
		"\nfailed to stack channel \"%s\"", Tcl_GetChannelName(chan));
	ReleaseData(dataPtr);
	return TCL_ERROR;
    }


    /*
     * At last initialize the transformation at the script level.
     */

    PreserveData(dataPtr);
    if ((dataPtr->mode & TCL_WRITABLE) && ExecuteCallback(dataPtr, NULL,







<


















>







294
295
296
297
298
299
300

301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
    dataPtr->readIsFlushed = 0;
    dataPtr->flags = 0;
    if (ds.string[0] == '0') {
	dataPtr->flags |= CHANNEL_ASYNC;
    }
    Tcl_DStringFree(&ds);


    dataPtr->watchMask = 0;
    dataPtr->mode = mode;
    dataPtr->timer = NULL;
    dataPtr->maxRead = 4096;	/* Initial value not relevant. */
    dataPtr->interp = interp;
    dataPtr->command = cmdObjPtr;
    Tcl_IncrRefCount(dataPtr->command);

    ResultInit(&dataPtr->result);

    dataPtr->self = Tcl_StackChannel(interp, &transformChannelType, dataPtr,
	    mode, chan);
    if (dataPtr->self == NULL) {
	Tcl_AppendPrintfToObj(Tcl_GetObjResult(interp),
		"\nfailed to stack channel \"%s\"", Tcl_GetChannelName(chan));
	ReleaseData(dataPtr);
	return TCL_ERROR;
    }
    Tcl_Preserve(dataPtr->self);

    /*
     * At last initialize the transformation at the script level.
     */

    PreserveData(dataPtr);
    if ((dataPtr->mode & TCL_WRITABLE) && ExecuteCallback(dataPtr, NULL,
433
434
435
436
437
438
439



440
441
442
443
444
445
446



447
448
449
450
451
452
453

    switch (transmit) {
    case TRANSMIT_DONT:
	/* nothing to do */
	break;

    case TRANSMIT_DOWN:



	resObj = Tcl_GetObjResult(eval);
	resBuf = Tcl_GetByteArrayFromObj(resObj, &resLen);
	Tcl_WriteRaw(Tcl_GetStackedChannel(dataPtr->self), (char *) resBuf,
		resLen);
	break;

    case TRANSMIT_SELF:



	resObj = Tcl_GetObjResult(eval);
	resBuf = Tcl_GetByteArrayFromObj(resObj, &resLen);
	Tcl_WriteRaw(dataPtr->self, (char *) resBuf, resLen);
	break;

    case TRANSMIT_IBUF:
	resObj = Tcl_GetObjResult(eval);







>
>
>







>
>
>







433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459

    switch (transmit) {
    case TRANSMIT_DONT:
	/* nothing to do */
	break;

    case TRANSMIT_DOWN:
	if (dataPtr->self == NULL) {
	    break;
	}
	resObj = Tcl_GetObjResult(eval);
	resBuf = Tcl_GetByteArrayFromObj(resObj, &resLen);
	Tcl_WriteRaw(Tcl_GetStackedChannel(dataPtr->self), (char *) resBuf,
		resLen);
	break;

    case TRANSMIT_SELF:
	if (dataPtr->self == NULL) {
	    break;
	}
	resObj = Tcl_GetObjResult(eval);
	resBuf = Tcl_GetByteArrayFromObj(resObj, &resLen);
	Tcl_WriteRaw(dataPtr->self, (char *) resBuf, resLen);
	break;

    case TRANSMIT_IBUF:
	resObj = Tcl_GetObjResult(eval);
575
576
577
578
579
580
581


582
583
584
585
586
587
588
    }
    ReleaseData(dataPtr);

    /*
     * General cleanup.
     */



    ReleaseData(dataPtr);
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *







>
>







581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
    }
    ReleaseData(dataPtr);

    /*
     * General cleanup.
     */

    Tcl_Release(dataPtr->self);
    dataPtr->self = NULL;
    ReleaseData(dataPtr);
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
    int gotBytes, read, copied;
    Tcl_Channel downChan;

    /*
     * Should assert(dataPtr->mode & TCL_READABLE);
     */

    if (toRead == 0) {
	/*
	 * Catch a no-op.
	 */
	return 0;
    }

    gotBytes = 0;







|







618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
    int gotBytes, read, copied;
    Tcl_Channel downChan;

    /*
     * Should assert(dataPtr->mode & TCL_READABLE);
     */

    if (toRead == 0 || dataPtr->self == NULL) {
	/*
	 * Catch a no-op.
	 */
	return 0;
    }

    gotBytes = 0;
1080
1081
1082
1083
1084
1085
1086



1087
1088
1089
1090
1091
1092
1093
     * events on the channel below via a call to our 'TransformNotifyProc'.
     * But we have to pass the interest down now. We are allowed to add
     * additional 'interest' to the mask if we want to. But this
     * transformation has no such interest. It just passes the request down,
     * unchanged.
     */




    downChan = Tcl_GetStackedChannel(dataPtr->self);

    Tcl_GetChannelType(downChan)->watchProc(
	    Tcl_GetChannelInstanceData(downChan), mask);

    /*
     * Management of the internal timer.







>
>
>







1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
     * events on the channel below via a call to our 'TransformNotifyProc'.
     * But we have to pass the interest down now. We are allowed to add
     * additional 'interest' to the mask if we want to. But this
     * transformation has no such interest. It just passes the request down,
     * unchanged.
     */

    if (dataPtr->self == NULL) {
	return;
    }
    downChan = Tcl_GetStackedChannel(dataPtr->self);

    Tcl_GetChannelType(downChan)->watchProc(
	    Tcl_GetChannelInstanceData(downChan), mask);

    /*
     * Management of the internal timer.

Changes to generic/tclIORChan.c.

446
447
448
449
450
451
452
453
454

455
456
457
458
459
460
461

static const char *msg_read_toomuch = "{read delivered more than requested}";
static const char *msg_write_toomuch = "{write wrote more than requested}";
static const char *msg_write_nothing = "{write wrote nothing}";
static const char *msg_seek_beforestart = "{Tried to seek before origin}";
#ifdef TCL_THREADS
static const char *msg_send_originlost = "{Channel thread lost}";
static const char *msg_send_dstlost    = "{Owner lost}";
#endif /* TCL_THREADS */

static const char *msg_dstlost    = "-code 1 -level 0 -errorcode NONE -errorinfo {} -errorline 1 {Owner lost}";

/*
 * Main methods to plug into the 'chan' ensemble'. ==================
 */

/*







<

>







446
447
448
449
450
451
452

453
454
455
456
457
458
459
460
461

static const char *msg_read_toomuch = "{read delivered more than requested}";
static const char *msg_write_toomuch = "{write wrote more than requested}";
static const char *msg_write_nothing = "{write wrote nothing}";
static const char *msg_seek_beforestart = "{Tried to seek before origin}";
#ifdef TCL_THREADS
static const char *msg_send_originlost = "{Channel thread lost}";

#endif /* TCL_THREADS */
static const char *msg_send_dstlost    = "{Owner lost}";
static const char *msg_dstlost    = "-code 1 -level 0 -errorcode NONE -errorinfo {} -errorline 1 {Owner lost}";

/*
 * Main methods to plug into the 'chan' ensemble'. ==================
 */

/*
1107
1108
1109
1110
1111
1112
1113

1114
1115
1116
1117
1118
1119
1120
{
    ReflectedChannel *rcPtr = clientData;
    int result;			/* Result code for 'close' */
    Tcl_Obj *resObj;		/* Result data for 'close' */
    ReflectedChannelMap *rcmPtr;/* Map of reflected channels with handlers in
				 * this interp */
    Tcl_HashEntry *hPtr;	/* Entry in the above map */


    if (TclInThreadExit()) {
	/*
	 * This call comes from TclFinalizeIOSystem. There are no
	 * interpreters, and therefore we cannot call upon the handler command
	 * anymore. Threading is irrelevant as well. We simply clean up all
	 * our C level data structures and leave the Tcl level to the other







>







1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
{
    ReflectedChannel *rcPtr = clientData;
    int result;			/* Result code for 'close' */
    Tcl_Obj *resObj;		/* Result data for 'close' */
    ReflectedChannelMap *rcmPtr;/* Map of reflected channels with handlers in
				 * this interp */
    Tcl_HashEntry *hPtr;	/* Entry in the above map */
    const Tcl_ChannelType *tctPtr;

    if (TclInThreadExit()) {
	/*
	 * This call comes from TclFinalizeIOSystem. There are no
	 * interpreters, and therefore we cannot call upon the handler command
	 * anymore. Threading is irrelevant as well. We simply clean up all
	 * our C level data structures and leave the Tcl level to the other
1145
1146
1147
1148
1149
1150
1151





1152
1153
1154
1155
1156
1157
1158
	    if (result != TCL_OK) {
		FreeReceivedError(&p);
	    }
	    return EOK;
	}
#endif






        Tcl_EventuallyFree(rcPtr, (Tcl_FreeProc *) FreeReflectedChannel);
	return EOK;
    }

    /*
     * Are we in the correct thread?
     */







>
>
>
>
>







1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
	    if (result != TCL_OK) {
		FreeReceivedError(&p);
	    }
	    return EOK;
	}
#endif

	tctPtr = ((Channel *)rcPtr->chan)->typePtr;
	if (tctPtr && tctPtr != &tclRChannelType) {
	    ckfree((char *)tctPtr);
	    ((Channel *)rcPtr->chan)->typePtr = NULL;
	}
        Tcl_EventuallyFree(rcPtr, (Tcl_FreeProc *) FreeReflectedChannel);
	return EOK;
    }

    /*
     * Are we in the correct thread?
     */
1209
1210
1211
1212
1213
1214
1215





1216
1217
1218
1219
1220
1221
1222
	hPtr = Tcl_FindHashEntry(&rcmPtr->map,
		Tcl_GetChannelName(rcPtr->chan));
	if (hPtr) {
	    Tcl_DeleteHashEntry(hPtr);
	}
#endif






        Tcl_EventuallyFree(rcPtr, (Tcl_FreeProc *) FreeReflectedChannel);
#ifdef TCL_THREADS
    }
#endif
    return (result == TCL_OK) ? EOK : EINVAL;
}








>
>
>
>
>







1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
	hPtr = Tcl_FindHashEntry(&rcmPtr->map,
		Tcl_GetChannelName(rcPtr->chan));
	if (hPtr) {
	    Tcl_DeleteHashEntry(hPtr);
	}
#endif

	tctPtr = ((Channel *)rcPtr->chan)->typePtr;
	if (tctPtr && tctPtr != &tclRChannelType) {
	    ckfree((char *)tctPtr);
	    ((Channel *)rcPtr->chan)->typePtr = NULL;
	}
        Tcl_EventuallyFree(rcPtr, (Tcl_FreeProc *) FreeReflectedChannel);
#ifdef TCL_THREADS
    }
#endif
    return (result == TCL_OK) ? EOK : EINVAL;
}

1382
1383
1384
1385
1386
1387
1388

1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404








1405
1406
1407
1408
1409
1410
1411
    }
#endif

    /* ASSERT: rcPtr->method & FLAG(METH_WRITE) */
    /* ASSERT: rcPtr->mode & TCL_WRITABLE */

    Tcl_Preserve(rcPtr);


    bufObj = Tcl_NewByteArrayObj((unsigned char *) buf, toWrite);
    Tcl_IncrRefCount(bufObj);

    if (InvokeTclMethod(rcPtr, METH_WRITE, bufObj, NULL, &resObj) != TCL_OK) {
	int code = ErrnoReturn(rcPtr, resObj);

	if (code < 0) {
	    *errorCodePtr = -code;
            goto error;
	}

	Tcl_SetChannelError(rcPtr->chan, resObj);
        goto invalid;
    }









    if (Tcl_GetIntFromObj(rcPtr->interp, resObj, &written) != TCL_OK) {
	Tcl_SetChannelError(rcPtr->chan, MarshallError(rcPtr->interp));
        goto invalid;
    }

    if ((written == 0) && (toWrite > 0)) {
	/*







>
















>
>
>
>
>
>
>
>







1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
    }
#endif

    /* ASSERT: rcPtr->method & FLAG(METH_WRITE) */
    /* ASSERT: rcPtr->mode & TCL_WRITABLE */

    Tcl_Preserve(rcPtr);
    Tcl_Preserve(rcPtr->interp);

    bufObj = Tcl_NewByteArrayObj((unsigned char *) buf, toWrite);
    Tcl_IncrRefCount(bufObj);

    if (InvokeTclMethod(rcPtr, METH_WRITE, bufObj, NULL, &resObj) != TCL_OK) {
	int code = ErrnoReturn(rcPtr, resObj);

	if (code < 0) {
	    *errorCodePtr = -code;
            goto error;
	}

	Tcl_SetChannelError(rcPtr->chan, resObj);
        goto invalid;
    }

    if (Tcl_InterpDeleted(rcPtr->interp)) {
	/*
	 * The interp was destroyed during InvokeTclMethod().
	 */

	SetChannelErrorStr(rcPtr->chan, msg_send_dstlost);
        goto invalid;
    }
    if (Tcl_GetIntFromObj(rcPtr->interp, resObj, &written) != TCL_OK) {
	Tcl_SetChannelError(rcPtr->chan, MarshallError(rcPtr->interp));
        goto invalid;
    }

    if ((written == 0) && (toWrite > 0)) {
	/*
1427
1428
1429
1430
1431
1432
1433

1434
1435
1436
1437
1438
1439
1440
        goto invalid;
    }

    *errorCodePtr = EOK;
 stop:
    Tcl_DecrRefCount(bufObj);
    Tcl_DecrRefCount(resObj);		/* Remove reference held from invoke */

    Tcl_Release(rcPtr);
    return written;
 invalid:
    *errorCodePtr = EINVAL;
 error:
    written = -1;
    goto stop;







>







1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
        goto invalid;
    }

    *errorCodePtr = EOK;
 stop:
    Tcl_DecrRefCount(bufObj);
    Tcl_DecrRefCount(resObj);		/* Remove reference held from invoke */
    Tcl_Release(rcPtr->interp);
    Tcl_Release(rcPtr);
    return written;
 invalid:
    *errorCodePtr = EINVAL;
 error:
    written = -1;
    goto stop;
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187

static void
FreeReflectedChannel(
    ReflectedChannel *rcPtr)
{
    Channel *chanPtr = (Channel *) rcPtr->chan;

    if (chanPtr->typePtr != &tclRChannelType) {
	/*
	 * Delete a cloned ChannelType structure.
	 */

	ckfree(chanPtr->typePtr);
	chanPtr->typePtr = NULL;
    }
    Tcl_Release(chanPtr);
    Tcl_DecrRefCount(rcPtr->name);
    Tcl_DecrRefCount(rcPtr->methods);
    Tcl_DecrRefCount(rcPtr->cmd);
    ckfree(rcPtr);
}








<
<
<
<
<
<
<
<







2187
2188
2189
2190
2191
2192
2193








2194
2195
2196
2197
2198
2199
2200

static void
FreeReflectedChannel(
    ReflectedChannel *rcPtr)
{
    Channel *chanPtr = (Channel *) rcPtr->chan;









    Tcl_Release(chanPtr);
    Tcl_DecrRefCount(rcPtr->name);
    Tcl_DecrRefCount(rcPtr->methods);
    Tcl_DecrRefCount(rcPtr->cmd);
    ckfree(rcPtr);
}

2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879


2880
2881
2882
2883
2884
2885
2886
    switch (evPtr->op) {
	/*
	 * The destination thread for the following operations is
	 * rcPtr->thread, which contains rcPtr->interp, the interp we have to
	 * call upon for the driver.
	 */

    case ForwardedClose:
	/*
	 * No parameters/results.
	 */



	if (InvokeTclMethod(rcPtr, METH_FINAL, NULL, NULL, &resObj)!=TCL_OK) {
	    ForwardSetObjError(paramPtr, resObj);
	}

	/*
	 * Freeing is done here, in the origin thread, callback command







|



>
>







2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
    switch (evPtr->op) {
	/*
	 * The destination thread for the following operations is
	 * rcPtr->thread, which contains rcPtr->interp, the interp we have to
	 * call upon for the driver.
	 */

    case ForwardedClose: {
	/*
	 * No parameters/results.
	 */

	const Tcl_ChannelType *tctPtr;

	if (InvokeTclMethod(rcPtr, METH_FINAL, NULL, NULL, &resObj)!=TCL_OK) {
	    ForwardSetObjError(paramPtr, resObj);
	}

	/*
	 * Freeing is done here, in the origin thread, callback command
2898
2899
2900
2901
2902
2903
2904





2905
2906

2907
2908
2909
2910
2911
2912
2913
	Tcl_DeleteHashEntry(hPtr);

	rcmPtr = GetThreadReflectedChannelMap();
	hPtr = Tcl_FindHashEntry(&rcmPtr->map,
                Tcl_GetChannelName(rcPtr->chan));
	Tcl_DeleteHashEntry(hPtr);






	Tcl_EventuallyFree(rcPtr, (Tcl_FreeProc *) FreeReflectedChannel);
	break;


    case ForwardedInput: {
	Tcl_Obj *toReadObj = Tcl_NewIntObj(paramPtr->input.toRead);
        Tcl_IncrRefCount(toReadObj);

        Tcl_Preserve(rcPtr);
	if (InvokeTclMethod(rcPtr, METH_READ, toReadObj, NULL, &resObj)!=TCL_OK){







>
>
>
>
>
|

>







2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
	Tcl_DeleteHashEntry(hPtr);

	rcmPtr = GetThreadReflectedChannelMap();
	hPtr = Tcl_FindHashEntry(&rcmPtr->map,
                Tcl_GetChannelName(rcPtr->chan));
	Tcl_DeleteHashEntry(hPtr);

	tctPtr = ((Channel *)rcPtr->chan)->typePtr;
	if (tctPtr && tctPtr != &tclRChannelType) {
	    ckfree((char *)tctPtr);
	    ((Channel *)rcPtr->chan)->typePtr = NULL;
	}
        Tcl_EventuallyFree(rcPtr, (Tcl_FreeProc *) FreeReflectedChannel);
	break;
    }

    case ForwardedInput: {
	Tcl_Obj *toReadObj = Tcl_NewIntObj(paramPtr->input.toRead);
        Tcl_IncrRefCount(toReadObj);

        Tcl_Preserve(rcPtr);
	if (InvokeTclMethod(rcPtr, METH_READ, toReadObj, NULL, &resObj)!=TCL_OK){

Changes to generic/tclInt.h.

2873
2874
2875
2876
2877
2878
2879


2880
2881
2882
2883
2884
2885
2886
			    int *typePtr);
MODULE_SCOPE int	TclGetOpenModeEx(Tcl_Interp *interp,
			    const char *modeString, int *seekFlagPtr,
			    int *binaryPtr);
MODULE_SCOPE Tcl_Obj *	TclGetProcessGlobalValue(ProcessGlobalValue *pgvPtr);
MODULE_SCOPE Tcl_Obj *	TclGetSourceFromFrame(CmdFrame *cfPtr, int objc,
			    Tcl_Obj *const objv[]);


MODULE_SCOPE int	TclIncrObj(Tcl_Interp *interp, Tcl_Obj *valuePtr,
			    Tcl_Obj *incrPtr);
MODULE_SCOPE Tcl_Obj *	TclIncrObjVar2(Tcl_Interp *interp, Tcl_Obj *part1Ptr,
			    Tcl_Obj *part2Ptr, Tcl_Obj *incrPtr, int flags);
MODULE_SCOPE int	TclInfoExistsCmd(ClientData dummy, Tcl_Interp *interp,
			    int objc, Tcl_Obj *const objv[]);
MODULE_SCOPE int	TclInfoCoroutineCmd(ClientData dummy, Tcl_Interp *interp,







>
>







2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
			    int *typePtr);
MODULE_SCOPE int	TclGetOpenModeEx(Tcl_Interp *interp,
			    const char *modeString, int *seekFlagPtr,
			    int *binaryPtr);
MODULE_SCOPE Tcl_Obj *	TclGetProcessGlobalValue(ProcessGlobalValue *pgvPtr);
MODULE_SCOPE Tcl_Obj *	TclGetSourceFromFrame(CmdFrame *cfPtr, int objc,
			    Tcl_Obj *const objv[]);
MODULE_SCOPE char *	TclGetStringStorage(Tcl_Obj *objPtr,
			    unsigned int *sizePtr);
MODULE_SCOPE int	TclIncrObj(Tcl_Interp *interp, Tcl_Obj *valuePtr,
			    Tcl_Obj *incrPtr);
MODULE_SCOPE Tcl_Obj *	TclIncrObjVar2(Tcl_Interp *interp, Tcl_Obj *part1Ptr,
			    Tcl_Obj *part2Ptr, Tcl_Obj *incrPtr, int flags);
MODULE_SCOPE int	TclInfoExistsCmd(ClientData dummy, Tcl_Interp *interp,
			    int objc, Tcl_Obj *const objv[]);
MODULE_SCOPE int	TclInfoCoroutineCmd(ClientData dummy, Tcl_Interp *interp,

Changes to generic/tclStringObj.c.

1119
1120
1121
1122
1123
1124
1125

1126
1127
1128
1129
1130
1131
1132
1133

    if (length <= limit) {
	toCopy = length;
    } else {
	if (ellipsis == NULL) {
	    ellipsis = "...";
	}

	toCopy = Tcl_UtfPrev(bytes+limit+1-strlen(ellipsis), bytes) - bytes;
    }

    /*
     * If objPtr has a valid Unicode rep, then append the Unicode conversion
     * of "bytes" to the objPtr's Unicode rep, otherwise append "bytes" to
     * objPtr's string rep.
     */







>
|







1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134

    if (length <= limit) {
	toCopy = length;
    } else {
	if (ellipsis == NULL) {
	    ellipsis = "...";
	}
	toCopy = (bytes == NULL) ? limit
		: Tcl_UtfPrev(bytes+limit+1-strlen(ellipsis), bytes) - bytes;
    }

    /*
     * If objPtr has a valid Unicode rep, then append the Unicode conversion
     * of "bytes" to the objPtr's Unicode rep, otherwise append "bytes" to
     * objPtr's string rep.
     */
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445

	/*
	 * Protect against case where unicode points into the existing
	 * stringPtr->unicode array. Force it to follow any relocations due to
	 * the reallocs below.
	 */

	if (unicode >= stringPtr->unicode
		&& unicode <= stringPtr->unicode + stringPtr->maxChars) {
	    offset = unicode - stringPtr->unicode;
	}

	GrowUnicodeBuffer(objPtr, numChars);
	stringPtr = GET_STRING(objPtr);








|







1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446

	/*
	 * Protect against case where unicode points into the existing
	 * stringPtr->unicode array. Force it to follow any relocations due to
	 * the reallocs below.
	 */

	if (unicode && unicode >= stringPtr->unicode
		&& unicode <= stringPtr->unicode + stringPtr->maxChars) {
	    offset = unicode - stringPtr->unicode;
	}

	GrowUnicodeBuffer(objPtr, numChars);
	stringPtr = GET_STRING(objPtr);

1453
1454
1455
1456
1457
1458
1459

1460
1461

1462
1463
1464
1465
1466
1467
1468
    }

    /*
     * Copy the new string onto the end of the old string, then add the
     * trailing null.
     */


    memmove(stringPtr->unicode + stringPtr->numChars, unicode,
	    appendNumChars * sizeof(Tcl_UniChar));

    stringPtr->unicode[numChars] = 0;
    stringPtr->numChars = numChars;
    stringPtr->allocated = 0;

    TclInvalidateStringRep(objPtr);
}








>
|
|
>







1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
    }

    /*
     * Copy the new string onto the end of the old string, then add the
     * trailing null.
     */

    if (unicode) {
	memmove(stringPtr->unicode + stringPtr->numChars, unicode,
		appendNumChars * sizeof(Tcl_UniChar));
    }
    stringPtr->unicode[numChars] = 0;
    stringPtr->numChars = numChars;
    stringPtr->allocated = 0;

    TclInvalidateStringRep(objPtr);
}

1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607

	/*
	 * Protect against case where unicode points into the existing
	 * stringPtr->unicode array. Force it to follow any relocations due to
	 * the reallocs below.
	 */

	if (bytes >= objPtr->bytes
		&& bytes <= objPtr->bytes + objPtr->length) {
	    offset = bytes - objPtr->bytes;
	}

	/*
	 * TODO: consider passing flag=1: no overalloc on first append. This
	 * would make test stringObj-8.1 fail.







|







1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610

	/*
	 * Protect against case where unicode points into the existing
	 * stringPtr->unicode array. Force it to follow any relocations due to
	 * the reallocs below.
	 */

	if (bytes && bytes >= objPtr->bytes
		&& bytes <= objPtr->bytes + objPtr->length) {
	    offset = bytes - objPtr->bytes;
	}

	/*
	 * TODO: consider passing flag=1: no overalloc on first append. This
	 * would make test stringObj-8.1 fail.
1621
1622
1623
1624
1625
1626
1627

1628

1629
1630
1631
1632
1633
1634
1635
    /*
     * Invalidate the unicode data.
     */

    stringPtr->numChars = -1;
    stringPtr->hasUnicode = 0;


    memmove(objPtr->bytes + oldLength, bytes, numBytes);

    objPtr->bytes[newLength] = 0;
    objPtr->length = newLength;
}

/*
 *----------------------------------------------------------------------
 *







>
|
>







1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
    /*
     * Invalidate the unicode data.
     */

    stringPtr->numChars = -1;
    stringPtr->hasUnicode = 0;

    if (bytes) {
	memmove(objPtr->bytes + oldLength, bytes, numBytes);
    }
    objPtr->bytes[newLength] = 0;
    objPtr->length = newLength;
}

/*
 *----------------------------------------------------------------------
 *
2654
2655
2656
2657
2658
2659
2660
































2661
2662
2663
2664
2665
2666
2667
    va_end(argList);
    return objPtr;
}

/*
 *---------------------------------------------------------------------------
 *
































 * TclStringObjReverse --
 *
 *	Implements the [string reverse] operation.
 *
 * Results:
 *	An unshared Tcl value which is the [string reverse] of the argument
 *	supplied. When sharing rules permit, the returned value might be the







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
    va_end(argList);
    return objPtr;
}

/*
 *---------------------------------------------------------------------------
 *
 * TclGetStringStorage --
 *
 *	Returns the string storage space of a Tcl_Obj.
 *
 * Results:
 *	The pointer value objPtr->bytes is returned and the number of bytes
 *	allocated there is written to *sizePtr (if known).
 *
 * Side effects:
 *	May set objPtr->bytes.
 *
 *---------------------------------------------------------------------------
 */

char *
TclGetStringStorage(
    Tcl_Obj *objPtr,
    unsigned int *sizePtr)
{
    String *stringPtr;

    if (objPtr->typePtr != &tclStringType || objPtr->bytes == NULL) {
	return TclGetStringFromObj(objPtr, (int *)sizePtr);
    }

    stringPtr = GET_STRING(objPtr);
    *sizePtr = stringPtr->allocated;
    return objPtr->bytes;
}
/*
 *---------------------------------------------------------------------------
 *
 * TclStringObjReverse --
 *
 *	Implements the [string reverse] operation.
 *
 * Results:
 *	An unshared Tcl value which is the [string reverse] of the argument
 *	supplied. When sharing rules permit, the returned value might be the
2844
2845
2846
2847
2848
2849
2850

2851



2852
2853
2854
2855
2856
2857
2858
	
    if (needed > stringPtr->maxChars) {
	GrowUnicodeBuffer(objPtr, needed);
	stringPtr = GET_STRING(objPtr);
    }

    stringPtr->hasUnicode = 1;

    stringPtr->numChars = needed;



    for (dst=stringPtr->unicode + numOrigChars; numAppendChars-- > 0; dst++) {
	bytes += TclUtfToUniChar(bytes, dst);
    }
    *dst = 0;
}

/*







>
|
>
>
>







2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
	
    if (needed > stringPtr->maxChars) {
	GrowUnicodeBuffer(objPtr, needed);
	stringPtr = GET_STRING(objPtr);
    }

    stringPtr->hasUnicode = 1;
    if (bytes) {
	stringPtr->numChars = needed;
    } else {
	numAppendChars = 0;
    }
    for (dst=stringPtr->unicode + numOrigChars; numAppendChars-- > 0; dst++) {
	bytes += TclUtfToUniChar(bytes, dst);
    }
    *dst = 0;
}

/*

Changes to tests/io.test.

4749
4750
4751
4752
4753
4754
4755
4756
4757
4758
4759
4760
4761
4762
4763
    set f [open $path(test1) r]
    fconfigure $f -translation crlf -eofchar \x1a
    set l [string length [set in [read $f]]]
    set e [eof $f]
    close $f
    list $s $l $e [scan [string index $in end] %c]
} -result {9 8 1 13}
test io-35.18b {Tcl_Eof, eof char, cr write, crlf read} -constraints knownBug -body {
    file delete $path(test1)
    set f [open $path(test1) w]
    fconfigure $f -translation cr -eofchar \x1a
    puts $f {}
    close $f
    set s [file size $path(test1)]
    set f [open $path(test1) r]







|







4749
4750
4751
4752
4753
4754
4755
4756
4757
4758
4759
4760
4761
4762
4763
    set f [open $path(test1) r]
    fconfigure $f -translation crlf -eofchar \x1a
    set l [string length [set in [read $f]]]
    set e [eof $f]
    close $f
    list $s $l $e [scan [string index $in end] %c]
} -result {9 8 1 13}
test io-35.18b {Tcl_Eof, eof char, cr write, crlf read} -body {
    file delete $path(test1)
    set f [open $path(test1) w]
    fconfigure $f -translation cr -eofchar \x1a
    puts $f {}
    close $f
    set s [file size $path(test1)]
    set f [open $path(test1) r]
4792
4793
4794
4795
4796
4797
4798















4799
4800
4801
4802
4803
4804
4805
    set f [open $path(test1) r]
    fconfigure $f -translation crlf -eofchar \x1a
    set l [string length [set in [read $f]]]
    set e [eof $f]
    close $f
    list $c $l $e [scan [string index $in end] %c]
} -result {17 8 1 13}
















# Test Tcl_InputBlocked

test io-36.1 {Tcl_InputBlocked on nonblocking pipe} {stdio openpipe} {
    set f1 [open "|[list [interpreter]]" r+]
    puts $f1 {puts hello_from_pipe}
    flush $f1







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







4792
4793
4794
4795
4796
4797
4798
4799
4800
4801
4802
4803
4804
4805
4806
4807
4808
4809
4810
4811
4812
4813
4814
4815
4816
4817
4818
4819
4820
    set f [open $path(test1) r]
    fconfigure $f -translation crlf -eofchar \x1a
    set l [string length [set in [read $f]]]
    set e [eof $f]
    close $f
    list $c $l $e [scan [string index $in end] %c]
} -result {17 8 1 13}
test io-35.20 {Tcl_Eof, eof char in middle, cr write, crlf read} {
    file delete $path(test1)
    set f [open $path(test1) w]
    fconfigure $f -translation cr -eofchar {}
    set i [format \n%cqrsuvw 26]
    puts $f $i
    close $f
    set c [file size $path(test1)]
    set f [open $path(test1) r]
    fconfigure $f -translation crlf -eofchar \x1a
    set l [string length [set in [read $f]]]
    set e [eof $f]
    close $f
    list $c $l $e [scan [string index $in end] %c]
} {9 1 1 13}

# Test Tcl_InputBlocked

test io-36.1 {Tcl_InputBlocked on nonblocking pipe} {stdio openpipe} {
    set f1 [open "|[list [interpreter]]" r+]
    puts $f1 {puts hello_from_pipe}
    flush $f1
6820
6821
6822
6823
6824
6825
6826













































































































































6827
6828
6829
6830
6831
6832
6833

    fcopy $in $out
    close $in
    close $out

    file size $path(kyrillic.txt)
} 3














































































































































test io-53.1 {CopyData} {fcopy} {
    file delete $path(test1)
    set f1 [open $thisScript]
    set f2 [open $path(test1) w]
    fconfigure $f1 -translation lf -blocking 0
    fconfigure $f2 -translation cr -blocking 0







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







6835
6836
6837
6838
6839
6840
6841
6842
6843
6844
6845
6846
6847
6848
6849
6850
6851
6852
6853
6854
6855
6856
6857
6858
6859
6860
6861
6862
6863
6864
6865
6866
6867
6868
6869
6870
6871
6872
6873
6874
6875
6876
6877
6878
6879
6880
6881
6882
6883
6884
6885
6886
6887
6888
6889
6890
6891
6892
6893
6894
6895
6896
6897
6898
6899
6900
6901
6902
6903
6904
6905
6906
6907
6908
6909
6910
6911
6912
6913
6914
6915
6916
6917
6918
6919
6920
6921
6922
6923
6924
6925
6926
6927
6928
6929
6930
6931
6932
6933
6934
6935
6936
6937
6938
6939
6940
6941
6942
6943
6944
6945
6946
6947
6948
6949
6950
6951
6952
6953
6954
6955
6956
6957
6958
6959
6960
6961
6962
6963
6964
6965
6966
6967
6968
6969
6970
6971
6972
6973
6974
6975
6976
6977
6978
6979
6980
6981
6982
6983
6984
6985
6986
6987
6988
6989

    fcopy $in $out
    close $in
    close $out

    file size $path(kyrillic.txt)
} 3

test io-52.12 {coverage of -translation auto} {
    file delete $path(test1) $path(test2)
    set out [open $path(test1) wb]
    chan configure $out -translation lf
    puts -nonewline $out abcdefg\rhijklmn\nopqrstu\r\nvwxyz
    close $out
    set in [open $path(test1)]
    chan configure $in -buffersize 8
    set out [open $path(test2) w]
    fcopy $in $out
    close $in
    close $out
    file size $path(test2)
} 29
test io-52.13 {coverage of -translation cr} {
    file delete $path(test1) $path(test2)
    set out [open $path(test1) wb]
    chan configure $out -translation lf
    puts -nonewline $out abcdefg\rhijklmn\nopqrstu\r\nvwxyz
    close $out
    set in [open $path(test1)]
    chan configure $in -buffersize 8 -translation cr
    set out [open $path(test2) w]
    fcopy $in $out
    close $in
    close $out
    file size $path(test2)
} 30
test io-52.14 {coverage of -translation crlf} {
    file delete $path(test1) $path(test2)
    set out [open $path(test1) wb]
    chan configure $out -translation lf
    puts -nonewline $out abcdefg\rhijklmn\nopqrstu\r\nvwxyz
    close $out
    set in [open $path(test1)]
    chan configure $in -buffersize 8 -translation crlf
    set out [open $path(test2) w]
    fcopy $in $out
    close $in
    close $out
    file size $path(test2)
} 29
test io-52.14.1 {coverage of -translation crlf} {
    file delete $path(test1) $path(test2)
    set out [open $path(test1) wb]
    chan configure $out -translation lf
    puts -nonewline $out abcdefg\rhijklmn\nopqrstu\r\nvwxyz
    close $out
    set in [open $path(test1)]
    chan configure $in -buffersize 8 -translation crlf
    set out [open $path(test2) w]
    fcopy $in $out -size 2
    close $in
    close $out
    file size $path(test2)
} 2
test io-52.14.2 {coverage of -translation crlf} {
    file delete $path(test1) $path(test2)
    set out [open $path(test1) wb]
    chan configure $out -translation lf
    puts -nonewline $out abcdefg\rhijklmn\nopqrstu\r\nvwxyz
    close $out
    set in [open $path(test1)]
    chan configure $in -translation crlf
    set out [open $path(test2) w]
    fcopy $in $out -size 9
    close $in
    close $out
    file size $path(test2)
} 9
test io-52.15 {coverage of -translation crlf} {
    file delete $path(test1) $path(test2)
    set out [open $path(test1) wb]
    chan configure $out -translation lf
    puts -nonewline $out abcdefg\r
    close $out
    set in [open $path(test1)]
    chan configure $in -buffersize 8 -translation crlf
    set out [open $path(test2) w]
    fcopy $in $out
    close $in
    close $out
    file size $path(test2)
} 8
test io-52.16 {coverage of eofChar handling} {
    file delete $path(test1) $path(test2)
    set out [open $path(test1) wb]
    chan configure $out -translation lf
    puts -nonewline $out abcdefg\rhijklmn\nopqrstu\r\nvwxyz
    close $out
    set in [open $path(test1)]
    chan configure $in -buffersize 8 -translation lf -eofchar a
    set out [open $path(test2) w]
    fcopy $in $out
    close $in
    close $out
    file size $path(test2)
} 0
test io-52.17 {coverage of eofChar handling} {
    file delete $path(test1) $path(test2)
    set out [open $path(test1) wb]
    chan configure $out -translation lf
    puts -nonewline $out abcdefg\rhijklmn\nopqrstu\r\nvwxyz
    close $out
    set in [open $path(test1)]
    chan configure $in -buffersize 8 -translation lf -eofchar d
    set out [open $path(test2) w]
    fcopy $in $out
    close $in
    close $out
    file size $path(test2)
} 3
test io-52.18 {coverage of eofChar handling} {
    file delete $path(test1) $path(test2)
    set out [open $path(test1) wb]
    chan configure $out -translation lf
    puts -nonewline $out abcdefg\rhijklmn\nopqrstu\r\nvwxyz
    close $out
    set in [open $path(test1)]
    chan configure $in -buffersize 8 -translation crlf -eofchar h
    set out [open $path(test2) w]
    fcopy $in $out
    close $in
    close $out
    file size $path(test2)
} 8
test io-52.19 {coverage of eofChar handling} {
    file delete $path(test1) $path(test2)
    set out [open $path(test1) wb]
    chan configure $out -translation lf
    puts -nonewline $out abcdefg\rhijklmn\nopqrstu\r\nvwxyz
    close $out
    set in [open $path(test1)]
    chan configure $in -buffersize 10 -translation crlf -eofchar h
    set out [open $path(test2) w]
    fcopy $in $out
    close $in
    close $out
    file size $path(test2)
} 8

test io-53.1 {CopyData} {fcopy} {
    file delete $path(test1)
    set f1 [open $thisScript]
    set f2 [open $path(test1) w]
    fconfigure $f1 -translation lf -blocking 0
    fconfigure $f2 -translation cr -blocking 0
7290
7291
7292
7293
7294
7295
7296



















7297
7298
7299
7300
7301
7302
7303
    set done
} -cleanup {
    close $outChan
    close $inChan
    removeFile out
    removeFile in
} -result {40 bytes copied}




















test io-54.1 {Recursive channel events} {socket fileevent} {
    # This test checks to see if file events are delivered during recursive
    # event loops when there is buffered data on the channel.

    proc accept {s a p} {
	variable as







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







7446
7447
7448
7449
7450
7451
7452
7453
7454
7455
7456
7457
7458
7459
7460
7461
7462
7463
7464
7465
7466
7467
7468
7469
7470
7471
7472
7473
7474
7475
7476
7477
7478
    set done
} -cleanup {
    close $outChan
    close $inChan
    removeFile out
    removeFile in
} -result {40 bytes copied}
test io-53.12 {CopyData: foreground short reads, aka bug 3096275} {stdio unix openpipe fcopy} {
    file delete $path(pipe)
    set f1 [open $path(pipe) w]
    puts -nonewline $f1 {
	fconfigure stdin -translation binary -blocking 0
	fconfigure stdout -buffering none -translation binary
	fcopy stdin stdout
    }
    close $f1
    set f1 [open "|[list [interpreter] $path(pipe)]" r+]
    fconfigure $f1 -translation binary -buffering none
    puts -nonewline $f1 A
    after 2000 {set ::done timeout}
    fileevent $f1 readable {set ::done ok}
    vwait ::done
    set ch [read $f1 1]
    close $f1
    list $::done $ch
} {ok A}

test io-54.1 {Recursive channel events} {socket fileevent} {
    # This test checks to see if file events are delivered during recursive
    # event loops when there is buffered data on the channel.

    proc accept {s a p} {
	variable as

Changes to tests/ioCmd.test.

2072
2073
2074
2075
2076
2077
2078
2079
2080
2081

2082
2083
2084
2085

2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111

    # Set up channel in thread
    set chan [interp eval $ida $helperscript]
    set chan [interp eval $ida {
	proc foo {args} {
	    oninit; onfinal; track;
	    # destroy interpreter during channel access
	    # Actually not possible for an interp to destroy itself.
	    interp delete {}
	    return}

	set chan [chan create {r w} foo]
	fconfigure $chan -buffering none
	set chan
    }]


    # Move channel to 2nd thread.
    interp eval $ida [list testchannel cut    $chan]
    interp eval $idb [list testchannel splice $chan]

    # Run access from interpreter B, this will give us a synchronous
    # response.

    interp eval $idb [list set chan $chan]
    set res [interp eval $idb {
	# wait a bit, give the main thread the time to start its event
	# loop to wait for the response from B
	after 2000
	catch { puts $chan shoo } res
	set res
    }]
    set res
} -constraints {testchannel impossible} \
    -result {Owner lost}

test iocmd-32.2 {delete interp of reflected chan} {
    # Bug 3034840
    # Run this test in an interp with memory debugging to panic
    # on the double free
    interp create slave
    slave eval {







<
<
|
>




>

















|
<







2072
2073
2074
2075
2076
2077
2078


2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103

2104
2105
2106
2107
2108
2109
2110

    # Set up channel in thread
    set chan [interp eval $ida $helperscript]
    set chan [interp eval $ida {
	proc foo {args} {
	    oninit; onfinal; track;
	    # destroy interpreter during channel access


	    suicide
	}
	set chan [chan create {r w} foo]
	fconfigure $chan -buffering none
	set chan
    }]
    interp alias $ida suicide {} interp delete $ida

    # Move channel to 2nd thread.
    interp eval $ida [list testchannel cut    $chan]
    interp eval $idb [list testchannel splice $chan]

    # Run access from interpreter B, this will give us a synchronous
    # response.

    interp eval $idb [list set chan $chan]
    set res [interp eval $idb {
	# wait a bit, give the main thread the time to start its event
	# loop to wait for the response from B
	after 2000
	catch { puts $chan shoo } res
	set res
    }]
    set res
} -constraints {testchannel} -result {Owner lost}


test iocmd-32.2 {delete interp of reflected chan} {
    # Bug 3034840
    # Run this test in an interp with memory debugging to panic
    # on the double free
    interp create slave
    slave eval {
2820
2821
2822
2823
2824
2825
2826

2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837

    LOG MAIN_WAITING
    vwait forever
    LOG MAIN_DONE

    set res
} -cleanup {

    rename LOG {}
    rename POST {}
    rename HANDLER {}
    unset beat drive data forever res tid ch
} -match glob \
    -result {{initialize rc* read} {watch rc* read} {read rc* 4096} {watch rc* {}} {watch rc* read} {read rc* 4096} {watch rc* {}} {finalize rc*}}

# --- === *** ###########################
# method cgetall

test iocmd.tf-25.1 {chan configure, cgetall, standard options} -match glob -body {







>



|







2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837

    LOG MAIN_WAITING
    vwait forever
    LOG MAIN_DONE

    set res
} -cleanup {
    after cancel $::timer
    rename LOG {}
    rename POST {}
    rename HANDLER {}
    unset beat drive data forever res tid ch timer
} -match glob \
    -result {{initialize rc* read} {watch rc* read} {read rc* 4096} {watch rc* {}} {watch rc* read} {read rc* 4096} {watch rc* {}} {finalize rc*}}

# --- === *** ###########################
# method cgetall

test iocmd.tf-25.1 {chan configure, cgetall, standard options} -match glob -body {

Changes to tests/ioTrans.test.

535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561




















562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
    set c [chan push [set c [tempchan]] [list foo $c]]
    lappend res [read $c]
    #lappend res [gets $c]
} -cleanup {
    tempdone
    rename foo {}
} -result {{read rt* {test data
}} file*}
test iortrans-4.8.1 {chan read, bug 721ec69271} -setup {
    set res {}
} -match glob -body {
    proc foo {fd args} {
	handle.initialize
	handle.finalize
	lappend ::res $args
	# Kill and recreate transform while it is operating
	chan pop $fd
	chan push $fd [list foo $fd]
    }
    set c [chan push [set c [tempchan]] [list foo $c]]
    chan configure $c -buffersize 2
    lappend res [read $c]
} -cleanup {
    tempdone
    rename foo {}
} -result {{read rt* {test data
}} file*}




















test iortrans-4.9 {chan read, gets, bug 2921116} -setup {
    set res {}
} -match glob -body {
    proc foo {fd args} {
	handle.initialize
	handle.finalize
	lappend ::res $args
	# Kill and recreate transform while it is operating
	chan pop $fd
	chan push $fd [list foo $fd]
    }
    set c [chan push [set c [tempchan]] [list foo $c]]
    lappend res [gets $c]
} -cleanup {
    tempdone
    rename foo {}
} -result {{read rt* {test data
}} file*}

# --- === *** ###########################
# method write (via puts)

test iortrans-5.1 {chan write, regular write} -setup {
    set res {}
} -match glob -body {







|

















|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

















|







535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
    set c [chan push [set c [tempchan]] [list foo $c]]
    lappend res [read $c]
    #lappend res [gets $c]
} -cleanup {
    tempdone
    rename foo {}
} -result {{read rt* {test data
}} {}}
test iortrans-4.8.1 {chan read, bug 721ec69271} -setup {
    set res {}
} -match glob -body {
    proc foo {fd args} {
	handle.initialize
	handle.finalize
	lappend ::res $args
	# Kill and recreate transform while it is operating
	chan pop $fd
	chan push $fd [list foo $fd]
    }
    set c [chan push [set c [tempchan]] [list foo $c]]
    chan configure $c -buffersize 2
    lappend res [read $c]
} -cleanup {
    tempdone
    rename foo {}
} -result {{read rt* te} {read rt* st} {read rt* { d}} {read rt* at} {read rt* {a
}} {}}
test iortrans-4.8.2 {chan read, bug 721ec69271} -setup {
    set res {}
} -match glob -body {
    proc foo {fd args} {
	handle.initialize
	handle.finalize
	lappend ::res $args
	# Kill and recreate transform while it is operating
	chan pop $fd
	chan push $fd [list foo $fd]
	return x
    }
    set c [chan push [set c [tempchan]] [list foo $c]]
    chan configure $c -buffersize 1
    lappend res [read $c]
} -cleanup {
    tempdone
    rename foo {}
} -result {{read rt* t} {read rt* e} {read rt* s} {read rt* t} {read rt* { }} {read rt* d} {read rt* a} {read rt* t} {read rt* a} {read rt* {
}} {}}
test iortrans-4.9 {chan read, gets, bug 2921116} -setup {
    set res {}
} -match glob -body {
    proc foo {fd args} {
	handle.initialize
	handle.finalize
	lappend ::res $args
	# Kill and recreate transform while it is operating
	chan pop $fd
	chan push $fd [list foo $fd]
    }
    set c [chan push [set c [tempchan]] [list foo $c]]
    lappend res [gets $c]
} -cleanup {
    tempdone
    rename foo {}
} -result {{read rt* {test data
}} {}}

# --- === *** ###########################
# method write (via puts)

test iortrans-5.1 {chan write, regular write} -setup {
    set res {}
} -match glob -body {

Changes to tests/iogt.test.

224
225
226
227
228
229
230
231
232










233
234
235
236
237
238
239
    switch -- $op {
	create/write -
	create/read  -
	delete/write -
	delete/read  -
	clear_read   {;#ignore}
	flush/write -
	flush/read  -
	write       -










	read        {
	    testchannel unstack $chan
	    testchannel transform $chan \
		-command [namespace code [list id_torture $chan]]
	    return $data
	}
	query/maxRead {return -1}







|
|
>
>
>
>
>
>
>
>
>
>







224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
    switch -- $op {
	create/write -
	create/read  -
	delete/write -
	delete/read  -
	clear_read   {;#ignore}
	flush/write -
	flush/read  {}
	write       {
	    global level
	    if {$level} {
		return
	    }
	    incr level
	    testchannel unstack $chan
	    testchannel transform $chan \
		-command [namespace code [list id_torture $chan]]
	    return $data
	}
	read        {
	    testchannel unstack $chan
	    testchannel transform $chan \
		-command [namespace code [list id_torture $chan]]
	    return $data
	}
	query/maxRead {return -1}
575
576
577
578
579
580
581









582
583
584
585
586
587
588
    set fh [open $path(dummy) r]
    torture -attach $fh
    chan configure $fh -buffersize 2
    set x [read $fh]
    testchannel unstack $fh
    close   $fh
    set x









} {}

test iogt-3.0 {Tcl_Channel valid after stack/unstack, fevent handling} -setup {
    proc DoneCopy {n {err {}}} {
	variable copy 1
    }
} -constraints {testchannel hangs} -body {







>
>
>
>
>
>
>
>
>







585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
    set fh [open $path(dummy) r]
    torture -attach $fh
    chan configure $fh -buffersize 2
    set x [read $fh]
    testchannel unstack $fh
    close   $fh
    set x
} {}
test iogt-2.5 {basic I/O, mixed trail} {testchannel} {
    set ::level 0
    set fh [open $path(dummyout) w]
    torture -attach $fh
    puts -nonewline $fh abcdef
    flush $fh
    testchannel unstack $fh
    close   $fh
} {}

test iogt-3.0 {Tcl_Channel valid after stack/unstack, fevent handling} -setup {
    proc DoneCopy {n {err {}}} {
	variable copy 1
    }
} -constraints {testchannel hangs} -body {

Changes to tests/winFCmd.test.

1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
test winFCmd-19.5 {Windows extended path names} -constraints nt -setup {
    set tmpfile [file join $::env(TEMP) tcl[string repeat x 248].tmp]
    set tmpfile [file normalize $tmpfile]
} -body {
    list [catch {
        set f [open $tmpfile [list WRONLY CREAT]]
        close $f
    } res] errormsg ;#$res
} -cleanup {
    catch {file delete $tmpfile}
} -result [list 1 errormsg]
test winFCmd-19.6 {Windows extended path names} -constraints nt -setup {
    set tmpfile [file join $::env(TEMP) tcl[string repeat x 248].tmp]
    set tmpfile //?/[file normalize $tmpfile]
} -body {
    list [catch {
        set f [open $tmpfile [list WRONLY CREAT]]
        close $f







|


|







1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
test winFCmd-19.5 {Windows extended path names} -constraints nt -setup {
    set tmpfile [file join $::env(TEMP) tcl[string repeat x 248].tmp]
    set tmpfile [file normalize $tmpfile]
} -body {
    list [catch {
        set f [open $tmpfile [list WRONLY CREAT]]
        close $f
    } res] $res
} -cleanup {
    catch {file delete $tmpfile}
} -result [list 0 {}]
test winFCmd-19.6 {Windows extended path names} -constraints nt -setup {
    set tmpfile [file join $::env(TEMP) tcl[string repeat x 248].tmp]
    set tmpfile //?/[file normalize $tmpfile]
} -body {
    list [catch {
        set f [open $tmpfile [list WRONLY CREAT]]
        close $f

Changes to unix/configure.

7075
7076
7077
7078
7079
7080
7081
7082
7083
7084
7085
7086
7087
7088
7089
7090
7091
	    if test "x${TCL_THREADS}" = "x0"; then
		{ { echo "$as_me:$LINENO: error: CYGWIN compile is only supported with --enable-threads" >&5
echo "$as_me: error: CYGWIN compile is only supported with --enable-threads" >&2;}
   { (exit 1); exit 1; }; }
	    fi
	    do64bit_ok=yes
	    if test "x${SHARED_BUILD}" = "x1"; then
		echo "running cd ../win; ${CONFIG_SHELL-/bin/sh} ./configure $ac_configure_args"
		# The eval makes quoting arguments work.
		if cd ../win; eval ${CONFIG_SHELL-/bin/sh} ./configure $ac_configure_args; cd ../unix
		then :
		else
		    { echo "configure: error: configure failed for ../win" 1>&2; exit 1; }
		fi
	    fi
	    ;;
	dgux*)







|

|







7075
7076
7077
7078
7079
7080
7081
7082
7083
7084
7085
7086
7087
7088
7089
7090
7091
	    if test "x${TCL_THREADS}" = "x0"; then
		{ { echo "$as_me:$LINENO: error: CYGWIN compile is only supported with --enable-threads" >&5
echo "$as_me: error: CYGWIN compile is only supported with --enable-threads" >&2;}
   { (exit 1); exit 1; }; }
	    fi
	    do64bit_ok=yes
	    if test "x${SHARED_BUILD}" = "x1"; then
		echo "running cd ${TCL_SRC_DIR}/win; ${CONFIG_SHELL-/bin/sh} ./configure $ac_configure_args"
		# The eval makes quoting arguments work.
		if cd ${TCL_SRC_DIR}/win; eval ${CONFIG_SHELL-/bin/sh} ./configure $ac_configure_args; cd ../unix
		then :
		else
		    { echo "configure: error: configure failed for ../win" 1>&2; exit 1; }
		fi
	    fi
	    ;;
	dgux*)

Changes to unix/tcl.m4.

1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
		AC_MSG_ERROR([${CC} is not a cygwin compiler.])
	    fi
	    if test "x${TCL_THREADS}" = "x0"; then
		AC_MSG_ERROR([CYGWIN compile is only supported with --enable-threads])
	    fi
	    do64bit_ok=yes
	    if test "x${SHARED_BUILD}" = "x1"; then
		echo "running cd ../win; ${CONFIG_SHELL-/bin/sh} ./configure $ac_configure_args"
		# The eval makes quoting arguments work.
		if cd ../win; eval ${CONFIG_SHELL-/bin/sh} ./configure $ac_configure_args; cd ../unix
		then :
		else
		    { echo "configure: error: configure failed for ../win" 1>&2; exit 1; }
		fi
	    fi
	    ;;
	dgux*)







|

|







1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
		AC_MSG_ERROR([${CC} is not a cygwin compiler.])
	    fi
	    if test "x${TCL_THREADS}" = "x0"; then
		AC_MSG_ERROR([CYGWIN compile is only supported with --enable-threads])
	    fi
	    do64bit_ok=yes
	    if test "x${SHARED_BUILD}" = "x1"; then
		echo "running cd ${TCL_SRC_DIR}/win; ${CONFIG_SHELL-/bin/sh} ./configure $ac_configure_args"
		# The eval makes quoting arguments work.
		if cd ${TCL_SRC_DIR}/win; eval ${CONFIG_SHELL-/bin/sh} ./configure $ac_configure_args; cd ../unix
		then :
		else
		    { echo "configure: error: configure failed for ../win" 1>&2; exit 1; }
		fi
	    fi
	    ;;
	dgux*)

Changes to win/tclWinFile.c.

2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
 *---------------------------------------------------------------------------
 */

ClientData
TclNativeCreateNativeRep(
    Tcl_Obj *pathPtr)
{
    char *nativePathPtr, *str;
    Tcl_DString ds;
    Tcl_Obj *validPathPtr;
    int len, i = 2;
    WCHAR *wp;

    if (TclFSCwdIsNative()) {
	/*
	 * The cwd is native, which means we can use the translated path
	 * without worrying about normalization (this will also usually be
	 * shorter so the utf-to-external conversion will be somewhat faster).







|
|

|







2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
 *---------------------------------------------------------------------------
 */

ClientData
TclNativeCreateNativeRep(
    Tcl_Obj *pathPtr)
{
    WCHAR *nativePathPtr;
    const char *str;
    Tcl_Obj *validPathPtr;
    int len;
    WCHAR *wp;

    if (TclFSCwdIsNative()) {
	/*
	 * The cwd is native, which means we can use the translated path
	 * without worrying about normalization (this will also usually be
	 * shorter so the utf-to-external conversion will be somewhat faster).
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935


2936






2937
2938
2939
2940












































2941
2942
2943
2944

2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
	if (validPathPtr == NULL) {
	    return NULL;
	}
	Tcl_IncrRefCount(validPathPtr);
    }

    str = Tcl_GetStringFromObj(validPathPtr, &len);
    Tcl_WinUtfToTChar(str, len, &ds);
    len = Tcl_DStringLength(&ds) + sizeof(WCHAR);
    wp = (WCHAR *) Tcl_DStringValue(&ds);
    for (i=sizeof(WCHAR); i<len; ++wp,i+=sizeof(WCHAR)) {
	if ( (*wp < ' ') || wcschr(L"\"*<>|", *wp) ){
	    if (!*wp){


		/* See bug [3118489]: NUL in filenames */






		Tcl_DecrRefCount(validPathPtr);
		Tcl_DStringFree(&ds);
		return NULL;
	    }












































	    *wp |= 0xF000;
	}else if (*wp=='/') {
	    *wp = '\\';
	}

    }
    Tcl_DecrRefCount(validPathPtr);
    nativePathPtr = ckalloc(len);
    memcpy(nativePathPtr, Tcl_DStringValue(&ds), (size_t) len);

    Tcl_DStringFree(&ds);
    return nativePathPtr;
}

/*
 *---------------------------------------------------------------------------
 *
 * TclNativeDupInternalRep --







|
|
|
<
<
|
>
>
|
>
>
>
>
>
>
|
<
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

|


>

<
<
<
<
<







2923
2924
2925
2926
2927
2928
2929
2930
2931
2932


2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943

2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995





2996
2997
2998
2999
3000
3001
3002
	if (validPathPtr == NULL) {
	    return NULL;
	}
	Tcl_IncrRefCount(validPathPtr);
    }

    str = Tcl_GetStringFromObj(validPathPtr, &len);

    if (strlen(str)!=len) {
	/* String contains NUL-bytes. This is invalid. */


	return 0;
    }
    /* Let MultiByteToWideChar check for other invalid sequences, like
     * 0xC0 0x80 (== overlong NUL). See bug [3118489]: NUL in filenames */
    len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str, -1, 0, 0);
    if (len==0) {
	return 0;
    }
    /* Overallocate 6 chars, making some room for extended paths */
    wp = nativePathPtr = ckalloc( (len+6) * sizeof(WCHAR) );
    if (nativePathPtr==0) {

      return 0;
    }
    MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str, -1, nativePathPtr, len);
    /*
    ** If path starts with "//?/" or "\\?\" (extended path), translate
    ** any slashes to backslashes but leave the '?' intact
    */
    if ((str[0]=='\\' || str[0]=='/') && (str[1]=='\\' || str[1]=='/')
	    && str[2]=='?' && (str[3]=='\\' || str[3]=='/')) {
	wp[0] = wp[1] = wp[3] = '\\';
	str += 4;
	wp += 4;
    }
    /*
    ** If there is no "\\?\" prefix but there is a drive or UNC
    ** path prefix and the path is larger than MAX_PATH chars,
    ** no Win32 API function can handle that unless it is
    ** prefixed with the extended path prefix. See:
    ** <http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx#maxpath>
    **/
    if (((str[0]>='A'&&str[0]<='Z') || (str[0]>='a'&&str[0]<='z'))
	    && str[1]==':' && (str[2]=='\\' || str[2]=='/')) {
	if (wp==nativePathPtr && len>MAX_PATH) {
	    memmove(wp+4, wp, len*sizeof(WCHAR));
	    memcpy(wp, L"\\\\?\\", 4*sizeof(WCHAR));
	    wp += 4;
	}
	/*
	 ** If (remainder of) path starts with "<drive>:/" or "<drive>:\",
	 ** leave the ':' intact but translate the backslash to a slash.
	 */
	wp[2] = '\\';
	wp += 3;
    } else if (wp==nativePathPtr && len>MAX_PATH
	    && (str[0]=='\\' || str[0]=='/')
	    && (str[1]=='\\' || str[1]=='/') && str[2]!='?') {
	memmove(wp+6, wp, len*sizeof(WCHAR));
	memcpy(wp, L"\\\\?\\UNC", 7*sizeof(WCHAR));
	wp += 7;
    }
    /*
    ** In the remainder of the path, translate invalid characters to
    ** characters in the Unicode private use area.
    */
    while (*wp != '\0') {
	if ((*wp < ' ') || wcschr(L"\"*:<>?|", *wp)) {
	    *wp |= 0xF000;
	} else if (*wp == '/') {
	    *wp = '\\';
	}
	++wp;
    }





    return nativePathPtr;
}

/*
 *---------------------------------------------------------------------------
 *
 * TclNativeDupInternalRep --