OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [insight/] [tcl/] [generic/] [tclIO.c] - Blame information for rev 1765

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 578 markom
/*
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.1.1 2002-01-16 10:25:27 markom 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 bufSize;                /* 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
 * The following defines the *default* buffer size for channels.
81
 */
82
 
83
#define CHANNELBUFFER_DEFAULT_SIZE      (1024 * 4)
84
 
85
/*
86
 * Structure to record a close callback. One such record exists for
87
 * each close callback registered for a channel.
88
 */
89
 
90
typedef struct CloseCallback {
91
    Tcl_CloseProc *proc;                /* The procedure to call. */
92
    ClientData clientData;              /* Arbitrary one-word data to pass
93
                                         * to the callback. */
94
    struct CloseCallback *nextPtr;      /* For chaining close callbacks. */
95
} CloseCallback;
96
 
97
/*
98
 * The following structure describes the information saved from a call to
99
 * "fileevent". This is used later when the event being waited for to
100
 * invoke the saved script in the interpreter designed in this record.
101
 */
102
 
103
typedef struct EventScriptRecord {
104
    struct Channel *chanPtr;    /* The channel for which this script is
105
                                 * registered. This is used only when an
106
                                 * error occurs during evaluation of the
107
                                 * script, to delete the handler. */
108
    char *script;               /* Script to invoke. */
109
    Tcl_Interp *interp;         /* In what interpreter to invoke script? */
110
    int mask;                   /* Events must overlap current mask for the
111
                                 * stored script to be invoked. */
112
    struct EventScriptRecord *nextPtr;
113
                                /* Next in chain of records. */
114
} EventScriptRecord;
115
 
116
/*
117
 * struct Channel:
118
 *
119
 * One of these structures is allocated for each open channel. It contains data
120
 * specific to the channel but which belongs to the generic part of the Tcl
121
 * channel mechanism, and it points at an instance specific (and type
122
 * specific) * instance data, and at a channel type structure.
123
 */
124
 
125
typedef struct Channel {
126
    char *channelName;          /* The name of the channel instance in Tcl
127
                                 * commands. Storage is owned by the generic IO
128
                                 * code,  is dynamically allocated. */
129
    int flags;                  /* ORed combination of the flags defined
130
                                 * below. */
131
    Tcl_EolTranslation inputTranslation;
132
                                /* What translation to apply for end of line
133
                                 * sequences on input? */
134
    Tcl_EolTranslation outputTranslation;
135
                                /* What translation to use for generating
136
                                 * end of line sequences in output? */
137
    int inEofChar;              /* If nonzero, use this as a signal of EOF
138
                                 * on input. */
139
    int outEofChar;             /* If nonzero, append this to the channel
140
                                 * when it is closed if it is open for
141
                                 * writing. */
142
    int unreportedError;        /* Non-zero if an error report was deferred
143
                                 * because it happened in the background. The
144
                                 * value is the POSIX error code. */
145
    ClientData instanceData;    /* Instance specific data. */
146
    Tcl_ChannelType *typePtr;   /* Pointer to channel type structure. */
147
    int refCount;               /* How many interpreters hold references to
148
                                 * this IO channel? */
149
    CloseCallback *closeCbPtr;  /* Callbacks registered to be called when the
150
                                 * channel is closed. */
151
    ChannelBuffer *curOutPtr;   /* Current output buffer being filled. */
152
    ChannelBuffer *outQueueHead;/* Points at first buffer in output queue. */
153
    ChannelBuffer *outQueueTail;/* Points at last buffer in output queue. */
154
 
155
    ChannelBuffer *saveInBufPtr;/* Buffer saved for input queue - eliminates
156
                                 * need to allocate a new buffer for "gets"
157
                                 * that crosses buffer boundaries. */
158
    ChannelBuffer *inQueueHead; /* Points at first buffer in input queue. */
159
    ChannelBuffer *inQueueTail; /* Points at last buffer in input queue. */
160
 
161
    struct ChannelHandler *chPtr;/* List of channel handlers registered
162
                                  * for this channel. */
163
    int interestMask;           /* Mask of all events this channel has
164
                                 * handlers for. */
165
    struct Channel *nextChanPtr;/* Next in list of channels currently open. */
166
    EventScriptRecord *scriptRecordPtr;
167
                                /* Chain of all scripts registered for
168
                                 * event handlers ("fileevent") on this
169
                                 * channel. */
170
    int bufSize;                /* What size buffers to allocate? */
171
    Tcl_TimerToken timer;       /* Handle to wakeup timer for this channel. */
172
    CopyState *csPtr;           /* State of background copy, or NULL. */
173
} Channel;
174
 
175
/*
176
 * Values for the flags field in Channel. Any ORed combination of the
177
 * following flags can be stored in the field. These flags record various
178
 * options and state bits about the channel. In addition to the flags below,
179
 * the channel can also have TCL_READABLE (1<<1) and TCL_WRITABLE (1<<2) set.
180
 */
181
 
182
#define CHANNEL_NONBLOCKING     (1<<3)  /* Channel is currently in
183
                                         * nonblocking mode. */
184
#define CHANNEL_LINEBUFFERED    (1<<4)  /* Output to the channel must be
185
                                         * flushed after every newline. */
186
#define CHANNEL_UNBUFFERED      (1<<5)  /* Output to the channel must always
187
                                         * be flushed immediately. */
188
#define BUFFER_READY            (1<<6)  /* Current output buffer (the
189
                                         * curOutPtr field in the
190
                                         * channel structure) should be
191
                                         * output as soon as possible even
192
                                         * though it may not be full. */
193
#define BG_FLUSH_SCHEDULED      (1<<7)  /* A background flush of the
194
                                         * queued output buffers has been
195
                                         * scheduled. */
196
#define CHANNEL_CLOSED          (1<<8)  /* Channel has been closed. No
197
                                         * further Tcl-level IO on the
198
                                         * channel is allowed. */
199
#define CHANNEL_EOF             (1<<9)  /* EOF occurred on this channel.
200
                                         * This bit is cleared before every
201
                                         * input operation. */
202
#define CHANNEL_STICKY_EOF      (1<<10) /* EOF occurred on this channel because
203
                                         * we saw the input eofChar. This bit
204
                                         * prevents clearing of the EOF bit
205
                                         * before every input operation. */
206
#define CHANNEL_BLOCKED (1<<11) /* EWOULDBLOCK or EAGAIN occurred
207
                                         * on this channel. This bit is
208
                                         * cleared before every input or
209
                                         * output operation. */
210
#define INPUT_SAW_CR            (1<<12) /* Channel is in CRLF eol input
211
                                         * translation mode and the last
212
                                         * byte seen was a "\r". */
213
#define CHANNEL_DEAD            (1<<13) /* The channel has been closed by
214
                                         * the exit handler (on exit) but
215
                                         * not deallocated. When any IO
216
                                         * operation sees this flag on a
217
                                         * channel, it does not call driver
218
                                         * level functions to avoid referring
219
                                         * to deallocated data. */
220
#define CHANNEL_GETS_BLOCKED    (1<<14) /* The last input operation was a gets
221
                                         * that failed to get a comlete line.
222
                                         * When set, file events will not be
223
                                         * delivered for buffered data unless
224
                                         * an EOL is present. */
225
 
226
/*
227
 * For each channel handler registered in a call to Tcl_CreateChannelHandler,
228
 * there is one record of the following type. All of records for a specific
229
 * channel are chained together in a singly linked list which is stored in
230
 * the channel structure.
231
 */
232
 
233
typedef struct ChannelHandler {
234
    Channel *chanPtr;           /* The channel structure for this channel. */
235
    int mask;                   /* Mask of desired events. */
236
    Tcl_ChannelProc *proc;      /* Procedure to call in the type of
237
                                 * Tcl_CreateChannelHandler. */
238
    ClientData clientData;      /* Argument to pass to procedure. */
239
    struct ChannelHandler *nextPtr;
240
                                /* Next one in list of registered handlers. */
241
} ChannelHandler;
242
 
243
/*
244
 * This structure keeps track of the current ChannelHandler being invoked in
245
 * the current invocation of ChannelHandlerEventProc. There is a potential
246
 * problem if a ChannelHandler is deleted while it is the current one, since
247
 * ChannelHandlerEventProc needs to look at the nextPtr field. To handle this
248
 * problem, structures of the type below indicate the next handler to be
249
 * processed for any (recursively nested) dispatches in progress. The
250
 * nextHandlerPtr field is updated if the handler being pointed to is deleted.
251
 * The nextPtr field is used to chain together all recursive invocations, so
252
 * that Tcl_DeleteChannelHandler can find all the recursively nested
253
 * invocations of ChannelHandlerEventProc and compare the handler being
254
 * deleted against the NEXT handler to be invoked in that invocation; when it
255
 * finds such a situation, Tcl_DeleteChannelHandler updates the nextHandlerPtr
256
 * field of the structure to the next handler.
257
 */
258
 
259
typedef struct NextChannelHandler {
260
    ChannelHandler *nextHandlerPtr;     /* The next handler to be invoked in
261
                                         * this invocation. */
262
    struct NextChannelHandler *nestedHandlerPtr;
263
                                        /* Next nested invocation of
264
                                         * ChannelHandlerEventProc. */
265
} NextChannelHandler;
266
 
267
/*
268
 * This variable holds the list of nested ChannelHandlerEventProc invocations.
269
 */
270
 
271
static NextChannelHandler *nestedHandlerPtr = (NextChannelHandler *) NULL;
272
 
273
/*
274
 * List of all channels currently open.
275
 */
276
 
277
static Channel *firstChanPtr = (Channel *) NULL;
278
 
279
/*
280
 * Has a channel exit handler been created yet?
281
 */
282
 
283
static int channelExitHandlerCreated = 0;
284
 
285
/*
286
 * The following structure describes the event that is added to the Tcl
287
 * event queue by the channel handler check procedure.
288
 */
289
 
290
typedef struct ChannelHandlerEvent {
291
    Tcl_Event header;           /* Standard header for all events. */
292
    Channel *chanPtr;           /* The channel that is ready. */
293
    int readyMask;              /* Events that have occurred. */
294
} ChannelHandlerEvent;
295
 
296
/*
297
 * Static variables to hold channels for stdin, stdout and stderr.
298
 */
299
 
300
static Tcl_Channel stdinChannel = NULL;
301
static int stdinInitialized = 0;
302
static Tcl_Channel stdoutChannel = NULL;
303
static int stdoutInitialized = 0;
304
static Tcl_Channel stderrChannel = NULL;
305
static int stderrInitialized = 0;
306
 
307
/*
308
 * Static functions in this file:
309
 */
310
 
311
static void             ChannelEventScriptInvoker _ANSI_ARGS_((
312
                            ClientData clientData, int flags));
313
static void             ChannelTimerProc _ANSI_ARGS_((
314
                            ClientData clientData));
315
static void             CheckForStdChannelsBeingClosed _ANSI_ARGS_((
316
                            Tcl_Channel chan));
317
static void             CleanupChannelHandlers _ANSI_ARGS_((
318
                            Tcl_Interp *interp, Channel *chanPtr));
319
static int              CloseChannel _ANSI_ARGS_((Tcl_Interp *interp,
320
                            Channel *chanPtr, int errorCode));
321
static void             CloseChannelsOnExit _ANSI_ARGS_((ClientData data));
322
static int              CopyAndTranslateBuffer _ANSI_ARGS_((
323
                            Channel *chanPtr, char *result, int space));
324
static int              CopyData _ANSI_ARGS_((CopyState *csPtr, int mask));
325
static void             CopyEventProc _ANSI_ARGS_((ClientData clientData,
326
                            int mask));
327
static void             CreateScriptRecord _ANSI_ARGS_((
328
                            Tcl_Interp *interp, Channel *chanPtr,
329
                            int mask, char *script));
330
static void             DeleteChannelTable _ANSI_ARGS_((
331
                            ClientData clientData, Tcl_Interp *interp));
332
static void             DeleteScriptRecord _ANSI_ARGS_((Tcl_Interp *interp,
333
                            Channel *chanPtr, int mask));
334
static void             DiscardInputQueued _ANSI_ARGS_((
335
                            Channel *chanPtr, int discardSavedBuffers));
336
static void             DiscardOutputQueued _ANSI_ARGS_((
337
                            Channel *chanPtr));
338
static int              DoRead _ANSI_ARGS_((Channel *chanPtr, char *srcPtr,
339
                            int slen));
340
static int              DoWrite _ANSI_ARGS_((Channel *chanPtr, char *srcPtr,
341
                            int slen));
342
static int              FlushChannel _ANSI_ARGS_((Tcl_Interp *interp,
343
                            Channel *chanPtr, int calledFromAsyncFlush));
344
static Tcl_HashTable    *GetChannelTable _ANSI_ARGS_((Tcl_Interp *interp));
345
static int              GetEOL _ANSI_ARGS_((Channel *chanPtr));
346
static int              GetInput _ANSI_ARGS_((Channel *chanPtr));
347
static void             RecycleBuffer _ANSI_ARGS_((Channel *chanPtr,
348
                            ChannelBuffer *bufPtr, int mustDiscard));
349
static int              ScanBufferForEOL _ANSI_ARGS_((Channel *chanPtr,
350
                            ChannelBuffer *bufPtr,
351
                            Tcl_EolTranslation translation, int eofChar,
352
                            int *bytesToEOLPtr, int *crSeenPtr));
353
static int              ScanInputForEOL _ANSI_ARGS_((Channel *chanPtr,
354
                            int *bytesQueuedPtr));
355
static int              SetBlockMode _ANSI_ARGS_((Tcl_Interp *interp,
356
                            Channel *chanPtr, int mode));
357
static void             StopCopy _ANSI_ARGS_((CopyState *csPtr));
358
static void             UpdateInterest _ANSI_ARGS_((Channel *chanPtr));
359
static int              CheckForDeadChannel _ANSI_ARGS_((Tcl_Interp *interp,
360
                                                        Channel *chan));
361
 
362
/*
363
 *----------------------------------------------------------------------
364
 *
365
 * SetBlockMode --
366
 *
367
 *      This function sets the blocking mode for a channel and updates
368
 *      the state flags.
369
 *
370
 * Results:
371
 *      A standard Tcl result.
372
 *
373
 * Side effects:
374
 *      Modifies the blocking mode of the channel and possibly generates
375
 *      an error.
376
 *
377
 *----------------------------------------------------------------------
378
 */
379
 
380
static int
381
SetBlockMode(interp, chanPtr, mode)
382
    Tcl_Interp *interp;         /* Interp for error reporting. */
383
    Channel *chanPtr;           /* Channel to modify. */
384
    int mode;                   /* One of TCL_MODE_BLOCKING or
385
                                 * TCL_MODE_NONBLOCKING. */
386
{
387
    int result = 0;
388
    if (chanPtr->typePtr->blockModeProc != NULL) {
389
        result = (chanPtr->typePtr->blockModeProc) (chanPtr->instanceData,
390
                mode);
391
    }
392
    if (result != 0) {
393
        Tcl_SetErrno(result);
394
        if (interp != (Tcl_Interp *) NULL) {
395
            Tcl_AppendResult(interp, "error setting blocking mode: ",
396
                    Tcl_PosixError(interp), (char *) NULL);
397
        }
398
        return TCL_ERROR;
399
    }
400
    if (mode == TCL_MODE_BLOCKING) {
401
        chanPtr->flags &= (~(CHANNEL_NONBLOCKING | BG_FLUSH_SCHEDULED));
402
    } else {
403
        chanPtr->flags |= CHANNEL_NONBLOCKING;
404
    }
405
    return TCL_OK;
406
}
407
 
408
/*
409
 *----------------------------------------------------------------------
410
 *
411
 * Tcl_SetStdChannel --
412
 *
413
 *      This function is used to change the channels that are used
414
 *      for stdin/stdout/stderr in new interpreters.
415
 *
416
 * Results:
417
 *      None
418
 *
419
 * Side effects:
420
 *      None.
421
 *
422
 *----------------------------------------------------------------------
423
 */
424
 
425
void
426
Tcl_SetStdChannel(channel, type)
427
    Tcl_Channel channel;
428
    int type;                   /* One of TCL_STDIN, TCL_STDOUT, TCL_STDERR. */
429
{
430
    switch (type) {
431
        case TCL_STDIN:
432
            stdinInitialized = 1;
433
            stdinChannel = channel;
434
            break;
435
        case TCL_STDOUT:
436
            stdoutInitialized = 1;
437
            stdoutChannel = channel;
438
            break;
439
        case TCL_STDERR:
440
            stderrInitialized = 1;
441
            stderrChannel = channel;
442
            break;
443
    }
444
}
445
 
446
/*
447
 *----------------------------------------------------------------------
448
 *
449
 * Tcl_GetStdChannel --
450
 *
451
 *      Returns the specified standard channel.
452
 *
453
 * Results:
454
 *      Returns the specified standard channel, or NULL.
455
 *
456
 * Side effects:
457
 *      May cause the creation of a standard channel and the underlying
458
 *      file.
459
 *
460
 *----------------------------------------------------------------------
461
 */
462
 
463
Tcl_Channel
464
Tcl_GetStdChannel(type)
465
    int type;                   /* One of TCL_STDIN, TCL_STDOUT, TCL_STDERR. */
466
{
467
    Tcl_Channel channel = NULL;
468
 
469
    /*
470
     * If the channels were not created yet, create them now and
471
     * store them in the static variables.  Note that we need to set
472
     * stdinInitialized before calling TclGetDefaultStdChannel in order
473
     * to avoid recursive loops when TclGetDefaultStdChannel calls
474
     * Tcl_CreateChannel.
475
     */
476
 
477
    switch (type) {
478
        case TCL_STDIN:
479
            if (!stdinInitialized) {
480
                stdinChannel = TclGetDefaultStdChannel(TCL_STDIN);
481
                stdinInitialized = 1;
482
 
483
                /*
484
                 * Artificially bump the refcount to ensure that the channel
485
                 * is only closed on exit.
486
                 *
487
                 * NOTE: Must only do this if stdinChannel is not NULL. It
488
                 * can be NULL in situations where Tcl is unable to connect
489
                 * to the standard input.
490
                 */
491
 
492
                if (stdinChannel != (Tcl_Channel) NULL) {
493
                    (void) Tcl_RegisterChannel((Tcl_Interp *) NULL,
494
                            stdinChannel);
495
                }
496
            }
497
            channel = stdinChannel;
498
            break;
499
        case TCL_STDOUT:
500
            if (!stdoutInitialized) {
501
                stdoutChannel = TclGetDefaultStdChannel(TCL_STDOUT);
502
                stdoutInitialized = 1;
503
 
504
                /*
505
                 * Artificially bump the refcount to ensure that the channel
506
                 * is only closed on exit.
507
                 *
508
                 * NOTE: Must only do this if stdoutChannel is not NULL. It
509
                 * can be NULL in situations where Tcl is unable to connect
510
                 * to the standard output.
511
                 */
512
 
513
                if (stdoutChannel != (Tcl_Channel) NULL) {
514
                    (void) Tcl_RegisterChannel((Tcl_Interp *) NULL,
515
                            stdoutChannel);
516
                }
517
            }
518
            channel = stdoutChannel;
519
            break;
520
        case TCL_STDERR:
521
            if (!stderrInitialized) {
522
                stderrChannel = TclGetDefaultStdChannel(TCL_STDERR);
523
                stderrInitialized = 1;
524
 
525
                /*
526
                 * Artificially bump the refcount to ensure that the channel
527
                 * is only closed on exit.
528
                 *
529
                 * NOTE: Must only do this if stderrChannel is not NULL. It
530
                 * can be NULL in situations where Tcl is unable to connect
531
                 * to the standard error.
532
                 */
533
 
534
                if (stderrChannel != (Tcl_Channel) NULL) {
535
                    (void) Tcl_RegisterChannel((Tcl_Interp *) NULL,
536
                            stderrChannel);
537
                }
538
            }
539
            channel = stderrChannel;
540
            break;
541
    }
542
    return channel;
543
}
544
 
545
/*
546
 *----------------------------------------------------------------------
547
 *
548
 * Tcl_CreateCloseHandler
549
 *
550
 *      Creates a close callback which will be called when the channel is
551
 *      closed.
552
 *
553
 * Results:
554
 *      None.
555
 *
556
 * Side effects:
557
 *      Causes the callback to be called in the future when the channel
558
 *      will be closed.
559
 *
560
 *----------------------------------------------------------------------
561
 */
562
 
563
void
564
Tcl_CreateCloseHandler(chan, proc, clientData)
565
    Tcl_Channel chan;           /* The channel for which to create the
566
                                 * close callback. */
567
    Tcl_CloseProc *proc;        /* The callback routine to call when the
568
                                 * channel will be closed. */
569
    ClientData clientData;      /* Arbitrary data to pass to the
570
                                 * close callback. */
571
{
572
    Channel *chanPtr;
573
    CloseCallback *cbPtr;
574
 
575
    chanPtr = (Channel *) chan;
576
 
577
    cbPtr = (CloseCallback *) ckalloc((unsigned) sizeof(CloseCallback));
578
    cbPtr->proc = proc;
579
    cbPtr->clientData = clientData;
580
 
581
    cbPtr->nextPtr = chanPtr->closeCbPtr;
582
    chanPtr->closeCbPtr = cbPtr;
583
}
584
 
585
/*
586
 *----------------------------------------------------------------------
587
 *
588
 * Tcl_DeleteCloseHandler --
589
 *
590
 *      Removes a callback that would have been called on closing
591
 *      the channel. If there is no matching callback then this
592
 *      function has no effect.
593
 *
594
 * Results:
595
 *      None.
596
 *
597
 * Side effects:
598
 *      The callback will not be called in the future when the channel
599
 *      is eventually closed.
600
 *
601
 *----------------------------------------------------------------------
602
 */
603
 
604
void
605
Tcl_DeleteCloseHandler(chan, proc, clientData)
606
    Tcl_Channel chan;           /* The channel for which to cancel the
607
                                 * close callback. */
608
    Tcl_CloseProc *proc;        /* The procedure for the callback to
609
                                 * remove. */
610
    ClientData clientData;      /* The callback data for the callback
611
                                 * to remove. */
612
{
613
    Channel *chanPtr;
614
    CloseCallback *cbPtr, *cbPrevPtr;
615
 
616
    chanPtr = (Channel *) chan;
617
    for (cbPtr = chanPtr->closeCbPtr, cbPrevPtr = (CloseCallback *) NULL;
618
             cbPtr != (CloseCallback *) NULL;
619
             cbPtr = cbPtr->nextPtr) {
620
        if ((cbPtr->proc == proc) && (cbPtr->clientData == clientData)) {
621
            if (cbPrevPtr == (CloseCallback *) NULL) {
622
                chanPtr->closeCbPtr = cbPtr->nextPtr;
623
            }
624
            ckfree((char *) cbPtr);
625
            break;
626
        } else {
627
            cbPrevPtr = cbPtr;
628
        }
629
    }
630
}
631
 
632
/*
633
 *----------------------------------------------------------------------
634
 *
635
 * CloseChannelsOnExit --
636
 *
637
 *      Closes all the existing channels, on exit. This routine is called
638
 *      during exit processing.
639
 *
640
 * Results:
641
 *      None.
642
 *
643
 * Side effects:
644
 *      Closes all channels.
645
 *
646
 *----------------------------------------------------------------------
647
 */
648
 
649
        /* ARGSUSED */
650
static void
651
CloseChannelsOnExit(clientData)
652
    ClientData clientData;              /* NULL - unused. */
653
{
654
    Channel *chanPtr;                   /* Iterates over open channels. */
655
    Channel *nextChanPtr;               /* Iterates over open channels. */
656
 
657
 
658
    for (chanPtr = firstChanPtr; chanPtr != (Channel *) NULL;
659
             chanPtr = nextChanPtr) {
660
        nextChanPtr = chanPtr->nextChanPtr;
661
 
662
        /*
663
         * Set the channel back into blocking mode to ensure that we wait
664
         * for all data to flush out.
665
         */
666
 
667
        (void) Tcl_SetChannelOption(NULL, (Tcl_Channel) chanPtr,
668
                "-blocking", "on");
669
 
670
        if ((chanPtr == (Channel *) stdinChannel) ||
671
                (chanPtr == (Channel *) stdoutChannel) ||
672
                (chanPtr == (Channel *) stderrChannel)) {
673
 
674
            /*
675
             * Decrement the refcount which was earlier artificially bumped
676
             * up to keep the channel from being closed.
677
             */
678
 
679
            chanPtr->refCount--;
680
        }
681
 
682
        if (chanPtr->refCount <= 0) {
683
 
684
            /*
685
             * Close it only if the refcount indicates that the channel is not
686
             * referenced from any interpreter. If it is, that interpreter will
687
             * close the channel when it gets destroyed.
688
             */
689
 
690
            (void) Tcl_Close((Tcl_Interp *) NULL, (Tcl_Channel) chanPtr);
691
 
692
        } else {
693
 
694
            /*
695
             * The refcount is greater than zero, so flush the channel.
696
             */
697
 
698
            Tcl_Flush((Tcl_Channel) chanPtr);
699
 
700
            /*
701
             * Call the device driver to actually close the underlying
702
             * device for this channel.
703
             */
704
 
705
            (chanPtr->typePtr->closeProc) (chanPtr->instanceData,
706
                    (Tcl_Interp *) NULL);
707
 
708
            /*
709
             * Finally, we clean up the fields in the channel data structure
710
             * since all of them have been deleted already. We mark the
711
             * channel with CHANNEL_DEAD to prevent any further IO operations
712
             * on it.
713
             */
714
 
715
            chanPtr->instanceData = (ClientData) NULL;
716
            chanPtr->flags |= CHANNEL_DEAD;
717
        }
718
    }
719
 
720
    /*
721
     * Reinitialize all the variables to the initial state:
722
     */
723
 
724
    firstChanPtr = (Channel *) NULL;
725
    nestedHandlerPtr = (NextChannelHandler *) NULL;
726
    channelExitHandlerCreated = 0;
727
    stdinChannel = NULL;
728
    stdinInitialized = 0;
729
    stdoutChannel = NULL;
730
    stdoutInitialized = 0;
731
    stderrChannel = NULL;
732
    stderrInitialized = 0;
733
}
734
 
735
/*
736
 *----------------------------------------------------------------------
737
 *
738
 * GetChannelTable --
739
 *
740
 *      Gets and potentially initializes the channel table for an
741
 *      interpreter. If it is initializing the table it also inserts
742
 *      channels for stdin, stdout and stderr if the interpreter is
743
 *      trusted.
744
 *
745
 * Results:
746
 *      A pointer to the hash table created, for use by the caller.
747
 *
748
 * Side effects:
749
 *      Initializes the channel table for an interpreter. May create
750
 *      channels for stdin, stdout and stderr.
751
 *
752
 *----------------------------------------------------------------------
753
 */
754
 
755
static Tcl_HashTable *
756
GetChannelTable(interp)
757
    Tcl_Interp *interp;
758
{
759
    Tcl_HashTable *hTblPtr;     /* Hash table of channels. */
760
    Tcl_Channel stdinChan, stdoutChan, stderrChan;
761
 
762
    hTblPtr = (Tcl_HashTable *) Tcl_GetAssocData(interp, "tclIO", NULL);
763
    if (hTblPtr == (Tcl_HashTable *) NULL) {
764
        hTblPtr = (Tcl_HashTable *) ckalloc((unsigned) sizeof(Tcl_HashTable));
765
        Tcl_InitHashTable(hTblPtr, TCL_STRING_KEYS);
766
 
767
        (void) Tcl_SetAssocData(interp, "tclIO",
768
                (Tcl_InterpDeleteProc *) DeleteChannelTable,
769
                (ClientData) hTblPtr);
770
 
771
        /*
772
         * If the interpreter is trusted (not "safe"), insert channels
773
         * for stdin, stdout and stderr (possibly creating them in the
774
         * process).
775
         */
776
 
777
        if (Tcl_IsSafe(interp) == 0) {
778
            stdinChan = Tcl_GetStdChannel(TCL_STDIN);
779
            if (stdinChan != NULL) {
780
                Tcl_RegisterChannel(interp, stdinChan);
781
            }
782
            stdoutChan = Tcl_GetStdChannel(TCL_STDOUT);
783
            if (stdoutChan != NULL) {
784
                Tcl_RegisterChannel(interp, stdoutChan);
785
            }
786
            stderrChan = Tcl_GetStdChannel(TCL_STDERR);
787
            if (stderrChan != NULL) {
788
                Tcl_RegisterChannel(interp, stderrChan);
789
            }
790
        }
791
 
792
    }
793
    return hTblPtr;
794
}
795
 
796
/*
797
 *----------------------------------------------------------------------
798
 *
799
 * DeleteChannelTable --
800
 *
801
 *      Deletes the channel table for an interpreter, closing any open
802
 *      channels whose refcount reaches zero. This procedure is invoked
803
 *      when an interpreter is deleted, via the AssocData cleanup
804
 *      mechanism.
805
 *
806
 * Results:
807
 *      None.
808
 *
809
 * Side effects:
810
 *      Deletes the hash table of channels. May close channels. May flush
811
 *      output on closed channels. Removes any channeEvent handlers that were
812
 *      registered in this interpreter.
813
 *
814
 *----------------------------------------------------------------------
815
 */
816
 
