Tcl Source Code

Check-in [2a40293a49]
Login
Bounty program for improvements to Tcl and certain Tcl packages.
Tcl 2019 Conference, Houston/TX, US, Nov 4-8
Send your abstracts to [email protected]
or submit via the online form by Sep 9.

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

Overview
Comment:Rework the management of the CHANNEL_BLOCKED and CHANNEL_EOF flags, in particular not allowing them to leak between multiple layers of a stacked channel. Much common code refactored into ChanRead().
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk | potential incompatibility
Files: files | file ages | folders
SHA1: 2a40293a49e81c07bd084aeef5a4214d111d4976
User & Date: dgp 2014-05-20 15:17:11
Context
2014-05-21
10:37
Fix c&p errors in test descriptions check-in: 9b5df9c9b9 user: max tags: trunk
2014-05-20
18:40
merge trunk check-in: 089274f4ba user: dgp tags: dgp-refactor
15:17
Rework the management of the CHANNEL_BLOCKED and CHANNEL_EOF flags, in particular not allowing them ... check-in: 2a40293a49 user: dgp tags: trunk, potential incompatibility
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
14:46
Fix and improve socket -async [13d3af3ad5] check-in: fdd8917252 user: oehhar tags: trunk
2014-05-19
18:29
Same improvements to the zlib transform operations. Closed-Leaf check-in: 6578106462 user: dgp tags: dgp-trunk-flag-repair
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to generic/tclIO.c.

163
164
165
166
167
168
169

170
171
172
173
174
175
176
...
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
....
4538
4539
4540
4541
4542
4543
4544

4545
4546
4547
4548
4549
4550
4551
....
4741
4742
4743
4744
4745
4746
4747
4748
4749
4750
4751
4752
4753
4754
4755
4756
4757
4758
4759
....
4808
4809
4810
4811
4812
4813
4814

4815
4816
4817
4818
4819
4820
4821
....
5011
5012
5013
5014
5015
5016
5017
5018
5019
5020
5021
5022
5023
5024
5025
5026
5027
5028
5029
5030
5031
5032
....
5106
5107
5108
5109
5110
5111
5112
5113
5114
5115






5116
5117
5118
5119
5120
5121
5122
....
5353
5354
5355
5356
5357
5358
5359
5360
5361
5362
5363
5364
5365
5366
5367
....
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
5441
5442
5443
5444
5445
5446
5447
5448
5449
5450
5451
5452
5453
5454
5455
5456
5457
5458
5459
....
5579
5580
5581
5582
5583
5584
5585


5586
5587
5588
5589
5590
5591
5592
....
5608
5609
5610
5611
5612
5613
5614
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
....
6480
6481
6482
6483
6484
6485
6486












6487
6488
6489
6490
6491
6492
6493
....
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
....
7043
7044
7045
7046
7047
7048
7049
7050
7051
7052
7053
7054
7055
7056
7057
7058
7059
7060
7061
7062
....
9243
9244
9245
9246
9247
9248
9249
9250
9251
9252
9253
9254
9255
9256
9257
....
9347
9348
9349
9350
9351
9352
9353
9354
9355
9356
9357
9358



9359
9360
9361
9362
9363
9364
9365
 */

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		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);
................................................................................
    Channel *chanPtr,
    Tcl_Interp *interp,
    int flags)
{
    return chanPtr->typePtr->close2Proc(chanPtr->instanceData, interp, flags);
}
























static inline int
ChanRead(
    Channel *chanPtr,
    char *dst,
    int dstSize,
    int *errnoPtr)
{













    if (WillRead(chanPtr) < 0) {
	*errnoPtr = Tcl_GetErrno();
        return -1;
    }

    return chanPtr->typePtr->inputProc(chanPtr->instanceData, dst, dstSize,
	    errnoPtr);


























}

static inline Tcl_WideInt
ChanSeek(
    Channel *chanPtr,
    Tcl_WideInt offset,
    int mode,
................................................................................
		 * If we didn't append any bytes before encountering EOF,
		 * caller needs to see -1.
		 */

		Tcl_SetObjLength(objPtr, oldLength);
		CommonGetsCleanup(chanPtr);
		copiedTotal = -1;

		goto done;
	    }
	    goto gotEOL;
	}
	dst = dstEnd;
    }

