Changes On Branch b7aafa1d2de2680e
Not logged in

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

Changes In Branch dgp-channel-flag-repair Through [b7aafa1d2d] Excluding Merge-Ins

This is equivalent to a diff from cf82fbc5f8 to b7aafa1d2d

2014-05-20
15:00
Rework the management of the CHANNEL_BLOCKED and CHANNEL_EOF flags, in particular not allowing them ... check-in: f6b7d496c4 user: dgp tags: core-8-5-branch
2014-05-16
19:11
Improved use of EOF state to avoid worthless allocations. check-in: 916e33672f user: dgp tags: dgp-channel-flag-repair
18:45
Move the resets and testings of the BLOCKED flag to where they make more sense. check-in: b7aafa1d2d user: dgp tags: dgp-channel-flag-repair
15:10
merge 8.5 check-in: 98814a9861 user: dgp tags: trunk
15:05
merge 8.5 check-in: 673526a228 user: dgp tags: dgp-channel-flag-repair
14:59
Fix for failing tests *io-32.11* check-in: cf82fbc5f8 user: dgp tags: core-8-5-branch
14:47
merge 8.5 Closed-Leaf check-in: 66fb298ca6 user: dgp tags: bug-io-32.11
2014-05-15
16:48
Portable test to demo bug otherwise seen only on Windows. check-in: f49bdfc563 user: dgp tags: core-8-5-branch

Changes to generic/tclIO.c.
8
9
10
11
12
13
14

15
16
17
18
19
20
21
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22







+







 * 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.
 */

#undef NDEBUG
#include "tclInt.h"
#include "tclIO.h"
#include <assert.h>

/*
 * For each channel handler registered in a call to Tcl_CreateChannelHandler,
 * there is one record of the following type. All of records for a specific
163
164
165
166
167
168
169

170
171
172
173
174
175
176
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178







+







 */

static ChannelBuffer *	AllocChannelBuffer(int length);
static void		PreserveChannelBuffer(ChannelBuffer *bufPtr);
static void		ReleaseChannelBuffer(ChannelBuffer *bufPtr);
static int		IsShared(ChannelBuffer *bufPtr);
static void		ChannelTimerProc(ClientData clientData);
static int		ChanRead(Channel *chanPtr, char *dst, int dstSize);
static int		CheckChannelErrors(ChannelState *statePtr,
			    int direction);
static int		CheckForDeadChannel(Tcl_Interp *interp,
			    ChannelState *statePtr);
static void		CheckForStdChannelsBeingClosed(Tcl_Channel chan);
static void		CleanupChannelHandlers(Tcl_Interp *interp,
			    Channel *chanPtr);
341
342
343
344
345
346
347


348



















349
350

351
352
353
354
355
356















357
358
359
360
361
362
363



























364
365
366
367
368
369
370
343
344
345
346
347
348
349
350
351

352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371

372
373
374
375



376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391

392
393
394


395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428







+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+



-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-



-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







#define BUSY_STATE(st,fl) \
     ((((st)->csPtrR) && ((fl) & TCL_READABLE)) || \
      (((st)->csPtrW) && ((fl) & TCL_WRITABLE)))

#define MAX_CHANNEL_BUFFER_SIZE (1024*1024)

/*
 *---------------------------------------------------------------------------
 *
 * ChanRead, dropped here by a time traveler, see 8.6
 * ChanRead --
 *
 *	Read up to dstSize bytes using the inputProc of chanPtr, store
 *	them at dst, and return the number of bytes stored.
 *
 * Results:
 *	The return value of the driver inputProc,
 *	  - number of bytes stored at dst, ot
 *	  - -1 on error, with a Posix error code available to the
 *	    caller by calling Tcl_GetErrno().
 *
 * Side effects:
 *	The CHANNEL_BLOCKED and CHANNEL_EOF flags of the channel state are
 *	set as appropriate.
 *	On EOF, the inputEncodingFlags are set to perform ending operations
 *	on decoding.
 *	TODO - Is this really the right place for that?
 *
 *---------------------------------------------------------------------------
 */
