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
43 typedef 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
63 typedef 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
98 typedef 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
111 typedef 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
133 typedef 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 <andreas_kupries@users.sourceforge.net>, 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
294 typedef 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
320 typedef 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
334 typedef 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
345 typedef 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
379 typedef 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
415 static Tcl_ThreadDataKey dataKey;
416
417
418 /*
419 * Static functions in this file:
420 */
421
422 static ChannelBuffer * AllocChannelBuffer _ANSI_ARGS_((int length));
423 static void ChannelEventScriptInvoker _ANSI_ARGS_((
424 ClientData clientData, int flags));
425 static void ChannelTimerProc _ANSI_ARGS_((
426 ClientData clientData));
427 static int CheckChannelErrors _ANSI_ARGS_((Channel *chanPtr,
428 int direction));
429 static int CheckFlush _ANSI_ARGS_((Channel *chanPtr,
430 ChannelBuffer *bufPtr, int newlineFlag));
431 static int CheckForDeadChannel _ANSI_ARGS_((Tcl_Interp *interp,
432 Channel *chan));
433 static void CheckForStdChannelsBeingClosed _ANSI_ARGS_((
434 Tcl_Channel chan));
435 static void CleanupChannelHandlers _ANSI_ARGS_((
436 Tcl_Interp *interp, Channel *chanPtr));
437 static int CloseChannel _ANSI_ARGS_((Tcl_Interp *interp,
438 Channel *chanPtr, int errorCode));
439 static void CommonGetsCleanup _ANSI_ARGS_((Channel *chanPtr,
440 Tcl_Encoding encoding));
441 static int CopyAndTranslateBuffer _ANSI_ARGS_((
442 Channel *chanPtr, char *result, int space));
443 static int CopyData _ANSI_ARGS_((CopyState *csPtr, int mask));
444 static void CopyEventProc _ANSI_ARGS_((ClientData clientData,
445 int mask));
446 static void CreateScriptRecord _ANSI_ARGS_((
447 Tcl_Interp *interp, Channel *chanPtr,
448 int mask, Tcl_Obj *scriptPtr));
449 static void DeleteChannelTable _ANSI_ARGS_((
450 ClientData clientData, Tcl_Interp *interp));
451 static void DeleteScriptRecord _ANSI_ARGS_((Tcl_Interp *interp,
452 Channel *chanPtr, int mask));
453 static void DiscardInputQueued _ANSI_ARGS_((
454 Channel *chanPtr, int discardSavedBuffers));
455 static void DiscardOutputQueued _ANSI_ARGS_((
456 Channel *chanPtr));
457 static int DoRead _ANSI_ARGS_((Channel *chanPtr, char *srcPtr,
458 int slen));
459 static int DoWrite _ANSI_ARGS_((Channel *chanPtr, char *src,
460 int srcLen));
461 static int FilterInputBytes _ANSI_ARGS_((Channel *chanPtr,
462 GetsState *statePtr));
463 static int FlushChannel _ANSI_ARGS_((Tcl_Interp *interp,
464 Channel *chanPtr, int calledFromAsyncFlush));
465 static Tcl_HashTable * GetChannelTable _ANSI_ARGS_((Tcl_Interp *interp));
466 static int GetInput _ANSI_ARGS_((Channel *chanPtr));
467 static void PeekAhead _ANSI_ARGS_((Channel *chanPtr,
468 char **dstEndPtr, GetsState *gsPtr));
469 static int ReadBytes _ANSI_ARGS_((Channel *chanPtr,
470 Tcl_Obj *objPtr, int charsLeft, int *offsetPtr));
471 static int ReadChars _ANSI_ARGS_((Channel *chanPtr,
472 Tcl_Obj *objPtr, int charsLeft, int *offsetPtr,
473 int *factorPtr));
474 static void RecycleBuffer _ANSI_ARGS_((Channel *chanPtr,
475 ChannelBuffer *bufPtr, int mustDiscard));
476 static int SetBlockMode _ANSI_ARGS_((Tcl_Interp *interp,
477 Channel *chanPtr, int mode));
478 static void StopCopy _ANSI_ARGS_((CopyState *csPtr));
479 static int TranslateInputEOL _ANSI_ARGS_((Channel *chanPtr,
480 char *dst, CONST char *src, int *dstLenPtr,
481 int *srcLenPtr));
482 static int TranslateOutputEOL _ANSI_ARGS_((Channel *chanPtr,
483 char *dst, CONST char *src, int *dstLenPtr,
484 int *srcLenPtr));
485 static void UpdateInterest _ANSI_ARGS_((Channel *chanPtr));
486 static int WriteBytes _ANSI_ARGS_((Channel *chanPtr,
487 CONST char *src, int srcLen));
488 static 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
509 void
TclInitIOSubsystem()510 TclInitIOSubsystem()
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 */
538 void
TclFinalizeIOSubsystem()539 TclFinalizeIOSubsystem()
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
633 void
Tcl_SetStdChannel(channel,type)634 Tcl_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 */
671 Tcl_Channel
Tcl_GetStdChannel(type)672 Tcl_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
750 void
Tcl_CreateCloseHandler(chan,proc,clientData)751 Tcl_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
791 void
Tcl_DeleteCloseHandler(chan,proc,clientData)792 Tcl_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
839 static Tcl_HashTable *
GetChannelTable(interp)840 GetChannelTable(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
901 static void
DeleteChannelTable(clientData,interp)902 DeleteChannelTable(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
994 static void
CheckForStdChannelsBeingClosed(chan)995 CheckForStdChannelsBeingClosed(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
1040 void
Tcl_RegisterChannel(interp,chan)1041 Tcl_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 <andreas_kupries@users.sourceforge.net>, 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
1102 int
Tcl_UnregisterChannel(interp,chan)1103 Tcl_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
1196 Tcl_Channel
Tcl_GetChannel(interp,chanName,modePtr)1197 Tcl_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
1268 Tcl_Channel
Tcl_CreateChannel(typePtr,chanName,instanceData,mask)1269 Tcl_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 <andreas_kupries@users.sourceforge.net>, 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 <andreas_kupries@users.sourceforge.net>, 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
1414 Tcl_Channel
Tcl_ReplaceChannel(interp,typePtr,instanceData,mask,prevChan)1415 Tcl_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
1571 void
Tcl_UndoReplaceChannel(interp,chan)1572 Tcl_UndoReplaceChannel (interp, chan)
1573 Tcl_Interp* interp; /* The interpreter we are working in */
1574 Tcl_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
1637 int
Tcl_GetChannelMode(chan)1638 Tcl_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
1666 char *
Tcl_GetChannelName(chan)1667 Tcl_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
1692 Tcl_ChannelType *
Tcl_GetChannelType(chan)1693 Tcl_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
1719 int
Tcl_GetChannelHandle(chan,direction,handlePtr)1720 Tcl_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
1754 ClientData
Tcl_GetChannelInstanceData(chan)1755 Tcl_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
1789 static ChannelBuffer *
AllocChannelBuffer(length)1790 AllocChannelBuffer(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
1825 static void
RecycleBuffer(chanPtr,bufPtr,mustDiscard)1826 RecycleBuffer(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
1875 keepit:
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
1897 static void
DiscardOutputQueued(chanPtr)1898 DiscardOutputQueued(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
1929 static int
CheckForDeadChannel(interp,chanPtr)1930 CheckForDeadChannel(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
1967 static int
FlushChannel(interp,chanPtr,calledFromAsyncFlush)1968 FlushChannel(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
2178 static int
CloseChannel(interp,chanPtr,errorCode)2179 CloseChannel(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 <andreas_kupries@users.sourceforge.net>, 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 */
2370 int
Tcl_Close(interp,chan)2371 Tcl_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
2527 int
Tcl_Write(chan,src,srcLen)2528 Tcl_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
2568 int
Tcl_WriteChars(chan,src,len)2569 Tcl_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
2627 int
Tcl_WriteObj(chan,objPtr)2628 Tcl_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
2670 static int
WriteBytes(chanPtr,src,srcLen)2671 WriteBytes(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
2757 static int
WriteChars(chanPtr,src,srcLen)2758 WriteChars(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
2933 static int
TranslateOutputEOL(chanPtr,dst,src,dstLenPtr,srcLenPtr)2934 TranslateOutputEOL(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
3039 static int
CheckFlush(chanPtr,bufPtr,newlineFlag)3040 CheckFlush(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
3091 int
Tcl_Gets(chan,lineRead)3092 Tcl_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
3138 int
Tcl_GetsObj(chan,objPtr)3139 Tcl_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
3465 static int
FilterInputBytes(chanPtr,gsPtr)3466 FilterInputBytes(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
3631 static void
PeekAhead(chanPtr,dstEndPtr,gsPtr)3632 PeekAhead(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
3709 static void
CommonGetsCleanup(chanPtr,encoding)3710 CommonGetsCleanup(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
3779 int
Tcl_Read(chan,dst,bytesToRead)3780 Tcl_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
3817 int
Tcl_ReadChars(chan,objPtr,toRead,appendFlag)3818 Tcl_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
3951 static int
ReadBytes(chanPtr,objPtr,bytesToRead,offsetPtr)3952 ReadBytes(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
4055 static int
ReadChars(chanPtr,objPtr,charsToRead,offsetPtr,factorPtr)4056 ReadChars(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
4262 static int
TranslateInputEOL(chanPtr,dstStart,srcStart,dstLenPtr,srcLenPtr)4263 TranslateInputEOL(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
4433 int
Tcl_Ungets(chan,str,len,atEnd)4434 Tcl_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
4516 int
Tcl_Flush(chan)4517 Tcl_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
4563 static void
DiscardInputQueued(chanPtr,discardSavedBuffers)4564 DiscardInputQueued(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
4610 static int
GetInput(chanPtr)4611 GetInput(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
4713 int
Tcl_Seek(chan,offset,mode)4714 Tcl_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
4894 int
Tcl_Tell(chan)4895 Tcl_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
4992 static int
CheckChannelErrors(chanPtr,direction)4993 CheckChannelErrors(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
5059 int
Tcl_Eof(chan)5060 Tcl_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
5087 int
Tcl_InputBlocked(chan)5088 Tcl_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
5115 int
Tcl_InputBuffered(chan)5116 Tcl_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
5149 void
Tcl_SetChannelBufferSize(chan,sz)5150 Tcl_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
5198 int
Tcl_GetChannelBufferSize(chan)5199 Tcl_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
5237 int
Tcl_BadChannelOption(interp,optionName,optionList)5238 Tcl_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
5299 int
Tcl_GetChannelOption(interp,chan,optionName,dsPtr)5300 Tcl_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
5514 int
Tcl_SetChannelOption(interp,chan,optionName,newValue)5515 Tcl_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
5817 static void
CleanupChannelHandlers(interp,chanPtr)5818 CleanupChannelHandlers(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
5871 void
Tcl_NotifyChannel(channel,mask)5872 Tcl_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
5957 static void
UpdateInterest(chanPtr)5958 UpdateInterest(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
6011 static void
ChannelTimerProc(clientData)6012 ChannelTimerProc(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
6059 void
Tcl_CreateChannelHandler(chan,mask,proc,clientData)6060 Tcl_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
6142 void
Tcl_DeleteChannelHandler(chan,proc,clientData)6143 Tcl_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
6237 static void
DeleteScriptRecord(interp,chanPtr,mask)6238 DeleteScriptRecord(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
6287 static void
CreateScriptRecord(interp,chanPtr,mask,scriptPtr)6288 CreateScriptRecord(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
6341 static void
ChannelEventScriptInvoker(clientData,mask)6342 ChannelEventScriptInvoker(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 */
6405 int
Tcl_FileEventObjCmd(clientData,interp,objc,objv)6406 Tcl_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 */
6503 int
TclTestChannelCmd(clientData,interp,argc,argv)6504 TclTestChannelCmd(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 */
6823 int
TclTestChannelEventCmd(dummy,interp,argc,argv)6824 TclTestChannelEventCmd(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
7037 int
TclCopyChannel(interp,inChan,outChan,toRead,cmdPtr)7038 TclCopyChannel(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
7146 static int
CopyData(csPtr,mask)7147 CopyData(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
7340 static int
DoRead(chanPtr,bufPtr,toRead)7341 DoRead(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
7417 static int
CopyAndTranslateBuffer(chanPtr,result,space)7418 CopyAndTranslateBuffer(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
7658 static int
DoWrite(chanPtr,src,srcLen)7659 DoWrite(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
7812 static void
CopyEventProc(clientData,mask)7813 CopyEventProc(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
7837 static void
StopCopy(csPtr)7838 StopCopy(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
7899 static int
SetBlockMode(interp,chanPtr,mode)7900 SetBlockMode(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