................................................................................
	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;
		}
		ResetFlag(statePtr, CHANNEL_BLOCKED);
	    }
	    if (GetInput(chanPtr) != 0) {
		goto restore;
	    }
	    bufPtr = statePtr->inQueueTail;
	    if (bufPtr == NULL) {
		goto restore;
................................................................................
		 * If we didn't append any bytes before encountering EOF,
		 * caller needs to see -1.
		 */

		byteArray = Tcl_SetByteArrayLength(objPtr, oldLength);
		CommonGetsCleanup(chanPtr);
		copiedTotal = -1;

		goto done;
	    }
	    goto gotEOL;
	}

	/*
	 * Copy bytes from the channel buffer to the ByteArray.
................................................................................
	/*
	 * 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;
................................................................................
		 * 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.
		 */







		goto read;
	    }
	} else {
	    if (nextPtr == NULL) {
		nextPtr = AllocChannelBuffer(statePtr->bufSize);
		bufPtr->nextPtr = nextPtr;
		statePtr->inQueueTail = nextPtr;
................................................................................
    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;

    /*
     * 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.
................................................................................
    /*
     * 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,
		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
	     * 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) {
			/*
			 * 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;
	    }

	    copied += nread;
	    goto done;
	}
    }

  done:
    Tcl_Release(chanPtr);
    return copied;
}
 
/*
 *---------------------------------------------------------------------------
 *
................................................................................
	     * 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 (copiedNow < 0) {
	    if (GotFlag(statePtr, CHANNEL_EOF)) {
		break;
	    }
	    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;
}
 
/*
 *---------------------------------------------------------------------------
................................................................................
     * channel cleanup has run but the channel is still registered in some
     * interpreter.
     */

    if (CheckForDeadChannel(NULL, statePtr)) {
	return EINVAL;
    }













    /*
     * 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.
     */
................................................................................
	    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);
	    result = EAGAIN;

	}
	Tcl_SetErrno(result);
    }
    ReleaseChannelBuffer(bufPtr);
    return result;
}
 
/*
 *----------------------------------------------------------------------
 *
................................................................................

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

	/* 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)) {
................................................................................
	    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);
}
 
/*
 *----------------------------------------------------------------------






>







 







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



|
<

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

<



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







 







>







 







|
|
|
<
<







 







>







 







<
<
<
<
<
<
<
<







 







|


>
>
>
>
>
>







 







|







 







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

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



<







 







>
>







 







|
|
|
<
<








|
|

|
<







<
<

>
>
|
<

>
>
|
<
>









>
>
>
>
>







 







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







 







<
<
<
<
<
<
<
<
<
<

|
<
<
<

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







 







<
<
<
<
<
|







 







<







 







|
|



>
>
>







163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
...
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

429
430
431
432

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
460
461
462
463
464
465
....
4598
4599
4600
4601
4602
4603
4604
4605
4606
4607
4608
4609
4610
4611
4612
....
4802
4803
4804
4805
4806
4807
4808
4809
4810
4811


4812
4813
4814
4815
4816
4817
4818
....
4867
4868
4869
4870
4871
4872
4873
4874
4875
4876
4877
4878
4879
4880
4881
....
5071
5072
5073
5074
5075
5076
5077








5078
5079
5080
5081
5082
5083
5084
....
5158
5159
5160
5161
5162
5163
5164
5165
5166
5167
5168
5169
5170
5171
5172
5173
5174
5175
5176
5177
5178
5179
5180
....
5411
5412
5413
5414
5415
5416
5417
5418
5419
5420
5421
5422
5423
5424
5425
....
5436
5437
5438
5439
5440
5441
5442
5443
5444
5445




5446



5447



5448
5449
5450
5451


5452
5453



5454
5455


5456








5457
5458
5459
5460


5461
5462


5463








5464










5465
5466
5467

5468
5469
5470
5471
5472
5473
5474
....
5594
5595
5596
5597
5598
5599
5600
5601
5602
5603
5604
5605
5606
5607
5608
5609
....
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
....
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
....
6577
6578
6579
6580
6581
6582
6583










6584
6585



6586







6587



6588


6589


6590
6591
6592

6593
6594
6595
6596
6597
6598
6599
6600
....
7048
7049
7050
7051
7052
7053
7054





7055
7056
7057
7058
7059
7060
7061
7062
....
9243
9244
9245
9246
9247
9248
9249

9250
9251
9252
9253
9254
9255
9256
....
9346
9347
9348
9349
9350
9351
9352
9353
9354
9355
9356
9357
9358
9359
9360
9361
9362
9363
9364
9365
9366
9367
 */

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);
................................................................................
    Channel *chanPtr,
    Tcl_Interp *interp,
    int flags)
{
    return chanPtr->typePtr->close2Proc(chanPtr->instanceData, interp, flags);
}