817
static void
818
DeleteChannelTable(clientData, interp)
819
    ClientData clientData;      /* The per-interpreter data structure. */
820
    Tcl_Interp *interp;         /* The interpreter being deleted. */
821
{
822
    Tcl_HashTable *hTblPtr;     /* The hash table. */
823
    Tcl_HashSearch hSearch;     /* Search variable. */
824
    Tcl_HashEntry *hPtr;        /* Search variable. */
825
    Channel *chanPtr;   /* Channel being deleted. */
826
    EventScriptRecord *sPtr, *prevPtr, *nextPtr;
827
                                /* Variables to loop over all channel events
828
                                 * registered, to delete the ones that refer
829
                                 * to the interpreter being deleted. */
830
 
831
    /*
832
     * Delete all the registered channels - this will close channels whose
833
     * refcount reaches zero.
834
     */
835
 
836
    hTblPtr = (Tcl_HashTable *) clientData;
837
    for (hPtr = Tcl_FirstHashEntry(hTblPtr, &hSearch);
838
             hPtr != (Tcl_HashEntry *) NULL;
839
             hPtr = Tcl_FirstHashEntry(hTblPtr, &hSearch)) {
840
 
841
        chanPtr = (Channel *) Tcl_GetHashValue(hPtr);
842
 
843
        /*
844
         * Remove any fileevents registered in this interpreter.
845
         */
846
 
847
        for (sPtr = chanPtr->scriptRecordPtr,
848
                 prevPtr = (EventScriptRecord *) NULL;
849
                 sPtr != (EventScriptRecord *) NULL;
850
                 sPtr = nextPtr) {
851
            nextPtr = sPtr->nextPtr;
852
            if (sPtr->interp == interp) {
853
                if (prevPtr == (EventScriptRecord *) NULL) {
854
                    chanPtr->scriptRecordPtr = nextPtr;
855
                } else {
856
                    prevPtr->nextPtr = nextPtr;
857
                }
858
 
859
                Tcl_DeleteChannelHandler((Tcl_Channel) chanPtr,
860
                        ChannelEventScriptInvoker, (ClientData) sPtr);
861
 
862
                ckfree(sPtr->script);
863
                ckfree((char *) sPtr);
864
            } else {
865
                prevPtr = sPtr;
866
            }
867
        }
868
 
869
        /*
870
         * Cannot call Tcl_UnregisterChannel because that procedure calls
871
         * Tcl_GetAssocData to get the channel table, which might already
872
         * be inaccessible from the interpreter structure. Instead, we
873
         * emulate the behavior of Tcl_UnregisterChannel directly here.
874
         */
875
 
876
        Tcl_DeleteHashEntry(hPtr);
877
        chanPtr->refCount--;
878
        if (chanPtr->refCount <= 0) {
879
            if (!(chanPtr->flags & BG_FLUSH_SCHEDULED)) {
880
                (void) Tcl_Close(interp, (Tcl_Channel) chanPtr);
881
            }
882
        }
883
    }
884
    Tcl_DeleteHashTable(hTblPtr);
885
    ckfree((char *) hTblPtr);
886
}
887
 
888
/*
889
 *----------------------------------------------------------------------
890
 *
891
 * CheckForStdChannelsBeingClosed --
892
 *
893
 *      Perform special handling for standard channels being closed. When
894
 *      given a standard channel, if the refcount is now 1, it means that
895
 *      the last reference to the standard channel is being explicitly
896
 *      closed. Now bump the refcount artificially down to 0, to ensure the
897
 *      normal handling of channels being closed will occur. Also reset the
898
 *      static pointer to the channel to NULL, to avoid dangling references.
899
 *
900
 * Results:
901
 *      None.
902
 *
903
 * Side effects:
904
 *      Manipulates the refcount on standard channels. May smash the global
905
 *      static pointer to a standard channel.
906
 *
907
 *----------------------------------------------------------------------
908
 */
909
 
910
static void
911
CheckForStdChannelsBeingClosed(chan)
912
    Tcl_Channel chan;
913
{
914
    Channel *chanPtr = (Channel *) chan;
915
 
916
    if ((chan == stdinChannel) && (stdinInitialized)) {
917
        if (chanPtr->refCount < 2) {
918
            chanPtr->refCount = 0;
919
            stdinChannel = NULL;
920
            return;
921
        }
922
    } else if ((chan == stdoutChannel) && (stdoutInitialized)) {
923
        if (chanPtr->refCount < 2) {
924
            chanPtr->refCount = 0;
925
            stdoutChannel = NULL;
926
            return;
927
        }
928
    } else if ((chan == stderrChannel) && (stderrInitialized)) {
929
        if (chanPtr->refCount < 2) {
930
            chanPtr->refCount = 0;
931
            stderrChannel = NULL;
932
            return;
933
        }
934
    }
935
}
936
 
937
/*
938
 *----------------------------------------------------------------------
939
 *
940
 * Tcl_UnregisterChannel --
941
 *
942
 *      Deletes the hash entry for a channel associated with an interpreter.
943
 *      If the interpreter given as argument is NULL, it only decrements the
944
 *      reference count.
945
 *
946
 * Results:
947
 *      A standard Tcl result.
948
 *
949
 * Side effects:
950
 *      Deletes the hash entry for a channel associated with an interpreter.
951
 *
952
 *----------------------------------------------------------------------
953
 */
954
 
955
int
956
Tcl_UnregisterChannel(interp, chan)
957
    Tcl_Interp *interp;         /* Interpreter in which channel is defined. */
958
    Tcl_Channel chan;           /* Channel to delete. */
959
{
960
    Tcl_HashTable *hTblPtr;     /* Hash table of channels. */
961
    Tcl_HashEntry *hPtr;        /* Search variable. */
962
    Channel *chanPtr;           /* The real IO channel. */
963
 
964
    chanPtr = (Channel *) chan;
965
 
966
    if (interp != (Tcl_Interp *) NULL) {
967
        hTblPtr = (Tcl_HashTable *) Tcl_GetAssocData(interp, "tclIO", NULL);
968
        if (hTblPtr == (Tcl_HashTable *) NULL) {
969
            return TCL_OK;
970
        }
971
        hPtr = Tcl_FindHashEntry(hTblPtr, chanPtr->channelName);
972
        if (hPtr == (Tcl_HashEntry *) NULL) {
973
            return TCL_OK;
974
        }
975
        if ((Channel *) Tcl_GetHashValue(hPtr) != chanPtr) {
976
            return TCL_OK;
977
        }
978
        Tcl_DeleteHashEntry(hPtr);
979
 
980
        /*
981
         * Remove channel handlers that refer to this interpreter, so that they
982
         * will not be present if the actual close is delayed and more events
983
         * happen on the channel. This may occur if the channel is shared
984
         * between several interpreters, or if the channel has async
985
         * flushing active.
986
         */
987
 
988
        CleanupChannelHandlers(interp, chanPtr);
989
    }
990
 
991
    chanPtr->refCount--;
992
 
993
    /*
994
     * Perform special handling for standard channels being closed. If the
995
     * refCount is now 1 it means that the last reference to the standard
996
     * channel is being explicitly closed, so bump the refCount down
997
     * artificially to 0. This will ensure that the channel is actually
998
     * closed, below. Also set the static pointer to NULL for the channel.
999
     */
1000
 
1001
    CheckForStdChannelsBeingClosed(chan);
1002
 
1003
    /*
1004
     * If the refCount reached zero, close the actual channel.
1005
     */
1006
 
1007
    if (chanPtr->refCount <= 0) {
1008
 
1009
        /*
1010
         * Ensure that if there is another buffer, it gets flushed
1011
         * whether or not we are doing a background flush.
1012
         */
1013
 
1014
        if ((chanPtr->curOutPtr != NULL) &&
1015
                (chanPtr->curOutPtr->nextAdded >
1016
                        chanPtr->curOutPtr->nextRemoved)) {
1017
            chanPtr->flags |= BUFFER_READY;
1018
        }
1019
        chanPtr->flags |= CHANNEL_CLOSED;
1020
        if (!(chanPtr->flags & BG_FLUSH_SCHEDULED)) {
1021
            if (Tcl_Close(interp, chan) != TCL_OK) {
1022
                return TCL_ERROR;
1023
            }
1024
        }
1025
    }
1026
    return TCL_OK;
1027
}
1028
 
1029
/*
1030
 *----------------------------------------------------------------------
1031
 *
1032
 * Tcl_RegisterChannel --
1033
 *
1034
 *      Adds an already-open channel to the channel table of an interpreter.
1035
 *      If the interpreter passed as argument is NULL, it only increments
1036
 *      the channel refCount.
1037
 *
1038
 * Results:
1039
 *      None.
1040
 *
1041
 * Side effects:
1042
 *      May increment the reference count of a channel.
1043
 *
1044
 *----------------------------------------------------------------------
1045
 */
1046
 
1047
void
1048
Tcl_RegisterChannel(interp, chan)
1049
    Tcl_Interp *interp;         /* Interpreter in which to add the channel. */
1050
    Tcl_Channel chan;           /* The channel to add to this interpreter
1051
                                 * channel table. */
1052
{
1053
    Tcl_HashTable *hTblPtr;     /* Hash table of channels. */
1054
    Tcl_HashEntry *hPtr;        /* Search variable. */
1055
    int new;                    /* Is the hash entry new or does it exist? */
1056
    Channel *chanPtr;           /* The actual channel. */
1057
 
1058
    chanPtr = (Channel *) chan;
1059
 
1060
    if (chanPtr->channelName == (char *) NULL) {
1061
        panic("Tcl_RegisterChannel: channel without name");
1062
    }
1063
    if (interp != (Tcl_Interp *) NULL) {
1064
        hTblPtr = GetChannelTable(interp);
1065
        hPtr = Tcl_CreateHashEntry(hTblPtr, chanPtr->channelName, &new);
1066
        if (new == 0) {
1067
            if (chan == (Tcl_Channel) Tcl_GetHashValue(hPtr)) {
1068
                return;
1069
            }
1070
            panic("Tcl_RegisterChannel: duplicate channel names");
1071
        }
1072
        Tcl_SetHashValue(hPtr, (ClientData) chanPtr);
1073
    }
1074
    chanPtr->refCount++;
1075
}
1076
 
1077
/*
1078
 *----------------------------------------------------------------------
1079
 *
1080
 * Tcl_GetChannel --
1081
 *
1082
 *      Finds an existing Tcl_Channel structure by name in a given
1083
 *      interpreter. This function is public because it is used by
1084
 *      channel-type-specific functions.
1085
 *
1086
 * Results:
1087
 *      A Tcl_Channel or NULL on failure. If failed, interp->result
1088
 *      contains an error message. It also returns, in modePtr, the
1089
 *      modes in which the channel is opened.
1090
 *
1091
 * Side effects:
1092
 *      None.
1093
 *
1094
 *----------------------------------------------------------------------
1095
 */
1096
 
1097
Tcl_Channel
1098
Tcl_GetChannel(interp, chanName, modePtr)
1099
    Tcl_Interp *interp;         /* Interpreter in which to find or create
1100
                                 * the channel. */
1101
    char *chanName;             /* The name of the channel. */
1102
    int *modePtr;               /* Where to store the mode in which the
1103
                                 * channel was opened? Will contain an ORed
1104
                                 * combination of TCL_READABLE and
1105
                                 * TCL_WRITABLE, if non-NULL. */
1106
{
1107
    Channel *chanPtr;           /* The actual channel. */
1108
    Tcl_HashTable *hTblPtr;     /* Hash table of channels. */
1109
    Tcl_HashEntry *hPtr;        /* Search variable. */
1110
    char *name;                 /* Translated name. */
1111
 
1112
    /*
1113
     * Substitute "stdin", etc.  Note that even though we immediately
1114
     * find the channel using Tcl_GetStdChannel, we still need to look
1115
     * it up in the specified interpreter to ensure that it is present
1116
     * in the channel table.  Otherwise, safe interpreters would always
1117
     * have access to the standard channels.
1118
     */
1119
 
1120
    name = chanName;
1121
    if ((chanName[0] == 's') && (chanName[1] == 't')) {
1122
        chanPtr = NULL;
1123
        if (strcmp(chanName, "stdin") == 0) {
1124
            chanPtr = (Channel *)Tcl_GetStdChannel(TCL_STDIN);
1125
        } else if (strcmp(chanName, "stdout") == 0) {
1126
            chanPtr = (Channel *)Tcl_GetStdChannel(TCL_STDOUT);
1127
        } else if (strcmp(chanName, "stderr") == 0) {
1128
            chanPtr = (Channel *)Tcl_GetStdChannel(TCL_STDERR);
1129
        }
1130
        if (chanPtr != NULL) {
1131
            name = chanPtr->channelName;
1132
        }
1133
    }
1134
 
1135
    hTblPtr = GetChannelTable(interp);
1136
    hPtr = Tcl_FindHashEntry(hTblPtr, name);
1137
    if (hPtr == (Tcl_HashEntry *) NULL) {
1138
        Tcl_AppendResult(interp, "can not find channel named \"",
1139
                chanName, "\"", (char *) NULL);
1140
        return NULL;
1141
    }
1142
 
1143
    chanPtr = (Channel *) Tcl_GetHashValue(hPtr);
1144
    if (modePtr != NULL) {
1145
        *modePtr = (chanPtr->flags & (TCL_READABLE|TCL_WRITABLE));
1146
    }
1147
 
1148
    return (Tcl_Channel) chanPtr;
1149
}
1150
 
1151
/*
1152
 *----------------------------------------------------------------------
1153
 *
1154
 * Tcl_CreateChannel --
1155
 *
1156
 *      Creates a new entry in the hash table for a Tcl_Channel
1157
 *      record.
1158
 *
1159
 * Results:
1160
 *      Returns the new Tcl_Channel.
1161
 *
1162
 * Side effects:
1163
 *      Creates a new Tcl_Channel instance and inserts it into the
1164
 *      hash table.
1165
 *
1166
 *----------------------------------------------------------------------
1167
 */
1168
 
1169
Tcl_Channel
1170
Tcl_CreateChannel(typePtr, chanName, instanceData, mask)
1171
    Tcl_ChannelType *typePtr;   /* The channel type record. */
1172
    char *chanName;             /* Name of channel to record. */
1173
    ClientData instanceData;    /* Instance specific data. */
1174
    int mask;                   /* TCL_READABLE & TCL_WRITABLE to indicate
1175
                                 * if the channel is readable, writable. */
1176
{
1177
    Channel *chanPtr;           /* The channel structure newly created. */
1178
 
1179
    chanPtr = (Channel *) ckalloc((unsigned) sizeof(Channel));
1180
 
1181
    if (chanName != (char *) NULL) {
1182
        chanPtr->channelName = ckalloc((unsigned) (strlen(chanName) + 1));
1183
        strcpy(chanPtr->channelName, chanName);
1184
    } else {
1185
        panic("Tcl_CreateChannel: NULL channel name");
1186
    }
1187
 
1188
    chanPtr->flags = mask;
1189
 
1190
    /*
1191
     * Set the channel up initially in AUTO input translation mode to
1192
     * accept "\n", "\r" and "\r\n". Output translation mode is set to
1193
     * a platform specific default value. The eofChar is set to 0 for both
1194
     * input and output, so that Tcl does not look for an in-file EOF
1195
     * indicator (e.g. ^Z) and does not append an EOF indicator to files.
1196
     */
1197
 
1198
    chanPtr->inputTranslation = TCL_TRANSLATE_AUTO;
1199
    chanPtr->outputTranslation = TCL_PLATFORM_TRANSLATION;
1200
    chanPtr->inEofChar = 0;
1201
    chanPtr->outEofChar = 0;
1202
 
1203
    chanPtr->unreportedError = 0;
1204
    chanPtr->instanceData = instanceData;
1205
    chanPtr->typePtr = typePtr;
1206
    chanPtr->refCount = 0;
1207
    chanPtr->closeCbPtr = (CloseCallback *) NULL;
1208
    chanPtr->curOutPtr = (ChannelBuffer *) NULL;
1209
    chanPtr->outQueueHead = (ChannelBuffer *) NULL;
1210
    chanPtr->outQueueTail = (ChannelBuffer *) NULL;
1211
    chanPtr->saveInBufPtr = (ChannelBuffer *) NULL;
1212
    chanPtr->inQueueHead = (ChannelBuffer *) NULL;
1213
    chanPtr->inQueueTail = (ChannelBuffer *) NULL;
1214
    chanPtr->chPtr = (ChannelHandler *) NULL;
1215
    chanPtr->interestMask = 0;
1216
    chanPtr->scriptRecordPtr = (EventScriptRecord *) NULL;
1217
    chanPtr->bufSize = CHANNELBUFFER_DEFAULT_SIZE;
1218
    chanPtr->timer = NULL;
1219
    chanPtr->csPtr = NULL;
1220
 
1221
    /*
1222
     * Link the channel into the list of all channels; create an on-exit
1223
     * handler if there is not one already, to close off all the channels
1224
     * in the list on exit.
1225
     */
1226
 
1227
    chanPtr->nextChanPtr = firstChanPtr;
1228
    firstChanPtr = chanPtr;
1229
 
1230
    if (!channelExitHandlerCreated) {
1231
        channelExitHandlerCreated = 1;
1232
        Tcl_CreateExitHandler(CloseChannelsOnExit, (ClientData) NULL);
1233
    }
1234
 
1235
    /*
1236
     * Install this channel in the first empty standard channel slot, if
1237
     * the channel was previously closed explicitly.
1238
     */
1239
 
1240
    if ((stdinChannel == NULL) && (stdinInitialized == 1)) {
1241
        Tcl_SetStdChannel((Tcl_Channel)chanPtr, TCL_STDIN);
1242
        Tcl_RegisterChannel((Tcl_Interp *) NULL, (Tcl_Channel) chanPtr);
1243
    } else if ((stdoutChannel == NULL) && (stdoutInitialized == 1)) {
1244
        Tcl_SetStdChannel((Tcl_Channel)chanPtr, TCL_STDOUT);
1245
        Tcl_RegisterChannel((Tcl_Interp *) NULL, (Tcl_Channel) chanPtr);
1246
    } else if ((stderrChannel == NULL) && (stderrInitialized == 1)) {
1247
        Tcl_SetStdChannel((Tcl_Channel)chanPtr, TCL_STDERR);
1248
        Tcl_RegisterChannel((Tcl_Interp *) NULL, (Tcl_Channel) chanPtr);
1249
    }
1250
    return (Tcl_Channel) chanPtr;
1251
}
1252
 
1253
/*
1254
 *----------------------------------------------------------------------
1255
 *
1256
 * Tcl_GetChannelMode --
1257
 *
1258
 *      Computes a mask indicating whether the channel is open for
1259
 *      reading and writing.
1260
 *
1261
 * Results:
1262
 *      An OR-ed combination of TCL_READABLE and TCL_WRITABLE.
1263
 *
1264
 * Side effects:
1265
 *      None.
1266
 *
1267
 *----------------------------------------------------------------------
1268
 */
1269
 
1270
int
1271
Tcl_GetChannelMode(chan)
1272
    Tcl_Channel chan;           /* The channel for which the mode is
1273
                                 * being computed. */
1274
{
1275
    Channel *chanPtr;           /* The actual channel. */
1276
 
1277
    chanPtr = (Channel *) chan;
1278
    return (chanPtr->flags & (TCL_READABLE | TCL_WRITABLE));
1279
}
1280
 
1281
/*
1282
 *----------------------------------------------------------------------
1283
 *
1284
 * Tcl_GetChannelName --
1285
 *
1286
 *      Returns the string identifying the channel name.
1287
 *
1288
 * Results:
1289
 *      The string containing the channel name. This memory is
1290
 *      owned by the generic layer and should not be modified by
1291
 *      the caller.
1292
 *
1293
 * Side effects:
1294
 *      None.
1295
 *
1296
 *----------------------------------------------------------------------
1297
 */
1298
 
1299
char *
1300
Tcl_GetChannelName(chan)
1301
    Tcl_Channel chan;           /* The channel for which to return the name. */
1302
{
1303
    Channel *chanPtr;           /* The actual channel. */
1304
 
1305
    chanPtr = (Channel *) chan;
1306
    return chanPtr->channelName;
1307
}
1308
 
1309
/*
1310
 *----------------------------------------------------------------------
1311
 *
1312
 * Tcl_GetChannelType --
1313
 *
1314
 *      Given a channel structure, returns the channel type structure.
1315
 *
1316
 * Results:
1317
 *      Returns a pointer to the channel type structure.
1318
 *
1319
 * Side effects:
1320
 *      None.
1321
 *
1322
 *----------------------------------------------------------------------
1323
 */
1324
 
1325
Tcl_ChannelType *
1326
Tcl_GetChannelType(chan)
1327
    Tcl_Channel chan;           /* The channel to return type for. */
1328
{
1329
    Channel *chanPtr;           /* The actual channel. */
1330
 
1331
    chanPtr = (Channel *) chan;
1332
    return chanPtr->typePtr;
1333
}
1334
 
1335
/*
1336
 *----------------------------------------------------------------------
1337
 *
1338
 * Tcl_GetChannelHandle --
1339
 *
1340
 *      Returns an OS handle associated with a channel.
1341
 *
1342
 * Results:
1343
 *      Returns TCL_OK and places the handle in handlePtr, or returns
1344
 *      TCL_ERROR on failure.
1345
 *
1346
 * Side effects:
1347
 *      None.
1348
 *
1349
 *----------------------------------------------------------------------
1350
 */
1351
 
1352
int
1353
Tcl_GetChannelHandle(chan, direction, handlePtr)
1354
    Tcl_Channel chan;           /* The channel to get file from. */
1355
    int direction;              /* TCL_WRITABLE or TCL_READABLE. */
1356
    ClientData *handlePtr;      /* Where to store handle */
1357
{
1358
    Channel *chanPtr;           /* The actual channel. */
1359
    ClientData handle;
1360
    int result;
1361
 
1362
    chanPtr = (Channel *) chan;
1363
    result = (chanPtr->typePtr->getHandleProc)(chanPtr->instanceData,
1364
            direction, &handle);
1365
    if (handlePtr) {
1366
        *handlePtr = handle;
1367
    }
1368
    return result;
1369
}
1370
 
1371
/*
1372
 *----------------------------------------------------------------------
1373
 *
1374
 * Tcl_GetChannelInstanceData --
1375
 *
1376
 *      Returns the client data associated with a channel.
1377
 *
1378
 * Results:
1379
 *      The client data.
1380
 *
1381
 * Side effects:
1382
 *      None.
1383
 *
1384
 *----------------------------------------------------------------------
1385
 */
1386
 
1387
ClientData
1388
Tcl_GetChannelInstanceData(chan)
1389
    Tcl_Channel chan;           /* Channel for which to return client data. */
1390
{
1391
    Channel *chanPtr;           /* The actual channel. */
1392
 
1393
    chanPtr = (Channel *) chan;
1394
    return chanPtr->instanceData;
1395
}
1396
 
1397
/*
1398
 *----------------------------------------------------------------------
1399
 *
1400
 * RecycleBuffer --
1401
 *
1402
 *      Helper function to recycle input and output buffers. Ensures
1403
 *      that two input buffers are saved (one in the input queue and
1404
 *      another in the saveInBufPtr field) and that curOutPtr is set
1405
 *      to a buffer. Only if these conditions are met is the buffer
1406
 *      freed to the OS.
1407
 *
1408
 * Results:
1409
 *      None.
1410
 *
1411
 * Side effects:
1412
 *      May free a buffer to the OS.
1413
 *
1414
 *----------------------------------------------------------------------
1415
 */
1416
 
1417
static void
1418
RecycleBuffer(chanPtr, bufPtr, mustDiscard)
1419
    Channel *chanPtr;           /* Channel for which to recycle buffers. */
1420
    ChannelBuffer *bufPtr;      /* The buffer to recycle. */
1421
    int mustDiscard;            /* If nonzero, free the buffer to the
1422
                                 * OS, always. */
1423
{
1424
    /*
1425
     * Do we have to free the buffer to the OS?
1426
     */
1427
 
1428
    if (mustDiscard) {
1429
        ckfree((char *) bufPtr);
1430
        return;
1431
    }
1432
 
1433
    /*
1434
     * Only save buffers for the input queue if the channel is readable.
1435
     */
1436
 
1437
    if (chanPtr->flags & TCL_READABLE) {
1438
        if (chanPtr->inQueueHead == (ChannelBuffer *) NULL) {
1439
            chanPtr->inQueueHead = bufPtr;
1440
            chanPtr->inQueueTail = bufPtr;
1441
            goto keepit;
1442
        }
1443
        if (chanPtr->saveInBufPtr == (ChannelBuffer *) NULL) {
1444
            chanPtr->saveInBufPtr = bufPtr;
1445
            goto keepit;
1446
        }
1447
    }
1448
 
1449
    /*
1450
     * Only save buffers for the output queue if the channel is writable.
1451
     */
1452
 
1453
    if (chanPtr->flags & TCL_WRITABLE) {
1454
        if (chanPtr->curOutPtr == (ChannelBuffer *) NULL) {
1455
            chanPtr->curOutPtr = bufPtr;
1456
            goto keepit;
1457
        }
1458
    }
1459
 
1460
    /*
1461
     * If we reached this code we return the buffer to the OS.
1462
     */
1463
 
1464
    ckfree((char *) bufPtr);
1465
    return;
1466
 
1467
keepit:
1468
    bufPtr->nextRemoved = 0;
1469
    bufPtr->nextAdded = 0;
1470
    bufPtr->nextPtr = (ChannelBuffer *) NULL;
1471
}
1472
 
1473
/*
1474
 *----------------------------------------------------------------------
1475
 *
1476
 * DiscardOutputQueued --
1477
 *
1478
 *      Discards all output queued in the output queue of a channel.
1479
 *
1480
 * Results:
1481
 *      None.
1482
 *
1483
 * Side effects:
1484
 *      Recycles buffers.
1485
 *
1486
 *----------------------------------------------------------------------
1487
 */
1488
 
1489
static void
1490
DiscardOutputQueued(chanPtr)
1491
    Channel *chanPtr;           /* The channel for which to discard output. */
1492
{
1493
    ChannelBuffer *bufPtr;
1494
 
1495
    while (chanPtr->outQueueHead != (ChannelBuffer *) NULL) {
1496
        bufPtr = chanPtr->outQueueHead;
1497
        chanPtr->outQueueHead = bufPtr->nextPtr;
1498
        RecycleBuffer(chanPtr, bufPtr, 0);
1499
    }
1500
    chanPtr->outQueueHead = (ChannelBuffer *) NULL;
1501
    chanPtr->outQueueTail = (ChannelBuffer *) NULL;
1502
}
1503
 
1504
/*
1505
 *----------------------------------------------------------------------
1506
 *
1507
 * CheckForDeadChannel --
1508
 *
1509
 *      This function checks is a given channel is Dead.
1510
 *      (A channel that has been closed but not yet deallocated.)
1511
 *
1512
 * Results:
1513
 *      True (1) if channel is Dead, False (0) if channel is Ok
1514
 *
1515
 * Side effects:
1516
 *      None
1517
 *
1518
 *----------------------------------------------------------------------
1519
 */
1520
 
1521
static int
1522
CheckForDeadChannel(interp, chanPtr)
1523
    Tcl_Interp *interp;         /* For error reporting (can be NULL) */
1524
    Channel    *chanPtr;        /* The channel to check. */
1525
{
1526
    if (chanPtr->flags & CHANNEL_DEAD) {
1527
        Tcl_SetErrno(EINVAL);
1528
        if (interp) {
1529
            Tcl_AppendResult(interp,
1530
                             "unable to access channel: invalid channel",
1531
                             (char *) NULL);
1532
        }
1533
        return 1;
1534
    }
1535
    return 0;
1536
}
1537
 
1538
/*
1539
 *----------------------------------------------------------------------
1540
 *
1541
 * FlushChannel --
1542
 *
1543
 *      This function flushes as much of the queued output as is possible
1544
 *      now. If calledFromAsyncFlush is nonzero, it is being called in an
1545
 *      event handler to flush channel output asynchronously.
1546
 *
1547
 * Results:
1548
 *      0 if successful, else the error code that was returned by the
1549
 *      channel type operation.
1550
 *
1551
 * Side effects:
1552
 *      May produce output on a channel. May block indefinitely if the
1553
 *      channel is synchronous. May schedule an async flush on the channel.
1554
 *      May recycle memory for buffers in the output queue.
1555
 *
1556
 *----------------------------------------------------------------------
1557
 */
1558
 
1559
static int
1560
FlushChannel(interp, chanPtr, calledFromAsyncFlush)
1561
    Tcl_Interp *interp;                 /* For error reporting during close. */
1562
    Channel *chanPtr;                   /* The channel to flush on. */
1563
    int calledFromAsyncFlush;           /* If nonzero then we are being
1564
                                         * called from an asynchronous
1565
                                         * flush callback. */
