Tcl Source Code

Check-in [58bcc7a29a]
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 | dgp-trunk-read
Files: files | file ages | folders
SHA1: 58bcc7a29a04ee087205635277e22ba2347a364b
User & Date: dgp 2014-04-21 20:02:39
Context
2014-04-24
18:21
merge trunk check-in: 63b6268aeb user: dgp tags: dgp-trunk-read
2014-04-21
20:02
merge trunk check-in: 58bcc7a29a user: dgp tags: dgp-trunk-read
19:04
Merge refcounting machinery for ChannelBuffer. check-in: 0c1015d94d user: dgp tags: trunk
2014-04-17
19:11
merge trunk check-in: 15f3a695ca user: dgp tags: dgp-trunk-read
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to generic/tclIO.c.

160
161
162
163
164
165
166


167
168
169
170
171
172
173
} CloseCallback;

/*
 * Static functions in this file:
 */

static ChannelBuffer *	AllocChannelBuffer(int length);


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,







>
>







160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
} CloseCallback;

/*
 * Static functions in this file:
 */

static ChannelBuffer *	AllocChannelBuffer(int length);
static void		PreserveChannelBuffer(ChannelBuffer *bufPtr);
static void		ReleaseChannelBuffer(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,
384
385
386
387
388
389
390

391
392
393
394
395
396
397
ChanRead(
    Channel *chanPtr,
    char *dst,
    int dstSize,
    int *errnoPtr)
{
    if (WillRead(chanPtr) < 0) {

        return -1;
    }

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








>







386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
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);
}

2285
2286
2287
2288
2289
2290
2291

2292
2293

















2294
2295
2296
2297
2298
2299
2300

    n = length + CHANNELBUFFER_HEADER_SIZE + BUFFER_PADDING + BUFFER_PADDING;
    bufPtr = ckalloc(n);
    bufPtr->nextAdded	= BUFFER_PADDING;
    bufPtr->nextRemoved	= BUFFER_PADDING;
    bufPtr->bufLength	= length + BUFFER_PADDING;
    bufPtr->nextPtr	= NULL;

    return bufPtr;
}


















/*
 *----------------------------------------------------------------------
 *
 * RecycleBuffer --
 *
 *	Helper function to recycle input and output buffers. Ensures that two







>


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







2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321

    n = length + CHANNELBUFFER_HEADER_SIZE + BUFFER_PADDING + BUFFER_PADDING;
    bufPtr = ckalloc(n);
    bufPtr->nextAdded	= BUFFER_PADDING;
    bufPtr->nextRemoved	= BUFFER_PADDING;
    bufPtr->bufLength	= length + BUFFER_PADDING;
    bufPtr->nextPtr	= NULL;
    bufPtr->refCount	= 1;
    return bufPtr;
}

static void
PreserveChannelBuffer(
    ChannelBuffer *bufPtr)
{
    bufPtr->refCount++;
}

static void
ReleaseChannelBuffer(
    ChannelBuffer *bufPtr)
{
    if (--bufPtr->refCount) {
	return;
    }
    ckfree(bufPtr);
}

/*
 *----------------------------------------------------------------------
 *
 * RecycleBuffer --
 *
 *	Helper function to recycle input and output buffers. Ensures that two
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
				 * always. */
{
    /*
     * Do we have to free the buffer to the OS?
     */

    if (mustDiscard) {
	ckfree(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) {
	ckfree(bufPtr);
	return;
    }

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








|










|







2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
				 * always. */
{
    /*
     * Do we have to free the buffer to the OS?
     */

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

2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
	}
    }

    /*
     * If we reached this code we return the buffer to the OS.
     */

    ckfree(bufPtr);
    return;

  keepBuffer:
    bufPtr->nextRemoved = BUFFER_PADDING;
    bufPtr->nextAdded = BUFFER_PADDING;
    bufPtr->nextPtr = NULL;
}







|