/*
 *---------------------------------------------------------------------------
 *
 * 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 int
ChanRead(
    Channel *chanPtr,
    char *dst,
    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) {

        return -1;
    }

    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,
................................................................................
		 * 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;
    }

................................................................................
	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|CHANNEL_NONBLOCKING)
		    == (CHANNEL_BLOCKED|CHANNEL_NONBLOCKING)) {
		goto restore;


	    }
	    if (GetInput(chanPtr) != 0) {
		goto restore;
	    }
	    bufPtr = statePtr->inQueueTail;
	    if (bufPtr == NULL) {
		goto restore;
................................................................................
		 * 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.
................................................................................
	/*
	 * 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 (GetInput(chanPtr) != 0) {
	    gsPtr->charsWrote = 0;
	    gsPtr->rawRead = 0;
	    return -1;
	}
	bufPtr = statePtr->inQueueTail;
	gsPtr->bufPtr = bufPtr;
................................................................................
		 * 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, 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;
................................................................................
    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, 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.
................................................................................
    /*
     * 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; bytesToRead > 0 && copiedNow > 0;
	    bufPtr+=copiedNow, bytesToRead-=copiedNow, copied+=copiedNow) {
	copiedNow = CopyBuffer(chanPtr, bufPtr, bytesToRead);




    }







    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


	 * the caller's buffer, retain the blocked flag.
	 */




	nread = ChanRead(chanPtr, bufPtr, bytesToRead);


	if (nread < 0) {








	    if (!GotFlag(statePtr, CHANNEL_BLOCKED) || copied == 0) {
		copied = -1;
	    }
	} else {


	    copied += nread;
	}


	if (copied != 0) {








	    ResetFlag(statePtr, CHANNEL_EOF);










	}
    }


    Tcl_Release(chanPtr);
    return copied;
}
 
/*
 *---------------------------------------------------------------------------
 *
................................................................................
	     * 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);
................................................................................
	    }
	}

	if (copiedNow < 0) {
	    if (GotFlag(statePtr, CHANNEL_EOF)) {
		break;
	    }
	    if (GotFlag(statePtr, CHANNEL_NONBLOCKING|CHANNEL_BLOCKED)
		    == (CHANNEL_NONBLOCKING|CHANNEL_BLOCKED)) {
		break;


	    }
	    result = GetInput(chanPtr);
	    if (chanPtr != statePtr->topChanPtr) {
		Tcl_Release(chanPtr);
		chanPtr = statePtr->topChanPtr;
		Tcl_Preserve(chanPtr);
	    }
	    if (result != 0) {
		if (!GotFlag(statePtr, CHANNEL_BLOCKED)) {
		    copied = -1;
		}
		break;

	    }
	} else {
	    copied += copiedNow;
	    toRead -= copiedNow;
	}
    }



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


    /*
     * 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;
}
 
/*
 *---------------------------------------------------------------------------
................................................................................
     * channel cleanup has run but the channel is still registered in some
     * interpreter.
     */

    if (CheckForDeadChannel(NULL, statePtr)) {
	return EINVAL;
    }

    /* 
     * For a channel at EOF do not bother allocating buffers; there's
     * nothing more to read.  Avoid calling the driver inputproc in
     * case some of them do not react well to additional calls after
     * they've reported an eof state..
     * TODO: Candidate for a can't happen panic.
     */

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

    /*
     * 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.
     */