1566
{
1567
    ChannelBuffer *bufPtr;              /* Iterates over buffered output
1568
                                         * queue. */
1569
    int toWrite;                        /* Amount of output data in current
1570
                                         * buffer available to be written. */
1571
    int written;                        /* Amount of output data actually
1572
                                         * written in current round. */
1573
    int errorCode;                      /* Stores POSIX error codes from
1574
                                         * channel driver operations. */
1575
    errorCode = 0;
1576
 
1577
    /*
1578
     * Prevent writing on a dead channel -- a channel that has been closed
1579
     * but not yet deallocated. This can occur if the exit handler for the
1580
     * channel deallocation runs before all channels are deregistered in
1581
     * all interpreters.
1582
     */
1583
 
1584
    if (CheckForDeadChannel(interp,chanPtr)) return -1;
1585
 
1586
    /*
1587
     * Loop over the queued buffers and attempt to flush as
1588
     * much as possible of the queued output to the channel.
1589
     */
1590
 
1591
    while (1) {
1592
 
1593
        /*
1594
         * If the queue is empty and there is a ready current buffer, OR if
1595
         * the current buffer is full, then move the current buffer to the
1596
         * queue.
1597
         */
1598
 
1599
        if (((chanPtr->curOutPtr != (ChannelBuffer *) NULL) &&
1600
                (chanPtr->curOutPtr->nextAdded == chanPtr->curOutPtr->bufSize))
1601
                || ((chanPtr->flags & BUFFER_READY) &&
1602
                        (chanPtr->outQueueHead == (ChannelBuffer *) NULL))) {
1603
            chanPtr->flags &= (~(BUFFER_READY));
1604
            chanPtr->curOutPtr->nextPtr = (ChannelBuffer *) NULL;
1605
            if (chanPtr->outQueueHead == (ChannelBuffer *) NULL) {
1606
                chanPtr->outQueueHead = chanPtr->curOutPtr;
1607
            } else {
1608
                chanPtr->outQueueTail->nextPtr = chanPtr->curOutPtr;
1609
            }
1610
            chanPtr->outQueueTail = chanPtr->curOutPtr;
1611
            chanPtr->curOutPtr = (ChannelBuffer *) NULL;
1612
        }
1613
        bufPtr = chanPtr->outQueueHead;
1614
 
1615
        /*
1616
         * If we are not being called from an async flush and an async
1617
         * flush is active, we just return without producing any output.
1618
         */
1619
 
1620
        if ((!calledFromAsyncFlush) &&
1621
                (chanPtr->flags & BG_FLUSH_SCHEDULED)) {
1622
            return 0;
1623
        }
1624
 
1625
        /*
1626
         * If the output queue is still empty, break out of the while loop.
1627
         */
1628
 
1629
        if (bufPtr == (ChannelBuffer *) NULL) {
1630
            break;      /* Out of the "while (1)". */
1631
        }
1632
 
1633
        /*
1634
         * Produce the output on the channel.
1635
         */
1636
 
1637
        toWrite = bufPtr->nextAdded - bufPtr->nextRemoved;
1638
        written = (chanPtr->typePtr->outputProc) (chanPtr->instanceData,
1639
                bufPtr->buf + bufPtr->nextRemoved, toWrite, &errorCode);
1640
 
1641
        /*
1642
         * If the write failed completely attempt to start the asynchronous
1643
         * flush mechanism and break out of this loop - do not attempt to
1644
         * write any more output at this time.
1645
         */
1646
 
1647
        if (written < 0) {
1648
 
1649
            /*
1650
             * If the last attempt to write was interrupted, simply retry.
1651
             */
1652
 
1653
            if (errorCode == EINTR) {
1654
                errorCode = 0;
1655
                continue;
1656
            }
1657
 
1658
            /*
1659
             * If the channel is non-blocking and we would have blocked,
1660
             * start a background flushing handler and break out of the loop.
1661
             */
1662
 
1663
            if ((errorCode == EWOULDBLOCK) || (errorCode == EAGAIN)) {
1664
                if (chanPtr->flags & CHANNEL_NONBLOCKING) {
1665
                    if (!(chanPtr->flags & BG_FLUSH_SCHEDULED)) {
1666
                        chanPtr->flags |= BG_FLUSH_SCHEDULED;
1667
                        UpdateInterest(chanPtr);
1668
                    }
1669
                    errorCode = 0;
1670
                    break;
1671
                } else {
1672
                    panic("Blocking channel driver did not block on output");
1673
                }
1674
            }
1675
 
1676
            /*
1677
             * Decide whether to report the error upwards or defer it.
1678
             */
1679
 
1680
            if (calledFromAsyncFlush) {
1681
                if (chanPtr->unreportedError == 0) {
1682
                    chanPtr->unreportedError = errorCode;
1683
                }
1684
            } else {
1685
                Tcl_SetErrno(errorCode);
1686
                if (interp != NULL) {
1687
                    Tcl_SetResult(interp,
1688
                            Tcl_PosixError(interp), TCL_VOLATILE);
1689
                }
1690
            }
1691
 
1692
            /*
1693
             * When we get an error we throw away all the output
1694
             * currently queued.
1695
             */
1696
 
1697
            DiscardOutputQueued(chanPtr);
1698
            continue;
1699
        }
1700
 
1701
        bufPtr->nextRemoved += written;
1702
 
1703
        /*
1704
         * If this buffer is now empty, recycle it.
1705
         */
1706
 
1707
        if (bufPtr->nextRemoved == bufPtr->nextAdded) {
1708
            chanPtr->outQueueHead = bufPtr->nextPtr;
1709
            if (chanPtr->outQueueHead == (ChannelBuffer *) NULL) {
1710
                chanPtr->outQueueTail = (ChannelBuffer *) NULL;
1711
            }
1712
            RecycleBuffer(chanPtr, bufPtr, 0);
1713
        }
1714
    }   /* Closes "while (1)". */
1715
 
1716
    /*
1717
     * If the queue became empty and we have the asynchronous flushing
1718
     * mechanism active, cancel the asynchronous flushing.
1719
     */
1720
 
1721
    if ((chanPtr->outQueueHead == (ChannelBuffer *) NULL) &&
1722
            (chanPtr->flags & BG_FLUSH_SCHEDULED)) {
1723
        chanPtr->flags &= (~(BG_FLUSH_SCHEDULED));
1724
        (chanPtr->typePtr->watchProc)(chanPtr->instanceData,
1725
                chanPtr->interestMask);
1726
    }
1727
 
1728
    /*
1729
     * If the channel is flagged as closed, delete it when the refCount
1730
     * drops to zero, the output queue is empty and there is no output
1731
     * in the current output buffer.
1732
     */
1733
 
1734
    if ((chanPtr->flags & CHANNEL_CLOSED) && (chanPtr->refCount <= 0) &&
1735
            (chanPtr->outQueueHead == (ChannelBuffer *) NULL) &&
1736
            ((chanPtr->curOutPtr == (ChannelBuffer *) NULL) ||
1737
                    (chanPtr->curOutPtr->nextAdded ==
1738
                            chanPtr->curOutPtr->nextRemoved))) {
1739
        return CloseChannel(interp, chanPtr, errorCode);
1740
    }
1741
    return errorCode;
1742
}
1743
 
1744
/*
1745
 *----------------------------------------------------------------------
1746
 *
1747
 * CloseChannel --
1748
 *
1749
 *      Utility procedure to close a channel and free its associated
1750
 *      resources.
1751
 *
1752
 * Results:
1753
 *      0 on success or a POSIX error code if the operation failed.
1754
 *
1755
 * Side effects:
1756
 *      May close the actual channel; may free memory.
1757
 *
1758
 *----------------------------------------------------------------------
1759
 */
1760
 
1761
static int
1762
CloseChannel(interp, chanPtr, errorCode)
1763
    Tcl_Interp *interp;                 /* For error reporting. */
1764
    Channel *chanPtr;                   /* The channel to close. */
1765
    int errorCode;                      /* Status of operation so far. */
1766
{
1767
    int result = 0;                      /* Of calling driver close
1768
                                         * operation. */
1769
    Channel *prevChanPtr;               /* Preceding channel in list of
1770
                                         * all channels - used to splice a
1771
                                         * channel out of the list on close. */
1772
 
1773
    if (chanPtr == NULL) {
1774
        return result;
1775
    }
1776
 
1777
    /*
1778
     * No more input can be consumed so discard any leftover input.
1779
     */
1780
 
1781
    DiscardInputQueued(chanPtr, 1);
1782
 
1783
    /*
1784
     * Discard a leftover buffer in the current output buffer field.
1785
     */
1786
 
1787
    if (chanPtr->curOutPtr != (ChannelBuffer *) NULL) {
1788
        ckfree((char *) chanPtr->curOutPtr);
1789
        chanPtr->curOutPtr = (ChannelBuffer *) NULL;
1790
    }
1791
 
1792
    /*
1793
     * The caller guarantees that there are no more buffers
1794
     * queued for output.
1795
     */
1796
 
1797
    if (chanPtr->outQueueHead != (ChannelBuffer *) NULL) {
1798
        panic("TclFlush, closed channel: queued output left");
1799
    }
1800
 
1801
    /*
1802
     * If the EOF character is set in the channel, append that to the
1803
     * output device.
1804
     */
1805
 
1806
    if ((chanPtr->outEofChar != 0) && (chanPtr->flags & TCL_WRITABLE)) {
1807
        int dummy;
1808
        char c;
1809
 
1810
        c = (char) chanPtr->outEofChar;
1811
        (chanPtr->typePtr->outputProc) (chanPtr->instanceData, &c, 1, &dummy);
1812
    }
1813
 
1814
    /*
1815
     * Remove TCL_READABLE and TCL_WRITABLE from chanPtr->flags, so
1816
     * that close callbacks can not do input or output (assuming they
1817
     * squirreled the channel away in their clientData). This also
1818
     * prevents infinite loops if the callback calls any C API that
1819
     * could call FlushChannel.
1820
     */
1821
 
1822
    chanPtr->flags &= (~(TCL_READABLE|TCL_WRITABLE));
1823
 
1824
    /*
1825
     * Splice this channel out of the list of all channels.
1826
     */
1827
 
1828
    if (chanPtr == firstChanPtr) {
1829
        firstChanPtr = chanPtr->nextChanPtr;
1830
    } else {
1831
        for (prevChanPtr = firstChanPtr;
1832
                 (prevChanPtr != (Channel *) NULL) &&
1833
                     (prevChanPtr->nextChanPtr != chanPtr);
1834
                 prevChanPtr = prevChanPtr->nextChanPtr) {
1835
            /* Empty loop body. */
1836
        }
1837
        if (prevChanPtr == (Channel *) NULL) {
1838
            panic("FlushChannel: damaged channel list");
1839
        }
1840
        prevChanPtr->nextChanPtr = chanPtr->nextChanPtr;
1841
    }
1842
 
1843
    /*
1844
     * OK, close the channel itself.
1845
     */
1846
 
1847
    result = (chanPtr->typePtr->closeProc) (chanPtr->instanceData, interp);
1848
 
1849
    if (chanPtr->channelName != (char *) NULL) {
1850
        ckfree(chanPtr->channelName);
1851
    }
1852
 
1853
    /*
1854
     * If we are being called synchronously, report either
1855
     * any latent error on the channel or the current error.
1856
     */
1857
 
1858
    if (chanPtr->unreportedError != 0) {
1859
        errorCode = chanPtr->unreportedError;
1860
    }
1861
    if (errorCode == 0) {
1862
        errorCode = result;
1863
        if (errorCode != 0) {
1864
            Tcl_SetErrno(errorCode);
1865
        }
1866
    }
1867
 
1868
    /*
1869
     * Cancel any outstanding timer.
1870
     */
1871
 
1872
    Tcl_DeleteTimerHandler(chanPtr->timer);
1873
 
1874
    /*
1875
     * Mark the channel as deleted by clearing the type structure.
1876
     */
1877
 
1878
    chanPtr->typePtr = NULL;
1879
 
1880
    Tcl_EventuallyFree((ClientData) chanPtr, TCL_DYNAMIC);
1881
 
1882
    return errorCode;
1883
}
1884
 
1885
/*
1886
 *----------------------------------------------------------------------
1887
 *
1888
 * Tcl_Close --
1889
 *
1890
 *      Closes a channel.
1891
 *
1892
 * Results:
1893
 *      A standard Tcl result.
1894
 *
1895
 * Side effects:
1896
 *      Closes the channel if this is the last reference.
1897
 *
1898
 * NOTE:
1899
 *      Tcl_Close removes the channel as far as the user is concerned.
1900
 *      However, it may continue to exist for a while longer if it has
1901
 *      a background flush scheduled. The device itself is eventually
1902
 *      closed and the channel record removed, in CloseChannel, above.
1903
 *
1904
 *----------------------------------------------------------------------
1905
 */
1906
 
1907
        /* ARGSUSED */
1908
int
1909
Tcl_Close(interp, chan)
1910
    Tcl_Interp *interp;                 /* Interpreter for errors. */
1911
    Tcl_Channel chan;                   /* The channel being closed. Must
1912
                                         * not be referenced in any
1913
                                         * interpreter. */
1914
{
1915
    ChannelHandler *chPtr, *chNext;     /* Iterate over channel handlers. */
1916
    CloseCallback *cbPtr;               /* Iterate over close callbacks
1917
                                         * for this channel. */
1918
    EventScriptRecord *ePtr, *eNextPtr; /* Iterate over eventscript records. */
1919
    Channel *chanPtr;                   /* The real IO channel. */
1920
    int result;                         /* Of calling FlushChannel. */
1921
    NextChannelHandler *nhPtr;
1922
 
1923
    if (chan == (Tcl_Channel) NULL) {
1924
        return TCL_OK;
1925
    }
1926
 
1927
    /*
1928
     * Perform special handling for standard channels being closed. If the
1929
     * refCount is now 1 it means that the last reference to the standard
1930
     * channel is being explicitly closed, so bump the refCount down
1931
     * artificially to 0. This will ensure that the channel is actually
1932
     * closed, below. Also set the static pointer to NULL for the channel.
1933
     */
1934
 
1935
    CheckForStdChannelsBeingClosed(chan);
1936
 
1937
    chanPtr = (Channel *) chan;
1938
    if (chanPtr->refCount > 0) {
1939
        panic("called Tcl_Close on channel with refCount > 0");
1940
    }
1941
 
1942
    /*
1943
     * Remove any references to channel handlers for this channel that
1944
     * may be about to be invoked.
1945
     */
1946
 
1947
    for (nhPtr = nestedHandlerPtr;
1948
             nhPtr != (NextChannelHandler *) NULL;
1949
             nhPtr = nhPtr->nestedHandlerPtr) {
1950
        if (nhPtr->nextHandlerPtr &&
1951
                (nhPtr->nextHandlerPtr->chanPtr == chanPtr)) {
1952
            nhPtr->nextHandlerPtr = NULL;
1953
        }
1954
    }
1955
 
1956
    /*
1957
     * Remove all the channel handler records attached to the channel
1958
     * itself.
1959
     */
1960
 
1961
    for (chPtr = chanPtr->chPtr;
1962
             chPtr != (ChannelHandler *) NULL;
1963
             chPtr = chNext) {
1964
        chNext = chPtr->nextPtr;
1965
        ckfree((char *) chPtr);
1966
    }
1967
    chanPtr->chPtr = (ChannelHandler *) NULL;
1968
 
1969
 
1970
    /*
1971
     * Cancel any pending copy operation.
1972
     */
1973
 
1974
    StopCopy(chanPtr->csPtr);
1975
 
1976
    /*
1977
     * Must set the interest mask now to 0, otherwise infinite loops
1978
     * will occur if Tcl_DoOneEvent is called before the channel is
1979
     * finally deleted in FlushChannel. This can happen if the channel
1980
     * has a background flush active.
1981
     */
1982
 
1983
    chanPtr->interestMask = 0;
1984
 
1985
    /*
1986
     * Remove any EventScript records for this channel.
1987
     */
1988
 
1989
    for (ePtr = chanPtr->scriptRecordPtr;
1990
             ePtr != (EventScriptRecord *) NULL;
1991
             ePtr = eNextPtr) {
1992
        eNextPtr = ePtr->nextPtr;
1993
        ckfree(ePtr->script);
1994
        ckfree((char *) ePtr);
1995
    }
1996
    chanPtr->scriptRecordPtr = (EventScriptRecord *) NULL;
1997
 
1998
    /*
1999
     * Invoke the registered close callbacks and delete their records.
2000
     */
2001
 
2002
    while (chanPtr->closeCbPtr != (CloseCallback *) NULL) {
2003
        cbPtr = chanPtr->closeCbPtr;
2004
        chanPtr->closeCbPtr = cbPtr->nextPtr;
2005
        (cbPtr->proc) (cbPtr->clientData);
2006
        ckfree((char *) cbPtr);
2007
    }
2008
 
2009
    /*
2010
     * Ensure that the last output buffer will be flushed.
2011
     */
2012
 
2013
    if ((chanPtr->curOutPtr != (ChannelBuffer *) NULL) &&
2014
           (chanPtr->curOutPtr->nextAdded > chanPtr->curOutPtr->nextRemoved)) {
2015
        chanPtr->flags |= BUFFER_READY;
2016
    }
2017
 
2018
    /*
2019
     * The call to FlushChannel will flush any queued output and invoke
2020
     * the close function of the channel driver, or it will set up the
2021
     * channel to be flushed and closed asynchronously.
2022
     */
2023
 
2024
    chanPtr->flags |= CHANNEL_CLOSED;
2025
    result = FlushChannel(interp, chanPtr, 0);
2026
    if (result != 0) {
2027
        return TCL_ERROR;
2028
    }
2029
 
2030
    return TCL_OK;
2031
}
2032
 
2033
/*
2034
 *----------------------------------------------------------------------
2035
 *
2036
 * Tcl_Write --
2037
 *
2038
 *      Puts a sequence of characters into an output buffer, may queue the
2039
 *      buffer for output if it gets full, and also remembers whether the
2040
 *      current buffer is ready e.g. if it contains a newline and we are in
2041
 *      line buffering mode.
2042
 *
2043
 * Results:
2044
 *      The number of bytes written or -1 in case of error. If -1,
2045
 *      Tcl_GetErrno will return the error code.
2046
 *
2047
 * Side effects:
2048
 *      May buffer up output and may cause output to be produced on the
2049
 *      channel.
2050
 *
2051
 *----------------------------------------------------------------------
2052
 */
2053
 
2054
int
2055
Tcl_Write(chan, srcPtr, slen)
2056
    Tcl_Channel chan;                   /* The channel to buffer output for. */
2057
    char *srcPtr;                       /* Output to buffer. */
2058
    int slen;                           /* Its length. Negative means
2059
                                         * the output is null terminated
2060
                                         * and we must compute its length. */
2061
{
2062
    Channel *chanPtr = (Channel *) chan;
2063
 
2064
    /*
2065
     * Check for unreported error.
2066
     */
2067
 
2068
    if (chanPtr->unreportedError != 0) {
2069
        Tcl_SetErrno(chanPtr->unreportedError);
2070
        chanPtr->unreportedError = 0;
2071
        return -1;
2072
    }
2073
 
2074
    /*
2075
     * If the channel is not open for writing punt.
2076
     */
2077
 
2078
    if (!(chanPtr->flags & TCL_WRITABLE)) {
2079
        Tcl_SetErrno(EACCES);
2080
        return -1;
2081
    }
2082
 
2083
    /*
2084
     * If the channel is in the middle of a background copy, fail.
2085
     */
2086
 
2087
    if (chanPtr->csPtr) {
2088
        Tcl_SetErrno(EBUSY);
2089
        return -1;
2090
    }
2091
 
2092
    /*
2093
     * If length passed is negative, assume that the output is null terminated
2094
     * and compute its length.
2095
     */
2096
 
2097
    if (slen < 0) {
2098
        slen = strlen(srcPtr);
2099
    }
2100
 
2101
    return DoWrite(chanPtr, srcPtr, slen);
2102
}
2103
 
2104
/*
2105
 *----------------------------------------------------------------------
2106
 *
2107
 * DoWrite --
2108
 *
2109
 *      Puts a sequence of characters into an output buffer, may queue the
2110
 *      buffer for output if it gets full, and also remembers whether the
2111
 *      current buffer is ready e.g. if it contains a newline and we are in
2112
 *      line buffering mode.
2113
 *
2114
 * Results:
2115
 *      The number of bytes written or -1 in case of error. If -1,
2116
 *      Tcl_GetErrno will return the error code.
2117
 *
2118
 * Side effects:
2119
 *      May buffer up output and may cause output to be produced on the
2120
 *      channel.
2121
 *
2122
 *----------------------------------------------------------------------
2123
 */
2124
 
2125
static int
2126
DoWrite(chanPtr, srcPtr, slen)
2127
    Channel *chanPtr;                   /* The channel to buffer output for. */
2128
    char *srcPtr;                       /* Data to write. */
2129
    int slen;                           /* Number of bytes to write. */
2130
{
2131
    ChannelBuffer *outBufPtr;           /* Current output buffer. */
2132
    int foundNewline;                   /* Did we find a newline in output? */
2133
    char *dPtr, *sPtr;                  /* Search variables for newline. */
2134
    int crsent;                         /* In CRLF eol translation mode,
2135
                                         * remember the fact that a CR was
2136
                                         * output to the channel without
2137
                                         * its following NL. */
2138
    int i;                              /* Loop index for newline search. */
2139
    int destCopied;                     /* How many bytes were used in this
2140
                                         * destination buffer to hold the
2141
                                         * output? */
2142
    int totalDestCopied;                /* How many bytes total were
2143
                                         * copied to the channel buffer? */
2144
    int srcCopied;                      /* How many bytes were copied from
2145
                                         * the source string? */
2146
    char *destPtr;                      /* Where in line to copy to? */
2147
 
2148
    /*
2149
     * If we are in network (or windows) translation mode, record the fact
2150
     * that we have not yet sent a CR to the channel.
2151
     */
2152
 
2153
    crsent = 0;
2154
 
2155
    /*
2156
     * Loop filling buffers and flushing them until all output has been
2157
     * consumed.
2158
     */
2159
 
2160
    srcCopied = 0;
2161
    totalDestCopied = 0;
2162
 
2163
    while (slen > 0) {
2164
 
2165
        /*
2166
         * Make sure there is a current output buffer to accept output.
2167
         */
2168
 
2169
        if (chanPtr->curOutPtr == (ChannelBuffer *) NULL) {
2170
            chanPtr->curOutPtr = (ChannelBuffer *) ckalloc((unsigned)
2171
                    (CHANNELBUFFER_HEADER_SIZE + chanPtr->bufSize));
2172
            chanPtr->curOutPtr->nextAdded = 0;
2173
            chanPtr->curOutPtr->nextRemoved = 0;
2174
            chanPtr->curOutPtr->bufSize = chanPtr->bufSize;
2175
            chanPtr->curOutPtr->nextPtr = (ChannelBuffer *) NULL;
2176
        }
2177
 
2178
        outBufPtr = chanPtr->curOutPtr;
2179
 
2180
        destCopied = outBufPtr->bufSize - outBufPtr->nextAdded;
2181
        if (destCopied > slen) {
2182
            destCopied = slen;
2183
        }
2184
 
2185
        destPtr = outBufPtr->buf + outBufPtr->nextAdded;
2186
        switch (chanPtr->outputTranslation) {
2187
            case TCL_TRANSLATE_LF:
2188
                srcCopied = destCopied;
2189
                memcpy((VOID *) destPtr, (VOID *) srcPtr, (size_t) destCopied);
2190
                break;
2191
            case TCL_TRANSLATE_CR:
2192
                srcCopied = destCopied;
2193
                memcpy((VOID *) destPtr, (VOID *) srcPtr, (size_t) destCopied);
2194
                for (dPtr = destPtr; dPtr < destPtr + destCopied; dPtr++) {
2195
                    if (*dPtr == '\n') {
2196
                        *dPtr = '\r';
2197
                    }
2198
                }
2199
                break;
2200
            case TCL_TRANSLATE_CRLF:
2201
                for (srcCopied = 0, dPtr = destPtr, sPtr = srcPtr;
2202
                     dPtr < destPtr + destCopied;
2203
                     dPtr++, sPtr++, srcCopied++) {
2204
                    if (*sPtr == '\n') {
2205
                        if (crsent) {
2206
                            *dPtr = '\n';
2207
                            crsent = 0;
2208
                        } else {
2209
                            *dPtr = '\r';
2210
                            crsent = 1;
2211
                            sPtr--, srcCopied--;
2212
                        }
2213
                    } else {
2214
                        *dPtr = *sPtr;
2215
                    }
2216
                }
2217
                break;
2218
            case TCL_TRANSLATE_AUTO:
2219
                panic("Tcl_Write: AUTO output translation mode not supported");
2220
            default:
2221
                panic("Tcl_Write: unknown output translation mode");
2222
        }
2223
 
2224
        /*
2225
         * The current buffer is ready for output if it is full, or if it
2226
         * contains a newline and this channel is line-buffered, or if it
2227
         * contains any output and this channel is unbuffered.
2228
         */
2229
 
2230
        outBufPtr->nextAdded += destCopied;
2231
        if (!(chanPtr->flags & BUFFER_READY)) {
2232
            if (outBufPtr->nextAdded == outBufPtr->bufSize) {
2233
                chanPtr->flags |= BUFFER_READY;
2234
            } else if (chanPtr->flags & CHANNEL_LINEBUFFERED) {
2235
                for (sPtr = srcPtr, i = 0, foundNewline = 0;
2236
                         (i < srcCopied) && (!foundNewline);
2237
                         i++, sPtr++) {
2238
                    if (*sPtr == '\n') {
2239
                        foundNewline = 1;
2240
                        break;
2241
                    }
2242
                }
2243
                if (foundNewline) {
2244
                    chanPtr->flags |= BUFFER_READY;
2245
                }
2246
            } else if (chanPtr->flags & CHANNEL_UNBUFFERED) {
2247
                chanPtr->flags |= BUFFER_READY;
2248
            }
2249
        }
2250
 
2251
        totalDestCopied += srcCopied;
2252
        srcPtr += srcCopied;
2253
        slen -= srcCopied;
2254
 
2255
        if (chanPtr->flags & BUFFER_READY) {
2256
            if (FlushChannel(NULL, chanPtr, 0) != 0) {
2257
                return -1;
2258
            }
2259
        }
2260
    } /* Closes "while" */
2261
 
2262
    return totalDestCopied;
2263
}
2264
 
2265
/*
2266
 *----------------------------------------------------------------------
2267
 *
2268
 * Tcl_Flush --
2269
 *
2270
 *      Flushes output data on a channel.
2271
 *
2272
 * Results:
2273
 *      A standard Tcl result.
2274
 *
2275
 * Side effects:
2276
 *      May flush output queued on this channel.
2277
 *
2278
 *----------------------------------------------------------------------
2279
 */
2280
 
2281
int
2282
Tcl_Flush(chan)
2283
    Tcl_Channel chan;                   /* The Channel to flush. */
2284
{
2285
    int result;                         /* Of calling FlushChannel. */
2286
    Channel *chanPtr;                   /* The actual channel. */
2287
 
2288
    chanPtr = (Channel *) chan;
2289
 
2290
    /*
2291
     * Check for unreported error.
2292
     */
2293
 
2294
    if (chanPtr->unreportedError != 0) {
2295
        Tcl_SetErrno(chanPtr->unreportedError);
2296
        chanPtr->unreportedError = 0;
2297
        return TCL_ERROR;
2298
    }
2299
 
2300
    /*
2301
     * If the channel is not open for writing punt.
2302
     */
2303
 
2304
    if (!(chanPtr->flags & TCL_WRITABLE)) {
2305
        Tcl_SetErrno(EACCES);
2306
        return TCL_ERROR;
2307
    }
2308
 
2309
    /*
2310
     * If the channel is in the middle of a background copy, fail.
2311
     */
2312
 
2313
    if (chanPtr->csPtr) {
2314
        Tcl_SetErrno(EBUSY);
2315
        return -1;
2316
    }
2317
 
2318
    /*
2319
     * Force current output buffer to be output also.
2320
     */
2321
 
2322
    if ((chanPtr->curOutPtr != (ChannelBuffer *) NULL) &&
2323
            (chanPtr->curOutPtr->nextAdded > 0)) {
2324
        chanPtr->flags |= BUFFER_READY;
2325
    }
2326
 
2327
    result = FlushChannel(NULL, chanPtr, 0);
2328
    if (result != 0) {
2329
        return TCL_ERROR;
2330
    }
2331
 
2332
    return TCL_OK;
2333
}
2334
 
2335
/*
2336
 *----------------------------------------------------------------------
2337
 *
2338
 * DiscardInputQueued --
2339
 *
2340
 *      Discards any input read from the channel but not yet consumed
2341
 *      by Tcl reading commands.
2342
 *
2343
 * Results:
2344
 *      None.
2345
 *
2346
 * Side effects:
2347
 *      May discard input from the channel. If discardLastBuffer is zero,
2348
 *      leaves one buffer in place for back-filling.
2349
 *
2350
 *----------------------------------------------------------------------
2351
 */
2352
 
2353
static void
2354
DiscardInputQueued(chanPtr, discardSavedBuffers)
2355
    Channel *chanPtr;           /* Channel on which to discard
2356
                                 * the queued input. */
2357
    int discardSavedBuffers;    /* If non-zero, discard all buffers including
2358
                                 * last one. */