2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
	}
    }

    /*
     * If we reached this code we return the buffer to the OS.
     */

    ReleaseChannelBuffer(bufPtr);
    return;

  keepBuffer:
    bufPtr->nextRemoved = BUFFER_PADDING;
    bufPtr->nextAdded = BUFFER_PADDING;
    bufPtr->nextPtr = NULL;
}
2541
2542
2543
2544
2545
2546
2547

2548
2549
2550
2551
2552
2553
2554
	    break;		/* Out of the "while (1)". */
	}

	/*
	 * Produce the output on the channel.
	 */


	toWrite = BytesLeft(bufPtr);
	if (toWrite == 0) {
            written = 0;
	} else {
	    written = ChanWrite(chanPtr, RemovePoint(bufPtr), toWrite,
		    &errorCode);
	}







>







2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
	    break;		/* Out of the "while (1)". */
	}

	/*
	 * Produce the output on the channel.
	 */

	PreserveChannelBuffer(bufPtr);
	toWrite = BytesLeft(bufPtr);
	if (toWrite == 0) {
            written = 0;
	} else {
	    written = ChanWrite(chanPtr, RemovePoint(bufPtr), toWrite,
		    &errorCode);
	}
2664
2665
2666
2667
2668
2669
2670

2671
2672
2673
2674
2675
2676
2677
	if (IsBufferEmpty(bufPtr)) {
	    statePtr->outQueueHead = bufPtr->nextPtr;
	    if (statePtr->outQueueHead == NULL) {
		statePtr->outQueueTail = NULL;
	    }
	    RecycleBuffer(statePtr, bufPtr, 0);
	}

    }	/* Closes "while (1)". */

    /*
     * If we wrote some data while flushing in the background, we are done.
     * We can't finish the background flush until we run out of data and the
     * channel becomes writable again. This ensures that all of the pending
     * data has been flushed at the system level.







>







2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
	if (IsBufferEmpty(bufPtr)) {
	    statePtr->outQueueHead = bufPtr->nextPtr;
	    if (statePtr->outQueueHead == NULL) {
		statePtr->outQueueTail = NULL;
	    }
	    RecycleBuffer(statePtr, bufPtr, 0);
	}
	ReleaseChannelBuffer(bufPtr);
    }	/* Closes "while (1)". */

    /*
     * If we wrote some data while flushing in the background, we are done.
     * We can't finish the background flush until we run out of data and the
     * channel becomes writable again. This ensures that all of the pending
     * data has been flushed at the system level.
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
    DiscardInputQueued(statePtr, 1);

    /*
     * Discard a leftover buffer in the current output buffer field.
     */

    if (statePtr->curOutPtr != NULL) {
	ckfree(statePtr->curOutPtr);
	statePtr->curOutPtr = NULL;
    }

    /*
     * The caller guarantees that there are no more buffers queued for output.
     */








|







2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
    DiscardInputQueued(statePtr, 1);

    /*
     * Discard a leftover buffer in the current output buffer field.
     */

    if (statePtr->curOutPtr != NULL) {
	ReleaseChannelBuffer(statePtr->curOutPtr);
	statePtr->curOutPtr = NULL;
    }

    /*
     * The caller guarantees that there are no more buffers queued for output.
     */

3974
3975
3976
3977
3978
3979
3980





3981
3982
3983
3984
3985
3986
3987
    }
}

