1/*
2 * tclIO.c --
3 *
4 *	This file provides the generic portions (those that are the same on
5 *	all platforms and for all channel types) of Tcl's IO facilities.
6 *
7 * Copyright (c) 1998 Scriptics Corporation
8 * Copyright (c) 1995-1997 Sun Microsystems, Inc.
9 *
10 * See the file "license.terms" for information on usage and redistribution
11 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
12 *
13 * RCS: @(#) $Id: tclIO.c,v 1.3 1999/04/18 13:27:07 aku Exp $
14 */
15
16#include "tclInt.h"
17#include "tclPort.h"
18
19/*
20 * Make sure that both EAGAIN and EWOULDBLOCK are defined. This does not
21 * compile on systems where neither is defined. We want both defined so
22 * that we can test safely for both. In the code we still have to test for
23 * both because there may be systems on which both are defined and have
24 * different values.
25 */
26
27#if ((!defined(EWOULDBLOCK)) && (defined(EAGAIN)))
28#   define EWOULDBLOCK EAGAIN
29#endif
30#if ((!defined(EAGAIN)) && (defined(EWOULDBLOCK)))
31#   define EAGAIN EWOULDBLOCK
32#endif
33#if ((!defined(EAGAIN)) && (!defined(EWOULDBLOCK)))
34    error one of EWOULDBLOCK or EAGAIN must be defined
35#endif
36
37/*
38 * The following structure encapsulates the state for a background channel
39 * copy.  Note that the data buffer for the copy will be appended to this
40 * structure.
41 */
42
43typedef struct CopyState {
44    struct Channel *readPtr;	/* Pointer to input channel. */
45    struct Channel *writePtr;	/* Pointer to output channel. */
46    int readFlags;		/* Original read channel flags. */
47    int writeFlags;		/* Original write channel flags. */
48    int toRead;			/* Number of bytes to copy, or -1. */
49    int total;			/* Total bytes transferred (written). */
50    Tcl_Interp *interp;		/* Interp that started the copy. */
51    Tcl_Obj *cmdPtr;		/* Command to be invoked at completion. */
52    int bufSize;		/* Size of appended buffer. */
53    char buffer[1];		/* Copy buffer, this must be the last
54				 * field. */
55} CopyState;
56
57/*
58 * struct ChannelBuffer:
59 *
60 * Buffers data being sent to or from a channel.
61 */
62
63typedef struct ChannelBuffer {
64    int nextAdded;		/* The next position into which a character
65                                 * will be put in the buffer. */
66    int nextRemoved;		/* Position of next byte to be removed
67                                 * from the buffer. */
68    int bufLength;		/* How big is the buffer? */
69    struct ChannelBuffer *nextPtr;
70    				/* Next buffer in chain. */
71    char buf[4];		/* Placeholder for real buffer. The real
72                                 * buffer occuppies this space + bufSize-4
73                                 * bytes. This must be the last field in
74                                 * the structure. */
75} ChannelBuffer;
76
77#define CHANNELBUFFER_HEADER_SIZE	(sizeof(ChannelBuffer) - 4)
78
79/*
80 * How much extra space to allocate in buffer to hold bytes from previous
81 * buffer (when converting to UTF-8) or to hold bytes that will go to
82 * next buffer (when converting from UTF-8).
83 */
84
85#define BUFFER_PADDING	    16
86
87/*
88 * The following defines the *default* buffer size for channels.
89 */
90
91#define CHANNELBUFFER_DEFAULT_SIZE	(1024 * 4)
92
93/*
94 * Structure to record a close callback. One such record exists for
95 * each close callback registered for a channel.
96 */
97
98typedef struct CloseCallback {
99    Tcl_CloseProc *proc;		/* The procedure to call. */
100    ClientData clientData;		/* Arbitrary one-word data to pass
101                                         * to the callback. */
102    struct CloseCallback *nextPtr;	/* For chaining close callbacks. */
103} CloseCallback;
104
105/*
106 * The following structure describes the information saved from a call to
107 * "fileevent". This is used later when the event being waited for to
108 * invoke the saved script in the interpreter designed in this record.
109 */
110
111typedef struct EventScriptRecord {
112    struct Channel *chanPtr;	/* The channel for which this script is
113                                 * registered. This is used only when an
114                                 * error occurs during evaluation of the
115                                 * script, to delete the handler. */
116    Tcl_Obj *scriptPtr;		/* Script to invoke. */
117    Tcl_Interp *interp;		/* In what interpreter to invoke script? */
118    int mask;			/* Events must overlap current mask for the
119                                 * stored script to be invoked. */
120    struct EventScriptRecord *nextPtr;
121    				/* Next in chain of records. */
122} EventScriptRecord;
123
124/*
125 * struct Channel:
126 *
127 * One of these structures is allocated for each open channel. It contains data
128 * specific to the channel but which belongs to the generic part of the Tcl
129 * channel mechanism, and it points at an instance specific (and type
130 * specific) * instance data, and at a channel type structure.
131 */
132
133typedef struct Channel {
134    char *channelName;		/* The name of the channel instance in Tcl
135                                 * commands. Storage is owned by the generic IO
136                                 * code,  is dynamically allocated. */
137    int	flags;			/* ORed combination of the flags defined
138                                 * below. */
139    Tcl_Encoding encoding;	/* Encoding to apply when reading or writing
140				 * data on this channel.  NULL means no
141				 * encoding is applied to data. */
142    Tcl_EncodingState inputEncodingState;
143				/* Current encoding state, used when converting
144				 * input data bytes to UTF-8. */
145    int inputEncodingFlags;	/* Encoding flags to pass to conversion
146				 * routine when converting input data bytes to
147				 * UTF-8.  May be TCL_ENCODING_START before
148				 * converting first byte and TCL_ENCODING_END
149				 * when EOF is seen. */
150    Tcl_EncodingState outputEncodingState;
151				/* Current encoding state, used when converting
152				 * UTF-8 to output data bytes. */
153    int outputEncodingFlags;	/* Encoding flags to pass to conversion
154				 * routine when converting UTF-8 to output
155				 * data bytes.  May be TCL_ENCODING_START
156				 * before converting first byte and
157				 * TCL_ENCODING_END when EOF is seen. */
158    Tcl_EolTranslation inputTranslation;
159				/* What translation to apply for end of line
160                                 * sequences on input? */
161    Tcl_EolTranslation outputTranslation;
162    				/* What translation to use for generating
163                                 * end of line sequences in output? */
164    int inEofChar;		/* If nonzero, use this as a signal of EOF
165                                 * on input. */
166    int outEofChar;             /* If nonzero, append this to the channel
167                                 * when it is closed if it is open for
168                                 * writing. */
169    int unreportedError;	/* Non-zero if an error report was deferred
170                                 * because it happened in the background. The
171                                 * value is the POSIX error code. */
172    ClientData instanceData;	/* Instance-specific data provided by
173				 * creator of channel. */
174
175    Tcl_ChannelType *typePtr;	/* Pointer to channel type structure. */
176    int refCount;		/* How many interpreters hold references to
177                                 * this IO channel? */
178    CloseCallback *closeCbPtr;	/* Callbacks registered to be called when the
179                                 * channel is closed. */
180    char *outputStage;		/* Temporary staging buffer used when
181				 * translating EOL before converting from
182				 * UTF-8 to external form. */
183    ChannelBuffer *curOutPtr;	/* Current output buffer being filled. */
184    ChannelBuffer *outQueueHead;/* Points at first buffer in output queue. */
185    ChannelBuffer *outQueueTail;/* Points at last buffer in output queue. */
186
187    ChannelBuffer *saveInBufPtr;/* Buffer saved for input queue - eliminates
188                                 * need to allocate a new buffer for "gets"
189                                 * that crosses buffer boundaries. */
190    ChannelBuffer *inQueueHead;	/* Points at first buffer in input queue. */
191    ChannelBuffer *inQueueTail;	/* Points at last buffer in input queue. */
192
193    struct ChannelHandler *chPtr;/* List of channel handlers registered
194                                  * for this channel. */
195    int interestMask;		/* Mask of all events this channel has
196                                 * handlers for. */
197    struct Channel *nextChanPtr;/* Next in list of channels currently open. */
198    EventScriptRecord *scriptRecordPtr;
199    				/* Chain of all scripts registered for
200                                 * event handlers ("fileevent") on this
201                                 * channel. */
202    int bufSize;		/* What size buffers to allocate? */
203    Tcl_TimerToken timer;	/* Handle to wakeup timer for this channel. */
204    CopyState *csPtr;		/* State of background copy, or NULL. */
205
206  /* Andreas Kupries <a.kupries@westend.com>, 12/13/1998
207   * "Trf-Patch for filtering channels"
208   *
209   * The single change to the internal datastructures of the core. Every
210   * channel now maintains a reference to the channel he is stacked upon.
211   * This reference is NULL for normal channels. Only the two exported
212   * procedures (Tcl_ReplaceChannel and Tcl_UndoReplaceChannel, see at the
213   * end of 'tcl.h') use this field in a non-trivial way.
214   *
215   * Of the existing procedures the only following are affected by this
216   * change:
217   *
218   * - Tcl_RegisterChannel
219   * - Tcl_CreateChannel
220   * - CloseChannel
221   *
222   * The why is explained at the changed locations.
223   */
224
225  struct Channel* supercedes; /* Refers to channel this one was stacked upon */
226
227} Channel;
228
229/*
230 * Values for the flags field in Channel. Any ORed combination of the
231 * following flags can be stored in the field. These flags record various
232 * options and state bits about the channel. In addition to the flags below,
233 * the channel can also have TCL_READABLE (1<<1) and TCL_WRITABLE (1<<2) set.
234 */
235
236#define CHANNEL_NONBLOCKING	(1<<3)	/* Channel is currently in
237					 * nonblocking mode. */
238#define CHANNEL_LINEBUFFERED	(1<<4)	/* Output to the channel must be
239					 * flushed after every newline. */
240#define CHANNEL_UNBUFFERED	(1<<5)	/* Output to the channel must always
241					 * be flushed immediately. */
242#define BUFFER_READY		(1<<6)	/* Current output buffer (the
243					 * curOutPtr field in the
244                                         * channel structure) should be
245                                         * output as soon as possible even
246                                         * though it may not be full. */
247#define BG_FLUSH_SCHEDULED	(1<<7)	/* A background flush of the
248					 * queued output buffers has been
249                                         * scheduled. */
250#define CHANNEL_CLOSED		(1<<8)	/* Channel has been closed. No
251					 * further Tcl-level IO on the
252                                         * channel is allowed. */
253#define CHANNEL_EOF		(1<<9)	/* EOF occurred on this channel.
254					 * This bit is cleared before every
255                                         * input operation. */
256#define CHANNEL_STICKY_EOF	(1<<10)	/* EOF occurred on this channel because
257					 * we saw the input eofChar. This bit
258                                         * prevents clearing of the EOF bit
259                                         * before every input operation. */
260#define CHANNEL_BLOCKED	(1<<11)	/* EWOULDBLOCK or EAGAIN occurred
261					 * on this channel. This bit is
262                                         * cleared before every input or
263                                         * output operation. */
264#define INPUT_SAW_CR		(1<<12)	/* Channel is in CRLF eol input
265					 * translation mode and the last
266                                         * byte seen was a "\r". */
267#define INPUT_NEED_NL		(1<<15)	/* Saw a '\r' at end of last buffer,
268					 * and there should be a '\n' at
269					 * beginning of next buffer. */
270#define CHANNEL_DEAD		(1<<13)	/* The channel has been closed by
271					 * the exit handler (on exit) but
272                                         * not deallocated. When any IO
273                                         * operation sees this flag on a
274                                         * channel, it does not call driver
275                                         * level functions to avoid referring
276                                         * to deallocated data. */
277#define CHANNEL_NEED_MORE_DATA	(1<<14)	/* The last input operation failed
278					 * because there was not enough data
279					 * to complete the operation.  This
280					 * flag is set when gets fails to
281					 * get a complete line or when read
282					 * fails to get a complete character.
283					 * When set, file events will not be
284					 * delivered for buffered data until
285					 * the state of the channel changes. */
286/* Andreas Kupries <a.kupries@westend.com>, 05/31/1997.
287 * "Trf-Patch for channels with a switchable byteorder"
288 */
289#define CHANNEL_IS_SMALLENDIAN	(1<<16)	/* Multibyte words are stored with MSB last  */
290
291/*
292 * For each channel handler registered in a call to Tcl_CreateChannelHandler,
293 * there is one record of the following type. All of records for a specific
294 * channel are chained together in a singly linked list which is stored in
295 * the channel structure.
296 */
297
298typedef struct ChannelHandler {
299    Channel *chanPtr;		/* The channel structure for this channel. */
300    int mask;			/* Mask of desired events. */
301    Tcl_ChannelProc *proc;	/* Procedure to call in the type of
302                                 * Tcl_CreateChannelHandler. */
303    ClientData clientData;	/* Argument to pass to procedure. */
304    struct ChannelHandler *nextPtr;
305    				/* Next one in list of registered handlers. */
306} ChannelHandler;
307
308/*
309 * This structure keeps track of the current ChannelHandler being invoked in
310 * the current invocation of ChannelHandlerEventProc. There is a potential
311 * problem if a ChannelHandler is deleted while it is the current one, since
312 * ChannelHandlerEventProc needs to look at the nextPtr field. To handle this
313 * problem, structures of the type below indicate the next handler to be
314 * processed for any (recursively nested) dispatches in progress. The
315 * nextHandlerPtr field is updated if the handler being pointed to is deleted.
316 * The nextPtr field is used to chain together all recursive invocations, so
317 * that Tcl_DeleteChannelHandler can find all the recursively nested
318 * invocations of ChannelHandlerEventProc and compare the handler being
319 * deleted against the NEXT handler to be invoked in that invocation; when it
320 * finds such a situation, Tcl_DeleteChannelHandler updates the nextHandlerPtr
321 * field of the structure to the next handler.
322 */
323
324typedef struct NextChannelHandler {
325    ChannelHandler *nextHandlerPtr;	/* The next handler to be invoked in
326                                         * this invocation. */
327    struct NextChannelHandler *nestedHandlerPtr;
328					/* Next nested invocation of
329                                         * ChannelHandlerEventProc. */
330} NextChannelHandler;
331
332
333/*
334 * The following structure describes the event that is added to the Tcl
335 * event queue by the channel handler check procedure.
336 */
337
338typedef struct ChannelHandlerEvent {
339    Tcl_Event header;		/* Standard header for all events. */
340    Channel *chanPtr;		/* The channel that is ready. */
341    int readyMask;		/* Events that have occurred. */
342} ChannelHandlerEvent;
343
344/*
345 * The following structure is used by Tcl_GetsObj() to encapsulates the
346 * state for a "gets" operation.
347 */
348
349typedef struct GetsState {
350    Tcl_Obj *objPtr;		/* The object to which UTF-8 characters
351				 * will be appended. */
352    char **dstPtr;		/* Pointer into objPtr's string rep where
353				 * next character should be stored. */
354    Tcl_Encoding encoding;	/* The encoding to use to convert raw bytes
355				 * to UTF-8.  */
356    ChannelBuffer *bufPtr;	/* The current buffer of raw bytes being
357				 * emptied. */
358    Tcl_EncodingState state;	/* The encoding state just before the last
359				 * external to UTF-8 conversion in
360				 * FilterInputBytes(). */
361    int rawRead;		/* The number of bytes removed from bufPtr
362				 * in the last call to FilterInputBytes(). */
363    int bytesWrote;		/* The number of bytes of UTF-8 data
364				 * appended to objPtr during the last call to
365				 * FilterInputBytes(). */
366    int charsWrote;		/* The corresponding number of UTF-8
367				 * characters appended to objPtr during the
368				 * last call to FilterInputBytes(). */
369    int totalChars;		/* The total number of UTF-8 characters
370				 * appended to objPtr so far, just before the
371				 * last call to FilterInputBytes(). */
372} GetsState;
373
374/*
375 * All static variables used in this file are collected into a single
376 * instance of the following structure.  For multi-threaded implementations,
377 * there is one instance of this structure for each thread.
378 *
379 * Notice that different structures with the same name appear in other
380 * files.  The structure defined below is used in this file only.
381 */
382
383typedef struct ThreadSpecificData {
384
385    /*
386     * This variable holds the list of nested ChannelHandlerEventProc
387     * invocations.
388     */
389    NextChannelHandler *nestedHandlerPtr;
390
391    /*
392     * List of all channels currently open.
393     */
394    Channel *firstChanPtr;
395#ifdef oldcode
396    /*
397     * Has a channel exit handler been created yet?
398     */
399    int channelExitHandlerCreated;
400
401    /*
402     * Has the channel event source been created and registered with the
403     * notifier?
404     */
405    int channelEventSourceCreated;
406#endif
407    /*
408     * Static variables to hold channels for stdin, stdout and stderr.
409     */
410    Tcl_Channel stdinChannel;
411    int stdinInitialized;
412    Tcl_Channel stdoutChannel;
413    int stdoutInitialized;
414    Tcl_Channel stderrChannel;
415    int stderrInitialized;
416
417} ThreadSpecificData;
418
419static Tcl_ThreadDataKey dataKey;
420
421
422/*
423 * Static functions in this file:
424 */
425
426static ChannelBuffer *	AllocChannelBuffer _ANSI_ARGS_((int length));
427static void		ChannelEventScriptInvoker _ANSI_ARGS_((
428			    ClientData clientData, int flags));
429static void		ChannelTimerProc _ANSI_ARGS_((
430			    ClientData clientData));
431static int		CheckChannelErrors _ANSI_ARGS_((Channel *chanPtr,
432			    int direction));
433static int		CheckFlush _ANSI_ARGS_((Channel *chanPtr,
434			    ChannelBuffer *bufPtr, int newlineFlag));
435static int		CheckForDeadChannel _ANSI_ARGS_((Tcl_Interp *interp,
436			    Channel *chan));
437static void		CheckForStdChannelsBeingClosed _ANSI_ARGS_((
438			    Tcl_Channel chan));
439static void		CleanupChannelHandlers _ANSI_ARGS_((
440			    Tcl_Interp *interp, Channel *chanPtr));
441static int		CloseChannel _ANSI_ARGS_((Tcl_Interp *interp,
442                            Channel *chanPtr, int errorCode));
443static void		CommonGetsCleanup _ANSI_ARGS_((Channel *chanPtr,
444			    Tcl_Encoding encoding));
445static int		CopyAndTranslateBuffer _ANSI_ARGS_((
446			    Channel *chanPtr, char *result, int space));
447static int		CopyData _ANSI_ARGS_((CopyState *csPtr, int mask));
448static void		CopyEventProc _ANSI_ARGS_((ClientData clientData,
449			    int mask));
450static void		CreateScriptRecord _ANSI_ARGS_((
451			    Tcl_Interp *interp, Channel *chanPtr,
452                            int mask, Tcl_Obj *scriptPtr));
453static void		DeleteChannelTable _ANSI_ARGS_((
454			    ClientData clientData, Tcl_Interp *interp));
455static void		DeleteScriptRecord _ANSI_ARGS_((Tcl_Interp *interp,
456        		    Channel *chanPtr, int mask));
457static void		DiscardInputQueued _ANSI_ARGS_((
458			    Channel *chanPtr, int discardSavedBuffers));
459static void		DiscardOutputQueued _ANSI_ARGS_((
460    			    Channel *chanPtr));
461static int		DoRead _ANSI_ARGS_((Channel *chanPtr, char *srcPtr,
462			    int slen));
463static int		DoWrite _ANSI_ARGS_((Channel *chanPtr, char *src,
464			    int srcLen));
465static int		FilterInputBytes _ANSI_ARGS_((Channel *chanPtr,
466			    GetsState *statePtr));
467static int		FlushChannel _ANSI_ARGS_((Tcl_Interp *interp,
468                            Channel *chanPtr, int calledFromAsyncFlush));
469static Tcl_HashTable *	GetChannelTable _ANSI_ARGS_((Tcl_Interp *interp));
470static int		GetInput _ANSI_ARGS_((Channel *chanPtr));
471static void		PeekAhead _ANSI_ARGS_((Channel *chanPtr,
472			    char **dstEndPtr, GetsState *gsPtr));
473static int		ReadBytes _ANSI_ARGS_((Channel *chanPtr,
474			    Tcl_Obj *objPtr, int charsLeft, int *offsetPtr));
475static int		ReadChars _ANSI_ARGS_((Channel *chanPtr,
476			    Tcl_Obj *objPtr, int charsLeft, int *offsetPtr,
477			    int *factorPtr));
478static void		RecycleBuffer _ANSI_ARGS_((Channel *chanPtr,
479		            ChannelBuffer *bufPtr, int mustDiscard));
480static int		SetBlockMode _ANSI_ARGS_((Tcl_Interp *interp,
481		            Channel *chanPtr, int mode));
482static void		StopCopy _ANSI_ARGS_((CopyState *csPtr));
483static int		TranslateInputEOL _ANSI_ARGS_((Channel *chanPtr,
484			    char *dst, CONST char *src, int *dstLenPtr,
485			    int *srcLenPtr));
486static int		TranslateOutputEOL _ANSI_ARGS_((Channel *chanPtr,
487			    char *dst, CONST char *src, int *dstLenPtr,
488			    int *srcLenPtr));
489static void		UpdateInterest _ANSI_ARGS_((Channel *chanPtr));
490static int		WriteBytes _ANSI_ARGS_((Channel *chanPtr,
491			    CONST char *src, int srcLen));
492static int		WriteChars _ANSI_ARGS_((Channel *chanPtr,
493			    CONST char *src, int srcLen));
494
495
496/*
497 *---------------------------------------------------------------------------
498 *
499 * TclInitIOSubsystem --
500 *
501 *	Initialize all resources used by this subsystem on a per-process
502 *	basis.
503 *
504 * Results:
505 *	None.
506 *
507 * Side effects:
508 *	Depends on the memory subsystems.
509 *
510 *---------------------------------------------------------------------------
511 */
512
513void
514TclInitIOSubsystem()
515{
516    /*
517     * By fetching thread local storage we take care of
518     * allocating it for each thread.
519     */
520    (void) TCL_TSD_INIT(&dataKey);
521}
522
523/*
524 *-------------------------------------------------------------------------
525 *
526 * TclFinalizeIOSubsystem --
527 *
528 *	Releases all resources used by this subsystem on a per-process
529 *	basis.  Closes all extant channels that have not already been
530 *	closed because they were not owned by any interp.
531 *
532 * Results:
533 *	None.
534 *
535 * Side effects:
536 *	Depends on encoding and memory subsystems.
537 *
538 *-------------------------------------------------------------------------
539 */
540
541	/* ARGSUSED */
542void
543TclFinalizeIOSubsystem()
544{
545    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
546    Channel *chanPtr;			/* Iterates over open channels. */
547    Channel *nextChanPtr;		/* Iterates over open channels. */
548
549
550    for (chanPtr = tsdPtr->firstChanPtr; chanPtr != (Channel *) NULL;
551             chanPtr = nextChanPtr) {
552        nextChanPtr = chanPtr->nextChanPtr;
553
554        /*
555         * Set the channel back into blocking mode to ensure that we wait
556         * for all data to flush out.
557         */
558
559        (void) Tcl_SetChannelOption(NULL, (Tcl_Channel) chanPtr,
560                "-blocking", "on");
561
562        if ((chanPtr == (Channel *) tsdPtr->stdinChannel) ||
563                (chanPtr == (Channel *) tsdPtr->stdoutChannel) ||
564                (chanPtr == (Channel *) tsdPtr->stderrChannel)) {
565
566            /*
567             * Decrement the refcount which was earlier artificially bumped
568             * up to keep the channel from being closed.
569             */
570
571            chanPtr->refCount--;
572        }
573
574        if (chanPtr->refCount <= 0) {
575
576	    /*
577             * Close it only if the refcount indicates that the channel is not
578             * referenced from any interpreter. If it is, that interpreter will
579             * close the channel when it gets destroyed.
580             */
581
582            (void) Tcl_Close((Tcl_Interp *) NULL, (Tcl_Channel) chanPtr);
583
584        } else {
585
586            /*
587             * The refcount is greater than zero, so flush the channel.
588             */
589
590            Tcl_Flush((Tcl_Channel) chanPtr);
591
592            /*
593             * Call the device driver to actually close the underlying
594             * device for this channel.
595             */
596
597	    if (chanPtr->typePtr->closeProc != TCL_CLOSE2PROC) {
598		(chanPtr->typePtr->closeProc)(chanPtr->instanceData,
599			(Tcl_Interp *) NULL);
600	    } else {
601		(chanPtr->typePtr->close2Proc)(chanPtr->instanceData,
602			(Tcl_Interp *) NULL, 0);
603	    }
604
605            /*
606             * Finally, we clean up the fields in the channel data structure
607             * since all of them have been deleted already. We mark the
608             * channel with CHANNEL_DEAD to prevent any further IO operations
609             * on it.
610             */
611
612            chanPtr->instanceData = (ClientData) NULL;
613            chanPtr->flags |= CHANNEL_DEAD;
614        }
615    }
616}
617
618
619
620/*
621 *----------------------------------------------------------------------
622 *
623 * Tcl_SetStdChannel --
624 *
625 *	This function is used to change the channels that are used
626 *	for stdin/stdout/stderr in new interpreters.
627 *
628 * Results:
629 *	None
630 *
631 * Side effects:
632 *	None.
633 *
634 *----------------------------------------------------------------------
635 */
636
637void
638Tcl_SetStdChannel(channel, type)
639    Tcl_Channel channel;
640    int type;			/* One of TCL_STDIN, TCL_STDOUT, TCL_STDERR. */
641{
642    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
643    switch (type) {
644	case TCL_STDIN:
645	    tsdPtr->stdinInitialized = 1;
646	    tsdPtr->stdinChannel = channel;
647	    break;
648	case TCL_STDOUT:
649	    tsdPtr->stdoutInitialized = 1;
650	    tsdPtr->stdoutChannel = channel;
651	    break;
652	case TCL_STDERR:
653	    tsdPtr->stderrInitialized = 1;
654	    tsdPtr->stderrChannel = channel;
655	    break;
656    }
657}
658
659/*
660 *----------------------------------------------------------------------
661 *
662 * Tcl_GetStdChannel --
663 *
664 *	Returns the specified standard channel.
665 *
666 * Results:
667 *	Returns the specified standard channel, or NULL.
668 *
669 * Side effects:
670 *	May cause the creation of a standard channel and the underlying
671 *	file.
672 *
673 *----------------------------------------------------------------------
674 */
675Tcl_Channel
676Tcl_GetStdChannel(type)
677    int type;			/* One of TCL_STDIN, TCL_STDOUT, TCL_STDERR. */
678{
679    Tcl_Channel channel = NULL;
680    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
681
682    /*
683     * If the channels were not created yet, create them now and
684     * store them in the static variables.
685     */
686
687    switch (type) {
688	case TCL_STDIN:
689	    if (!tsdPtr->stdinInitialized) {
690		tsdPtr->stdinChannel = TclpGetDefaultStdChannel(TCL_STDIN);
691		tsdPtr->stdinInitialized = 1;
692
693		/*
694                 * Artificially bump the refcount to ensure that the channel
695                 * is only closed on exit.
696                 *
697                 * NOTE: Must only do this if stdinChannel is not NULL. It
698                 * can be NULL in situations where Tcl is unable to connect
699                 * to the standard input.
700                 */
701
702                if (tsdPtr->stdinChannel != (Tcl_Channel) NULL) {
703                    (void) Tcl_RegisterChannel((Tcl_Interp *) NULL,
704                            tsdPtr->stdinChannel);
705                }
706	    }
707	    channel = tsdPtr->stdinChannel;
708	    break;
709	case TCL_STDOUT:
710	    if (!tsdPtr->stdoutInitialized) {
711		tsdPtr->stdoutChannel = TclpGetDefaultStdChannel(TCL_STDOUT);
712		tsdPtr->stdoutInitialized = 1;
713                if (tsdPtr->stdoutChannel != (Tcl_Channel) NULL) {
714                    (void) Tcl_RegisterChannel((Tcl_Interp *) NULL,
715                            tsdPtr->stdoutChannel);
716                }
717	    }
718	    channel = tsdPtr->stdoutChannel;
719	    break;
720	case TCL_STDERR:
721	    if (!tsdPtr->stderrInitialized) {
722		tsdPtr->stderrChannel = TclpGetDefaultStdChannel(TCL_STDERR);
723		tsdPtr->stderrInitialized = 1;
724                if (tsdPtr->stderrChannel != (Tcl_Channel) NULL) {
725                    (void) Tcl_RegisterChannel((Tcl_Interp *) NULL,
726                            tsdPtr->stderrChannel);
727                }
728	    }
729	    channel = tsdPtr->stderrChannel;
730	    break;
731    }
732    return channel;
733}
734
735
736/*
737 *----------------------------------------------------------------------
738 *
739 * Tcl_CreateCloseHandler
740 *
741 *	Creates a close callback which will be called when the channel is
742 *	closed.
743 *
744 * Results:
745 *	None.
746 *
747 * Side effects:
748 *	Causes the callback to be called in the future when the channel
749 *	will be closed.
750 *
751 *----------------------------------------------------------------------
752 */
753
754void
755Tcl_CreateCloseHandler(chan, proc, clientData)
756    Tcl_Channel chan;		/* The channel for which to create the
757                                 * close callback. */
758    Tcl_CloseProc *proc;	/* The callback routine to call when the
759                                 * channel will be closed. */
760    ClientData clientData;	/* Arbitrary data to pass to the
761                                 * close callback. */
762{
763    Channel *chanPtr;
764    CloseCallback *cbPtr;
765
766    chanPtr = (Channel *) chan;
767
768    cbPtr = (CloseCallback *) ckalloc((unsigned) sizeof(CloseCallback));
769    cbPtr->proc = proc;
770    cbPtr->clientData = clientData;
771
772    cbPtr->nextPtr = chanPtr->closeCbPtr;
773    chanPtr->closeCbPtr = cbPtr;
774}
775
776/*
777 *----------------------------------------------------------------------
778 *
779 * Tcl_DeleteCloseHandler --
780 *
781 *	Removes a callback that would have been called on closing
782 *	the channel. If there is no matching callback then this
783 *	function has no effect.
784 *
785 * Results:
786 *	None.
787 *
788 * Side effects:
789 *	The callback will not be called in the future when the channel
790 *	is eventually closed.
791 *
792 *----------------------------------------------------------------------
793 */
794
795void
796Tcl_DeleteCloseHandler(chan, proc, clientData)
797    Tcl_Channel chan;		/* The channel for which to cancel the
798                                 * close callback. */
799    Tcl_CloseProc *proc;	/* The procedure for the callback to
800                                 * remove. */
801    ClientData clientData;	/* The callback data for the callback
802                                 * to remove. */
803{
804    Channel *chanPtr;
805    CloseCallback *cbPtr, *cbPrevPtr;
806
807    chanPtr = (Channel *) chan;
808    for (cbPtr = chanPtr->closeCbPtr, cbPrevPtr = (CloseCallback *) NULL;
809             cbPtr != (CloseCallback *) NULL;
810             cbPtr = cbPtr->nextPtr) {
811        if ((cbPtr->proc == proc) && (cbPtr->clientData == clientData)) {
812            if (cbPrevPtr == (CloseCallback *) NULL) {
813                chanPtr->closeCbPtr = cbPtr->nextPtr;
814            }
815            ckfree((char *) cbPtr);
816            break;
817        } else {
818            cbPrevPtr = cbPtr;
819        }
820    }
821}
822
823/*
824 *----------------------------------------------------------------------
825 *
826 * GetChannelTable --
827 *
828 *	Gets and potentially initializes the channel table for an
829 *	interpreter. If it is initializing the table it also inserts
830 *	channels for stdin, stdout and stderr if the interpreter is
831 *	trusted.
832 *
833 * Results:
834 *	A pointer to the hash table created, for use by the caller.
835 *
836 * Side effects:
837 *	Initializes the channel table for an interpreter. May create
838 *	channels for stdin, stdout and stderr.
839 *
840 *----------------------------------------------------------------------
841 */
842
843static Tcl_HashTable *
844GetChannelTable(interp)
845    Tcl_Interp *interp;
846{
847    Tcl_HashTable *hTblPtr;	/* Hash table of channels. */
848    Tcl_Channel stdinChan, stdoutChan, stderrChan;
849
850    hTblPtr = (Tcl_HashTable *) Tcl_GetAssocData(interp, "tclIO", NULL);
851    if (hTblPtr == (Tcl_HashTable *) NULL) {
852        hTblPtr = (Tcl_HashTable *) ckalloc((unsigned) sizeof(Tcl_HashTable));
853        Tcl_InitHashTable(hTblPtr, TCL_STRING_KEYS);
854
855        (void) Tcl_SetAssocData(interp, "tclIO",
856                (Tcl_InterpDeleteProc *) DeleteChannelTable,
857                (ClientData) hTblPtr);
858
859        /*
860         * If the interpreter is trusted (not "safe"), insert channels
861         * for stdin, stdout and stderr (possibly creating them in the
862         * process).
863         */
864
865        if (Tcl_IsSafe(interp) == 0) {
866            stdinChan = Tcl_GetStdChannel(TCL_STDIN);
867            if (stdinChan != NULL) {
868                Tcl_RegisterChannel(interp, stdinChan);
869            }
870            stdoutChan = Tcl_GetStdChannel(TCL_STDOUT);
871            if (stdoutChan != NULL) {
872                Tcl_RegisterChannel(interp, stdoutChan);
873            }
874            stderrChan = Tcl_GetStdChannel(TCL_STDERR);
875            if (stderrChan != NULL) {
876                Tcl_RegisterChannel(interp, stderrChan);
877            }
878        }
879
880    }
881    return hTblPtr;
882}
883
884/*
885 *----------------------------------------------------------------------
886 *
887 * DeleteChannelTable --
888 *
889 *	Deletes the channel table for an interpreter, closing any open
890 *	channels whose refcount reaches zero. This procedure is invoked
891 *	when an interpreter is deleted, via the AssocData cleanup
892 *	mechanism.
893 *
894 * Results:
895 *	None.
896 *
897 * Side effects:
898 *	Deletes the hash table of channels. May close channels. May flush
899 *	output on closed channels. Removes any channeEvent handlers that were
900 *	registered in this interpreter.
901 *
902 *----------------------------------------------------------------------
903 */
904
905static void
906DeleteChannelTable(clientData, interp)
907    ClientData clientData;	/* The per-interpreter data structure. */
908    Tcl_Interp *interp;		/* The interpreter being deleted. */
909{
910    Tcl_HashTable *hTblPtr;	/* The hash table. */
911    Tcl_HashSearch hSearch;	/* Search variable. */
912    Tcl_HashEntry *hPtr;	/* Search variable. */
913    Channel *chanPtr;	/* Channel being deleted. */
914    EventScriptRecord *sPtr, *prevPtr, *nextPtr;
915    				/* Variables to loop over all channel events
916                                 * registered, to delete the ones that refer
917                                 * to the interpreter being deleted. */
918
919    /*
920     * Delete all the registered channels - this will close channels whose
921     * refcount reaches zero.
922     */
923
924    hTblPtr = (Tcl_HashTable *) clientData;
925    for (hPtr = Tcl_FirstHashEntry(hTblPtr, &hSearch);
926             hPtr != (Tcl_HashEntry *) NULL;
927             hPtr = Tcl_FirstHashEntry(hTblPtr, &hSearch)) {
928
929        chanPtr = (Channel *) Tcl_GetHashValue(hPtr);
930
931        /*
932         * Remove any fileevents registered in this interpreter.
933         */
934
935        for (sPtr = chanPtr->scriptRecordPtr,
936                 prevPtr = (EventScriptRecord *) NULL;
937                 sPtr != (EventScriptRecord *) NULL;
938                 sPtr = nextPtr) {
939            nextPtr = sPtr->nextPtr;
940            if (sPtr->interp == interp) {
941                if (prevPtr == (EventScriptRecord *) NULL) {
942                    chanPtr->scriptRecordPtr = nextPtr;
943                } else {
944                    prevPtr->nextPtr = nextPtr;
945                }
946
947                Tcl_DeleteChannelHandler((Tcl_Channel) chanPtr,
948                        ChannelEventScriptInvoker, (ClientData) sPtr);
949
950		Tcl_DecrRefCount(sPtr->scriptPtr);
951                ckfree((char *) sPtr);
952            } else {
953                prevPtr = sPtr;
954            }
955        }
956
957        /*
958         * Cannot call Tcl_UnregisterChannel because that procedure calls
959         * Tcl_GetAssocData to get the channel table, which might already
960         * be inaccessible from the interpreter structure. Instead, we
961         * emulate the behavior of Tcl_UnregisterChannel directly here.
962         */
963
964        Tcl_DeleteHashEntry(hPtr);
965        chanPtr->refCount--;
966        if (chanPtr->refCount <= 0) {
967            if (!(chanPtr->flags & BG_FLUSH_SCHEDULED)) {
968                (void) Tcl_Close(interp, (Tcl_Channel) chanPtr);
969            }
970        }
971    }
972    Tcl_DeleteHashTable(hTblPtr);
973    ckfree((char *) hTblPtr);
974}
975
976/*
977 *----------------------------------------------------------------------
978 *
979 * CheckForStdChannelsBeingClosed --
980 *
981 *	Perform special handling for standard channels being closed. When
982 *	given a standard channel, if the refcount is now 1, it means that
983 *	the last reference to the standard channel is being explicitly
984 *	closed. Now bump the refcount artificially down to 0, to ensure the
985 *	normal handling of channels being closed will occur. Also reset the
986 *	static pointer to the channel to NULL, to avoid dangling references.
987 *
988 * Results:
989 *	None.
990 *
991 * Side effects:
992 *	Manipulates the refcount on standard channels. May smash the global
993 *	static pointer to a standard channel.
994 *
995 *----------------------------------------------------------------------
996 */
997
998static void
999CheckForStdChannelsBeingClosed(chan)
1000    Tcl_Channel chan;
1001{
1002    Channel *chanPtr = (Channel *) chan;
1003    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1004
1005    if ((chan == tsdPtr->stdinChannel) && (tsdPtr->stdinInitialized)) {
1006        if (chanPtr->refCount < 2) {
1007            chanPtr->refCount = 0;
1008            tsdPtr->stdinChannel = NULL;
1009            return;
1010        }
1011    } else if ((chan == tsdPtr->stdoutChannel) && (tsdPtr->stdoutInitialized)) {
1012        if (chanPtr->refCount < 2) {
1013            chanPtr->refCount = 0;
1014            tsdPtr->stdoutChannel = NULL;
1015            return;
1016        }
1017    } else if ((chan == tsdPtr->stderrChannel) && (tsdPtr->stderrInitialized)) {
1018        if (chanPtr->refCount < 2) {
1019            chanPtr->refCount = 0;
1020            tsdPtr->stderrChannel = NULL;
1021            return;
1022        }
1023    }
1024}
1025
1026/*
1027 *----------------------------------------------------------------------
1028 *
1029 * Tcl_RegisterChannel --
1030 *
1031 *	Adds an already-open channel to the channel table of an interpreter.
1032 *	If the interpreter passed as argument is NULL, it only increments
1033 *	the channel refCount.
1034 *
1035 * Results:
1036 *	None.
1037 *
1038 * Side effects:
1039 *	May increment the reference count of a channel.
1040 *
1041 *----------------------------------------------------------------------
1042 */
1043
1044void
1045Tcl_RegisterChannel(interp, chan)
1046    Tcl_Interp *interp;		/* Interpreter in which to add the channel. */
1047    Tcl_Channel chan;		/* The channel to add to this interpreter
1048                                 * channel table. */
1049{
1050    Tcl_HashTable *hTblPtr;	/* Hash table of channels. */
1051    Tcl_HashEntry *hPtr;	/* Search variable. */
1052    int new;			/* Is the hash entry new or does it exist? */
1053    Channel *chanPtr;		/* The actual channel. */
1054
1055    chanPtr = (Channel *) chan;
1056
1057    if (chanPtr->channelName == (char *) NULL) {
1058        panic("Tcl_RegisterChannel: channel without name");
1059    }
1060    if (interp != (Tcl_Interp *) NULL) {
1061        hTblPtr = GetChannelTable(interp);
1062        hPtr = Tcl_CreateHashEntry(hTblPtr, chanPtr->channelName, &new);
1063        if (new == 0) {
1064            if (chan == (Tcl_Channel) Tcl_GetHashValue(hPtr)) {
1065                return;
1066            }
1067
1068	    /* Andreas Kupries <a.kupries@westend.com>, 12/13/1998
1069	     * "Trf-Patch for filtering channels"
1070	     *
1071	     * This is the change to 'Tcl_RegisterChannel'.
1072	     *
1073	     * Explanation:
1074	     *		The moment a channel is stacked upon another he
1075	     *		takes the identity of the channel he supercedes,
1076	     *		i.e. he gets the *same* name. Because of this we
1077	     *		cannot check for duplicate names anymore, they
1078	     *		have to be allowed now.
1079	     */
1080
1081	    /* panic("Tcl_RegisterChannel: duplicate channel names"); */
1082        }
1083        Tcl_SetHashValue(hPtr, (ClientData) chanPtr);
1084    }
1085    chanPtr->refCount++;
1086}
1087
1088/*
1089 *----------------------------------------------------------------------
1090 *
1091 * Tcl_UnregisterChannel --
1092 *
1093 *	Deletes the hash entry for a channel associated with an interpreter.
1094 *	If the interpreter given as argument is NULL, it only decrements the
1095 *	reference count.
1096 *
1097 * Results:
1098 *	A standard Tcl result.
1099 *
1100 * Side effects:
1101 *	Deletes the hash entry for a channel associated with an interpreter.
1102 *
1103 *----------------------------------------------------------------------
1104 */
1105
1106int
1107Tcl_UnregisterChannel(interp, chan)
1108    Tcl_Interp *interp;		/* Interpreter in which channel is defined. */
1109    Tcl_Channel chan;		/* Channel to delete. */
1110{
1111    Tcl_HashTable *hTblPtr;	/* Hash table of channels. */
1112    Tcl_HashEntry *hPtr;	/* Search variable. */
1113    Channel *chanPtr;		/* The real IO channel. */
1114
1115    chanPtr = (Channel *) chan;
1116
1117    if (interp != (Tcl_Interp *) NULL) {
1118        hTblPtr = (Tcl_HashTable *) Tcl_GetAssocData(interp, "tclIO", NULL);
1119        if (hTblPtr == (Tcl_HashTable *) NULL) {
1120            return TCL_OK;
1121        }
1122        hPtr = Tcl_FindHashEntry(hTblPtr, chanPtr->channelName);
1123        if (hPtr == (Tcl_HashEntry *) NULL) {
1124            return TCL_OK;
1125        }
1126        if ((Channel *) Tcl_GetHashValue(hPtr) != chanPtr) {
1127            return TCL_OK;
1128        }
1129        Tcl_DeleteHashEntry(hPtr);
1130
1131        /*
1132         * Remove channel handlers that refer to this interpreter, so that they
1133         * will not be present if the actual close is delayed and more events
1134         * happen on the channel. This may occur if the channel is shared
1135         * between several interpreters, or if the channel has async
1136         * flushing active.
1137         */
1138
1139        CleanupChannelHandlers(interp, chanPtr);
1140    }
1141
1142    chanPtr->refCount--;
1143
1144    /*
1145     * Perform special handling for standard channels being closed. If the
1146     * refCount is now 1 it means that the last reference to the standard
1147     * channel is being explicitly closed, so bump the refCount down
1148     * artificially to 0. This will ensure that the channel is actually
1149     * closed, below. Also set the static pointer to NULL for the channel.
1150     */
1151
1152    CheckForStdChannelsBeingClosed(chan);
1153
1154    /*
1155     * If the refCount reached zero, close the actual channel.
1156     */
1157
1158    if (chanPtr->refCount <= 0) {
1159
1160        /*
1161         * Ensure that if there is another buffer, it gets flushed
1162         * whether or not we are doing a background flush.
1163         */
1164
1165        if ((chanPtr->curOutPtr != NULL) &&
1166                (chanPtr->curOutPtr->nextAdded >
1167                        chanPtr->curOutPtr->nextRemoved)) {
1168            chanPtr->flags |= BUFFER_READY;
1169        }
1170        chanPtr->flags |= CHANNEL_CLOSED;
1171        if (!(chanPtr->flags & BG_FLUSH_SCHEDULED)) {
1172            if (Tcl_Close(interp, chan) != TCL_OK) {
1173                return TCL_ERROR;
1174            }
1175        }
1176    }
1177    return TCL_OK;
1178}
1179
1180/*
1181 *---------------------------------------------------------------------------
1182 *
1183 * Tcl_GetChannel --
1184 *
1185 *	Finds an existing Tcl_Channel structure by name in a given
1186 *	interpreter. This function is public because it is used by
1187 *	channel-type-specific functions.
1188 *
1189 * Results:
1190 *	A Tcl_Channel or NULL on failure. If failed, interp's result
1191 *	object contains an error message.  *modePtr is filled with the
1192 *	modes in which the channel was opened.
1193 *
1194 * Side effects:
1195 *	None.
1196 *
1197 *---------------------------------------------------------------------------
1198 */
1199
1200Tcl_Channel
1201Tcl_GetChannel(interp, chanName, modePtr)
1202    Tcl_Interp *interp;		/* Interpreter in which to find or create
1203                                 * the channel. */
1204    char *chanName;		/* The name of the channel. */
1205    int *modePtr;		/* Where to store the mode in which the
1206                                 * channel was opened? Will contain an ORed
1207                                 * combination of TCL_READABLE and
1208                                 * TCL_WRITABLE, if non-NULL. */
1209{
1210    Channel *chanPtr;		/* The actual channel. */
1211    Tcl_HashTable *hTblPtr;	/* Hash table of channels. */
1212    Tcl_HashEntry *hPtr;	/* Search variable. */
1213    char *name;			/* Translated name. */
1214
1215    /*
1216     * Substitute "stdin", etc.  Note that even though we immediately
1217     * find the channel using Tcl_GetStdChannel, we still need to look
1218     * it up in the specified interpreter to ensure that it is present
1219     * in the channel table.  Otherwise, safe interpreters would always
1220     * have access to the standard channels.
1221     */
1222
1223    name = chanName;
1224    if ((chanName[0] == 's') && (chanName[1] == 't')) {
1225	chanPtr = NULL;
1226	if (strcmp(chanName, "stdin") == 0) {
1227	    chanPtr = (Channel *)Tcl_GetStdChannel(TCL_STDIN);
1228	} else if (strcmp(chanName, "stdout") == 0) {
1229	    chanPtr = (Channel *)Tcl_GetStdChannel(TCL_STDOUT);
1230	} else if (strcmp(chanName, "stderr") == 0) {
1231	    chanPtr = (Channel *)Tcl_GetStdChannel(TCL_STDERR);
1232	}
1233	if (chanPtr != NULL) {
1234	    name = chanPtr->channelName;
1235	}
1236    }
1237
1238    hTblPtr = GetChannelTable(interp);
1239    hPtr = Tcl_FindHashEntry(hTblPtr, name);
1240    if (hPtr == (Tcl_HashEntry *) NULL) {
1241        Tcl_AppendResult(interp, "can not find channel named \"",
1242                chanName, "\"", (char *) NULL);
1243        return NULL;
1244    }
1245
1246    chanPtr = (Channel *) Tcl_GetHashValue(hPtr);
1247    if (modePtr != NULL) {
1248        *modePtr = (chanPtr->flags & (TCL_READABLE|TCL_WRITABLE));
1249    }
1250
1251    return (Tcl_Channel) chanPtr;
1252}
1253
1254/*
1255 *----------------------------------------------------------------------
1256 *
1257 * Tcl_CreateChannel --
1258 *
1259 *	Creates a new entry in the hash table for a Tcl_Channel
1260 *	record.
1261 *
1262 * Results:
1263 *	Returns the new Tcl_Channel.
1264 *
1265 * Side effects:
1266 *	Creates a new Tcl_Channel instance and inserts it into the
1267 *	hash table.
1268 *
1269 *----------------------------------------------------------------------
1270 */
1271
1272Tcl_Channel
1273Tcl_CreateChannel(typePtr, chanName, instanceData, mask)
1274    Tcl_ChannelType *typePtr;	/* The channel type record. */
1275    char *chanName;		/* Name of channel to record. */
1276    ClientData instanceData;	/* Instance specific data. */
1277    int mask;			/* TCL_READABLE & TCL_WRITABLE to indicate
1278                                 * if the channel is readable, writable. */
1279{
1280    Channel *chanPtr;		/* The channel structure newly created. */
1281    CONST char *name;
1282    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1283
1284    /* Andreas Kupries <a.kupries@westend.com>, 05/31/1997.
1285     * "Trf-Patch for channels with a switchable byteorder"
1286     * Location: Tcl_CreateChannel.
1287     */
1288    union {
1289	char c[sizeof(short)];
1290	short s;
1291    } order;
1292
1293    order.s = 1;
1294    if (order.c[0] == 1) {
1295	mask |= CHANNEL_IS_SMALLENDIAN;
1296    }
1297
1298    chanPtr = (Channel *) ckalloc((unsigned) sizeof(Channel));
1299
1300    if (chanName != (char *) NULL) {
1301        chanPtr->channelName = ckalloc((unsigned) (strlen(chanName) + 1));
1302        strcpy(chanPtr->channelName, chanName);
1303    } else {
1304        panic("Tcl_CreateChannel: NULL channel name");
1305    }
1306
1307    chanPtr->flags = mask;
1308
1309    /*
1310     * Set the channel to system default encoding.
1311     */
1312
1313    chanPtr->encoding = NULL;
1314    name = Tcl_GetEncodingName(NULL);
1315    if (strcmp(name, "binary") != 0) {
1316    	chanPtr->encoding = Tcl_GetEncoding(NULL, name);
1317    }
1318    chanPtr->inputEncodingState = NULL;
1319    chanPtr->inputEncodingFlags = TCL_ENCODING_START;
1320    chanPtr->outputEncodingState = NULL;
1321    chanPtr->outputEncodingFlags = TCL_ENCODING_START;
1322
1323    /*
1324     * Set the channel up initially in AUTO input translation mode to
1325     * accept "\n", "\r" and "\r\n". Output translation mode is set to
1326     * a platform specific default value. The eofChar is set to 0 for both
1327     * input and output, so that Tcl does not look for an in-file EOF
1328     * indicator (e.g. ^Z) and does not append an EOF indicator to files.
1329     */
1330
1331    chanPtr->inputTranslation = TCL_TRANSLATE_AUTO;
1332    chanPtr->outputTranslation = TCL_PLATFORM_TRANSLATION;
1333    chanPtr->inEofChar = 0;
1334    chanPtr->outEofChar = 0;
1335
1336    chanPtr->unreportedError = 0;
1337    chanPtr->instanceData = instanceData;
1338    chanPtr->typePtr = typePtr;
1339    chanPtr->refCount = 0;
1340    chanPtr->closeCbPtr = (CloseCallback *) NULL;
1341    chanPtr->curOutPtr = (ChannelBuffer *) NULL;
1342    chanPtr->outQueueHead = (ChannelBuffer *) NULL;
1343    chanPtr->outQueueTail = (ChannelBuffer *) NULL;
1344    chanPtr->saveInBufPtr = (ChannelBuffer *) NULL;
1345    chanPtr->inQueueHead = (ChannelBuffer *) NULL;
1346    chanPtr->inQueueTail = (ChannelBuffer *) NULL;
1347    chanPtr->chPtr = (ChannelHandler *) NULL;
1348    chanPtr->interestMask = 0;
1349    chanPtr->scriptRecordPtr = (EventScriptRecord *) NULL;
1350    chanPtr->bufSize = CHANNELBUFFER_DEFAULT_SIZE;
1351    chanPtr->timer = NULL;
1352    chanPtr->csPtr = NULL;
1353
1354    /* Andreas Kupries <a.kupries@westend.com>, 12/13/1998
1355     * "Trf-Patch for filtering channels"
1356     *
1357     * This is the change to 'Tcl_CreateChannel'.
1358     *
1359     * Explanation:
1360     *	It is of course necessary to initialize the new field
1361     *	in the Channel structure. The chosen value indicates
1362     *	that the created channel is a normal one, and not
1363     *	stacked upon another.
1364     */
1365
1366    chanPtr->supercedes = (Channel*) NULL;
1367
1368    chanPtr->outputStage = NULL;
1369    if ((chanPtr->encoding != NULL) && (chanPtr->flags & TCL_WRITABLE)) {
1370	chanPtr->outputStage = (char *)
1371		ckalloc((unsigned) (chanPtr->bufSize + 2));
1372    }
1373
1374    /*
1375     * Link the channel into the list of all channels; create an on-exit
1376     * handler if there is not one already, to close off all the channels
1377     * in the list on exit.
1378     */
1379
1380    chanPtr->nextChanPtr = tsdPtr->firstChanPtr;
1381    tsdPtr->firstChanPtr = chanPtr;
1382
1383    /*
1384     * Install this channel in the first empty standard channel slot, if
1385     * the channel was previously closed explicitly.
1386     */
1387
1388    if ((tsdPtr->stdinChannel == NULL) && (tsdPtr->stdinInitialized == 1)) {
1389	Tcl_SetStdChannel((Tcl_Channel)chanPtr, TCL_STDIN);
1390        Tcl_RegisterChannel((Tcl_Interp *) NULL, (Tcl_Channel) chanPtr);
1391    } else if ((tsdPtr->stdoutChannel == NULL) && (tsdPtr->stdoutInitialized == 1)) {
1392	Tcl_SetStdChannel((Tcl_Channel)chanPtr, TCL_STDOUT);
1393        Tcl_RegisterChannel((Tcl_Interp *) NULL, (Tcl_Channel) chanPtr);
1394    } else if ((tsdPtr->stderrChannel == NULL) && (tsdPtr->stderrInitialized == 1)) {
1395	Tcl_SetStdChannel((Tcl_Channel)chanPtr, TCL_STDERR);
1396        Tcl_RegisterChannel((Tcl_Interp *) NULL, (Tcl_Channel) chanPtr);
1397    }
1398    return (Tcl_Channel) chanPtr;
1399}
1400
1401/* Andreas Kupries <a.kupries@westend.com>, 12/13/1998
1402 * "Trf-Patch for filtering channels"
1403 *
1404 * The following two procedures are the new, exported ones. They
1405 * - create a channel stacked upon an existing one and
1406 * - pop a stacked channel off, thus revealing the superceded one.
1407 *
1408 * Please read the following completely.
1409 */
1410
1411/*
1412 *----------------------------------------------------------------------
1413 *
1414 * Tcl_ReplaceChannel --
1415 *
1416 *	Replaces an entry in the hash table for a Tcl_Channel
1417 *	record. The replacement is a new channel with same name,
1418 *	it supercedes the replaced channel. Input and output of
1419 *	the superceded channel is now going through the newly
1420 *	created channel and allows the arbitrary filtering/manipulation
1421 *	of the dataflow.
1422 *
1423 * Results:
1424 *	Returns the new Tcl_Channel.
1425 *
1426 * Side effects:
1427 *	See above.
1428 *
1429 *----------------------------------------------------------------------
1430 */
1431
1432Tcl_Channel
1433Tcl_ReplaceChannel(interp, typePtr, instanceData, mask, prevChan)
1434    Tcl_Interp*      interp;       /* The interpreter we are working in */
1435    Tcl_ChannelType *typePtr;	   /* The channel type record for the new
1436				    * channel. */
1437    ClientData       instanceData; /* Instance specific data for the new
1438				    * channel. */
1439    int              mask;	   /* TCL_READABLE & TCL_WRITABLE to indicate
1440				    * if the channel is readable, writable. */
1441    Tcl_Channel      prevChan;	   /* The channel structure to replace */
1442{
1443  ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1444  Channel            *chanPtr, *pt, *prevPt;
1445
1446  /*
1447   * Find the given channel in the list of all channels, compute enough
1448   * information to allow easy removal after the conditions are met.
1449   */
1450
1451  prevPt = (Channel*) NULL;
1452  pt     = (Channel*) tsdPtr->firstChanPtr;
1453
1454  while (pt != (Channel *) prevChan) {
1455    prevPt = pt;
1456    pt     = pt->nextChanPtr;
1457  }
1458
1459  /*
1460   * 'pt == prevChan' now
1461   */
1462
1463  if (!pt) {
1464    return (Tcl_Channel) NULL;
1465  }
1466
1467  /*
1468   * Here we check if the given "mask" matches the "flags"
1469   * of the already existing channel.
1470   *
1471   *	  | - | R | W | RW |
1472   *	--+---+---+---+----+	<=>  0 != (chan->mask & prevChan->mask)
1473   *	- |   |   |   |    |
1474   *	R |   | + |   | +  |	The superceding channel is allowed to
1475   *	W |   |   | + | +  |	restrict the capabilities of the
1476   *	RW|   | + | + | +  |	superceded one !
1477   *	--+---+---+---+----+
1478   */
1479
1480  if ((mask & Tcl_GetChannelMode (prevChan)) == 0) {
1481    return (Tcl_Channel) NULL;
1482  }
1483
1484
1485  chanPtr = (Channel *) ckalloc((unsigned) sizeof(Channel));
1486  chanPtr->flags = mask;
1487
1488  /*
1489   * Set the channel up initially in no Input translation mode and
1490   * no Output translation mode.
1491   */
1492
1493  chanPtr->inputTranslation = TCL_TRANSLATE_LF;
1494  chanPtr->outputTranslation = TCL_TRANSLATE_LF;
1495  chanPtr->inEofChar = 0;
1496  chanPtr->outEofChar = 0;
1497
1498  chanPtr->unreportedError = 0;
1499  chanPtr->instanceData = instanceData;
1500  chanPtr->typePtr = typePtr;
1501  chanPtr->refCount = 0;
1502  chanPtr->closeCbPtr = (CloseCallback *) NULL;
1503  chanPtr->curOutPtr = (ChannelBuffer *) NULL;
1504  chanPtr->outQueueHead = (ChannelBuffer *) NULL;
1505  chanPtr->outQueueTail = (ChannelBuffer *) NULL;
1506  chanPtr->saveInBufPtr = (ChannelBuffer *) NULL;
1507  chanPtr->inQueueHead = (ChannelBuffer *) NULL;
1508  chanPtr->inQueueTail = (ChannelBuffer *) NULL;
1509  chanPtr->chPtr = (ChannelHandler *) NULL;
1510  chanPtr->interestMask = 0;
1511  chanPtr->scriptRecordPtr = (EventScriptRecord *) NULL;
1512  chanPtr->bufSize = CHANNELBUFFER_DEFAULT_SIZE;
1513  chanPtr->timer = NULL;
1514  chanPtr->csPtr = NULL;
1515
1516  /* 06/12/1998: New for Tcl 8.1
1517   *
1518   * Take over the encoding from the superceded channel, so that it will be
1519   * executed in the future despite the replacement, and at the proper time
1520   * (*after* / *before* our transformation, depending on the direction of
1521   * the dataflow).
1522   *
1523   * *Important*
1524   * The I/O functionality of the filtering channel has to use 'Tcl_Read' to
1525   * get at the underlying information. This will circumvent the de/encoder
1526   * stage [*] in the superceded channel and removes the need to trouble
1527   * ourselves with 'ByteArray's too.
1528   *
1529   * [*] I'm talking about the conversion between UNICODE and other
1530   *     representations, like ASCII.
1531   */
1532
1533  chanPtr->encoding            = pt->encoding;
1534  chanPtr->inputEncodingState  = pt->inputEncodingState;
1535  chanPtr->inputEncodingFlags  = pt->inputEncodingFlags;
1536  chanPtr->outputEncodingState = pt->outputEncodingState;
1537  chanPtr->outputEncodingFlags = pt->outputEncodingFlags;
1538
1539  chanPtr->outputStage = NULL;
1540
1541  if ((chanPtr->encoding != NULL) && (chanPtr->flags & TCL_WRITABLE)) {
1542    chanPtr->outputStage = (char *)
1543      ckalloc((unsigned) (chanPtr->bufSize + 2));
1544  }
1545
1546  chanPtr->supercedes = (Channel*) prevChan;
1547
1548  chanPtr->channelName = (char *) ckalloc (strlen(pt->channelName)+1);
1549  strcpy (chanPtr->channelName, pt->channelName);
1550
1551  if (prevPt) {
1552    prevPt->nextChanPtr = chanPtr;
1553  } else {
1554    tsdPtr->firstChanPtr = chanPtr;
1555  }
1556
1557  chanPtr->nextChanPtr = pt->nextChanPtr;
1558
1559  Tcl_RegisterChannel (interp, (Tcl_Channel) chanPtr);
1560
1561  /*
1562   * The superceded channel is effectively unregistered
1563   */
1564
1565  /*chanPtr->supercedes->refCount --;*/
1566
1567  return (Tcl_Channel) chanPtr;
1568}
1569
1570/*
1571 *----------------------------------------------------------------------
1572 *
1573 * Tcl_UndoReplaceChannel --
1574 *
1575 *	Unstacks an entry in the hash table for a Tcl_Channel
1576 *	record. This is the reverse to 'Tcl_ReplaceChannel'.
1577 *	The old, superceded channel is uncovered and re-registered
1578 *	in the appropriate datastructures.
1579 *
1580 * Results:
1581 *	Returns the old Tcl_Channel, i.e. the one which was stacked over.
1582 *
1583 * Side effects:
1584 *	See above.
1585 *
1586 *----------------------------------------------------------------------
1587 */
1588
1589void
1590Tcl_UndoReplaceChannel (interp, chan)
1591Tcl_Interp* interp; /* The interpreter we are working in */
1592Tcl_Channel chan;   /* The channel to unstack */
1593{
1594  ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1595  Channel* chanPtr = (Channel*) chan;
1596
1597  if (chanPtr->supercedes != (Channel*) NULL) {
1598    Tcl_HashTable *hTblPtr;	/* Hash table of channels. */
1599    Tcl_HashEntry *hPtr;	/* Search variable. */
1600    int new;			/* Is the hash entry new or does it exist? */
1601
1602    /*
1603     * Insert the channel we were stacked upon back into
1604     * the list of open channels. Place it back into the hashtable too.
1605     * Correct 'refCount', as this actually unregisters 'chan'.
1606     */
1607
1608    chanPtr->supercedes->nextChanPtr = tsdPtr->firstChanPtr;
1609    tsdPtr->firstChanPtr             = chanPtr->supercedes;
1610
1611    hTblPtr = GetChannelTable (interp);
1612    hPtr    = Tcl_CreateHashEntry (hTblPtr, chanPtr->channelName, &new);
1613
1614    Tcl_SetHashValue(hPtr, (ClientData) chanPtr->supercedes);
1615    chanPtr->refCount --;
1616
1617    /*
1618     * The superceded channel is effectively registered again
1619     */
1620
1621    /*chanPtr->supercedes->refCount ++;*/
1622  }
1623
1624  /*
1625   * Disconnect the channels, then do a regular close upon the
1626   * stacked one, the filtering channel. This may cause flushing
1627   * of data into the superceded channel (if the filtering channel
1628   * ('chan') remembered its parent in itself).
1629   */
1630
1631  chanPtr->supercedes = NULL;
1632
1633  if (chanPtr->refCount == 0) {
1634    Tcl_Close (interp, chan);
1635  }
1636}
1637
1638/*
1639 *----------------------------------------------------------------------
1640 *
1641 * Tcl_GetChannelMode --
1642 *
1643 *	Computes a mask indicating whether the channel is open for
1644 *	reading and writing.
1645 *
1646 * Results:
1647 *	An OR-ed combination of TCL_READABLE and TCL_WRITABLE.
1648 *
1649 * Side effects:
1650 *	None.
1651 *
1652 *----------------------------------------------------------------------
1653 */
1654
1655int
1656Tcl_GetChannelMode(chan)
1657    Tcl_Channel chan;		/* The channel for which the mode is
1658                                 * being computed. */
1659{
1660    Channel *chanPtr;		/* The actual channel. */
1661
1662    chanPtr = (Channel *) chan;
1663    return (chanPtr->flags & (TCL_READABLE | TCL_WRITABLE));
1664}
1665
1666/*
1667 *----------------------------------------------------------------------
1668 *
1669 * Tcl_GetChannelName --
1670 *
1671 *	Returns the string identifying the channel name.
1672 *
1673 * Results:
1674 *	The string containing the channel name. This memory is
1675 *	owned by the generic layer and should not be modified by
1676 *	the caller.
1677 *
1678 * Side effects:
1679 *	None.
1680 *
1681 *----------------------------------------------------------------------
1682 */
1683
1684char *
1685Tcl_GetChannelName(chan)
1686    Tcl_Channel chan;		/* The channel for which to return the name. */
1687{
1688    Channel *chanPtr;		/* The actual channel. */
1689
1690    chanPtr = (Channel *) chan;
1691    return chanPtr->channelName;
1692}
1693
1694/*
1695 *----------------------------------------------------------------------
1696 *
1697 * Tcl_GetChannelType --
1698 *
1699 *	Given a channel structure, returns the channel type structure.
1700 *
1701 * Results:
1702 *	Returns a pointer to the channel type structure.
1703 *
1704 * Side effects:
1705 *	None.
1706 *
1707 *----------------------------------------------------------------------
1708 */
1709
1710Tcl_ChannelType *
1711Tcl_GetChannelType(chan)
1712    Tcl_Channel chan;		/* The channel to return type for. */
1713{
1714    Channel *chanPtr;		/* The actual channel. */
1715
1716    chanPtr = (Channel *) chan;
1717    return chanPtr->typePtr;
1718}
1719
1720/*
1721 *----------------------------------------------------------------------
1722 *
1723 * Tcl_GetChannelHandle --
1724 *
1725 *	Returns an OS handle associated with a channel.
1726 *
1727 * Results:
1728 *	Returns TCL_OK and places the handle in handlePtr, or returns
1729 *	TCL_ERROR on failure.
1730 *
1731 * Side effects:
1732 *	None.
1733 *
1734 *----------------------------------------------------------------------
1735 */
1736
1737int
1738Tcl_GetChannelHandle(chan, direction, handlePtr)
1739    Tcl_Channel chan;		/* The channel to get file from. */
1740    int direction;		/* TCL_WRITABLE or TCL_READABLE. */
1741    ClientData *handlePtr;	/* Where to store handle */
1742{
1743    Channel *chanPtr;		/* The actual channel. */
1744    ClientData handle;
1745    int result;
1746
1747    chanPtr = (Channel *) chan;
1748    result = (chanPtr->typePtr->getHandleProc)(chanPtr->instanceData,
1749	    direction, &handle);
1750    if (handlePtr) {
1751	*handlePtr = handle;
1752    }
1753    return result;
1754}
1755
1756/*
1757 *----------------------------------------------------------------------
1758 *
1759 * Tcl_GetChannelInstanceData --
1760 *
1761 *	Returns the client data associated with a channel.
1762 *
1763 * Results:
1764 *	The client data.
1765 *
1766 * Side effects:
1767 *	None.
1768 *
1769 *----------------------------------------------------------------------
1770 */
1771
1772ClientData
1773Tcl_GetChannelInstanceData(chan)
1774    Tcl_Channel chan;		/* Channel for which to return client data. */
1775{
1776    Channel *chanPtr;		/* The actual channel. */
1777
1778    chanPtr = (Channel *) chan;
1779    return chanPtr->instanceData;
1780}
1781
1782/*
1783 *---------------------------------------------------------------------------
1784 *
1785 * AllocChannelBuffer --
1786 *
1787 *	A channel buffer has BUFFER_PADDING bytes extra at beginning to
1788 *	hold any bytes of a native-encoding character that got split by
1789 *	the end of the previous buffer and need to be moved to the
1790 *	beginning of the next buffer to make a contiguous string so it
1791 *	can be converted to UTF-8.
1792 *
1793 *	A channel buffer has BUFFER_PADDING bytes extra at the end to
1794 *	hold any bytes of a native-encoding character (generated from a
1795 *	UTF-8 character) that overflow past the end of the buffer and
1796 *	need to be moved to the next buffer.
1797 *
1798 * Results:
1799 *	A newly allocated channel buffer.
1800 *
1801 * Side effects:
1802 *	None.
1803 *
1804 *---------------------------------------------------------------------------
1805 */
1806
1807static ChannelBuffer *
1808AllocChannelBuffer(length)
1809    int length;			/* Desired length of channel buffer. */
1810{
1811    ChannelBuffer *bufPtr;
1812    int n;
1813
1814    n = length + CHANNELBUFFER_HEADER_SIZE + BUFFER_PADDING + BUFFER_PADDING;
1815    bufPtr = (ChannelBuffer *) ckalloc((unsigned) n);
1816    bufPtr->nextAdded	= BUFFER_PADDING;
1817    bufPtr->nextRemoved	= BUFFER_PADDING;
1818    bufPtr->bufLength	= length + BUFFER_PADDING;
1819    bufPtr->nextPtr	= (ChannelBuffer *) NULL;
1820    return bufPtr;
1821}
1822
1823/*
1824 *----------------------------------------------------------------------
1825 *
1826 * RecycleBuffer --
1827 *
1828 *	Helper function to recycle input and output buffers. Ensures
1829 *	that two input buffers are saved (one in the input queue and
1830 *	another in the saveInBufPtr field) and that curOutPtr is set
1831 *	to a buffer. Only if these conditions are met is the buffer
1832 *	freed to the OS.
1833 *
1834 * Results:
1835 *	None.
1836 *
1837 * Side effects:
1838 *	May free a buffer to the OS.
1839 *
1840 *----------------------------------------------------------------------
1841 */
1842
1843static void
1844RecycleBuffer(chanPtr, bufPtr, mustDiscard)
1845    Channel *chanPtr;		/* Channel for which to recycle buffers. */
1846    ChannelBuffer *bufPtr;	/* The buffer to recycle. */
1847    int mustDiscard;		/* If nonzero, free the buffer to the
1848                                 * OS, always. */
1849{
1850    /*
1851     * Do we have to free the buffer to the OS?
1852     */
1853
1854    if (mustDiscard) {
1855        ckfree((char *) bufPtr);
1856        return;
1857    }
1858
1859    /*
1860     * Only save buffers for the input queue if the channel is readable.
1861     */
1862
1863    if (chanPtr->flags & TCL_READABLE) {
1864        if (chanPtr->inQueueHead == (ChannelBuffer *) NULL) {
1865            chanPtr->inQueueHead = bufPtr;
1866            chanPtr->inQueueTail = bufPtr;
1867            goto keepit;
1868        }
1869        if (chanPtr->saveInBufPtr == (ChannelBuffer *) NULL) {
1870            chanPtr->saveInBufPtr = bufPtr;
1871            goto keepit;
1872        }
1873    }
1874
1875    /*
1876     * Only save buffers for the output queue if the channel is writable.
1877     */
1878
1879    if (chanPtr->flags & TCL_WRITABLE) {
1880        if (chanPtr->curOutPtr == (ChannelBuffer *) NULL) {
1881            chanPtr->curOutPtr = bufPtr;
1882            goto keepit;
1883        }
1884    }
1885
1886    /*
1887     * If we reached this code we return the buffer to the OS.
1888     */
1889
1890    ckfree((char *) bufPtr);
1891    return;
1892
1893keepit:
1894    bufPtr->nextRemoved = BUFFER_PADDING;
1895    bufPtr->nextAdded = BUFFER_PADDING;
1896    bufPtr->nextPtr = (ChannelBuffer *) NULL;
1897}
1898
1899/*
1900 *----------------------------------------------------------------------
1901 *
1902 * DiscardOutputQueued --
1903 *
1904 *	Discards all output queued in the output queue of a channel.
1905 *
1906 * Results:
1907 *	None.
1908 *
1909 * Side effects:
1910 *	Recycles buffers.
1911 *
1912 *----------------------------------------------------------------------
1913 */
1914
1915static void
1916DiscardOutputQueued(chanPtr)
1917    Channel *chanPtr;		/* The channel for which to discard output. */
1918{
1919    ChannelBuffer *bufPtr;
1920
1921    while (chanPtr->outQueueHead != (ChannelBuffer *) NULL) {
1922        bufPtr = chanPtr->outQueueHead;
1923        chanPtr->outQueueHead = bufPtr->nextPtr;
1924        RecycleBuffer(chanPtr, bufPtr, 0);
1925    }
1926    chanPtr->outQueueHead = (ChannelBuffer *) NULL;
1927    chanPtr->outQueueTail = (ChannelBuffer *) NULL;
1928}
1929
1930/*
1931 *----------------------------------------------------------------------
1932 *
1933 * CheckForDeadChannel --
1934 *
1935 *	This function checks is a given channel is Dead.
1936 *      (A channel that has been closed but not yet deallocated.)
1937 *
1938 * Results:
1939 *	True (1) if channel is Dead, False (0) if channel is Ok
1940 *
1941 * Side effects:
1942 *      None
1943 *
1944 *----------------------------------------------------------------------
1945 */
1946
1947static int
1948CheckForDeadChannel(interp, chanPtr)
1949    Tcl_Interp *interp;		/* For error reporting (can be NULL) */
1950    Channel    *chanPtr;	/* The channel to check. */
1951{
1952    if (chanPtr->flags & CHANNEL_DEAD) {
1953        Tcl_SetErrno(EINVAL);
1954	if (interp) {
1955	    Tcl_AppendResult(interp,
1956			     "unable to access channel: invalid channel",
1957			     (char *) NULL);
1958	}
1959	return 1;
1960    }
1961    return 0;
1962}
1963
1964/*
1965 *----------------------------------------------------------------------
1966 *
1967 * FlushChannel --
1968 *
1969 *	This function flushes as much of the queued output as is possible
1970 *	now. If calledFromAsyncFlush is nonzero, it is being called in an
1971 *	event handler to flush channel output asynchronously.
1972 *
1973 * Results:
1974 *	0 if successful, else the error code that was returned by the
1975 *	channel type operation.
1976 *
1977 * Side effects:
1978 *	May produce output on a channel. May block indefinitely if the
1979 *	channel is synchronous. May schedule an async flush on the channel.
1980 *	May recycle memory for buffers in the output queue.
1981 *
1982 *----------------------------------------------------------------------
1983 */
1984
1985static int
1986FlushChannel(interp, chanPtr, calledFromAsyncFlush)
1987    Tcl_Interp *interp;			/* For error reporting during close. */
1988    Channel *chanPtr;			/* The channel to flush on. */
1989    int calledFromAsyncFlush;		/* If nonzero then we are being
1990                                         * called from an asynchronous
1991                                         * flush callback. */
1992{
1993    ChannelBuffer *bufPtr;		/* Iterates over buffered output
1994                                         * queue. */
1995    int toWrite;			/* Amount of output data in current
1996                                         * buffer available to be written. */
1997    int written;			/* Amount of output data actually
1998                                         * written in current round. */
1999    int errorCode = 0;			/* Stores POSIX error codes from
2000                                         * channel driver operations. */
2001    int wroteSome = 0;			/* Set to one if any data was
2002					 * written to the driver. */
2003
2004    /*
2005     * Prevent writing on a dead channel -- a channel that has been closed
2006     * but not yet deallocated. This can occur if the exit handler for the
2007     * channel deallocation runs before all channels are deregistered in
2008     * all interpreters.
2009     */
2010
2011    if (CheckForDeadChannel(interp,chanPtr)) return -1;
2012
2013    /*
2014     * Loop over the queued buffers and attempt to flush as
2015     * much as possible of the queued output to the channel.
2016     */
2017
2018    while (1) {
2019
2020        /*
2021         * If the queue is empty and there is a ready current buffer, OR if
2022         * the current buffer is full, then move the current buffer to the
2023         * queue.
2024         */
2025
2026        if (((chanPtr->curOutPtr != (ChannelBuffer *) NULL) &&
2027                (chanPtr->curOutPtr->nextAdded == chanPtr->curOutPtr->bufLength))
2028                || ((chanPtr->flags & BUFFER_READY) &&
2029                        (chanPtr->outQueueHead == (ChannelBuffer *) NULL))) {
2030            chanPtr->flags &= (~(BUFFER_READY));
2031            chanPtr->curOutPtr->nextPtr = (ChannelBuffer *) NULL;
2032            if (chanPtr->outQueueHead == (ChannelBuffer *) NULL) {
2033                chanPtr->outQueueHead = chanPtr->curOutPtr;
2034            } else {
2035                chanPtr->outQueueTail->nextPtr = chanPtr->curOutPtr;
2036            }
2037            chanPtr->outQueueTail = chanPtr->curOutPtr;
2038            chanPtr->curOutPtr = (ChannelBuffer *) NULL;
2039        }
2040        bufPtr = chanPtr->outQueueHead;
2041
2042        /*
2043         * If we are not being called from an async flush and an async
2044         * flush is active, we just return without producing any output.
2045         */
2046
2047        if ((!calledFromAsyncFlush) &&
2048                (chanPtr->flags & BG_FLUSH_SCHEDULED)) {
2049            return 0;
2050        }
2051
2052        /*
2053         * If the output queue is still empty, break out of the while loop.
2054         */
2055
2056        if (bufPtr == (ChannelBuffer *) NULL) {
2057            break;	/* Out of the "while (1)". */
2058        }
2059
2060        /*
2061         * Produce the output on the channel.
2062         */
2063
2064        toWrite = bufPtr->nextAdded - bufPtr->nextRemoved;
2065        written = (chanPtr->typePtr->outputProc) (chanPtr->instanceData,
2066                (char *) bufPtr->buf + bufPtr->nextRemoved, toWrite,
2067		&errorCode);
2068
2069	/*
2070         * If the write failed completely attempt to start the asynchronous
2071         * flush mechanism and break out of this loop - do not attempt to
2072         * write any more output at this time.
2073         */
2074
2075        if (written < 0) {
2076
2077            /*
2078             * If the last attempt to write was interrupted, simply retry.
2079             */
2080
2081            if (errorCode == EINTR) {
2082                errorCode = 0;
2083                continue;
2084            }
2085
2086            /*
2087             * If the channel is non-blocking and we would have blocked,
2088             * start a background flushing handler and break out of the loop.
2089             */
2090
2091            if ((errorCode == EWOULDBLOCK) || (errorCode == EAGAIN)) {
2092		if (chanPtr->flags & CHANNEL_NONBLOCKING) {
2093		    if (!(chanPtr->flags & BG_FLUSH_SCHEDULED)) {
2094			chanPtr->flags |= BG_FLUSH_SCHEDULED;
2095			UpdateInterest(chanPtr);
2096                    }
2097                    errorCode = 0;
2098                    break;
2099		} else {
2100		    panic("Blocking channel driver did not block on output");
2101                }
2102            }
2103
2104            /*
2105             * Decide whether to report the error upwards or defer it.
2106             */
2107
2108            if (calledFromAsyncFlush) {
2109                if (chanPtr->unreportedError == 0) {
2110                    chanPtr->unreportedError = errorCode;
2111                }
2112            } else {
2113                Tcl_SetErrno(errorCode);
2114		if (interp != NULL) {
2115		    Tcl_SetResult(interp,
2116			    Tcl_PosixError(interp), TCL_VOLATILE);
2117		}
2118            }
2119
2120            /*
2121             * When we get an error we throw away all the output
2122             * currently queued.
2123             */
2124
2125            DiscardOutputQueued(chanPtr);
2126            continue;
2127        } else {
2128	    wroteSome = 1;
2129	}
2130
2131        bufPtr->nextRemoved += written;
2132
2133        /*
2134         * If this buffer is now empty, recycle it.
2135         */
2136
2137        if (bufPtr->nextRemoved == bufPtr->nextAdded) {
2138            chanPtr->outQueueHead = bufPtr->nextPtr;
2139            if (chanPtr->outQueueHead == (ChannelBuffer *) NULL) {
2140                chanPtr->outQueueTail = (ChannelBuffer *) NULL;
2141            }
2142            RecycleBuffer(chanPtr, bufPtr, 0);
2143        }
2144    }	/* Closes "while (1)". */
2145
2146    /*
2147     * If we wrote some data while flushing in the background, we are done.
2148     * We can't finish the background flush until we run out of data and
2149     * the channel becomes writable again.  This ensures that all of the
2150     * pending data has been flushed at the system level.
2151     */
2152
2153    if (chanPtr->flags & BG_FLUSH_SCHEDULED) {
2154	if (wroteSome) {
2155	    return errorCode;
2156	} else if (chanPtr->outQueueHead == (ChannelBuffer *) NULL) {
2157	    chanPtr->flags &= (~(BG_FLUSH_SCHEDULED));
2158	    (chanPtr->typePtr->watchProc)(chanPtr->instanceData,
2159		    chanPtr->interestMask);
2160	}
2161    }
2162
2163    /*
2164     * If the channel is flagged as closed, delete it when the refCount
2165     * drops to zero, the output queue is empty and there is no output
2166     * in the current output buffer.
2167     */
2168
2169    if ((chanPtr->flags & CHANNEL_CLOSED) && (chanPtr->refCount <= 0) &&
2170            (chanPtr->outQueueHead == (ChannelBuffer *) NULL) &&
2171            ((chanPtr->curOutPtr == (ChannelBuffer *) NULL) ||
2172                    (chanPtr->curOutPtr->nextAdded ==
2173                            chanPtr->curOutPtr->nextRemoved))) {
2174        return CloseChannel(interp, chanPtr, errorCode);
2175    }
2176    return errorCode;
2177}
2178
2179/*
2180 *----------------------------------------------------------------------
2181 *
2182 * CloseChannel --
2183 *
2184 *	Utility procedure to close a channel and free its associated
2185 *	resources.
2186 *
2187 * Results:
2188 *	0 on success or a POSIX error code if the operation failed.
2189 *
2190 * Side effects:
2191 *	May close the actual channel; may free memory.
2192 *
2193 *----------------------------------------------------------------------
2194 */
2195
2196static int
2197CloseChannel(interp, chanPtr, errorCode)
2198    Tcl_Interp *interp;			/* For error reporting. */
2199    Channel *chanPtr;			/* The channel to close. */
2200    int errorCode;			/* Status of operation so far. */
2201{
2202    int result = 0;			/* Of calling driver close
2203                                         * operation. */
2204    Channel *prevChanPtr;		/* Preceding channel in list of
2205                                         * all channels - used to splice a
2206                                         * channel out of the list on close. */
2207    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
2208
2209    if (chanPtr == NULL) {
2210        return result;
2211    }
2212
2213    /*
2214     * No more input can be consumed so discard any leftover input.
2215     */
2216
2217    DiscardInputQueued(chanPtr, 1);
2218
2219    /*
2220     * Discard a leftover buffer in the current output buffer field.
2221     */
2222
2223    if (chanPtr->curOutPtr != (ChannelBuffer *) NULL) {
2224        ckfree((char *) chanPtr->curOutPtr);
2225        chanPtr->curOutPtr = (ChannelBuffer *) NULL;
2226    }
2227
2228    /*
2229     * The caller guarantees that there are no more buffers
2230     * queued for output.
2231     */
2232
2233    if (chanPtr->outQueueHead != (ChannelBuffer *) NULL) {
2234        panic("TclFlush, closed channel: queued output left");
2235    }
2236
2237    /*
2238     * If the EOF character is set in the channel, append that to the
2239     * output device.
2240     */
2241
2242    if ((chanPtr->outEofChar != 0) && (chanPtr->flags & TCL_WRITABLE)) {
2243        int dummy;
2244        char c;
2245
2246        c = (char) chanPtr->outEofChar;
2247        (chanPtr->typePtr->outputProc) (chanPtr->instanceData, &c, 1, &dummy);
2248    }
2249
2250    /*
2251     * Remove TCL_READABLE and TCL_WRITABLE from chanPtr->flags, so
2252     * that close callbacks can not do input or output (assuming they
2253     * squirreled the channel away in their clientData). This also
2254     * prevents infinite loops if the callback calls any C API that
2255     * could call FlushChannel.
2256     */
2257
2258    chanPtr->flags &= (~(TCL_READABLE|TCL_WRITABLE));
2259
2260    /*
2261     * Splice this channel out of the list of all channels.
2262     */
2263
2264    if (chanPtr == tsdPtr->firstChanPtr) {
2265        tsdPtr->firstChanPtr = chanPtr->nextChanPtr;
2266    } else {
2267        for (prevChanPtr = tsdPtr->firstChanPtr;
2268                 (prevChanPtr != (Channel *) NULL) &&
2269                     (prevChanPtr->nextChanPtr != chanPtr);
2270                 prevChanPtr = prevChanPtr->nextChanPtr) {
2271            /* Empty loop body. */
2272        }
2273        if (prevChanPtr == (Channel *) NULL) {
2274            panic("FlushChannel: damaged channel list");
2275        }
2276        prevChanPtr->nextChanPtr = chanPtr->nextChanPtr;
2277    }
2278
2279    /*
2280     * Close and free the channel driver state.
2281     */
2282
2283    if (chanPtr->typePtr->closeProc != TCL_CLOSE2PROC) {
2284	result = (chanPtr->typePtr->closeProc)(chanPtr->instanceData, interp);
2285    } else {
2286	result = (chanPtr->typePtr->close2Proc)(chanPtr->instanceData, interp,
2287		0);
2288    }
2289
2290    if (chanPtr->channelName != (char *) NULL) {
2291        ckfree(chanPtr->channelName);
2292    }
2293    Tcl_FreeEncoding(chanPtr->encoding);
2294    if (chanPtr->outputStage != NULL) {
2295	ckfree((char *) chanPtr->outputStage);
2296    }
2297
2298    /*
2299     * If we are being called synchronously, report either
2300     * any latent error on the channel or the current error.
2301     */
2302
2303    if (chanPtr->unreportedError != 0) {
2304        errorCode = chanPtr->unreportedError;
2305    }
2306    if (errorCode == 0) {
2307        errorCode = result;
2308        if (errorCode != 0) {
2309            Tcl_SetErrno(errorCode);
2310        }
2311    }
2312
2313    /* Andreas Kupries <a.kupries@westend.com>, 12/13/1998
2314     * "Trf-Patch for filtering channels"
2315     *
2316     * This is the change to 'CloseChannel'.
2317     *
2318     * Explanation
2319     *		Closing a filtering channel closes the one it
2320     *		superceded too. This basically ripples through
2321     *		the whole chain of filters until it reaches
2322     *		the underlying normal channel.
2323     *
2324     *		This is done by reintegrating the superceded
2325     *		channel into the (thread) global list of open
2326     *		channels and then invoking a regular close.
2327     *		There is no need to handle the complexities of
2328     *		this process by ourselves.
2329     *
2330     *		*Note*
2331     *		This has to be done after the call to the
2332     *		'closeProc' of the filtering channel to allow
2333     *		that one the flushing of internal buffers into
2334     *		the underlying channel.
2335     */
2336
2337    if (chanPtr->supercedes != (Channel*) NULL) {
2338      /* Insert the channel we were stacked upon back into
2339       * the list of open channels, then do a regular close.
2340       */
2341
2342      chanPtr->supercedes->nextChanPtr = tsdPtr->firstChanPtr;
2343      tsdPtr->firstChanPtr             = chanPtr->supercedes;
2344      chanPtr->supercedes->refCount --; /* is deregistered */
2345      Tcl_Close (interp, (Tcl_Channel) chanPtr->supercedes);
2346    }
2347
2348    /*
2349     * Cancel any outstanding timer.
2350     */
2351
2352    Tcl_DeleteTimerHandler(chanPtr->timer);
2353
2354    /*
2355     * Mark the channel as deleted by clearing the type structure.
2356     */
2357
2358    chanPtr->typePtr = NULL;
2359
2360    Tcl_EventuallyFree((ClientData) chanPtr, TCL_DYNAMIC);
2361
2362    return errorCode;
2363}
2364
2365/*
2366 *----------------------------------------------------------------------
2367 *
2368 * Tcl_Close --
2369 *
2370 *	Closes a channel.
2371 *
2372 * Results:
2373 *	A standard Tcl result.
2374 *
2375 * Side effects:
2376 *	Closes the channel if this is the last reference.
2377 *
2378 * NOTE:
2379 *	Tcl_Close removes the channel as far as the user is concerned.
2380 *	However, it may continue to exist for a while longer if it has
2381 *	a background flush scheduled. The device itself is eventually
2382 *	closed and the channel record removed, in CloseChannel, above.
2383 *
2384 *----------------------------------------------------------------------
2385 */
2386
2387	/* ARGSUSED */
2388int
2389Tcl_Close(interp, chan)
2390    Tcl_Interp *interp;			/* Interpreter for errors. */
2391    Tcl_Channel chan;			/* The channel being closed. Must
2392                                         * not be referenced in any
2393                                         * interpreter. */
2394{
2395    ChannelHandler *chPtr, *chNext;	/* Iterate over channel handlers. */
2396    CloseCallback *cbPtr;		/* Iterate over close callbacks
2397                                         * for this channel. */
2398    EventScriptRecord *ePtr, *eNextPtr;	/* Iterate over eventscript records. */
2399    Channel *chanPtr;			/* The real IO channel. */
2400    int result;				/* Of calling FlushChannel. */
2401    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
2402    NextChannelHandler *nhPtr;
2403
2404    if (chan == (Tcl_Channel) NULL) {
2405        return TCL_OK;
2406    }
2407
2408    /*
2409     * Perform special handling for standard channels being closed. If the
2410     * refCount is now 1 it means that the last reference to the standard
2411     * channel is being explicitly closed, so bump the refCount down
2412     * artificially to 0. This will ensure that the channel is actually
2413     * closed, below. Also set the static pointer to NULL for the channel.
2414     */
2415
2416    CheckForStdChannelsBeingClosed(chan);
2417
2418    chanPtr = (Channel *) chan;
2419    if (chanPtr->refCount > 0) {
2420        panic("called Tcl_Close on channel with refCount > 0");
2421    }
2422
2423    /*
2424     * Remove any references to channel handlers for this channel that
2425     * may be about to be invoked.
2426     */
2427
2428    for (nhPtr = tsdPtr->nestedHandlerPtr;
2429             nhPtr != (NextChannelHandler *) NULL;
2430             nhPtr = nhPtr->nestedHandlerPtr) {
2431        if (nhPtr->nextHandlerPtr &&
2432		(nhPtr->nextHandlerPtr->chanPtr == chanPtr)) {
2433	    nhPtr->nextHandlerPtr = NULL;
2434        }
2435    }
2436
2437    /*
2438     * Remove all the channel handler records attached to the channel
2439     * itself.
2440     */
2441
2442    for (chPtr = chanPtr->chPtr;
2443             chPtr != (ChannelHandler *) NULL;
2444             chPtr = chNext) {
2445        chNext = chPtr->nextPtr;
2446        ckfree((char *) chPtr);
2447    }
2448    chanPtr->chPtr = (ChannelHandler *) NULL;
2449
2450
2451    /*
2452     * Cancel any pending copy operation.
2453     */
2454
2455    StopCopy(chanPtr->csPtr);
2456
2457    /*
2458     * Must set the interest mask now to 0, otherwise infinite loops
2459     * will occur if Tcl_DoOneEvent is called before the channel is
2460     * finally deleted in FlushChannel. This can happen if the channel
2461     * has a background flush active.
2462     */
2463
2464    chanPtr->interestMask = 0;
2465
2466    /*
2467     * Remove any EventScript records for this channel.
2468     */
2469
2470    for (ePtr = chanPtr->scriptRecordPtr;
2471             ePtr != (EventScriptRecord *) NULL;
2472             ePtr = eNextPtr) {
2473        eNextPtr = ePtr->nextPtr;
2474	Tcl_DecrRefCount(ePtr->scriptPtr);
2475        ckfree((char *) ePtr);
2476    }
2477    chanPtr->scriptRecordPtr = (EventScriptRecord *) NULL;
2478
2479    /*
2480     * Invoke the registered close callbacks and delete their records.
2481     */
2482
2483    while (chanPtr->closeCbPtr != (CloseCallback *) NULL) {
2484        cbPtr = chanPtr->closeCbPtr;
2485        chanPtr->closeCbPtr = cbPtr->nextPtr;
2486        (cbPtr->proc) (cbPtr->clientData);
2487        ckfree((char *) cbPtr);
2488    }
2489
2490    /*
2491     * Ensure that the last output buffer will be flushed.
2492     */
2493
2494    if ((chanPtr->curOutPtr != (ChannelBuffer *) NULL) &&
2495           (chanPtr->curOutPtr->nextAdded > chanPtr->curOutPtr->nextRemoved)) {
2496        chanPtr->flags |= BUFFER_READY;
2497    }
2498
2499    /*
2500     * If this channel supports it, close the read side, since we don't need it
2501     * anymore and this will help avoid deadlocks on some channel types.
2502     */
2503
2504    if (chanPtr->typePtr->closeProc == TCL_CLOSE2PROC) {
2505	result = (chanPtr->typePtr->close2Proc)(chanPtr->instanceData, interp,
2506		TCL_CLOSE_READ);
2507    } else {
2508	result = 0;
2509    }
2510
2511    /*
2512     * The call to FlushChannel will flush any queued output and invoke
2513     * the close function of the channel driver, or it will set up the
2514     * channel to be flushed and closed asynchronously.
2515     */
2516
2517    chanPtr->flags |= CHANNEL_CLOSED;
2518    if ((FlushChannel(interp, chanPtr, 0) != 0) || (result != 0)) {
2519        return TCL_ERROR;
2520    }
2521    return TCL_OK;
2522}
2523
2524/*
2525 *----------------------------------------------------------------------
2526 *
2527 * Tcl_Write --
2528 *
2529 *	Puts a sequence of bytes into an output buffer, may queue the
2530 *	buffer for output if it gets full, and also remembers whether the
2531 *	current buffer is ready e.g. if it contains a newline and we are in
2532 *	line buffering mode.
2533 *
2534 * Results:
2535 *	The number of bytes written or -1 in case of error. If -1,
2536 *	Tcl_GetErrno will return the error code.
2537 *
2538 * Side effects:
2539 *	May buffer up output and may cause output to be produced on the
2540 *	channel.
2541 *
2542 *----------------------------------------------------------------------
2543 */
2544
2545int
2546Tcl_Write(chan, src, srcLen)
2547    Tcl_Channel chan;			/* The channel to buffer output for. */
2548    char *src;				/* Data to queue in output buffer. */
2549    int srcLen;				/* Length of data in bytes, or < 0 for
2550					 * strlen(). */
2551{
2552    Channel *chanPtr;
2553
2554    chanPtr = (Channel *) chan;
2555    if (CheckChannelErrors(chanPtr, TCL_WRITABLE) != 0) {
2556	return -1;
2557    }
2558    if (srcLen < 0) {
2559        srcLen = strlen(src);
2560    }
2561    return DoWrite(chanPtr, src, srcLen);
2562}
2563
2564/*
2565 *---------------------------------------------------------------------------
2566 *
2567 * Tcl_WriteChars --
2568 *
2569 *	Takes a sequence of UTF-8 characters and converts them for output
2570 *	using the channel's current encoding, may queue the buffer for
2571 *	output if it gets full, and also remembers whether the current
2572 *	buffer is ready e.g. if it contains a newline and we are in
2573 *	line buffering mode.
2574 *
2575 * Results:
2576 *	The number of bytes written or -1 in case of error. If -1,
2577 *	Tcl_GetErrno will return the error code.
2578 *
2579 * Side effects:
2580 *	May buffer up output and may cause output to be produced on the
2581 *	channel.
2582 *
2583 *----------------------------------------------------------------------
2584 */
2585
2586int
2587Tcl_WriteChars(chan, src, len)
2588    Tcl_Channel chan;		/* The channel to buffer output for. */
2589    CONST char *src;		/* UTF-8 characters to queue in output buffer. */
2590    int len;			/* Length of string in bytes, or < 0 for
2591				 * strlen(). */
2592{
2593    Channel *chanPtr;
2594
2595    chanPtr = (Channel *) chan;
2596    if (CheckChannelErrors(chanPtr, TCL_WRITABLE) != 0) {
2597	return -1;
2598    }
2599    if (len < 0) {
2600        len = strlen(src);
2601    }
2602    if (chanPtr->encoding == NULL) {
2603	/*
2604	 * Inefficient way to convert UTF-8 to byte-array, but the
2605	 * code parallels the way it is done for objects.
2606	 */
2607
2608	Tcl_Obj *objPtr;
2609	int result;
2610
2611	objPtr = Tcl_NewStringObj(src, len);
2612	src = (char *) Tcl_GetByteArrayFromObj(objPtr, &len);
2613	result = WriteBytes(chanPtr, src, len);
2614	Tcl_DecrRefCount(objPtr);
2615	return result;
2616    }
2617    return WriteChars(chanPtr, src, len);
2618}
2619
2620/*
2621 *---------------------------------------------------------------------------
2622 *
2623 * Tcl_WriteObj --
2624 *
2625 *	Takes the Tcl object and queues its contents for output.  If the
2626 *	encoding of the channel is NULL, takes the byte-array representation
2627 *	of the object and queues those bytes for output.  Otherwise, takes
2628 *	the characters in the UTF-8 (string) representation of the object
2629 *	and converts them for output using the channel's current encoding.
2630 *	May flush internal buffers to output if one becomes full or is ready
2631 *	for some other reason, e.g. if it contains a newline and the channel
2632 *	is in line buffering mode.
2633 *
2634 * Results:
2635 *	The number of bytes written or -1 in case of error. If -1,
2636 *	Tcl_GetErrno() will return the error code.
2637 *
2638 * Side effects:
2639 *	May buffer up output and may cause output to be produced on the
2640 *	channel.
2641 *
2642 *----------------------------------------------------------------------
2643 */
2644
2645int
2646Tcl_WriteObj(chan, objPtr)
2647    Tcl_Channel chan;		/* The channel to buffer output for. */
2648    Tcl_Obj *objPtr;		/* The object to write. */
2649{
2650    Channel *chanPtr;
2651    char *src;
2652    int srcLen;
2653
2654    chanPtr = (Channel *) chan;
2655    if (CheckChannelErrors(chanPtr, TCL_WRITABLE) != 0) {
2656	return -1;
2657    }
2658    if (chanPtr->encoding == NULL) {
2659	src = (char *) Tcl_GetByteArrayFromObj(objPtr, &srcLen);
2660	return WriteBytes(chanPtr, src, srcLen);
2661    } else {
2662	src = Tcl_GetStringFromObj(objPtr, &srcLen);
2663	return WriteChars(chanPtr, src, srcLen);
2664    }
2665}
2666
2667/*
2668 *----------------------------------------------------------------------
2669 *
2670 * WriteBytes --
2671 *
2672 *	Write a sequence of bytes into an output buffer, may queue the
2673 *	buffer for output if it gets full, and also remembers whether the
2674 *	current buffer is ready e.g. if it contains a newline and we are in
2675 *	line buffering mode.
2676 *
2677 * Results:
2678 *	The number of bytes written or -1 in case of error. If -1,
2679 *	Tcl_GetErrno will return the error code.
2680 *
2681 * Side effects:
2682 *	May buffer up output and may cause output to be produced on the
2683 *	channel.
2684 *
2685 *----------------------------------------------------------------------
2686 */
2687
2688static int
2689WriteBytes(chanPtr, src, srcLen)
2690    Channel *chanPtr;		/* The channel to buffer output for. */
2691    CONST char *src;		/* Bytes to write. */
2692    int srcLen;			/* Number of bytes to write. */
2693{
2694    ChannelBuffer *bufPtr;
2695    char *dst;
2696    int dstLen, dstMax, sawLF, savedLF, total, toWrite;
2697
2698    total = 0;
2699    sawLF = 0;
2700    savedLF = 0;
2701
2702    /*
2703     * Loop over all bytes in src, storing them in output buffer with
2704     * proper EOL translation.
2705     */
2706
2707    while (srcLen + savedLF > 0) {
2708	bufPtr = chanPtr->curOutPtr;
2709	if (bufPtr == NULL) {
2710	    bufPtr = AllocChannelBuffer(chanPtr->bufSize);
2711	    chanPtr->curOutPtr	= bufPtr;
2712	}
2713	dst = bufPtr->buf + bufPtr->nextAdded;
2714	dstMax = bufPtr->bufLength - bufPtr->nextAdded;
2715	dstLen = dstMax;
2716
2717	toWrite = dstLen;
2718	if (toWrite > srcLen) {
2719	    toWrite = srcLen;
2720	}
2721
2722	if (savedLF) {
2723	    /*
2724	     * A '\n' was left over from last call to TranslateOutputEOL()
2725	     * and we need to store it in this buffer.  If the channel is
2726	     * line-based, we will need to flush it.
2727	     */
2728
2729	    *dst++ = '\n';
2730	    dstLen--;
2731	    sawLF++;
2732	}
2733	sawLF += TranslateOutputEOL(chanPtr, dst, src, &dstLen, &toWrite);
2734	dstLen += savedLF;
2735	savedLF = 0;
2736
2737	if (dstLen > dstMax) {
2738	    savedLF = 1;
2739	    dstLen = dstMax;
2740	}
2741	bufPtr->nextAdded += dstLen;
2742	if (CheckFlush(chanPtr, bufPtr, sawLF) != 0) {
2743	    return -1;
2744	}
2745	total += dstLen;
2746	src += toWrite;
2747	srcLen -= toWrite;
2748	sawLF = 0;
2749    }
2750    return total;
2751}
2752
2753/*
2754 *----------------------------------------------------------------------
2755 *
2756 * WriteChars --
2757 *
2758 *	Convert UTF-8 bytes to the channel's external encoding and
2759 *	write the produced bytes into an output buffer, may queue the
2760 *	buffer for output if it gets full, and also remembers whether the
2761 *	current buffer is ready e.g. if it contains a newline and we are in
2762 *	line buffering mode.
2763 *
2764 * Results:
2765 *	The number of bytes written or -1 in case of error. If -1,
2766 *	Tcl_GetErrno will return the error code.
2767 *
2768 * Side effects:
2769 *	May buffer up output and may cause output to be produced on the
2770 *	channel.
2771 *
2772 *----------------------------------------------------------------------
2773 */
2774
2775static int
2776WriteChars(chanPtr, src, srcLen)
2777    Channel *chanPtr;		/* The channel to buffer output for. */
2778    CONST char *src;		/* UTF-8 string to write. */
2779    int srcLen;			/* Length of UTF-8 string in bytes. */
2780{
2781    ChannelBuffer *bufPtr;
2782    char *dst, *stage;
2783    int saved, savedLF, sawLF, total, toWrite, flags;
2784    int dstWrote, dstLen, stageLen, stageMax, stageRead;
2785    Tcl_Encoding encoding;
2786    char safe[BUFFER_PADDING];
2787
2788    total = 0;
2789    sawLF = 0;
2790    savedLF = 0;
2791    saved = 0;
2792    encoding = chanPtr->encoding;
2793
2794    /*
2795     * Loop over all UTF-8 characters in src, storing them in staging buffer
2796     * with proper EOL translation.
2797     */
2798
2799    while (srcLen + savedLF > 0) {
2800	stage = chanPtr->outputStage;
2801	stageMax = chanPtr->bufSize;
2802	stageLen = stageMax;
2803
2804	toWrite = stageLen;
2805	if (toWrite > srcLen) {
2806	    toWrite = srcLen;
2807	}
2808
2809	if (savedLF) {
2810	    /*
2811	     * A '\n' was left over from last call to TranslateOutputEOL()
2812	     * and we need to store it in the staging buffer.  If the
2813	     * channel is line-based, we will need to flush the output
2814	     * buffer (after translating the staging buffer).
2815	     */
2816
2817	    *stage++ = '\n';
2818	    stageLen--;
2819	    sawLF++;
2820	}
2821	sawLF += TranslateOutputEOL(chanPtr, stage, src, &stageLen, &toWrite);
2822
2823	stage -= savedLF;
2824	stageLen += savedLF;
2825	savedLF = 0;
2826
2827	if (stageLen > stageMax) {
2828	    savedLF = 1;
2829	    stageLen = stageMax;
2830	}
2831	src += toWrite;
2832	srcLen -= toWrite;
2833
2834	flags = chanPtr->outputEncodingFlags;
2835	if (srcLen == 0) {
2836	    flags |= TCL_ENCODING_END;
2837	}
2838
2839	/*
2840	 * Loop over all UTF-8 characters in staging buffer, converting them
2841	 * to external encoding, storing them in output buffer.
2842	 */
2843
2844	while (stageLen + saved > 0) {
2845	    bufPtr = chanPtr->curOutPtr;
2846	    if (bufPtr == NULL) {
2847		bufPtr = AllocChannelBuffer(chanPtr->bufSize);
2848		chanPtr->curOutPtr = bufPtr;
2849	    }
2850	    dst = bufPtr->buf + bufPtr->nextAdded;
2851	    dstLen = bufPtr->bufLength - bufPtr->nextAdded;
2852
2853	    if (saved != 0) {
2854		/*
2855		 * Here's some translated bytes left over from the last
2856		 * buffer that we need to stick at the beginning of this
2857		 * buffer.
2858		 */
2859
2860		memcpy((VOID *) dst, (VOID *) safe, (size_t) saved);
2861		bufPtr->nextAdded += saved;
2862		dst += saved;
2863		dstLen -= saved;
2864		saved = 0;
2865	    }
2866
2867	    Tcl_UtfToExternal(NULL, encoding, stage, stageLen, flags,
2868		    &chanPtr->outputEncodingState, dst,
2869		    dstLen + BUFFER_PADDING, &stageRead, &dstWrote, NULL);
2870	    if (stageRead + dstWrote == 0) {
2871		/*
2872		 * We have an incomplete UTF-8 character at the end of the
2873		 * staging buffer.  It will get moved to the beginning of the
2874		 * staging buffer followed by more bytes from src.
2875		 */
2876
2877		src -= stageLen;
2878		srcLen += stageLen;
2879		stageLen = 0;
2880		savedLF = 0;
2881		break;
2882	    }
2883	    bufPtr->nextAdded += dstWrote;
2884	    if (bufPtr->nextAdded > bufPtr->bufLength) {
2885		/*
2886		 * When translating from UTF-8 to external encoding, we
2887		 * allowed the translation to produce a character that
2888		 * crossed the end of the output buffer, so that we would
2889		 * get a completely full buffer before flushing it.  The
2890		 * extra bytes will be moved to the beginning of the next
2891		 * buffer.
2892		 */
2893
2894		saved = bufPtr->nextAdded - bufPtr->bufLength;
2895		memcpy((VOID *) safe, (VOID *) (dst + dstLen), (size_t) saved);
2896		bufPtr->nextAdded = bufPtr->bufLength;
2897	    }
2898	    if (CheckFlush(chanPtr, bufPtr, sawLF) != 0) {
2899		return -1;
2900	    }
2901
2902	    total += dstWrote;
2903	    stage += stageRead;
2904	    stageLen -= stageRead;
2905	    sawLF = 0;
2906	}
2907    }
2908    return total;
2909}
2910
2911/*
2912 *---------------------------------------------------------------------------
2913 *
2914 * TranslateOutputEOL --
2915 *
2916 *	Helper function for WriteBytes() and WriteChars().  Converts the
2917 *	'\n' characters in the source buffer into the appropriate EOL
2918 *	form specified by the output translation mode.
2919 *
2920 *	EOL translation stops either when the source buffer is empty
2921 *	or the output buffer is full.
2922 *
2923 *	When converting to CRLF mode and there is only 1 byte left in
2924 *	the output buffer, this routine stores the '\r' in the last
2925 *	byte and then stores the '\n' in the byte just past the end of the
2926 *	buffer.  The caller is responsible for passing in a buffer that
2927 *	is large enough to hold the extra byte.
2928 *
2929 * Results:
2930 *	The return value is 1 if a '\n' was translated from the source
2931 *	buffer, or 0 otherwise -- this can be used by the caller to
2932 *	decide to flush a line-based channel even though the channel
2933 *	buffer is not full.
2934 *
2935 *	*dstLenPtr is filled with how many bytes of the output buffer
2936 *	were used.  As mentioned above, this can be one more that
2937 *	the output buffer's specified length if a CRLF was stored.
2938 *
2939 *	*srcLenPtr is filled with how many bytes of the source buffer
2940 *	were consumed.
2941 *
2942 * Side effects:
2943 *	It may be obvious, but bears mentioning that when converting
2944 *	in CRLF mode (which requires two bytes of storage in the output
2945 *	buffer), the number of bytes consumed from the source buffer
2946 *	will be less than the number of bytes stored in the output buffer.
2947 *
2948 *---------------------------------------------------------------------------
2949 */
2950
2951static int
2952TranslateOutputEOL(chanPtr, dst, src, dstLenPtr, srcLenPtr)
2953    Channel *chanPtr;		/* Channel being read, for translation and
2954				 * buffering modes. */
2955    char *dst;			/* Output buffer filled with UTF-8 chars by
2956				 * applying appropriate EOL translation to
2957				 * source characters. */
2958    CONST char *src;		/* Source UTF-8 characters. */
2959    int *dstLenPtr;		/* On entry, the maximum length of output
2960				 * buffer in bytes.  On exit, the number of
2961				 * bytes actually used in output buffer. */
2962    int *srcLenPtr;		/* On entry, the length of source buffer.
2963				 * On exit, the number of bytes read from
2964				 * the source buffer. */
2965{
2966    char *dstEnd;
2967    int srcLen, newlineFound;
2968
2969    newlineFound = 0;
2970    srcLen = *srcLenPtr;
2971
2972    switch (chanPtr->outputTranslation) {
2973	case TCL_TRANSLATE_LF: {
2974	    for (dstEnd = dst + srcLen; dst < dstEnd; ) {
2975		if (*src == '\n') {
2976		    newlineFound = 1;
2977		}
2978		*dst++ = *src++;
2979	    }
2980	    *dstLenPtr = srcLen;
2981	    break;
2982	}
2983	case TCL_TRANSLATE_CR: {
2984	    for (dstEnd = dst + srcLen; dst < dstEnd;) {
2985		if (*src == '\n') {
2986		    *dst++ = '\r';
2987		    newlineFound = 1;
2988		    src++;
2989		} else {
2990		    *dst++ = *src++;
2991		}
2992	    }
2993	    *dstLenPtr = srcLen;
2994	    break;
2995	}
2996	case TCL_TRANSLATE_CRLF: {
2997	    /*
2998	     * Since this causes the number of bytes to grow, we
2999	     * start off trying to put 'srcLen' bytes into the
3000	     * output buffer, but allow it to store more bytes, as
3001	     * long as there's still source bytes and room in the
3002	     * output buffer.
3003	     */
3004
3005	    char *dstStart, *dstMax;
3006	    CONST char *srcStart;
3007
3008	    dstStart = dst;
3009	    dstMax = dst + *dstLenPtr;
3010
3011	    srcStart = src;
3012
3013	    if (srcLen < *dstLenPtr) {
3014		dstEnd = dst + srcLen;
3015	    } else {
3016		dstEnd = dst + *dstLenPtr;
3017	    }
3018	    while (dst < dstEnd) {
3019		if (*src == '\n') {
3020		    if (dstEnd < dstMax) {
3021			dstEnd++;
3022		    }
3023		    *dst++ = '\r';
3024		    newlineFound = 1;
3025		}
3026		*dst++ = *src++;
3027	    }
3028	    *srcLenPtr = src - srcStart;
3029	    *dstLenPtr = dst - dstStart;
3030	    break;
3031	}
3032	default: {
3033	    break;
3034	}
3035    }
3036    return newlineFound;
3037}
3038
3039/*
3040 *---------------------------------------------------------------------------
3041 *
3042 * CheckFlush --
3043 *
3044 *	Helper function for WriteBytes() and WriteChars().  If the
3045 *	channel buffer is ready to be flushed, flush it.
3046 *
3047 * Results:
3048 *	The return value is -1 if there was a problem flushing the
3049 *	channel buffer, or 0 otherwise.
3050 *
3051 * Side effects:
3052 *	The buffer will be recycled if it is flushed.
3053 *
3054 *---------------------------------------------------------------------------
3055 */
3056
3057static int
3058CheckFlush(chanPtr, bufPtr, newlineFlag)
3059    Channel *chanPtr;		/* Channel being read, for buffering mode. */
3060    ChannelBuffer *bufPtr;	/* Channel buffer to possibly flush. */
3061    int newlineFlag;		/* Non-zero if a the channel buffer
3062				 * contains a newline. */
3063{
3064    /*
3065     * The current buffer is ready for output:
3066     * 1. if it is full.
3067     * 2. if it contains a newline and this channel is line-buffered.
3068     * 3. if it contains any output and this channel is unbuffered.
3069     */
3070
3071    if ((chanPtr->flags & BUFFER_READY) == 0) {
3072	if (bufPtr->nextAdded == bufPtr->bufLength) {
3073	    chanPtr->flags |= BUFFER_READY;
3074	} else if (chanPtr->flags & CHANNEL_LINEBUFFERED) {
3075	    if (newlineFlag != 0) {
3076		chanPtr->flags |= BUFFER_READY;
3077	    }
3078	} else if (chanPtr->flags & CHANNEL_UNBUFFERED) {
3079	    chanPtr->flags |= BUFFER_READY;
3080	}
3081    }
3082    if (chanPtr->flags & BUFFER_READY) {
3083	if (FlushChannel(NULL, chanPtr, 0) != 0) {
3084	    return -1;
3085	}
3086    }
3087    return 0;
3088}
3089
3090/*
3091 *---------------------------------------------------------------------------
3092 *
3093 * Tcl_Gets --
3094 *
3095 *	Reads a complete line of input from the channel into a Tcl_DString.
3096 *
3097 * Results:
3098 *	Length of line read (in characters) or -1 if error, EOF, or blocked.
3099 *	If -1, use Tcl_GetErrno() to retrieve the POSIX error code for the
3100 *	error or condition that occurred.
3101 *
3102 * Side effects:
3103 *	May flush output on the channel.  May cause input to be consumed
3104 *	from the channel.
3105 *
3106 *---------------------------------------------------------------------------
3107 */
3108
3109int
3110Tcl_Gets(chan, lineRead)
3111    Tcl_Channel chan;		/* Channel from which to read. */
3112    Tcl_DString *lineRead;	/* The line read will be appended to this
3113				 * DString as UTF-8 characters.  The caller
3114				 * must have initialized it and is responsible
3115				 * for managing the storage. */
3116{
3117    Tcl_Obj *objPtr;
3118    int charsStored, length;
3119    char *string;
3120
3121    objPtr = Tcl_NewObj();
3122    charsStored = Tcl_GetsObj(chan, objPtr);
3123    if (charsStored > 0) {
3124	string = Tcl_GetStringFromObj(objPtr, &length);
3125	Tcl_DStringAppend(lineRead, string, length);
3126    }
3127    Tcl_DecrRefCount(objPtr);
3128    return charsStored;
3129}
3130
3131/*
3132 *---------------------------------------------------------------------------
3133 *
3134 * Tcl_GetsObj --
3135 *
3136 *	Accumulate input from the input channel until end-of-line or
3137 *	end-of-file has been seen.  Bytes read from the input channel
3138 *	are converted to UTF-8 using the encoding specified by the
3139 *	channel.
3140 *
3141 * Results:
3142 *	Number of characters accumulated in the object or -1 if error,
3143 *	blocked, or EOF.  If -1, use Tcl_GetErrno() to retrieve the
3144 *	POSIX error code for the error or condition that occurred.
3145 *
3146 * Side effects:
3147 *	Consumes input from the channel.
3148 *
3149 *	On reading EOF, leave channel pointing at EOF char.
3150 *	On reading EOL, leave channel pointing after EOL, but don't
3151 *	return EOL in dst buffer.
3152 *
3153 *---------------------------------------------------------------------------
3154 */
3155
3156int
3157Tcl_GetsObj(chan, objPtr)
3158    Tcl_Channel chan;		/* Channel from which to read. */
3159    Tcl_Obj *objPtr;		/* The line read will be appended to this
3160				 * object as UTF-8 characters. */
3161{
3162    GetsState gs;
3163    Channel *chanPtr;
3164    int inEofChar, skip, copiedTotal;
3165    ChannelBuffer *bufPtr;
3166    Tcl_Encoding encoding;
3167    char *dst, *dstEnd, *eol, *eof;
3168    Tcl_EncodingState oldState;
3169    int oldLength, oldFlags, oldRemoved;
3170
3171    chanPtr = (Channel *) chan;
3172    if (CheckChannelErrors(chanPtr, TCL_READABLE) != 0) {
3173	copiedTotal = -1;
3174	goto done;
3175    }
3176
3177    bufPtr = chanPtr->inQueueHead;
3178    encoding = chanPtr->encoding;
3179
3180    /*
3181     * Preserved so we can restore the channel's state in case we don't
3182     * find a newline in the available input.
3183     */
3184
3185    Tcl_GetStringFromObj(objPtr, &oldLength);
3186    oldFlags = chanPtr->inputEncodingFlags;
3187    oldState = chanPtr->inputEncodingState;
3188    oldRemoved = BUFFER_PADDING;
3189    if (bufPtr != NULL) {
3190	oldRemoved = bufPtr->nextRemoved;
3191    }
3192
3193    /*
3194     * If there is no encoding, use "iso8859-1" -- Tcl_GetsObj() doesn't
3195     * produce ByteArray objects.  To avoid circularity problems,
3196     * "iso8859-1" is builtin to Tcl.
3197     */
3198
3199    if (encoding == NULL) {
3200	encoding = Tcl_GetEncoding(NULL, "iso8859-1");
3201    }
3202
3203    /*
3204     * Object used by FilterInputBytes to keep track of how much data has
3205     * been consumed from the channel buffers.
3206     */
3207
3208    gs.objPtr		= objPtr;
3209    gs.dstPtr		= &dst;
3210    gs.encoding		= encoding;
3211    gs.bufPtr		= bufPtr;
3212    gs.state		= oldState;
3213    gs.rawRead		= 0;
3214    gs.bytesWrote	= 0;
3215    gs.charsWrote	= 0;
3216    gs.totalChars	= 0;
3217
3218    dst = objPtr->bytes + oldLength;
3219    dstEnd = dst;
3220
3221    skip = 0;
3222    eof = NULL;
3223    inEofChar = chanPtr->inEofChar;
3224
3225    while (1) {
3226	if (dst >= dstEnd) {
3227	    if (FilterInputBytes(chanPtr, &gs) != 0) {
3228		goto restore;
3229	    }
3230	    dstEnd = dst + gs.bytesWrote;
3231	}
3232
3233	/*
3234	 * Remember if EOF char is seen, then look for EOL anyhow, because
3235	 * the EOL might be before the EOF char.
3236	 */
3237
3238	if (inEofChar != '\0') {
3239	    for (eol = dst; eol < dstEnd; eol++) {
3240		if (*eol == inEofChar) {
3241		    dstEnd = eol;
3242		    eof = eol;
3243		    break;
3244		}
3245	    }
3246	}
3247
3248	/*
3249	 * On EOL, leave current file position pointing after the EOL, but
3250	 * don't store the EOL in the output string.
3251	 */
3252
3253	eol = dst;
3254	switch (chanPtr->inputTranslation) {
3255	    case TCL_TRANSLATE_LF: {
3256		for (eol = dst; eol < dstEnd; eol++) {
3257		    if (*eol == '\n') {
3258			skip = 1;
3259			goto goteol;
3260		    }
3261		}
3262		break;
3263	    }
3264	    case TCL_TRANSLATE_CR: {
3265		for (eol = dst; eol < dstEnd; eol++) {
3266		    if (*eol == '\r') {
3267			skip = 1;
3268			goto goteol;
3269		    }
3270		}
3271		break;
3272	    }
3273	    case TCL_TRANSLATE_CRLF: {
3274		for (eol = dst; eol < dstEnd; eol++) {
3275		    if (*eol == '\r') {
3276			eol++;
3277			if (eol >= dstEnd) {
3278			    int offset;
3279
3280			    offset = eol - objPtr->bytes;
3281			    dst = dstEnd;
3282			    if (FilterInputBytes(chanPtr, &gs) != 0) {
3283				goto restore;
3284			    }
3285			    dstEnd = dst + gs.bytesWrote;
3286			    eol = objPtr->bytes + offset;
3287			    if (eol >= dstEnd) {
3288				skip = 0;
3289				goto goteol;
3290			    }
3291			}
3292			if (*eol == '\n') {
3293			    eol--;
3294			    skip = 2;
3295			    goto goteol;
3296			}
3297		    }
3298		}
3299		break;
3300	    }
3301	    case TCL_TRANSLATE_AUTO: {
3302		skip = 1;
3303		if (chanPtr->flags & INPUT_SAW_CR) {
3304		    chanPtr->flags &= ~INPUT_SAW_CR;
3305		    if (*eol == '\n') {
3306			/*
3307			 * Skip the raw bytes that make up the '\n'.
3308			 */
3309
3310			char tmp[1 + TCL_UTF_MAX];
3311			int rawRead;
3312
3313			bufPtr = gs.bufPtr;
3314			Tcl_ExternalToUtf(NULL, gs.encoding,
3315				bufPtr->buf + bufPtr->nextRemoved,
3316				gs.rawRead, chanPtr->inputEncodingFlags,
3317				&gs.state, tmp, 1 + TCL_UTF_MAX, &rawRead,
3318				NULL, NULL);
3319			bufPtr->nextRemoved += rawRead;
3320			gs.rawRead -= rawRead;
3321			gs.bytesWrote--;
3322			gs.charsWrote--;
3323			memmove(dst, dst + 1, (size_t) (dstEnd - dst));
3324			dstEnd--;
3325		    }
3326		}
3327		for (eol = dst; eol < dstEnd; eol++) {
3328		    if (*eol == '\r') {
3329			eol++;
3330			if (eol == dstEnd) {
3331			    /*
3332			     * If buffer ended on \r, peek ahead to see if a
3333			     * \n is available.
3334			     */
3335
3336			    int offset;
3337
3338			    offset = eol - objPtr->bytes;
3339			    dst = dstEnd;
3340			    PeekAhead(chanPtr, &dstEnd, &gs);
3341			    eol = objPtr->bytes + offset;
3342			    if (eol >= dstEnd) {
3343				eol--;
3344				chanPtr->flags |= INPUT_SAW_CR;
3345				goto goteol;
3346			    }
3347			}
3348			if (*eol == '\n') {
3349			    skip++;
3350			}
3351			eol--;
3352			goto goteol;
3353		    } else if (*eol == '\n') {
3354			goto goteol;
3355		    }
3356		}
3357	    }
3358	}
3359	if (eof != NULL) {
3360	    /*
3361	     * EOF character was seen.  On EOF, leave current file position
3362	     * pointing at the EOF character, but don't store the EOF
3363	     * character in the output string.
3364	     */
3365
3366	    dstEnd = eof;
3367	    chanPtr->flags |= (CHANNEL_EOF | CHANNEL_STICKY_EOF);
3368	    chanPtr->inputEncodingFlags |= TCL_ENCODING_END;
3369	}
3370	if (chanPtr->flags & CHANNEL_EOF) {
3371	    skip = 0;
3372	    eol = dstEnd;
3373	    if (eol == objPtr->bytes) {
3374		/*
3375		 * If we didn't produce any bytes before encountering EOF,
3376		 * caller needs to see -1.
3377		 */
3378
3379		Tcl_SetObjLength(objPtr, 0);
3380		CommonGetsCleanup(chanPtr, encoding);
3381		copiedTotal = -1;
3382		goto done;
3383	    }
3384	    goto goteol;
3385	}
3386	dst = dstEnd;
3387    }
3388
3389    /*
3390     * Found EOL or EOF, but the output buffer may now contain too many
3391     * UTF-8 characters.  We need to know how many raw bytes correspond to
3392     * the number of UTF-8 characters we want, plus how many raw bytes
3393     * correspond to the character(s) making up EOL (if any), so we can
3394     * remove the correct number of bytes from the channel buffer.
3395     */
3396
3397    goteol:
3398    bufPtr = gs.bufPtr;
3399    chanPtr->inputEncodingState = gs.state;
3400    Tcl_ExternalToUtf(NULL, gs.encoding, bufPtr->buf + bufPtr->nextRemoved,
3401	    gs.rawRead, chanPtr->inputEncodingFlags,
3402	    &chanPtr->inputEncodingState, dst, eol - dst + skip + TCL_UTF_MAX,
3403	    &gs.rawRead, NULL, &gs.charsWrote);
3404    bufPtr->nextRemoved += gs.rawRead;
3405
3406    /*
3407     * Recycle all the emptied buffers.
3408     */
3409
3410    Tcl_SetObjLength(objPtr, eol - objPtr->bytes);
3411    CommonGetsCleanup(chanPtr, encoding);
3412    chanPtr->flags &= ~CHANNEL_BLOCKED;
3413    copiedTotal = gs.totalChars + gs.charsWrote - skip;
3414    goto done;
3415
3416    /*
3417     * Couldn't get a complete line.  This only happens if we get a error
3418     * reading from the channel or we are non-blocking and there wasn't
3419     * an EOL or EOF in the data available.
3420     */
3421
3422    restore:
3423    bufPtr = chanPtr->inQueueHead;
3424    bufPtr->nextRemoved = oldRemoved;
3425
3426    for (bufPtr = bufPtr->nextPtr; bufPtr != NULL; bufPtr = bufPtr->nextPtr) {
3427	bufPtr->nextRemoved = BUFFER_PADDING;
3428    }
3429    CommonGetsCleanup(chanPtr, encoding);
3430
3431    chanPtr->inputEncodingState = oldState;
3432    chanPtr->inputEncodingFlags = oldFlags;
3433    Tcl_SetObjLength(objPtr, oldLength);
3434
3435    /*
3436     * We didn't get a complete line so we need to indicate to UpdateInterest
3437     * that the gets blocked.  It will wait for more data instead of firing
3438     * a timer, avoiding a busy wait.  This is where we are assuming that the
3439     * next operation is a gets.  No more file events will be delivered on
3440     * this channel until new data arrives or some operation is performed
3441     * on the channel (e.g. gets, read, fconfigure) that changes the blocking
3442     * state.  Note that this means a file event will not be delivered even
3443     * though a read would be able to consume the buffered data.
3444     */
3445
3446    chanPtr->flags |= CHANNEL_NEED_MORE_DATA;
3447    copiedTotal = -1;
3448
3449    done:
3450    /*
3451     * Update the notifier state so we don't block while there is still
3452     * data in the buffers.
3453     */
3454
3455    UpdateInterest(chanPtr);
3456    return copiedTotal;
3457}
3458
3459/*
3460 *---------------------------------------------------------------------------
3461 *
3462 * FilterInputBytes --
3463 *
3464 *	Helper function for Tcl_GetsObj.  Produces UTF-8 characters from
3465 *	raw bytes read from the channel.
3466 *
3467 *	Consumes available bytes from channel buffers.  When channel
3468 *	buffers are exhausted, reads more bytes from channel device into
3469 *	a new channel buffer.  It is the caller's responsibility to
3470 *	free the channel buffers that have been exhausted.
3471 *
3472 * Results:
3473 *	The return value is -1 if there was an error reading from the
3474 *	channel, 0 otherwise.
3475 *
3476 * Side effects:
3477 *	Status object keeps track of how much data from channel buffers
3478 *	has been consumed and where UTF-8 bytes should be stored.
3479 *
3480 *---------------------------------------------------------------------------
3481 */
3482
3483static int
3484FilterInputBytes(chanPtr, gsPtr)
3485    Channel *chanPtr;		/* Channel to read. */
3486    GetsState *gsPtr;		/* Current state of gets operation. */
3487{
3488    ChannelBuffer *bufPtr;
3489    char *raw, *rawStart, *rawEnd;
3490    char *dst;
3491    int offset, toRead, dstNeeded, spaceLeft, result, rawLen, length;
3492    Tcl_Obj *objPtr;
3493#define ENCODING_LINESIZE   30	/* Lower bound on how many bytes to convert
3494				 * at a time.  Since we don't know a priori
3495				 * how many bytes of storage this many source
3496				 * bytes will use, we actually need at least
3497				 * ENCODING_LINESIZE * TCL_MAX_UTF bytes of
3498				 * room. */
3499
3500    objPtr = gsPtr->objPtr;
3501
3502    /*
3503     * Subtract the number of bytes that were removed from channel buffer
3504     * during last call.
3505     */
3506
3507    bufPtr = gsPtr->bufPtr;
3508    if (bufPtr != NULL) {
3509	bufPtr->nextRemoved += gsPtr->rawRead;
3510	if (bufPtr->nextRemoved >= bufPtr->nextAdded) {
3511	    bufPtr = bufPtr->nextPtr;
3512	}
3513    }
3514    gsPtr->totalChars += gsPtr->charsWrote;
3515
3516    if ((bufPtr == NULL) || (bufPtr->nextAdded == BUFFER_PADDING)) {
3517	/*
3518	 * All channel buffers were exhausted and the caller still hasn't
3519	 * seen EOL.  Need to read more bytes from the channel device.
3520	 * Side effect is to allocate another channel buffer.
3521	 */
3522
3523	read:
3524        if (chanPtr->flags & CHANNEL_BLOCKED) {
3525            if (chanPtr->flags & CHANNEL_NONBLOCKING) {
3526		gsPtr->charsWrote = 0;
3527		gsPtr->rawRead = 0;
3528		return -1;
3529	    }
3530            chanPtr->flags &= ~CHANNEL_BLOCKED;
3531        }
3532	if (GetInput(chanPtr) != 0) {
3533	    gsPtr->charsWrote = 0;
3534	    gsPtr->rawRead = 0;
3535	    return -1;
3536	}
3537	bufPtr = chanPtr->inQueueTail;
3538	gsPtr->bufPtr = bufPtr;
3539    }
3540
3541    /*
3542     * Convert some of the bytes from the channel buffer to UTF-8.  Space in
3543     * objPtr's string rep is used to hold the UTF-8 characters.  Grow the
3544     * string rep if we need more space.
3545     */
3546
3547    rawStart = bufPtr->buf + bufPtr->nextRemoved;
3548    raw = rawStart;
3549    rawEnd = bufPtr->buf + bufPtr->nextAdded;
3550    rawLen = rawEnd - rawStart;
3551
3552    dst = *gsPtr->dstPtr;
3553    offset = dst - objPtr->bytes;
3554    toRead = ENCODING_LINESIZE;
3555    if (toRead > rawLen) {
3556	toRead = rawLen;
3557    }
3558    dstNeeded = toRead * TCL_UTF_MAX + 1;
3559    spaceLeft = objPtr->length - offset - TCL_UTF_MAX - 1;
3560    if (dstNeeded > spaceLeft) {
3561	length = offset * 2;
3562	if (offset < dstNeeded) {
3563	    length = offset + dstNeeded;
3564	}
3565	length += TCL_UTF_MAX + 1;
3566	Tcl_SetObjLength(objPtr, length);
3567	spaceLeft = length - offset;
3568	dst = objPtr->bytes + offset;
3569	*gsPtr->dstPtr = dst;
3570    }
3571    gsPtr->state = chanPtr->inputEncodingState;
3572    result = Tcl_ExternalToUtf(NULL, gsPtr->encoding, raw, rawLen,
3573	    chanPtr->inputEncodingFlags, &chanPtr->inputEncodingState,
3574	    dst, spaceLeft, &gsPtr->rawRead, &gsPtr->bytesWrote,
3575	    &gsPtr->charsWrote);
3576    if (result == TCL_CONVERT_MULTIBYTE) {
3577	/*
3578	 * The last few bytes in this channel buffer were the start of a
3579	 * multibyte sequence.  If this buffer was full, then move them to
3580	 * the next buffer so the bytes will be contiguous.
3581	 */
3582
3583	ChannelBuffer *nextPtr;
3584	int extra;
3585
3586	nextPtr = bufPtr->nextPtr;
3587	if (bufPtr->nextAdded < bufPtr->bufLength) {
3588	    if (gsPtr->rawRead > 0) {
3589		/*
3590		 * Some raw bytes were converted to UTF-8.  Fall through,
3591		 * returning those UTF-8 characters because a EOL might be
3592		 * present in them.
3593		 */
3594	    } else if (chanPtr->flags & CHANNEL_EOF) {
3595		/*
3596		 * There was a partial character followed by EOF on the
3597		 * device.  Fall through, returning that nothing was found.
3598		 */
3599
3600		 bufPtr->nextRemoved = bufPtr->nextAdded;
3601	    } else {
3602		/*
3603		 * There are no more cached raw bytes left.  See if we can
3604		 * get some more.
3605		 */
3606
3607		goto read;
3608	    }
3609	} else {
3610	    if (nextPtr == NULL) {
3611		nextPtr = AllocChannelBuffer(chanPtr->bufSize);
3612		bufPtr->nextPtr = nextPtr;
3613		chanPtr->inQueueTail = nextPtr;
3614	    }
3615	    extra = rawLen - gsPtr->rawRead;
3616	    memcpy((VOID *) (nextPtr->buf + BUFFER_PADDING - extra),
3617		    (VOID *) (raw + gsPtr->rawRead), (size_t) extra);
3618	    nextPtr->nextRemoved -= extra;
3619	    bufPtr->nextAdded -= extra;
3620	}
3621    }
3622
3623    gsPtr->bufPtr = bufPtr;
3624    return 0;
3625}
3626
3627/*
3628 *---------------------------------------------------------------------------
3629 *
3630 * PeekAhead --
3631 *
3632 *	Helper function used by Tcl_GetsObj().  Called when we've seen a
3633 *	\r at the end of the UTF-8 string and want to look ahead one
3634 *	character to see if it is a \n.
3635 *
3636 * Results:
3637 *	*gsPtr->dstPtr is filled with a pointer to the start of the range of
3638 *	UTF-8 characters that were found by peeking and *dstEndPtr is filled
3639 *	with a pointer to the bytes just after the end of the range.
3640 *
3641 * Side effects:
3642 *	If no more raw bytes were available in one of the channel buffers,
3643 *	tries to perform a non-blocking read to get more bytes from the
3644 *	channel device.
3645 *
3646 *---------------------------------------------------------------------------
3647 */
3648
3649static void
3650PeekAhead(chanPtr, dstEndPtr, gsPtr)
3651    Channel *chanPtr;		/* The channel to read. */
3652    char **dstEndPtr;		/* Filled with pointer to end of new range
3653				 * of UTF-8 characters. */
3654    GetsState *gsPtr;		/* Current state of gets operation. */
3655{
3656    ChannelBuffer *bufPtr;
3657    Tcl_DriverBlockModeProc *blockModeProc;
3658    int bytesLeft;
3659
3660    bufPtr = gsPtr->bufPtr;
3661
3662    /*
3663     * If there's any more raw input that's still buffered, we'll peek into
3664     * that.  Otherwise, only get more data from the channel driver if it
3665     * looks like there might actually be more data.  The assumption is that
3666     * if the channel buffer is filled right up to the end, then there
3667     * might be more data to read.
3668     */
3669
3670    blockModeProc = NULL;
3671    if (bufPtr->nextPtr == NULL) {
3672	bytesLeft = bufPtr->nextAdded - (bufPtr->nextRemoved + gsPtr->rawRead);
3673	if (bytesLeft == 0) {
3674	    if (bufPtr->nextAdded < bufPtr->bufLength) {
3675		/*
3676		 * Don't peek ahead if last read was short read.
3677		 */
3678
3679		goto cleanup;
3680	    }
3681	    if ((chanPtr->flags & CHANNEL_NONBLOCKING) == 0) {
3682		blockModeProc = chanPtr->typePtr->blockModeProc;
3683		if (blockModeProc == NULL) {
3684		    /*
3685		     * Don't peek ahead if cannot set non-blocking mode.
3686		     */
3687
3688		    goto cleanup;
3689		}
3690		(*blockModeProc)(chanPtr->instanceData, TCL_MODE_NONBLOCKING);
3691	    }
3692	}
3693    }
3694    if (FilterInputBytes(chanPtr, gsPtr) == 0) {
3695	*dstEndPtr = *gsPtr->dstPtr + gsPtr->bytesWrote;
3696    }
3697    if (blockModeProc != NULL) {
3698	(*blockModeProc)(chanPtr->instanceData, TCL_MODE_BLOCKING);
3699    }
3700    return;
3701
3702    cleanup:
3703    bufPtr->nextRemoved += gsPtr->rawRead;
3704    gsPtr->rawRead = 0;
3705    gsPtr->totalChars += gsPtr->charsWrote;
3706    gsPtr->bytesWrote = 0;
3707    gsPtr->charsWrote = 0;
3708}
3709
3710/*
3711 *---------------------------------------------------------------------------
3712 *
3713 * CommonGetsCleanup --
3714 *
3715 *	Helper function for Tcl_GetsObj() to restore the channel after
3716 *	a "gets" operation.
3717 *
3718 * Results:
3719 *	None.
3720 *
3721 * Side effects:
3722 *	Encoding may be freed.
3723 *
3724 *---------------------------------------------------------------------------
3725 */
3726
3727static void
3728CommonGetsCleanup(chanPtr, encoding)
3729    Channel *chanPtr;
3730    Tcl_Encoding encoding;
3731{
3732    ChannelBuffer *bufPtr, *nextPtr;
3733
3734    bufPtr = chanPtr->inQueueHead;
3735    for ( ; bufPtr != NULL; bufPtr = nextPtr) {
3736	nextPtr = bufPtr->nextPtr;
3737	if (bufPtr->nextRemoved < bufPtr->nextAdded) {
3738	    break;
3739	}
3740	RecycleBuffer(chanPtr, bufPtr, 0);
3741    }
3742    chanPtr->inQueueHead = bufPtr;
3743    if (bufPtr == NULL) {
3744	chanPtr->inQueueTail = NULL;
3745    } else {
3746	/*
3747	 * If any multi-byte characters were split across channel buffer
3748	 * boundaries, the split-up bytes were moved to the next channel
3749	 * buffer by FilterInputBytes().  Move the bytes back to their
3750	 * original buffer because the caller could change the channel's
3751	 * encoding which could change the interpretation of whether those
3752	 * bytes really made up multi-byte characters after all.
3753	 */
3754
3755	nextPtr = bufPtr->nextPtr;
3756	for ( ; nextPtr != NULL; nextPtr = bufPtr->nextPtr) {
3757	    int extra;
3758
3759	    extra = bufPtr->bufLength - bufPtr->nextAdded;
3760	    if (extra > 0) {
3761		memcpy((VOID *) (bufPtr->buf + bufPtr->nextAdded),
3762			(VOID *) (nextPtr->buf + BUFFER_PADDING - extra),
3763			(size_t) extra);
3764		bufPtr->nextAdded += extra;
3765		nextPtr->nextRemoved = BUFFER_PADDING;
3766	    }
3767	    bufPtr = nextPtr;
3768	}
3769    }
3770    if (chanPtr->encoding == NULL) {
3771	Tcl_FreeEncoding(encoding);
3772    }
3773}
3774
3775/*
3776 *----------------------------------------------------------------------
3777 *
3778 * Tcl_Read --
3779 *
3780 *	Reads a given number of bytes from a channel.  EOL and EOF
3781 *	translation is done on the bytes being read, so the the number
3782 *	of bytes consumed from the channel may not be equal to the
3783 *	number of bytes stored in the destination buffer.
3784 *
3785 *	No encoding conversions are applied to the bytes being read.
3786 *
3787 * Results:
3788 *	The number of bytes read, or -1 on error. Use Tcl_GetErrno()
3789 *	to retrieve the error code for the error that occurred.
3790 *
3791 * Side effects:
3792 *	May cause input to be buffered.
3793 *
3794 *----------------------------------------------------------------------
3795 */
3796
3797int
3798Tcl_Read(chan, dst, bytesToRead)
3799    Tcl_Channel chan;		/* The channel from which to read. */
3800    char *dst;			/* Where to store input read. */
3801    int bytesToRead;		/* Maximum number of bytes to read. */
3802{
3803    Channel *chanPtr;
3804
3805    chanPtr = (Channel *) chan;
3806    if (CheckChannelErrors(chanPtr, TCL_READABLE) != 0) {
3807	return -1;
3808    }
3809
3810    return DoRead(chanPtr, dst, bytesToRead);
3811}
3812
3813/*
3814 *---------------------------------------------------------------------------
3815 *
3816 * Tcl_ReadChars --
3817 *
3818 *	Reads from the channel until the requested number of characters
3819 *	have been seen, EOF is seen, or the channel would block.  EOL
3820 *	and EOF translation is done.  If reading binary data, the raw
3821 *	bytes are wrapped in a Tcl byte array object.  Otherwise, the raw
3822 *	bytes are converted to UTF-8 using the channel's current encoding
3823 *	and stored in a Tcl string object.
3824 *
3825 * Results:
3826 *	The number of characters read, or -1 on error. Use Tcl_GetErrno()
3827 *	to retrieve the error code for the error that occurred.
3828 *
3829 * Side effects:
3830 *	May cause input to be buffered.
3831 *
3832 *---------------------------------------------------------------------------
3833 */
3834
3835int
3836Tcl_ReadChars(chan, objPtr, toRead, appendFlag)
3837    Tcl_Channel chan;		/* The channel to read. */
3838    Tcl_Obj *objPtr;		/* Input data is stored in this object. */
3839    int toRead;			/* Maximum number of characters to store,
3840				 * or -1 to read all available data (up to EOF
3841				 * or when channel blocks). */
3842    int appendFlag;		/* If non-zero, data read from the channel
3843				 * will be appended to the object.  Otherwise,
3844				 * the data will replace the existing contents
3845				 * of the object. */
3846
3847{
3848    Channel *chanPtr;
3849    int offset, factor, copied, copiedNow, result;
3850    ChannelBuffer *bufPtr;
3851    Tcl_Encoding encoding;
3852#define UTF_EXPANSION_FACTOR	1024
3853
3854    chanPtr = (Channel *) chan;
3855    if (CheckChannelErrors(chanPtr, TCL_READABLE) != 0) {
3856	copied = -1;
3857	goto done;
3858    }
3859
3860    encoding = chanPtr->encoding;
3861    factor = UTF_EXPANSION_FACTOR;
3862
3863    if (appendFlag == 0) {
3864	if (encoding == NULL) {
3865	    Tcl_SetByteArrayLength(objPtr, 0);
3866	} else {
3867	    Tcl_SetObjLength(objPtr, 0);
3868	}
3869	offset = 0;
3870    } else {
3871	if (encoding == NULL) {
3872	    Tcl_GetByteArrayFromObj(objPtr, &offset);
3873	} else {
3874	    Tcl_GetStringFromObj(objPtr, &offset);
3875	}
3876    }
3877
3878    for (copied = 0; (unsigned) toRead > 0; ) {
3879	copiedNow = -1;
3880	if (chanPtr->inQueueHead != NULL) {
3881	    if (encoding == NULL) {
3882		copiedNow = ReadBytes(chanPtr, objPtr, toRead, &offset);
3883	    } else {
3884		copiedNow = ReadChars(chanPtr, objPtr, toRead, &offset,
3885			&factor);
3886	    }
3887
3888	    /*
3889	     * If the current buffer is empty recycle it.
3890	     */
3891
3892	    bufPtr = chanPtr->inQueueHead;
3893	    if (bufPtr->nextRemoved == bufPtr->nextAdded) {
3894		ChannelBuffer *nextPtr;
3895
3896		nextPtr = bufPtr->nextPtr;
3897		RecycleBuffer(chanPtr, bufPtr, 0);
3898		chanPtr->inQueueHead = nextPtr;
3899		if (nextPtr == NULL) {
3900		    chanPtr->inQueueTail = nextPtr;
3901		}
3902	    }
3903	}
3904	if (copiedNow < 0) {
3905	    if (chanPtr->flags & CHANNEL_EOF) {
3906		break;
3907	    }
3908	    if (chanPtr->flags & CHANNEL_BLOCKED) {
3909		if (chanPtr->flags & CHANNEL_NONBLOCKING) {
3910		    break;
3911		}
3912		chanPtr->flags &= ~CHANNEL_BLOCKED;
3913	    }
3914	    result = GetInput(chanPtr);
3915	    if (result != 0) {
3916		if (result == EAGAIN) {
3917		    break;
3918		}
3919		copied = -1;
3920		goto done;
3921	    }
3922	} else {
3923	    copied += copiedNow;
3924	    toRead -= copiedNow;
3925	}
3926    }
3927    chanPtr->flags &= ~CHANNEL_BLOCKED;
3928    if (encoding == NULL) {
3929	Tcl_SetByteArrayLength(objPtr, offset);
3930    } else {
3931	Tcl_SetObjLength(objPtr, offset);
3932    }
3933
3934    done:
3935    /*
3936     * Update the notifier state so we don't block while there is still
3937     * data in the buffers.
3938     */
3939
3940    UpdateInterest(chanPtr);
3941    return copied;
3942}
3943/*
3944 *---------------------------------------------------------------------------
3945 *
3946 * ReadBytes --
3947 *
3948 *	Reads from the channel until the requested number of bytes have
3949 *	been seen, EOF is seen, or the channel would block.  Bytes from
3950 *	the channel are stored in objPtr as a ByteArray object.  EOL
3951 *	and EOF translation are done.
3952 *
3953 *	'bytesToRead' can safely be a very large number because
3954 *	space is only allocated to hold data read from the channel
3955 *	as needed.
3956 *
3957 * Results:
3958 *	The return value is the number of bytes appended to the object
3959 *	and *offsetPtr is filled with the total number of bytes in the
3960 *	object (greater than the return value if there were already bytes
3961 *	in the object).
3962 *
3963 * Side effects:
3964 *	None.
3965 *
3966 *---------------------------------------------------------------------------
3967 */
3968
3969static int
3970ReadBytes(chanPtr, objPtr, bytesToRead, offsetPtr)
3971    Channel *chanPtr;		/* The channel to read. */
3972    int bytesToRead;		/* Maximum number of characters to store,
3973				 * or < 0 to get all available characters.
3974				 * Characters are obtained from the first
3975				 * buffer in the queue -- even if this number
3976				 * is larger than the number of characters
3977				 * available in the first buffer, only the
3978				 * characters from the first buffer are
3979				 * returned. */
3980    Tcl_Obj *objPtr;		/* Input data is appended to this ByteArray
3981				 * object.  Its length is how much space
3982				 * has been allocated to hold data, not how
3983				 * many bytes of data have been stored in the
3984				 * object. */
3985    int *offsetPtr;		/* On input, contains how many bytes of
3986				 * objPtr have been used to hold data.  On
3987				 * output, filled with how many bytes are now
3988				 * being used. */
3989{
3990    int toRead, srcLen, srcRead, dstWrote, offset, length;
3991    ChannelBuffer *bufPtr;
3992    char *src, *dst;
3993
3994    offset = *offsetPtr;
3995
3996    bufPtr = chanPtr->inQueueHead;
3997    src = bufPtr->buf + bufPtr->nextRemoved;
3998    srcLen = bufPtr->nextAdded - bufPtr->nextRemoved;
3999
4000    toRead = bytesToRead;
4001    if ((unsigned) toRead > (unsigned) srcLen) {
4002	toRead = srcLen;
4003    }
4004
4005    dst = (char *) Tcl_GetByteArrayFromObj(objPtr, &length);
4006    if (toRead > length - offset - 1) {
4007	/*
4008	 * Double the existing size of the object or make enough room to
4009	 * hold all the characters we may get from the source buffer,
4010	 * whichever is larger.
4011	 */
4012
4013	length = offset * 2;
4014	if (offset < toRead) {
4015	    length = offset + toRead + 1;
4016	}
4017	dst = (char *) Tcl_SetByteArrayLength(objPtr, length);
4018    }
4019    dst += offset;
4020
4021    if (chanPtr->flags & INPUT_NEED_NL) {
4022	chanPtr->flags &= ~INPUT_NEED_NL;
4023	if ((srcLen == 0) || (*src != '\n')) {
4024	    *dst = '\r';
4025	    *offsetPtr += 1;
4026	    return 1;
4027	}
4028	*dst++ = '\n';
4029	src++;
4030	srcLen--;
4031	toRead--;
4032    }
4033
4034    srcRead = srcLen;
4035    dstWrote = toRead;
4036    if (TranslateInputEOL(chanPtr, dst, src, &dstWrote, &srcRead) != 0) {
4037	if (dstWrote == 0) {
4038	    return -1;
4039	}
4040    }
4041    bufPtr->nextRemoved += srcRead;
4042    *offsetPtr += dstWrote;
4043    return dstWrote;
4044}
4045
4046/*
4047 *---------------------------------------------------------------------------
4048 *
4049 * ReadChars --
4050 *
4051 *	Reads from the channel until the requested number of UTF-8
4052 *	characters have been seen, EOF is seen, or the channel would
4053 *	block.  Raw bytes from the channel are converted to UTF-8
4054 *	and stored in objPtr.  EOL and EOF translation is done.
4055 *
4056 *	'charsToRead' can safely be a very large number because
4057 *	space is only allocated to hold data read from the channel
4058 *	as needed.
4059 *
4060 * Results:
4061 *	The return value is the number of characters appended to
4062 *	the object, *offsetPtr is filled with the number of bytes that
4063 *	were appended, and *factorPtr is filled with the expansion
4064 *	factor used to guess how many bytes of UTF-8 to allocate to
4065 *	hold N source bytes.
4066 *
4067 * Side effects:
4068 *	None.
4069 *
4070 *---------------------------------------------------------------------------
4071 */
4072
4073static int
4074ReadChars(chanPtr, objPtr, charsToRead, offsetPtr, factorPtr)
4075    Channel *chanPtr;		/* The channel to read. */
4076    int charsToRead;		/* Maximum number of characters to store,
4077				 * or -1 to get all available characters.
4078				 * Characters are obtained from the first
4079				 * buffer in the queue -- even if this number
4080				 * is larger than the number of characters
4081				 * available in the first buffer, only the
4082				 * characters from the first buffer are
4083				 * returned. */
4084    Tcl_Obj *objPtr;		/* Input data is appended to this object.
4085				 * objPtr->length is how much space has been
4086				 * allocated to hold data, not how many bytes
4087				 * of data have been stored in the object. */
4088    int *offsetPtr;		/* On input, contains how many bytes of
4089				 * objPtr have been used to hold data.  On
4090				 * output, filled with how many bytes are now
4091				 * being used. */
4092    int *factorPtr;		/* On input, contains a guess of how many
4093				 * bytes need to be allocated to hold the
4094				 * result of converting N source bytes to
4095				 * UTF-8.  On output, contains another guess
4096				 * based on the data seen so far. */
4097{
4098    int toRead, factor, offset, spaceLeft, length;
4099    int srcLen, srcRead, dstNeeded, dstRead, dstWrote, numChars;
4100    ChannelBuffer *bufPtr;
4101    char *src, *dst;
4102    Tcl_EncodingState oldState;
4103
4104    factor = *factorPtr;
4105    offset = *offsetPtr;
4106
4107    bufPtr = chanPtr->inQueueHead;
4108    src = bufPtr->buf + bufPtr->nextRemoved;
4109    srcLen = bufPtr->nextAdded - bufPtr->nextRemoved;
4110
4111    toRead = charsToRead;
4112    if ((unsigned) toRead > (unsigned) srcLen) {
4113	toRead = srcLen;
4114    }
4115
4116    /*
4117     * 'factor' is how much we guess that the bytes in the source buffer
4118     * will expand when converted to UTF-8 chars.  This guess comes from
4119     * analyzing how many characters were produced by the previous
4120     * pass.
4121     */
4122
4123    dstNeeded = toRead * factor / UTF_EXPANSION_FACTOR;
4124    spaceLeft = objPtr->length - offset - TCL_UTF_MAX - 1;
4125
4126    if (dstNeeded > spaceLeft) {
4127	/*
4128	 * Double the existing size of the object or make enough room to
4129	 * hold all the characters we want from the source buffer,
4130	 * whichever is larger.
4131	 */
4132
4133	length = offset * 2;
4134	if (offset < dstNeeded) {
4135	    length = offset + dstNeeded;
4136	}
4137	spaceLeft = length - offset;
4138	length += TCL_UTF_MAX + 1;
4139	Tcl_SetObjLength(objPtr, length);
4140    }
4141    if (toRead == srcLen) {
4142	/*
4143	 * Want to convert the whole buffer in one pass.  If we have
4144	 * enough space, convert it using all available space in object
4145	 * rather than using the factor.
4146	 */
4147
4148	dstNeeded = spaceLeft;
4149    }
4150    dst = objPtr->bytes + offset;
4151
4152    oldState = chanPtr->inputEncodingState;
4153    if (chanPtr->flags & INPUT_NEED_NL) {
4154	/*
4155	 * We want a '\n' because the last character we saw was '\r'.
4156	 */
4157
4158	chanPtr->flags &= ~INPUT_NEED_NL;
4159	Tcl_ExternalToUtf(NULL, chanPtr->encoding, src, srcLen,
4160		chanPtr->inputEncodingFlags, &chanPtr->inputEncodingState,
4161		dst, TCL_UTF_MAX + 1, &srcRead, &dstWrote, &numChars);
4162	if ((dstWrote > 0) && (*dst == '\n')) {
4163	    /*
4164	     * The next char was a '\n'.  Consume it and produce a '\n'.
4165	     */
4166
4167	    bufPtr->nextRemoved += srcRead;
4168	} else {
4169	    /*
4170	     * The next char was not a '\n'.  Produce a '\r'.
4171	     */
4172
4173	    *dst = '\r';
4174	}
4175	chanPtr->inputEncodingFlags &= ~TCL_ENCODING_START;
4176	*offsetPtr += 1;
4177        return 1;
4178    }
4179
4180    Tcl_ExternalToUtf(NULL, chanPtr->encoding, src, srcLen,
4181	    chanPtr->inputEncodingFlags, &chanPtr->inputEncodingState, dst,
4182	    dstNeeded + TCL_UTF_MAX, &srcRead, &dstWrote, &numChars);
4183    if (srcRead == 0) {
4184	/*
4185	 * Not enough bytes in src buffer to make a complete char.  Copy
4186	 * the bytes to the next buffer to make a new contiguous string,
4187	 * then tell the caller to fill the buffer with more bytes.
4188	 */
4189
4190	ChannelBuffer *nextPtr;
4191
4192	nextPtr = bufPtr->nextPtr;
4193	if (nextPtr == NULL) {
4194	    /*
4195	     * There isn't enough data in the buffers to complete the next
4196	     * character, so we need to wait for more data before the next
4197	     * file event can be delivered.
4198	     */
4199
4200	    chanPtr->flags |= CHANNEL_NEED_MORE_DATA;
4201	    return -1;
4202	}
4203	nextPtr->nextRemoved -= srcLen;
4204	memcpy((VOID *) (nextPtr->buf + nextPtr->nextRemoved), (VOID *) src,
4205		(size_t) srcLen);
4206	RecycleBuffer(chanPtr, bufPtr, 0);
4207	chanPtr->inQueueHead = nextPtr;
4208	return ReadChars(chanPtr, objPtr, charsToRead, offsetPtr, factorPtr);
4209    }
4210
4211    dstRead = dstWrote;
4212    if (TranslateInputEOL(chanPtr, dst, dst, &dstWrote, &dstRead) != 0) {
4213	/*
4214	 * Hit EOF char.  How many bytes of src correspond to where the
4215	 * EOF was located in dst?
4216	 */
4217
4218	if (dstWrote == 0) {
4219	    return -1;
4220	}
4221	chanPtr->inputEncodingState = oldState;
4222	Tcl_ExternalToUtf(NULL, chanPtr->encoding, src, srcLen,
4223		chanPtr->inputEncodingFlags, &chanPtr->inputEncodingState,
4224		dst, dstRead + TCL_UTF_MAX, &srcRead, &dstWrote, &numChars);
4225	TranslateInputEOL(chanPtr, dst, dst, &dstWrote, &dstRead);
4226    }
4227
4228    /*
4229     * The number of characters that we got may be less than the number
4230     * that we started with because "\r\n" sequences may have been
4231     * turned into just '\n' in dst.
4232     */
4233
4234    numChars -= (dstRead - dstWrote);
4235
4236    if ((unsigned) numChars > (unsigned) toRead) {
4237	/*
4238	 * Got too many chars.
4239	 */
4240
4241	char *eof;
4242
4243	eof = Tcl_UtfAtIndex(dst, toRead);
4244	chanPtr->inputEncodingState = oldState;
4245	Tcl_ExternalToUtf(NULL, chanPtr->encoding, src, srcLen,
4246		chanPtr->inputEncodingFlags, &chanPtr->inputEncodingState,
4247		dst, eof - dst + TCL_UTF_MAX, &srcRead, &dstWrote, &numChars);
4248	dstRead = dstWrote;
4249	TranslateInputEOL(chanPtr, dst, dst, &dstWrote, &dstRead);
4250	numChars -= (dstRead - dstWrote);
4251    }
4252    chanPtr->inputEncodingFlags &= ~TCL_ENCODING_START;
4253
4254    bufPtr->nextRemoved += srcRead;
4255    if (dstWrote > srcRead + 1) {
4256	*factorPtr = dstWrote * UTF_EXPANSION_FACTOR / srcRead;
4257    }
4258    *offsetPtr += dstWrote;
4259    return numChars;
4260}
4261
4262/*
4263 *---------------------------------------------------------------------------
4264 *
4265 * TranslateInputEOL --
4266 *
4267 *	Perform input EOL and EOF translation on the source buffer,
4268 *	leaving the translated result in the destination buffer.
4269 *
4270 * Results:
4271 *	The return value is 1 if the EOF character was found when copying
4272 *	bytes to the destination buffer, 0 otherwise.
4273 *
4274 * Side effects:
4275 *	None.
4276 *
4277 *---------------------------------------------------------------------------
4278 */
4279
4280static int
4281TranslateInputEOL(chanPtr, dstStart, srcStart, dstLenPtr, srcLenPtr)
4282    Channel *chanPtr;		/* Channel being read, for EOL translation
4283				 * and EOF character. */
4284    char *dstStart;		/* Output buffer filled with chars by
4285				 * applying appropriate EOL translation to
4286				 * source characters. */
4287    CONST char *srcStart;	/* Source characters. */
4288    int *dstLenPtr;		/* On entry, the maximum length of output
4289				 * buffer in bytes; must be <= *srcLenPtr.  On
4290				 * exit, the number of bytes actually used in
4291				 * output buffer. */
4292    int *srcLenPtr;		/* On entry, the length of source buffer.
4293				 * On exit, the number of bytes read from
4294				 * the source buffer. */
4295{
4296    int dstLen, srcLen, inEofChar;
4297    CONST char *eof;
4298
4299    dstLen = *dstLenPtr;
4300
4301    eof = NULL;
4302    inEofChar = chanPtr->inEofChar;
4303    if (inEofChar != '\0') {
4304	/*
4305	 * Find EOF in translated buffer then compress out the EOL.  The
4306	 * source buffer may be much longer than the destination buffer --
4307	 * we only want to return EOF if the EOF has been copied to the
4308	 * destination buffer.
4309	 */
4310
4311	CONST char *src, *srcMax;
4312
4313	srcMax = srcStart + *srcLenPtr;
4314	for (src = srcStart; src < srcMax; src++) {
4315	    if (*src == inEofChar) {
4316		eof = src;
4317		srcLen = src - srcStart;
4318		if (srcLen < dstLen) {
4319		    dstLen = srcLen;
4320		}
4321		*srcLenPtr = srcLen;
4322		break;
4323	    }
4324	}
4325    }
4326    switch (chanPtr->inputTranslation) {
4327	case TCL_TRANSLATE_LF: {
4328	    if (dstStart != srcStart) {
4329		memcpy((VOID *) dstStart, (VOID *) srcStart, (size_t) dstLen);
4330	    }
4331	    srcLen = dstLen;
4332	    break;
4333	}
4334	case TCL_TRANSLATE_CR: {
4335	    char *dst, *dstEnd;
4336
4337	    if (dstStart != srcStart) {
4338		memcpy((VOID *) dstStart, (VOID *) srcStart, (size_t) dstLen);
4339	    }
4340	    dstEnd = dstStart + dstLen;
4341	    for (dst = dstStart; dst < dstEnd; dst++) {
4342		if (*dst == '\r') {
4343		    *dst = '\n';
4344		}
4345	    }
4346	    srcLen = dstLen;
4347	    break;
4348	}
4349	case TCL_TRANSLATE_CRLF: {
4350	    char *dst;
4351	    CONST char *src, *srcEnd, *srcMax;
4352
4353	    dst = dstStart;
4354	    src = srcStart;
4355	    srcEnd = srcStart + dstLen;
4356	    srcMax = srcStart + *srcLenPtr;
4357
4358	    for ( ; src < srcEnd; ) {
4359		if (*src == '\r') {
4360		    src++;
4361		    if (src >= srcMax) {
4362			chanPtr->flags |= INPUT_NEED_NL;
4363		    } else if (*src == '\n') {
4364			*dst++ = *src++;
4365		    } else {
4366			*dst++ = '\r';
4367		    }
4368		} else {
4369		    *dst++ = *src++;
4370		}
4371	    }
4372	    srcLen = src - srcStart;
4373	    dstLen = dst - dstStart;
4374	    break;
4375	}
4376	case TCL_TRANSLATE_AUTO: {
4377	    char *dst;
4378	    CONST char *src, *srcEnd, *srcMax;
4379
4380	    dst = dstStart;
4381	    src = srcStart;
4382	    srcEnd = srcStart + dstLen;
4383	    srcMax = srcStart + *srcLenPtr;
4384
4385	    if ((chanPtr->flags & INPUT_SAW_CR) && (src < srcMax)) {
4386		if (*src == '\n') {
4387		    src++;
4388		}
4389		chanPtr->flags &= ~INPUT_SAW_CR;
4390	    }
4391	    for ( ; src < srcEnd; ) {
4392		if (*src == '\r') {
4393		    src++;
4394		    if (src >= srcMax) {
4395			chanPtr->flags |= INPUT_SAW_CR;
4396		    } else if (*src == '\n') {
4397			if (srcEnd < srcMax) {
4398			    srcEnd++;
4399			}
4400			src++;
4401		    }
4402		    *dst++ = '\n';
4403		} else {
4404		    *dst++ = *src++;
4405		}
4406	    }
4407	    srcLen = src - srcStart;
4408	    dstLen = dst - dstStart;
4409	    break;
4410	}
4411	default: {		/* lint. */
4412	    return 0;
4413	}
4414    }
4415    *dstLenPtr = dstLen;
4416
4417    if ((eof != NULL) && (srcStart + srcLen >= eof)) {
4418	/*
4419	 * EOF character was seen in EOL translated range.  Leave current
4420	 * file position pointing at the EOF character, but don't store the
4421	 * EOF character in the output string.
4422	 */
4423
4424	chanPtr->flags |= (CHANNEL_EOF | CHANNEL_STICKY_EOF);
4425	chanPtr->inputEncodingFlags |= TCL_ENCODING_END;
4426	chanPtr->flags &= ~(INPUT_SAW_CR | INPUT_NEED_NL);
4427	return 1;
4428    }
4429
4430    *srcLenPtr = srcLen;
4431    return 0;
4432}
4433
4434/*
4435 *----------------------------------------------------------------------
4436 *
4437 * Tcl_Ungets --
4438 *
4439 *	Causes the supplied string to be added to the input queue of
4440 *	the channel, at either the head or tail of the queue.
4441 *
4442 * Results:
4443 *	The number of bytes stored in the channel, or -1 on error.
4444 *
4445 * Side effects:
4446 *	Adds input to the input queue of a channel.
4447 *
4448 *----------------------------------------------------------------------
4449 */
4450
4451int
4452Tcl_Ungets(chan, str, len, atEnd)
4453    Tcl_Channel chan;		/* The channel for which to add the input. */
4454    char *str;			/* The input itself. */
4455    int len;			/* The length of the input. */
4456    int atEnd;			/* If non-zero, add at end of queue; otherwise
4457                                 * add at head of queue. */
4458{
4459    Channel *chanPtr;		/* The real IO channel. */
4460    ChannelBuffer *bufPtr;	/* Buffer to contain the data. */
4461    int i, flags;
4462
4463    chanPtr = (Channel *) chan;
4464
4465    /*
4466     * CheckChannelErrors clears too many flag bits in this one case.
4467     */
4468
4469    flags = chanPtr->flags;
4470    if (CheckChannelErrors(chanPtr, TCL_READABLE) != 0) {
4471	len = -1;
4472	goto done;
4473    }
4474    chanPtr->flags = flags;
4475
4476    /*
4477     * If we have encountered a sticky EOF, just punt without storing.
4478     * (sticky EOF is set if we have seen the input eofChar, to prevent
4479     * reading beyond the eofChar). Otherwise, clear the EOF flags, and
4480     * clear the BLOCKED bit. We want to discover these conditions anew
4481     * in each operation.
4482     */
4483
4484    if (chanPtr->flags & CHANNEL_STICKY_EOF) {
4485	goto done;
4486    }
4487    chanPtr->flags &= (~(CHANNEL_BLOCKED | CHANNEL_EOF));
4488
4489    bufPtr = AllocChannelBuffer(len);
4490    for (i = 0; i < len; i++) {
4491        bufPtr->buf[i] = str[i];
4492    }
4493    bufPtr->nextAdded += len;
4494
4495    if (chanPtr->inQueueHead == (ChannelBuffer *) NULL) {
4496        bufPtr->nextPtr = (ChannelBuffer *) NULL;
4497        chanPtr->inQueueHead = bufPtr;
4498        chanPtr->inQueueTail = bufPtr;
4499    } else if (atEnd) {
4500        bufPtr->nextPtr = (ChannelBuffer *) NULL;
4501        chanPtr->inQueueTail->nextPtr = bufPtr;
4502        chanPtr->inQueueTail = bufPtr;
4503    } else {
4504        bufPtr->nextPtr = chanPtr->inQueueHead;
4505        chanPtr->inQueueHead = bufPtr;
4506    }
4507
4508    done:
4509    /*
4510     * Update the notifier state so we don't block while there is still
4511     * data in the buffers.
4512     */
4513
4514    UpdateInterest(chanPtr);
4515    return len;
4516}
4517
4518/*
4519 *----------------------------------------------------------------------
4520 *
4521 * Tcl_Flush --
4522 *
4523 *	Flushes output data on a channel.
4524 *
4525 * Results:
4526 *	A standard Tcl result.
4527 *
4528 * Side effects:
4529 *	May flush output queued on this channel.
4530 *
4531 *----------------------------------------------------------------------
4532 */
4533
4534int
4535Tcl_Flush(chan)
4536    Tcl_Channel chan;			/* The Channel to flush. */
4537{
4538    int result;				/* Of calling FlushChannel. */
4539    Channel *chanPtr;			/* The actual channel. */
4540
4541    chanPtr = (Channel *) chan;
4542    if (CheckChannelErrors(chanPtr, TCL_WRITABLE) != 0) {
4543	return -1;
4544    }
4545
4546    /*
4547     * Force current output buffer to be output also.
4548     */
4549
4550    if ((chanPtr->curOutPtr != NULL)
4551	    && (chanPtr->curOutPtr->nextAdded > 0)) {
4552        chanPtr->flags |= BUFFER_READY;
4553    }
4554
4555    result = FlushChannel(NULL, chanPtr, 0);
4556    if (result != 0) {
4557        return TCL_ERROR;
4558    }
4559
4560    return TCL_OK;
4561}
4562
4563/*
4564 *----------------------------------------------------------------------
4565 *
4566 * DiscardInputQueued --
4567 *
4568 *	Discards any input read from the channel but not yet consumed
4569 *	by Tcl reading commands.
4570 *
4571 * Results:
4572 *	None.
4573 *
4574 * Side effects:
4575 *	May discard input from the channel. If discardLastBuffer is zero,
4576 *	leaves one buffer in place for back-filling.
4577 *
4578 *----------------------------------------------------------------------
4579 */
4580
4581static void
4582DiscardInputQueued(chanPtr, discardSavedBuffers)
4583    Channel *chanPtr;		/* Channel on which to discard
4584                                 * the queued input. */
4585    int discardSavedBuffers;	/* If non-zero, discard all buffers including
4586                                 * last one. */
4587{
4588    ChannelBuffer *bufPtr, *nxtPtr;	/* Loop variables. */
4589
4590    bufPtr = chanPtr->inQueueHead;
4591    chanPtr->inQueueHead = (ChannelBuffer *) NULL;
4592    chanPtr->inQueueTail = (ChannelBuffer *) NULL;
4593    for (; bufPtr != (ChannelBuffer *) NULL; bufPtr = nxtPtr) {
4594        nxtPtr = bufPtr->nextPtr;
4595        RecycleBuffer(chanPtr, bufPtr, discardSavedBuffers);
4596    }
4597
4598    /*
4599     * If discardSavedBuffers is nonzero, must also discard any previously
4600     * saved buffer in the saveInBufPtr field.
4601     */
4602
4603    if (discardSavedBuffers) {
4604        if (chanPtr->saveInBufPtr != (ChannelBuffer *) NULL) {
4605            ckfree((char *) chanPtr->saveInBufPtr);
4606            chanPtr->saveInBufPtr = (ChannelBuffer *) NULL;
4607        }
4608    }
4609}
4610
4611/*
4612 *---------------------------------------------------------------------------
4613 *
4614 * GetInput --
4615 *
4616 *	Reads input data from a device into a channel buffer.
4617 *
4618 * Results:
4619 *	The return value is the Posix error code if an error occurred while
4620 *	reading from the file, or 0 otherwise.
4621 *
4622 * Side effects:
4623 *	Reads from the underlying device.
4624 *
4625 *---------------------------------------------------------------------------
4626 */
4627
4628static int
4629GetInput(chanPtr)
4630    Channel *chanPtr;		/* Channel to read input from. */
4631{
4632    int toRead;			/* How much to read? */
4633    int result;			/* Of calling driver. */
4634    int nread;			/* How much was read from channel? */
4635    ChannelBuffer *bufPtr;	/* New buffer to add to input queue. */
4636
4637    /*
4638     * Prevent reading from a dead channel -- a channel that has been closed
4639     * but not yet deallocated, which can happen if the exit handler for
4640     * channel cleanup has run but the channel is still registered in some
4641     * interpreter.
4642     */
4643
4644    if (CheckForDeadChannel(NULL, chanPtr)) {
4645	return EINVAL;
4646    }
4647
4648    /*
4649     * See if we can fill an existing buffer. If we can, read only
4650     * as much as will fit in it. Otherwise allocate a new buffer,
4651     * add it to the input queue and attempt to fill it to the max.
4652     */
4653
4654    bufPtr = chanPtr->inQueueTail;
4655    if ((bufPtr != NULL) && (bufPtr->nextAdded < bufPtr->bufLength)) {
4656        toRead = bufPtr->bufLength - bufPtr->nextAdded;
4657    } else {
4658	bufPtr = chanPtr->saveInBufPtr;
4659	chanPtr->saveInBufPtr = NULL;
4660	if (bufPtr == NULL) {
4661	    bufPtr = AllocChannelBuffer(chanPtr->bufSize);
4662	}
4663        bufPtr->nextPtr = (ChannelBuffer *) NULL;
4664
4665        toRead = chanPtr->bufSize;
4666        if (chanPtr->inQueueTail == NULL) {
4667            chanPtr->inQueueHead = bufPtr;
4668        } else {
4669            chanPtr->inQueueTail->nextPtr = bufPtr;
4670        }
4671        chanPtr->inQueueTail = bufPtr;
4672    }
4673
4674    /*
4675     * If EOF is set, we should avoid calling the driver because on some
4676     * platforms it is impossible to read from a device after EOF.
4677     */
4678
4679    if (chanPtr->flags & CHANNEL_EOF) {
4680	return 0;
4681    }
4682
4683    nread = (*chanPtr->typePtr->inputProc)(chanPtr->instanceData,
4684	    bufPtr->buf + bufPtr->nextAdded, toRead, &result);
4685
4686    if (nread > 0) {
4687	bufPtr->nextAdded += nread;
4688
4689	/*
4690	 * If we get a short read, signal up that we may be BLOCKED. We
4691	 * should avoid calling the driver because on some platforms we
4692	 * will block in the low level reading code even though the
4693	 * channel is set into nonblocking mode.
4694	 */
4695
4696	if (nread < toRead) {
4697	    chanPtr->flags |= CHANNEL_BLOCKED;
4698	}
4699    } else if (nread == 0) {
4700	chanPtr->flags |= CHANNEL_EOF;
4701	chanPtr->inputEncodingFlags |= TCL_ENCODING_END;
4702    } else if (nread < 0) {
4703	if ((result == EWOULDBLOCK) || (result == EAGAIN)) {
4704	    chanPtr->flags |= CHANNEL_BLOCKED;
4705	    result = EAGAIN;
4706	}
4707	Tcl_SetErrno(result);
4708	return result;
4709    }
4710    return 0;
4711}
4712
4713/*
4714 *----------------------------------------------------------------------
4715 *
4716 * Tcl_Seek --
4717 *
4718 *	Implements seeking on Tcl Channels. This is a public function
4719 *	so that other C facilities may be implemented on top of it.
4720 *
4721 * Results:
4722 *	The new access point or -1 on error. If error, use Tcl_GetErrno()
4723 *	to retrieve the POSIX error code for the error that occurred.
4724 *
4725 * Side effects:
4726 *	May flush output on the channel. May discard queued input.
4727 *
4728 *----------------------------------------------------------------------
4729 */
4730
4731int
4732Tcl_Seek(chan, offset, mode)
4733    Tcl_Channel chan;		/* The channel on which to seek. */
4734    int offset;			/* Offset to seek to. */
4735    int mode;			/* Relative to which location to seek? */
4736{
4737    Channel *chanPtr;		/* The real IO channel. */
4738    ChannelBuffer *bufPtr;
4739    int inputBuffered, outputBuffered;
4740    int result;			/* Of device driver operations. */
4741    int curPos;			/* Position on the device. */
4742    int wasAsync;		/* Was the channel nonblocking before the
4743                                 * seek operation? If so, must restore to
4744                                 * nonblocking mode after the seek. */
4745
4746    chanPtr = (Channel *) chan;
4747    if (CheckChannelErrors(chanPtr, TCL_WRITABLE | TCL_READABLE) != 0) {
4748	return -1;
4749    }
4750
4751    /*
4752     * Disallow seek on dead channels -- channels that have been closed but
4753     * not yet been deallocated. Such channels can be found if the exit
4754     * handler for channel cleanup has run but the channel is still
4755     * registered in an interpreter.
4756     */
4757
4758    if (CheckForDeadChannel(NULL,chanPtr)) return -1;
4759
4760    /*
4761     * Disallow seek on channels whose type does not have a seek procedure
4762     * defined. This means that the channel does not support seeking.
4763     */
4764
4765    if (chanPtr->typePtr->seekProc == (Tcl_DriverSeekProc *) NULL) {
4766        Tcl_SetErrno(EINVAL);
4767        return -1;
4768    }
4769
4770    /*
4771     * Compute how much input and output is buffered. If both input and
4772     * output is buffered, cannot compute the current position.
4773     */
4774
4775    for (bufPtr = chanPtr->inQueueHead, inputBuffered = 0;
4776             bufPtr != (ChannelBuffer *) NULL;
4777             bufPtr = bufPtr->nextPtr) {
4778        inputBuffered += (bufPtr->nextAdded - bufPtr->nextRemoved);
4779    }
4780    for (bufPtr = chanPtr->outQueueHead, outputBuffered = 0;
4781             bufPtr != (ChannelBuffer *) NULL;
4782             bufPtr = bufPtr->nextPtr) {
4783        outputBuffered += (bufPtr->nextAdded - bufPtr->nextRemoved);
4784    }
4785    if ((chanPtr->curOutPtr != (ChannelBuffer *) NULL) &&
4786           (chanPtr->curOutPtr->nextAdded > chanPtr->curOutPtr->nextRemoved)) {
4787        chanPtr->flags |= BUFFER_READY;
4788        outputBuffered +=
4789            (chanPtr->curOutPtr->nextAdded - chanPtr->curOutPtr->nextRemoved);
4790    }
4791
4792    if ((inputBuffered != 0) && (outputBuffered != 0)) {
4793        Tcl_SetErrno(EFAULT);
4794        return -1;
4795    }
4796
4797    /*
4798     * If we are seeking relative to the current position, compute the
4799     * corrected offset taking into account the amount of unread input.
4800     */
4801
4802    if (mode == SEEK_CUR) {
4803        offset -= inputBuffered;
4804    }
4805
4806    /*
4807     * Discard any queued input - this input should not be read after
4808     * the seek.
4809     */
4810
4811    DiscardInputQueued(chanPtr, 0);
4812
4813    /*
4814     * Reset EOF and BLOCKED flags. We invalidate them by moving the
4815     * access point. Also clear CR related flags.
4816     */
4817
4818    chanPtr->flags &=
4819        (~(CHANNEL_EOF | CHANNEL_STICKY_EOF | CHANNEL_BLOCKED | INPUT_SAW_CR));
4820
4821    /*
4822     * If the channel is in asynchronous output mode, switch it back
4823     * to synchronous mode and cancel any async flush that may be
4824     * scheduled. After the flush, the channel will be put back into
4825     * asynchronous output mode.
4826     */
4827
4828    wasAsync = 0;
4829    if (chanPtr->flags & CHANNEL_NONBLOCKING) {
4830        wasAsync = 1;
4831        result = 0;
4832        if (chanPtr->typePtr->blockModeProc != NULL) {
4833            result = (chanPtr->typePtr->blockModeProc) (chanPtr->instanceData,
4834                    TCL_MODE_BLOCKING);
4835        }
4836        if (result != 0) {
4837            Tcl_SetErrno(result);
4838            return -1;
4839        }
4840        chanPtr->flags &= (~(CHANNEL_NONBLOCKING));
4841        if (chanPtr->flags & BG_FLUSH_SCHEDULED) {
4842            chanPtr->flags &= (~(BG_FLUSH_SCHEDULED));
4843        }
4844    }
4845
4846    /*
4847     * If the flush fails we cannot recover the original position. In
4848     * that case the seek is not attempted because we do not know where
4849     * the access position is - instead we return the error. FlushChannel
4850     * has already called Tcl_SetErrno() to report the error upwards.
4851     * If the flush succeeds we do the seek also.
4852     */
4853
4854    if (FlushChannel(NULL, chanPtr, 0) != 0) {
4855        curPos = -1;
4856    } else {
4857
4858        /*
4859         * Now seek to the new position in the channel as requested by the
4860         * caller.
4861         */
4862
4863        curPos = (chanPtr->typePtr->seekProc) (chanPtr->instanceData,
4864                (long) offset, mode, &result);
4865        if (curPos == -1) {
4866            Tcl_SetErrno(result);
4867        }
4868    }
4869
4870    /*
4871     * Restore to nonblocking mode if that was the previous behavior.
4872     *
4873     * NOTE: Even if there was an async flush active we do not restore
4874     * it now because we already flushed all the queued output, above.
4875     */
4876
4877    if (wasAsync) {
4878        chanPtr->flags |= CHANNEL_NONBLOCKING;
4879        result = 0;
4880        if (chanPtr->typePtr->blockModeProc != NULL) {
4881            result = (chanPtr->typePtr->blockModeProc) (chanPtr->instanceData,
4882                    TCL_MODE_NONBLOCKING);
4883        }
4884        if (result != 0) {
4885            Tcl_SetErrno(result);
4886            return -1;
4887        }
4888    }
4889
4890    return curPos;
4891}
4892
4893/*
4894 *----------------------------------------------------------------------
4895 *
4896 * Tcl_Tell --
4897 *
4898 *	Returns the position of the next character to be read/written on
4899 *	this channel.
4900 *
4901 * Results:
4902 *	A nonnegative integer on success, -1 on failure. If failed,
4903 *	use Tcl_GetErrno() to retrieve the POSIX error code for the
4904 *	error that occurred.
4905 *
4906 * Side effects:
4907 *	None.
4908 *
4909 *----------------------------------------------------------------------
4910 */
4911
4912int
4913Tcl_Tell(chan)
4914    Tcl_Channel chan;			/* The channel to return pos for. */
4915{
4916    Channel *chanPtr;			/* The actual channel to tell on. */
4917    ChannelBuffer *bufPtr;
4918    int inputBuffered, outputBuffered;
4919    int result;				/* Of calling device driver. */
4920    int curPos;				/* Position on device. */
4921
4922    chanPtr = (Channel *) chan;
4923    if (CheckChannelErrors(chanPtr, TCL_WRITABLE | TCL_READABLE) != 0) {
4924	return -1;
4925    }
4926
4927    /*
4928     * Disallow tell on dead channels -- channels that have been closed but
4929     * not yet been deallocated. Such channels can be found if the exit
4930     * handler for channel cleanup has run but the channel is still
4931     * registered in an interpreter.
4932     */
4933
4934    if (CheckForDeadChannel(NULL,chanPtr)) {
4935	return -1;
4936    }
4937
4938    /*
4939     * Disallow tell on channels whose type does not have a seek procedure
4940     * defined. This means that the channel does not support seeking.
4941     */
4942
4943    if (chanPtr->typePtr->seekProc == (Tcl_DriverSeekProc *) NULL) {
4944        Tcl_SetErrno(EINVAL);
4945        return -1;
4946    }
4947
4948    /*
4949     * Compute how much input and output is buffered. If both input and
4950     * output is buffered, cannot compute the current position.
4951     */
4952
4953    for (bufPtr = chanPtr->inQueueHead, inputBuffered = 0;
4954             bufPtr != (ChannelBuffer *) NULL;
4955             bufPtr = bufPtr->nextPtr) {
4956        inputBuffered += (bufPtr->nextAdded - bufPtr->nextRemoved);
4957    }
4958    for (bufPtr = chanPtr->outQueueHead, outputBuffered = 0;
4959             bufPtr != (ChannelBuffer *) NULL;
4960             bufPtr = bufPtr->nextPtr) {
4961        outputBuffered += (bufPtr->nextAdded - bufPtr->nextRemoved);
4962    }
4963    if ((chanPtr->curOutPtr != (ChannelBuffer *) NULL) &&
4964           (chanPtr->curOutPtr->nextAdded > chanPtr->curOutPtr->nextRemoved)) {
4965        chanPtr->flags |= BUFFER_READY;
4966        outputBuffered +=
4967            (chanPtr->curOutPtr->nextAdded - chanPtr->curOutPtr->nextRemoved);
4968    }
4969
4970    if ((inputBuffered != 0) && (outputBuffered != 0)) {
4971        Tcl_SetErrno(EFAULT);
4972        return -1;
4973    }
4974
4975    /*
4976     * Get the current position in the device and compute the position
4977     * where the next character will be read or written.
4978     */
4979
4980    curPos = (chanPtr->typePtr->seekProc) (chanPtr->instanceData,
4981            (long) 0, SEEK_CUR, &result);
4982    if (curPos == -1) {
4983        Tcl_SetErrno(result);
4984        return -1;
4985    }
4986    if (inputBuffered != 0) {
4987        return (curPos - inputBuffered);
4988    }
4989    return (curPos + outputBuffered);
4990}
4991
4992/*
4993 *---------------------------------------------------------------------------
4994 *
4995 * CheckChannelErrors --
4996 *
4997 *	See if the channel is in an ready state and can perform the
4998 *	desired operation.
4999 *
5000 * Results:
5001 *	The return value is 0 if the channel is OK, otherwise the
5002 *	return value is -1 and errno is set to indicate the error.
5003 *
5004 * Side effects:
5005 *	May clear the EOF and/or BLOCKED bits if reading from channel.
5006 *
5007 *---------------------------------------------------------------------------
5008 */
5009
5010static int
5011CheckChannelErrors(chanPtr, direction)
5012    Channel *chanPtr;	    /* Channel to check. */
5013    int direction;	    /* Test if channel supports desired operation:
5014			     * TCL_READABLE, TCL_WRITABLE. */
5015{
5016    /*
5017     * Check for unreported error.
5018     */
5019
5020    if (chanPtr->unreportedError != 0) {
5021        Tcl_SetErrno(chanPtr->unreportedError);
5022        chanPtr->unreportedError = 0;
5023        return -1;
5024    }
5025
5026    /*
5027     * Fail if the channel is not opened for desired operation.
5028     */
5029
5030    if ((chanPtr->flags & direction) == 0) {
5031        Tcl_SetErrno(EACCES);
5032        return -1;
5033    }
5034
5035    /*
5036     * Fail if the channel is in the middle of a background copy.
5037     */
5038
5039    if (chanPtr->csPtr != NULL) {
5040	Tcl_SetErrno(EBUSY);
5041	return -1;
5042    }
5043
5044    if (direction == TCL_READABLE) {
5045	/*
5046	 * If we have not encountered a sticky EOF, clear the EOF bit
5047	 * (sticky EOF is set if we have seen the input eofChar, to prevent
5048	 * reading beyond the eofChar). Also, always clear the BLOCKED bit.
5049	 * We want to discover these conditions anew in each operation.
5050	 */
5051
5052	if ((chanPtr->flags & CHANNEL_STICKY_EOF) == 0) {
5053	    chanPtr->flags &= ~CHANNEL_EOF;
5054	}
5055	chanPtr->flags &= ~(CHANNEL_BLOCKED | CHANNEL_NEED_MORE_DATA);
5056    }
5057
5058    return 0;
5059}
5060
5061/*
5062 *----------------------------------------------------------------------
5063 *
5064 * Tcl_Eof --
5065 *
5066 *	Returns 1 if the channel is at EOF, 0 otherwise.
5067 *
5068 * Results:
5069 *	1 or 0, always.
5070 *
5071 * Side effects:
5072 *	None.
5073 *
5074 *----------------------------------------------------------------------
5075 */
5076
5077int
5078Tcl_Eof(chan)
5079    Tcl_Channel chan;			/* Does this channel have EOF? */
5080{
5081    Channel *chanPtr;		/* The real channel structure. */
5082
5083    chanPtr = (Channel *) chan;
5084    return ((chanPtr->flags & CHANNEL_STICKY_EOF) ||
5085            ((chanPtr->flags & CHANNEL_EOF) && (Tcl_InputBuffered(chan) == 0)))
5086        ? 1 : 0;
5087}
5088
5089/*
5090 *----------------------------------------------------------------------
5091 *
5092 * Tcl_InputBlocked --
5093 *
5094 *	Returns 1 if input is blocked on this channel, 0 otherwise.
5095 *
5096 * Results:
5097 *	0 or 1, always.
5098 *
5099 * Side effects:
5100 *	None.
5101 *
5102 *----------------------------------------------------------------------
5103 */
5104
5105int
5106Tcl_InputBlocked(chan)
5107    Tcl_Channel chan;			/* Is this channel blocked? */
5108{
5109    Channel *chanPtr;		/* The real channel structure. */
5110
5111    chanPtr = (Channel *) chan;
5112    return (chanPtr->flags & CHANNEL_BLOCKED) ? 1 : 0;
5113}
5114
5115/*
5116 *----------------------------------------------------------------------
5117 *
5118 * Tcl_InputBuffered --
5119 *
5120 *	Returns the number of bytes of input currently buffered in the
5121 *	internal buffer of a channel.
5122 *
5123 * Results:
5124 *	The number of input bytes buffered, or zero if the channel is not
5125 *	open for reading.
5126 *
5127 * Side effects:
5128 *	None.
5129 *
5130 *----------------------------------------------------------------------
5131 */
5132
5133int
5134Tcl_InputBuffered(chan)
5135    Tcl_Channel chan;			/* The channel to query. */
5136{
5137    Channel *chanPtr;
5138    int bytesBuffered;
5139    ChannelBuffer *bufPtr;
5140
5141    chanPtr = (Channel *) chan;
5142    for (bytesBuffered = 0, bufPtr = chanPtr->inQueueHead;
5143             bufPtr != (ChannelBuffer *) NULL;
5144             bufPtr = bufPtr->nextPtr) {
5145        bytesBuffered += (bufPtr->nextAdded - bufPtr->nextRemoved);
5146    }
5147    return bytesBuffered;
5148}
5149
5150/*
5151 *----------------------------------------------------------------------
5152 *
5153 * Tcl_SetChannelBufferSize --
5154 *
5155 *	Sets the size of buffers to allocate to store input or output
5156 *	in the channel. The size must be between 10 bytes and 1 MByte.
5157 *
5158 * Results:
5159 *	None.
5160 *
5161 * Side effects:
5162 *	Sets the size of buffers subsequently allocated for this channel.
5163 *
5164 *----------------------------------------------------------------------
5165 */
5166
5167void
5168Tcl_SetChannelBufferSize(chan, sz)
5169    Tcl_Channel chan;			/* The channel whose buffer size
5170                                         * to set. */
5171    int sz;				/* The size to set. */
5172{
5173    Channel *chanPtr;
5174
5175    /*
5176     * If the buffer size is smaller than 10 bytes or larger than one MByte,
5177     * do not accept the requested size and leave the current buffer size.
5178     */
5179
5180    if (sz < 10) {
5181        return;
5182    }
5183    if (sz > (1024 * 1024)) {
5184        return;
5185    }
5186
5187    chanPtr = (Channel *) chan;
5188    chanPtr->bufSize = sz;
5189
5190    if (chanPtr->outputStage != NULL) {
5191	ckfree((char *) chanPtr->outputStage);
5192	chanPtr->outputStage = NULL;
5193    }
5194    if ((chanPtr->encoding != NULL) && (chanPtr->flags & TCL_WRITABLE)) {
5195	chanPtr->outputStage = (char *)
5196		ckalloc((unsigned) (chanPtr->bufSize + 2));
5197    }
5198}
5199
5200/*
5201 *----------------------------------------------------------------------
5202 *
5203 * Tcl_GetChannelBufferSize --
5204 *
5205 *	Retrieves the size of buffers to allocate for this channel.
5206 *
5207 * Results:
5208 *	The size.
5209 *
5210 * Side effects:
5211 *	None.
5212 *
5213 *----------------------------------------------------------------------
5214 */
5215
5216int
5217Tcl_GetChannelBufferSize(chan)
5218    Tcl_Channel chan;		/* The channel for which to find the
5219                                 * buffer size. */
5220{
5221    Channel *chanPtr;
5222
5223    chanPtr = (Channel *) chan;
5224    return chanPtr->bufSize;
5225}
5226
5227/*
5228 *----------------------------------------------------------------------
5229 *
5230 * Tcl_BadChannelOption --
5231 *
5232 *	This procedure generates a "bad option" error message in an
5233 *	(optional) interpreter.  It is used by channel drivers when
5234 *      a invalid Set/Get option is requested. Its purpose is to concatenate
5235 *      the generic options list to the specific ones and factorize
5236 *      the generic options error message string.
5237 *
5238 * Results:
5239 *	TCL_ERROR.
5240 *
5241 * Side effects:
5242 *	An error message is generated in interp's result object to
5243 *	indicate that a command was invoked with the a bad option
5244 *	The message has the form
5245 *		bad option "blah": should be one of
5246 *              <...generic options...>+<...specific options...>
5247 *	"blah" is the optionName argument and "<specific options>"
5248 *	is a space separated list of specific option words.
5249 *      The function takes good care of inserting minus signs before
5250 *      each option, commas after, and an "or" before the last option.
5251 *
5252 *----------------------------------------------------------------------
5253 */
5254
5255int
5256Tcl_BadChannelOption(interp, optionName, optionList)
5257    Tcl_Interp *interp;			/* Current interpreter. (can be NULL)*/
5258    char *optionName;			/* 'bad option' name */
5259    char *optionList;			/* Specific options list to append
5260					 * to the standard generic options.
5261					 * can be NULL for generic options
5262					 * only.
5263					 */
5264{
5265    if (interp) {
5266	CONST char *genericopt =
5267	    	"blocking buffering buffersize byteorder eofchar translation";
5268	char **argv;
5269	int  argc, i;
5270	Tcl_DString ds;
5271
5272	Tcl_DStringInit(&ds);
5273	Tcl_DStringAppend(&ds, (char *) genericopt, -1);
5274	if (optionList && (*optionList)) {
5275	    Tcl_DStringAppend(&ds, " ", 1);
5276	    Tcl_DStringAppend(&ds, optionList, -1);
5277	}
5278	if (Tcl_SplitList(interp, Tcl_DStringValue(&ds),
5279	      	  &argc, &argv) != TCL_OK) {
5280	    panic("malformed option list in channel driver");
5281	}
5282	Tcl_ResetResult(interp);
5283	Tcl_AppendResult(interp, "bad option \"", optionName,
5284		 "\": should be one of ", (char *) NULL);
5285	argc--;
5286	for (i = 0; i < argc; i++) {
5287	    Tcl_AppendResult(interp, "-", argv[i], ", ", (char *) NULL);
5288	}
5289	Tcl_AppendResult(interp, "or -", argv[i], (char *) NULL);
5290	Tcl_DStringFree(&ds);
5291	ckfree((char *) argv);
5292    }
5293    Tcl_SetErrno(EINVAL);
5294    return TCL_ERROR;
5295}
5296
5297/* Andreas Kupries <a.kupries@westend.com>, 05/31/1997.
5298 * "Trf-Patch for channels with a switchable byteorder"
5299 * Exported functionality.
5300 */
5301
5302/*
5303 *----------------------------------------------------------------------
5304 *
5305 * Tcl_GetChannelByteorder --
5306 *
5307 *	Retrieves the byteorder set for this channel.
5308 *
5309 * Results:
5310 *	The size.
5311 *
5312 * Side effects:
5313 *	None.
5314 *
5315 *----------------------------------------------------------------------
5316 */
5317
5318int
5319Tcl_GetChannelByteorder(chan)
5320    Tcl_Channel chan;		/* The channel for which to find the
5321                                 * buffer size. */
5322{
5323    Channel *chanPtr;
5324
5325    chanPtr = (Channel *) chan;
5326    return ((chanPtr->flags & CHANNEL_IS_SMALLENDIAN) != 0);
5327}
5328
5329/*
5330 *----------------------------------------------------------------------
5331 *
5332 * Tcl_GetChannelOption --
5333 *
5334 *	Gets a mode associated with an IO channel. If the optionName arg
5335 *	is non NULL, retrieves the value of that option. If the optionName
5336 *	arg is NULL, retrieves a list of alternating option names and
5337 *	values for the given channel.
5338 *
5339 * Results:
5340 *	A standard Tcl result. Also sets the supplied DString to the
5341 *	string value of the option(s) returned.
5342 *
5343 * Side effects:
5344 *      None.
5345 *
5346 *----------------------------------------------------------------------
5347 */
5348
5349int
5350Tcl_GetChannelOption(interp, chan, optionName, dsPtr)
5351    Tcl_Interp *interp;		/* For error reporting - can be NULL. */
5352    Tcl_Channel chan;		/* Channel on which to get option. */
5353    char *optionName;		/* Option to get. */
5354    Tcl_DString *dsPtr;		/* Where to store value(s). */
5355{
5356    size_t len;			/* Length of optionName string. */
5357    char optionVal[128];	/* Buffer for sprintf. */
5358    Channel *chanPtr = (Channel *) chan;
5359    int flags;
5360
5361    /*
5362     * If we are in the middle of a background copy, use the saved flags.
5363     */
5364
5365    if (chanPtr->csPtr) {
5366	if (chanPtr == chanPtr->csPtr->readPtr) {
5367	    flags = chanPtr->csPtr->readFlags;
5368	} else {
5369	    flags = chanPtr->csPtr->writeFlags;
5370	}
5371    } else {
5372	flags = chanPtr->flags;
5373    }
5374
5375    /*
5376     * Disallow options on dead channels -- channels that have been closed but
5377     * not yet been deallocated. Such channels can be found if the exit
5378     * handler for channel cleanup has run but the channel is still
5379     * registered in an interpreter.
5380     */
5381
5382    if (CheckForDeadChannel(interp,chanPtr)) return TCL_ERROR;
5383
5384    /*
5385     * If the optionName is NULL it means that we want a list of all
5386     * options and values.
5387     */
5388
5389    if (optionName == (char *) NULL) {
5390        len = 0;
5391    } else {
5392        len = strlen(optionName);
5393    }
5394
5395    if ((len == 0) || ((len > 2) && (optionName[1] == 'b') &&
5396            (strncmp(optionName, "-blocking", len) == 0))) {
5397        if (len == 0) {
5398            Tcl_DStringAppendElement(dsPtr, "-blocking");
5399        }
5400        Tcl_DStringAppendElement(dsPtr,
5401		(flags & CHANNEL_NONBLOCKING) ? "0" : "1");
5402        if (len > 0) {
5403            return TCL_OK;
5404        }
5405    }
5406    if ((len == 0) || ((len > 7) && (optionName[1] == 'b') &&
5407            (strncmp(optionName, "-buffering", len) == 0))) {
5408        if (len == 0) {
5409            Tcl_DStringAppendElement(dsPtr, "-buffering");
5410        }
5411        if (flags & CHANNEL_LINEBUFFERED) {
5412            Tcl_DStringAppendElement(dsPtr, "line");
5413        } else if (flags & CHANNEL_UNBUFFERED) {
5414            Tcl_DStringAppendElement(dsPtr, "none");
5415        } else {
5416            Tcl_DStringAppendElement(dsPtr, "full");
5417        }
5418        if (len > 0) {
5419            return TCL_OK;
5420        }
5421    }
5422    if ((len == 0) || ((len > 7) && (optionName[1] == 'b') &&
5423            (strncmp(optionName, "-buffersize", len) == 0))) {
5424        if (len == 0) {
5425            Tcl_DStringAppendElement(dsPtr, "-buffersize");
5426        }
5427        TclFormatInt(optionVal, chanPtr->bufSize);
5428        Tcl_DStringAppendElement(dsPtr, optionVal);
5429        if (len > 0) {
5430            return TCL_OK;
5431        }
5432    }
5433    if ((len == 0) ||
5434	    ((len > 2) && (optionName[1] == 'e') &&
5435		    (strncmp(optionName, "-encoding", len) == 0))) {
5436	if (len == 0) {
5437	    Tcl_DStringAppendElement(dsPtr, "-encoding");
5438	}
5439	if (chanPtr->encoding == NULL) {
5440	    Tcl_DStringAppendElement(dsPtr, "binary");
5441	} else {
5442	    Tcl_DStringAppendElement(dsPtr,
5443		    Tcl_GetEncodingName(chanPtr->encoding));
5444	}
5445	if (len > 0) {
5446	    return TCL_OK;
5447	}
5448    }
5449
5450    /* Andreas Kupries <a.kupries@westend.com>, 05/31/1997.
5451     * "Trf-Patch for channels with a switchable byteorder"
5452     * Location: Tcl_GetChannelOption
5453     */
5454
5455    if ((len == 0) || ((len > 2) && (optionName[1] == 'b') &&
5456		       (strncmp(optionName, "-byteorder", len) == 0))) {
5457      if (len == 0) {
5458        Tcl_DStringAppendElement(dsPtr, "-byteorder");
5459      }
5460      Tcl_DStringAppendElement(dsPtr,
5461			       (chanPtr->flags & CHANNEL_IS_SMALLENDIAN) ?
5462			       "smallendian" : "bigendian");
5463      if (len > 0) {
5464        return TCL_OK;
5465      }
5466    }
5467
5468    if ((len == 0) ||
5469            ((len > 2) && (optionName[1] == 'e') &&
5470                    (strncmp(optionName, "-eofchar", len) == 0))) {
5471        if (len == 0) {
5472            Tcl_DStringAppendElement(dsPtr, "-eofchar");
5473        }
5474        if (((flags & (TCL_READABLE|TCL_WRITABLE)) ==
5475                (TCL_READABLE|TCL_WRITABLE)) && (len == 0)) {
5476            Tcl_DStringStartSublist(dsPtr);
5477        }
5478        if (flags & TCL_READABLE) {
5479            if (chanPtr->inEofChar == 0) {
5480                Tcl_DStringAppendElement(dsPtr, "");
5481            } else {
5482                char buf[4];
5483
5484                sprintf(buf, "%c", chanPtr->inEofChar);
5485                Tcl_DStringAppendElement(dsPtr, buf);
5486            }
5487        }
5488        if (flags & TCL_WRITABLE) {
5489            if (chanPtr->outEofChar == 0) {
5490                Tcl_DStringAppendElement(dsPtr, "");
5491            } else {
5492                char buf[4];
5493
5494                sprintf(buf, "%c", chanPtr->outEofChar);
5495                Tcl_DStringAppendElement(dsPtr, buf);
5496            }
5497        }
5498        if (((flags & (TCL_READABLE|TCL_WRITABLE)) ==
5499                (TCL_READABLE|TCL_WRITABLE)) && (len == 0)) {
5500            Tcl_DStringEndSublist(dsPtr);
5501        }
5502        if (len > 0) {
5503            return TCL_OK;
5504        }
5505    }
5506    if ((len == 0) ||
5507            ((len > 1) && (optionName[1] == 't') &&
5508                    (strncmp(optionName, "-translation", len) == 0))) {
5509        if (len == 0) {
5510            Tcl_DStringAppendElement(dsPtr, "-translation");
5511        }
5512        if (((flags & (TCL_READABLE|TCL_WRITABLE)) ==
5513                (TCL_READABLE|TCL_WRITABLE)) && (len == 0)) {
5514            Tcl_DStringStartSublist(dsPtr);
5515        }
5516        if (flags & TCL_READABLE) {
5517            if (chanPtr->inputTranslation == TCL_TRANSLATE_AUTO) {
5518                Tcl_DStringAppendElement(dsPtr, "auto");
5519            } else if (chanPtr->inputTranslation == TCL_TRANSLATE_CR) {
5520                Tcl_DStringAppendElement(dsPtr, "cr");
5521            } else if (chanPtr->inputTranslation == TCL_TRANSLATE_CRLF) {
5522                Tcl_DStringAppendElement(dsPtr, "crlf");
5523            } else {
5524                Tcl_DStringAppendElement(dsPtr, "lf");
5525            }
5526        }
5527        if (flags & TCL_WRITABLE) {
5528            if (chanPtr->outputTranslation == TCL_TRANSLATE_AUTO) {
5529                Tcl_DStringAppendElement(dsPtr, "auto");
5530            } else if (chanPtr->outputTranslation == TCL_TRANSLATE_CR) {
5531                Tcl_DStringAppendElement(dsPtr, "cr");
5532            } else if (chanPtr->outputTranslation == TCL_TRANSLATE_CRLF) {
5533                Tcl_DStringAppendElement(dsPtr, "crlf");
5534            } else {
5535                Tcl_DStringAppendElement(dsPtr, "lf");
5536            }
5537        }
5538        if (((flags & (TCL_READABLE|TCL_WRITABLE)) ==
5539                (TCL_READABLE|TCL_WRITABLE)) && (len == 0)) {
5540            Tcl_DStringEndSublist(dsPtr);
5541        }
5542        if (len > 0) {
5543            return TCL_OK;
5544        }
5545    }
5546    if (chanPtr->typePtr->getOptionProc != (Tcl_DriverGetOptionProc *) NULL) {
5547	/*
5548	 * let the driver specific handle additional options
5549	 * and result code and message.
5550	 */
5551
5552        return (chanPtr->typePtr->getOptionProc) (chanPtr->instanceData,
5553		  interp, optionName, dsPtr);
5554    } else {
5555	/*
5556	 * no driver specific options case.
5557	 */
5558
5559        if (len == 0) {
5560            return TCL_OK;
5561        }
5562	return Tcl_BadChannelOption(interp, optionName, NULL);
5563    }
5564}
5565
5566/*
5567 *---------------------------------------------------------------------------
5568 *
5569 * Tcl_SetChannelOption --
5570 *
5571 *	Sets an option on a channel.
5572 *
5573 * Results:
5574 *	A standard Tcl result.  On error, sets interp's result object
5575 *	if interp is not NULL.
5576 *
5577 * Side effects:
5578 *	May modify an option on a device.
5579 *
5580 *---------------------------------------------------------------------------
5581 */
5582
5583int
5584Tcl_SetChannelOption(interp, chan, optionName, newValue)
5585    Tcl_Interp *interp;		/* For error reporting - can be NULL. */
5586    Tcl_Channel chan;		/* Channel on which to set mode. */
5587    char *optionName;		/* Which option to set? */
5588    char *newValue;		/* New value for option. */
5589{
5590    int newMode;		/* New (numeric) mode to sert. */
5591    Channel *chanPtr;		/* The real IO channel. */
5592    size_t len;			/* Length of optionName string. */
5593    int argc;
5594    char **argv;
5595
5596    chanPtr = (Channel *) chan;
5597
5598    /*
5599     * If the channel is in the middle of a background copy, fail.
5600     */
5601
5602    if (chanPtr->csPtr) {
5603	if (interp) {
5604	    Tcl_AppendResult(interp,
5605	         "unable to set channel options: background copy in progress",
5606		 (char *) NULL);
5607	}
5608        return TCL_ERROR;
5609    }
5610
5611
5612    /*
5613     * Disallow options on dead channels -- channels that have been closed but
5614     * not yet been deallocated. Such channels can be found if the exit
5615     * handler for channel cleanup has run but the channel is still
5616     * registered in an interpreter.
5617     */
5618
5619    if (CheckForDeadChannel(NULL,chanPtr)) return TCL_ERROR;
5620
5621    len = strlen(optionName);
5622
5623    if ((len > 2) && (optionName[1] == 'b') &&
5624            (strncmp(optionName, "-blocking", len) == 0)) {
5625        if (Tcl_GetBoolean(interp, newValue, &newMode) == TCL_ERROR) {
5626            return TCL_ERROR;
5627        }
5628        if (newMode) {
5629            newMode = TCL_MODE_BLOCKING;
5630        } else {
5631            newMode = TCL_MODE_NONBLOCKING;
5632        }
5633	return SetBlockMode(interp, chanPtr, newMode);
5634    } else if ((len > 7) && (optionName[1] == 'b') &&
5635            (strncmp(optionName, "-buffering", len) == 0)) {
5636        len = strlen(newValue);
5637        if ((newValue[0] == 'f') && (strncmp(newValue, "full", len) == 0)) {
5638            chanPtr->flags &=
5639                (~(CHANNEL_UNBUFFERED|CHANNEL_LINEBUFFERED));
5640        } else if ((newValue[0] == 'l') &&
5641                (strncmp(newValue, "line", len) == 0)) {
5642            chanPtr->flags &= (~(CHANNEL_UNBUFFERED));
5643            chanPtr->flags |= CHANNEL_LINEBUFFERED;
5644        } else if ((newValue[0] == 'n') &&
5645                (strncmp(newValue, "none", len) == 0)) {
5646            chanPtr->flags &= (~(CHANNEL_LINEBUFFERED));
5647            chanPtr->flags |= CHANNEL_UNBUFFERED;
5648        } else {
5649            if (interp) {
5650                Tcl_AppendResult(interp, "bad value for -buffering: ",
5651                        "must be one of full, line, or none",
5652                        (char *) NULL);
5653                return TCL_ERROR;
5654            }
5655        }
5656	return TCL_OK;
5657    } else if ((len > 7) && (optionName[1] == 'b') &&
5658            (strncmp(optionName, "-buffersize", len) == 0)) {
5659        chanPtr->bufSize = atoi(newValue);	/* INTL: "C", UTF safe. */
5660        if ((chanPtr->bufSize < 10) || (chanPtr->bufSize > (1024 * 1024))) {
5661            chanPtr->bufSize = CHANNELBUFFER_DEFAULT_SIZE;
5662        }
5663
5664	/* Andreas Kupries <a.kupries@westend.com>, 05/31/1997.
5665	 * "Trf-Patch for channels with a switchable byteorder"
5666	 * Location: Tcl_SetChannelOption.
5667	 */
5668
5669    } else if ((len > 2) && (optionName[1] == 'b') &&
5670	       (strncmp(optionName, "-byteorder", len) == 0)) {
5671      int nv_len = strlen (newValue);
5672
5673      if ((nv_len > 0) &&
5674	  (strncmp (newValue, "smallendian", nv_len) == 0)) {
5675	chanPtr->flags |= CHANNEL_IS_SMALLENDIAN;
5676	return TCL_OK;
5677      } else if ((nv_len > 0) &&
5678		 (strncmp (newValue, "littleendian", nv_len) == 0)) {
5679	chanPtr->flags |= CHANNEL_IS_SMALLENDIAN;
5680	return TCL_OK;
5681      } else if ((nv_len > 0) &&
5682		 (strncmp (newValue, "network", nv_len) == 0)) {
5683	chanPtr->flags &= ~CHANNEL_IS_SMALLENDIAN;
5684	return TCL_OK;
5685      } else if ((nv_len > 0) &&
5686		 (strncmp (newValue, "bigendian", nv_len) == 0)) {
5687	chanPtr->flags &= ~CHANNEL_IS_SMALLENDIAN;
5688	return TCL_OK;
5689      }
5690
5691      if (interp != (Tcl_Interp *) NULL) {
5692	Tcl_AppendResult(interp,
5693			 "bad value for -byteorder: ",
5694			 "must be one of smallendian, littleendian, bigendian or network",
5695			 (char *) NULL);
5696      }
5697      return TCL_ERROR;
5698    } else if ((len > 2) && (optionName[1] == 'e') &&
5699	    (strncmp(optionName, "-encoding", len) == 0)) {
5700	Tcl_Encoding encoding;
5701
5702	if ((newValue[0] == '\0') || (strcmp(newValue, "binary") == 0)) {
5703	    encoding = NULL;
5704	} else {
5705	    encoding = Tcl_GetEncoding(interp, newValue);
5706	    if (encoding == NULL) {
5707		return TCL_ERROR;
5708	    }
5709	}
5710	Tcl_FreeEncoding(chanPtr->encoding);
5711	chanPtr->encoding = encoding;
5712	chanPtr->inputEncodingState = NULL;
5713	chanPtr->inputEncodingFlags = TCL_ENCODING_START;
5714	chanPtr->outputEncodingState = NULL;
5715	chanPtr->outputEncodingFlags = TCL_ENCODING_START;
5716	chanPtr->flags &= ~CHANNEL_NEED_MORE_DATA;
5717	UpdateInterest(chanPtr);
5718    } else if ((len > 2) && (optionName[1] == 'e') &&
5719            (strncmp(optionName, "-eofchar", len) == 0)) {
5720        if (Tcl_SplitList(interp, newValue, &argc, &argv) == TCL_ERROR) {
5721            return TCL_ERROR;
5722        }
5723        if (argc == 0) {
5724            chanPtr->inEofChar = 0;
5725            chanPtr->outEofChar = 0;
5726        } else if (argc == 1) {
5727            if (chanPtr->flags & TCL_WRITABLE) {
5728                chanPtr->outEofChar = (int) argv[0][0];
5729            }
5730            if (chanPtr->flags & TCL_READABLE) {
5731                chanPtr->inEofChar = (int) argv[0][0];
5732            }
5733        } else if (argc != 2) {
5734            if (interp) {
5735                Tcl_AppendResult(interp,
5736                        "bad value for -eofchar: should be a list of one or",
5737                        " two elements", (char *) NULL);
5738            }
5739            ckfree((char *) argv);
5740            return TCL_ERROR;
5741        } else {
5742            if (chanPtr->flags & TCL_READABLE) {
5743                chanPtr->inEofChar = (int) argv[0][0];
5744            }
5745            if (chanPtr->flags & TCL_WRITABLE) {
5746                chanPtr->outEofChar = (int) argv[1][0];
5747            }
5748        }
5749        if (argv != (char **) NULL) {
5750            ckfree((char *) argv);
5751        }
5752	return TCL_OK;
5753    } else if ((len > 1) && (optionName[1] == 't') &&
5754            (strncmp(optionName, "-translation", len) == 0)) {
5755	char *readMode, *writeMode;
5756
5757        if (Tcl_SplitList(interp, newValue, &argc, &argv) == TCL_ERROR) {
5758            return TCL_ERROR;
5759        }
5760
5761        if (argc == 1) {
5762	    readMode = (chanPtr->flags & TCL_READABLE) ? argv[0] : NULL;
5763	    writeMode = (chanPtr->flags & TCL_WRITABLE) ? argv[0] : NULL;
5764	} else if (argc == 2) {
5765	    readMode = (chanPtr->flags & TCL_READABLE) ? argv[0] : NULL;
5766	    writeMode = (chanPtr->flags & TCL_WRITABLE) ? argv[1] : NULL;
5767	} else {
5768            if (interp) {
5769                Tcl_AppendResult(interp,
5770                        "bad value for -translation: must be a one or two",
5771                        " element list", (char *) NULL);
5772            }
5773            ckfree((char *) argv);
5774            return TCL_ERROR;
5775	}
5776
5777	if (readMode) {
5778	    if (*readMode == '\0') {
5779		newMode = chanPtr->inputTranslation;
5780	    } else if (strcmp(readMode, "auto") == 0) {
5781		newMode = TCL_TRANSLATE_AUTO;
5782	    } else if (strcmp(readMode, "binary") == 0) {
5783		newMode = TCL_TRANSLATE_LF;
5784		chanPtr->inEofChar = 0;
5785		Tcl_FreeEncoding(chanPtr->encoding);
5786		chanPtr->encoding = NULL;
5787	    } else if (strcmp(readMode, "lf") == 0) {
5788		newMode = TCL_TRANSLATE_LF;
5789	    } else if (strcmp(readMode, "cr") == 0) {
5790		newMode = TCL_TRANSLATE_CR;
5791	    } else if (strcmp(readMode, "crlf") == 0) {
5792		newMode = TCL_TRANSLATE_CRLF;
5793	    } else if (strcmp(readMode, "platform") == 0) {
5794		newMode = TCL_PLATFORM_TRANSLATION;
5795	    } else {
5796		if (interp) {
5797		    Tcl_AppendResult(interp,
5798			    "bad value for -translation: ",
5799			    "must be one of auto, binary, cr, lf, crlf,",
5800			    " or platform", (char *) NULL);
5801		}
5802		ckfree((char *) argv);
5803		return TCL_ERROR;
5804	    }
5805
5806	    /*
5807	     * Reset the EOL flags since we need to look at any buffered
5808	     * data to see if the new translation mode allows us to
5809	     * complete the line.
5810	     */
5811
5812	    if (newMode != chanPtr->inputTranslation) {
5813		chanPtr->inputTranslation = (Tcl_EolTranslation) newMode;
5814		chanPtr->flags &= ~(INPUT_SAW_CR);
5815		chanPtr->flags &= ~(CHANNEL_NEED_MORE_DATA);
5816		UpdateInterest(chanPtr);
5817	    }
5818	}
5819	if (writeMode) {
5820	    if (*writeMode == '\0') {
5821		/* Do nothing. */
5822	    } else if (strcmp(writeMode, "auto") == 0) {
5823		/*
5824		 * This is a hack to get TCP sockets to produce output
5825		 * in CRLF mode if they are being set into AUTO mode.
5826		 * A better solution for achieving this effect will be
5827		 * coded later.
5828		 */
5829
5830		if (strcmp(chanPtr->typePtr->typeName, "tcp") == 0) {
5831		    chanPtr->outputTranslation = TCL_TRANSLATE_CRLF;
5832		} else {
5833		    chanPtr->outputTranslation = TCL_PLATFORM_TRANSLATION;
5834		}
5835	    } else if (strcmp(writeMode, "binary") == 0) {
5836		chanPtr->outEofChar = 0;
5837		chanPtr->outputTranslation = TCL_TRANSLATE_LF;
5838		Tcl_FreeEncoding(chanPtr->encoding);
5839		chanPtr->encoding = NULL;
5840	    } else if (strcmp(writeMode, "lf") == 0) {
5841		chanPtr->outputTranslation = TCL_TRANSLATE_LF;
5842	    } else if (strcmp(writeMode, "cr") == 0) {
5843		chanPtr->outputTranslation = TCL_TRANSLATE_CR;
5844	    } else if (strcmp(writeMode, "crlf") == 0) {
5845		chanPtr->outputTranslation = TCL_TRANSLATE_CRLF;
5846	    } else if (strcmp(writeMode, "platform") == 0) {
5847		chanPtr->outputTranslation = TCL_PLATFORM_TRANSLATION;
5848	    } else {
5849		if (interp) {
5850		    Tcl_AppendResult(interp,
5851			    "bad value for -translation: ",
5852			    "must be one of auto, binary, cr, lf, crlf,",
5853			    " or platform", (char *) NULL);
5854		}
5855		ckfree((char *) argv);
5856		return TCL_ERROR;
5857	    }
5858	}
5859        ckfree((char *) argv);
5860        return TCL_OK;
5861    } else if (chanPtr->typePtr->setOptionProc != NULL) {
5862        return (*chanPtr->typePtr->setOptionProc)(chanPtr->instanceData,
5863                interp, optionName, newValue);
5864    } else {
5865	return Tcl_BadChannelOption(interp, optionName, (char *) NULL);
5866    }
5867
5868    /*
5869     * If bufsize changes, need to get rid of old utility buffer.
5870     */
5871
5872    if (chanPtr->saveInBufPtr != NULL) {
5873	RecycleBuffer(chanPtr, chanPtr->saveInBufPtr, 1);
5874	chanPtr->saveInBufPtr = NULL;
5875    }
5876    if (chanPtr->inQueueHead != NULL) {
5877	if ((chanPtr->inQueueHead->nextPtr == NULL)
5878		&& (chanPtr->inQueueHead->nextAdded ==
5879			chanPtr->inQueueHead->nextRemoved)) {
5880	    RecycleBuffer(chanPtr, chanPtr->inQueueHead, 1);
5881	    chanPtr->inQueueHead = NULL;
5882	    chanPtr->inQueueTail = NULL;
5883	}
5884    }
5885
5886    /*
5887     * If encoding or bufsize changes, need to update output staging buffer.
5888     */
5889
5890    if (chanPtr->outputStage != NULL) {
5891	ckfree((char *) chanPtr->outputStage);
5892	chanPtr->outputStage = NULL;
5893    }
5894    if ((chanPtr->encoding != NULL) && (chanPtr->flags & TCL_WRITABLE)) {
5895	chanPtr->outputStage = (char *)
5896		ckalloc((unsigned) (chanPtr->bufSize + 2));
5897    }
5898    return TCL_OK;
5899}
5900
5901/*
5902 *----------------------------------------------------------------------
5903 *
5904 * CleanupChannelHandlers --
5905 *
5906 *	Removes channel handlers that refer to the supplied interpreter,
5907 *	so that if the actual channel is not closed now, these handlers
5908 *	will not run on subsequent events on the channel. This would be
5909 *	erroneous, because the interpreter no longer has a reference to
5910 *	this channel.
5911 *
5912 * Results:
5913 *	None.
5914 *
5915 * Side effects:
5916 *	Removes channel handlers.
5917 *
5918 *----------------------------------------------------------------------
5919 */
5920
5921static void
5922CleanupChannelHandlers(interp, chanPtr)
5923    Tcl_Interp *interp;
5924    Channel *chanPtr;
5925{
5926    EventScriptRecord *sPtr, *prevPtr, *nextPtr;
5927
5928    /*
5929     * Remove fileevent records on this channel that refer to the
5930     * given interpreter.
5931     */
5932
5933    for (sPtr = chanPtr->scriptRecordPtr,
5934             prevPtr = (EventScriptRecord *) NULL;
5935             sPtr != (EventScriptRecord *) NULL;
5936             sPtr = nextPtr) {
5937        nextPtr = sPtr->nextPtr;
5938        if (sPtr->interp == interp) {
5939            if (prevPtr == (EventScriptRecord *) NULL) {
5940                chanPtr->scriptRecordPtr = nextPtr;
5941            } else {
5942                prevPtr->nextPtr = nextPtr;
5943            }
5944
5945            Tcl_DeleteChannelHandler((Tcl_Channel) chanPtr,
5946                    ChannelEventScriptInvoker, (ClientData) sPtr);
5947
5948	    Tcl_DecrRefCount(sPtr->scriptPtr);
5949            ckfree((char *) sPtr);
5950        } else {
5951            prevPtr = sPtr;
5952        }
5953    }
5954}
5955
5956/*
5957 *----------------------------------------------------------------------
5958 *
5959 * Tcl_NotifyChannel --
5960 *
5961 *	This procedure is called by a channel driver when a driver
5962 *	detects an event on a channel.  This procedure is responsible
5963 *	for actually handling the event by invoking any channel
5964 *	handler callbacks.
5965 *
5966 * Results:
5967 *	None.
5968 *
5969 * Side effects:
5970 *	Whatever the channel handler callback procedure does.
5971 *
5972 *----------------------------------------------------------------------
5973 */
5974
5975void
5976Tcl_NotifyChannel(channel, mask)
5977    Tcl_Channel channel;	/* Channel that detected an event. */
5978    int mask;			/* OR'ed combination of TCL_READABLE,
5979				 * TCL_WRITABLE, or TCL_EXCEPTION: indicates
5980				 * which events were detected. */
5981{
5982    Channel *chanPtr = (Channel *) channel;
5983    ChannelHandler *chPtr;
5984    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
5985    NextChannelHandler nh;
5986
5987    /*
5988     * Preserve the channel struct in case the script closes it.
5989     */
5990
5991    Tcl_Preserve((ClientData) channel);
5992
5993    /*
5994     * If we are flushing in the background, be sure to call FlushChannel
5995     * for writable events.  Note that we have to discard the writable
5996     * event so we don't call any write handlers before the flush is
5997     * complete.
5998     */
5999
6000    if ((chanPtr->flags & BG_FLUSH_SCHEDULED) && (mask & TCL_WRITABLE)) {
6001	FlushChannel(NULL, chanPtr, 1);
6002	mask &= ~TCL_WRITABLE;
6003    }
6004
6005    /*
6006     * Add this invocation to the list of recursive invocations of
6007     * ChannelHandlerEventProc.
6008     */
6009
6010    nh.nextHandlerPtr = (ChannelHandler *) NULL;
6011    nh.nestedHandlerPtr = tsdPtr->nestedHandlerPtr;
6012    tsdPtr->nestedHandlerPtr = &nh;
6013
6014    for (chPtr = chanPtr->chPtr; chPtr != (ChannelHandler *) NULL; ) {
6015
6016        /*
6017         * If this channel handler is interested in any of the events that
6018         * have occurred on the channel, invoke its procedure.
6019         */
6020
6021        if ((chPtr->mask & mask) != 0) {
6022            nh.nextHandlerPtr = chPtr->nextPtr;
6023	    (*(chPtr->proc))(chPtr->clientData, mask);
6024            chPtr = nh.nextHandlerPtr;
6025        } else {
6026            chPtr = chPtr->nextPtr;
6027	}
6028    }
6029
6030    /*
6031     * Update the notifier interest, since it may have changed after
6032     * invoking event handlers.
6033     */
6034
6035    if (chanPtr->typePtr != NULL) {
6036	UpdateInterest(chanPtr);
6037    }
6038
6039    Tcl_Release((ClientData) channel);
6040
6041    tsdPtr->nestedHandlerPtr = nh.nestedHandlerPtr;
6042}
6043
6044/*
6045 *----------------------------------------------------------------------
6046 *
6047 * UpdateInterest --
6048 *
6049 *	Arrange for the notifier to call us back at appropriate times
6050 *	based on the current state of the channel.
6051 *
6052 * Results:
6053 *	None.
6054 *
6055 * Side effects:
6056 *	May schedule a timer or driver handler.
6057 *
6058 *----------------------------------------------------------------------
6059 */
6060
6061static void
6062UpdateInterest(chanPtr)
6063    Channel *chanPtr;		/* Channel to update. */
6064{
6065    int mask = chanPtr->interestMask;
6066
6067    /*
6068     * If there are flushed buffers waiting to be written, then
6069     * we need to watch for the channel to become writable.
6070     */
6071
6072    if (chanPtr->flags & BG_FLUSH_SCHEDULED) {
6073	mask |= TCL_WRITABLE;
6074    }
6075
6076    /*
6077     * If there is data in the input queue, and we aren't waiting for more
6078     * data, then we need to schedule a timer so we don't block in the
6079     * notifier.  Also, cancel the read interest so we don't get duplicate
6080     * events.
6081     */
6082
6083    if (mask & TCL_READABLE) {
6084	if (!(chanPtr->flags & CHANNEL_NEED_MORE_DATA)
6085		&& (chanPtr->inQueueHead != (ChannelBuffer *) NULL)
6086		&& (chanPtr->inQueueHead->nextRemoved <
6087			chanPtr->inQueueHead->nextAdded)) {
6088	    mask &= ~TCL_READABLE;
6089	    if (!chanPtr->timer) {
6090		chanPtr->timer = Tcl_CreateTimerHandler(0, ChannelTimerProc,
6091			(ClientData) chanPtr);
6092	    }
6093	}
6094    }
6095    (chanPtr->typePtr->watchProc)(chanPtr->instanceData, mask);
6096}
6097
6098/*
6099 *----------------------------------------------------------------------
6100 *
6101 * ChannelTimerProc --
6102 *
6103 *	Timer handler scheduled by UpdateInterest to monitor the
6104 *	channel buffers until they are empty.
6105 *
6106 * Results:
6107 *	None.
6108 *
6109 * Side effects:
6110 *	May invoke channel handlers.
6111 *
6112 *----------------------------------------------------------------------
6113 */
6114
6115static void
6116ChannelTimerProc(clientData)
6117    ClientData clientData;
6118{
6119    Channel *chanPtr = (Channel *) clientData;
6120
6121    if (!(chanPtr->flags & CHANNEL_NEED_MORE_DATA)
6122	    && (chanPtr->interestMask & TCL_READABLE)
6123	    && (chanPtr->inQueueHead != (ChannelBuffer *) NULL)
6124	    && (chanPtr->inQueueHead->nextRemoved <
6125		    chanPtr->inQueueHead->nextAdded)) {
6126	/*
6127	 * Restart the timer in case a channel handler reenters the
6128	 * event loop before UpdateInterest gets called by Tcl_NotifyChannel.
6129	 */
6130
6131	chanPtr->timer = Tcl_CreateTimerHandler(0, ChannelTimerProc,
6132			(ClientData) chanPtr);
6133	Tcl_NotifyChannel((Tcl_Channel)chanPtr, TCL_READABLE);
6134
6135   } else {
6136	chanPtr->timer = NULL;
6137	UpdateInterest(chanPtr);
6138    }
6139}
6140
6141/*
6142 *----------------------------------------------------------------------
6143 *
6144 * Tcl_CreateChannelHandler --
6145 *
6146 *	Arrange for a given procedure to be invoked whenever the
6147 *	channel indicated by the chanPtr arg becomes readable or
6148 *	writable.
6149 *
6150 * Results:
6151 *	None.
6152 *
6153 * Side effects:
6154 *	From now on, whenever the I/O channel given by chanPtr becomes
6155 *	ready in the way indicated by mask, proc will be invoked.
6156 *	See the manual entry for details on the calling sequence
6157 *	to proc.  If there is already an event handler for chan, proc
6158 *	and clientData, then the mask will be updated.
6159 *
6160 *----------------------------------------------------------------------
6161 */
6162
6163void
6164Tcl_CreateChannelHandler(chan, mask, proc, clientData)
6165    Tcl_Channel chan;		/* The channel to create the handler for. */
6166    int mask;			/* OR'ed combination of TCL_READABLE,
6167				 * TCL_WRITABLE, and TCL_EXCEPTION:
6168				 * indicates conditions under which
6169				 * proc should be called. Use 0 to
6170                                 * disable a registered handler. */
6171    Tcl_ChannelProc *proc;	/* Procedure to call for each
6172				 * selected event. */
6173    ClientData clientData;	/* Arbitrary data to pass to proc. */
6174{
6175    ChannelHandler *chPtr;
6176    Channel *chanPtr;
6177
6178    chanPtr = (Channel *) chan;
6179
6180    /*
6181     * Check whether this channel handler is not already registered. If
6182     * it is not, create a new record, else reuse existing record (smash
6183     * current values).
6184     */
6185
6186    for (chPtr = chanPtr->chPtr;
6187             chPtr != (ChannelHandler *) NULL;
6188             chPtr = chPtr->nextPtr) {
6189        if ((chPtr->chanPtr == chanPtr) && (chPtr->proc == proc) &&
6190                (chPtr->clientData == clientData)) {
6191            break;
6192        }
6193    }
6194    if (chPtr == (ChannelHandler *) NULL) {
6195        chPtr = (ChannelHandler *) ckalloc((unsigned) sizeof(ChannelHandler));
6196        chPtr->mask = 0;
6197        chPtr->proc = proc;
6198        chPtr->clientData = clientData;
6199        chPtr->chanPtr = chanPtr;
6200        chPtr->nextPtr = chanPtr->chPtr;
6201        chanPtr->chPtr = chPtr;
6202    }
6203
6204    /*
6205     * The remainder of the initialization below is done regardless of
6206     * whether or not this is a new record or a modification of an old
6207     * one.
6208     */
6209
6210    chPtr->mask = mask;
6211
6212    /*
6213     * Recompute the interest mask for the channel - this call may actually
6214     * be disabling an existing handler.
6215     */
6216
6217    chanPtr->interestMask = 0;
6218    for (chPtr = chanPtr->chPtr;
6219	 chPtr != (ChannelHandler *) NULL;
6220	 chPtr = chPtr->nextPtr) {
6221	chanPtr->interestMask |= chPtr->mask;
6222    }
6223
6224    UpdateInterest(chanPtr);
6225}
6226
6227/*
6228 *----------------------------------------------------------------------
6229 *
6230 * Tcl_DeleteChannelHandler --
6231 *
6232 *	Cancel a previously arranged callback arrangement for an IO
6233 *	channel.
6234 *
6235 * Results:
6236 *	None.
6237 *
6238 * Side effects:
6239 *	If a callback was previously registered for this chan, proc and
6240 *	 clientData , it is removed and the callback will no longer be called
6241 *	when the channel becomes ready for IO.
6242 *
6243 *----------------------------------------------------------------------
6244 */
6245
6246void
6247Tcl_DeleteChannelHandler(chan, proc, clientData)
6248    Tcl_Channel chan;		/* The channel for which to remove the
6249                                 * callback. */
6250    Tcl_ChannelProc *proc;	/* The procedure in the callback to delete. */
6251    ClientData clientData;	/* The client data in the callback
6252                                 * to delete. */
6253
6254{
6255    ChannelHandler *chPtr, *prevChPtr;
6256    Channel *chanPtr;
6257    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
6258    NextChannelHandler *nhPtr;
6259
6260    chanPtr = (Channel *) chan;
6261
6262    /*
6263     * Find the entry and the previous one in the list.
6264     */
6265
6266    for (prevChPtr = (ChannelHandler *) NULL, chPtr = chanPtr->chPtr;
6267             chPtr != (ChannelHandler *) NULL;
6268             chPtr = chPtr->nextPtr) {
6269        if ((chPtr->chanPtr == chanPtr) && (chPtr->clientData == clientData)
6270                && (chPtr->proc == proc)) {
6271            break;
6272        }
6273        prevChPtr = chPtr;
6274    }
6275
6276    /*
6277     * If not found, return without doing anything.
6278     */
6279
6280    if (chPtr == (ChannelHandler *) NULL) {
6281        return;
6282    }
6283
6284    /*
6285     * If ChannelHandlerEventProc is about to process this handler, tell it to
6286     * process the next one instead - we are going to delete *this* one.
6287     */
6288
6289    for (nhPtr = tsdPtr->nestedHandlerPtr;
6290             nhPtr != (NextChannelHandler *) NULL;
6291             nhPtr = nhPtr->nestedHandlerPtr) {
6292        if (nhPtr->nextHandlerPtr == chPtr) {
6293            nhPtr->nextHandlerPtr = chPtr->nextPtr;
6294        }
6295    }
6296
6297    /*
6298     * Splice it out of the list of channel handlers.
6299     */
6300
6301    if (prevChPtr == (ChannelHandler *) NULL) {
6302        chanPtr->chPtr = chPtr->nextPtr;
6303    } else {
6304        prevChPtr->nextPtr = chPtr->nextPtr;
6305    }
6306    ckfree((char *) chPtr);
6307
6308    /*
6309     * Recompute the interest list for the channel, so that infinite loops
6310     * will not result if Tcl_DeleteChanelHandler is called inside an event.
6311     */
6312
6313    chanPtr->interestMask = 0;
6314    for (chPtr = chanPtr->chPtr;
6315             chPtr != (ChannelHandler *) NULL;
6316             chPtr = chPtr->nextPtr) {
6317        chanPtr->interestMask |= chPtr->mask;
6318    }
6319
6320    UpdateInterest(chanPtr);
6321}
6322
6323/*
6324 *----------------------------------------------------------------------
6325 *
6326 * DeleteScriptRecord --
6327 *
6328 *	Delete a script record for this combination of channel, interp
6329 *	and mask.
6330 *
6331 * Results:
6332 *	None.
6333 *
6334 * Side effects:
6335 *	Deletes a script record and cancels a channel event handler.
6336 *
6337 *----------------------------------------------------------------------
6338 */
6339
6340static void
6341DeleteScriptRecord(interp, chanPtr, mask)
6342    Tcl_Interp *interp;		/* Interpreter in which script was to be
6343                                 * executed. */
6344    Channel *chanPtr;		/* The channel for which to delete the
6345                                 * script record (if any). */
6346    int mask;			/* Events in mask must exactly match mask
6347                                 * of script to delete. */
6348{
6349    EventScriptRecord *esPtr, *prevEsPtr;
6350
6351    for (esPtr = chanPtr->scriptRecordPtr,
6352             prevEsPtr = (EventScriptRecord *) NULL;
6353             esPtr != (EventScriptRecord *) NULL;
6354             prevEsPtr = esPtr, esPtr = esPtr->nextPtr) {
6355        if ((esPtr->interp == interp) && (esPtr->mask == mask)) {
6356            if (esPtr == chanPtr->scriptRecordPtr) {
6357                chanPtr->scriptRecordPtr = esPtr->nextPtr;
6358            } else {
6359                prevEsPtr->nextPtr = esPtr->nextPtr;
6360            }
6361
6362            Tcl_DeleteChannelHandler((Tcl_Channel) chanPtr,
6363                    ChannelEventScriptInvoker, (ClientData) esPtr);
6364
6365	    Tcl_DecrRefCount(esPtr->scriptPtr);
6366            ckfree((char *) esPtr);
6367
6368            break;
6369        }
6370    }
6371}
6372
6373/*
6374 *----------------------------------------------------------------------
6375 *
6376 * CreateScriptRecord --
6377 *
6378 *	Creates a record to store a script to be executed when a specific
6379 *	event fires on a specific channel.
6380 *
6381 * Results:
6382 *	None.
6383 *
6384 * Side effects:
6385 *	Causes the script to be stored for later execution.
6386 *
6387 *----------------------------------------------------------------------
6388 */
6389
6390static void
6391CreateScriptRecord(interp, chanPtr, mask, scriptPtr)
6392    Tcl_Interp *interp;			/* Interpreter in which to execute
6393                                         * the stored script. */
6394    Channel *chanPtr;			/* Channel for which script is to
6395                                         * be stored. */
6396    int mask;				/* Set of events for which script
6397                                         * will be invoked. */
6398    Tcl_Obj *scriptPtr;			/* Pointer to script object. */
6399{
6400    EventScriptRecord *esPtr;
6401
6402    for (esPtr = chanPtr->scriptRecordPtr;
6403             esPtr != (EventScriptRecord *) NULL;
6404             esPtr = esPtr->nextPtr) {
6405        if ((esPtr->interp == interp) && (esPtr->mask == mask)) {
6406	    Tcl_DecrRefCount(esPtr->scriptPtr);
6407	    esPtr->scriptPtr = (Tcl_Obj *) NULL;
6408            break;
6409        }
6410    }
6411    if (esPtr == (EventScriptRecord *) NULL) {
6412        esPtr = (EventScriptRecord *) ckalloc((unsigned)
6413                sizeof(EventScriptRecord));
6414        Tcl_CreateChannelHandler((Tcl_Channel) chanPtr, mask,
6415                ChannelEventScriptInvoker, (ClientData) esPtr);
6416        esPtr->nextPtr = chanPtr->scriptRecordPtr;
6417        chanPtr->scriptRecordPtr = esPtr;
6418    }
6419    esPtr->chanPtr = chanPtr;
6420    esPtr->interp = interp;
6421    esPtr->mask = mask;
6422    Tcl_IncrRefCount(scriptPtr);
6423    esPtr->scriptPtr = scriptPtr;
6424}
6425
6426/*
6427 *----------------------------------------------------------------------
6428 *
6429 * ChannelEventScriptInvoker --
6430 *
6431 *	Invokes a script scheduled by "fileevent" for when the channel
6432 *	becomes ready for IO. This function is invoked by the channel
6433 *	handler which was created by the Tcl "fileevent" command.
6434 *
6435 * Results:
6436 *	None.
6437 *
6438 * Side effects:
6439 *	Whatever the script does.
6440 *
6441 *----------------------------------------------------------------------
6442 */
6443
6444static void
6445ChannelEventScriptInvoker(clientData, mask)
6446    ClientData clientData;	/* The script+interp record. */
6447    int mask;			/* Not used. */
6448{
6449    Tcl_Interp *interp;		/* Interpreter in which to eval the script. */
6450    Channel *chanPtr;		/* The channel for which this handler is
6451                                 * registered. */
6452    EventScriptRecord *esPtr;	/* The event script + interpreter to eval it
6453                                 * in. */
6454    int result;			/* Result of call to eval script. */
6455
6456    esPtr = (EventScriptRecord *) clientData;
6457
6458    chanPtr = esPtr->chanPtr;
6459    mask = esPtr->mask;
6460    interp = esPtr->interp;
6461
6462    /*
6463     * We must preserve the interpreter so we can report errors on it
6464     * later.  Note that we do not need to preserve the channel because
6465     * that is done by Tcl_NotifyChannel before calling channel handlers.
6466     */
6467
6468    Tcl_Preserve((ClientData) interp);
6469    result = Tcl_EvalObj(interp, esPtr->scriptPtr, TCL_EVAL_GLOBAL);
6470
6471    /*
6472     * On error, cause a background error and remove the channel handler
6473     * and the script record.
6474     *
6475     * NOTE: Must delete channel handler before causing the background error
6476     * because the background error may want to reinstall the handler.
6477     */
6478
6479    if (result != TCL_OK) {
6480	if (chanPtr->typePtr != NULL) {
6481	    DeleteScriptRecord(interp, chanPtr, mask);
6482	}
6483        Tcl_BackgroundError(interp);
6484    }
6485    Tcl_Release((ClientData) interp);
6486}
6487
6488/*
6489 *----------------------------------------------------------------------
6490 *
6491 * Tcl_FileEventObjCmd --
6492 *
6493 *	This procedure implements the "fileevent" Tcl command. See the
6494 *	user documentation for details on what it does. This command is
6495 *	based on the Tk command "fileevent" which in turn is based on work
6496 *	contributed by Mark Diekhans.
6497 *
6498 * Results:
6499 *	A standard Tcl result.
6500 *
6501 * Side effects:
6502 *	May create a channel handler for the specified channel.
6503 *
6504 *----------------------------------------------------------------------
6505 */
6506
6507	/* ARGSUSED */
6508int
6509Tcl_FileEventObjCmd(clientData, interp, objc, objv)
6510    ClientData clientData;		/* Not used. */
6511    Tcl_Interp *interp;			/* Interpreter in which the channel
6512                                         * for which to create the handler
6513                                         * is found. */
6514    int objc;				/* Number of arguments. */
6515    Tcl_Obj *CONST objv[];		/* Argument objects. */
6516{
6517    Channel *chanPtr;			/* The channel to create
6518                                         * the handler for. */
6519    Tcl_Channel chan;			/* The opaque type for the channel. */
6520    char *chanName;
6521    int modeIndex;			/* Index of mode argument. */
6522    int mask;
6523    static char *modeOptions[] = {"readable", "writable", NULL};
6524    static int maskArray[] = {TCL_READABLE, TCL_WRITABLE};
6525
6526    if ((objc != 3) && (objc != 4)) {
6527	Tcl_WrongNumArgs(interp, 1, objv, "channelId event ?script?");
6528	return TCL_ERROR;
6529    }
6530    if (Tcl_GetIndexFromObj(interp, objv[2], modeOptions, "event name", 0,
6531	    &modeIndex) != TCL_OK) {
6532	return TCL_ERROR;
6533    }
6534    mask = maskArray[modeIndex];
6535
6536    chanName = Tcl_GetString(objv[1]);
6537    chan = Tcl_GetChannel(interp, chanName, NULL);
6538    if (chan == (Tcl_Channel) NULL) {
6539	return TCL_ERROR;
6540    }
6541    chanPtr = (Channel *) chan;
6542    if ((chanPtr->flags & mask) == 0) {
6543        Tcl_AppendResult(interp, "channel is not ",
6544                (mask == TCL_READABLE) ? "readable" : "writable",
6545                (char *) NULL);
6546        return TCL_ERROR;
6547    }
6548
6549    /*
6550     * If we are supposed to return the script, do so.
6551     */
6552
6553    if (objc == 3) {
6554	EventScriptRecord *esPtr;
6555	for (esPtr = chanPtr->scriptRecordPtr;
6556             esPtr != (EventScriptRecord *) NULL;
6557             esPtr = esPtr->nextPtr) {
6558	    if ((esPtr->interp == interp) && (esPtr->mask == mask)) {
6559		Tcl_SetObjResult(interp, esPtr->scriptPtr);
6560		break;
6561	    }
6562	}
6563        return TCL_OK;
6564    }
6565
6566    /*
6567     * If we are supposed to delete a stored script, do so.
6568     */
6569
6570    if (*(Tcl_GetString(objv[3])) == '\0') {
6571        DeleteScriptRecord(interp, chanPtr, mask);
6572        return TCL_OK;
6573    }
6574
6575    /*
6576     * Make the script record that will link between the event and the
6577     * script to invoke. This also creates a channel event handler which
6578     * will evaluate the script in the supplied interpreter.
6579     */
6580
6581    CreateScriptRecord(interp, chanPtr, mask, objv[3]);
6582
6583    return TCL_OK;
6584}
6585
6586/*
6587 *----------------------------------------------------------------------
6588 *
6589 * TclTestChannelCmd --
6590 *
6591 *	Implements the Tcl "testchannel" debugging command and its
6592 *	subcommands. This is part of the testing environment but must be
6593 *	in this file instead of tclTest.c because it needs access to the
6594 *	fields of struct Channel.
6595 *
6596 * Results:
6597 *	A standard Tcl result.
6598 *
6599 * Side effects:
6600 *	None.
6601 *
6602 *----------------------------------------------------------------------
6603 */
6604
6605	/* ARGSUSED */
6606int
6607TclTestChannelCmd(clientData, interp, argc, argv)
6608    ClientData clientData;	/* Not used. */
6609    Tcl_Interp *interp;		/* Interpreter for result. */
6610    int argc;			/* Count of additional args. */
6611    char **argv;		/* Additional arg strings. */
6612{
6613    char *cmdName;		/* Sub command. */
6614    Tcl_HashTable *hTblPtr;	/* Hash table of channels. */
6615    Tcl_HashSearch hSearch;	/* Search variable. */
6616    Tcl_HashEntry *hPtr;	/* Search variable. */
6617    Channel *chanPtr;		/* The actual channel. */
6618    Tcl_Channel chan;		/* The opaque type. */
6619    size_t len;			/* Length of subcommand string. */
6620    int IOQueued;		/* How much IO is queued inside channel? */
6621    ChannelBuffer *bufPtr;	/* For iterating over queued IO. */
6622    char buf[TCL_INTEGER_SPACE];/* For sprintf. */
6623
6624    if (argc < 2) {
6625        Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
6626                " subcommand ?additional args..?\"", (char *) NULL);
6627        return TCL_ERROR;
6628    }
6629    cmdName = argv[1];
6630    len = strlen(cmdName);
6631
6632    chanPtr = (Channel *) NULL;
6633
6634    if (argc > 2) {
6635        chan = Tcl_GetChannel(interp, argv[2], NULL);
6636        if (chan == (Tcl_Channel) NULL) {
6637            return TCL_ERROR;
6638        }
6639        chanPtr = (Channel *) chan;
6640    }
6641
6642
6643    if ((cmdName[0] == 'i') && (strncmp(cmdName, "info", len) == 0)) {
6644        if (argc != 3) {
6645            Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
6646                    " info channelName\"", (char *) NULL);
6647            return TCL_ERROR;
6648        }
6649        Tcl_AppendElement(interp, argv[2]);
6650        Tcl_AppendElement(interp, chanPtr->typePtr->typeName);
6651        if (chanPtr->flags & TCL_READABLE) {
6652            Tcl_AppendElement(interp, "read");
6653        } else {
6654            Tcl_AppendElement(interp, "");
6655        }
6656        if (chanPtr->flags & TCL_WRITABLE) {
6657            Tcl_AppendElement(interp, "write");
6658        } else {
6659            Tcl_AppendElement(interp, "");
6660        }
6661        if (chanPtr->flags & CHANNEL_NONBLOCKING) {
6662            Tcl_AppendElement(interp, "nonblocking");
6663        } else {
6664            Tcl_AppendElement(interp, "blocking");
6665        }
6666        if (chanPtr->flags & CHANNEL_LINEBUFFERED) {
6667            Tcl_AppendElement(interp, "line");
6668        } else if (chanPtr->flags & CHANNEL_UNBUFFERED) {
6669            Tcl_AppendElement(interp, "none");
6670        } else {
6671            Tcl_AppendElement(interp, "full");
6672        }
6673        if (chanPtr->flags & BG_FLUSH_SCHEDULED) {
6674            Tcl_AppendElement(interp, "async_flush");
6675        } else {
6676            Tcl_AppendElement(interp, "");
6677        }
6678        if (chanPtr->flags & CHANNEL_EOF) {
6679            Tcl_AppendElement(interp, "eof");
6680        } else {
6681            Tcl_AppendElement(interp, "");
6682        }
6683        if (chanPtr->flags & CHANNEL_BLOCKED) {
6684            Tcl_AppendElement(interp, "blocked");
6685        } else {
6686            Tcl_AppendElement(interp, "unblocked");
6687        }
6688        if (chanPtr->inputTranslation == TCL_TRANSLATE_AUTO) {
6689            Tcl_AppendElement(interp, "auto");
6690            if (chanPtr->flags & INPUT_SAW_CR) {
6691                Tcl_AppendElement(interp, "saw_cr");
6692            } else {
6693                Tcl_AppendElement(interp, "");
6694            }
6695        } else if (chanPtr->inputTranslation == TCL_TRANSLATE_LF) {
6696            Tcl_AppendElement(interp, "lf");
6697            Tcl_AppendElement(interp, "");
6698        } else if (chanPtr->inputTranslation == TCL_TRANSLATE_CR) {
6699            Tcl_AppendElement(interp, "cr");
6700            Tcl_AppendElement(interp, "");
6701        } else if (chanPtr->inputTranslation == TCL_TRANSLATE_CRLF) {
6702            Tcl_AppendElement(interp, "crlf");
6703            if (chanPtr->flags & INPUT_SAW_CR) {
6704                Tcl_AppendElement(interp, "queued_cr");
6705            } else {
6706                Tcl_AppendElement(interp, "");
6707            }
6708        }
6709        if (chanPtr->outputTranslation == TCL_TRANSLATE_AUTO) {
6710            Tcl_AppendElement(interp, "auto");
6711        } else if (chanPtr->outputTranslation == TCL_TRANSLATE_LF) {
6712            Tcl_AppendElement(interp, "lf");
6713        } else if (chanPtr->outputTranslation == TCL_TRANSLATE_CR) {
6714            Tcl_AppendElement(interp, "cr");
6715        } else if (chanPtr->outputTranslation == TCL_TRANSLATE_CRLF) {
6716            Tcl_AppendElement(interp, "crlf");
6717        }
6718        for (IOQueued = 0, bufPtr = chanPtr->inQueueHead;
6719                 bufPtr != (ChannelBuffer *) NULL;
6720                 bufPtr = bufPtr->nextPtr) {
6721            IOQueued += bufPtr->nextAdded - bufPtr->nextRemoved;
6722        }
6723        TclFormatInt(buf, IOQueued);
6724        Tcl_AppendElement(interp, buf);
6725
6726        IOQueued = 0;
6727        if (chanPtr->curOutPtr != (ChannelBuffer *) NULL) {
6728            IOQueued = chanPtr->curOutPtr->nextAdded -
6729                chanPtr->curOutPtr->nextRemoved;
6730        }
6731        for (bufPtr = chanPtr->outQueueHead;
6732                 bufPtr != (ChannelBuffer *) NULL;
6733                 bufPtr = bufPtr->nextPtr) {
6734            IOQueued += (bufPtr->nextAdded - bufPtr->nextRemoved);
6735        }
6736        TclFormatInt(buf, IOQueued);
6737        Tcl_AppendElement(interp, buf);
6738
6739        TclFormatInt(buf, Tcl_Tell((Tcl_Channel) chanPtr));
6740        Tcl_AppendElement(interp, buf);
6741
6742        TclFormatInt(buf, chanPtr->refCount);
6743        Tcl_AppendElement(interp, buf);
6744
6745        return TCL_OK;
6746    }
6747
6748    if ((cmdName[0] == 'i') &&
6749            (strncmp(cmdName, "inputbuffered", len) == 0)) {
6750        if (argc != 3) {
6751            Tcl_AppendResult(interp, "channel name required",
6752                    (char *) NULL);
6753            return TCL_ERROR;
6754        }
6755
6756        for (IOQueued = 0, bufPtr = chanPtr->inQueueHead;
6757                 bufPtr != (ChannelBuffer *) NULL;
6758                 bufPtr = bufPtr->nextPtr) {
6759            IOQueued += bufPtr->nextAdded - bufPtr->nextRemoved;
6760        }
6761        TclFormatInt(buf, IOQueued);
6762        Tcl_AppendResult(interp, buf, (char *) NULL);
6763        return TCL_OK;
6764    }
6765
6766    if ((cmdName[0] == 'm') && (strncmp(cmdName, "mode", len) == 0)) {
6767        if (argc != 3) {
6768            Tcl_AppendResult(interp, "channel name required",
6769                    (char *) NULL);
6770            return TCL_ERROR;
6771        }
6772
6773        if (chanPtr->flags & TCL_READABLE) {
6774            Tcl_AppendElement(interp, "read");
6775        } else {
6776            Tcl_AppendElement(interp, "");
6777        }
6778        if (chanPtr->flags & TCL_WRITABLE) {
6779            Tcl_AppendElement(interp, "write");
6780        } else {
6781            Tcl_AppendElement(interp, "");
6782        }
6783        return TCL_OK;
6784    }
6785
6786    if ((cmdName[0] == 'n') && (strncmp(cmdName, "name", len) == 0)) {
6787        if (argc != 3) {
6788            Tcl_AppendResult(interp, "channel name required",
6789                    (char *) NULL);
6790            return TCL_ERROR;
6791        }
6792        Tcl_AppendResult(interp, chanPtr->channelName, (char *) NULL);
6793        return TCL_OK;
6794    }
6795
6796    if ((cmdName[0] == 'o') && (strncmp(cmdName, "open", len) == 0)) {
6797        hTblPtr = (Tcl_HashTable *) Tcl_GetAssocData(interp, "tclIO", NULL);
6798        if (hTblPtr == (Tcl_HashTable *) NULL) {
6799            return TCL_OK;
6800        }
6801        for (hPtr = Tcl_FirstHashEntry(hTblPtr, &hSearch);
6802                 hPtr != (Tcl_HashEntry *) NULL;
6803                 hPtr = Tcl_NextHashEntry(&hSearch)) {
6804            Tcl_AppendElement(interp, Tcl_GetHashKey(hTblPtr, hPtr));
6805        }
6806        return TCL_OK;
6807    }
6808
6809    if ((cmdName[0] == 'o') &&
6810            (strncmp(cmdName, "outputbuffered", len) == 0)) {
6811        if (argc != 3) {
6812            Tcl_AppendResult(interp, "channel name required",
6813                    (char *) NULL);
6814            return TCL_ERROR;
6815        }
6816
6817        IOQueued = 0;
6818        if (chanPtr->curOutPtr != (ChannelBuffer *) NULL) {
6819            IOQueued = chanPtr->curOutPtr->nextAdded -
6820                chanPtr->curOutPtr->nextRemoved;
6821        }
6822        for (bufPtr = chanPtr->outQueueHead;
6823                 bufPtr != (ChannelBuffer *) NULL;
6824                 bufPtr = bufPtr->nextPtr) {
6825            IOQueued += (bufPtr->nextAdded - bufPtr->nextRemoved);
6826        }
6827        TclFormatInt(buf, IOQueued);
6828        Tcl_AppendResult(interp, buf, (char *) NULL);
6829        return TCL_OK;
6830    }
6831
6832    if ((cmdName[0] == 'q') &&
6833            (strncmp(cmdName, "queuedcr", len) == 0)) {
6834        if (argc != 3) {
6835            Tcl_AppendResult(interp, "channel name required",
6836                    (char *) NULL);
6837            return TCL_ERROR;
6838        }
6839
6840        Tcl_AppendResult(interp,
6841                (chanPtr->flags & INPUT_SAW_CR) ? "1" : "0",
6842                (char *) NULL);
6843        return TCL_OK;
6844    }
6845
6846    if ((cmdName[0] == 'r') && (strncmp(cmdName, "readable", len) == 0)) {
6847        hTblPtr = (Tcl_HashTable *) Tcl_GetAssocData(interp, "tclIO", NULL);
6848        if (hTblPtr == (Tcl_HashTable *) NULL) {
6849            return TCL_OK;
6850        }
6851        for (hPtr = Tcl_FirstHashEntry(hTblPtr, &hSearch);
6852                 hPtr != (Tcl_HashEntry *) NULL;
6853                 hPtr = Tcl_NextHashEntry(&hSearch)) {
6854            chanPtr = (Channel *) Tcl_GetHashValue(hPtr);
6855            if (chanPtr->flags & TCL_READABLE) {
6856                Tcl_AppendElement(interp, Tcl_GetHashKey(hTblPtr, hPtr));
6857            }
6858        }
6859        return TCL_OK;
6860    }
6861
6862    if ((cmdName[0] == 'r') && (strncmp(cmdName, "refcount", len) == 0)) {
6863        if (argc != 3) {
6864            Tcl_AppendResult(interp, "channel name required",
6865                    (char *) NULL);
6866            return TCL_ERROR;
6867        }
6868
6869        TclFormatInt(buf, chanPtr->refCount);
6870        Tcl_AppendResult(interp, buf, (char *) NULL);
6871        return TCL_OK;
6872    }
6873
6874    if ((cmdName[0] == 't') && (strncmp(cmdName, "type", len) == 0)) {
6875        if (argc != 3) {
6876            Tcl_AppendResult(interp, "channel name required",
6877                    (char *) NULL);
6878            return TCL_ERROR;
6879        }
6880        Tcl_AppendResult(interp, chanPtr->typePtr->typeName, (char *) NULL);
6881        return TCL_OK;
6882    }
6883
6884    if ((cmdName[0] == 'w') && (strncmp(cmdName, "writable", len) == 0)) {
6885        hTblPtr = (Tcl_HashTable *) Tcl_GetAssocData(interp, "tclIO", NULL);
6886        if (hTblPtr == (Tcl_HashTable *) NULL) {
6887            return TCL_OK;
6888        }
6889        for (hPtr = Tcl_FirstHashEntry(hTblPtr, &hSearch);
6890                 hPtr != (Tcl_HashEntry *) NULL;
6891                 hPtr = Tcl_NextHashEntry(&hSearch)) {
6892            chanPtr = (Channel *) Tcl_GetHashValue(hPtr);
6893            if (chanPtr->flags & TCL_WRITABLE) {
6894                Tcl_AppendElement(interp, Tcl_GetHashKey(hTblPtr, hPtr));
6895            }
6896        }
6897        return TCL_OK;
6898    }
6899
6900    Tcl_AppendResult(interp, "bad option \"", cmdName, "\": should be ",
6901            "info, open, readable, or writable",
6902            (char *) NULL);
6903    return TCL_ERROR;
6904}
6905
6906/*
6907 *----------------------------------------------------------------------
6908 *
6909 * TclTestChannelEventCmd --
6910 *
6911 *	This procedure implements the "testchannelevent" command. It is
6912 *	used to test the Tcl channel event mechanism. It is present in
6913 *	this file instead of tclTest.c because it needs access to the
6914 *	internal structure of the channel.
6915 *
6916 * Results:
6917 *	A standard Tcl result.
6918 *
6919 * Side effects:
6920 *	Creates, deletes and returns channel event handlers.
6921 *
6922 *----------------------------------------------------------------------
6923 */
6924
6925	/* ARGSUSED */
6926int
6927TclTestChannelEventCmd(dummy, interp, argc, argv)
6928    ClientData dummy;			/* Not used. */
6929    Tcl_Interp *interp;			/* Current interpreter. */
6930    int argc;				/* Number of arguments. */
6931    char **argv;			/* Argument strings. */
6932{
6933    Tcl_Obj *resultListPtr;
6934    Channel *chanPtr;
6935    EventScriptRecord *esPtr, *prevEsPtr, *nextEsPtr;
6936    char *cmd;
6937    int index, i, mask, len;
6938
6939    if ((argc < 3) || (argc > 5)) {
6940        Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
6941                " channelName cmd ?arg1? ?arg2?\"", (char *) NULL);
6942        return TCL_ERROR;
6943    }
6944    chanPtr = (Channel *) Tcl_GetChannel(interp, argv[1], NULL);
6945    if (chanPtr == (Channel *) NULL) {
6946        return TCL_ERROR;
6947    }
6948    cmd = argv[2];
6949    len = strlen(cmd);
6950    if ((cmd[0] == 'a') && (strncmp(cmd, "add", (unsigned) len) == 0)) {
6951        if (argc != 5) {
6952            Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
6953                    " channelName add eventSpec script\"", (char *) NULL);
6954            return TCL_ERROR;
6955        }
6956        if (strcmp(argv[3], "readable") == 0) {
6957            mask = TCL_READABLE;
6958        } else if (strcmp(argv[3], "writable") == 0) {
6959            mask = TCL_WRITABLE;
6960        } else if (strcmp(argv[3], "none") == 0) {
6961            mask = 0;
6962	} else {
6963            Tcl_AppendResult(interp, "bad event name \"", argv[3],
6964                    "\": must be readable, writable, or none", (char *) NULL);
6965            return TCL_ERROR;
6966        }
6967
6968        esPtr = (EventScriptRecord *) ckalloc((unsigned)
6969                sizeof(EventScriptRecord));
6970        esPtr->nextPtr = chanPtr->scriptRecordPtr;
6971        chanPtr->scriptRecordPtr = esPtr;
6972
6973        esPtr->chanPtr = chanPtr;
6974        esPtr->interp = interp;
6975        esPtr->mask = mask;
6976	esPtr->scriptPtr = Tcl_NewStringObj(argv[4], -1);
6977	Tcl_IncrRefCount(esPtr->scriptPtr);
6978
6979        Tcl_CreateChannelHandler((Tcl_Channel) chanPtr, mask,
6980                ChannelEventScriptInvoker, (ClientData) esPtr);
6981
6982        return TCL_OK;
6983    }
6984
6985    if ((cmd[0] == 'd') && (strncmp(cmd, "delete", (unsigned) len) == 0)) {
6986        if (argc != 4) {
6987            Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
6988                    " channelName delete index\"", (char *) NULL);
6989            return TCL_ERROR;
6990        }
6991        if (Tcl_GetInt(interp, argv[3], &index) == TCL_ERROR) {
6992            return TCL_ERROR;
6993        }
6994        if (index < 0) {
6995            Tcl_AppendResult(interp, "bad event index: ", argv[3],
6996                    ": must be nonnegative", (char *) NULL);
6997            return TCL_ERROR;
6998        }
6999        for (i = 0, esPtr = chanPtr->scriptRecordPtr;
7000                 (i < index) && (esPtr != (EventScriptRecord *) NULL);
7001                 i++, esPtr = esPtr->nextPtr) {
7002	    /* Empty loop body. */
7003        }
7004        if (esPtr == (EventScriptRecord *) NULL) {
7005            Tcl_AppendResult(interp, "bad event index ", argv[3],
7006                    ": out of range", (char *) NULL);
7007            return TCL_ERROR;
7008        }
7009        if (esPtr == chanPtr->scriptRecordPtr) {
7010            chanPtr->scriptRecordPtr = esPtr->nextPtr;
7011        } else {
7012            for (prevEsPtr = chanPtr->scriptRecordPtr;
7013                     (prevEsPtr != (EventScriptRecord *) NULL) &&
7014                         (prevEsPtr->nextPtr != esPtr);
7015                     prevEsPtr = prevEsPtr->nextPtr) {
7016                /* Empty loop body. */
7017            }
7018            if (prevEsPtr == (EventScriptRecord *) NULL) {
7019                panic("TclTestChannelEventCmd: damaged event script list");
7020            }
7021            prevEsPtr->nextPtr = esPtr->nextPtr;
7022        }
7023        Tcl_DeleteChannelHandler((Tcl_Channel) chanPtr,
7024                ChannelEventScriptInvoker, (ClientData) esPtr);
7025	Tcl_DecrRefCount(esPtr->scriptPtr);
7026        ckfree((char *) esPtr);
7027
7028        return TCL_OK;
7029    }
7030
7031    if ((cmd[0] == 'l') && (strncmp(cmd, "list", (unsigned) len) == 0)) {
7032        if (argc != 3) {
7033            Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
7034                    " channelName list\"", (char *) NULL);
7035            return TCL_ERROR;
7036        }
7037	resultListPtr = Tcl_GetObjResult(interp);
7038        for (esPtr = chanPtr->scriptRecordPtr;
7039                 esPtr != (EventScriptRecord *) NULL;
7040                 esPtr = esPtr->nextPtr) {
7041	    if (esPtr->mask) {
7042 	        Tcl_ListObjAppendElement(interp, resultListPtr, Tcl_NewStringObj(
7043		    (esPtr->mask == TCL_READABLE) ? "readable" : "writable", -1));
7044 	    } else {
7045 	        Tcl_ListObjAppendElement(interp, resultListPtr,
7046                    Tcl_NewStringObj("none", -1));
7047	    }
7048  	    Tcl_ListObjAppendElement(interp, resultListPtr, esPtr->scriptPtr);
7049        }
7050	Tcl_SetObjResult(interp, resultListPtr);
7051        return TCL_OK;
7052    }
7053
7054    if ((cmd[0] == 'r') && (strncmp(cmd, "removeall", (unsigned) len) == 0)) {
7055        if (argc != 3) {
7056            Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
7057                    " channelName removeall\"", (char *) NULL);
7058            return TCL_ERROR;
7059        }
7060        for (esPtr = chanPtr->scriptRecordPtr;
7061                 esPtr != (EventScriptRecord *) NULL;
7062                 esPtr = nextEsPtr) {
7063            nextEsPtr = esPtr->nextPtr;
7064            Tcl_DeleteChannelHandler((Tcl_Channel) chanPtr,
7065                    ChannelEventScriptInvoker, (ClientData) esPtr);
7066	    Tcl_DecrRefCount(esPtr->scriptPtr);
7067            ckfree((char *) esPtr);
7068        }
7069        chanPtr->scriptRecordPtr = (EventScriptRecord *) NULL;
7070        return TCL_OK;
7071    }
7072
7073    if  ((cmd[0] == 's') && (strncmp(cmd, "set", (unsigned) len) == 0)) {
7074        if (argc != 5) {
7075            Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
7076                    " channelName delete index event\"", (char *) NULL);
7077            return TCL_ERROR;
7078        }
7079        if (Tcl_GetInt(interp, argv[3], &index) == TCL_ERROR) {
7080            return TCL_ERROR;
7081        }
7082        if (index < 0) {
7083            Tcl_AppendResult(interp, "bad event index: ", argv[3],
7084                    ": must be nonnegative", (char *) NULL);
7085            return TCL_ERROR;
7086        }
7087        for (i = 0, esPtr = chanPtr->scriptRecordPtr;
7088                 (i < index) && (esPtr != (EventScriptRecord *) NULL);
7089                 i++, esPtr = esPtr->nextPtr) {
7090	    /* Empty loop body. */
7091        }
7092        if (esPtr == (EventScriptRecord *) NULL) {
7093            Tcl_AppendResult(interp, "bad event index ", argv[3],
7094                    ": out of range", (char *) NULL);
7095            return TCL_ERROR;
7096        }
7097
7098        if (strcmp(argv[4], "readable") == 0) {
7099            mask = TCL_READABLE;
7100        } else if (strcmp(argv[4], "writable") == 0) {
7101            mask = TCL_WRITABLE;
7102        } else if (strcmp(argv[4], "none") == 0) {
7103            mask = 0;
7104	} else {
7105            Tcl_AppendResult(interp, "bad event name \"", argv[4],
7106                    "\": must be readable, writable, or none", (char *) NULL);
7107            return TCL_ERROR;
7108        }
7109	esPtr->mask = mask;
7110        Tcl_CreateChannelHandler((Tcl_Channel) chanPtr, mask,
7111                ChannelEventScriptInvoker, (ClientData) esPtr);
7112	return TCL_OK;
7113    }
7114    Tcl_AppendResult(interp, "bad command ", cmd, ", must be one of ",
7115            "add, delete, list, set, or removeall", (char *) NULL);
7116    return TCL_ERROR;
7117}
7118
7119/*
7120 *----------------------------------------------------------------------
7121 *
7122 * TclCopyChannel --
7123 *
7124 *	This routine copies data from one channel to another, either
7125 *	synchronously or asynchronously.  If a command script is
7126 *	supplied, the operation runs in the background.  The script
7127 *	is invoked when the copy completes.  Otherwise the function
7128 *	waits until the copy is completed before returning.
7129 *
7130 * Results:
7131 *	A standard Tcl result.
7132 *
7133 * Side effects:
7134 *	May schedule a background copy operation that causes both
7135 *	channels to be marked busy.
7136 *
7137 *----------------------------------------------------------------------
7138 */
7139
7140int
7141TclCopyChannel(interp, inChan, outChan, toRead, cmdPtr)
7142    Tcl_Interp *interp;		/* Current interpreter. */
7143    Tcl_Channel inChan;		/* Channel to read from. */
7144    Tcl_Channel outChan;	/* Channel to write to. */
7145    int toRead;			/* Amount of data to copy, or -1 for all. */
7146    Tcl_Obj *cmdPtr;		/* Pointer to script to execute or NULL. */
7147{
7148    Channel *inPtr = (Channel *) inChan;
7149    Channel *outPtr = (Channel *) outChan;
7150    int readFlags, writeFlags;
7151    CopyState *csPtr;
7152    int nonBlocking = (cmdPtr) ? CHANNEL_NONBLOCKING : 0;
7153
7154    if (inPtr->csPtr) {
7155	Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "channel \"",
7156		Tcl_GetChannelName(inChan), "\" is busy", NULL);
7157	return TCL_ERROR;
7158    }
7159    if (outPtr->csPtr) {
7160	Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "channel \"",
7161		Tcl_GetChannelName(outChan), "\" is busy", NULL);
7162	return TCL_ERROR;
7163    }
7164
7165    readFlags = inPtr->flags;
7166    writeFlags = outPtr->flags;
7167
7168    /*
7169     * Set up the blocking mode appropriately.  Background copies need
7170     * non-blocking channels.  Foreground copies need blocking channels.
7171     * If there is an error, restore the old blocking mode.
7172     */
7173
7174    if (nonBlocking != (readFlags & CHANNEL_NONBLOCKING)) {
7175	if (SetBlockMode(interp, inPtr,
7176		nonBlocking ? TCL_MODE_NONBLOCKING : TCL_MODE_BLOCKING)
7177		!= TCL_OK) {
7178	    return TCL_ERROR;
7179	}
7180    }
7181    if (inPtr != outPtr) {
7182	if (nonBlocking != (writeFlags & CHANNEL_NONBLOCKING)) {
7183	    if (SetBlockMode(NULL, outPtr,
7184		    nonBlocking ? TCL_MODE_BLOCKING : TCL_MODE_NONBLOCKING)
7185		    != TCL_OK) {
7186		if (nonBlocking != (readFlags & CHANNEL_NONBLOCKING)) {
7187		    SetBlockMode(NULL, inPtr,
7188			    (readFlags & CHANNEL_NONBLOCKING)
7189			    ? TCL_MODE_NONBLOCKING : TCL_MODE_BLOCKING);
7190		    return TCL_ERROR;
7191		}
7192	    }
7193	}
7194    }
7195
7196    /*
7197     * Make sure the output side is unbuffered.
7198     */
7199
7200    outPtr->flags = (outPtr->flags & ~(CHANNEL_LINEBUFFERED))
7201	| CHANNEL_UNBUFFERED;
7202
7203    /*
7204     * Allocate a new CopyState to maintain info about the current copy in
7205     * progress.  This structure will be deallocated when the copy is
7206     * completed.
7207     */
7208
7209    csPtr = (CopyState*) ckalloc(sizeof(CopyState) + inPtr->bufSize);
7210    csPtr->bufSize = inPtr->bufSize;
7211    csPtr->readPtr = inPtr;
7212    csPtr->writePtr = outPtr;
7213    csPtr->readFlags = readFlags;
7214    csPtr->writeFlags = writeFlags;
7215    csPtr->toRead = toRead;
7216    csPtr->total = 0;
7217    csPtr->interp = interp;
7218    if (cmdPtr) {
7219	Tcl_IncrRefCount(cmdPtr);
7220    }
7221    csPtr->cmdPtr = cmdPtr;
7222    inPtr->csPtr = csPtr;
7223    outPtr->csPtr = csPtr;
7224
7225    /*
7226     * Start copying data between the channels.
7227     */
7228
7229    return CopyData(csPtr, 0);
7230}
7231
7232/*
7233 *----------------------------------------------------------------------
7234 *
7235 * CopyData --
7236 *
7237 *	This function implements the lowest level of the copying
7238 *	mechanism for TclCopyChannel.
7239 *
7240 * Results:
7241 *	Returns TCL_OK on success, else TCL_ERROR.
7242 *
7243 * Side effects:
7244 *	Moves data between channels, may create channel handlers.
7245 *
7246 *----------------------------------------------------------------------
7247 */
7248
7249static int
7250CopyData(csPtr, mask)
7251    CopyState *csPtr;		/* State of copy operation. */
7252    int mask;			/* Current channel event flags. */
7253{
7254    Tcl_Interp *interp;
7255    Tcl_Obj *cmdPtr, *errObj = NULL;
7256    Tcl_Channel inChan, outChan;
7257    int result = TCL_OK;
7258    int size;
7259    int total;
7260
7261    inChan = (Tcl_Channel)csPtr->readPtr;
7262    outChan = (Tcl_Channel)csPtr->writePtr;
7263    interp = csPtr->interp;
7264    cmdPtr = csPtr->cmdPtr;
7265
7266    /*
7267     * Copy the data the slow way, using the translation mechanism.
7268     */
7269
7270    while (csPtr->toRead != 0) {
7271
7272	/*
7273	 * Check for unreported background errors.
7274	 */
7275
7276	if (csPtr->readPtr->unreportedError != 0) {
7277	    Tcl_SetErrno(csPtr->readPtr->unreportedError);
7278	    csPtr->readPtr->unreportedError = 0;
7279	    goto readError;
7280	}
7281	if (csPtr->writePtr->unreportedError != 0) {
7282	    Tcl_SetErrno(csPtr->writePtr->unreportedError);
7283	    csPtr->writePtr->unreportedError = 0;
7284	    goto writeError;
7285	}
7286
7287	/*
7288	 * Read up to bufSize bytes.
7289	 */
7290
7291	if ((csPtr->toRead == -1)
7292		|| (csPtr->toRead > csPtr->bufSize)) {
7293	    size = csPtr->bufSize;
7294	} else {
7295	    size = csPtr->toRead;
7296	}
7297	size = DoRead(csPtr->readPtr, csPtr->buffer, size);
7298
7299	if (size < 0) {
7300	    readError:
7301	    errObj = Tcl_NewObj();
7302	    Tcl_AppendStringsToObj(errObj, "error reading \"",
7303		    Tcl_GetChannelName(inChan), "\": ",
7304		    Tcl_PosixError(interp), (char *) NULL);
7305	    break;
7306	} else if (size == 0) {
7307	    /*
7308	     * We had an underflow on the read side.  If we are at EOF,
7309	     * then the copying is done, otherwise set up a channel
7310	     * handler to detect when the channel becomes readable again.
7311	     */
7312
7313	    if (Tcl_Eof(inChan)) {
7314		break;
7315	    } else if (!(mask & TCL_READABLE)) {
7316		if (mask & TCL_WRITABLE) {
7317		    Tcl_DeleteChannelHandler(outChan, CopyEventProc,
7318			    (ClientData) csPtr);
7319		}
7320		Tcl_CreateChannelHandler(inChan, TCL_READABLE,
7321			CopyEventProc, (ClientData) csPtr);
7322	    }
7323	    return TCL_OK;
7324	}
7325
7326	/*
7327	 * Now write the buffer out.
7328	 */
7329
7330	size = DoWrite(csPtr->writePtr, csPtr->buffer, size);
7331	if (size < 0) {
7332	    writeError:
7333	    errObj = Tcl_NewObj();
7334	    Tcl_AppendStringsToObj(errObj, "error writing \"",
7335		    Tcl_GetChannelName(outChan), "\": ",
7336		    Tcl_PosixError(interp), (char *) NULL);
7337	    break;
7338	}
7339
7340	/*
7341	 * Check to see if the write is happening in the background.  If so,
7342	 * stop copying and wait for the channel to become writable again.
7343	 */
7344
7345	if (csPtr->writePtr->flags & BG_FLUSH_SCHEDULED) {
7346	    if (!(mask & TCL_WRITABLE)) {
7347		if (mask & TCL_READABLE) {
7348		    Tcl_DeleteChannelHandler(outChan, CopyEventProc,
7349			    (ClientData) csPtr);
7350		}
7351		Tcl_CreateChannelHandler(outChan, TCL_WRITABLE,
7352			CopyEventProc, (ClientData) csPtr);
7353	    }
7354	    return TCL_OK;
7355	}
7356
7357	/*
7358	 * Update the current byte count if we care.
7359	 */
7360
7361	if (csPtr->toRead != -1) {
7362	    csPtr->toRead -= size;
7363	}
7364	csPtr->total += size;
7365
7366	/*
7367	 * For background copies, we only do one buffer per invocation so
7368	 * we don't starve the rest of the system.
7369	 */
7370
7371	if (cmdPtr) {
7372	    /*
7373	     * The first time we enter this code, there won't be a
7374	     * channel handler established yet, so do it here.
7375	     */
7376
7377	    if (mask == 0) {
7378		Tcl_CreateChannelHandler(outChan, TCL_WRITABLE,
7379			CopyEventProc, (ClientData) csPtr);
7380	    }
7381	    return TCL_OK;
7382	}
7383    }
7384
7385    /*
7386     * Make the callback or return the number of bytes transferred.
7387     * The local total is used because StopCopy frees csPtr.
7388     */
7389
7390    total = csPtr->total;
7391    if (cmdPtr) {
7392	/*
7393	 * Get a private copy of the command so we can mutate it
7394	 * by adding arguments.  Note that StopCopy frees our saved
7395	 * reference to the original command obj.
7396	 */
7397
7398	cmdPtr = Tcl_DuplicateObj(cmdPtr);
7399	Tcl_IncrRefCount(cmdPtr);
7400	StopCopy(csPtr);
7401	Tcl_Preserve((ClientData) interp);
7402
7403	Tcl_ListObjAppendElement(interp, cmdPtr, Tcl_NewIntObj(total));
7404	if (errObj) {
7405	    Tcl_ListObjAppendElement(interp, cmdPtr, errObj);
7406	}
7407	if (Tcl_EvalObj(interp, cmdPtr, TCL_EVAL_GLOBAL) != TCL_OK) {
7408	    Tcl_BackgroundError(interp);
7409	    result = TCL_ERROR;
7410	}
7411	Tcl_DecrRefCount(cmdPtr);
7412	Tcl_Release((ClientData) interp);
7413    } else {
7414	StopCopy(csPtr);
7415	if (errObj) {
7416	    Tcl_SetObjResult(interp, errObj);
7417	    result = TCL_ERROR;
7418	} else {
7419	    Tcl_ResetResult(interp);
7420	    Tcl_SetIntObj(Tcl_GetObjResult(interp), total);
7421	}
7422    }
7423    return result;
7424}
7425
7426/*
7427 *----------------------------------------------------------------------
7428 *
7429 * DoRead --
7430 *
7431 *	Reads a given number of bytes from a channel.
7432 *
7433 * Results:
7434 *	The number of characters read, or -1 on error. Use Tcl_GetErrno()
7435 *	to retrieve the error code for the error that occurred.
7436 *
7437 * Side effects:
7438 *	May cause input to be buffered.
7439 *
7440 *----------------------------------------------------------------------
7441 */
7442
7443static int
7444DoRead(chanPtr, bufPtr, toRead)
7445    Channel *chanPtr;		/* The channel from which to read. */
7446    char *bufPtr;		/* Where to store input read. */
7447    int toRead;			/* Maximum number of bytes to read. */
7448{
7449    int copied;			/* How many characters were copied into
7450                                 * the result string? */
7451    int copiedNow;		/* How many characters were copied from
7452                                 * the current input buffer? */
7453    int result;			/* Of calling GetInput. */
7454
7455    /*
7456     * If we have not encountered a sticky EOF, clear the EOF bit. Either
7457     * way clear the BLOCKED bit. We want to discover these anew during
7458     * each operation.
7459     */
7460
7461    if (!(chanPtr->flags & CHANNEL_STICKY_EOF)) {
7462        chanPtr->flags &= ~CHANNEL_EOF;
7463    }
7464    chanPtr->flags &= ~(CHANNEL_BLOCKED | CHANNEL_NEED_MORE_DATA);
7465
7466    for (copied = 0; copied < toRead; copied += copiedNow) {
7467        copiedNow = CopyAndTranslateBuffer(chanPtr, bufPtr + copied,
7468                toRead - copied);
7469        if (copiedNow == 0) {
7470            if (chanPtr->flags & CHANNEL_EOF) {
7471		goto done;
7472            }
7473            if (chanPtr->flags & CHANNEL_BLOCKED) {
7474                if (chanPtr->flags & CHANNEL_NONBLOCKING) {
7475		    goto done;
7476                }
7477                chanPtr->flags &= (~(CHANNEL_BLOCKED));
7478            }
7479            result = GetInput(chanPtr);
7480            if (result != 0) {
7481                if (result != EAGAIN) {
7482                    copied = -1;
7483                }
7484		goto done;
7485            }
7486        }
7487    }
7488
7489    chanPtr->flags &= (~(CHANNEL_BLOCKED));
7490
7491    done:
7492    /*
7493     * Update the notifier state so we don't block while there is still
7494     * data in the buffers.
7495     */
7496
7497    UpdateInterest(chanPtr);
7498    return copied;
7499}
7500
7501/*
7502 *----------------------------------------------------------------------
7503 *
7504 * CopyAndTranslateBuffer --
7505 *
7506 *	Copy at most one buffer of input to the result space, doing
7507 *	eol translations according to mode in effect currently.
7508 *
7509 * Results:
7510 *	Number of bytes stored in the result buffer (as opposed to the
7511 *	number of bytes read from the channel).  May return
7512 *	zero if no input is available to be translated.
7513 *
7514 * Side effects:
7515 *	Consumes buffered input. May deallocate one buffer.
7516 *
7517 *----------------------------------------------------------------------
7518 */
7519
7520static int
7521CopyAndTranslateBuffer(chanPtr, result, space)
7522    Channel *chanPtr;		/* The channel from which to read input. */
7523    char *result;		/* Where to store the copied input. */
7524    int space;			/* How many bytes are available in result
7525                                 * to store the copied input? */
7526{
7527    int bytesInBuffer;		/* How many bytes are available to be
7528                                 * copied in the current input buffer? */
7529    int copied;			/* How many characters were already copied
7530                                 * into the destination space? */
7531    ChannelBuffer *bufPtr;	/* The buffer from which to copy bytes. */
7532    int i;			/* Iterates over the copied input looking
7533                                 * for the input eofChar. */
7534
7535    /*
7536     * If there is no input at all, return zero. The invariant is that either
7537     * there is no buffer in the queue, or if the first buffer is empty, it
7538     * is also the last buffer (and thus there is no input in the queue).
7539     * Note also that if the buffer is empty, we leave it in the queue.
7540     */
7541
7542    if (chanPtr->inQueueHead == (ChannelBuffer *) NULL) {
7543        return 0;
7544    }
7545    bufPtr = chanPtr->inQueueHead;
7546    bytesInBuffer = bufPtr->nextAdded - bufPtr->nextRemoved;
7547
7548    copied = 0;
7549    switch (chanPtr->inputTranslation) {
7550        case TCL_TRANSLATE_LF: {
7551            if (bytesInBuffer == 0) {
7552                return 0;
7553            }
7554
7555	    /*
7556             * Copy the current chunk into the result buffer.
7557             */
7558
7559	    if (bytesInBuffer < space) {
7560		space = bytesInBuffer;
7561	    }
7562	    memcpy((VOID *) result,
7563		    (VOID *) (bufPtr->buf + bufPtr->nextRemoved),
7564		    (size_t) space);
7565	    bufPtr->nextRemoved += space;
7566	    copied = space;
7567            break;
7568	}
7569        case TCL_TRANSLATE_CR: {
7570	    char *end;
7571
7572            if (bytesInBuffer == 0) {
7573                return 0;
7574            }
7575
7576	    /*
7577             * Copy the current chunk into the result buffer, then
7578             * replace all \r with \n.
7579             */
7580
7581	    if (bytesInBuffer < space) {
7582		space = bytesInBuffer;
7583	    }
7584	    memcpy((VOID *) result,
7585		    (VOID *) (bufPtr->buf + bufPtr->nextRemoved),
7586		    (size_t) space);
7587	    bufPtr->nextRemoved += space;
7588	    copied = space;
7589
7590	    for (end = result + copied; result < end; result++) {
7591		if (*result == '\r') {
7592		    *result = '\n';
7593		}
7594            }
7595            break;
7596	}
7597        case TCL_TRANSLATE_CRLF: {
7598	    char *src, *end, *dst;
7599	    int curByte;
7600
7601            /*
7602             * If there is a held-back "\r" at EOF, produce it now.
7603             */
7604
7605	    if (bytesInBuffer == 0) {
7606                if ((chanPtr->flags & (INPUT_SAW_CR | CHANNEL_EOF)) ==
7607                        (INPUT_SAW_CR | CHANNEL_EOF)) {
7608                    result[0] = '\r';
7609                    chanPtr->flags &= ~INPUT_SAW_CR;
7610                    return 1;
7611                }
7612                return 0;
7613            }
7614
7615            /*
7616             * Copy the current chunk and replace "\r\n" with "\n"
7617             * (but not standalone "\r"!).
7618             */
7619
7620	    if (bytesInBuffer < space) {
7621		space = bytesInBuffer;
7622	    }
7623	    memcpy((VOID *) result,
7624		    (VOID *) (bufPtr->buf + bufPtr->nextRemoved),
7625		    (size_t) space);
7626	    bufPtr->nextRemoved += space;
7627	    copied = space;
7628
7629	    end = result + copied;
7630	    dst = result;
7631	    for (src = result; src < end; src++) {
7632		curByte = *src;
7633		if (curByte == '\n') {
7634                    chanPtr->flags &= ~INPUT_SAW_CR;
7635		} else if (chanPtr->flags & INPUT_SAW_CR) {
7636		    chanPtr->flags &= ~INPUT_SAW_CR;
7637		    *dst = '\r';
7638		    dst++;
7639		}
7640		if (curByte == '\r') {
7641		    chanPtr->flags |= INPUT_SAW_CR;
7642		} else {
7643		    *dst = (char) curByte;
7644		    dst++;
7645		}
7646	    }
7647	    copied = dst - result;
7648	    break;
7649	}
7650        case TCL_TRANSLATE_AUTO: {
7651	    char *src, *end, *dst;
7652	    int curByte;
7653
7654            if (bytesInBuffer == 0) {
7655                return 0;
7656            }
7657
7658            /*
7659             * Loop over the current buffer, converting "\r" and "\r\n"
7660             * to "\n".
7661             */
7662
7663	    if (bytesInBuffer < space) {
7664		space = bytesInBuffer;
7665	    }
7666	    memcpy((VOID *) result,
7667		    (VOID *) (bufPtr->buf + bufPtr->nextRemoved),
7668		    (size_t) space);
7669	    bufPtr->nextRemoved += space;
7670	    copied = space;
7671
7672	    end = result + copied;
7673	    dst = result;
7674	    for (src = result; src < end; src++) {
7675		curByte = *src;
7676		if (curByte == '\r') {
7677		    chanPtr->flags |= INPUT_SAW_CR;
7678		    *dst = '\n';
7679		    dst++;
7680		} else {
7681		    if ((curByte != '\n') ||
7682			    !(chanPtr->flags & INPUT_SAW_CR)) {
7683			*dst = (char) curByte;
7684			dst++;
7685		    }
7686		    chanPtr->flags &= ~INPUT_SAW_CR;
7687		}
7688	    }
7689	    copied = dst - result;
7690            break;
7691	}
7692        default: {
7693            panic("unknown eol translation mode");
7694	}
7695    }
7696
7697    /*
7698     * If an in-stream EOF character is set for this channel, check that
7699     * the input we copied so far does not contain the EOF char.  If it does,
7700     * copy only up to and excluding that character.
7701     */
7702
7703    if (chanPtr->inEofChar != 0) {
7704        for (i = 0; i < copied; i++) {
7705            if (result[i] == (char) chanPtr->inEofChar) {
7706		/*
7707		 * Set sticky EOF so that no further input is presented
7708		 * to the caller.
7709		 */
7710
7711		chanPtr->flags |= (CHANNEL_EOF | CHANNEL_STICKY_EOF);
7712		chanPtr->inputEncodingFlags |= TCL_ENCODING_END;
7713		copied = i;
7714                break;
7715            }
7716        }
7717    }
7718
7719    /*
7720     * If the current buffer is empty recycle it.
7721     */
7722
7723    if (bufPtr->nextRemoved == bufPtr->nextAdded) {
7724        chanPtr->inQueueHead = bufPtr->nextPtr;
7725        if (chanPtr->inQueueHead == (ChannelBuffer *) NULL) {
7726            chanPtr->inQueueTail = (ChannelBuffer *) NULL;
7727        }
7728        RecycleBuffer(chanPtr, bufPtr, 0);
7729    }
7730
7731    /*
7732     * Return the number of characters copied into the result buffer.
7733     * This may be different from the number of bytes consumed, because
7734     * of EOL translations.
7735     */
7736
7737    return copied;
7738}
7739
7740/*
7741 *----------------------------------------------------------------------
7742 *
7743 * DoWrite --
7744 *
7745 *	Puts a sequence of characters into an output buffer, may queue the
7746 *	buffer for output if it gets full, and also remembers whether the
7747 *	current buffer is ready e.g. if it contains a newline and we are in
7748 *	line buffering mode.
7749 *
7750 * Results:
7751 *	The number of bytes written or -1 in case of error. If -1,
7752 *	Tcl_GetErrno will return the error code.
7753 *
7754 * Side effects:
7755 *	May buffer up output and may cause output to be produced on the
7756 *	channel.
7757 *
7758 *----------------------------------------------------------------------
7759 */
7760
7761static int
7762DoWrite(chanPtr, src, srcLen)
7763    Channel *chanPtr;			/* The channel to buffer output for. */
7764    char *src;				/* Data to write. */
7765    int srcLen;				/* Number of bytes to write. */
7766{
7767    ChannelBuffer *outBufPtr;		/* Current output buffer. */
7768    int foundNewline;			/* Did we find a newline in output? */
7769    char *dPtr;
7770    char *sPtr;				/* Search variables for newline. */
7771    int crsent;				/* In CRLF eol translation mode,
7772                                         * remember the fact that a CR was
7773                                         * output to the channel without
7774                                         * its following NL. */
7775    int i;				/* Loop index for newline search. */
7776    int destCopied;			/* How many bytes were used in this
7777                                         * destination buffer to hold the
7778                                         * output? */
7779    int totalDestCopied;		/* How many bytes total were
7780                                         * copied to the channel buffer? */
7781    int srcCopied;			/* How many bytes were copied from
7782                                         * the source string? */
7783    char *destPtr;			/* Where in line to copy to? */
7784
7785    /*
7786     * If we are in network (or windows) translation mode, record the fact
7787     * that we have not yet sent a CR to the channel.
7788     */
7789
7790    crsent = 0;
7791
7792    /*
7793     * Loop filling buffers and flushing them until all output has been
7794     * consumed.
7795     */
7796
7797    srcCopied = 0;
7798    totalDestCopied = 0;
7799
7800    while (srcLen > 0) {
7801
7802        /*
7803         * Make sure there is a current output buffer to accept output.
7804         */
7805
7806        if (chanPtr->curOutPtr == (ChannelBuffer *) NULL) {
7807            chanPtr->curOutPtr = AllocChannelBuffer(chanPtr->bufSize);
7808        }
7809
7810        outBufPtr = chanPtr->curOutPtr;
7811
7812        destCopied = outBufPtr->bufLength - outBufPtr->nextAdded;
7813        if (destCopied > srcLen) {
7814            destCopied = srcLen;
7815        }
7816
7817        destPtr = outBufPtr->buf + outBufPtr->nextAdded;
7818        switch (chanPtr->outputTranslation) {
7819            case TCL_TRANSLATE_LF:
7820                srcCopied = destCopied;
7821                memcpy((VOID *) destPtr, (VOID *) src, (size_t) destCopied);
7822                break;
7823            case TCL_TRANSLATE_CR:
7824                srcCopied = destCopied;
7825                memcpy((VOID *) destPtr, (VOID *) src, (size_t) destCopied);
7826                for (dPtr = destPtr; dPtr < destPtr + destCopied; dPtr++) {
7827                    if (*dPtr == '\n') {
7828                        *dPtr = '\r';
7829                    }
7830                }
7831                break;
7832            case TCL_TRANSLATE_CRLF:
7833                for (srcCopied = 0, dPtr = destPtr, sPtr = src;
7834                     dPtr < destPtr + destCopied;
7835                     dPtr++, sPtr++, srcCopied++) {
7836                    if (*sPtr == '\n') {
7837                        if (crsent) {
7838                            *dPtr = '\n';
7839                            crsent = 0;
7840                        } else {
7841                            *dPtr = '\r';
7842                            crsent = 1;
7843                            sPtr--, srcCopied--;
7844                        }
7845                    } else {
7846                        *dPtr = *sPtr;
7847                    }
7848                }
7849                break;
7850            case TCL_TRANSLATE_AUTO:
7851                panic("Tcl_Write: AUTO output translation mode not supported");
7852            default:
7853                panic("Tcl_Write: unknown output translation mode");
7854        }
7855
7856        /*
7857         * The current buffer is ready for output if it is full, or if it
7858         * contains a newline and this channel is line-buffered, or if it
7859         * contains any output and this channel is unbuffered.
7860         */
7861
7862        outBufPtr->nextAdded += destCopied;
7863        if (!(chanPtr->flags & BUFFER_READY)) {
7864            if (outBufPtr->nextAdded == outBufPtr->bufLength) {
7865                chanPtr->flags |= BUFFER_READY;
7866            } else if (chanPtr->flags & CHANNEL_LINEBUFFERED) {
7867                for (sPtr = src, i = 0, foundNewline = 0;
7868                         (i < srcCopied) && (!foundNewline);
7869                         i++, sPtr++) {
7870                    if (*sPtr == '\n') {
7871                        foundNewline = 1;
7872                        break;
7873                    }
7874                }
7875                if (foundNewline) {
7876                    chanPtr->flags |= BUFFER_READY;
7877                }
7878            } else if (chanPtr->flags & CHANNEL_UNBUFFERED) {
7879                chanPtr->flags |= BUFFER_READY;
7880            }
7881        }
7882
7883        totalDestCopied += srcCopied;
7884        src += srcCopied;
7885        srcLen -= srcCopied;
7886
7887        if (chanPtr->flags & BUFFER_READY) {
7888            if (FlushChannel(NULL, chanPtr, 0) != 0) {
7889                return -1;
7890            }
7891        }
7892    } /* Closes "while" */
7893
7894    return totalDestCopied;
7895}
7896
7897/*
7898 *----------------------------------------------------------------------
7899 *
7900 * CopyEventProc --
7901 *
7902 *	This routine is invoked as a channel event handler for
7903 *	the background copy operation.  It is just a trivial wrapper
7904 *	around the CopyData routine.
7905 *
7906 * Results:
7907 *	None.
7908 *
7909 * Side effects:
7910 *	None.
7911 *
7912 *----------------------------------------------------------------------
7913 */
7914
7915static void
7916CopyEventProc(clientData, mask)
7917    ClientData clientData;
7918    int mask;
7919{
7920    (void) CopyData((CopyState *)clientData, mask);
7921}
7922
7923/*
7924 *----------------------------------------------------------------------
7925 *
7926 * StopCopy --
7927 *
7928 *	This routine halts a copy that is in progress.
7929 *
7930 * Results:
7931 *	None.
7932 *
7933 * Side effects:
7934 *	Removes any pending channel handlers and restores the blocking
7935 *	and buffering modes of the channels.  The CopyState is freed.
7936 *
7937 *----------------------------------------------------------------------
7938 */
7939
7940static void
7941StopCopy(csPtr)
7942    CopyState *csPtr;		/* State for bg copy to stop . */
7943{
7944    int nonBlocking;
7945
7946    if (!csPtr) {
7947	return;
7948    }
7949
7950    /*
7951     * Restore the old blocking mode and output buffering mode.
7952     */
7953
7954    nonBlocking = (csPtr->readFlags & CHANNEL_NONBLOCKING);
7955    if (nonBlocking != (csPtr->readPtr->flags & CHANNEL_NONBLOCKING)) {
7956	SetBlockMode(NULL, csPtr->readPtr,
7957		nonBlocking ? TCL_MODE_NONBLOCKING : TCL_MODE_BLOCKING);
7958    }
7959    if (csPtr->writePtr != csPtr->writePtr) {
7960	if (nonBlocking != (csPtr->writePtr->flags & CHANNEL_NONBLOCKING)) {
7961	    SetBlockMode(NULL, csPtr->writePtr,
7962		    nonBlocking ? TCL_MODE_NONBLOCKING : TCL_MODE_BLOCKING);
7963	}
7964    }
7965    csPtr->writePtr->flags &= ~(CHANNEL_LINEBUFFERED | CHANNEL_UNBUFFERED);
7966    csPtr->writePtr->flags |=
7967	csPtr->writeFlags & (CHANNEL_LINEBUFFERED | CHANNEL_UNBUFFERED);
7968
7969
7970    if (csPtr->cmdPtr) {
7971	Tcl_DeleteChannelHandler((Tcl_Channel)csPtr->readPtr, CopyEventProc,
7972	    (ClientData)csPtr);
7973	if (csPtr->readPtr != csPtr->writePtr) {
7974	    Tcl_DeleteChannelHandler((Tcl_Channel)csPtr->writePtr,
7975		    CopyEventProc, (ClientData)csPtr);
7976	}
7977        Tcl_DecrRefCount(csPtr->cmdPtr);
7978    }
7979    csPtr->readPtr->csPtr = NULL;
7980    csPtr->writePtr->csPtr = NULL;
7981    ckfree((char*) csPtr);
7982}
7983
7984/*
7985 *----------------------------------------------------------------------
7986 *
7987 * SetBlockMode --
7988 *
7989 *	This function sets the blocking mode for a channel and updates
7990 *	the state flags.
7991 *
7992 * Results:
7993 *	A standard Tcl result.
7994 *
7995 * Side effects:
7996 *	Modifies the blocking mode of the channel and possibly generates
7997 *	an error.
7998 *
7999 *----------------------------------------------------------------------
8000 */
8001
8002static int
8003SetBlockMode(interp, chanPtr, mode)
8004    Tcl_Interp *interp;		/* Interp for error reporting. */
8005    Channel *chanPtr;		/* Channel to modify. */
8006    int mode;			/* One of TCL_MODE_BLOCKING or
8007				 * TCL_MODE_NONBLOCKING. */
8008{
8009    int result = 0;
8010    if (chanPtr->typePtr->blockModeProc != NULL) {
8011	result = (chanPtr->typePtr->blockModeProc) (chanPtr->instanceData,
8012		mode);
8013    }
8014    if (result != 0) {
8015	Tcl_SetErrno(result);
8016	if (interp != (Tcl_Interp *) NULL) {
8017	    Tcl_AppendResult(interp, "error setting blocking mode: ",
8018		    Tcl_PosixError(interp), (char *) NULL);
8019	}
8020	return TCL_ERROR;
8021    }
8022    if (mode == TCL_MODE_BLOCKING) {
8023	chanPtr->flags &= (~(CHANNEL_NONBLOCKING | BG_FLUSH_SCHEDULED));
8024    } else {
8025	chanPtr->flags |= CHANNEL_NONBLOCKING;
8026    }
8027    return TCL_OK;
8028}
8029