2359
{
2360
    ChannelBuffer *bufPtr, *nxtPtr;     /* Loop variables. */
2361
 
2362
    bufPtr = chanPtr->inQueueHead;
2363
    chanPtr->inQueueHead = (ChannelBuffer *) NULL;
2364
    chanPtr->inQueueTail = (ChannelBuffer *) NULL;
2365
    for (; bufPtr != (ChannelBuffer *) NULL; bufPtr = nxtPtr) {
2366
        nxtPtr = bufPtr->nextPtr;
2367
        RecycleBuffer(chanPtr, bufPtr, discardSavedBuffers);
2368
    }
2369
 
2370
    /*
2371
     * If discardSavedBuffers is nonzero, must also discard any previously
2372
     * saved buffer in the saveInBufPtr field.
2373
     */
2374
 
2375
    if (discardSavedBuffers) {
2376
        if (chanPtr->saveInBufPtr != (ChannelBuffer *) NULL) {
2377
            ckfree((char *) chanPtr->saveInBufPtr);
2378
            chanPtr->saveInBufPtr = (ChannelBuffer *) NULL;
2379
        }
2380
    }
2381
}
2382
 
2383
/*
2384
 *----------------------------------------------------------------------
2385
 *
2386
 * GetInput --
2387
 *
2388
 *      Reads input data from a device or file into an input buffer.
2389
 *
2390
 * Results:
2391
 *      A Posix error code or 0.
2392
 *
2393
 * Side effects:
2394
 *      Reads from the underlying device.
2395
 *
2396
 *----------------------------------------------------------------------
2397
 */
2398
 
2399
static int
2400
GetInput(chanPtr)
2401
    Channel *chanPtr;                   /* Channel to read input from. */
2402
{
2403
    int toRead;                         /* How much to read? */
2404
    int result;                         /* Of calling driver. */
2405
    int nread;                          /* How much was read from channel? */
2406
    ChannelBuffer *bufPtr;              /* New buffer to add to input queue. */
2407
 
2408
    /*
2409
     * Prevent reading from a dead channel -- a channel that has been closed
2410
     * but not yet deallocated, which can happen if the exit handler for
2411
     * channel cleanup has run but the channel is still registered in some
2412
     * interpreter.
2413
     */
2414
 
2415
    if (CheckForDeadChannel(NULL,chanPtr)) return EINVAL;
2416
 
2417
    /*
2418
     * See if we can fill an existing buffer. If we can, read only
2419
     * as much as will fit in it. Otherwise allocate a new buffer,
2420
     * add it to the input queue and attempt to fill it to the max.
2421
     */
2422
 
2423
    if ((chanPtr->inQueueTail != (ChannelBuffer *) NULL) &&
2424
           (chanPtr->inQueueTail->nextAdded < chanPtr->inQueueTail->bufSize)) {
2425
        bufPtr = chanPtr->inQueueTail;
2426
        toRead = bufPtr->bufSize - bufPtr->nextAdded;
2427
    } else {
2428
        if (chanPtr->saveInBufPtr != (ChannelBuffer *) NULL) {
2429
            bufPtr = chanPtr->saveInBufPtr;
2430
            chanPtr->saveInBufPtr = (ChannelBuffer *) NULL;
2431
        } else {
2432
            bufPtr = (ChannelBuffer *) ckalloc(
2433
                ((unsigned) CHANNELBUFFER_HEADER_SIZE + chanPtr->bufSize));
2434
            bufPtr->bufSize = chanPtr->bufSize;
2435
        }
2436
        bufPtr->nextRemoved = 0;
2437
        bufPtr->nextAdded = 0;
2438
        toRead = bufPtr->bufSize;
2439
        if (chanPtr->inQueueTail == (ChannelBuffer *) NULL) {
2440
            chanPtr->inQueueHead = bufPtr;
2441
        } else {
2442
            chanPtr->inQueueTail->nextPtr = bufPtr;
2443
        }
2444
        chanPtr->inQueueTail = bufPtr;
2445
        bufPtr->nextPtr = (ChannelBuffer *) NULL;
2446
    }
2447
 
2448
    /*
2449
     * If EOF is set, we should avoid calling the driver because on some
2450
     * platforms it is impossible to read from a device after EOF.
2451
     */
2452
 
2453
    if (chanPtr->flags & CHANNEL_EOF) {
2454
        return 0;
2455
    }
2456
 
2457
    nread = (chanPtr->typePtr->inputProc) (chanPtr->instanceData,
2458
            bufPtr->buf + bufPtr->nextAdded, toRead, &result);
2459
 
2460
    if (nread == 0) {
2461
        chanPtr->flags |= CHANNEL_EOF;
2462
    } else if (nread < 0) {
2463
        if ((result == EWOULDBLOCK) || (result == EAGAIN)) {
2464
            chanPtr->flags |= CHANNEL_BLOCKED;
2465
            result = EAGAIN;
2466
            if (chanPtr->flags & CHANNEL_NONBLOCKING) {
2467
                Tcl_SetErrno(result);
2468
            } else {
2469
                panic("Blocking channel driver did not block on input");
2470
            }
2471
        } else {
2472
            Tcl_SetErrno(result);
2473
        }
2474
        return result;
2475
    } else {
2476
        bufPtr->nextAdded += nread;
2477
 
2478
        /*
2479
         * If we get a short read, signal up that we may be BLOCKED. We
2480
         * should avoid calling the driver because on some platforms we
2481
         * will block in the low level reading code even though the
2482
         * channel is set into nonblocking mode.
2483
         */
2484
 
2485
        if (nread < toRead) {
2486
            chanPtr->flags |= CHANNEL_BLOCKED;
2487
        }
2488
    }
2489
    return 0;
2490
}
2491
 
2492
/*
2493
 *----------------------------------------------------------------------
2494
 *
2495
 * CopyAndTranslateBuffer --
2496
 *
2497
 *      Copy at most one buffer of input to the result space, doing
2498
 *      eol translations according to mode in effect currently.
2499
 *
2500
 * Results:
2501
 *      Number of characters (as opposed to bytes) copied. May return
2502
 *      zero if no input is available to be translated.
2503
 *
2504
 * Side effects:
2505
 *      Consumes buffered input. May deallocate one buffer.
2506
 *
2507
 *----------------------------------------------------------------------
2508
 */
2509
 
2510
static int
2511
CopyAndTranslateBuffer(chanPtr, result, space)
2512
    Channel *chanPtr;           /* The channel from which to read input. */
2513
    char *result;               /* Where to store the copied input. */
2514
    int space;                  /* How many bytes are available in result
2515
                                 * to store the copied input? */
2516
{
2517
    int bytesInBuffer;          /* How many bytes are available to be
2518
                                 * copied in the current input buffer? */
2519
    int copied;                 /* How many characters were already copied
2520
                                 * into the destination space? */
2521
    ChannelBuffer *bufPtr;      /* The buffer from which to copy bytes. */
2522
    char curByte;               /* The byte we are currently translating. */
2523
    int i;                      /* Iterates over the copied input looking
2524
                                 * for the input eofChar. */
2525
 
2526
    /*
2527
     * If there is no input at all, return zero. The invariant is that either
2528
     * there is no buffer in the queue, or if the first buffer is empty, it
2529
     * is also the last buffer (and thus there is no input in the queue).
2530
     * Note also that if the buffer is empty, we leave it in the queue.
2531
     */
2532
 
2533
    if (chanPtr->inQueueHead == (ChannelBuffer *) NULL) {
2534
        return 0;
2535
    }
2536
    bufPtr = chanPtr->inQueueHead;
2537
    bytesInBuffer = bufPtr->nextAdded - bufPtr->nextRemoved;
2538
    if (bytesInBuffer < space) {
2539
        space = bytesInBuffer;
2540
    }
2541
    copied = 0;
2542
    switch (chanPtr->inputTranslation) {
2543
        case TCL_TRANSLATE_LF:
2544
 
2545
            if (space == 0) {
2546
                return 0;
2547
            }
2548
 
2549
            /*
2550
             * Copy the current chunk into the result buffer.
2551
             */
2552
 
2553
            memcpy((VOID *) result,
2554
                    (VOID *)(bufPtr->buf + bufPtr->nextRemoved),
2555
                    (size_t) space);
2556
            bufPtr->nextRemoved += space;
2557
            copied = space;
2558
            break;
2559
 
2560
        case TCL_TRANSLATE_CR:
2561
 
2562
            if (space == 0) {
2563
                return 0;
2564
            }
2565
 
2566
            /*
2567
             * Copy the current chunk into the result buffer, then
2568
             * replace all \r with \n.
2569
             */
2570
 
2571
            memcpy((VOID *) result,
2572
                    (VOID *)(bufPtr->buf + bufPtr->nextRemoved),
2573
                    (size_t) space);
2574
            bufPtr->nextRemoved += space;
2575
            for (copied = 0; copied < space; copied++) {
2576
                if (result[copied] == '\r') {
2577
                    result[copied] = '\n';
2578
                }
2579
            }
2580
            break;
2581
 
2582
        case TCL_TRANSLATE_CRLF:
2583
 
2584
            /*
2585
             * If there is a held-back "\r" at EOF, produce it now.
2586
             */
2587
 
2588
            if (space == 0) {
2589
                if ((chanPtr->flags & (INPUT_SAW_CR | CHANNEL_EOF)) ==
2590
                        (INPUT_SAW_CR | CHANNEL_EOF)) {
2591
                    result[0] = '\r';
2592
                    chanPtr->flags &= (~(INPUT_SAW_CR));
2593
                    return 1;
2594
                }
2595
                return 0;
2596
            }
2597
 
2598
            /*
2599
             * Copy the current chunk and replace "\r\n" with "\n"
2600
             * (but not standalone "\r"!).
2601
             */
2602
 
2603
            for (copied = 0;
2604
                     (copied < space) &&
2605
                         (bufPtr->nextRemoved < bufPtr->nextAdded);
2606
                     copied++) {
2607
                curByte = bufPtr->buf[bufPtr->nextRemoved];
2608
                bufPtr->nextRemoved++;
2609
                if (curByte == '\r') {
2610
                    if (chanPtr->flags & INPUT_SAW_CR) {
2611
                        result[copied] = '\r';
2612
                    } else {
2613
                        chanPtr->flags |= INPUT_SAW_CR;
2614
                        copied--;
2615
                    }
2616
                } else if (curByte == '\n') {
2617
                    chanPtr->flags &= (~(INPUT_SAW_CR));
2618
                    result[copied] = '\n';
2619
                } else {
2620
                    if (chanPtr->flags & INPUT_SAW_CR) {
2621
                        chanPtr->flags &= (~(INPUT_SAW_CR));
2622
                        result[copied] = '\r';
2623
                        bufPtr->nextRemoved--;
2624
                    } else {
2625
                        result[copied] = curByte;
2626
                    }
2627
                }
2628
            }
2629
            break;
2630
 
2631
        case TCL_TRANSLATE_AUTO:
2632
 
2633
            if (space == 0) {
2634
                return 0;
2635
            }
2636
 
2637
            /*
2638
             * Loop over the current buffer, converting "\r" and "\r\n"
2639
             * to "\n".
2640
             */
2641
 
2642
            for (copied = 0;
2643
                     (copied < space) &&
2644
                         (bufPtr->nextRemoved < bufPtr->nextAdded); ) {
2645
                curByte = bufPtr->buf[bufPtr->nextRemoved];
2646
                bufPtr->nextRemoved++;
2647
                if (curByte == '\r') {
2648
                    result[copied] = '\n';
2649
                    copied++;
2650
                    if (bufPtr->nextRemoved < bufPtr->nextAdded) {
2651
                        if (bufPtr->buf[bufPtr->nextRemoved] == '\n') {
2652
                            bufPtr->nextRemoved++;
2653
                        }
2654
                        chanPtr->flags &= (~(INPUT_SAW_CR));
2655
                    } else {
2656
                        chanPtr->flags |= INPUT_SAW_CR;
2657
                    }
2658
                } else {
2659
                    if (curByte == '\n') {
2660
                        if (!(chanPtr->flags & INPUT_SAW_CR)) {
2661
                            result[copied] = '\n';
2662
                            copied++;
2663
                        }
2664
                    } else {
2665
                        result[copied] = curByte;
2666
                        copied++;
2667
                    }
2668
                    chanPtr->flags &= (~(INPUT_SAW_CR));
2669
                }
2670
            }
2671
            break;
2672
 
2673
        default:
2674
            panic("unknown eol translation mode");
2675
    }
2676
 
2677
    /*
2678
     * If an in-stream EOF character is set for this channel,, check that
2679
     * the input we copied so far does not contain the EOF char. If it does,
2680
     * copy only up to and excluding that character.
2681
     */
2682
 
2683
    if (chanPtr->inEofChar != 0) {
2684
        for (i = 0; i < copied; i++) {
2685
            if (result[i] == (char) chanPtr->inEofChar) {
2686
                break;
2687
            }
2688
        }
2689
        if (i < copied) {
2690
 
2691
            /*
2692
             * Set sticky EOF so that no further input is presented
2693
             * to the caller.
2694
             */
2695
 
2696
            chanPtr->flags |= (CHANNEL_EOF | CHANNEL_STICKY_EOF);
2697
 
2698
            /*
2699
             * Reset the start of valid data in the input buffer to the
2700
             * position of the eofChar, so that subsequent reads will
2701
             * encounter it immediately. First we set it to the position
2702
             * of the last byte consumed if all result bytes were the
2703
             * product of one input byte; since it is possible that "\r\n"
2704
             * contracted to "\n" in the result, we have to search back
2705
             * from that position until we find the eofChar, because it
2706
             * is possible that its actual position in the buffer is n
2707
             * bytes further back (n is the number of "\r\n" sequences
2708
             * that were contracted to "\n" in the result).
2709
             */
2710
 
2711
            bufPtr->nextRemoved -= (copied - i);
2712
            while ((bufPtr->nextRemoved > 0) &&
2713
                    (bufPtr->buf[bufPtr->nextRemoved] !=
2714
                            (char) chanPtr->inEofChar)) {
2715
                bufPtr->nextRemoved--;
2716
            }
2717
            copied = i;
2718
        }
2719
    }
2720
 
2721
    /*
2722
     * If the current buffer is empty recycle it.
2723
     */
2724
 
2725
    if (bufPtr->nextRemoved == bufPtr->nextAdded) {
2726
        chanPtr->inQueueHead = bufPtr->nextPtr;
2727
        if (chanPtr->inQueueHead == (ChannelBuffer *) NULL) {
2728
            chanPtr->inQueueTail = (ChannelBuffer *) NULL;
2729
        }
2730
        RecycleBuffer(chanPtr, bufPtr, 0);
2731
    }
2732
 
2733
    /*
2734
     * Return the number of characters copied into the result buffer.
2735
     * This may be different from the number of bytes consumed, because
2736
     * of EOL translations.
2737
     */
2738
 
2739
    return copied;
2740
}
2741
 
2742
/*
2743
 *----------------------------------------------------------------------
2744
 *
2745
 * ScanBufferForEOL --
2746
 *
2747
 *      Scans one buffer for EOL according to the specified EOL
2748
 *      translation mode. If it sees the input eofChar for the channel
2749
 *      it stops also.
2750
 *
2751
 * Results:
2752
 *      TRUE if EOL is found, FALSE otherwise. Also sets output parameter
2753
 *      bytesToEOLPtr to the number of bytes so far to EOL, and crSeenPtr
2754
 *      to whether a "\r" was seen.
2755
 *
2756
 * Side effects:
2757
 *      None.
2758
 *
2759
 *----------------------------------------------------------------------
2760
 */
2761
 
2762
static int
2763
ScanBufferForEOL(chanPtr, bufPtr, translation, eofChar, bytesToEOLPtr,
2764
                 crSeenPtr)
2765
    Channel *chanPtr;
2766
    ChannelBuffer *bufPtr;              /* Buffer to scan for EOL. */
2767
    Tcl_EolTranslation translation;     /* Translation mode to use. */
2768
    int eofChar;                        /* EOF char to look for. */
2769
    int *bytesToEOLPtr;                 /* Running counter. */
2770
    int *crSeenPtr;                     /* Has "\r" been seen? */
2771
{
2772
    char *rPtr;                         /* Iterates over input string. */
2773
    char *sPtr;                         /* Where to stop search? */
2774
    int EOLFound;
2775
    int bytesToEOL;
2776
 
2777
    for (EOLFound = 0, rPtr = bufPtr->buf + bufPtr->nextRemoved,
2778
             sPtr = bufPtr->buf + bufPtr->nextAdded,
2779
             bytesToEOL = *bytesToEOLPtr;
2780
             (!EOLFound) && (rPtr < sPtr);
2781
             rPtr++) {
2782
        switch (translation) {
2783
            case TCL_TRANSLATE_AUTO:
2784
                if ((*rPtr == (char) eofChar) && (eofChar != 0)) {
2785
                    chanPtr->flags |= (CHANNEL_EOF | CHANNEL_STICKY_EOF);
2786
                    EOLFound = 1;
2787
                } else if (*rPtr == '\n') {
2788
 
2789
                    /*
2790
                     * CopyAndTranslateBuffer wants to know the length
2791
                     * of the result, not the input. The input is one
2792
                     * larger because "\r\n" shrinks to "\n".
2793
                     */
2794
 
2795
                    if (!(*crSeenPtr)) {
2796
                        bytesToEOL++;
2797
                        EOLFound = 1;
2798
                    } else {
2799
 
2800
                        /*
2801
                         * This is a lf at the begining of a buffer
2802
                         * where the previous buffer ended in a cr.
2803
                         * Consume this lf because we've already emitted
2804
                         * the newline for this crlf sequence. ALSO, if
2805
                         * bytesToEOL is 0 (which means that we are at the
2806
                         * first character of the scan), unset the
2807
                         * INPUT_SAW_CR flag in the channel, because we
2808
                         * already handled it; leaving it set would cause
2809
                         * CopyAndTranslateBuffer to potentially consume
2810
                         * another lf if one follows the current byte.
2811
                         */
2812
 
2813
                        bufPtr->nextRemoved++;
2814
                        *crSeenPtr = 0;
2815
                        chanPtr->flags &= (~(INPUT_SAW_CR));
2816
                    }
2817
                } else if (*rPtr == '\r') {
2818
                    bytesToEOL++;
2819
                    EOLFound = 1;
2820
                } else {
2821
                    *crSeenPtr = 0;
2822
                    bytesToEOL++;
2823
                }
2824
                break;
2825
            case TCL_TRANSLATE_LF:
2826
                if ((*rPtr == (char) eofChar) && (eofChar != 0)) {
2827
                    chanPtr->flags |= (CHANNEL_EOF | CHANNEL_STICKY_EOF);
2828
                    EOLFound = 1;
2829
                } else {
2830
                    if (*rPtr == '\n') {
2831
                        EOLFound = 1;
2832
                    }
2833
                    bytesToEOL++;
2834
                }
2835
                break;
2836
            case TCL_TRANSLATE_CR:
2837
                if ((*rPtr == (char) eofChar) && (eofChar != 0)) {
2838
                    chanPtr->flags |= (CHANNEL_EOF | CHANNEL_STICKY_EOF);
2839
                    EOLFound = 1;
2840
                } else {
2841
                    if (*rPtr == '\r') {
2842
                        EOLFound = 1;
2843
                    }
2844
                    bytesToEOL++;
2845
                }
2846
                break;
2847
            case TCL_TRANSLATE_CRLF:
2848
                if ((*rPtr == (char) eofChar) && (eofChar != 0)) {
2849
                    chanPtr->flags |= (CHANNEL_EOF | CHANNEL_STICKY_EOF);
2850
                    EOLFound = 1;
2851
                } else if (*rPtr == '\n') {
2852
 
2853
                    /*
2854
                     * CopyAndTranslateBuffer wants to know the length
2855
                     * of the result, not the input. The input is one
2856
                     * larger because crlf shrinks to lf.
2857
                     */
2858
 
2859
                    if (*crSeenPtr) {
2860
                        EOLFound = 1;
2861
                    } else {
2862
                        bytesToEOL++;
2863
                    }
2864
                } else {
2865
                    if (*rPtr == '\r') {
2866
                        *crSeenPtr = 1;
2867
                    } else {
2868
                        *crSeenPtr = 0;
2869
                    }
2870
                    bytesToEOL++;
2871
                }
2872
                break;
2873
            default:
2874
                panic("unknown eol translation mode");
2875
        }
2876
    }
2877
 
2878
    *bytesToEOLPtr = bytesToEOL;
2879
    return EOLFound;
2880
}
2881
 
2882
/*
2883
 *----------------------------------------------------------------------
2884
 *
2885
 * ScanInputForEOL --
2886
 *
2887
 *      Scans queued input for chanPtr for an end of line (according to the
2888
 *      current EOL translation mode) and returns the number of bytes
2889
 *      upto and including the end of line, or -1 if none was found.
2890
 *
2891
 * Results:
2892
 *      Count of bytes upto and including the end of line if one is present
2893
 *      or -1 if none was found. Also returns in an output parameter the
2894
 *      number of bytes queued if no end of line was found.
2895
 *
2896
 * Side effects:
2897
 *      None.
2898
 *
2899
 *----------------------------------------------------------------------
2900
 */
2901
 
2902
static int
2903
ScanInputForEOL(chanPtr, bytesQueuedPtr)
2904
    Channel *chanPtr;   /* Channel for which to scan queued
2905
                                 * input for end of line. */
2906
    int *bytesQueuedPtr;        /* Where to store the number of bytes
2907
                                 * currently queued if no end of line
2908
                                 * was found. */
2909
{
2910
    ChannelBuffer *bufPtr;      /* Iterates over queued buffers. */
2911
    int bytesToEOL;             /* How many bytes to end of line? */
2912
    int EOLFound;               /* Did we find an end of line? */
2913
    int crSeen;                 /* Did we see a "\r" in CRLF mode? */
2914
 
2915
    *bytesQueuedPtr = 0;
2916
    bytesToEOL = 0;
2917
    EOLFound = 0;
2918
    for (bufPtr = chanPtr->inQueueHead,
2919
             crSeen = (chanPtr->flags & INPUT_SAW_CR) ? 1 : 0;
2920
            (!EOLFound) && (bufPtr != (ChannelBuffer *) NULL);
2921
            bufPtr = bufPtr->nextPtr) {
2922
        EOLFound = ScanBufferForEOL(chanPtr, bufPtr, chanPtr->inputTranslation,
2923
                chanPtr->inEofChar, &bytesToEOL, &crSeen);
2924
    }
2925
 
2926
    if (EOLFound == 0) {
2927
        *bytesQueuedPtr = bytesToEOL;
2928
        return -1;
2929
    }
2930
    return bytesToEOL;
2931
}
2932
 
2933
/*
2934
 *----------------------------------------------------------------------
2935
 *
2936
 * GetEOL --
2937
 *
2938
 *      Accumulate input into the channel input buffer queue until an
2939
 *      end of line has been seen.
2940
 *
2941
 * Results:
2942
 *      Number of bytes buffered (at least 1) or -1 on failure.
2943
 *
2944
 * Side effects:
2945
 *      Consumes input from the channel.
2946
 *
2947
 *----------------------------------------------------------------------
2948
 */
2949
 
2950
static int
2951
GetEOL(chanPtr)
2952
    Channel *chanPtr;   /* Channel to queue input on. */
2953
{
2954
    int bytesToEOL;             /* How many bytes in buffer up to and
2955
                                 * including the end of line? */
2956
    int bytesQueued;            /* How many bytes are queued currently
2957
                                 * in the input chain of the channel? */
2958
 
2959
    /*
2960
     * Check for unreported error.
2961
     */
2962
 
2963
    if (chanPtr->unreportedError != 0) {
2964
        Tcl_SetErrno(chanPtr->unreportedError);
2965
        chanPtr->unreportedError = 0;
2966
        return -1;
2967
    }
2968
 
2969
    /*
2970
     * Punt if the channel is not opened for reading.
2971
     */
2972
 
2973
    if (!(chanPtr->flags & TCL_READABLE)) {
2974
        Tcl_SetErrno(EACCES);
2975
        return -1;
2976
    }
2977
 
2978
    /*
2979
     * If the channel is in the middle of a background copy, fail.
2980
     */
2981
 
2982
    if (chanPtr->csPtr) {
2983
        Tcl_SetErrno(EBUSY);
2984
        return -1;
2985
    }
2986
 
2987
    /*
2988
     * If we have not encountered a sticky EOF, clear the EOF bit
2989
     * (sticky EOF is set if we have seen the input eofChar, to prevent
2990
     * reading beyond the eofChar). Also, always clear the BLOCKED bit.
2991
     * We want to discover these conditions anew in each operation.
2992
     */
2993
 
2994
    if (!(chanPtr->flags & CHANNEL_STICKY_EOF)) {
2995
        chanPtr->flags &= (~(CHANNEL_EOF));
2996
    }
2997
    chanPtr->flags &= (~(CHANNEL_BLOCKED | CHANNEL_GETS_BLOCKED));
2998
 
2999
    while (1) {
3000
        bytesToEOL = ScanInputForEOL(chanPtr, &bytesQueued);
3001
        if (bytesToEOL > 0) {
3002
            chanPtr->flags &= (~(CHANNEL_BLOCKED));
3003
            return bytesToEOL;
3004
        }
3005
        if (chanPtr->flags & CHANNEL_EOF) {
3006
            /*
3007
             * Boundary case where cr was at the end of the previous buffer
3008
             * and this buffer just has a newline.  At EOF our caller wants
3009
             * to see -1 for the line length.
3010
             */
3011
            return (bytesQueued == 0) ? -1 : bytesQueued ;
3012
        }
3013
        if (chanPtr->flags & CHANNEL_BLOCKED) {
3014
            if (chanPtr->flags & CHANNEL_NONBLOCKING) {
3015
                goto blocked;
3016
            }
3017
            chanPtr->flags &= (~(CHANNEL_BLOCKED));
3018
        }
3019
        if (GetInput(chanPtr) != 0) {
3020
            goto blocked;
3021
        }
3022
    }
3023
 
3024
    blocked:
3025
 
3026
    /*
3027
     * We didn't get a complete line so we need to indicate to UpdateInterest
3028
     * that the gets blocked.  It will wait for more data instead of firing
3029
     * a timer, avoiding a busy wait.  This is where we are assuming that the
3030
     * next operation is a gets.  No more file events will be delivered on
3031
     * this channel until new data arrives or some operation is performed
3032
     * on the channel (e.g. gets, read, fconfigure) that changes the blocking
3033
     * state.  Note that this means a file event will not be delivered even
3034
     * though a read would be able to consume the buffered data.
3035
     */
3036
 
3037
    chanPtr->flags |= CHANNEL_GETS_BLOCKED;
3038
    return -1;
3039
}
3040
 
3041
/*
3042
 *----------------------------------------------------------------------
3043
 *
3044
 * Tcl_Read --
3045
 *
3046
 *      Reads a given number of characters from a channel.
3047
 *
3048
 * Results:
3049
 *      The number of characters read, or -1 on error. Use Tcl_GetErrno()
3050
 *      to retrieve the error code for the error that occurred.
3051
 *
3052
 * Side effects:
3053
 *      May cause input to be buffered.
3054
 *
3055
 *----------------------------------------------------------------------
3056
 */
3057
 
3058
int
3059
Tcl_Read(chan, bufPtr, toRead)
3060
    Tcl_Channel chan;           /* The channel from which to read. */
3061
    char *bufPtr;               /* Where to store input read. */
3062
    int toRead;                 /* Maximum number of characters to read. */
3063
{
3064
    Channel *chanPtr;           /* The real IO channel. */
3065
 
3066
    chanPtr = (Channel *) chan;
3067
 
3068
    /*
3069
     * Check for unreported error.
3070
     */
3071
 
3072
    if (chanPtr->unreportedError != 0) {
3073
        Tcl_SetErrno(chanPtr->unreportedError);
3074
        chanPtr->unreportedError = 0;
3075
        return -1;
3076
    }
3077
 
3078
    /*
3079
     * Punt if the channel is not opened for reading.
3080
     */
3081
 
3082
    if (!(chanPtr->flags & TCL_READABLE)) {
3083
        Tcl_SetErrno(EACCES);
3084
        return -1;
3085
    }
3086
 
3087
    /*
3088
     * If the channel is in the middle of a background copy, fail.
3089
     */
3090
 
3091
    if (chanPtr->csPtr) {
3092
        Tcl_SetErrno(EBUSY);
3093
        return -1;
3094
    }
3095
 
3096
    return DoRead(chanPtr, bufPtr, toRead);
3097
}
3098
 
3099
/*
3100
 *----------------------------------------------------------------------
3101
 *
3102
 * DoRead --
3103
 *
3104
 *      Reads a given number of characters from a channel.
3105
 *
3106
 * Results:
3107
 *      The number of characters read, or -1 on error. Use Tcl_GetErrno()
3108
 *      to retrieve the error code for the error that occurred.
3109
 *
3110
 * Side effects:
3111
 *      May cause input to be buffered.
3112
 *
3113
 *----------------------------------------------------------------------
3114
 */
3115
 
3116
static int
3117
DoRead(chanPtr, bufPtr, toRead)
3118
    Channel *chanPtr;           /* The channel from which to read. */
3119
    char *bufPtr;               /* Where to store input read. */
3120
    int toRead;                 /* Maximum number of characters to read. */