static inline int
static int
ChanRead(
    Channel *chanPtr,
    char *dst,
    int dstSize,
    int *errnoPtr)
{
    int dstSize)
{
    int bytesRead, result;

    /*
     * If the caller asked for zero bytes, we'd force the inputProc
     * to return zero bytes, and then misinterpret that as EOF.
     */
    assert(dstSize > 0);

    /*
     * Each read op must set the blocked and eof states anew, not let
     * the effect of prior reads leak through.
     */
    ResetFlag(chanPtr->state, CHANNEL_BLOCKED | CHANNEL_EOF);
    if (WillRead(chanPtr) < 0) {
	*errnoPtr = Tcl_GetErrno();
        return -1;
    }

    return chanPtr->typePtr->inputProc(chanPtr->instanceData, dst, dstSize,
	    errnoPtr);
    bytesRead = chanPtr->typePtr->inputProc(chanPtr->instanceData,
	    dst, dstSize, &result);

    /* Stop any flag leakage through stacked channel levels */
    ResetFlag(chanPtr->state, CHANNEL_BLOCKED | CHANNEL_EOF);
    if (bytesRead > 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 (bytesRead < dstSize) {
	    SetFlag(chanPtr->state, CHANNEL_BLOCKED);
	}
    } else if (bytesRead == 0) {
	SetFlag(chanPtr->state, CHANNEL_EOF);
	chanPtr->state->inputEncodingFlags |= TCL_ENCODING_END;
    } else if (bytesRead < 0) {
	if ((result == EWOULDBLOCK) || (result == EAGAIN)) {
	    SetFlag(chanPtr->state, CHANNEL_BLOCKED);
	    result = EAGAIN;
	}
	Tcl_SetErrno(result);
    }
    return bytesRead;
}

static inline Tcl_WideInt
ChanSeek(
    Channel *chanPtr,
    Tcl_WideInt offset,
    int mode,
4117
4118
4119
4120
4121
4122
4123

4124
4125
4126
4127
4128
4129
4130
4175
4176
4177
4178
4179
4180
4181
4182
4183
4184
4185
4186
4187
4188
4189







+







		 * If we didn't append any bytes before encountering EOF,
		 * caller needs to see -1.
		 */

		Tcl_SetObjLength(objPtr, oldLength);
		CommonGetsCleanup(chanPtr);
		copiedTotal = -1;
		ResetFlag(statePtr, CHANNEL_BLOCKED);
		goto done;
	    }
	    goto gotEOL;
	}
	dst = dstEnd;
    }

4316
4317
4318
4319
4320
4321
4322
4323
4324
4325



4326
4327
4328
4329
4330
4331
4332
4333
4334
4375
4376
4377
4378
4379
4380
4381



4382
4383
4384


4385
4386
4387
4388
4389
4390
4391