................................................................................
	    statePtr->inQueueHead = bufPtr;
	} else {
	    statePtr->inQueueTail->nextPtr = bufPtr;
	}
	statePtr->inQueueTail = bufPtr;
    }











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











    if (nread < 0) {



	result = Tcl_GetErrno();


    } else {


	result = 0;
	bufPtr->nextAdded += nread;
    }


    ReleaseChannelBuffer(bufPtr);
    return result;
}
 
/*
 *----------------------------------------------------------------------
 *
................................................................................

    if (BUSY_STATE(statePtr, flags) && ((flags & CHANNEL_RAW_MODE) == 0)) {
	Tcl_SetErrno(EBUSY);
	return -1;
    }

    if (direction == TCL_READABLE) {





	ResetFlag(statePtr, CHANNEL_NEED_MORE_DATA);
    }

    return 0;
}
 
/*
 *----------------------------------------------------------------------
................................................................................
	}

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

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


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

	    assert (bufPtr != NULL);

	    if (statePtr->flags & (CHANNEL_EOF|CHANNEL_BLOCKED)) {
................................................................................
	    statePtr->inQueueHead = bufPtr->nextPtr;
	    if (statePtr->inQueueHead == NULL) {
		statePtr->inQueueTail = NULL;
	    }
	    RecycleBuffer(statePtr, bufPtr, 0);
	}

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

    Tcl_Release(chanPtr);
    return (int)(p - dst);
}
 
/*
 *----------------------------------------------------------------------

Changes to generic/tclIOGT.c.

679
680
681
682
683
684
685

686
687
688
689

690
691
692
693
694
695
696








697
698
699
700

701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
	/*
	 * Get bytes from the underlying channel.
	 */

	read = Tcl_ReadRaw(downChan, buf, toRead);
	if (read < 0) {

	    /*
	     * Report errors to caller. EAGAIN is a special situation. If we
	     * had some data before we report that instead of the request to
	     * re-try.

	     */
		int error = Tcl_GetErrno();

	    if ((error == EAGAIN) && (gotBytes > 0)) {
		break;
	    }









	    *errorCodePtr = error;
	    gotBytes = -1;
	    break;
	} else if (read == 0) {

	    /*
	     * Check wether we hit on EOF in the underlying channel or not. If
	     * not differentiate between blocking and non-blocking modes. In
	     * non-blocking mode we ran temporarily out of data. Signal this
	     * to the caller via EWOULDBLOCK and error return (-1). In the
	     * other cases we simply return what we got and let the caller
	     * wait for more. On the other hand, if we got an EOF we have to
	     * convert and flush all waiting partial data.
	     */

	    if (!Tcl_Eof(downChan)) {
		if ((gotBytes == 0) && (dataPtr->flags & CHANNEL_ASYNC)) {
		    *errorCodePtr = EWOULDBLOCK;
		    gotBytes = -1;
		}
		break;
	    }

	    if (dataPtr->readIsFlushed) {
		/*
		 * Already flushed, nothing to do anymore.
		 */

		break;
	    }






>
|
<
|
|
>
|
<

<



>
>
>
>
>
>
>
>
|



>

<
|
|
<
<
<
<


<
<
<
<
<
<
<
<







679
680
681
682
683
684
685
686
687

688
689
690
691

692

693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709

710
711




712
713








714
715
716
717
718
719
720
	/*
	 * Get bytes from the underlying channel.
	 */

	read = Tcl_ReadRaw(downChan, buf, toRead);
	if (read < 0) {
	    if (Tcl_InputBlocked(downChan) && (gotBytes > 0)) {
		/*

		 * Zero bytes available from downChan because blocked.
		 * But nonzero bytes already copied, so total is a
		 * valid blocked short read. Return to caller.
		 */



		break;
	    }

	    /*
	     * Either downChan is not blocked (there's a real error).
	     * or it is and there are no bytes copied yet.  In either
	     * case we want to pass the "error" along to the caller,
	     * either to report an error, or to signal to the caller
	     * that zero bytes are available because blocked.
	     */

	    *errorCodePtr = Tcl_GetErrno();
	    gotBytes = -1;
	    break;
	} else if (read == 0) {

	    /*

	     * Zero returned from Tcl_ReadRaw() always indicates EOF
	     * on the down channel.




	     */









	    if (dataPtr->readIsFlushed) {
		/*
		 * Already flushed, nothing to do anymore.
		 */

		break;
	    }

Changes to generic/tclIORTrans.c.

1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142

1143
1144
1145
1146
1147



1148
1149
1150
1151
1152








1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170


1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
....
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
	    goto stop;
	}


	readBytes = Tcl_ReadRaw(rtPtr->parent,
		(char *) Tcl_SetByteArrayLength(bufObj, toRead), toRead);
	if (readBytes < 0) {
	    /*
	     * Report errors to caller. The state of the seek system is
	     * unchanged!
	     */


	    if ((Tcl_GetErrno() == EAGAIN) && (gotBytes > 0)) {
		/*
		 * EAGAIN is a special situation. If we had some data before
		 * we report that instead of the request to re-try.



		 */

		goto stop;
	    }









	    *errorCodePtr = Tcl_GetErrno();
	    goto error;
	}

	if (readBytes == 0) {
	    /*
	     * Check wether we hit on EOF in 'parent' or not. If not
	     * differentiate between blocking and non-blocking modes. In
	     * non-blocking mode we ran temporarily out of data. Signal this
	     * to the caller via EWOULDBLOCK and error return (-1). In the
	     * other cases we simply return what we got and let the caller
	     * wait for more. On the other hand, if we got an EOF we have to
	     * convert and flush all waiting partial data.
	     */

	    if (!Tcl_Eof(rtPtr->parent)) {
		/*
		 * The state of the seek system is unchanged!


		 */

		if ((gotBytes == 0) && rtPtr->nonblocking) {
		    *errorCodePtr = EWOULDBLOCK;
		    goto error;
		}
		goto stop;
	    } else {
		/*
		 * Eof in parent.
		 */

		if (rtPtr->readIsDrained) {
		    goto stop;
		}

		/*
		 * Now this is a bit different. The partial data waiting is
		 * converted and returned.
................................................................................
		    /*
		     * The drain delivered nothing.
		     */

		    goto stop;
		}

		/*
		 * Reset eof, force caller to drain result buffer.
		 */

		((Channel *) rtPtr->parent)->state->flags &= ~CHANNEL_EOF;
		continue; /* at: while (toRead > 0) */
	    }
	} /* readBytes == 0 */

	/*
	 * Transform the read chunk, which was not empty. Anything we got back
	 * is a transformation result is put into our buffers, and the next
	 * iteration will put it into the result.
	 */