3121
{
3122
    int copied;                 /* How many characters were copied into
3123
                                 * the result string? */
3124
    int copiedNow;              /* How many characters were copied from
3125
                                 * the current input buffer? */
3126
    int result;                 /* Of calling GetInput. */
3127
 
3128
    /*
3129
     * If we have not encountered a sticky EOF, clear the EOF bit. Either
3130
     * way clear the BLOCKED bit. We want to discover these anew during
3131
     * each operation.
3132
     */
3133
 
3134
    if (!(chanPtr->flags & CHANNEL_STICKY_EOF)) {
3135
        chanPtr->flags &= (~(CHANNEL_EOF));
3136
    }
3137
    chanPtr->flags &= (~(CHANNEL_BLOCKED | CHANNEL_GETS_BLOCKED));
3138
 
3139
    for (copied = 0; copied < toRead; copied += copiedNow) {
3140
        copiedNow = CopyAndTranslateBuffer(chanPtr, bufPtr + copied,
3141
                toRead - copied);
3142
        if (copiedNow == 0) {
3143
            if (chanPtr->flags & CHANNEL_EOF) {
3144
                goto done;
3145
            }
3146
            if (chanPtr->flags & CHANNEL_BLOCKED) {
3147
                if (chanPtr->flags & CHANNEL_NONBLOCKING) {
3148
                    goto done;
3149
                }
3150
                chanPtr->flags &= (~(CHANNEL_BLOCKED));
3151
            }
3152
            result = GetInput(chanPtr);
3153
            if (result != 0) {
3154
                if (result != EAGAIN) {
3155
                    copied = -1;
3156
                }
3157
                goto done;
3158
            }
3159
        }
3160
    }
3161
 
3162
    chanPtr->flags &= (~(CHANNEL_BLOCKED));
3163
 
3164
    done:
3165
    /*
3166
     * Update the notifier state so we don't block while there is still
3167
     * data in the buffers.
3168
     */
3169
 
3170
    UpdateInterest(chanPtr);
3171
    return copied;
3172
}
3173
 
3174
/*
3175
 *----------------------------------------------------------------------
3176
 *
3177
 * Tcl_Gets --
3178
 *
3179
 *      Reads a complete line of input from the channel into a
3180
 *      Tcl_DString.
3181
 *
3182
 * Results:
3183
 *      Length of line read or -1 if error, EOF or blocked. If -1, use
3184
 *      Tcl_GetErrno() to retrieve the POSIX error code for the
3185
 *      error or condition that occurred.
3186
 *
3187
 * Side effects:
3188
 *      May flush output on the channel. May cause input to be
3189
 *      consumed from the channel.
3190
 *
3191
 *----------------------------------------------------------------------
3192
 */
3193
 
3194
int
3195
Tcl_Gets(chan, lineRead)
3196
    Tcl_Channel chan;           /* Channel from which to read. */
3197
    Tcl_DString *lineRead;      /* The characters of the line read
3198
                                 * (excluding the terminating newline if
3199
                                 * present) will be appended to this
3200
                                 * DString. The caller must have initialized
3201
                                 * it and is responsible for managing the
3202
                                 * storage. */
3203
{
3204
    Channel *chanPtr;           /* The channel to read from. */
3205
    char *buf;                  /* Points into DString where data
3206
                                 * will be stored. */
3207
    int offset;                 /* Offset from start of DString at
3208
                                 * which to append the line just read. */
3209
    int copiedTotal;            /* Accumulates total length of input copied. */
3210
    int copiedNow;              /* How many bytes were copied from the
3211
                                 * current input buffer? */
3212
    int lineLen;                /* Length of line read, including the
3213
                                 * translated newline. If this is zero
3214
                                 * and neither EOF nor BLOCKED is set,
3215
                                 * the current line is empty. */
3216
 
3217
    chanPtr = (Channel *) chan;
3218
 
3219
    lineLen = GetEOL(chanPtr);
3220
    if (lineLen < 0) {
3221
        copiedTotal = -1;
3222
        goto done;
3223
    }
3224
    offset = Tcl_DStringLength(lineRead);
3225
    Tcl_DStringSetLength(lineRead, lineLen + offset);
3226
    buf = Tcl_DStringValue(lineRead) + offset;
3227
 
3228
    for (copiedTotal = 0; copiedTotal < lineLen; copiedTotal += copiedNow) {
3229
        copiedNow = CopyAndTranslateBuffer(chanPtr, buf + copiedTotal,
3230
                lineLen - copiedTotal);
3231
    }
3232
    if ((copiedTotal > 0) && (buf[copiedTotal - 1] == '\n')) {
3233
        copiedTotal--;
3234
    }
3235
    Tcl_DStringSetLength(lineRead, copiedTotal + offset);
3236
 
3237
    done:
3238
    /*
3239
     * Update the notifier state so we don't block while there is still
3240
     * data in the buffers.
3241
     */
3242
 
3243
    UpdateInterest(chanPtr);
3244
    return copiedTotal;
3245
}
3246
 
3247
/*
3248
 *----------------------------------------------------------------------
3249
 *
3250
 * Tcl_GetsObj --
3251
 *
3252
 *      Reads a complete line of input from the channel into a
3253
 *      string object.
3254
 *
3255
 * Results:
3256
 *      Length of line read or -1 if error, EOF or blocked. If -1, use
3257
 *      Tcl_GetErrno() to retrieve the POSIX error code for the
3258
 *      error or condition that occurred.
3259
 *
3260
 * Side effects:
3261
 *      May flush output on the channel. May cause input to be
3262
 *      consumed from the channel.
3263
 *
3264
 *----------------------------------------------------------------------
3265
 */
3266
 
3267
int
3268
Tcl_GetsObj(chan, objPtr)
3269
    Tcl_Channel chan;           /* Channel from which to read. */
3270
    Tcl_Obj *objPtr;            /* The characters of the line read
3271
                                 * (excluding the terminating newline if
3272
                                 * present) will be appended to this
3273
                                 * object. The caller must have initialized
3274
                                 * it and is responsible for managing the
3275
                                 * storage. */
3276
{
3277
    Channel *chanPtr;           /* The channel to read from. */
3278
    char *buf;                  /* Points into DString where data
3279
                                 * will be stored. */
3280
    int offset;                 /* Offset from start of DString at
3281
                                 * which to append the line just read. */
3282
    int copiedTotal;            /* Accumulates total length of input copied. */
3283
    int copiedNow;              /* How many bytes were copied from the
3284
                                 * current input buffer? */
3285
    int lineLen;                /* Length of line read, including the
3286
                                 * translated newline. If this is zero
3287
                                 * and neither EOF nor BLOCKED is set,
3288
                                 * the current line is empty. */
3289
 
3290
    chanPtr = (Channel *) chan;
3291
 
3292
    lineLen = GetEOL(chanPtr);
3293
    if (lineLen < 0) {
3294
        copiedTotal = -1;
3295
        goto done;
3296
    }
3297
 
3298
    (void) Tcl_GetStringFromObj(objPtr, &offset);
3299
    Tcl_SetObjLength(objPtr, lineLen + offset);
3300
    buf = Tcl_GetStringFromObj(objPtr, NULL) + offset;
3301
 
3302
    for (copiedTotal = 0; copiedTotal < lineLen; copiedTotal += copiedNow) {
3303
        copiedNow = CopyAndTranslateBuffer(chanPtr, buf + copiedTotal,
3304
                lineLen - copiedTotal);
3305
    }
3306
    if ((copiedTotal > 0) && (buf[copiedTotal - 1] == '\n')) {
3307
        copiedTotal--;
3308
    }
3309
    Tcl_SetObjLength(objPtr, copiedTotal + offset);
3310
 
3311
    done:
3312
    /*
3313
     * Update the notifier state so we don't block while there is still
3314
     * data in the buffers.
3315
     */
3316
 
3317
    UpdateInterest(chanPtr);
3318
    return copiedTotal;
3319
}
3320
 
3321
/*
3322
 *----------------------------------------------------------------------
3323
 *
3324
 * Tcl_Ungets --
3325
 *
3326
 *      Causes the supplied string to be added to the input queue of
3327
 *      the channel, at either the head or tail of the queue.
3328
 *
3329
 * Results:
3330
 *      The number of bytes stored in the channel, or -1 on error.
3331
 *
3332
 * Side effects:
3333
 *      Adds input to the input queue of a channel.
3334
 *
3335
 *----------------------------------------------------------------------
3336
 */
3337
 
3338
int
3339
Tcl_Ungets(chan, str, len, atEnd)
3340
    Tcl_Channel chan;           /* The channel for which to add the input. */
3341
    char *str;                  /* The input itself. */
3342
    int len;                    /* The length of the input. */
3343
    int atEnd;                  /* If non-zero, add at end of queue; otherwise
3344
                                 * add at head of queue. */
3345
{
3346
    Channel *chanPtr;           /* The real IO channel. */
3347
    ChannelBuffer *bufPtr;      /* Buffer to contain the data. */
3348
    int i;
3349
 
3350
    chanPtr = (Channel *) chan;
3351
 
3352
    /*
3353
     * Check for unreported error.
3354
     */
3355
 
3356
    if (chanPtr->unreportedError != 0) {
3357
        Tcl_SetErrno(chanPtr->unreportedError);
3358
        chanPtr->unreportedError = 0;
3359
        return -1;
3360
    }
3361
 
3362
    /*
3363
     * Punt if the channel is not opened for reading.
3364
     */
3365
 
3366
    if (!(chanPtr->flags & TCL_READABLE)) {
3367
        Tcl_SetErrno(EACCES);
3368
        return -1;
3369
    }
3370
 
3371
    /*
3372
     * If the channel is in the middle of a background copy, fail.
3373
     */
3374
 
3375
    if (chanPtr->csPtr) {
3376
        Tcl_SetErrno(EBUSY);
3377
        return -1;
3378
    }
3379
 
3380
    /*
3381
     * If we have encountered a sticky EOF, just punt without storing.
3382
     * (sticky EOF is set if we have seen the input eofChar, to prevent
3383
     * reading beyond the eofChar). Otherwise, clear the EOF flags, and
3384
     * clear the BLOCKED bit. We want to discover these conditions anew
3385
     * in each operation.
3386
     */
3387
 
3388
    if (chanPtr->flags & CHANNEL_STICKY_EOF) {
3389
        return len;
3390
    }
3391
    chanPtr->flags &= (~(CHANNEL_BLOCKED | CHANNEL_EOF));
3392
 
3393
    bufPtr = (ChannelBuffer *) ckalloc((unsigned)
3394
            (CHANNELBUFFER_HEADER_SIZE + len));
3395
    for (i = 0; i < len; i++) {
3396
        bufPtr->buf[i] = str[i];
3397
    }
3398
    bufPtr->bufSize = len;
3399
    bufPtr->nextAdded = len;
3400
    bufPtr->nextRemoved = 0;
3401
 
3402
    if (chanPtr->inQueueHead == (ChannelBuffer *) NULL) {
3403
        bufPtr->nextPtr = (ChannelBuffer *) NULL;
3404
        chanPtr->inQueueHead = bufPtr;
3405
        chanPtr->inQueueTail = bufPtr;
3406
    } else if (atEnd) {
3407
        bufPtr->nextPtr = (ChannelBuffer *) NULL;
3408
        chanPtr->inQueueTail->nextPtr = bufPtr;
3409
        chanPtr->inQueueTail = bufPtr;
3410
    } else {
3411
        bufPtr->nextPtr = chanPtr->inQueueHead;
3412
        chanPtr->inQueueHead = bufPtr;
3413
    }
3414
 
3415
    /*
3416
     * Update the notifier state so we don't block while there is still
3417
     * data in the buffers.
3418
     */
3419
 
3420
    UpdateInterest(chanPtr);
3421
    return len;
3422
}
3423
 
3424
/*
3425
 *----------------------------------------------------------------------
3426
 *
3427
 * Tcl_Seek --
3428
 *
3429
 *      Implements seeking on Tcl Channels. This is a public function
3430
 *      so that other C facilities may be implemented on top of it.
3431
 *
3432
 * Results:
3433
 *      The new access point or -1 on error. If error, use Tcl_GetErrno()
3434
 *      to retrieve the POSIX error code for the error that occurred.
3435
 *
3436
 * Side effects:
3437
 *      May flush output on the channel. May discard queued input.
3438
 *
3439
 *----------------------------------------------------------------------
3440
 */
3441
 
3442
int
3443
Tcl_Seek(chan, offset, mode)
3444
    Tcl_Channel chan;           /* The channel on which to seek. */
3445
    int offset;                 /* Offset to seek to. */
3446
    int mode;                   /* Relative to which location to seek? */
3447
{
3448
    Channel *chanPtr;           /* The real IO channel. */
3449
    ChannelBuffer *bufPtr;
3450
    int inputBuffered, outputBuffered;
3451
    int result;                 /* Of device driver operations. */
3452
    int curPos;                 /* Position on the device. */
3453
    int wasAsync;               /* Was the channel nonblocking before the
3454
                                 * seek operation? If so, must restore to
3455
                                 * nonblocking mode after the seek. */
3456
 
3457
    chanPtr = (Channel *) chan;
3458
 
3459
    /*
3460
     * Check for unreported error.
3461
     */
3462
 
3463
    if (chanPtr->unreportedError != 0) {
3464
        Tcl_SetErrno(chanPtr->unreportedError);
3465
        chanPtr->unreportedError = 0;
3466
        return -1;
3467
    }
3468
 
3469
    /*
3470
     * Disallow seek on channels that are open for neither writing nor
3471
     * reading (e.g. socket server channels).
3472
     */
3473
 
3474
    if (!(chanPtr->flags & (TCL_WRITABLE|TCL_READABLE))) {
3475
        Tcl_SetErrno(EACCES);
3476
        return -1;
3477
    }
3478
 
3479
    /*
3480
     * If the channel is in the middle of a background copy, fail.
3481
     */
3482
 
3483
    if (chanPtr->csPtr) {
3484
        Tcl_SetErrno(EBUSY);
3485
        return -1;
3486
    }
3487
 
3488
    /*
3489
     * Disallow seek on dead channels -- channels that have been closed but
3490
     * not yet been deallocated. Such channels can be found if the exit
3491
     * handler for channel cleanup has run but the channel is still
3492
     * registered in an interpreter.
3493
     */
3494
 
3495
    if (CheckForDeadChannel(NULL,chanPtr)) return -1;
3496
 
3497
    /*
3498
     * Disallow seek on channels whose type does not have a seek procedure
3499
     * defined. This means that the channel does not support seeking.
3500
     */
3501
 
3502
    if (chanPtr->typePtr->seekProc == (Tcl_DriverSeekProc *) NULL) {
3503
        Tcl_SetErrno(EINVAL);
3504
        return -1;
3505
    }
3506
 
3507
    /*
3508
     * Compute how much input and output is buffered. If both input and
3509
     * output is buffered, cannot compute the current position.
3510
     */
3511
 
3512
    for (bufPtr = chanPtr->inQueueHead, inputBuffered = 0;
3513
             bufPtr != (ChannelBuffer *) NULL;
3514
             bufPtr = bufPtr->nextPtr) {
3515
        inputBuffered += (bufPtr->nextAdded - bufPtr->nextRemoved);
3516
    }
3517
    for (bufPtr = chanPtr->outQueueHead, outputBuffered = 0;
3518
             bufPtr != (ChannelBuffer *) NULL;
3519
             bufPtr = bufPtr->nextPtr) {
3520
        outputBuffered += (bufPtr->nextAdded - bufPtr->nextRemoved);
3521
    }
3522
    if ((chanPtr->curOutPtr != (ChannelBuffer *) NULL) &&
3523
           (chanPtr->curOutPtr->nextAdded > chanPtr->curOutPtr->nextRemoved)) {
3524
        chanPtr->flags |= BUFFER_READY;
3525
        outputBuffered +=
3526
            (chanPtr->curOutPtr->nextAdded - chanPtr->curOutPtr->nextRemoved);
3527
    }
3528
 
3529
    if ((inputBuffered != 0) && (outputBuffered != 0)) {
3530
        Tcl_SetErrno(EFAULT);
3531
        return -1;
3532
    }
3533
 
3534
    /*
3535
     * If we are seeking relative to the current position, compute the
3536
     * corrected offset taking into account the amount of unread input.
3537
     */
3538
 
3539
    if (mode == SEEK_CUR) {
3540
        offset -= inputBuffered;
3541
    }
3542
 
3543
    /*
3544
     * Discard any queued input - this input should not be read after
3545
     * the seek.
3546
     */
3547
 
3548
    DiscardInputQueued(chanPtr, 0);
3549
 
3550
    /*
3551
     * Reset EOF and BLOCKED flags. We invalidate them by moving the
3552
     * access point. Also clear CR related flags.
3553
     */
3554
 
3555
    chanPtr->flags &=
3556
        (~(CHANNEL_EOF | CHANNEL_STICKY_EOF | CHANNEL_BLOCKED | INPUT_SAW_CR));
3557
 
3558
    /*
3559
     * If the channel is in asynchronous output mode, switch it back
3560
     * to synchronous mode and cancel any async flush that may be
3561
     * scheduled. After the flush, the channel will be put back into
3562
     * asynchronous output mode.
3563
     */
3564
 
3565
    wasAsync = 0;
3566
    if (chanPtr->flags & CHANNEL_NONBLOCKING) {
3567
        wasAsync = 1;
3568
        result = 0;
3569
        if (chanPtr->typePtr->blockModeProc != NULL) {
3570
            result = (chanPtr->typePtr->blockModeProc) (chanPtr->instanceData,
3571
                    TCL_MODE_BLOCKING);
3572
        }
3573
        if (result != 0) {
3574
            Tcl_SetErrno(result);
3575
            return -1;
3576
        }
3577
        chanPtr->flags &= (~(CHANNEL_NONBLOCKING));
3578
        if (chanPtr->flags & BG_FLUSH_SCHEDULED) {
3579
            chanPtr->flags &= (~(BG_FLUSH_SCHEDULED));
3580
        }
3581
    }
3582
 
3583
    /*
3584
     * If the flush fails we cannot recover the original position. In
3585
     * that case the seek is not attempted because we do not know where
3586
     * the access position is - instead we return the error. FlushChannel
3587
     * has already called Tcl_SetErrno() to report the error upwards.
3588
     * If the flush succeeds we do the seek also.
3589
     */
3590
 
3591
    if (FlushChannel(NULL, chanPtr, 0) != 0) {
3592
        curPos = -1;
3593
    } else {
3594
 
3595
        /*
3596
         * Now seek to the new position in the channel as requested by the
3597
         * caller.
3598
         */
3599
 
3600
        curPos = (chanPtr->typePtr->seekProc) (chanPtr->instanceData,
3601
                (long) offset, mode, &result);
3602
        if (curPos == -1) {
3603
            Tcl_SetErrno(result);
3604
        }
3605
    }
3606
 
3607
    /*
3608
     * Restore to nonblocking mode if that was the previous behavior.
3609
     *
3610
     * NOTE: Even if there was an async flush active we do not restore
3611
     * it now because we already flushed all the queued output, above.
3612
     */
3613
 
3614
    if (wasAsync) {
3615
        chanPtr->flags |= CHANNEL_NONBLOCKING;
3616
        result = 0;
3617
        if (chanPtr->typePtr->blockModeProc != NULL) {
3618
            result = (chanPtr->typePtr->blockModeProc) (chanPtr->instanceData,
3619
                    TCL_MODE_NONBLOCKING);
3620
        }
3621
        if (result != 0) {
3622
            Tcl_SetErrno(result);
3623
            return -1;
3624
        }
3625
    }
3626
 
3627
    return curPos;
3628
}
3629
 
3630
/*
3631
 *----------------------------------------------------------------------
3632
 *
3633
 * Tcl_Tell --
3634
 *
3635
 *      Returns the position of the next character to be read/written on
3636
 *      this channel.
3637
 *
3638
 * Results:
3639
 *      A nonnegative integer on success, -1 on failure. If failed,
3640
 *      use Tcl_GetErrno() to retrieve the POSIX error code for the
3641
 *      error that occurred.
3642
 *
3643
 * Side effects:
3644
 *      None.
3645
 *
3646
 *----------------------------------------------------------------------
3647
 */
3648
 
3649
int
3650
Tcl_Tell(chan)
3651
    Tcl_Channel chan;                   /* The channel to return pos for. */
3652
{
3653
    Channel *chanPtr;                   /* The actual channel to tell on. */
3654
    ChannelBuffer *bufPtr;
3655
    int inputBuffered, outputBuffered;
3656
    int result;                         /* Of calling device driver. */
3657
    int curPos;                         /* Position on device. */
3658
 
3659
    chanPtr = (Channel *) chan;
3660
 
3661
    /*
3662
     * Check for unreported error.
3663
     */
3664
 
3665
    if (chanPtr->unreportedError != 0) {
3666
        Tcl_SetErrno(chanPtr->unreportedError);
3667
        chanPtr->unreportedError = 0;
3668
        return -1;
3669
    }
3670
 
3671
    /*
3672
     * Disallow tell on dead channels -- channels that have been closed but
3673
     * not yet been deallocated. Such channels can be found if the exit
3674
     * handler for channel cleanup has run but the channel is still
3675
     * registered in an interpreter.
3676
     */
3677
 
3678
    if (CheckForDeadChannel(NULL,chanPtr)) return -1;
3679
 
3680
    /*
3681
     * Disallow tell on channels that are open for neither
3682
     * writing nor reading (e.g. socket server channels).
3683
     */
3684
 
3685
    if (!(chanPtr->flags & (TCL_WRITABLE|TCL_READABLE))) {
3686
        Tcl_SetErrno(EACCES);
3687
        return -1;
3688
    }
3689
 
3690
    /*
3691
     * If the channel is in the middle of a background copy, fail.
3692
     */
3693
 
3694
    if (chanPtr->csPtr) {
3695
        Tcl_SetErrno(EBUSY);
3696
        return -1;
3697
    }
3698
 
3699
    /*
3700
     * Disallow tell on channels whose type does not have a seek procedure
3701
     * defined. This means that the channel does not support seeking.
3702
     */
3703
 
3704
    if (chanPtr->typePtr->seekProc == (Tcl_DriverSeekProc *) NULL) {
3705
        Tcl_SetErrno(EINVAL);
3706
        return -1;
3707
    }
3708
 
3709
    /*
3710
     * Compute how much input and output is buffered. If both input and
3711
     * output is buffered, cannot compute the current position.
3712
     */
3713
 
3714
    for (bufPtr = chanPtr->inQueueHead, inputBuffered = 0;
3715
             bufPtr != (ChannelBuffer *) NULL;
3716
             bufPtr = bufPtr->nextPtr) {
3717
        inputBuffered += (bufPtr->nextAdded - bufPtr->nextRemoved);
3718
    }
3719
    for (bufPtr = chanPtr->outQueueHead, outputBuffered = 0;
3720
             bufPtr != (ChannelBuffer *) NULL;
3721
             bufPtr = bufPtr->nextPtr) {
3722
        outputBuffered += (bufPtr->nextAdded - bufPtr->nextRemoved);
3723
    }
3724
    if ((chanPtr->curOutPtr != (ChannelBuffer *) NULL) &&
3725
           (chanPtr->curOutPtr->nextAdded > chanPtr->curOutPtr->nextRemoved)) {
3726
        chanPtr->flags |= BUFFER_READY;
3727
        outputBuffered +=
3728
            (chanPtr->curOutPtr->nextAdded - chanPtr->curOutPtr->nextRemoved);
3729
    }
3730
 
3731
    if ((inputBuffered != 0) && (outputBuffered != 0)) {
3732
        Tcl_SetErrno(EFAULT);
3733
        return -1;
3734
    }
3735
 
3736
    /*
3737
     * Get the current position in the device and compute the position
3738
     * where the next character will be read or written.
3739
     */
3740
 
3741
    curPos = (chanPtr->typePtr->seekProc) (chanPtr->instanceData,
3742
            (long) 0, SEEK_CUR, &result);
3743
    if (curPos == -1) {
3744
        Tcl_SetErrno(result);
3745
        return -1;
3746
    }
3747
    if (inputBuffered != 0) {
3748
        return (curPos - inputBuffered);
3749
    }
3750
    return (curPos + outputBuffered);
3751
}
3752
 
3753
/*
3754
 *----------------------------------------------------------------------
3755
 *
3756
 * Tcl_Eof --
3757
 *
3758
 *      Returns 1 if the channel is at EOF, 0 otherwise.
3759
 *
3760
 * Results:
3761
 *      1 or 0, always.
3762
 *
3763
 * Side effects:
3764
 *      None.
3765
 *
3766
 *----------------------------------------------------------------------
3767
 */
3768
 
3769
int
3770
Tcl_Eof(chan)
3771
    Tcl_Channel chan;                   /* Does this channel have EOF? */
3772
{
3773
    Channel *chanPtr;           /* The real channel structure. */
3774
 
3775
    chanPtr = (Channel *) chan;
3776
    return ((chanPtr->flags & CHANNEL_STICKY_EOF) ||
3777
            ((chanPtr->flags & CHANNEL_EOF) && (Tcl_InputBuffered(chan) == 0)))
3778
        ? 1 : 0;
3779
}
3780
 
3781
/*
3782
 *----------------------------------------------------------------------
3783
 *
3784
 * Tcl_InputBlocked --
3785
 *
3786
 *      Returns 1 if input is blocked on this channel, 0 otherwise.
3787
 *
3788
 * Results:
3789
 *      0 or 1, always.
3790
 *
3791
 * Side effects:
3792
 *      None.
3793
 *
3794
 *----------------------------------------------------------------------
3795
 */
3796
 
3797
int
3798
Tcl_InputBlocked(chan)
3799
    Tcl_Channel chan;                   /* Is this channel blocked? */
3800
{
3801
    Channel *chanPtr;           /* The real channel structure. */
3802
 
3803
    chanPtr = (Channel *) chan;
3804
    return (chanPtr->flags & CHANNEL_BLOCKED) ? 1 : 0;
3805
}
3806
 
3807
/*
3808
 *----------------------------------------------------------------------
3809
 *
3810
 * Tcl_InputBuffered --
3811
 *
3812
 *      Returns the number of bytes of input currently buffered in the
3813
 *      internal buffer of a channel.
3814
 *
3815
 * Results:
3816
 *      The number of input bytes buffered, or zero if the channel is not
3817
 *      open for reading.
3818
 *
3819
 * Side effects:
3820
 *      None.
3821
 *
3822
 *----------------------------------------------------------------------
3823
 */
3824
 
3825
int
3826
Tcl_InputBuffered(chan)
3827
    Tcl_Channel chan;                   /* The channel to query. */
3828
{
3829
    Channel *chanPtr;
3830
    int bytesBuffered;
3831
    ChannelBuffer *bufPtr;
3832
 
3833
    chanPtr = (Channel *) chan;
3834
    for (bytesBuffered = 0, bufPtr = chanPtr->inQueueHead;
3835
             bufPtr != (ChannelBuffer *) NULL;
3836
             bufPtr = bufPtr->nextPtr) {
3837
        bytesBuffered += (bufPtr->nextAdded - bufPtr->nextRemoved);
3838
    }
3839
    return bytesBuffered;
3840
}
3841
 
3842
/*
3843
 *----------------------------------------------------------------------
3844
 *
3845
 * Tcl_SetChannelBufferSize --
3846
 *
3847
 *      Sets the size of buffers to allocate to store input or output
3848
 *      in the channel. The size must be between 10 bytes and 1 MByte.
3849
 *
3850
 * Results:
3851
 *      None.
3852
 *
3853
 * Side effects:
3854
 *      Sets the size of buffers subsequently allocated for this channel.
3855
 *
3856
 *----------------------------------------------------------------------
3857
 */
3858
 
3859
void
3860
Tcl_SetChannelBufferSize(chan, sz)
3861
    Tcl_Channel chan;                   /* The channel whose buffer size
3862
                                         * to set. */
3863
    int sz;                             /* The size to set. */
3864
{
3865
    Channel *chanPtr;
3866
 
3867
    /*
3868
     * If the buffer size is smaller than 10 bytes or larger than one MByte,
3869
     * do not accept the requested size and leave the current buffer size.
3870
     */
3871
 
3872
    if (sz < 10) {
3873
        return;
3874
    }
3875
    if (sz > (1024 * 1024)) {
3876
        return;
3877
    }
3878
 
3879
    chanPtr = (Channel *) chan;
3880
    chanPtr->bufSize = sz;
3881
}
3882
 
3883
/*
3884
 *----------------------------------------------------------------------
3885
 *
3886
 * Tcl_GetChannelBufferSize --
3887
 *
3888
 *      Retrieves the size of buffers to allocate for this channel.
3889
 *
3890
 * Results:
3891
 *      The size.
3892
 *
3893
 * Side effects:
3894
 *      None.
3895
 *
3896
 *----------------------------------------------------------------------
3897
 */
3898
 
3899
int
3900
Tcl_GetChannelBufferSize(chan)
3901
    Tcl_Channel chan;           /* The channel for which to find the
3902
                                 * buffer size. */
3903
{
3904
    Channel *chanPtr;
3905
 
3906
    chanPtr = (Channel *) chan;
3907
    return chanPtr->bufSize;
3908
}
3909
 