-
-
-
+
+
+
-
-







	if ((bufPtr == NULL) || (bufPtr->nextAdded == BUFFER_PADDING)) {
	    /*
	     * All channel buffers were exhausted and the caller still
	     * hasn't seen EOL. Need to read more bytes from the channel
	     * device. Side effect is to allocate another channel buffer.
	     */

	    if (GotFlag(statePtr, CHANNEL_BLOCKED)) {
		if (GotFlag(statePtr, CHANNEL_NONBLOCKING)) {
		    goto restore;
	    if (GotFlag(statePtr, CHANNEL_BLOCKED|CHANNEL_NONBLOCKING)
		    == (CHANNEL_BLOCKED|CHANNEL_NONBLOCKING)) {
		goto restore;
		}
		ResetFlag(statePtr, CHANNEL_BLOCKED);
	    }
	    if (GetInput(chanPtr) != 0) {
		goto restore;
	    }
	    bufPtr = statePtr->inQueueTail;
	    if (bufPtr == NULL) {
		goto restore;
4383
4384
4385
4386
4387
4388
4389

4390
4391
4392
4393
4394
4395
4396
4440
4441
4442
4443
4444
4445
4446
4447
4448
4449
4450
4451
4452
4453
4454







+







		 * If we didn't append any bytes before encountering EOF,
		 * caller needs to see -1.
		 */

		byteArray = Tcl_SetByteArrayLength(objPtr, oldLength);
		CommonGetsCleanup(chanPtr);
		copiedTotal = -1;
		ResetFlag(statePtr, CHANNEL_BLOCKED);
		goto done;
	    }
	    goto gotEOL;
	}

	/*
	 * Copy bytes from the channel buffer to the ByteArray.
4586
4587
4588
4589
4590
4591
4592
4593
4594
4595
4596
4597
4598
4599
4600
4601
4602
4603
4604
4605
4606
4607
4644
4645
4646
4647
4648
4649
4650








4651
4652
4653
4654
4655
4656
4657







-
-
-
-
-
-
-
-







	/*
	 * All channel buffers were exhausted and the caller still hasn't seen
	 * EOL. Need to read more bytes from the channel device. Side effect
	 * is to allocate another channel buffer.
	 */

    read:
	if (GotFlag(statePtr, CHANNEL_BLOCKED)) {
	    if (GotFlag(statePtr, CHANNEL_NONBLOCKING)) {
		gsPtr->charsWrote = 0;
		gsPtr->rawRead = 0;
		return -1;
	    }
	    ResetFlag(statePtr, CHANNEL_BLOCKED);
	}
	if (GetInput(chanPtr) != 0) {
	    gsPtr->charsWrote = 0;
	    gsPtr->rawRead = 0;
	    return -1;
	}
	bufPtr = statePtr->inQueueTail;
	gsPtr->bufPtr = bufPtr;
4681
4682
4683
4684
4685
4686
4687
4688

4689
4690






4691
4692
4693
4694
4695
4696
4697
4731
4732
4733
4734
4735
4736
4737

4738
4739
4740
4741
4742
4743
4744
4745
4746
4747
4748
4749
4750
4751
4752
4753







-
+


+
+
+
+
+
+







		 * device. Fall through, returning that nothing was found.
		 */

		bufPtr->nextRemoved = bufPtr->nextAdded;
	    } else {
		/*
		 * There are no more cached raw bytes left. See if we can get
		 * some more.
		 * some more, but avoid blocking on a non-blocking channel.
		 */

		if (GotFlag(statePtr, CHANNEL_NONBLOCKING|CHANNEL_BLOCKED)
			== (CHANNEL_NONBLOCKING|CHANNEL_BLOCKED)) {
		    gsPtr->charsWrote = 0;
		    gsPtr->rawRead = 0;
		    return -1;
		}
		goto read;
	    }
	} else {
	    if (nextPtr == NULL) {
		nextPtr = AllocChannelBuffer(statePtr->bufSize);
		bufPtr->nextPtr = nextPtr;
		statePtr->inQueueTail = nextPtr;
4928
4929
4930
4931
4932
4933
4934
4935

4936
4937
4938
4939
4940
4941
4942
4984
4985
4986
4987
4988
4989
4990

4991
4992
4993
4994
4995
4996
4997
4998







-
+







    Tcl_Channel chan,		/* The channel from which to read. */
    char *bufPtr,		/* Where to store input read. */
    int bytesToRead)		/* Maximum number of bytes to read. */
{
    Channel *chanPtr = (Channel *) chan;
    ChannelState *statePtr = chanPtr->state;
				/* State info for channel */
    int nread, result, copied, copiedNow;
    int nread, copied, copiedNow = INT_MAX;

    /*
     * The check below does too much because it will reject a call to this
     * function with a channel which is part of an 'fcopy'. But we have to
     * allow this here or else the chaining in the transformation drivers will
     * fail with 'file busy' error instead of retrieving and transforming the
     * data to copy.
4953
4954
4955
4956
4957
4958
4959
4960
4961



4962
4963
4964
4965
4966

4967
4968
4969
4970

4971
4972
4973
4974
4975
4976




4977
4978
4979
4980

4981
4982

4983
4984

4985
4986
4987

4988
4989
4990
4991
4992
4993
4994
4995
4996

4997
4998
4999
5000
5001
5002
5003
5004
5005
5006
5007
5008
5009
5010
5011
5012
5013
5014
5015
5016
5017
5018
5019
5020
5021

5022
5023



5024
5025
5026
5027
5028
5029
5030
5031
5032
5033
5034
5009
5010
5011
5012
5013
5014
5015


5016
5017
5018





5019




5020






5021
5022
5023
5024




5025


5026
5027

5028



5029









5030





















5031

5032

5033
5034

5035
5036
5037
5038
5039
5040

5041
5042
5043
5044
5045
5046
5047







-
-
+
+
+
-
-
-
-
-
+
-
-
-
-
+
-
-
-
-
-
-
+
+
+
+
-
-
-
-
+
-
-
+

-
+
-
-
-
+
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

-

-
+

-
+
+
+



-







    /*
     * Check for information in the push-back buffers. If there is some, use
     * it. Go to the driver only if there is none (anymore) and the caller
     * requests more bytes.
     */

    Tcl_Preserve(chanPtr);
    for (copied = 0; copied < bytesToRead; copied += copiedNow) {
	copiedNow = CopyBuffer(chanPtr, bufPtr + copied,
    for (copied = 0; bytesToRead > 0 && copiedNow > 0;
	    bufPtr+=copiedNow, bytesToRead-=copiedNow, copied+=copiedNow) {
	copiedNow = CopyBuffer(chanPtr, bufPtr, bytesToRead);
		bytesToRead - 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);
	    }

	    /*
	     * Now go to the driver to get as much as is possible to
	     * fill the remaining request. Do all the error handling by
    if (bytesToRead > 0) {
	/*
	 * Now go to the driver to get as much as is possible to
	 * fill the remaining request.  Since we're directly filling
	     * ourselves. The code was stolen from 'GetInput' and
	     * slightly adapted (different return value here).
	     *
	     * The case of 'bytesToRead == 0' at this point cannot
	 * the caller's buffer, retain the blocked flag.
	     * happen.
	     */
	 */

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

	    if (nread > 0) {
	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);
	    if (!GotFlag(statePtr, CHANNEL_BLOCKED) || copied == 0) {
		}
	    } 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) {
			/*
			 * Information that was copied earlier has precedence
			 * over EAGAIN/WOULDBLOCK handling.
			 */

			goto done;
		    }

		    SetFlag(statePtr, CHANNEL_BLOCKED);
		    result = EAGAIN;
		}

		Tcl_SetErrno(result);
		copied = -1;
		goto done;
	    }

	} else {
	    copied += nread;
	    goto done;
	}
	if (copied != 0) {
	    ResetFlag(statePtr, CHANNEL_EOF);
	}
    }

  done:
    Tcl_Release(chanPtr);
    return copied;
}