static int
WillRead(
    Channel *chanPtr)
{





    if ((chanPtr->typePtr->seekProc != NULL)
            && (Tcl_OutputBuffered((Tcl_Channel) chanPtr) > 0)) {
        if ((chanPtr->state->curOutPtr != NULL)
                && IsBufferReady(chanPtr->state->curOutPtr)) {
            SetFlag(chanPtr->state, BUFFER_READY);
        }
        if (FlushChannel(NULL, chanPtr, 0) != 0) {







>
>
>
>
>







3997
3998
3999
4000
4001
4002
4003
4004
4005
4006
4007
4008
4009
4010
4011
4012
4013
4014
4015
    }
}

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)) {
            SetFlag(chanPtr->state, BUFFER_READY);
        }
        if (FlushChannel(NULL, chanPtr, 0) != 0) {
4060
4061
4062
4063
4064
4065
4066

4067
4068
4069
4070
4071
4072
4073
4074
4075
4076
4077
4078
4079

4080
4081
4082
4083
4084
4085
4086
	     * that we need to stick at the beginning of this buffer.
	     */

	    memcpy(InsertPoint(bufPtr), safe, (size_t) saved);
	    bufPtr->nextAdded += saved;
	    saved = 0;
	}

	dst = InsertPoint(bufPtr);
	dstLen = SpaceLeft(bufPtr);

	result = Tcl_UtfToExternal(NULL, encoding, src, srcLimit,
		statePtr->outputEncodingFlags,
		&statePtr->outputEncodingState, dst,
		dstLen + BUFFER_PADDING, &srcRead, &dstWrote, NULL);

	/* See chan-io-1.[89]. Tcl Bug 506297. */
	statePtr->outputEncodingFlags &= ~TCL_ENCODING_START;
	
	if ((result != TCL_OK) && (srcRead + dstWrote == 0)) {
	    /* We're reading from invalid/incomplete UTF-8 */

	    if (total == 0) {
		Tcl_SetErrno(EINVAL);
		return -1;
	    }
	    break;
	}








>













>







4088
4089
4090
4091
4092
4093
4094
4095
4096
4097
4098
4099
4100
4101
4102
4103
4104
4105
4106
4107
4108
4109
4110
4111
4112
4113
4114
4115
4116
	     * that we need to stick at the beginning of this buffer.
	     */

	    memcpy(InsertPoint(bufPtr), safe, (size_t) saved);
	    bufPtr->nextAdded += saved;
	    saved = 0;
	}
	PreserveChannelBuffer(bufPtr);
	dst = InsertPoint(bufPtr);
	dstLen = SpaceLeft(bufPtr);

	result = Tcl_UtfToExternal(NULL, encoding, src, srcLimit,
		statePtr->outputEncodingFlags,
		&statePtr->outputEncodingState, dst,
		dstLen + BUFFER_PADDING, &srcRead, &dstWrote, NULL);

	/* See chan-io-1.[89]. Tcl Bug 506297. */
	statePtr->outputEncodingFlags &= ~TCL_ENCODING_START;
	
	if ((result != TCL_OK) && (srcRead + dstWrote == 0)) {
	    /* We're reading from invalid/incomplete UTF-8 */
	    ReleaseChannelBuffer(bufPtr);
	    if (total == 0) {
		Tcl_SetErrno(EINVAL);
		return -1;
	    }
	    break;
	}

4154
4155
4156
4157
4158
4159
4160

4161
4162
4163
4164
4165
4166
4167
		return -1;
	    }
	    flushed += statePtr->bufSize;
	    if (saved == 0 || src[-1] != '\n') {
		needNlFlush = 0;
	    }
	}

    }
    if ((flushed < total) && (GotFlag(statePtr, CHANNEL_UNBUFFERED) ||
	    (needNlFlush && GotFlag(statePtr, CHANNEL_LINEBUFFERED)))) {
	SetFlag(statePtr, BUFFER_READY);
	if (FlushChannel(NULL, chanPtr, 0) != 0) {
	    return -1;
	}







>







4184
4185
4186
4187
4188
4189
4190
4191
4192
4193
4194
4195
4196
4197
4198
		return -1;
	    }
	    flushed += statePtr->bufSize;
	    if (saved == 0 || src[-1] != '\n') {
		needNlFlush = 0;
	    }
	}
	ReleaseChannelBuffer(bufPtr);
    }
    if ((flushed < total) && (GotFlag(statePtr, CHANNEL_UNBUFFERED) ||
	    (needNlFlush && GotFlag(statePtr, CHANNEL_LINEBUFFERED)))) {
	SetFlag(statePtr, BUFFER_READY);
	if (FlushChannel(NULL, chanPtr, 0) != 0) {
	    return -1;
	}
6325
6326
6327
6328
6329
6330
6331
6332
6333
6334
6335
6336
6337
6338
6339

    /*
     * If discardSavedBuffers is nonzero, must also discard any previously
     * saved buffer in the saveInBufPtr field.
     */

    if (discardSavedBuffers && statePtr->saveInBufPtr != NULL) {
	ckfree(statePtr->saveInBufPtr);
	statePtr->saveInBufPtr = NULL;
    }
}