3910
/*
3911
 *----------------------------------------------------------------------
3912
 *
3913
 * Tcl_BadChannelOption --
3914
 *
3915
 *      This procedure generates a "bad option" error message in an
3916
 *      (optional) interpreter.  It is used by channel drivers when
3917
 *      a invalid Set/Get option is requested. Its purpose is to concatenate
3918
 *      the generic options list to the specific ones and factorize
3919
 *      the generic options error message string.
3920
 *
3921
 * Results:
3922
 *      TCL_ERROR.
3923
 *
3924
 * Side effects:
3925
 *      An error message is generated in interp's result object to
3926
 *      indicate that a command was invoked with the a bad option
3927
 *      The message has the form
3928
 *              bad option "blah": should be one of
3929
 *              <...generic options...>+<...specific options...>
3930
 *      "blah" is the optionName argument and "<specific options>"
3931
 *      is a space separated list of specific option words.
3932
 *      The function takes good care of inserting minus signs before
3933
 *      each option, commas after, and an "or" before the last option.
3934
 *
3935
 *----------------------------------------------------------------------
3936
 */
3937
 
3938
int
3939
Tcl_BadChannelOption(interp, optionName, optionList)
3940
    Tcl_Interp *interp;                 /* Current interpreter. (can be NULL)*/
3941
    char *optionName;                   /* 'bad option' name */
3942
    char *optionList;                   /* Specific options list to append
3943
                                         * to the standard generic options.
3944
                                         * can be NULL for generic options
3945
                                         * only.
3946
                                         */
3947
{
3948
    if (interp) {
3949
        CONST char *genericopt =
3950
                "blocking buffering buffersize eofchar translation";
3951
        char **argv;
3952
        int  argc, i;
3953
        Tcl_DString ds;
3954
 
3955
        Tcl_DStringInit(&ds);
3956
        Tcl_DStringAppend(&ds, (char *) genericopt, -1);
3957
        if (optionList && (*optionList)) {
3958
            Tcl_DStringAppend(&ds, " ", 1);
3959
            Tcl_DStringAppend(&ds, optionList, -1);
3960
        }
3961
        if (Tcl_SplitList(interp, Tcl_DStringValue(&ds),
3962
                  &argc, &argv) != TCL_OK) {
3963
            panic("malformed option list in channel driver");
3964
        }
3965
        Tcl_ResetResult(interp);
3966
        Tcl_AppendResult(interp, "bad option \"", optionName,
3967
                 "\": should be one of ", (char *) NULL);
3968
        argc--;
3969
        for (i = 0; i < argc; i++) {
3970
            Tcl_AppendResult(interp, "-", argv[i], ", ", (char *) NULL);
3971
        }
3972
        Tcl_AppendResult(interp, "or -", argv[i], (char *) NULL);
3973
        Tcl_DStringFree(&ds);
3974
        ckfree((char *) argv);
3975
    }
3976
    Tcl_SetErrno(EINVAL);
3977
    return TCL_ERROR;
3978
}
3979
 
3980
/*
3981
 *----------------------------------------------------------------------
3982
 *
3983
 * Tcl_GetChannelOption --
3984
 *
3985
 *      Gets a mode associated with an IO channel. If the optionName arg
3986
 *      is non NULL, retrieves the value of that option. If the optionName
3987
 *      arg is NULL, retrieves a list of alternating option names and
3988
 *      values for the given channel.
3989
 *
3990
 * Results:
3991
 *      A standard Tcl result. Also sets the supplied DString to the
3992
 *      string value of the option(s) returned.
3993
 *
3994
 * Side effects:
3995
 *      None.
3996
 *
3997
 *----------------------------------------------------------------------
3998
 */
3999
 
4000
int
4001
Tcl_GetChannelOption(interp, chan, optionName, dsPtr)
4002
    Tcl_Interp *interp;         /* For error reporting - can be NULL. */
4003
    Tcl_Channel chan;           /* Channel on which to get option. */
4004
    char *optionName;           /* Option to get. */
4005
    Tcl_DString *dsPtr;         /* Where to store value(s). */
4006
{
4007
    size_t len;                 /* Length of optionName string. */
4008
    char optionVal[128];        /* Buffer for sprintf. */
4009
    Channel *chanPtr = (Channel *) chan;
4010
    int flags;
4011
 
4012
    /*
4013
     * If we are in the middle of a background copy, use the saved flags.
4014
     */
4015
 
4016
    if (chanPtr->csPtr) {
4017
        if (chanPtr == chanPtr->csPtr->readPtr) {
4018
            flags = chanPtr->csPtr->readFlags;
4019
        } else {
4020
            flags = chanPtr->csPtr->writeFlags;
4021
        }
4022
    } else {
4023
        flags = chanPtr->flags;
4024
    }
4025
 
4026
    /*
4027
     * Disallow options on dead channels -- channels that have been closed but
4028
     * not yet been deallocated. Such channels can be found if the exit
4029
     * handler for channel cleanup has run but the channel is still
4030
     * registered in an interpreter.
4031
     */
4032
 
4033
    if (CheckForDeadChannel(interp,chanPtr)) return TCL_ERROR;
4034
 
4035
    /*
4036
     * If the optionName is NULL it means that we want a list of all
4037
     * options and values.
4038
     */
4039
 
4040
    if (optionName == (char *) NULL) {
4041
        len = 0;
4042
    } else {
4043
        len = strlen(optionName);
4044
    }
4045
 
4046
    if ((len == 0) || ((len > 2) && (optionName[1] == 'b') &&
4047
            (strncmp(optionName, "-blocking", len) == 0))) {
4048
        if (len == 0) {
4049
            Tcl_DStringAppendElement(dsPtr, "-blocking");
4050
        }
4051
        Tcl_DStringAppendElement(dsPtr,
4052
                (flags & CHANNEL_NONBLOCKING) ? "0" : "1");
4053
        if (len > 0) {
4054
            return TCL_OK;
4055
        }
4056
    }
4057
    if ((len == 0) || ((len > 7) && (optionName[1] == 'b') &&
4058
            (strncmp(optionName, "-buffering", len) == 0))) {
4059
        if (len == 0) {
4060
            Tcl_DStringAppendElement(dsPtr, "-buffering");
4061
        }
4062
        if (flags & CHANNEL_LINEBUFFERED) {
4063
            Tcl_DStringAppendElement(dsPtr, "line");
4064
        } else if (flags & CHANNEL_UNBUFFERED) {
4065
            Tcl_DStringAppendElement(dsPtr, "none");
4066
        } else {
4067
            Tcl_DStringAppendElement(dsPtr, "full");
4068
        }
4069
        if (len > 0) {
4070
            return TCL_OK;
4071
        }
4072
    }
4073
    if ((len == 0) || ((len > 7) && (optionName[1] == 'b') &&
4074
            (strncmp(optionName, "-buffersize", len) == 0))) {
4075
        if (len == 0) {
4076
            Tcl_DStringAppendElement(dsPtr, "-buffersize");
4077
        }
4078
        TclFormatInt(optionVal, chanPtr->bufSize);
4079
        Tcl_DStringAppendElement(dsPtr, optionVal);
4080
        if (len > 0) {
4081
            return TCL_OK;
4082
        }
4083
    }
4084
    if ((len == 0) ||
4085
            ((len > 1) && (optionName[1] == 'e') &&
4086
                    (strncmp(optionName, "-eofchar", len) == 0))) {
4087
        if (len == 0) {
4088
            Tcl_DStringAppendElement(dsPtr, "-eofchar");
4089
        }
4090
        if (((flags & (TCL_READABLE|TCL_WRITABLE)) ==
4091
                (TCL_READABLE|TCL_WRITABLE)) && (len == 0)) {
4092
            Tcl_DStringStartSublist(dsPtr);
4093
        }
4094
        if (flags & TCL_READABLE) {
4095
            if (chanPtr->inEofChar == 0) {
4096
                Tcl_DStringAppendElement(dsPtr, "");
4097
            } else {
4098
                char buf[4];
4099
 
4100
                sprintf(buf, "%c", chanPtr->inEofChar);
4101
                Tcl_DStringAppendElement(dsPtr, buf);
4102
            }
4103
        }
4104
        if (flags & TCL_WRITABLE) {
4105
            if (chanPtr->outEofChar == 0) {
4106
                Tcl_DStringAppendElement(dsPtr, "");
4107
            } else {
4108
                char buf[4];
4109
 
4110
                sprintf(buf, "%c", chanPtr->outEofChar);
4111
                Tcl_DStringAppendElement(dsPtr, buf);
4112
            }
4113
        }
4114
        if (((flags & (TCL_READABLE|TCL_WRITABLE)) ==
4115
                (TCL_READABLE|TCL_WRITABLE)) && (len == 0)) {
4116
            Tcl_DStringEndSublist(dsPtr);
4117
        }
4118
        if (len > 0) {
4119
            return TCL_OK;
4120
        }
4121
    }
4122
    if ((len == 0) ||
4123
            ((len > 1) && (optionName[1] == 't') &&
4124
                    (strncmp(optionName, "-translation", len) == 0))) {
4125
        if (len == 0) {
4126
            Tcl_DStringAppendElement(dsPtr, "-translation");
4127
        }
4128
        if (((flags & (TCL_READABLE|TCL_WRITABLE)) ==
4129
                (TCL_READABLE|TCL_WRITABLE)) && (len == 0)) {
4130
            Tcl_DStringStartSublist(dsPtr);
4131
        }
4132
        if (flags & TCL_READABLE) {
4133
            if (chanPtr->inputTranslation == TCL_TRANSLATE_AUTO) {
4134
                Tcl_DStringAppendElement(dsPtr, "auto");
4135
            } else if (chanPtr->inputTranslation == TCL_TRANSLATE_CR) {
4136
                Tcl_DStringAppendElement(dsPtr, "cr");
4137
            } else if (chanPtr->inputTranslation == TCL_TRANSLATE_CRLF) {
4138
                Tcl_DStringAppendElement(dsPtr, "crlf");
4139
            } else {
4140
                Tcl_DStringAppendElement(dsPtr, "lf");
4141
            }
4142
        }
4143
        if (flags & TCL_WRITABLE) {
4144
            if (chanPtr->outputTranslation == TCL_TRANSLATE_AUTO) {
4145
                Tcl_DStringAppendElement(dsPtr, "auto");
4146
            } else if (chanPtr->outputTranslation == TCL_TRANSLATE_CR) {
4147
                Tcl_DStringAppendElement(dsPtr, "cr");
4148
            } else if (chanPtr->outputTranslation == TCL_TRANSLATE_CRLF) {
4149
                Tcl_DStringAppendElement(dsPtr, "crlf");
4150
            } else {
4151
                Tcl_DStringAppendElement(dsPtr, "lf");
4152
            }
4153
        }
4154
        if (((flags & (TCL_READABLE|TCL_WRITABLE)) ==
4155
                (TCL_READABLE|TCL_WRITABLE)) && (len == 0)) {
4156
            Tcl_DStringEndSublist(dsPtr);
4157
        }
4158
        if (len > 0) {
4159
            return TCL_OK;
4160
        }
4161
    }
4162
    if (chanPtr->typePtr->getOptionProc != (Tcl_DriverGetOptionProc *) NULL) {
4163
        /*
4164
         * let the driver specific handle additional options
4165
         * and result code and message.
4166
         */
4167
 
4168
        return (chanPtr->typePtr->getOptionProc) (chanPtr->instanceData,
4169
                  interp, optionName, dsPtr);
4170
    } else {
4171
        /*
4172
         * no driver specific options case.
4173
         */
4174
 
4175
        if (len == 0) {
4176
            return TCL_OK;
4177
        }
4178
        return Tcl_BadChannelOption(interp, optionName, NULL);
4179
    }
4180
}
4181
 
4182
/*
4183
 *----------------------------------------------------------------------
4184
 *
4185
 * Tcl_SetChannelOption --
4186
 *
4187
 *      Sets an option on a channel.
4188
 *
4189
 * Results:
4190
 *      A standard Tcl result. Also sets interp->result on error if
4191
 *      interp is not NULL.
4192
 *
4193
 * Side effects:
4194
 *      May modify an option on a device.
4195
 *
4196
 *----------------------------------------------------------------------
4197
 */
4198
 
4199
int
4200
Tcl_SetChannelOption(interp, chan, optionName, newValue)
4201
    Tcl_Interp *interp;         /* For error reporting - can be NULL. */
4202
    Tcl_Channel chan;           /* Channel on which to set mode. */
4203
    char *optionName;           /* Which option to set? */
4204
    char *newValue;             /* New value for option. */
4205
{
4206
    int newMode;                /* New (numeric) mode to sert. */
4207
    Channel *chanPtr;           /* The real IO channel. */
4208
    size_t len;                 /* Length of optionName string. */
4209
    int argc;
4210
    char **argv;
4211
 
4212
    chanPtr = (Channel *) chan;
4213
 
4214
    /*
4215
     * If the channel is in the middle of a background copy, fail.
4216
     */
4217
 
4218
    if (chanPtr->csPtr) {
4219
        if (interp) {
4220
            Tcl_AppendResult(interp,
4221
                 "unable to set channel options: background copy in progress",
4222
                 (char *) NULL);
4223
        }
4224
        return TCL_ERROR;
4225
    }
4226
 
4227
 
4228
    /*
4229
     * Disallow options on dead channels -- channels that have been closed but
4230
     * not yet been deallocated. Such channels can be found if the exit
4231
     * handler for channel cleanup has run but the channel is still
4232
     * registered in an interpreter.
4233
     */
4234
 
4235
    if (CheckForDeadChannel(NULL,chanPtr)) return TCL_ERROR;
4236
 
4237
    len = strlen(optionName);
4238
 
4239
    if ((len > 2) && (optionName[1] == 'b') &&
4240
            (strncmp(optionName, "-blocking", len) == 0)) {
4241
        if (Tcl_GetBoolean(interp, newValue, &newMode) == TCL_ERROR) {
4242
            return TCL_ERROR;
4243
        }
4244
        if (newMode) {
4245
            newMode = TCL_MODE_BLOCKING;
4246
        } else {
4247
            newMode = TCL_MODE_NONBLOCKING;
4248
        }
4249
        return SetBlockMode(interp, chanPtr, newMode);
4250
    }
4251
 
4252
    if ((len > 7) && (optionName[1] == 'b') &&
4253
            (strncmp(optionName, "-buffering", len) == 0)) {
4254
        len = strlen(newValue);
4255
        if ((newValue[0] == 'f') && (strncmp(newValue, "full", len) == 0)) {
4256
            chanPtr->flags &=
4257
                (~(CHANNEL_UNBUFFERED|CHANNEL_LINEBUFFERED));
4258
        } else if ((newValue[0] == 'l') &&
4259
                (strncmp(newValue, "line", len) == 0)) {
4260
            chanPtr->flags &= (~(CHANNEL_UNBUFFERED));
4261
            chanPtr->flags |= CHANNEL_LINEBUFFERED;
4262
        } else if ((newValue[0] == 'n') &&
4263
                (strncmp(newValue, "none", len) == 0)) {
4264
            chanPtr->flags &= (~(CHANNEL_LINEBUFFERED));
4265
            chanPtr->flags |= CHANNEL_UNBUFFERED;
4266
        } else {
4267
            if (interp) {
4268
                Tcl_AppendResult(interp, "bad value for -buffering: ",
4269
                        "must be one of full, line, or none",
4270
                        (char *) NULL);
4271
                return TCL_ERROR;
4272
            }
4273
        }
4274
        return TCL_OK;
4275
    }
4276
 
4277
    if ((len > 7) && (optionName[1] == 'b') &&
4278
            (strncmp(optionName, "-buffersize", len) == 0)) {
4279
        chanPtr->bufSize = atoi(newValue);
4280
        if ((chanPtr->bufSize < 10) || (chanPtr->bufSize > (1024 * 1024))) {
4281
            chanPtr->bufSize = CHANNELBUFFER_DEFAULT_SIZE;
4282
        }
4283
        return TCL_OK;
4284
    }
4285
 
4286
    if ((len > 1) && (optionName[1] == 'e') &&
4287
            (strncmp(optionName, "-eofchar", len) == 0)) {
4288
        if (Tcl_SplitList(interp, newValue, &argc, &argv) == TCL_ERROR) {
4289
            return TCL_ERROR;
4290
        }
4291
        if (argc == 0) {
4292
            chanPtr->inEofChar = 0;
4293
            chanPtr->outEofChar = 0;
4294
        } else if (argc == 1) {
4295
            if (chanPtr->flags & TCL_WRITABLE) {
4296
                chanPtr->outEofChar = (int) argv[0][0];
4297
            }
4298
            if (chanPtr->flags & TCL_READABLE) {
4299
                chanPtr->inEofChar = (int) argv[0][0];
4300
            }
4301
        } else if (argc != 2) {
4302
            if (interp) {
4303
                Tcl_AppendResult(interp,
4304
                        "bad value for -eofchar: should be a list of one or",
4305
                        " two elements", (char *) NULL);
4306
            }
4307
            ckfree((char *) argv);
4308
            return TCL_ERROR;
4309
        } else {
4310
            if (chanPtr->flags & TCL_READABLE) {
4311
                chanPtr->inEofChar = (int) argv[0][0];
4312
            }
4313
            if (chanPtr->flags & TCL_WRITABLE) {
4314
                chanPtr->outEofChar = (int) argv[1][0];
4315
            }
4316
        }
4317
        if (argv != (char **) NULL) {
4318
            ckfree((char *) argv);
4319
        }
4320
        return TCL_OK;
4321
    }
4322
 
4323
    if ((len > 1) && (optionName[1] == 't') &&
4324
            (strncmp(optionName, "-translation", len) == 0)) {
4325
        char *readMode, *writeMode;
4326
 
4327
        if (Tcl_SplitList(interp, newValue, &argc, &argv) == TCL_ERROR) {
4328
            return TCL_ERROR;
4329
        }
4330
 
4331
        if (argc == 1) {
4332
            readMode = (chanPtr->flags & TCL_READABLE) ? argv[0] : NULL;
4333
            writeMode = (chanPtr->flags & TCL_WRITABLE) ? argv[0] : NULL;
4334
        } else if (argc == 2) {
4335
            readMode = (chanPtr->flags & TCL_READABLE) ? argv[0] : NULL;
4336
            writeMode = (chanPtr->flags & TCL_WRITABLE) ? argv[1] : NULL;
4337
        } else {
4338
            if (interp) {
4339
                Tcl_AppendResult(interp,
4340
                        "bad value for -translation: must be a one or two",
4341
                        " element list", (char *) NULL);
4342
            }
4343
            ckfree((char *) argv);
4344
            return TCL_ERROR;
4345
        }
4346
 
4347
        if (readMode) {
4348
            if (*readMode == '\0') {
4349
                newMode = chanPtr->inputTranslation;
4350
            } else if (strcmp(readMode, "auto") == 0) {
4351
                newMode = TCL_TRANSLATE_AUTO;
4352
            } else if (strcmp(readMode, "binary") == 0) {
4353
                chanPtr->inEofChar = 0;
4354
                newMode = TCL_TRANSLATE_LF;
4355
            } else if (strcmp(readMode, "lf") == 0) {
4356
                newMode = TCL_TRANSLATE_LF;
4357
            } else if (strcmp(readMode, "cr") == 0) {
4358
                newMode = TCL_TRANSLATE_CR;
4359
            } else if (strcmp(readMode, "crlf") == 0) {
4360
                newMode = TCL_TRANSLATE_CRLF;
4361
            } else if (strcmp(readMode, "platform") == 0) {
4362
                newMode = TCL_PLATFORM_TRANSLATION;
4363
            } else {
4364
                if (interp) {
4365
                    Tcl_AppendResult(interp,
4366
                            "bad value for -translation: ",
4367
                            "must be one of auto, binary, cr, lf, crlf,",
4368
                            " or platform", (char *) NULL);
4369
                }
4370
                ckfree((char *) argv);
4371
                return TCL_ERROR;
4372
            }
4373
 
4374
            /*
4375
             * Reset the EOL flags since we need to look at any buffered
4376
             * data to see if the new translation mode allows us to
4377
             * complete the line.
4378
             */
4379
 
4380
            if (newMode != chanPtr->inputTranslation) {
4381
                chanPtr->inputTranslation = (Tcl_EolTranslation) newMode;
4382
                chanPtr->flags &= ~(INPUT_SAW_CR);
4383
                chanPtr->flags &= ~(CHANNEL_GETS_BLOCKED);
4384
                UpdateInterest(chanPtr);
4385
            }
4386
        }
4387
        if (writeMode) {
4388
            if (*writeMode == '\0') {
4389
                /* Do nothing. */
4390
            } else if (strcmp(writeMode, "auto") == 0) {
4391
                /*
4392
                 * This is a hack to get TCP sockets to produce output
4393
                 * in CRLF mode if they are being set into AUTO mode.
4394
                 * A better solution for achieving this effect will be
4395
                 * coded later.
4396
                 */
4397
 
4398
                if (strcmp(chanPtr->typePtr->typeName, "tcp") == 0) {
4399
                    chanPtr->outputTranslation = TCL_TRANSLATE_CRLF;
4400
                } else {
4401
                    chanPtr->outputTranslation = TCL_PLATFORM_TRANSLATION;
4402
                }
4403
            } else if (strcmp(writeMode, "binary") == 0) {
4404
                chanPtr->outEofChar = 0;
4405
                chanPtr->outputTranslation = TCL_TRANSLATE_LF;
4406
            } else if (strcmp(writeMode, "lf") == 0) {
4407
                chanPtr->outputTranslation = TCL_TRANSLATE_LF;
4408
            } else if (strcmp(writeMode, "cr") == 0) {
4409
                chanPtr->outputTranslation = TCL_TRANSLATE_CR;
4410
            } else if (strcmp(writeMode, "crlf") == 0) {
4411
                chanPtr->outputTranslation = TCL_TRANSLATE_CRLF;
4412
            } else if (strcmp(writeMode, "platform") == 0) {
4413
                chanPtr->outputTranslation = TCL_PLATFORM_TRANSLATION;
4414
            } else {
4415
                if (interp) {
4416
                    Tcl_AppendResult(interp,
4417
                            "bad value for -translation: ",
4418
                            "must be one of auto, binary, cr, lf, crlf,",
4419
                            " or platform", (char *) NULL);
4420
                }
4421
                ckfree((char *) argv);
4422
                return TCL_ERROR;
4423
            }
4424
        }
4425
        ckfree((char *) argv);
4426
        return TCL_OK;
4427
    }
4428
 
4429
    if (chanPtr->typePtr->setOptionProc != (Tcl_DriverSetOptionProc *) NULL) {
4430
        return (chanPtr->typePtr->setOptionProc) (chanPtr->instanceData,
4431
                interp, optionName, newValue);
4432
    }
4433
 
4434
    return Tcl_BadChannelOption(interp, optionName, (char *) NULL);
4435
}
4436
 
4437
/*
4438
 *----------------------------------------------------------------------
4439
 *
4440
 * CleanupChannelHandlers --
4441
 *
4442
 *      Removes channel handlers that refer to the supplied interpreter,
4443
 *      so that if the actual channel is not closed now, these handlers
4444
 *      will not run on subsequent events on the channel. This would be
4445
 *      erroneous, because the interpreter no longer has a reference to
4446
 *      this channel.
4447
 *
4448
 * Results:
4449
 *      None.
4450
 *
4451
 * Side effects:
4452
 *      Removes channel handlers.
4453
 *
4454
 *----------------------------------------------------------------------
4455
 */
4456
 
4457
static void
4458
CleanupChannelHandlers(interp, chanPtr)
4459
    Tcl_Interp *interp;
4460
    Channel *chanPtr;
4461
{
4462
    EventScriptRecord *sPtr, *prevPtr, *nextPtr;
4463
 
4464
    /*
4465
     * Remove fileevent records on this channel that refer to the
4466
     * given interpreter.
4467
     */
4468
 
4469
    for (sPtr = chanPtr->scriptRecordPtr,
4470
             prevPtr = (EventScriptRecord *) NULL;
4471
             sPtr != (EventScriptRecord *) NULL;
4472
             sPtr = nextPtr) {
4473
        nextPtr = sPtr->nextPtr;
4474
        if (sPtr->interp == interp) {
4475
            if (prevPtr == (EventScriptRecord *) NULL) {
4476
                chanPtr->scriptRecordPtr = nextPtr;
4477
            } else {
4478
                prevPtr->nextPtr = nextPtr;
4479
            }
4480
 
4481
            Tcl_DeleteChannelHandler((Tcl_Channel) chanPtr,
4482
                    ChannelEventScriptInvoker, (ClientData) sPtr);
4483
 
4484
            ckfree(sPtr->script);
4485
            ckfree((char *) sPtr);
4486
        } else {
4487
            prevPtr = sPtr;
4488
        }
4489
    }
4490
}
4491
 
4492
/*
4493
 *----------------------------------------------------------------------
4494
 *
4495
 * Tcl_NotifyChannel --
4496
 *
4497
 *      This procedure is called by a channel driver when a driver
4498
 *      detects an event on a channel.  This procedure is responsible
4499
 *      for actually handling the event by invoking any channel
4500
 *      handler callbacks.
4501
 *
4502
 * Results:
4503
 *      None.
4504
 *
4505
 * Side effects:
4506
 *      Whatever the channel handler callback procedure does.
4507
 *
4508
 *----------------------------------------------------------------------
4509
 */
4510
 
4511
void
4512
Tcl_NotifyChannel(channel, mask)
4513
    Tcl_Channel channel;        /* Channel that detected an event. */
4514
    int mask;                   /* OR'ed combination of TCL_READABLE,
4515
                                 * TCL_WRITABLE, or TCL_EXCEPTION: indicates
4516
                                 * which events were detected. */
4517
{
4518
    Channel *chanPtr = (Channel *) channel;
4519
    ChannelHandler *chPtr;
4520
    NextChannelHandler nh;
4521
 
4522
    /*
4523
     * Preserve the channel struct in case the script closes it.
4524
     */
4525
 
4526
    Tcl_Preserve((ClientData) channel);
4527
 
4528
    /*
4529
     * If we are flushing in the background, be sure to call FlushChannel
4530
     * for writable events.  Note that we have to discard the writable
4531
     * event so we don't call any write handlers before the flush is
4532
     * complete.
4533
     */
4534
 
4535
    if ((chanPtr->flags & BG_FLUSH_SCHEDULED) && (mask & TCL_WRITABLE)) {
4536
        FlushChannel(NULL, chanPtr, 1);
4537
        mask &= ~TCL_WRITABLE;
4538
    }
4539
 
4540
    /*
4541
     * Add this invocation to the list of recursive invocations of
4542
     * ChannelHandlerEventProc.
4543
     */
4544
 
4545
    nh.nextHandlerPtr = (ChannelHandler *) NULL;
4546
    nh.nestedHandlerPtr = nestedHandlerPtr;
4547
    nestedHandlerPtr = &nh;
4548
 
4549
    for (chPtr = chanPtr->chPtr; chPtr != (ChannelHandler *) NULL; ) {
4550
 
4551
        /*
4552
         * If this channel handler is interested in any of the events that
4553
         * have occurred on the channel, invoke its procedure.
4554
         */
4555
 
4556
        if ((chPtr->mask & mask) != 0) {
4557
            nh.nextHandlerPtr = chPtr->nextPtr;
4558
            (*(chPtr->proc))(chPtr->clientData, mask);
4559
            chPtr = nh.nextHandlerPtr;
4560
        } else {
4561
            chPtr = chPtr->nextPtr;
4562
        }
4563
    }
4564
 
4565
    /*
4566
     * Update the notifier interest, since it may have changed after
4567
     * invoking event handlers.
4568
     */
4569
 
4570
    if (chanPtr->typePtr != NULL) {
4571
        UpdateInterest(chanPtr);
4572
    }
4573
 
4574
    Tcl_Release((ClientData) channel);
4575
 
4576
    nestedHandlerPtr = nh.nestedHandlerPtr;
4577
}
4578
 
4579
/*
4580
 *----------------------------------------------------------------------
4581
 *
4582
 * UpdateInterest --
4583
 *
4584
 *      Arrange for the notifier to call us back at appropriate times
4585
 *      based on the current state of the channel.
4586
 *
4587
 * Results:
4588
 *      None.
4589
 *
4590
 * Side effects:
4591
 *      May schedule a timer or driver handler.
4592
 *
4593
 *----------------------------------------------------------------------
4594
 */
4595
 
4596
static void
4597
UpdateInterest(chanPtr)
4598
    Channel *chanPtr;           /* Channel to update. */
4599
{
4600
    int mask = chanPtr->interestMask;
4601
 
4602
    /*
4603
     * If there are flushed buffers waiting to be written, then
4604
     * we need to watch for the channel to become writable.
4605
     */
4606
 
4607
    if (chanPtr->flags & BG_FLUSH_SCHEDULED) {
4608
        mask |= TCL_WRITABLE;
4609
    }
4610
 
4611
    /*
4612
     * If there is data in the input queue, and we aren't blocked waiting for
4613
     * an EOL, then we need to schedule a timer so we don't block in the
4614
     * notifier.  Also, cancel the read interest so we don't get duplicate
4615
     * events.
4616
     */
4617
 
4618
    if (mask & TCL_READABLE) {
4619
        if (!(chanPtr->flags & CHANNEL_GETS_BLOCKED)
4620
                && (chanPtr->inQueueHead != (ChannelBuffer *) NULL)
4621
                && (chanPtr->inQueueHead->nextRemoved <
4622
                        chanPtr->inQueueHead->nextAdded)) {
4623
            mask &= ~TCL_READABLE;
4624
            if (!chanPtr->timer) {
4625
                chanPtr->timer = Tcl_CreateTimerHandler(0, ChannelTimerProc,
4626
                        (ClientData) chanPtr);
4627
            }
4628
        }
4629
    }
4630
    (chanPtr->typePtr->watchProc)(chanPtr->instanceData, mask);
4631
}
4632
 