/*
 *---------------------------------------------------------------------------
 *
5154
5155
5156
5157
5158
5159
5160


5161
5162
5163
5164
5165
5166
5167
5167
5168
5169
5170
5171
5172
5173
5174
5175
5176
5177
5178
5179
5180
5181
5182







+
+







	     * Probably not needed anymore.
	     */

	    TclGetString(objPtr);
	}
    }

    /* Must clear the BLOCKED flag here since we check before reading */
    ResetFlag(statePtr, CHANNEL_BLOCKED);
    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);
5184
5185
5186
5187
5188
5189
5190
5191
5192
5193



5194
5195
5196
5197
5198
5199
5200
5201
5202
5203
5204
5205


5206
5207
5208

5209
5210
5211
5212
5213
5214
5215
5216
5217
5218
5219
5220



5221


5222
5223


5224
5225
5226
5227
5228
5229
5230
5231
5232





5233
5234
5235
5236
5237
5238
5239
5199
5200
5201
5202
5203
5204
5205



5206
5207
5208


5209
5210
5211
5212
5213
5214
5215
5216


5217
5218
5219


5220
5221
5222
5223
5224
5225
5226
5227


5228


5229
5230
5231
5232
5233
5234


5235
5236
5237
5238
5239
5240
5241
5242
5243
5244
5245
5246
5247
5248
5249
5250
5251
5252
5253
5254
5255
5256
5257







-
-
-
+
+
+
-
-








-
-
+
+

-
-
+







-
-

-
-
+
+
+

+
+
-
-
+
+









+
+
+
+
+







	    }
	}

	if (copiedNow < 0) {
	    if (GotFlag(statePtr, CHANNEL_EOF)) {
		break;
	    }
	    if (GotFlag(statePtr, CHANNEL_BLOCKED)) {
		if (GotFlag(statePtr, CHANNEL_NONBLOCKING)) {
		    break;
	    if (GotFlag(statePtr, CHANNEL_NONBLOCKING|CHANNEL_BLOCKED)
		    == (CHANNEL_NONBLOCKING|CHANNEL_BLOCKED)) {
		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;
		if (!GotFlag(statePtr, CHANNEL_BLOCKED)) {
		    copied = -1;
		}
		copied = -1;
		goto done;
		break;
	    }
	} 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.
     * Failure to fill a channel buffer may have left channel reporting
     * a "blocked" state, but so long as we fulfilled the request here,
     * the caller does not consider us blocked.
     */
    if (toRead == 0) {
	ResetFlag(statePtr, CHANNEL_BLOCKED);

  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);
    }

    /*
     * Update the notifier state so we don't block while there is still data
     * in the buffers.
     */
    UpdateInterest(chanPtr);
    Tcl_Release(chanPtr);
    return copied;
}

