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