4633
/*
4634
 *----------------------------------------------------------------------
4635
 *
4636
 * ChannelTimerProc --
4637
 *
4638
 *      Timer handler scheduled by UpdateInterest to monitor the
4639
 *      channel buffers until they are empty.
4640
 *
4641
 * Results:
4642
 *      None.
4643
 *
4644
 * Side effects:
4645
 *      May invoke channel handlers.
4646
 *
4647
 *----------------------------------------------------------------------
4648
 */
4649
 
4650
static void
4651
ChannelTimerProc(clientData)
4652
    ClientData clientData;
4653
{
4654
    Channel *chanPtr = (Channel *) clientData;
4655
 
4656
    if (!(chanPtr->flags & CHANNEL_GETS_BLOCKED)
4657
            && (chanPtr->interestMask & TCL_READABLE)
4658
            && (chanPtr->inQueueHead != (ChannelBuffer *) NULL)
4659
            && (chanPtr->inQueueHead->nextRemoved <
4660
                    chanPtr->inQueueHead->nextAdded)) {
4661
        /*
4662
         * Restart the timer in case a channel handler reenters the
4663
         * event loop before UpdateInterest gets called by Tcl_NotifyChannel.
4664
         */
4665
 
4666
        chanPtr->timer = Tcl_CreateTimerHandler(0, ChannelTimerProc,
4667
                        (ClientData) chanPtr);
4668
        Tcl_NotifyChannel((Tcl_Channel)chanPtr, TCL_READABLE);
4669
 
4670
   } else {
4671
        chanPtr->timer = NULL;
4672
        UpdateInterest(chanPtr);
4673
    }
4674
}
4675
 
4676
/*
4677
 *----------------------------------------------------------------------
4678
 *
4679
 * Tcl_CreateChannelHandler --
4680
 *
4681
 *      Arrange for a given procedure to be invoked whenever the
4682
 *      channel indicated by the chanPtr arg becomes readable or
4683
 *      writable.
4684
 *
4685
 * Results:
4686
 *      None.
4687
 *
4688
 * Side effects:
4689
 *      From now on, whenever the I/O channel given by chanPtr becomes
4690
 *      ready in the way indicated by mask, proc will be invoked.
4691
 *      See the manual entry for details on the calling sequence
4692
 *      to proc.  If there is already an event handler for chan, proc
4693
 *      and clientData, then the mask will be updated.
4694
 *
4695
 *----------------------------------------------------------------------
4696
 */
4697
 
4698
void
4699
Tcl_CreateChannelHandler(chan, mask, proc, clientData)
4700
    Tcl_Channel chan;           /* The channel to create the handler for. */
4701
    int mask;                   /* OR'ed combination of TCL_READABLE,
4702
                                 * TCL_WRITABLE, and TCL_EXCEPTION:
4703
                                 * indicates conditions under which
4704
                                 * proc should be called. Use 0 to
4705
                                 * disable a registered handler. */
4706
    Tcl_ChannelProc *proc;      /* Procedure to call for each
4707
                                 * selected event. */
4708
    ClientData clientData;      /* Arbitrary data to pass to proc. */
4709
{
4710
    ChannelHandler *chPtr;
4711
    Channel *chanPtr;
4712
 
4713
    chanPtr = (Channel *) chan;
4714
 
4715
    /*
4716
     * Check whether this channel handler is not already registered. If
4717
     * it is not, create a new record, else reuse existing record (smash
4718
     * current values).
4719
     */
4720
 
4721
    for (chPtr = chanPtr->chPtr;
4722
             chPtr != (ChannelHandler *) NULL;
4723
             chPtr = chPtr->nextPtr) {
4724
        if ((chPtr->chanPtr == chanPtr) && (chPtr->proc == proc) &&
4725
                (chPtr->clientData == clientData)) {
4726
            break;
4727
        }
4728
    }
4729
    if (chPtr == (ChannelHandler *) NULL) {
4730
        chPtr = (ChannelHandler *) ckalloc((unsigned) sizeof(ChannelHandler));
4731
        chPtr->mask = 0;
4732
        chPtr->proc = proc;
4733
        chPtr->clientData = clientData;
4734
        chPtr->chanPtr = chanPtr;
4735
        chPtr->nextPtr = chanPtr->chPtr;
4736
        chanPtr->chPtr = chPtr;
4737
    }
4738
 
4739
    /*
4740
     * The remainder of the initialization below is done regardless of
4741
     * whether or not this is a new record or a modification of an old
4742
     * one.
4743
     */
4744
 
4745
    chPtr->mask = mask;
4746
 
4747
    /*
4748
     * Recompute the interest mask for the channel - this call may actually
4749
     * be disabling an existing handler.
4750
     */
4751
 
4752
    chanPtr->interestMask = 0;
4753
    for (chPtr = chanPtr->chPtr;
4754
         chPtr != (ChannelHandler *) NULL;
4755
         chPtr = chPtr->nextPtr) {
4756
        chanPtr->interestMask |= chPtr->mask;
4757
    }
4758
 
4759
    UpdateInterest(chanPtr);
4760
}
4761
 
4762
/*
4763
 *----------------------------------------------------------------------
4764
 *
4765
 * Tcl_DeleteChannelHandler --
4766
 *
4767
 *      Cancel a previously arranged callback arrangement for an IO
4768
 *      channel.
4769
 *
4770
 * Results:
4771
 *      None.
4772
 *
4773
 * Side effects:
4774
 *      If a callback was previously registered for this chan, proc and
4775
 *       clientData , it is removed and the callback will no longer be called
4776
 *      when the channel becomes ready for IO.
4777
 *
4778
 *----------------------------------------------------------------------
4779
 */
4780
 
4781
void
4782
Tcl_DeleteChannelHandler(chan, proc, clientData)
4783
    Tcl_Channel chan;           /* The channel for which to remove the
4784
                                 * callback. */
4785
    Tcl_ChannelProc *proc;      /* The procedure in the callback to delete. */
4786
    ClientData clientData;      /* The client data in the callback
4787
                                 * to delete. */
4788
 
4789
{
4790
    ChannelHandler *chPtr, *prevChPtr;
4791
    Channel *chanPtr;
4792
    NextChannelHandler *nhPtr;
4793
 
4794
    chanPtr = (Channel *) chan;
4795
 
4796
    /*
4797
     * Find the entry and the previous one in the list.
4798
     */
4799
 
4800
    for (prevChPtr = (ChannelHandler *) NULL, chPtr = chanPtr->chPtr;
4801
             chPtr != (ChannelHandler *) NULL;
4802
             chPtr = chPtr->nextPtr) {
4803
        if ((chPtr->chanPtr == chanPtr) && (chPtr->clientData == clientData)
4804
                && (chPtr->proc == proc)) {
4805
            break;
4806
        }
4807
        prevChPtr = chPtr;
4808
    }
4809
 
4810
    /*
4811
     * If not found, return without doing anything.
4812
     */
4813
 
4814
    if (chPtr == (ChannelHandler *) NULL) {
4815
        return;
4816
    }
4817
 
4818
    /*
4819
     * If ChannelHandlerEventProc is about to process this handler, tell it to
4820
     * process the next one instead - we are going to delete *this* one.
4821
     */
4822
 
4823
    for (nhPtr = nestedHandlerPtr;
4824
             nhPtr != (NextChannelHandler *) NULL;
4825
             nhPtr = nhPtr->nestedHandlerPtr) {
4826
        if (nhPtr->nextHandlerPtr == chPtr) {
4827
            nhPtr->nextHandlerPtr = chPtr->nextPtr;
4828
        }
4829
    }
4830
 
4831
    /*
4832
     * Splice it out of the list of channel handlers.
4833
     */
4834
 
4835
    if (prevChPtr == (ChannelHandler *) NULL) {
4836
        chanPtr->chPtr = chPtr->nextPtr;
4837
    } else {
4838
        prevChPtr->nextPtr = chPtr->nextPtr;
4839
    }
4840
    ckfree((char *) chPtr);
4841
 
4842
    /*
4843
     * Recompute the interest list for the channel, so that infinite loops
4844
     * will not result if Tcl_DeleteChanelHandler is called inside an event.
4845
     */
4846
 
4847
    chanPtr->interestMask = 0;
4848
    for (chPtr = chanPtr->chPtr;
4849
             chPtr != (ChannelHandler *) NULL;
4850
             chPtr = chPtr->nextPtr) {
4851
        chanPtr->interestMask |= chPtr->mask;
4852
    }
4853
 
4854
    UpdateInterest(chanPtr);
4855
}
4856
 
4857
/*
4858
 *----------------------------------------------------------------------
4859
 *
4860
 * DeleteScriptRecord --
4861
 *
4862
 *      Delete a script record for this combination of channel, interp
4863
 *      and mask.
4864
 *
4865
 * Results:
4866
 *      None.
4867
 *
4868
 * Side effects:
4869
 *      Deletes a script record and cancels a channel event handler.
4870
 *
4871
 *----------------------------------------------------------------------
4872
 */
4873
 
4874
static void
4875
DeleteScriptRecord(interp, chanPtr, mask)
4876
    Tcl_Interp *interp;         /* Interpreter in which script was to be
4877
                                 * executed. */
4878
    Channel *chanPtr;           /* The channel for which to delete the
4879
                                 * script record (if any). */
4880
    int mask;                   /* Events in mask must exactly match mask
4881
                                 * of script to delete. */
4882
{
4883
    EventScriptRecord *esPtr, *prevEsPtr;
4884
 
4885
    for (esPtr = chanPtr->scriptRecordPtr,
4886
             prevEsPtr = (EventScriptRecord *) NULL;
4887
             esPtr != (EventScriptRecord *) NULL;
4888
             prevEsPtr = esPtr, esPtr = esPtr->nextPtr) {
4889
        if ((esPtr->interp == interp) && (esPtr->mask == mask)) {
4890
            if (esPtr == chanPtr->scriptRecordPtr) {
4891
                chanPtr->scriptRecordPtr = esPtr->nextPtr;
4892
            } else {
4893
                prevEsPtr->nextPtr = esPtr->nextPtr;
4894
            }
4895
 
4896
            Tcl_DeleteChannelHandler((Tcl_Channel) chanPtr,
4897
                    ChannelEventScriptInvoker, (ClientData) esPtr);
4898
 
4899
            ckfree(esPtr->script);
4900
            ckfree((char *) esPtr);
4901
 
4902
            break;
4903
        }
4904
    }
4905
}
4906
 
4907
/*
4908
 *----------------------------------------------------------------------
4909
 *
4910
 * CreateScriptRecord --
4911
 *
4912
 *      Creates a record to store a script to be executed when a specific
4913
 *      event fires on a specific channel.
4914
 *
4915
 * Results:
4916
 *      None.
4917
 *
4918
 * Side effects:
4919
 *      Causes the script to be stored for later execution.
4920
 *
4921
 *----------------------------------------------------------------------
4922
 */
4923
 
4924
static void
4925
CreateScriptRecord(interp, chanPtr, mask, script)
4926
    Tcl_Interp *interp;                 /* Interpreter in which to execute
4927
                                         * the stored script. */
4928
    Channel *chanPtr;                   /* Channel for which script is to
4929
                                         * be stored. */
4930
    int mask;                           /* Set of events for which script
4931
                                         * will be invoked. */
4932
    char *script;                       /* A copy of this script is stored
4933
                                         * in the newly created record. */
4934
{
4935
    EventScriptRecord *esPtr;
4936
 
4937
    for (esPtr = chanPtr->scriptRecordPtr;
4938
             esPtr != (EventScriptRecord *) NULL;
4939
             esPtr = esPtr->nextPtr) {
4940
        if ((esPtr->interp == interp) && (esPtr->mask == mask)) {
4941
            ckfree(esPtr->script);
4942
            esPtr->script = (char *) NULL;
4943
            break;
4944
        }
4945
    }
4946
    if (esPtr == (EventScriptRecord *) NULL) {
4947
        esPtr = (EventScriptRecord *) ckalloc((unsigned)
4948
                sizeof(EventScriptRecord));
4949
        Tcl_CreateChannelHandler((Tcl_Channel) chanPtr, mask,
4950
                ChannelEventScriptInvoker, (ClientData) esPtr);
4951
        esPtr->nextPtr = chanPtr->scriptRecordPtr;
4952
        chanPtr->scriptRecordPtr = esPtr;
4953
    }
4954
    esPtr->chanPtr = chanPtr;
4955
    esPtr->interp = interp;
4956
    esPtr->mask = mask;
4957
    esPtr->script = ckalloc((unsigned) (strlen(script) + 1));
4958
    strcpy(esPtr->script, script);
4959
}
4960
 
4961
/*
4962
 *----------------------------------------------------------------------
4963
 *
4964
 * ChannelEventScriptInvoker --
4965
 *
4966
 *      Invokes a script scheduled by "fileevent" for when the channel
4967
 *      becomes ready for IO. This function is invoked by the channel
4968
 *      handler which was created by the Tcl "fileevent" command.
4969
 *
4970
 * Results:
4971
 *      None.
4972
 *
4973
 * Side effects:
4974
 *      Whatever the script does.
4975
 *
4976
 *----------------------------------------------------------------------
4977
 */
4978
 
4979
static void
4980
ChannelEventScriptInvoker(clientData, mask)
4981
    ClientData clientData;      /* The script+interp record. */
4982
    int mask;                   /* Not used. */
4983
{
4984
    Tcl_Interp *interp;         /* Interpreter in which to eval the script. */
4985
    Channel *chanPtr;           /* The channel for which this handler is
4986
                                 * registered. */
4987
    char *script;               /* Script to eval. */
4988
    EventScriptRecord *esPtr;   /* The event script + interpreter to eval it
4989
                                 * in. */
4990
    int result;                 /* Result of call to eval script. */
4991
 
4992
    esPtr = (EventScriptRecord *) clientData;
4993
 
4994
    chanPtr = esPtr->chanPtr;
4995
    mask = esPtr->mask;
4996
    interp = esPtr->interp;
4997
    script = esPtr->script;
4998
 
4999
    /*
5000
     * We must preserve the interpreter so we can report errors on it
5001
     * later.  Note that we do not need to preserve the channel because
5002
     * that is done by Tcl_NotifyChannel before calling channel handlers.
5003
     */
5004
 
5005
    Tcl_Preserve((ClientData) interp);
5006
    result = Tcl_GlobalEval(interp, script);
5007
 
5008
    /*
5009
     * On error, cause a background error and remove the channel handler
5010
     * and the script record.
5011
     *
5012
     * NOTE: Must delete channel handler before causing the background error
5013
     * because the background error may want to reinstall the handler.
5014
     */
5015
 
5016
    if (result != TCL_OK) {
5017
        if (chanPtr->typePtr != NULL) {
5018
            DeleteScriptRecord(interp, chanPtr, mask);
5019
        }
5020
        Tcl_BackgroundError(interp);
5021
    }
5022
    Tcl_Release((ClientData) interp);
5023
}
5024
 
5025
/*
5026
 *----------------------------------------------------------------------
5027
 *
5028
 * Tcl_FileEventCmd --
5029
 *
5030
 *      This procedure implements the "fileevent" Tcl command. See the
5031
 *      user documentation for details on what it does. This command is
5032
 *      based on the Tk command "fileevent" which in turn is based on work
5033
 *      contributed by Mark Diekhans.
5034
 *
5035
 * Results:
5036
 *      A standard Tcl result.
5037
 *
5038
 * Side effects:
5039
 *      May create a channel handler for the specified channel.
5040
 *
5041
 *----------------------------------------------------------------------
5042
 */
5043
 
5044
        /* ARGSUSED */
5045
int
5046
Tcl_FileEventCmd(clientData, interp, argc, argv)
5047
    ClientData clientData;              /* Not used. */
5048
    Tcl_Interp *interp;                 /* Interpreter in which the channel
5049
                                         * for which to create the handler
5050
                                         * is found. */
5051
    int argc;                           /* Number of arguments. */
5052
    char **argv;                        /* Argument strings. */
5053
{
5054
    Channel *chanPtr;                   /* The channel to create
5055
                                         * the handler for. */
5056
    Tcl_Channel chan;                   /* The opaque type for the channel. */
5057
    int c;                              /* First char of mode argument. */
5058
    int mask;                           /* Mask for events of interest. */
5059
    size_t length;                      /* Length of mode argument. */
5060
 
5061
    /*
5062
     * Parse arguments.
5063
     */
5064
 
5065
    if ((argc != 3) && (argc != 4)) {
5066
        Tcl_AppendResult(interp, "wrong # args: must be \"", argv[0],
5067
                " channelId event ?script?", (char *) NULL);
5068
        return TCL_ERROR;
5069
    }
5070
    c = argv[2][0];
5071
    length = strlen(argv[2]);
5072
    if ((c == 'r') && (strncmp(argv[2], "readable", length) == 0)) {
5073
        mask = TCL_READABLE;
5074
    } else if ((c == 'w') && (strncmp(argv[2], "writable", length) == 0)) {
5075
        mask = TCL_WRITABLE;
5076
    } else {
5077
        Tcl_AppendResult(interp, "bad event name \"", argv[2],
5078
                "\": must be readable or writable", (char *) NULL);
5079
        return TCL_ERROR;
5080
    }
5081
    chan = Tcl_GetChannel(interp, argv[1], NULL);
5082
    if (chan == (Tcl_Channel) NULL) {
5083
        return TCL_ERROR;
5084
    }
5085
 
5086
    chanPtr = (Channel *) chan;
5087
    if ((chanPtr->flags & mask) == 0) {
5088
        Tcl_AppendResult(interp, "channel is not ",
5089
                (mask == TCL_READABLE) ? "readable" : "writable",
5090
                (char *) NULL);
5091
        return TCL_ERROR;
5092
    }
5093
 
5094
    /*
5095
     * If we are supposed to return the script, do so.
5096
     */
5097
 
5098
    if (argc == 3) {
5099
        EventScriptRecord *esPtr;
5100
        for (esPtr = chanPtr->scriptRecordPtr;
5101
             esPtr != (EventScriptRecord *) NULL;
5102
             esPtr = esPtr->nextPtr) {
5103
            if ((esPtr->interp == interp) && (esPtr->mask == mask)) {
5104
                Tcl_SetResult(interp, esPtr->script, TCL_STATIC);
5105
                break;
5106
            }
5107
        }
5108
        return TCL_OK;
5109
    }
5110
 
5111
    /*
5112
     * If we are supposed to delete a stored script, do so.
5113
     */
5114
 
5115
    if (argv[3][0] == 0) {
5116
        DeleteScriptRecord(interp, chanPtr, mask);
5117
        return TCL_OK;
5118
    }
5119
 
5120
    /*
5121
     * Make the script record that will link between the event and the
5122
     * script to invoke. This also creates a channel event handler which
5123
     * will evaluate the script in the supplied interpreter.
5124
     */
5125
 
5126
    CreateScriptRecord(interp, chanPtr, mask, argv[3]);
5127
 
5128
    return TCL_OK;
5129
}
5130
 
5131
/*
5132
 *----------------------------------------------------------------------
5133
 *
5134
 * TclTestChannelCmd --
5135
 *
5136
 *      Implements the Tcl "testchannel" debugging command and its
5137
 *      subcommands. This is part of the testing environment but must be
5138
 *      in this file instead of tclTest.c because it needs access to the
5139
 *      fields of struct Channel.
5140
 *
5141
 * Results:
5142
 *      A standard Tcl result.
5143
 *
5144
 * Side effects:
5145
 *      None.
5146
 *
5147
 *----------------------------------------------------------------------
5148
 */
5149
 
5150
        /* ARGSUSED */
5151
int
5152
TclTestChannelCmd(clientData, interp, argc, argv)
5153
    ClientData clientData;      /* Not used. */
5154
    Tcl_Interp *interp;         /* Interpreter for result. */
5155
    int argc;                   /* Count of additional args. */
5156
    char **argv;                /* Additional arg strings. */
5157
{
5158
    char *cmdName;              /* Sub command. */
5159
    Tcl_HashTable *hTblPtr;     /* Hash table of channels. */
5160
    Tcl_HashSearch hSearch;     /* Search variable. */
5161
    Tcl_HashEntry *hPtr;        /* Search variable. */
5162
    Channel *chanPtr;           /* The actual channel. */
5163
    Tcl_Channel chan;           /* The opaque type. */
5164
    size_t len;                 /* Length of subcommand string. */
5165
    int IOQueued;               /* How much IO is queued inside channel? */
5166
    ChannelBuffer *bufPtr;      /* For iterating over queued IO. */
5167
    char buf[128];              /* For sprintf. */
5168
 
5169
    if (argc < 2) {
5170
        Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
5171
                " subcommand ?additional args..?\"", (char *) NULL);
5172
        return TCL_ERROR;
5173
    }
5174
    cmdName = argv[1];
5175
    len = strlen(cmdName);
5176
 
5177
    chanPtr = (Channel *) NULL;
5178
    if (argc > 2) {
5179
        chan = Tcl_GetChannel(interp, argv[2], NULL);
5180
        if (chan == (Tcl_Channel) NULL) {
5181
            return TCL_ERROR;
5182
        }
5183
        chanPtr = (Channel *) chan;
5184
    }
5185
 
5186
    if ((cmdName[0] == 'i') && (strncmp(cmdName, "info", len) == 0)) {
5187
        if (argc != 3) {
5188
            Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
5189
                    " info channelName\"", (char *) NULL);
5190
            return TCL_ERROR;
5191
        }
5192
        Tcl_AppendElement(interp, argv[2]);
5193
        Tcl_AppendElement(interp, chanPtr->typePtr->typeName);
5194
        if (chanPtr->flags & TCL_READABLE) {
5195
            Tcl_AppendElement(interp, "read");
5196
        } else {
5197
            Tcl_AppendElement(interp, "");
5198
        }
5199
        if (chanPtr->flags & TCL_WRITABLE) {
5200
            Tcl_AppendElement(interp, "write");
5201
        } else {
5202
            Tcl_AppendElement(interp, "");
5203
        }
5204
        if (chanPtr->flags & CHANNEL_NONBLOCKING) {
5205
            Tcl_AppendElement(interp, "nonblocking");
5206
        } else {
5207
            Tcl_AppendElement(interp, "blocking");
5208
        }
5209
        if (chanPtr->flags & CHANNEL_LINEBUFFERED) {
5210
            Tcl_AppendElement(interp, "line");
5211
        } else if (chanPtr->flags & CHANNEL_UNBUFFERED) {
5212
            Tcl_AppendElement(interp, "none");
5213
        } else {
5214
            Tcl_AppendElement(interp, "full");
5215
        }
5216
        if (chanPtr->flags & BG_FLUSH_SCHEDULED) {
5217
            Tcl_AppendElement(interp, "async_flush");
5218
        } else {
5219
            Tcl_AppendElement(interp, "");
5220
        }
5221
        if (chanPtr->flags & CHANNEL_EOF) {
5222
            Tcl_AppendElement(interp, "eof");
5223
        } else {
5224
            Tcl_AppendElement(interp, "");
5225
        }
5226
        if (chanPtr->flags & CHANNEL_BLOCKED) {
5227
            Tcl_AppendElement(interp, "blocked");
5228
        } else {
5229
            Tcl_AppendElement(interp, "unblocked");
5230
        }
5231
        if (chanPtr->inputTranslation == TCL_TRANSLATE_AUTO) {
5232
            Tcl_AppendElement(interp, "auto");
5233
            if (chanPtr->flags & INPUT_SAW_CR) {
5234
                Tcl_AppendElement(interp, "saw_cr");
5235
            } else {
5236
                Tcl_AppendElement(interp, "");
5237
            }
5238
        } else if (chanPtr->inputTranslation == TCL_TRANSLATE_LF) {
5239
            Tcl_AppendElement(interp, "lf");
5240
            Tcl_AppendElement(interp, "");
5241
        } else if (chanPtr->inputTranslation == TCL_TRANSLATE_CR) {
5242
            Tcl_AppendElement(interp, "cr");
5243
            Tcl_AppendElement(interp, "");
5244
        } else if (chanPtr->inputTranslation == TCL_TRANSLATE_CRLF) {
5245
            Tcl_AppendElement(interp, "crlf");
5246
            if (chanPtr->flags & INPUT_SAW_CR) {
5247
                Tcl_AppendElement(interp, "queued_cr");
5248
            } else {
5249
                Tcl_AppendElement(interp, "");
5250
            }
5251
        }
5252
        if (chanPtr->outputTranslation == TCL_TRANSLATE_AUTO) {
5253
            Tcl_AppendElement(interp, "auto");
5254
        } else if (chanPtr->outputTranslation == TCL_TRANSLATE_LF) {
5255
            Tcl_AppendElement(interp, "lf");
5256
        } else if (chanPtr->outputTranslation == TCL_TRANSLATE_CR) {
5257
            Tcl_AppendElement(interp, "cr");
5258
        } else if (chanPtr->outputTranslation == TCL_TRANSLATE_CRLF) {
5259
            Tcl_AppendElement(interp, "crlf");
5260
        }
5261
        for (IOQueued = 0, bufPtr = chanPtr->inQueueHead;
5262
                 bufPtr != (ChannelBuffer *) NULL;
5263
                 bufPtr = bufPtr->nextPtr) {
5264
            IOQueued += bufPtr->nextAdded - bufPtr->nextRemoved;
5265
        }
5266
        TclFormatInt(buf, IOQueued);
5267
        Tcl_AppendElement(interp, buf);
5268
 
5269
        IOQueued = 0;
5270
        if (chanPtr->curOutPtr != (ChannelBuffer *) NULL) {
5271
            IOQueued = chanPtr->curOutPtr->nextAdded -
5272
                chanPtr->curOutPtr->nextRemoved;
5273
        }
5274
        for (bufPtr = chanPtr->outQueueHead;
5275
                 bufPtr != (ChannelBuffer *) NULL;
5276
                 bufPtr = bufPtr->nextPtr) {
5277
            IOQueued += (bufPtr->nextAdded - bufPtr->nextRemoved);
5278
        }
5279
        TclFormatInt(buf, IOQueued);
5280
        Tcl_AppendElement(interp, buf);
5281
 
5282
        TclFormatInt(buf, Tcl_Tell((Tcl_Channel) chanPtr));
5283
        Tcl_AppendElement(interp, buf);
5284
 
5285
        TclFormatInt(buf, chanPtr->refCount);
5286
        Tcl_AppendElement(interp, buf);
5287
 
5288
        return TCL_OK;
5289
    }
5290
 
5291
    if ((cmdName[0] == 'i') &&
5292
            (strncmp(cmdName, "inputbuffered", len) == 0)) {
5293
        if (argc != 3) {
5294
            Tcl_AppendResult(interp, "channel name required",
5295
                    (char *) NULL);
5296
            return TCL_ERROR;
5297
        }
5298
 
5299
        for (IOQueued = 0, bufPtr = chanPtr->inQueueHead;
5300
                 bufPtr != (ChannelBuffer *) NULL;
5301
                 bufPtr = bufPtr->nextPtr) {
5302
            IOQueued += bufPtr->nextAdded - bufPtr->nextRemoved;
5303
        }
5304
        sprintf(buf, "%d", IOQueued);
5305
        Tcl_AppendResult(interp, buf, (char *) NULL);
5306
        return TCL_OK;
5307
    }
5308
 
5309
    if ((cmdName[0] == 'm') && (strncmp(cmdName, "mode", len) == 0)) {
5310
        if (argc != 3) {
5311
            Tcl_AppendResult(interp, "channel name required",
5312
                    (char *) NULL);
5313
            return TCL_ERROR;
5314
        }
5315
 
5316
        if (chanPtr->flags & TCL_READABLE) {
5317
            Tcl_AppendElement(interp, "read");
5318
        } else {
5319
            Tcl_AppendElement(interp, "");
5320
        }
5321
        if (chanPtr->flags & TCL_WRITABLE) {
5322
            Tcl_AppendElement(interp, "write");
5323
        } else {
5324
            Tcl_AppendElement(interp, "");
5325
        }
5326
        return TCL_OK;
5327
    }
5328
 
5329
    if ((cmdName[0] == 'n') && (strncmp(cmdName, "name", len) == 0)) {
5330
        if (argc != 3) {
5331
            Tcl_AppendResult(interp, "channel name required",
5332
                    (char *) NULL);
5333
            return TCL_ERROR;
5334
        }
5335
        Tcl_AppendResult(interp, chanPtr->channelName, (char *) NULL);
5336
        return TCL_OK;
5337
    }
5338
 
5339
    if ((cmdName[0] == 'o') && (strncmp(cmdName, "open", len) == 0)) {
5340
        hTblPtr = (Tcl_HashTable *) Tcl_GetAssocData(interp, "tclIO", NULL);
5341
        if (hTblPtr == (Tcl_HashTable *) NULL) {
5342
            return TCL_OK;
5343
        }
5344
        for (hPtr = Tcl_FirstHashEntry(hTblPtr, &hSearch);
5345
                 hPtr != (Tcl_HashEntry *) NULL;
5346
                 hPtr = Tcl_NextHashEntry(&hSearch)) {
5347
            Tcl_AppendElement(interp, Tcl_GetHashKey(hTblPtr, hPtr));
5348
        }
5349
        return TCL_OK;
5350
    }
5351
 