/*
 *---------------------------------------------------------------------------
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
6150
6151
6152
6153
6154
6155
6156

6157



6158








6159




6160



6161




6162
6163
6164


6165
6166
6167
6168
6169
6170
6171
6172







-
+
-
-
-

-
-
-
-
-
-
-
-
+
-
-
-
-
+
-
-
-
+
-
-
-
-
+
+
+
-
-
+







     */

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

    PreserveChannelBuffer(bufPtr);
    nread = ChanRead(chanPtr, InsertPoint(bufPtr), toRead, &result);
    nread = ChanRead(chanPtr, InsertPoint(bufPtr), toRead);
    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) {
    if (nread < 0) {
	    SetFlag(statePtr, CHANNEL_BLOCKED);
	}
    } else if (nread == 0) {
	result = 0;
	result = Tcl_GetErrno();
	SetFlag(statePtr, CHANNEL_EOF);
	statePtr->inputEncodingFlags |= TCL_ENCODING_END;
    } else if (nread < 0) {
    } else {
	if ((result == EWOULDBLOCK) || (result == EAGAIN)) {
	    SetFlag(statePtr, CHANNEL_BLOCKED);
	    result = EAGAIN;
	}
	result = 0;
	bufPtr->nextAdded += nread;
    }
	Tcl_SetErrno(result);
    }

    ReleaseChannelBuffer(bufPtr);
    return result;
}

/*
 *----------------------------------------------------------------------
 *
6645
6646
6647
6648
6649
6650
6651
6652
6653
6654
6655
6656
6657

6658
6659
6660
6661
6662
6663
6664
6646
6647
6648
6649
6650
6651
6652






6653
6654
6655
6656
6657
6658
6659
6660







-
-
-
-
-
-
+








    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);
	ResetFlag(statePtr, CHANNEL_NEED_MORE_DATA);
    }

    return 0;
}

/*
 *----------------------------------------------------------------------
8827
8828
8829
8830
8831
8832
8833
8834
8835
8836
8837
8838
8839
8840
8841
8823
8824
8825
8826
8827
8828
8829

8830
8831
8832
8833
8834
8835
8836







-







	}

	/* 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 (GotFlag(statePtr, CHANNEL_EOF|CHANNEL_BLOCKED)) {
8931
8932
8933
8934
8935
8936
8937
8938
8939


8940
8941
8942



8943
8944
8945
8946
8947
8948
8949
8926
8927
8928
8929
8930
8931
8932


8933
8934
8935
8936
8937
8938
8939
8940
8941
8942
8943
8944
8945
8946
8947







-
-
+
+



+
+
+







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

	if (GotFlag(statePtr, CHANNEL_NONBLOCKING)
		&& GotFlag(statePtr, CHANNEL_BLOCKED)) {
	if (GotFlag(statePtr, CHANNEL_NONBLOCKING|CHANNEL_BLOCKED)
		== (CHANNEL_NONBLOCKING|CHANNEL_BLOCKED)) {
	    break;
	}
    }
    if (bytesToRead == 0) {
	ResetFlag(statePtr, CHANNEL_BLOCKED);
    }

    Tcl_Release(chanPtr);
    return (int)(p - dst);
}

/*
 *----------------------------------------------------------------------
Changes to tests/io.test.
6701
6702
6703
6704
6705
6706
6707
6708













6709
6710
6711
6712

6713
6714
6715
6716
6717
6718
6719
6701
6702
6703
6704
6705
6706
6707

6708
6709
6710
6711
6712
6713
6714
6715
6716
6717
6718
6719
6720
6721
6722
6723

6724
6725
6726
6727
6728
6729
6730
6731







-
+
+
+
+
+
+
+
+
+
+
+
+
+



-
+







test io-52.4 {TclCopyChannel} {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
    fcopy $f1 $f2 -size 40
    set result [list [fconfigure $f1 -blocking] [fconfigure $f2 -blocking]]
    set result [list [fblocked $f1] [fconfigure $f1 -blocking] [fconfigure $f2 -blocking]]
    close $f1
    close $f2
    lappend result [file size $path(test1)]
} {0 0 0 40}
test io-52.4.1 {TclCopyChannel} {fcopy} {
    file delete $path(test1)
    set f1 [open $thisScript]
    set f2 [open $path(test1) w]
    fconfigure $f1 -translation lf -blocking 0 -buffersize 10000000
    fconfigure $f2 -translation cr -blocking 0
    fcopy $f1 $f2 -size 40
    set result [list [fblocked $f1] [fconfigure $f1 -blocking] [fconfigure $f2 -blocking]]
    close $f1
    close $f2
    lappend result [file size $path(test1)]
} {0 0 40}
} {0 0 0 40}
test io-52.5 {TclCopyChannel, all} {fcopy} {
    file delete $path(test1)
    set f1 [open $thisScript]
    set f2 [open $path(test1) w]
    fconfigure $f1 -translation lf -blocking 0
    fconfigure $f2 -translation lf -blocking 0
    fcopy $f1 $f2 -size -1 ;# -1 means 'copy all', same as if no -size specified.