<
<
<
<
>

<

<
<
>
>
>





>
>
>
>
>
>
>
>





<
<
<
<
<
<
<
<
<

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







 







<
<
<
<
<

<







1132
1133
1134
1135
1136
1137
1138




1139
1140

1141


1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162









1163

1164

1165
1166
1167
1168










1169
1170
1171
1172
1173
1174
1175
....
1185
1186
1187
1188
1189
1190
1191





1192

1193
1194
1195
1196
1197
1198
1199
	    goto stop;
	}


	readBytes = Tcl_ReadRaw(rtPtr->parent,
		(char *) Tcl_SetByteArrayLength(bufObj, toRead), toRead);
	if (readBytes < 0) {




	    if (Tcl_InputBlocked(rtPtr->parent) && (gotBytes > 0)) {


		/*


		 * Down channel is blocked and offers zero additional bytes.
		 * The nonzero gotBytes already returned makes the total
		 * operation a valid short read.  Return to caller.
		 */

		goto stop;
	    }

	    /*
	     * Either the down channel is not blocked (a real error)
	     * or it is and there are gotBytes==0 byte copied so far.
	     * In either case, pass up the error, so we either report
	     * any real error, or do not mistakenly signal EOF by
	     * returning 0 to the caller.
	     */

	    *errorCodePtr = Tcl_GetErrno();
	    goto error;
	}

	if (readBytes == 0) {











	    /*

	     * Zero returned from Tcl_ReadRaw() always indicates EOF
	     * on the down channel.
	     */
	










		if (rtPtr->readIsDrained) {
		    goto stop;
		}

		/*
		 * Now this is a bit different. The partial data waiting is
		 * converted and returned.
................................................................................
		    /*
		     * The drain delivered nothing.
		     */

		    goto stop;
		}






		continue; /* at: while (toRead > 0) */

	} /* readBytes == 0 */

	/*
	 * Transform the read chunk, which was not empty. Anything we got back
	 * is a transformation result is put into our buffers, and the next
	 * iteration will put it into the result.
	 */