/*
 *---------------------------------------------------------------------------
 *







|







6356
6357
6358
6359
6360
6361
6362
6363
6364
6365
6366
6367
6368
6369
6370

    /*
     * If discardSavedBuffers is nonzero, must also discard any previously
     * saved buffer in the saveInBufPtr field.
     */

    if (discardSavedBuffers && statePtr->saveInBufPtr != NULL) {
	ReleaseChannelBuffer(statePtr->saveInBufPtr);
	statePtr->saveInBufPtr = NULL;
    }
}

/*
 *---------------------------------------------------------------------------
 *
6416
6417
6418
6419
6420
6421
6422
6423
6424
6425
6426
6427
6428
6429
6430
	 * 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)) {
	    ckfree(bufPtr);
	    bufPtr = NULL;
	}

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







|







6447
6448
6449
6450
6451
6452
6453
6454
6455
6456
6457
6458
6459
6460
6461
	 * 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;
6446
6447
6448
6449
6450
6451
6452

6453
6454

6455
6456
6457
6458
6459
6460
6461
6462
6463
6464
6465
6466
6467

6468
6469
6470
6471
6472
6473
6474
6475
6476
6477

6478
6479
6480
6481
6482
6483
6484
6485
     * platforms it is impossible to read from a device after EOF.
     */

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


    nread = ChanRead(chanPtr, InsertPoint(bufPtr), toRead, &result);
    if (nread > 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) {

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

    return 0;
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_Seek --
 *







>


>













>








<

>
|







6477
6478
6479
6480
6481
6482
6483
6484
6485
6486
6487
6488
6489
6490
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
     * 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;
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_Seek --
 *

Changes to generic/tclIO.h.

32
33
34
35
36
37
38

39
40
41
42
43
44
45
/*
 * struct ChannelBuffer:
 *
 * Buffers data being sent to or from a channel.
 */

typedef struct ChannelBuffer {

    int nextAdded;		/* The next position into which a character
				 * will be put in the buffer. */
    int nextRemoved;		/* Position of next byte to be removed from
				 * the buffer. */
    int bufLength;		/* How big is the buffer? */
    struct ChannelBuffer *nextPtr;
    				/* Next buffer in chain. */







>







32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/*
 * struct ChannelBuffer:
 *
 * Buffers data being sent to or from a channel.
 */

typedef struct ChannelBuffer {
    int refCount;		/* Current uses count */
    int nextAdded;		/* The next position into which a character
				 * will be put in the buffer. */
    int nextRemoved;		/* Position of next byte to be removed from
				 * the buffer. */
    int bufLength;		/* How big is the buffer? */
    struct ChannelBuffer *nextPtr;
    				/* Next buffer in chain. */

Changes to generic/tclIOCmd.c.

449
450
451
452
453
454
455

456
457
458
459
460
461
462
463
464
465
466
467
468
469

470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487

488
489
490
491
492
493
494
	    newline = 1;
#endif
	}
    }

    resultPtr = Tcl_NewObj();
    Tcl_IncrRefCount(resultPtr);

    charactersRead = Tcl_ReadChars(chan, resultPtr, toRead, 0);
    if (charactersRead < 0) {
	/*
	 * TIP #219.
	 * Capture error messages put by the driver into the bypass area and
	 * put them into the regular interpreter result. Fall back to the
	 * regular message if nothing was found in the bypass.
	 */

	if (!TclChanCaughtErrorBypass(interp, chan)) {
	    Tcl_SetObjResult(interp, Tcl_ObjPrintf(
		    "error reading \"%s\": %s",
		    TclGetString(chanObjPtr), Tcl_PosixError(interp)));
	}

	Tcl_DecrRefCount(resultPtr);
	return TCL_ERROR;
    }

    /*
     * If requested, remove the last newline in the channel if at EOF.
     */

    if ((charactersRead > 0) && (newline != 0)) {
	const char *result;
	int length;

	result = TclGetStringFromObj(resultPtr, &length);
	if (result[length - 1] == '\n') {
	    Tcl_SetObjLength(resultPtr, length - 1);
	}
    }
    Tcl_SetObjResult(interp, resultPtr);

    Tcl_DecrRefCount(resultPtr);
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *







>














>


















>







449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
	    newline = 1;
#endif
	}
    }

    resultPtr = Tcl_NewObj();
    Tcl_IncrRefCount(resultPtr);
    Tcl_Preserve(chan);
    charactersRead = Tcl_ReadChars(chan, resultPtr, toRead, 0);
    if (charactersRead < 0) {
	/*
	 * TIP #219.
	 * Capture error messages put by the driver into the bypass area and
	 * put them into the regular interpreter result. Fall back to the
	 * regular message if nothing was found in the bypass.
	 */

	if (!TclChanCaughtErrorBypass(interp, chan)) {
	    Tcl_SetObjResult(interp, Tcl_ObjPrintf(
		    "error reading \"%s\": %s",
		    TclGetString(chanObjPtr), Tcl_PosixError(interp)));
	}
	Tcl_Release(chan);
	Tcl_DecrRefCount(resultPtr);
	return TCL_ERROR;
    }

    /*
     * If requested, remove the last newline in the channel if at EOF.
     */

    if ((charactersRead > 0) && (newline != 0)) {
	const char *result;
	int length;

	result = TclGetStringFromObj(resultPtr, &length);
	if (result[length - 1] == '\n') {
	    Tcl_SetObjLength(resultPtr, length - 1);
	}
    }
    Tcl_SetObjResult(interp, resultPtr);
    Tcl_Release(chan);
    Tcl_DecrRefCount(resultPtr);
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *

Changes to tests/ioCmd.test.

808
809
810
811
812
813
814
































815
816
817
818
819
820
821
    set ch [chan create {read write} foo]
} -body {
    list [catch {chan configure $ch -blocking 0} m] $m
} -cleanup {
    close $ch
    rename foo {}
} -match glob -result {1 {*nested eval*}}

































# --- --- --- --------- --------- ---------
# Helper commands to record the arguments to handler methods.

# Stored in a script so that the threads and interpreters needing this
# code do not need their own copy but can access this variable.








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







808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
    set ch [chan create {read write} foo]
} -body {
    list [catch {chan configure $ch -blocking 0} m] $m
} -cleanup {
    close $ch
    rename foo {}
} -match glob -result {1 {*nested eval*}}
test iocmd-21.21 {[close] in [read] segfaults} -setup {
    proc foo {method chan args} {
	switch -- $method initialize {
	    return {initialize finalize watch read}
	} finalize {} watch {} read {
	    close $chan
	    return a
	}
    }
    set ch [chan create read foo]
} -body {
    read $ch 0
} -cleanup {
    close $ch
    rename foo {}
} -result {}
test iocmd-21.22 {[close] in [read] segfaults} -setup {
    proc foo {method chan args} {
	switch -- $method initialize {
	    return {initialize finalize watch read}
	} finalize {} watch {} read {
	    catch {close $chan}
	    return a
	}
    }
    set ch [chan create read foo]
} -body {
    read $ch 1
} -returnCodes error -cleanup {
    catch {close $ch}
    rename foo {}
} -match glob -result {*invalid argument*}

# --- --- --- --------- --------- ---------
# Helper commands to record the arguments to handler methods.

# Stored in a script so that the threads and interpreters needing this
# code do not need their own copy but can access this variable.