5352
    if ((cmdName[0] == 'o') &&
5353
            (strncmp(cmdName, "outputbuffered", len) == 0)) {
5354
        if (argc != 3) {
5355
            Tcl_AppendResult(interp, "channel name required",
5356
                    (char *) NULL);
5357
            return TCL_ERROR;
5358
        }
5359
 
5360
        IOQueued = 0;
5361
        if (chanPtr->curOutPtr != (ChannelBuffer *) NULL) {
5362
            IOQueued = chanPtr->curOutPtr->nextAdded -
5363
                chanPtr->curOutPtr->nextRemoved;
5364
        }
5365
        for (bufPtr = chanPtr->outQueueHead;
5366
                 bufPtr != (ChannelBuffer *) NULL;
5367
                 bufPtr = bufPtr->nextPtr) {
5368
            IOQueued += (bufPtr->nextAdded - bufPtr->nextRemoved);
5369
        }
5370
        sprintf(buf, "%d", IOQueued);
5371
        Tcl_AppendResult(interp, buf, (char *) NULL);
5372
        return TCL_OK;
5373
    }
5374
 
5375
    if ((cmdName[0] == 'q') &&
5376
            (strncmp(cmdName, "queuedcr", len) == 0)) {
5377
        if (argc != 3) {
5378
            Tcl_AppendResult(interp, "channel name required",
5379
                    (char *) NULL);
5380
            return TCL_ERROR;
5381
        }
5382
 
5383
        Tcl_AppendResult(interp,
5384
                (chanPtr->flags & INPUT_SAW_CR) ? "1" : "0",
5385
                (char *) NULL);
5386
        return TCL_OK;
5387
    }
5388
 
5389
    if ((cmdName[0] == 'r') && (strncmp(cmdName, "readable", len) == 0)) {
5390
        hTblPtr = (Tcl_HashTable *) Tcl_GetAssocData(interp, "tclIO", NULL);
5391
        if (hTblPtr == (Tcl_HashTable *) NULL) {
5392
            return TCL_OK;
5393
        }
5394
        for (hPtr = Tcl_FirstHashEntry(hTblPtr, &hSearch);
5395
                 hPtr != (Tcl_HashEntry *) NULL;
5396
                 hPtr = Tcl_NextHashEntry(&hSearch)) {
5397
            chanPtr = (Channel *) Tcl_GetHashValue(hPtr);
5398
            if (chanPtr->flags & TCL_READABLE) {
5399
                Tcl_AppendElement(interp, Tcl_GetHashKey(hTblPtr, hPtr));
5400
            }
5401
        }
5402
        return TCL_OK;
5403
    }
5404
 
5405
    if ((cmdName[0] == 'r') && (strncmp(cmdName, "refcount", len) == 0)) {
5406
        if (argc != 3) {
5407
            Tcl_AppendResult(interp, "channel name required",
5408
                    (char *) NULL);
5409
            return TCL_ERROR;
5410
        }
5411
 
5412
        sprintf(buf, "%d", chanPtr->refCount);
5413
        Tcl_AppendResult(interp, buf, (char *) NULL);
5414
        return TCL_OK;
5415
    }
5416
 
5417
    if ((cmdName[0] == 't') && (strncmp(cmdName, "type", len) == 0)) {
5418
        if (argc != 3) {
5419
            Tcl_AppendResult(interp, "channel name required",
5420
                    (char *) NULL);
5421
            return TCL_ERROR;
5422
        }
5423
        Tcl_AppendResult(interp, chanPtr->typePtr->typeName, (char *) NULL);
5424
        return TCL_OK;
5425
    }
5426
 
5427
    if ((cmdName[0] == 'w') && (strncmp(cmdName, "writable", len) == 0)) {
5428
        hTblPtr = (Tcl_HashTable *) Tcl_GetAssocData(interp, "tclIO", NULL);
5429
        if (hTblPtr == (Tcl_HashTable *) NULL) {
5430
            return TCL_OK;
5431
        }
5432
        for (hPtr = Tcl_FirstHashEntry(hTblPtr, &hSearch);
5433
                 hPtr != (Tcl_HashEntry *) NULL;
5434
                 hPtr = Tcl_NextHashEntry(&hSearch)) {
5435
            chanPtr = (Channel *) Tcl_GetHashValue(hPtr);
5436
            if (chanPtr->flags & TCL_WRITABLE) {
5437
                Tcl_AppendElement(interp, Tcl_GetHashKey(hTblPtr, hPtr));
5438
            }
5439
        }
5440
        return TCL_OK;
5441
    }
5442
 
5443
    Tcl_AppendResult(interp, "bad option \"", cmdName, "\": should be ",
5444
            "info, open, readable, or writable",
5445
            (char *) NULL);
5446
    return TCL_ERROR;
5447
}
5448
 
5449
/*
5450
 *----------------------------------------------------------------------
5451
 *
5452
 * TclTestChannelEventCmd --
5453
 *
5454
 *      This procedure implements the "testchannelevent" command. It is
5455
 *      used to test the Tcl channel event mechanism. It is present in
5456
 *      this file instead of tclTest.c because it needs access to the
5457
 *      internal structure of the channel.
5458
 *
5459
 * Results:
5460
 *      A standard Tcl result.
5461
 *
5462
 * Side effects:
5463
 *      Creates, deletes and returns channel event handlers.
5464
 *
5465
 *----------------------------------------------------------------------
5466
 */
5467
 
5468
        /* ARGSUSED */
5469
int
5470
TclTestChannelEventCmd(dummy, interp, argc, argv)
5471
    ClientData dummy;                   /* Not used. */
5472
    Tcl_Interp *interp;                 /* Current interpreter. */
5473
    int argc;                           /* Number of arguments. */
5474
    char **argv;                        /* Argument strings. */
5475
{
5476
    Channel *chanPtr;
5477
    EventScriptRecord *esPtr, *prevEsPtr, *nextEsPtr;
5478
    char *cmd;
5479
    int index, i, mask, len;
5480
 
5481
    if ((argc < 3) || (argc > 5)) {
5482
        Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
5483
                " channelName cmd ?arg1? ?arg2?\"", (char *) NULL);
5484
        return TCL_ERROR;
5485
    }
5486
    chanPtr = (Channel *) Tcl_GetChannel(interp, argv[1], NULL);
5487
    if (chanPtr == (Channel *) NULL) {
5488
        return TCL_ERROR;
5489
    }
5490
    cmd = argv[2];
5491
    len = strlen(cmd);
5492
    if ((cmd[0] == 'a') && (strncmp(cmd, "add", (unsigned) len) == 0)) {
5493
        if (argc != 5) {
5494
            Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
5495
                    " channelName add eventSpec script\"", (char *) NULL);
5496
            return TCL_ERROR;
5497
        }
5498
        if (strcmp(argv[3], "readable") == 0) {
5499
            mask = TCL_READABLE;
5500
        } else if (strcmp(argv[3], "writable") == 0) {
5501
            mask = TCL_WRITABLE;
5502
        } else if (strcmp(argv[3], "none") == 0) {
5503
            mask = 0;
5504
        } else {
5505
            Tcl_AppendResult(interp, "bad event name \"", argv[3],
5506
                    "\": must be readable, writable, or none", (char *) NULL);
5507
            return TCL_ERROR;
5508
        }
5509
 
5510
        esPtr = (EventScriptRecord *) ckalloc((unsigned)
5511
                sizeof(EventScriptRecord));
5512
        esPtr->nextPtr = chanPtr->scriptRecordPtr;
5513
        chanPtr->scriptRecordPtr = esPtr;
5514
 
5515
        esPtr->chanPtr = chanPtr;
5516
        esPtr->interp = interp;
5517
        esPtr->mask = mask;
5518
        esPtr->script = ckalloc((unsigned) (strlen(argv[4]) + 1));
5519
        strcpy(esPtr->script, argv[4]);
5520
 
5521
        Tcl_CreateChannelHandler((Tcl_Channel) chanPtr, mask,
5522
                ChannelEventScriptInvoker, (ClientData) esPtr);
5523
 
5524
        return TCL_OK;
5525
    }
5526
 
5527
    if ((cmd[0] == 'd') && (strncmp(cmd, "delete", (unsigned) len) == 0)) {
5528
        if (argc != 4) {
5529
            Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
5530
                    " channelName delete index\"", (char *) NULL);
5531
            return TCL_ERROR;
5532
        }
5533
        if (Tcl_GetInt(interp, argv[3], &index) == TCL_ERROR) {
5534
            return TCL_ERROR;
5535
        }
5536
        if (index < 0) {
5537
            Tcl_AppendResult(interp, "bad event index: ", argv[3],
5538
                    ": must be nonnegative", (char *) NULL);
5539
            return TCL_ERROR;
5540
        }
5541
        for (i = 0, esPtr = chanPtr->scriptRecordPtr;
5542
                 (i < index) && (esPtr != (EventScriptRecord *) NULL);
5543
                 i++, esPtr = esPtr->nextPtr) {
5544
            /* Empty loop body. */
5545
        }
5546
        if (esPtr == (EventScriptRecord *) NULL) {
5547
            Tcl_AppendResult(interp, "bad event index ", argv[3],
5548
                    ": out of range", (char *) NULL);
5549
            return TCL_ERROR;
5550
        }
5551
        if (esPtr == chanPtr->scriptRecordPtr) {
5552
            chanPtr->scriptRecordPtr = esPtr->nextPtr;
5553
        } else {
5554
            for (prevEsPtr = chanPtr->scriptRecordPtr;
5555
                     (prevEsPtr != (EventScriptRecord *) NULL) &&
5556
                         (prevEsPtr->nextPtr != esPtr);
5557
                     prevEsPtr = prevEsPtr->nextPtr) {
5558
                /* Empty loop body. */
5559
            }
5560
            if (prevEsPtr == (EventScriptRecord *) NULL) {
5561
                panic("TclTestChannelEventCmd: damaged event script list");
5562
            }
5563
            prevEsPtr->nextPtr = esPtr->nextPtr;
5564
        }
5565
        Tcl_DeleteChannelHandler((Tcl_Channel) chanPtr,
5566
                ChannelEventScriptInvoker, (ClientData) esPtr);
5567
        ckfree(esPtr->script);
5568
        ckfree((char *) esPtr);
5569
 
5570
        return TCL_OK;
5571
    }
5572
 
5573
    if ((cmd[0] == 'l') && (strncmp(cmd, "list", (unsigned) len) == 0)) {
5574
        if (argc != 3) {
5575
            Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
5576
                    " channelName list\"", (char *) NULL);
5577
            return TCL_ERROR;
5578
        }
5579
        for (esPtr = chanPtr->scriptRecordPtr;
5580
                 esPtr != (EventScriptRecord *) NULL;
5581
                 esPtr = esPtr->nextPtr) {
5582
            char *event;
5583
            if (esPtr->mask) {
5584
                event = ((esPtr->mask == TCL_READABLE)
5585
                        ? "readable" : "writable");
5586
            } else {
5587
                event = "none";
5588
            }
5589
            Tcl_AppendElement(interp, event);
5590
            Tcl_AppendElement(interp, esPtr->script);
5591
        }
5592
        return TCL_OK;
5593
    }
5594
 
5595
    if ((cmd[0] == 'r') && (strncmp(cmd, "removeall", (unsigned) len) == 0)) {
5596
        if (argc != 3) {
5597
            Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
5598
                    " channelName removeall\"", (char *) NULL);
5599
            return TCL_ERROR;
5600
        }
5601
        for (esPtr = chanPtr->scriptRecordPtr;
5602
                 esPtr != (EventScriptRecord *) NULL;
5603
                 esPtr = nextEsPtr) {
5604
            nextEsPtr = esPtr->nextPtr;
5605
            Tcl_DeleteChannelHandler((Tcl_Channel) chanPtr,
5606
                    ChannelEventScriptInvoker, (ClientData) esPtr);
5607
            ckfree(esPtr->script);
5608
            ckfree((char *) esPtr);
5609
        }
5610
        chanPtr->scriptRecordPtr = (EventScriptRecord *) NULL;
5611
        return TCL_OK;
5612
    }
5613
 
5614
    if  ((cmd[0] == 's') && (strncmp(cmd, "set", (unsigned) len) == 0)) {
5615
        if (argc != 5) {
5616
            Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
5617
                    " channelName delete index event\"", (char *) NULL);
5618
            return TCL_ERROR;
5619
        }
5620
        if (Tcl_GetInt(interp, argv[3], &index) == TCL_ERROR) {
5621
            return TCL_ERROR;
5622
        }
5623
        if (index < 0) {
5624
            Tcl_AppendResult(interp, "bad event index: ", argv[3],
5625
                    ": must be nonnegative", (char *) NULL);
5626
            return TCL_ERROR;
5627
        }
5628
        for (i = 0, esPtr = chanPtr->scriptRecordPtr;
5629
                 (i < index) && (esPtr != (EventScriptRecord *) NULL);
5630
                 i++, esPtr = esPtr->nextPtr) {
5631
            /* Empty loop body. */
5632
        }
5633
        if (esPtr == (EventScriptRecord *) NULL) {
5634
            Tcl_AppendResult(interp, "bad event index ", argv[3],
5635
                    ": out of range", (char *) NULL);
5636
            return TCL_ERROR;
5637
        }
5638
 
5639
        if (strcmp(argv[4], "readable") == 0) {
5640
            mask = TCL_READABLE;
5641
        } else if (strcmp(argv[4], "writable") == 0) {
5642
            mask = TCL_WRITABLE;
5643
        } else if (strcmp(argv[4], "none") == 0) {
5644
            mask = 0;
5645
        } else {
5646
            Tcl_AppendResult(interp, "bad event name \"", argv[4],
5647
                    "\": must be readable, writable, or none", (char *) NULL);
5648
            return TCL_ERROR;
5649
        }
5650
        esPtr->mask = mask;
5651
        Tcl_CreateChannelHandler((Tcl_Channel) chanPtr, mask,
5652
                ChannelEventScriptInvoker, (ClientData) esPtr);
5653
        return TCL_OK;
5654
    }
5655
    Tcl_AppendResult(interp, "bad command ", cmd, ", must be one of ",
5656
            "add, delete, list, set, or removeall", (char *) NULL);
5657
    return TCL_ERROR;
5658
 
5659
}
5660
 
5661
/*
5662
 *----------------------------------------------------------------------
5663
 *
5664
 * TclCopyChannel --
5665
 *
5666
 *      This routine copies data from one channel to another, either
5667
 *      synchronously or asynchronously.  If a command script is
5668
 *      supplied, the operation runs in the background.  The script
5669
 *      is invoked when the copy completes.  Otherwise the function
5670
 *      waits until the copy is completed before returning.
5671
 *
5672
 * Results:
5673
 *      A standard Tcl result.
5674
 *
5675
 * Side effects:
5676
 *      May schedule a background copy operation that causes both
5677
 *      channels to be marked busy.
5678
 *
5679
 *----------------------------------------------------------------------
5680
 */
5681
 
5682
int
5683
TclCopyChannel(interp, inChan, outChan, toRead, cmdPtr)
5684
    Tcl_Interp *interp;         /* Current interpreter. */
5685
    Tcl_Channel inChan;         /* Channel to read from. */
5686
    Tcl_Channel outChan;        /* Channel to write to. */
5687
    int toRead;                 /* Amount of data to copy, or -1 for all. */
5688
    Tcl_Obj *cmdPtr;            /* Pointer to script to execute or NULL. */
5689
{
5690
    Channel *inPtr = (Channel *) inChan;
5691
    Channel *outPtr = (Channel *) outChan;
5692
    int readFlags, writeFlags;
5693
    CopyState *csPtr;
5694
    int nonBlocking = (cmdPtr) ? CHANNEL_NONBLOCKING : 0;
5695
 
5696
    if (inPtr->csPtr) {
5697
        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "channel \"",
5698
                Tcl_GetChannelName(inChan), "\" is busy", NULL);
5699
        return TCL_ERROR;
5700
    }
5701
    if (outPtr->csPtr) {
5702
        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "channel \"",
5703
                Tcl_GetChannelName(outChan), "\" is busy", NULL);
5704
        return TCL_ERROR;
5705
    }
5706
 
5707
    readFlags = inPtr->flags;
5708
    writeFlags = outPtr->flags;
5709
 
5710
    /*
5711
     * Set up the blocking mode appropriately.  Background copies need
5712
     * non-blocking channels.  Foreground copies need blocking channels.
5713
     * If there is an error, restore the old blocking mode.
5714
     */
5715
 
5716
    if (nonBlocking != (readFlags & CHANNEL_NONBLOCKING)) {
5717
        if (SetBlockMode(interp, inPtr,
5718
                nonBlocking ? TCL_MODE_NONBLOCKING : TCL_MODE_BLOCKING)
5719
                != TCL_OK) {
5720
            return TCL_ERROR;
5721
        }
5722
    }
5723
    if (inPtr != outPtr) {
5724
        if (nonBlocking != (writeFlags & CHANNEL_NONBLOCKING)) {
5725
            if (SetBlockMode(NULL, outPtr,
5726
                    nonBlocking ? TCL_MODE_BLOCKING : TCL_MODE_NONBLOCKING)
5727
                    != TCL_OK) {
5728
                if (nonBlocking != (readFlags & CHANNEL_NONBLOCKING)) {
5729
                    SetBlockMode(NULL, inPtr,
5730
                            (readFlags & CHANNEL_NONBLOCKING)
5731
                            ? TCL_MODE_NONBLOCKING : TCL_MODE_BLOCKING);
5732
                    return TCL_ERROR;
5733
                }
5734
            }
5735
        }
5736
    }
5737
 
5738
    /*
5739
     * Make sure the output side is unbuffered.
5740
     */
5741
 
5742
    outPtr->flags = (outPtr->flags & ~(CHANNEL_LINEBUFFERED))
5743
        | CHANNEL_UNBUFFERED;
5744
 
5745
    /*
5746
     * Allocate a new CopyState to maintain info about the current copy in
5747
     * progress.  This structure will be deallocated when the copy is
5748
     * completed.
5749
     */
5750
 
5751
    csPtr = (CopyState*) ckalloc(sizeof(CopyState) + inPtr->bufSize);
5752
    csPtr->bufSize = inPtr->bufSize;
5753
    csPtr->readPtr = inPtr;
5754
    csPtr->writePtr = outPtr;
5755
    csPtr->readFlags = readFlags;
5756
    csPtr->writeFlags = writeFlags;
5757
    csPtr->toRead = toRead;
5758
    csPtr->total = 0;
5759
    csPtr->interp = interp;
5760
    if (cmdPtr) {
5761
        Tcl_IncrRefCount(cmdPtr);
5762
    }
5763
    csPtr->cmdPtr = cmdPtr;
5764
    inPtr->csPtr = csPtr;
5765
    outPtr->csPtr = csPtr;
5766
 
5767
    /*
5768
     * Start copying data between the channels.
5769
     */
5770
 
5771
    return CopyData(csPtr, 0);
5772
}
5773
 
5774
/*
5775
 *----------------------------------------------------------------------
5776
 *
5777
 * CopyData --
5778
 *
5779
 *      This function implements the lowest level of the copying
5780
 *      mechanism for TclCopyChannel.
5781
 *
5782
 * Results:
5783
 *      Returns TCL_OK on success, else TCL_ERROR.
5784
 *
5785
 * Side effects:
5786
 *      Moves data between channels, may create channel handlers.
5787
 *
5788
 *----------------------------------------------------------------------
5789
 */
5790
 
5791
static int
5792
CopyData(csPtr, mask)
5793
    CopyState *csPtr;           /* State of copy operation. */
5794
    int mask;                   /* Current channel event flags. */
5795
{
5796
    Tcl_Interp *interp;
5797
    Tcl_Obj *cmdPtr, *errObj = NULL;
5798
    Tcl_Channel inChan, outChan;
5799
    int result = TCL_OK;
5800
    int size;
5801
    int total;
5802
 
5803
    inChan = (Tcl_Channel)csPtr->readPtr;
5804
    outChan = (Tcl_Channel)csPtr->writePtr;
5805
    interp = csPtr->interp;
5806
    cmdPtr = csPtr->cmdPtr;
5807
 
5808
    /*
5809
     * Copy the data the slow way, using the translation mechanism.
5810
     */
5811
 
5812
    while (csPtr->toRead != 0) {
5813
 
5814
        /*
5815
         * Check for unreported background errors.
5816
         */
5817
 
5818
        if (csPtr->readPtr->unreportedError != 0) {
5819
            Tcl_SetErrno(csPtr->readPtr->unreportedError);
5820
            csPtr->readPtr->unreportedError = 0;
5821
            goto readError;
5822
        }
5823
        if (csPtr->writePtr->unreportedError != 0) {
5824
            Tcl_SetErrno(csPtr->writePtr->unreportedError);
5825
            csPtr->writePtr->unreportedError = 0;
5826
            goto writeError;
5827
        }
5828
 
5829
        /*
5830
         * Read up to bufSize bytes.
5831
         */
5832
 
5833
        if ((csPtr->toRead == -1)
5834
                || (csPtr->toRead > csPtr->bufSize)) {
5835
            size = csPtr->bufSize;
5836
        } else {
5837
            size = csPtr->toRead;
5838
        }
5839
        size = DoRead(csPtr->readPtr, csPtr->buffer, size);
5840
 
5841
        if (size < 0) {
5842
            readError:
5843
            errObj = Tcl_NewObj();
5844
            Tcl_AppendStringsToObj(errObj, "error reading \"",
5845
                    Tcl_GetChannelName(inChan), "\": ",
5846
                    Tcl_PosixError(interp), (char *) NULL);
5847
            break;
5848
        } else if (size == 0) {
5849
            /*
5850
             * We had an underflow on the read side.  If we are at EOF,
5851
             * then the copying is done, otherwise set up a channel
5852
             * handler to detect when the channel becomes readable again.
5853
             */
5854
 
5855
            if (Tcl_Eof(inChan)) {
5856
                break;
5857
            } else if (!(mask & TCL_READABLE)) {
5858
                if (mask & TCL_WRITABLE) {
5859
                    Tcl_DeleteChannelHandler(outChan, CopyEventProc,
5860
                            (ClientData) csPtr);
5861
                }
5862
                Tcl_CreateChannelHandler(inChan, TCL_READABLE,
5863
                        CopyEventProc, (ClientData) csPtr);
5864
            }
5865
            return TCL_OK;
5866
        }
5867
 
5868
        /*
5869
         * Now write the buffer out.
5870
         */
5871
 
5872
        size = DoWrite(csPtr->writePtr, csPtr->buffer, size);
5873
        if (size < 0) {
5874
            writeError:
5875
            errObj = Tcl_NewObj();
5876
            Tcl_AppendStringsToObj(errObj, "error writing \"",
5877
                    Tcl_GetChannelName(outChan), "\": ",
5878
                    Tcl_PosixError(interp), (char *) NULL);
5879
            break;
5880
        }
5881
 
5882
        /*
5883
         * Check to see if the write is happening in the background.  If so,
5884
         * stop copying and wait for the channel to become writable again.
5885
         */
5886
 
5887
        if (csPtr->writePtr->flags & BG_FLUSH_SCHEDULED) {
5888
            if (!(mask & TCL_WRITABLE)) {
5889
                if (mask & TCL_READABLE) {
5890
                    Tcl_DeleteChannelHandler(outChan, CopyEventProc,
5891
                            (ClientData) csPtr);
5892
                }
5893
                Tcl_CreateChannelHandler(outChan, TCL_WRITABLE,
5894
                        CopyEventProc, (ClientData) csPtr);
5895
            }
5896
            return TCL_OK;
5897
        }
5898
 
5899
        /*
5900
         * Update the current byte count if we care.
5901
         */
5902
 
5903
        if (csPtr->toRead != -1) {
5904
            csPtr->toRead -= size;
5905
        }
5906
        csPtr->total += size;
5907
 
5908
        /*
5909
         * For background copies, we only do one buffer per invocation so
5910
         * we don't starve the rest of the system.
5911
         */
5912
 
5913
        if (cmdPtr) {
5914
            /*
5915
             * The first time we enter this code, there won't be a
5916
             * channel handler established yet, so do it here.
5917
             */
5918
 
5919
            if (mask == 0) {
5920
                Tcl_CreateChannelHandler(outChan, TCL_WRITABLE,
5921
                        CopyEventProc, (ClientData) csPtr);
5922
            }
5923
            return TCL_OK;
5924
        }
5925
    }
5926
 
5927
    /*
5928
     * Make the callback or return the number of bytes transferred.
5929
     * The local total is used because StopCopy frees csPtr.
5930
     */
5931
 
5932
    total = csPtr->total;
5933
    if (cmdPtr) {
5934
        /*
5935
         * Get a private copy of the command so we can mutate it
5936
         * by adding arguments.  Note that StopCopy frees our saved
5937
         * reference to the original command obj.
5938
         */
5939
 
5940
        cmdPtr = Tcl_DuplicateObj(cmdPtr);
5941
        Tcl_IncrRefCount(cmdPtr);
5942
        StopCopy(csPtr);
5943
        Tcl_Preserve((ClientData) interp);
5944
 
5945
        Tcl_ListObjAppendElement(interp, cmdPtr, Tcl_NewIntObj(total));
5946
        if (errObj) {
5947
            Tcl_ListObjAppendElement(interp, cmdPtr, errObj);
5948
        }
5949
        if (Tcl_GlobalEvalObj(interp, cmdPtr) != TCL_OK) {
5950
            Tcl_BackgroundError(interp);
5951
            result = TCL_ERROR;
5952
        }
5953
        Tcl_DecrRefCount(cmdPtr);
5954
        Tcl_Release((ClientData) interp);
5955
    } else {
5956
        StopCopy(csPtr);
5957
        if (errObj) {
5958
            Tcl_SetObjResult(interp, errObj);
5959
            result = TCL_ERROR;
5960
        } else {
5961
            Tcl_ResetResult(interp);
5962
            Tcl_SetIntObj(Tcl_GetObjResult(interp), total);
5963
        }
5964
    }
5965
    return result;
5966
}
5967
 
5968
/*
5969
 *----------------------------------------------------------------------
5970
 *
5971
 * CopyEventProc --
5972
 *
5973
 *      This routine is invoked as a channel event handler for
5974
 *      the background copy operation.  It is just a trivial wrapper
5975
 *      around the CopyData routine.
5976
 *
5977
 * Results:
5978
 *      None.
5979
 *
5980
 * Side effects:
5981
 *      None.
5982
 *
5983
 *----------------------------------------------------------------------
5984
 */
5985
 
5986
static void
5987
CopyEventProc(clientData, mask)
5988
    ClientData clientData;
5989
    int mask;
5990
{
5991
    (void) CopyData((CopyState *)clientData, mask);
5992
}
5993
 
5994
/*
5995
 *----------------------------------------------------------------------
5996
 *
5997
 * StopCopy --
5998
 *
5999
 *      This routine halts a copy that is in progress.
6000
 *
6001
 * Results:
6002
 *      None.
6003
 *
6004
 * Side effects:
6005
 *      Removes any pending channel handlers and restores the blocking
6006
 *      and buffering modes of the channels.  The CopyState is freed.
6007
 *
6008
 *----------------------------------------------------------------------
6009
 */
6010
 
6011
static void
6012
StopCopy(csPtr)
6013
    CopyState *csPtr;           /* State for bg copy to stop . */
6014
{
6015
    int nonBlocking;
6016
 
6017
    if (!csPtr) {
6018
        return;
6019
    }
6020
 
6021
    /*
6022
     * Restore the old blocking mode and output buffering mode.
6023
     */
6024
 
6025
    nonBlocking = (csPtr->readFlags & CHANNEL_NONBLOCKING);
6026
    if (nonBlocking != (csPtr->readPtr->flags & CHANNEL_NONBLOCKING)) {
6027
        SetBlockMode(NULL, csPtr->readPtr,
6028
                nonBlocking ? TCL_MODE_NONBLOCKING : TCL_MODE_BLOCKING);
6029
    }
6030
    if (csPtr->writePtr != csPtr->writePtr) {
6031
        if (nonBlocking != (csPtr->writePtr->flags & CHANNEL_NONBLOCKING)) {
6032
            SetBlockMode(NULL, csPtr->writePtr,
6033
                    nonBlocking ? TCL_MODE_NONBLOCKING : TCL_MODE_BLOCKING);
6034
        }
6035
    }
6036
    csPtr->writePtr->flags &= ~(CHANNEL_LINEBUFFERED | CHANNEL_UNBUFFERED);
6037
    csPtr->writePtr->flags |=
6038
        csPtr->writeFlags & (CHANNEL_LINEBUFFERED | CHANNEL_UNBUFFERED);
6039
 
6040
 
6041
    if (csPtr->cmdPtr) {
6042
        Tcl_DeleteChannelHandler((Tcl_Channel)csPtr->readPtr, CopyEventProc,
6043
            (ClientData)csPtr);
6044
        if (csPtr->readPtr != csPtr->writePtr) {
6045
            Tcl_DeleteChannelHandler((Tcl_Channel)csPtr->writePtr,
6046
                    CopyEventProc, (ClientData)csPtr);
6047
        }
6048
        Tcl_DecrRefCount(csPtr->cmdPtr);
6049
    }
6050
    csPtr->readPtr->csPtr = NULL;
6051
    csPtr->writePtr->csPtr = NULL;
6052
    ckfree((char*) csPtr);
6053
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.