Changes to generic/tclZlib.c.

2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001

3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
....
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
	 *  2.	Got an error (readBytes < 0) which we should report up except
	 *	for the case where we can convert it to a short read.
	 *  3.	Got an end-of-data from EOF or blocking (readBytes == 0). If
	 *	it is EOF, try flushing the data out of the decompressor.
	 */

	if (readBytes < 0) {
	    /*
	     * Report errors to caller. The state of the seek system is
	     * unchanged!
	     */


	    if ((Tcl_GetErrno() == EAGAIN) && (gotBytes > 0)) {
		/*
		 * EAGAIN is a special situation. If we had some data before
		 * we report that instead of the request to re-try.
		 */

		return gotBytes;
	    }

	    *errorCodePtr = Tcl_GetErrno();
	    return -1;
	} else if (readBytes == 0) {
	    /*
	     * Check wether we hit on EOF in 'parent' or not. If not,
	     * differentiate between blocking and non-blocking modes. In
	     * non-blocking mode we ran temporarily out of data. Signal this
	     * to the caller via EWOULDBLOCK and error return (-1). In the
	     * other cases we simply return what we got and let the caller
	     * wait for more. On the other hand, if we got an EOF we have to
	     * convert and flush all waiting partial data.
	     */

	    if (!Tcl_Eof(cd->parent)) {
		/*
		 * The state of the seek system is unchanged!
		 */

		if ((gotBytes == 0) && (cd->flags & ASYNC)) {
		    *errorCodePtr = EWOULDBLOCK;
		    return -1;
		}
		return gotBytes;
	    }

	    /*
	     * (Semi-)Eof in parent.
	     *
	     * Now this is a bit different. The partial data waiting is
	     * converted and returned.
	     */

	    if (ResultGenerate(cd, 0, Z_SYNC_FLUSH, errorCodePtr) != TCL_OK) {
		return -1;
................................................................................
		/*
		 * The drain delivered nothing. Time to deliver what we've
		 * got.
		 */

		return gotBytes;
	    }

	    /*
	     * Reset eof, force caller to drain result buffer.
	     */

	    ((Channel *) cd->parent)->state->flags &= ~CHANNEL_EOF;
	} else /* readBytes > 0 */ {
	    /*
	     * Transform the read chunk, which was not empty. Anything we get
	     * back is a transformation result to be put into our buffers, and
	     * the next iteration will put it into the result.
	     */







<
<
<
<

>
|
<
<
<
<
<





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

|







 







<
<
<
<
<
<







2990
2991
2992
2993
2994
2995
2996




2997
2998
2999





3000
3001
3002
3003
3004










3005





3006






3007
3008
3009
3010
3011
3012
3013
3014
3015
....
3019
3020
3021
3022
3023
3024
3025






3026
3027
3028
3029
3030
3031
3032
	 *  2.	Got an error (readBytes < 0) which we should report up except
	 *	for the case where we can convert it to a short read.
	 *  3.	Got an end-of-data from EOF or blocking (readBytes == 0). If
	 *	it is EOF, try flushing the data out of the decompressor.
	 */

	if (readBytes < 0) {





	    /* See ReflectInput() in tclIORTrans.c */
	    if (Tcl_InputBlocked(cd->parent) && (gotBytes > 0)) {





		return gotBytes;
	    }

	    *errorCodePtr = Tcl_GetErrno();
	    return -1;










	}





	if (readBytes == 0) {






	    /*
	     * Eof in parent.
	     *
	     * Now this is a bit different. The partial data waiting is
	     * converted and returned.
	     */

	    if (ResultGenerate(cd, 0, Z_SYNC_FLUSH, errorCodePtr) != TCL_OK) {
		return -1;
................................................................................
		/*
		 * The drain delivered nothing. Time to deliver what we've
		 * got.
		 */

		return gotBytes;
	    }






	} else /* readBytes > 0 */ {
	    /*
	     * Transform the read chunk, which was not empty. Anything we get
	     * back is a transformation result to be put into our buffers, and
	     * the next iteration will put it into the result.
	     */

Changes to tests/io.test.

6725
6726
6727
6728
6729
6730
6731
6732












6733
6734
6735
6736
6737
6738
6739
6740
6741
6742
6743
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]]












    close $f1
    close $f2
    lappend result [file size $path(test1)]
} {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.






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



|







6725
6726
6727
6728
6729
6730
6731
6732
6733
6734
6735
6736
6737
6738
6739
6740
6741
6742
6743
6744
6745
6746
6747
6748
6749
6750
6751
6752
6753
6754
6755
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 [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 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.

Changes to tests/iogt.test.

476
477
478
479
480
481
482

483
484
485
486
487
488
489
...
522
523
524
525
526
527
528

529
530
531
532
533
534
535
...
573
574
575
576
577
578
579

580
581
582
583
584
585
586
read
query/maxRead
read
query/maxRead
read
query/maxRead
flush/read

delete/read
--------
create/write
write
write
write
write
................................................................................
read %^&*()_+-= %^&*()_+-=
query/maxRead {} -1
read {
} {
}
query/maxRead {} -1
flush/read {} {}

delete/read {} *ignored*
--------
create/write {} *ignored*
write abcdefghij abcdefghij
write klmnopqrst klmnopqrst
write uvwxyz0123 uvwxyz0123
write 456789,./? 456789,./?
................................................................................
}
query/maxRead {} -1
flush/read {} {}
write %^&*()_+-= %^&*()_+-=
write {
} {
}

delete/read {} *ignored*
flush/write {} {}
delete/write {} *ignored*}

