Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/tcl8.5.2/generic/tclIOCmd.c @ 63

Last change on this file since 63 was 25, checked in by landauf, 17 years ago

added tcl to libs

File size: 48.3 KB
Line 
1/*
2 * tclIOCmd.c --
3 *
4 *      Contains the definitions of most of the Tcl commands relating to IO.
5 *
6 * Copyright (c) 1995-1997 Sun Microsystems, Inc.
7 *
8 * See the file "license.terms" for information on usage and redistribution of
9 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
10 *
11 * RCS: @(#) $Id: tclIOCmd.c,v 1.51 2007/12/13 15:23:18 dgp Exp $
12 */
13
14#include "tclInt.h"
15
16/*
17 * Callback structure for accept callback in a TCP server.
18 */
19
20typedef struct AcceptCallback {
21    char *script;                       /* Script to invoke. */
22    Tcl_Interp *interp;                 /* Interpreter in which to run it. */
23} AcceptCallback;
24
25/*
26 * Thread local storage used to maintain a per-thread stdout channel obj.
27 * It must be per-thread because of std channel limitations.
28 */
29
30typedef struct ThreadSpecificData {
31    int initialized;            /* Set to 1 when the module is initialized. */
32    Tcl_Obj *stdoutObjPtr;      /* Cached stdout channel Tcl_Obj */
33} ThreadSpecificData;
34
35static Tcl_ThreadDataKey dataKey;
36
37/*
38 * Static functions for this file:
39 */
40
41static void             FinalizeIOCmdTSD(ClientData clientData);
42static void             AcceptCallbackProc(ClientData callbackData,
43                            Tcl_Channel chan, char *address, int port);
44static int              ChanPendingObjCmd(ClientData unused,
45                            Tcl_Interp *interp, int objc,
46                            Tcl_Obj *const objv[]);
47static int              ChanTruncateObjCmd(ClientData dummy,
48                            Tcl_Interp *interp, int objc,
49                            Tcl_Obj *const objv[]);
50static void             RegisterTcpServerInterpCleanup(Tcl_Interp *interp,
51                            AcceptCallback *acceptCallbackPtr);
52static void             TcpAcceptCallbacksDeleteProc(ClientData clientData,
53                            Tcl_Interp *interp);
54static void             TcpServerCloseProc(ClientData callbackData);
55static void             UnregisterTcpServerInterpCleanupProc(
56                            Tcl_Interp *interp,
57                            AcceptCallback *acceptCallbackPtr);
58
59/*
60 *----------------------------------------------------------------------
61 *
62 * FinalizeIOCmdTSD --
63 *
64 *      Release the storage associated with the per-thread cache.
65 *
66 * Results:
67 *      None.
68 *
69 * Side effects:
70 *      None.
71 *
72 *----------------------------------------------------------------------
73 */
74
75static void
76FinalizeIOCmdTSD(
77    ClientData clientData)      /* Not used. */
78{
79    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
80
81    if (tsdPtr->stdoutObjPtr != NULL) {
82        Tcl_DecrRefCount(tsdPtr->stdoutObjPtr);
83        tsdPtr->stdoutObjPtr = NULL;
84    }
85    tsdPtr->initialized = 0;
86}
87
88/*
89 *----------------------------------------------------------------------
90 *
91 * Tcl_PutsObjCmd --
92 *
93 *      This function is invoked to process the "puts" Tcl command. See the
94 *      user documentation for details on what it does.
95 *
96 * Results:
97 *      A standard Tcl result.
98 *
99 * Side effects:
100 *      Produces output on a channel.
101 *
102 *----------------------------------------------------------------------
103 */
104
105        /* ARGSUSED */
106int
107Tcl_PutsObjCmd(
108    ClientData dummy,           /* Not used. */
109    Tcl_Interp *interp,         /* Current interpreter. */
110    int objc,                   /* Number of arguments. */
111    Tcl_Obj *const objv[])      /* Argument objects. */
112{
113    Tcl_Channel chan;           /* The channel to puts on. */
114    Tcl_Obj *string;            /* String to write. */
115    Tcl_Obj *chanObjPtr = NULL; /* channel object. */
116    int newline;                /* Add a newline at end? */
117    int result;                 /* Result of puts operation. */
118    int mode;                   /* Mode in which channel is opened. */
119    ThreadSpecificData *tsdPtr;
120
121    switch (objc) {
122    case 2: /* [puts $x] */
123        string = objv[1];
124        newline = 1;
125        break;
126
127    case 3: /* [puts -nonewline $x] or [puts $chan $x] */
128        if (strcmp(TclGetString(objv[1]), "-nonewline") == 0) {
129            newline = 0;
130        } else {
131            newline = 1;
132            chanObjPtr = objv[1];
133        }
134        string = objv[2];
135        break;
136
137    case 4: /* [puts -nonewline $chan $x] or [puts $chan $x nonewline] */
138        if (strcmp(TclGetString(objv[1]), "-nonewline") == 0) {
139            chanObjPtr = objv[2];
140            string = objv[3];
141        } else {
142            /*
143             * The code below provides backwards compatibility with an old
144             * form of the command that is no longer recommended or
145             * documented.
146             */
147
148            char *arg;
149            int length;
150
151            arg = TclGetStringFromObj(objv[3], &length);
152            if ((length != 9)
153                    || (strncmp(arg, "nonewline", (size_t) length) != 0)) {
154                Tcl_AppendResult(interp, "bad argument \"", arg,
155                        "\": should be \"nonewline\"", NULL);
156                return TCL_ERROR;
157            }
158            chanObjPtr = objv[1];
159            string = objv[2];
160        }
161        newline = 0;
162        break;
163
164    default:
165        /* [puts] or [puts some bad number of arguments...] */
166        Tcl_WrongNumArgs(interp, 1, objv, "?-nonewline? ?channelId? string");
167        return TCL_ERROR;
168    }
169
170    if (chanObjPtr == NULL) {
171        tsdPtr = TCL_TSD_INIT(&dataKey);
172
173        if (!tsdPtr->initialized) {
174            tsdPtr->initialized = 1;
175            TclNewLiteralStringObj(tsdPtr->stdoutObjPtr, "stdout");
176            Tcl_IncrRefCount(tsdPtr->stdoutObjPtr);
177            Tcl_CreateThreadExitHandler(FinalizeIOCmdTSD, NULL);
178        }
179        chanObjPtr = tsdPtr->stdoutObjPtr;
180    }
181    if (TclGetChannelFromObj(interp, chanObjPtr, &chan, &mode, 0) != TCL_OK) {
182        return TCL_ERROR;
183    }
184    if ((mode & TCL_WRITABLE) == 0) {
185        Tcl_AppendResult(interp, "channel \"", TclGetString(chanObjPtr),
186                "\" wasn't opened for writing", NULL);
187        return TCL_ERROR;
188    }
189
190    result = Tcl_WriteObj(chan, string);
191    if (result < 0) {
192        goto error;
193    }
194    if (newline != 0) {
195        result = Tcl_WriteChars(chan, "\n", 1);
196        if (result < 0) {
197            goto error;
198        }
199    }
200    return TCL_OK;
201
202    /*
203     * TIP #219.
204     * Capture error messages put by the driver into the bypass area and put
205     * them into the regular interpreter result. Fall back to the regular
206     * message if nothing was found in the bypass.
207     */
208
209  error:
210    if (!TclChanCaughtErrorBypass(interp, chan)) {
211        Tcl_AppendResult(interp, "error writing \"",
212                TclGetString(chanObjPtr), "\": ",
213                Tcl_PosixError(interp), NULL);
214    }
215    return TCL_ERROR;
216}
217
218/*
219 *----------------------------------------------------------------------
220 *
221 * Tcl_FlushObjCmd --
222 *
223 *      This function is called to process the Tcl "flush" command. See the
224 *      user documentation for details on what it does.
225 *
226 * Results:
227 *      A standard Tcl result.
228 *
229 * Side effects:
230 *      May cause output to appear on the specified channel.
231 *
232 *----------------------------------------------------------------------
233 */
234
235        /* ARGSUSED */
236int
237Tcl_FlushObjCmd(
238    ClientData dummy,           /* Not used. */
239    Tcl_Interp *interp,         /* Current interpreter. */
240    int objc,                   /* Number of arguments. */
241    Tcl_Obj *const objv[])      /* Argument objects. */
242{
243    Tcl_Obj *chanObjPtr;
244    Tcl_Channel chan;           /* The channel to flush on. */
245    int mode;
246
247    if (objc != 2) {
248        Tcl_WrongNumArgs(interp, 1, objv, "channelId");
249        return TCL_ERROR;
250    }
251    chanObjPtr = objv[1];
252    if (TclGetChannelFromObj(interp, chanObjPtr, &chan, &mode, 0) != TCL_OK) {
253        return TCL_ERROR;
254    }
255    if ((mode & TCL_WRITABLE) == 0) {
256        Tcl_AppendResult(interp, "channel \"", TclGetString(chanObjPtr),
257                "\" wasn't opened for writing", NULL);
258        return TCL_ERROR;
259    }
260
261    if (Tcl_Flush(chan) != TCL_OK) {
262        /*
263         * TIP #219.
264         * Capture error messages put by the driver into the bypass area and
265         * put them into the regular interpreter result. Fall back to the
266         * regular message if nothing was found in the bypass.
267         */
268
269        if (!TclChanCaughtErrorBypass(interp, chan)) {
270            Tcl_AppendResult(interp, "error flushing \"",
271                    TclGetString(chanObjPtr), "\": ",
272                    Tcl_PosixError(interp), NULL);
273        }
274        return TCL_ERROR;
275    }
276    return TCL_OK;
277}
278
279/*
280 *----------------------------------------------------------------------
281 *
282 * Tcl_GetsObjCmd --
283 *
284 *      This function is called to process the Tcl "gets" command. See the
285 *      user documentation for details on what it does.
286 *
287 * Results:
288 *      A standard Tcl result.
289 *
290 * Side effects:
291 *      May consume input from channel.
292 *
293 *----------------------------------------------------------------------
294 */
295
296        /* ARGSUSED */
297int
298Tcl_GetsObjCmd(
299    ClientData dummy,           /* Not used. */
300    Tcl_Interp *interp,         /* Current interpreter. */
301    int objc,                   /* Number of arguments. */
302    Tcl_Obj *const objv[])      /* Argument objects. */
303{
304    Tcl_Channel chan;           /* The channel to read from. */
305    int lineLen;                /* Length of line just read. */
306    int mode;                   /* Mode in which channel is opened. */
307    Tcl_Obj *linePtr, *chanObjPtr;
308
309    if ((objc != 2) && (objc != 3)) {
310        Tcl_WrongNumArgs(interp, 1, objv, "channelId ?varName?");
311        return TCL_ERROR;
312    }
313    chanObjPtr = objv[1];
314    if (TclGetChannelFromObj(interp, chanObjPtr, &chan, &mode, 0) != TCL_OK) {
315        return TCL_ERROR;
316    }
317    if ((mode & TCL_READABLE) == 0) {
318        Tcl_AppendResult(interp, "channel \"", TclGetString(chanObjPtr),
319                "\" wasn't opened for reading", NULL);
320        return TCL_ERROR;
321    }
322
323    linePtr = Tcl_NewObj();
324    lineLen = Tcl_GetsObj(chan, linePtr);
325    if (lineLen < 0) {
326        if (!Tcl_Eof(chan) && !Tcl_InputBlocked(chan)) {
327            Tcl_DecrRefCount(linePtr);
328
329            /*
330             * TIP #219. Capture error messages put by the driver into the
331             * bypass area and put them into the regular interpreter result.
332             * Fall back to the regular message if nothing was found in the
333             * bypass.
334             */
335
336            if (!TclChanCaughtErrorBypass(interp, chan)) {
337                Tcl_ResetResult(interp);
338                Tcl_AppendResult(interp, "error reading \"",
339                        TclGetString(chanObjPtr), "\": ",
340                        Tcl_PosixError(interp), NULL);
341            }
342            return TCL_ERROR;
343        }
344        lineLen = -1;
345    }
346    if (objc == 3) {
347        if (Tcl_ObjSetVar2(interp, objv[2], NULL, linePtr,
348                TCL_LEAVE_ERR_MSG) == NULL) {
349            return TCL_ERROR;
350        }
351        Tcl_SetObjResult(interp, Tcl_NewIntObj(lineLen));
352        return TCL_OK;
353    } else {
354        Tcl_SetObjResult(interp, linePtr);
355    }
356    return TCL_OK;
357}
358
359/*
360 *----------------------------------------------------------------------
361 *
362 * Tcl_ReadObjCmd --
363 *
364 *      This function is invoked to process the Tcl "read" command. See the
365 *      user documentation for details on what it does.
366 *
367 * Results:
368 *      A standard Tcl result.
369 *
370 * Side effects:
371 *      May consume input from channel.
372 *
373 *----------------------------------------------------------------------
374 */
375
376        /* ARGSUSED */
377int
378Tcl_ReadObjCmd(
379    ClientData dummy,           /* Not used. */
380    Tcl_Interp *interp,         /* Current interpreter. */
381    int objc,                   /* Number of arguments. */
382    Tcl_Obj *const objv[])      /* Argument objects. */
383{
384    Tcl_Channel chan;           /* The channel to read from. */
385    int newline, i;             /* Discard newline at end? */
386    int toRead;                 /* How many bytes to read? */
387    int charactersRead;         /* How many characters were read? */
388    int mode;                   /* Mode in which channel is opened. */
389    Tcl_Obj *resultPtr, *chanObjPtr;
390
391    if ((objc != 2) && (objc != 3)) {
392        Interp *iPtr;
393
394    argerror:
395        iPtr = (Interp *) interp;
396        Tcl_WrongNumArgs(interp, 1, objv, "channelId ?numChars?");
397
398        /*
399         * Do not append directly; that makes ensembles using this command as
400         * a subcommand produce the wrong message.
401         */
402
403        iPtr->flags |= INTERP_ALTERNATE_WRONG_ARGS;
404        Tcl_WrongNumArgs(interp, 1, objv, "?-nonewline? channelId");
405        iPtr->flags &= ~INTERP_ALTERNATE_WRONG_ARGS;
406        return TCL_ERROR;
407    }
408
409    i = 1;
410    newline = 0;
411    if (strcmp(TclGetString(objv[1]), "-nonewline") == 0) {
412        newline = 1;
413        i++;
414    }
415
416    if (i == objc) {
417        goto argerror;
418    }
419
420    chanObjPtr = objv[i];
421    if (TclGetChannelFromObj(interp, chanObjPtr, &chan, &mode, 0) != TCL_OK) {
422        return TCL_ERROR;
423    }
424    if ((mode & TCL_READABLE) == 0) {
425        Tcl_AppendResult(interp, "channel \"", TclGetString(chanObjPtr),
426                "\" wasn't opened for reading", NULL);
427        return TCL_ERROR;
428    }
429    i++;        /* Consumed channel name. */
430
431    /*
432     * Compute how many bytes to read, and see whether the final newline
433     * should be dropped.
434     */
435
436    toRead = -1;
437    if (i < objc) {
438        char *arg;
439
440        arg = TclGetString(objv[i]);
441        if (isdigit(UCHAR(arg[0]))) { /* INTL: digit */
442            if (TclGetIntFromObj(interp, objv[i], &toRead) != TCL_OK) {
443                return TCL_ERROR;
444            }
445        } else if (strcmp(arg, "nonewline") == 0) {
446            newline = 1;
447        } else {
448            Tcl_AppendResult(interp, "bad argument \"", arg,
449                    "\": should be \"nonewline\"", NULL);
450            return TCL_ERROR;
451        }
452    }
453
454    resultPtr = Tcl_NewObj();
455    Tcl_IncrRefCount(resultPtr);
456    charactersRead = Tcl_ReadChars(chan, resultPtr, toRead, 0);
457    if (charactersRead < 0) {
458        /*
459         * TIP #219.
460         * Capture error messages put by the driver into the bypass area and
461         * put them into the regular interpreter result. Fall back to the
462         * regular message if nothing was found in the bypass.
463         */
464
465        if (!TclChanCaughtErrorBypass(interp, chan)) {
466            Tcl_ResetResult(interp);
467            Tcl_AppendResult(interp, "error reading \"",
468                    TclGetString(chanObjPtr), "\": ",
469                    Tcl_PosixError(interp), NULL);
470        }
471        Tcl_DecrRefCount(resultPtr);
472        return TCL_ERROR;
473    }
474
475    /*
476     * If requested, remove the last newline in the channel if at EOF.
477     */
478
479    if ((charactersRead > 0) && (newline != 0)) {
480        char *result;
481        int length;
482
483        result = TclGetStringFromObj(resultPtr, &length);
484        if (result[length - 1] == '\n') {
485            Tcl_SetObjLength(resultPtr, length - 1);
486        }
487    }
488    Tcl_SetObjResult(interp, resultPtr);
489    Tcl_DecrRefCount(resultPtr);
490    return TCL_OK;
491}
492
493/*
494 *----------------------------------------------------------------------
495 *
496 * Tcl_SeekObjCmd --
497 *
498 *      This function is invoked to process the Tcl "seek" command. See the
499 *      user documentation for details on what it does.
500 *
501 * Results:
502 *      A standard Tcl result.
503 *
504 * Side effects:
505 *      Moves the position of the access point on the specified channel.  May
506 *      flush queued output.
507 *
508 *----------------------------------------------------------------------
509 */
510
511        /* ARGSUSED */
512int
513Tcl_SeekObjCmd(
514    ClientData clientData,      /* Not used. */
515    Tcl_Interp *interp,         /* Current interpreter. */
516    int objc,                   /* Number of arguments. */
517    Tcl_Obj *const objv[])      /* Argument objects. */
518{
519    Tcl_Channel chan;           /* The channel to tell on. */
520    Tcl_WideInt offset;         /* Where to seek? */
521    int mode;                   /* How to seek? */
522    Tcl_WideInt result;         /* Of calling Tcl_Seek. */
523    int optionIndex;
524    static const char *originOptions[] = {
525        "start", "current", "end", NULL
526    };
527    static int modeArray[] = {SEEK_SET, SEEK_CUR, SEEK_END};
528
529    if ((objc != 3) && (objc != 4)) {
530        Tcl_WrongNumArgs(interp, 1, objv, "channelId offset ?origin?");
531        return TCL_ERROR;
532    }
533    if (TclGetChannelFromObj(interp, objv[1], &chan, NULL, 0) != TCL_OK) {
534        return TCL_ERROR;
535    }
536    if (Tcl_GetWideIntFromObj(interp, objv[2], &offset) != TCL_OK) {
537        return TCL_ERROR;
538    }
539    mode = SEEK_SET;
540    if (objc == 4) {
541        if (Tcl_GetIndexFromObj(interp, objv[3], originOptions, "origin", 0,
542                &optionIndex) != TCL_OK) {
543            return TCL_ERROR;
544        }
545        mode = modeArray[optionIndex];
546    }
547
548    result = Tcl_Seek(chan, offset, mode);
549    if (result == Tcl_LongAsWide(-1)) {
550        /*
551         * TIP #219.
552         * Capture error messages put by the driver into the bypass area and
553         * put them into the regular interpreter result. Fall back to the
554         * regular message if nothing was found in the bypass.
555         */
556        if (!TclChanCaughtErrorBypass(interp, chan)) {
557            Tcl_AppendResult(interp, "error during seek on \"",
558                    TclGetString(objv[1]), "\": ",
559                    Tcl_PosixError(interp), NULL);
560        }
561        return TCL_ERROR;
562    }
563    return TCL_OK;
564}
565
566/*
567 *----------------------------------------------------------------------
568 *
569 * Tcl_TellObjCmd --
570 *
571 *      This function is invoked to process the Tcl "tell" command. See the
572 *      user documentation for details on what it does.
573 *
574 * Results:
575 *      A standard Tcl result.
576 *
577 * Side effects:
578 *      None.
579 *
580 *----------------------------------------------------------------------
581 */
582
583        /* ARGSUSED */
584int
585Tcl_TellObjCmd(
586    ClientData clientData,      /* Not used. */
587    Tcl_Interp *interp,         /* Current interpreter. */
588    int objc,                   /* Number of arguments. */
589    Tcl_Obj *const objv[])      /* Argument objects. */
590{
591    Tcl_Channel chan;           /* The channel to tell on. */
592    Tcl_WideInt newLoc;
593
594    if (objc != 2) {
595        Tcl_WrongNumArgs(interp, 1, objv, "channelId");
596        return TCL_ERROR;
597    }
598
599    /*
600     * Try to find a channel with the right name and permissions in the IO
601     * channel table of this interpreter.
602     */
603
604    if (TclGetChannelFromObj(interp, objv[1], &chan, NULL, 0) != TCL_OK) {
605        return TCL_ERROR;
606    }
607
608    newLoc = Tcl_Tell(chan);
609
610    /*
611     * TIP #219.
612     * Capture error messages put by the driver into the bypass area and put
613     * them into the regular interpreter result.
614     */
615
616    if (TclChanCaughtErrorBypass(interp, chan)) {
617        return TCL_ERROR;
618    }
619
620    Tcl_SetObjResult(interp, Tcl_NewWideIntObj(newLoc));
621    return TCL_OK;
622}
623
624/*
625 *----------------------------------------------------------------------
626 *
627 * Tcl_CloseObjCmd --
628 *
629 *      This function is invoked to process the Tcl "close" command. See the
630 *      user documentation for details on what it does.
631 *
632 * Results:
633 *      A standard Tcl result.
634 *
635 * Side effects:
636 *      May discard queued input; may flush queued output.
637 *
638 *----------------------------------------------------------------------
639 */
640
641        /* ARGSUSED */
642int
643Tcl_CloseObjCmd(
644    ClientData clientData,      /* Not used. */
645    Tcl_Interp *interp,         /* Current interpreter. */
646    int objc,                   /* Number of arguments. */
647    Tcl_Obj *const objv[])      /* Argument objects. */
648{
649    Tcl_Channel chan;           /* The channel to close. */
650
651    if (objc != 2) {
652        Tcl_WrongNumArgs(interp, 1, objv, "channelId");
653        return TCL_ERROR;
654    }
655
656    if (TclGetChannelFromObj(interp, objv[1], &chan, NULL, 0) != TCL_OK) {
657        return TCL_ERROR;
658    }
659
660    if (Tcl_UnregisterChannel(interp, chan) != TCL_OK) {
661        /*
662         * If there is an error message and it ends with a newline, remove the
663         * newline. This is done for command pipeline channels where the error
664         * output from the subprocesses is stored in interp's result.
665         *
666         * NOTE: This is likely to not have any effect on regular error
667         * messages produced by drivers during the closing of a channel,
668         * because the Tcl convention is that such error messages do not have
669         * a terminating newline.
670         */
671
672        Tcl_Obj *resultPtr = Tcl_GetObjResult(interp);
673        char *string;
674        int len;
675
676        if (Tcl_IsShared(resultPtr)) {
677            resultPtr = Tcl_DuplicateObj(resultPtr);
678            Tcl_SetObjResult(interp, resultPtr);
679        }
680        string = TclGetStringFromObj(resultPtr, &len);
681        if ((len > 0) && (string[len - 1] == '\n')) {
682            Tcl_SetObjLength(resultPtr, len - 1);
683        }
684        return TCL_ERROR;
685    }
686
687    return TCL_OK;
688}
689
690/*
691 *----------------------------------------------------------------------
692 *
693 * Tcl_FconfigureObjCmd --
694 *
695 *      This function is invoked to process the Tcl "fconfigure" command. See
696 *      the user documentation for details on what it does.
697 *
698 * Results:
699 *      A standard Tcl result.
700 *
701 * Side effects:
702 *      May modify the behavior of an IO channel.
703 *
704 *----------------------------------------------------------------------
705 */
706
707        /* ARGSUSED */
708int
709Tcl_FconfigureObjCmd(
710    ClientData clientData,      /* Not used. */
711    Tcl_Interp *interp,         /* Current interpreter. */
712    int objc,                   /* Number of arguments. */
713    Tcl_Obj *const objv[])      /* Argument objects. */
714{
715    char *optionName, *valueName;
716    Tcl_Channel chan;           /* The channel to set a mode on. */
717    int i;                      /* Iterate over arg-value pairs. */
718
719    if ((objc < 2) || (((objc % 2) == 1) && (objc != 3))) {
720        Tcl_WrongNumArgs(interp, 1, objv,
721                "channelId ?optionName? ?value? ?optionName value?...");
722        return TCL_ERROR;
723    }
724
725    if (TclGetChannelFromObj(interp, objv[1], &chan, NULL, 0) != TCL_OK) {
726        return TCL_ERROR;
727    }
728
729    if (objc == 2) {
730        Tcl_DString ds;         /* DString to hold result of calling
731                                 * Tcl_GetChannelOption. */
732
733        Tcl_DStringInit(&ds);
734        if (Tcl_GetChannelOption(interp, chan, NULL, &ds) != TCL_OK) {
735            Tcl_DStringFree(&ds);
736            return TCL_ERROR;
737        }
738        Tcl_DStringResult(interp, &ds);
739        return TCL_OK;
740    } else if (objc == 3) {
741        Tcl_DString ds;         /* DString to hold result of calling
742                                 * Tcl_GetChannelOption. */
743
744        Tcl_DStringInit(&ds);
745        optionName = TclGetString(objv[2]);
746        if (Tcl_GetChannelOption(interp, chan, optionName, &ds) != TCL_OK) {
747            Tcl_DStringFree(&ds);
748            return TCL_ERROR;
749        }
750        Tcl_DStringResult(interp, &ds);
751        return TCL_OK;
752    }
753
754    for (i = 3; i < objc; i += 2) {
755        optionName = TclGetString(objv[i-1]);
756        valueName = TclGetString(objv[i]);
757        if (Tcl_SetChannelOption(interp, chan, optionName, valueName)
758                != TCL_OK) {
759            return TCL_ERROR;
760        }
761    }
762
763    return TCL_OK;
764}
765
766/*
767 *---------------------------------------------------------------------------
768 *
769 * Tcl_EofObjCmd --
770 *
771 *      This function is invoked to process the Tcl "eof" command. See the
772 *      user documentation for details on what it does.
773 *
774 * Results:
775 *      A standard Tcl result.
776 *
777 * Side effects:
778 *      Sets interp's result to boolean true or false depending on whether the
779 *      specified channel has an EOF condition.
780 *
781 *---------------------------------------------------------------------------
782 */
783
784        /* ARGSUSED */
785int
786Tcl_EofObjCmd(
787    ClientData unused,          /* Not used. */
788    Tcl_Interp *interp,         /* Current interpreter. */
789    int objc,                   /* Number of arguments. */
790    Tcl_Obj *const objv[])      /* Argument objects. */
791{
792    Tcl_Channel chan;
793
794    if (objc != 2) {
795        Tcl_WrongNumArgs(interp, 1, objv, "channelId");
796        return TCL_ERROR;
797    }
798
799    if (TclGetChannelFromObj(interp, objv[1], &chan, NULL, 0) != TCL_OK) {
800        return TCL_ERROR;
801    }
802
803    Tcl_SetObjResult(interp, Tcl_NewBooleanObj(Tcl_Eof(chan)));
804    return TCL_OK;
805}
806
807/*
808 *----------------------------------------------------------------------
809 *
810 * Tcl_ExecObjCmd --
811 *
812 *      This function is invoked to process the "exec" Tcl command. See the
813 *      user documentation for details on what it does.
814 *
815 * Results:
816 *      A standard Tcl result.
817 *
818 * Side effects:
819 *      See the user documentation.
820 *
821 *----------------------------------------------------------------------
822 */
823
824        /* ARGSUSED */
825int
826Tcl_ExecObjCmd(
827    ClientData dummy,           /* Not used. */
828    Tcl_Interp *interp,         /* Current interpreter. */
829    int objc,                   /* Number of arguments. */
830    Tcl_Obj *const objv[])      /* Argument objects. */
831{
832    /*
833     * This function generates an argv array for the string arguments. It
834     * starts out with stack-allocated space but uses dynamically-allocated
835     * storage if needed.
836     */
837
838    Tcl_Obj *resultPtr;
839    const char **argv;
840    char *string;
841    Tcl_Channel chan;
842    int argc, background, i, index, keepNewline, result, skip, length;
843    int ignoreStderr;
844    static const char *options[] = {
845        "-ignorestderr", "-keepnewline", "--", NULL
846    };
847    enum options {
848        EXEC_IGNORESTDERR, EXEC_KEEPNEWLINE, EXEC_LAST
849    };
850
851    /*
852     * Check for any leading option arguments.
853     */
854
855    keepNewline = 0;
856    ignoreStderr = 0;
857    for (skip = 1; skip < objc; skip++) {
858        string = TclGetString(objv[skip]);
859        if (string[0] != '-') {
860            break;
861        }
862        if (Tcl_GetIndexFromObj(interp, objv[skip], options, "switch",
863                TCL_EXACT, &index) != TCL_OK) {
864            return TCL_ERROR;
865        }
866        if (index == EXEC_KEEPNEWLINE) {
867            keepNewline = 1;
868        } else if (index == EXEC_IGNORESTDERR) {
869            ignoreStderr = 1;
870        } else {
871            skip++;
872            break;
873        }
874    }
875    if (objc <= skip) {
876        Tcl_WrongNumArgs(interp, 1, objv, "?switches? arg ?arg ...?");
877        return TCL_ERROR;
878    }
879
880    /*
881     * See if the command is to be run in background.
882     */
883
884    background = 0;
885    string = TclGetString(objv[objc - 1]);
886    if ((string[0] == '&') && (string[1] == '\0')) {
887        objc--;
888        background = 1;
889    }
890
891    /*
892     * Create the string argument array "argv". Make sure argv is large enough
893     * to hold the argc arguments plus 1 extra for the zero end-of-argv word.
894     */
895
896    argc = objc - skip;
897    argv = (const char **)
898            TclStackAlloc(interp, (unsigned)(argc + 1) * sizeof(char *));
899
900    /*
901     * Copy the string conversions of each (post option) object into the
902     * argument vector.
903     */
904
905    for (i = 0; i < argc; i++) {
906        argv[i] = TclGetString(objv[i + skip]);
907    }
908    argv[argc] = NULL;
909    chan = Tcl_OpenCommandChannel(interp, argc, argv, (background ? 0 :
910            (ignoreStderr ? TCL_STDOUT : TCL_STDOUT|TCL_STDERR)));
911
912    /*
913     * Free the argv array.
914     */
915
916    TclStackFree(interp, (void *)argv);
917
918    if (chan == NULL) {
919        return TCL_ERROR;
920    }
921
922    if (background) {
923        /*
924         * Store the list of PIDs from the pipeline in interp's result and
925         * detach the PIDs (instead of waiting for them).
926         */
927
928        TclGetAndDetachPids(interp, chan);
929        if (Tcl_Close(interp, chan) != TCL_OK) {
930            return TCL_ERROR;
931        }
932        return TCL_OK;
933    }
934
935    resultPtr = Tcl_NewObj();
936    if (Tcl_GetChannelHandle(chan, TCL_READABLE, NULL) == TCL_OK) {
937        if (Tcl_ReadChars(chan, resultPtr, -1, 0) < 0) {
938            /*
939             * TIP #219.
940             * Capture error messages put by the driver into the bypass area
941             * and put them into the regular interpreter result. Fall back to
942             * the regular message if nothing was found in the bypass.
943             */
944
945            if (!TclChanCaughtErrorBypass(interp, chan)) {
946                Tcl_ResetResult(interp);
947                Tcl_AppendResult(interp, "error reading output from command: ",
948                        Tcl_PosixError(interp), NULL);
949                Tcl_DecrRefCount(resultPtr);
950            }
951            return TCL_ERROR;
952        }
953    }
954
955    /*
956     * If the process produced anything on stderr, it will have been returned
957     * in the interpreter result. It needs to be appended to the result
958     * string.
959     */
960
961    result = Tcl_Close(interp, chan);
962    Tcl_AppendObjToObj(resultPtr, Tcl_GetObjResult(interp));
963
964    /*
965     * If the last character of the result is a newline, then remove the
966     * newline character.
967     */
968
969    if (keepNewline == 0) {
970        string = TclGetStringFromObj(resultPtr, &length);
971        if ((length > 0) && (string[length - 1] == '\n')) {
972            Tcl_SetObjLength(resultPtr, length - 1);
973        }
974    }
975    Tcl_SetObjResult(interp, resultPtr);
976
977    return result;
978}
979
980/*
981 *---------------------------------------------------------------------------
982 *
983 * Tcl_FblockedObjCmd --
984 *
985 *      This function is invoked to process the Tcl "fblocked" command. See
986 *      the user documentation for details on what it does.
987 *
988 * Results:
989 *      A standard Tcl result.
990 *
991 * Side effects:
992 *      Sets interp's result to boolean true or false depending on whether the
993 *      preceeding input operation on the channel would have blocked.
994 *
995 *---------------------------------------------------------------------------
996 */
997
998        /* ARGSUSED */
999int
1000Tcl_FblockedObjCmd(
1001    ClientData unused,          /* Not used. */
1002    Tcl_Interp *interp,         /* Current interpreter. */
1003    int objc,                   /* Number of arguments. */
1004    Tcl_Obj *const objv[])      /* Argument objects. */
1005{
1006    Tcl_Channel chan;
1007    int mode;
1008
1009    if (objc != 2) {
1010        Tcl_WrongNumArgs(interp, 1, objv, "channelId");
1011        return TCL_ERROR;
1012    }
1013
1014    if (TclGetChannelFromObj(interp, objv[1], &chan, &mode, 0) != TCL_OK) {
1015        return TCL_ERROR;
1016    }
1017    if ((mode & TCL_READABLE) == 0) {
1018        Tcl_AppendResult(interp, "channel \"", TclGetString(objv[1]),
1019                "\" wasn't opened for reading", NULL);
1020        return TCL_ERROR;
1021    }
1022
1023    Tcl_SetObjResult(interp, Tcl_NewBooleanObj(Tcl_InputBlocked(chan)));
1024    return TCL_OK;
1025}
1026
1027/*
1028 *----------------------------------------------------------------------
1029 *
1030 * Tcl_OpenObjCmd --
1031 *
1032 *      This function is invoked to process the "open" Tcl command. See the
1033 *      user documentation for details on what it does.
1034 *
1035 * Results:
1036 *      A standard Tcl result.
1037 *
1038 * Side effects:
1039 *      See the user documentation.
1040 *
1041 *----------------------------------------------------------------------
1042 */
1043
1044        /* ARGSUSED */
1045int
1046Tcl_OpenObjCmd(
1047    ClientData notUsed,         /* Not used. */
1048    Tcl_Interp *interp,         /* Current interpreter. */
1049    int objc,                   /* Number of arguments. */
1050    Tcl_Obj *const objv[])      /* Argument objects. */
1051{
1052    int pipeline, prot;
1053    const char *modeString, *what;
1054    Tcl_Channel chan;
1055
1056    if ((objc < 2) || (objc > 4)) {
1057        Tcl_WrongNumArgs(interp, 1, objv, "fileName ?access? ?permissions?");
1058        return TCL_ERROR;
1059    }
1060    prot = 0666;
1061    if (objc == 2) {
1062        modeString = "r";
1063    } else {
1064        modeString = TclGetString(objv[2]);
1065        if (objc == 4) {
1066            char *permString = TclGetString(objv[3]);
1067            int code = TCL_ERROR;
1068            int scanned = TclParseAllWhiteSpace(permString, -1);
1069
1070            /* Support legacy octal numbers */
1071            if ((permString[scanned] == '0')
1072                    && (permString[scanned+1] >= '0')
1073                    && (permString[scanned+1] <= '7')) {
1074
1075                Tcl_Obj *permObj;
1076
1077                TclNewLiteralStringObj(permObj, "0o");
1078                Tcl_AppendToObj(permObj, permString+scanned+1, -1);
1079                code = TclGetIntFromObj(NULL, permObj, &prot);
1080                Tcl_DecrRefCount(permObj);
1081            }
1082
1083            if ((code == TCL_ERROR)
1084                    && TclGetIntFromObj(interp, objv[3], &prot) != TCL_OK) {
1085                return TCL_ERROR;
1086            }
1087        }
1088    }
1089
1090    pipeline = 0;
1091    what = TclGetString(objv[1]);
1092    if (what[0] == '|') {
1093        pipeline = 1;
1094    }
1095
1096    /*
1097     * Open the file or create a process pipeline.
1098     */
1099
1100    if (!pipeline) {
1101        chan = Tcl_FSOpenFileChannel(interp, objv[1], modeString, prot);
1102    } else {
1103        int mode, seekFlag, cmdObjc, binary;
1104        const char **cmdArgv;
1105
1106        if (Tcl_SplitList(interp, what+1, &cmdObjc, &cmdArgv) != TCL_OK) {
1107            return TCL_ERROR;
1108        }
1109
1110        mode = TclGetOpenModeEx(interp, modeString, &seekFlag, &binary);
1111        if (mode == -1) {
1112            chan = NULL;
1113        } else {
1114            int flags = TCL_STDERR | TCL_ENFORCE_MODE;
1115
1116            switch (mode & (O_RDONLY | O_WRONLY | O_RDWR)) {
1117            case O_RDONLY:
1118                flags |= TCL_STDOUT;
1119                break;
1120            case O_WRONLY:
1121                flags |= TCL_STDIN;
1122                break;
1123            case O_RDWR:
1124                flags |= (TCL_STDIN | TCL_STDOUT);
1125                break;
1126            default:
1127                Tcl_Panic("Tcl_OpenCmd: invalid mode value");
1128                break;
1129            }
1130            chan = Tcl_OpenCommandChannel(interp, cmdObjc, cmdArgv, flags);
1131            if (binary) {
1132                Tcl_SetChannelOption(interp, chan, "-translation", "binary");
1133            }
1134        }
1135        ckfree((char *) cmdArgv);
1136    }
1137    if (chan == NULL) {
1138        return TCL_ERROR;
1139    }
1140    Tcl_RegisterChannel(interp, chan);
1141    Tcl_AppendResult(interp, Tcl_GetChannelName(chan), NULL);
1142    return TCL_OK;
1143}
1144
1145/*
1146 *----------------------------------------------------------------------
1147 *
1148 * TcpAcceptCallbacksDeleteProc --
1149 *
1150 *      Assocdata cleanup routine called when an interpreter is being deleted
1151 *      to set the interp field of all the accept callback records registered
1152 *      with the interpreter to NULL. This will prevent the interpreter from
1153 *      being used in the future to eval accept scripts.
1154 *
1155 * Results:
1156 *      None.
1157 *
1158 * Side effects:
1159 *      Deallocates memory and sets the interp field of all the accept
1160 *      callback records to NULL to prevent this interpreter from being used
1161 *      subsequently to eval accept scripts.
1162 *
1163 *----------------------------------------------------------------------
1164 */
1165
1166        /* ARGSUSED */
1167static void
1168TcpAcceptCallbacksDeleteProc(
1169    ClientData clientData,      /* Data which was passed when the assocdata
1170                                 * was registered. */
1171    Tcl_Interp *interp)         /* Interpreter being deleted - not used. */
1172{
1173    Tcl_HashTable *hTblPtr = clientData;
1174    Tcl_HashEntry *hPtr;
1175    Tcl_HashSearch hSearch;
1176
1177    for (hPtr = Tcl_FirstHashEntry(hTblPtr, &hSearch);
1178            hPtr != NULL; hPtr = Tcl_NextHashEntry(&hSearch)) {
1179        AcceptCallback *acceptCallbackPtr = Tcl_GetHashValue(hPtr);
1180
1181        acceptCallbackPtr->interp = NULL;
1182    }
1183    Tcl_DeleteHashTable(hTblPtr);
1184    ckfree((char *) hTblPtr);
1185}
1186
1187/*
1188 *----------------------------------------------------------------------
1189 *
1190 * RegisterTcpServerInterpCleanup --
1191 *
1192 *      Registers an accept callback record to have its interp field set to
1193 *      NULL when the interpreter is deleted.
1194 *
1195 * Results:
1196 *      None.
1197 *
1198 * Side effects:
1199 *      When, in the future, the interpreter is deleted, the interp field of
1200 *      the accept callback data structure will be set to NULL. This will
1201 *      prevent attempts to eval the accept script in a deleted interpreter.
1202 *
1203 *----------------------------------------------------------------------
1204 */
1205
1206static void
1207RegisterTcpServerInterpCleanup(
1208    Tcl_Interp *interp,         /* Interpreter for which we want to be
1209                                 * informed of deletion. */
1210    AcceptCallback *acceptCallbackPtr)
1211                                /* The accept callback record whose interp
1212                                 * field we want set to NULL when the
1213                                 * interpreter is deleted. */
1214{
1215    Tcl_HashTable *hTblPtr;     /* Hash table for accept callback records to
1216                                 * smash when the interpreter will be
1217                                 * deleted. */
1218    Tcl_HashEntry *hPtr;        /* Entry for this record. */
1219    int isNew;                  /* Is the entry new? */
1220
1221    hTblPtr = (Tcl_HashTable *)
1222            Tcl_GetAssocData(interp, "tclTCPAcceptCallbacks", NULL);
1223
1224    if (hTblPtr == NULL) {
1225        hTblPtr = (Tcl_HashTable *) ckalloc((unsigned) sizeof(Tcl_HashTable));
1226        Tcl_InitHashTable(hTblPtr, TCL_ONE_WORD_KEYS);
1227        (void) Tcl_SetAssocData(interp, "tclTCPAcceptCallbacks",
1228                TcpAcceptCallbacksDeleteProc, hTblPtr);
1229    }
1230
1231    hPtr = Tcl_CreateHashEntry(hTblPtr, (char *) acceptCallbackPtr, &isNew);
1232    if (!isNew) {
1233        Tcl_Panic("RegisterTcpServerCleanup: damaged accept record table");
1234    }
1235    Tcl_SetHashValue(hPtr, acceptCallbackPtr);
1236}
1237
1238/*
1239 *----------------------------------------------------------------------
1240 *
1241 * UnregisterTcpServerInterpCleanupProc --
1242 *
1243 *      Unregister a previously registered accept callback record. The interp
1244 *      field of this record will no longer be set to NULL in the future when
1245 *      the interpreter is deleted.
1246 *
1247 * Results:
1248 *      None.
1249 *
1250 * Side effects:
1251 *      Prevents the interp field of the accept callback record from being set
1252 *      to NULL in the future when the interpreter is deleted.
1253 *
1254 *----------------------------------------------------------------------
1255 */
1256
1257static void
1258UnregisterTcpServerInterpCleanupProc(
1259    Tcl_Interp *interp,         /* Interpreter in which the accept callback
1260                                 * record was registered. */
1261    AcceptCallback *acceptCallbackPtr)
1262                                /* The record for which to delete the
1263                                 * registration. */
1264{
1265    Tcl_HashTable *hTblPtr;
1266    Tcl_HashEntry *hPtr;
1267
1268    hTblPtr = (Tcl_HashTable *) Tcl_GetAssocData(interp,
1269            "tclTCPAcceptCallbacks", NULL);
1270    if (hTblPtr == NULL) {
1271        return;
1272    }
1273
1274    hPtr = Tcl_FindHashEntry(hTblPtr, (char *) acceptCallbackPtr);
1275    if (hPtr != NULL) {
1276        Tcl_DeleteHashEntry(hPtr);
1277    }
1278}
1279
1280/*
1281 *----------------------------------------------------------------------
1282 *
1283 * AcceptCallbackProc --
1284 *
1285 *      This callback is invoked by the TCP channel driver when it accepts a
1286 *      new connection from a client on a server socket.
1287 *
1288 * Results:
1289 *      None.
1290 *
1291 * Side effects:
1292 *      Whatever the script does.
1293 *
1294 *----------------------------------------------------------------------
1295 */
1296
1297static void
1298AcceptCallbackProc(
1299    ClientData callbackData,    /* The data stored when the callback was
1300                                 * created in the call to
1301                                 * Tcl_OpenTcpServer. */
1302    Tcl_Channel chan,           /* Channel for the newly accepted
1303                                 * connection. */
1304    char *address,              /* Address of client that was accepted. */
1305    int port)                   /* Port of client that was accepted. */
1306{
1307    AcceptCallback *acceptCallbackPtr = (AcceptCallback *) callbackData;
1308
1309    /*
1310     * Check if the callback is still valid; the interpreter may have gone
1311     * away, this is signalled by setting the interp field of the callback
1312     * data to NULL.
1313     */
1314
1315    if (acceptCallbackPtr->interp != NULL) {
1316        char portBuf[TCL_INTEGER_SPACE];
1317        char *script = acceptCallbackPtr->script;
1318        Tcl_Interp *interp = acceptCallbackPtr->interp;
1319        int result;
1320
1321        Tcl_Preserve(script);
1322        Tcl_Preserve(interp);
1323
1324        TclFormatInt(portBuf, port);
1325        Tcl_RegisterChannel(interp, chan);
1326
1327        /*
1328         * Artificially bump the refcount to protect the channel from being
1329         * deleted while the script is being evaluated.
1330         */
1331
1332        Tcl_RegisterChannel(NULL, chan);
1333
1334        result = Tcl_VarEval(interp, script, " ", Tcl_GetChannelName(chan),
1335                " ", address, " ", portBuf, NULL);
1336        if (result != TCL_OK) {
1337            TclBackgroundException(interp, result);
1338            Tcl_UnregisterChannel(interp, chan);
1339        }
1340
1341        /*
1342         * Decrement the artificially bumped refcount. After this it is not
1343         * safe anymore to use "chan", because it may now be deleted.
1344         */
1345
1346        Tcl_UnregisterChannel(NULL, chan);
1347
1348        Tcl_Release(interp);
1349        Tcl_Release(script);
1350    } else {
1351        /*
1352         * The interpreter has been deleted, so there is no useful way to
1353         * utilize the client socket - just close it.
1354         */
1355
1356        Tcl_Close(NULL, chan);
1357    }
1358}
1359
1360/*
1361 *----------------------------------------------------------------------
1362 *
1363 * TcpServerCloseProc --
1364 *
1365 *      This callback is called when the TCP server channel for which it was
1366 *      registered is being closed. It informs the interpreter in which the
1367 *      accept script is evaluated (if that interpreter still exists) that
1368 *      this channel no longer needs to be informed if the interpreter is
1369 *      deleted.
1370 *
1371 * Results:
1372 *      None.
1373 *
1374 * Side effects:
1375 *      In the future, if the interpreter is deleted this channel will no
1376 *      longer be informed.
1377 *
1378 *----------------------------------------------------------------------
1379 */
1380
1381static void
1382TcpServerCloseProc(
1383    ClientData callbackData)    /* The data passed in the call to
1384                                 * Tcl_CreateCloseHandler. */
1385{
1386    AcceptCallback *acceptCallbackPtr = (AcceptCallback *) callbackData;
1387                                /* The actual data. */
1388
1389    if (acceptCallbackPtr->interp != NULL) {
1390        UnregisterTcpServerInterpCleanupProc(acceptCallbackPtr->interp,
1391                acceptCallbackPtr);
1392    }
1393    Tcl_EventuallyFree(acceptCallbackPtr->script, TCL_DYNAMIC);
1394    ckfree((char *) acceptCallbackPtr);
1395}
1396
1397/*
1398 *----------------------------------------------------------------------
1399 *
1400 * Tcl_SocketObjCmd --
1401 *
1402 *      This function is invoked to process the "socket" Tcl command. See the
1403 *      user documentation for details on what it does.
1404 *
1405 * Results:
1406 *      A standard Tcl result.
1407 *
1408 * Side effects:
1409 *      Creates a socket based channel.
1410 *
1411 *----------------------------------------------------------------------
1412 */
1413
1414int
1415Tcl_SocketObjCmd(
1416    ClientData notUsed,         /* Not used. */
1417    Tcl_Interp *interp,         /* Current interpreter. */
1418    int objc,                   /* Number of arguments. */
1419    Tcl_Obj *const objv[])      /* Argument objects. */
1420{
1421    static const char *socketOptions[] = {
1422        "-async", "-myaddr", "-myport","-server", NULL
1423    };
1424    enum socketOptions {
1425        SKT_ASYNC, SKT_MYADDR, SKT_MYPORT, SKT_SERVER
1426    };
1427    int optionIndex, a, server = 0, port, myport = 0, async = 0;
1428    char *host, *script = NULL, *myaddr = NULL;
1429    Tcl_Channel chan;
1430
1431    if (TclpHasSockets(interp) != TCL_OK) {
1432        return TCL_ERROR;
1433    }
1434
1435    for (a = 1; a < objc; a++) {
1436        const char *arg = Tcl_GetString(objv[a]);
1437
1438        if (arg[0] != '-') {
1439            break;
1440        }
1441        if (Tcl_GetIndexFromObj(interp, objv[a], socketOptions, "option",
1442                TCL_EXACT, &optionIndex) != TCL_OK) {
1443            return TCL_ERROR;
1444        }
1445        switch ((enum socketOptions) optionIndex) {
1446        case SKT_ASYNC:
1447            if (server == 1) {
1448                Tcl_AppendResult(interp,
1449                        "cannot set -async option for server sockets", NULL);
1450                return TCL_ERROR;
1451            }
1452            async = 1;
1453            break;
1454        case SKT_MYADDR:
1455            a++;
1456            if (a >= objc) {
1457                Tcl_AppendResult(interp,
1458                        "no argument given for -myaddr option", NULL);
1459                return TCL_ERROR;
1460            }
1461            myaddr = TclGetString(objv[a]);
1462            break;
1463        case SKT_MYPORT: {
1464            char *myPortName;
1465
1466            a++;
1467            if (a >= objc) {
1468                Tcl_AppendResult(interp,
1469                        "no argument given for -myport option", NULL);
1470                return TCL_ERROR;
1471            }
1472            myPortName = TclGetString(objv[a]);
1473            if (TclSockGetPort(interp, myPortName, "tcp", &myport) != TCL_OK) {
1474                return TCL_ERROR;
1475            }
1476            break;
1477        }
1478        case SKT_SERVER:
1479            if (async == 1) {
1480                Tcl_AppendResult(interp,
1481                        "cannot set -async option for server sockets", NULL);
1482                return TCL_ERROR;
1483            }
1484            server = 1;
1485            a++;
1486            if (a >= objc) {
1487                Tcl_AppendResult(interp,
1488                        "no argument given for -server option", NULL);
1489                return TCL_ERROR;
1490            }
1491            script = TclGetString(objv[a]);
1492            break;
1493        default:
1494            Tcl_Panic("Tcl_SocketObjCmd: bad option index to SocketOptions");
1495        }
1496    }
1497    if (server) {
1498        host = myaddr;          /* NULL implies INADDR_ANY */
1499        if (myport != 0) {
1500            Tcl_AppendResult(interp, "option -myport is not valid for servers",
1501                    NULL);
1502            return TCL_ERROR;
1503        }
1504    } else if (a < objc) {
1505        host = TclGetString(objv[a]);
1506        a++;
1507    } else {
1508        Interp *iPtr;
1509
1510    wrongNumArgs:
1511        iPtr = (Interp *) interp;
1512        Tcl_WrongNumArgs(interp, 1, objv,
1513                "?-myaddr addr? ?-myport myport? ?-async? host port");
1514        iPtr->flags |= INTERP_ALTERNATE_WRONG_ARGS;
1515        Tcl_WrongNumArgs(interp, 1, objv,
1516                "-server command ?-myaddr addr? port");
1517        iPtr->flags &= ~INTERP_ALTERNATE_WRONG_ARGS;
1518        return TCL_ERROR;
1519    }
1520
1521    if (a == objc-1) {
1522        if (TclSockGetPort(interp, TclGetString(objv[a]), "tcp",
1523                &port) != TCL_OK) {
1524            return TCL_ERROR;
1525        }
1526    } else {
1527        goto wrongNumArgs;
1528    }
1529
1530    if (server) {
1531        AcceptCallback *acceptCallbackPtr = (AcceptCallback *)
1532                ckalloc((unsigned) sizeof(AcceptCallback));
1533        unsigned len = strlen(script) + 1;
1534        char *copyScript = ckalloc(len);
1535
1536        memcpy(copyScript, script, len);
1537        acceptCallbackPtr->script = copyScript;
1538        acceptCallbackPtr->interp = interp;
1539        chan = Tcl_OpenTcpServer(interp, port, host, AcceptCallbackProc,
1540                acceptCallbackPtr);
1541        if (chan == NULL) {
1542            ckfree(copyScript);
1543            ckfree((char *) acceptCallbackPtr);
1544            return TCL_ERROR;
1545        }
1546
1547        /*
1548         * Register with the interpreter to let us know when the interpreter
1549         * is deleted (by having the callback set the interp field of the
1550         * acceptCallbackPtr's structure to NULL). This is to avoid trying to
1551         * eval the script in a deleted interpreter.
1552         */
1553
1554        RegisterTcpServerInterpCleanup(interp, acceptCallbackPtr);
1555
1556        /*
1557         * Register a close callback. This callback will inform the
1558         * interpreter (if it still exists) that this channel does not need to
1559         * be informed when the interpreter is deleted.
1560         */
1561
1562        Tcl_CreateCloseHandler(chan, TcpServerCloseProc, acceptCallbackPtr);
1563    } else {
1564        chan = Tcl_OpenTcpClient(interp, port, host, myaddr, myport, async);
1565        if (chan == NULL) {
1566            return TCL_ERROR;
1567        }
1568    }
1569    Tcl_RegisterChannel(interp, chan);
1570    Tcl_AppendResult(interp, Tcl_GetChannelName(chan), NULL);
1571
1572    return TCL_OK;
1573}
1574
1575/*
1576 *----------------------------------------------------------------------
1577 *
1578 * Tcl_FcopyObjCmd --
1579 *
1580 *      This function is invoked to process the "fcopy" Tcl command. See the
1581 *      user documentation for details on what it does.
1582 *
1583 * Results:
1584 *      A standard Tcl result.
1585 *
1586 * Side effects:
1587 *      Moves data between two channels and possibly sets up a background copy
1588 *      handler.
1589 *
1590 *----------------------------------------------------------------------
1591 */
1592
1593int
1594Tcl_FcopyObjCmd(
1595    ClientData dummy,           /* Not used. */
1596    Tcl_Interp *interp,         /* Current interpreter. */
1597    int objc,                   /* Number of arguments. */
1598    Tcl_Obj *const objv[])      /* Argument objects. */
1599{
1600    Tcl_Channel inChan, outChan;
1601    int mode, i, toRead, index;
1602    Tcl_Obj *cmdPtr;
1603    static const char* switches[] = { "-size", "-command", NULL };
1604    enum { FcopySize, FcopyCommand };
1605
1606    if ((objc < 3) || (objc > 7) || (objc == 4) || (objc == 6)) {
1607        Tcl_WrongNumArgs(interp, 1, objv,
1608                "input output ?-size size? ?-command callback?");
1609        return TCL_ERROR;
1610    }
1611
1612    /*
1613     * Parse the channel arguments and verify that they are readable or
1614     * writable, as appropriate.
1615     */
1616
1617    if (TclGetChannelFromObj(interp, objv[1], &inChan, &mode, 0) != TCL_OK) {
1618        return TCL_ERROR;
1619    }
1620    if ((mode & TCL_READABLE) == 0) {
1621        Tcl_AppendResult(interp, "channel \"", TclGetString(objv[1]),
1622                "\" wasn't opened for reading", NULL);
1623        return TCL_ERROR;
1624    }
1625    if (TclGetChannelFromObj(interp, objv[2], &outChan, &mode, 0) != TCL_OK) {
1626        return TCL_ERROR;
1627    }
1628    if ((mode & TCL_WRITABLE) == 0) {
1629        Tcl_AppendResult(interp, "channel \"", TclGetString(objv[2]),
1630                "\" wasn't opened for writing", NULL);
1631        return TCL_ERROR;
1632    }
1633
1634    toRead = -1;
1635    cmdPtr = NULL;
1636    for (i = 3; i < objc; i += 2) {
1637        if (Tcl_GetIndexFromObj(interp, objv[i], switches, "switch", 0,
1638                &index) != TCL_OK) {
1639            return TCL_ERROR;
1640        }
1641        switch (index) {
1642        case FcopySize:
1643            if (TclGetIntFromObj(interp, objv[i+1], &toRead) != TCL_OK) {
1644                return TCL_ERROR;
1645            }
1646            break;
1647        case FcopyCommand:
1648            cmdPtr = objv[i+1];
1649            break;
1650        }
1651    }
1652
1653    return TclCopyChannel(interp, inChan, outChan, toRead, cmdPtr);
1654}
1655
1656/*
1657 *---------------------------------------------------------------------------
1658 *
1659 * ChanPendingObjCmd --
1660 *
1661 *      This function is invoked to process the Tcl "chan pending" command
1662 *      (TIP #287). See the user documentation for details on what it does.
1663 *
1664 * Results:
1665 *      A standard Tcl result.
1666 *
1667 * Side effects:
1668 *      Sets interp's result to the number of bytes of buffered input or
1669 *      output (depending on whether the first argument is "input" or
1670 *      "output"), or -1 if the channel wasn't opened for that mode.
1671 *
1672 *---------------------------------------------------------------------------
1673 */
1674
1675        /* ARGSUSED */
1676static int
1677ChanPendingObjCmd(
1678    ClientData unused,          /* Not used. */
1679    Tcl_Interp *interp,         /* Current interpreter. */
1680    int objc,                   /* Number of arguments. */
1681    Tcl_Obj *const objv[])      /* Argument objects. */
1682{
1683    Tcl_Channel chan;
1684    int index, mode;
1685    static const char *options[] = {"input", "output", NULL};
1686    enum options {PENDING_INPUT, PENDING_OUTPUT};
1687
1688    if (objc != 3) {
1689        Tcl_WrongNumArgs(interp, 1, objv, "mode channelId");
1690        return TCL_ERROR;
1691    }
1692
1693    if (Tcl_GetIndexFromObj(interp, objv[1], options, "mode", 0,
1694            &index) != TCL_OK) {
1695        return TCL_ERROR;
1696    }
1697
1698    if (TclGetChannelFromObj(interp, objv[2], &chan, &mode, 0) != TCL_OK) {
1699        return TCL_ERROR;
1700    }
1701
1702    switch ((enum options) index) {
1703    case PENDING_INPUT:
1704        if ((mode & TCL_READABLE) == 0) {
1705            Tcl_SetObjResult(interp, Tcl_NewIntObj(-1));
1706        } else {
1707            Tcl_SetObjResult(interp, Tcl_NewIntObj(Tcl_InputBuffered(chan)));
1708        }
1709        break;
1710    case PENDING_OUTPUT:
1711        if ((mode & TCL_WRITABLE) == 0) {
1712            Tcl_SetObjResult(interp, Tcl_NewIntObj(-1));
1713        } else {
1714            Tcl_SetObjResult(interp, Tcl_NewIntObj(Tcl_OutputBuffered(chan)));
1715        }
1716        break;
1717    }
1718    return TCL_OK;
1719}
1720
1721/*
1722 *----------------------------------------------------------------------
1723 *
1724 * ChanTruncateObjCmd --
1725 *
1726 *      This function is invoked to process the "chan truncate" Tcl command.
1727 *      See the user documentation for details on what it does.
1728 *
1729 * Results:
1730 *      A standard Tcl result.
1731 *
1732 * Side effects:
1733 *      Truncates a channel (or rather a file underlying a channel).
1734 *
1735 *----------------------------------------------------------------------
1736 */
1737
1738static int
1739ChanTruncateObjCmd(
1740    ClientData dummy,           /* Not used. */
1741    Tcl_Interp *interp,         /* Current interpreter. */
1742    int objc,                   /* Number of arguments. */
1743    Tcl_Obj *const objv[])      /* Argument objects. */
1744{
1745    Tcl_Channel chan;
1746    Tcl_WideInt length;
1747
1748    if ((objc < 2) || (objc > 3)) {
1749        Tcl_WrongNumArgs(interp, 1, objv, "channelId ?length?");
1750        return TCL_ERROR;
1751    }
1752    if (TclGetChannelFromObj(interp, objv[1], &chan, NULL, 0) != TCL_OK) {
1753        return TCL_ERROR;
1754    }
1755
1756    if (objc == 3) {
1757        /*
1758         * User is supplying an explicit length.
1759         */
1760
1761        if (Tcl_GetWideIntFromObj(interp, objv[2], &length) != TCL_OK) {
1762            return TCL_ERROR;
1763        }
1764        if (length < 0) {
1765            Tcl_AppendResult(interp,
1766                    "cannot truncate to negative length of file", NULL);
1767            return TCL_ERROR;
1768        }
1769    } else {
1770        /*
1771         * User wants to truncate to the current file position.
1772         */
1773
1774        length = Tcl_Tell(chan);
1775        if (length == Tcl_WideAsLong(-1)) {
1776            Tcl_AppendResult(interp,
1777                    "could not determine current location in \"",
1778                    TclGetString(objv[1]), "\": ",
1779                    Tcl_PosixError(interp), NULL);
1780            return TCL_ERROR;
1781        }
1782    }
1783
1784    if (Tcl_TruncateChannel(chan, length) != TCL_OK) {
1785        Tcl_AppendResult(interp, "error during truncate on \"",
1786                TclGetString(objv[1]), "\": ",
1787                Tcl_PosixError(interp), NULL);
1788        return TCL_ERROR;
1789    }
1790
1791    return TCL_OK;
1792}
1793
1794/*
1795 *----------------------------------------------------------------------
1796 *
1797 * TclInitChanCmd --
1798 *
1799 *      This function is invoked to create the "chan" Tcl command. See the
1800 *      user documentation for details on what it does.
1801 *
1802 * Results:
1803 *      A Tcl command handle.
1804 *
1805 * Side effects:
1806 *      None (since nothing is byte-compiled).
1807 *
1808 *----------------------------------------------------------------------
1809 */
1810
1811Tcl_Command
1812TclInitChanCmd(
1813    Tcl_Interp *interp)
1814{
1815    /*
1816     * Most commands are plugged directly together, but some are done via
1817     * alias-like rewriting; [chan configure] is this way for security reasons
1818     * (want overwriting of [fconfigure] to control that nicely), and [chan
1819     * names] because the functionality isn't available as a separate command
1820     * function at the moment.
1821     */
1822    static const EnsembleImplMap initMap[] = {
1823        {"blocked",     Tcl_FblockedObjCmd},
1824        {"close",       Tcl_CloseObjCmd},
1825        {"copy",        Tcl_FcopyObjCmd},
1826        {"create",      TclChanCreateObjCmd},           /* TIP #219 */
1827        {"eof",         Tcl_EofObjCmd},
1828        {"event",       Tcl_FileEventObjCmd},
1829        {"flush",       Tcl_FlushObjCmd},
1830        {"gets",        Tcl_GetsObjCmd},
1831        {"pending",     ChanPendingObjCmd},             /* TIP #287 */
1832        {"postevent",   TclChanPostEventObjCmd},        /* TIP #219 */
1833        {"puts",        Tcl_PutsObjCmd},
1834        {"read",        Tcl_ReadObjCmd},
1835        {"seek",        Tcl_SeekObjCmd},
1836        {"tell",        Tcl_TellObjCmd},
1837        {"truncate",    ChanTruncateObjCmd},            /* TIP #208 */
1838        {NULL}
1839    };
1840    static const char *extras[] = {
1841        "configure",    "::fconfigure",
1842        "names",        "::file channels",
1843        NULL
1844    };
1845    Tcl_Command ensemble;
1846    Tcl_Obj *mapObj;
1847    int i;
1848
1849    ensemble = TclMakeEnsemble(interp, "chan", initMap);
1850    Tcl_GetEnsembleMappingDict(NULL, ensemble, &mapObj);
1851    for (i=0 ; extras[i] ; i+=2) {
1852        /*
1853         * Can assume that reference counts are all incremented.
1854         */
1855
1856        Tcl_DictObjPut(NULL, mapObj, Tcl_NewStringObj(extras[i], -1),
1857                Tcl_NewStringObj(extras[i+1], -1));
1858    }
1859    Tcl_SetEnsembleMappingDict(interp, ensemble, mapObj);
1860    return ensemble;
1861}
1862
1863/*
1864 * Local Variables:
1865 * mode: c
1866 * c-basic-offset: 4
1867 * fill-column: 78
1868 * End:
1869 */
Note: See TracBrowser for help on using the repository browser.