test iogt-2.4 {basic I/O, mixed trail} {testchannel} {
    set fh [open $path(dummy) r]
    torture -attach $fh






>







 







>







 







>







476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
...
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
...
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
read
query/maxRead
read
query/maxRead
read
query/maxRead
flush/read
query/maxRead
delete/read
--------
create/write
write
write
write
write
................................................................................
read %^&*()_+-= %^&*()_+-=
query/maxRead {} -1
read {
} {
}
query/maxRead {} -1
flush/read {} {}
query/maxRead {} -1
delete/read {} *ignored*
--------
create/write {} *ignored*
write abcdefghij abcdefghij
write klmnopqrst klmnopqrst
write uvwxyz0123 uvwxyz0123
write 456789,./? 456789,./?
................................................................................
}
query/maxRead {} -1
flush/read {} {}
write %^&*()_+-= %^&*()_+-=
write {
} {
}
query/maxRead {} -1
delete/read {} *ignored*
flush/write {} {}
delete/write {} *ignored*}

test iogt-2.4 {basic I/O, mixed trail} {testchannel} {
    set fh [open $path(dummy) r]
    torture -attach $fh

Changes to tests/zlib.test.

211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
    zlib push decompress $r
    fileevent $r readable {set msg [read $r];lappend ::res $msg;if {[eof $r]} {set ::done 1}}
    after 250 {lappend ::res MIDDLE}
    vwait ::done
    set ::res
} -cleanup {
    catch {close $r}
} -result {qwertyuiop MIDDLE asdfghjkl}
test zlib-8.6 {transformation and fconfigure} -setup {
    set file [makeFile {} test.z]
    set fd [open $file wb]
} -constraints zlib -body {
    list [fconfigure $fd] [zlib push compress $fd; fconfigure $fd] \
	[chan pop $fd; fconfigure $fd]
} -cleanup {






|







211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
    zlib push decompress $r
    fileevent $r readable {set msg [read $r];lappend ::res $msg;if {[eof $r]} {set ::done 1}}
    after 250 {lappend ::res MIDDLE}
    vwait ::done
    set ::res
} -cleanup {
    catch {close $r}
} -result {qwertyuiop MIDDLE asdfghjkl {}}
test zlib-8.6 {transformation and fconfigure} -setup {
    set file [makeFile {} test.z]
    set fd [open $file wb]
} -constraints zlib -body {
    list [fconfigure $fd] [zlib push compress $fd; fconfigure $fd] \
	[chan pop $fd; fconfigure $fd]
} -cleanup {