os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/generic/tclIOCmd.c
Update contrib.
4 * Contains the definitions of most of the Tcl commands relating to IO.
6 * Copyright (c) 1995-1997 Sun Microsystems, Inc.
8 * See the file "license.terms" for information on usage and redistribution
9 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
11 * RCS: @(#) $Id: tclIOCmd.c,v 1.15.2.2 2004/07/16 22:38:37 andreas_kupries Exp $
18 * Callback structure for accept callback in a TCP server.
21 typedef struct AcceptCallback {
22 char *script; /* Script to invoke. */
23 Tcl_Interp *interp; /* Interpreter in which to run it. */
27 * Static functions for this file:
30 static void AcceptCallbackProc _ANSI_ARGS_((ClientData callbackData,
31 Tcl_Channel chan, char *address, int port));
32 static void RegisterTcpServerInterpCleanup _ANSI_ARGS_((Tcl_Interp *interp,
33 AcceptCallback *acceptCallbackPtr));
34 static void TcpAcceptCallbacksDeleteProc _ANSI_ARGS_((
35 ClientData clientData, Tcl_Interp *interp));
36 static void TcpServerCloseProc _ANSI_ARGS_((ClientData callbackData));
37 static void UnregisterTcpServerInterpCleanupProc _ANSI_ARGS_((
38 Tcl_Interp *interp, AcceptCallback *acceptCallbackPtr));
41 *----------------------------------------------------------------------
45 * This procedure is invoked to process the "puts" Tcl command.
46 * See the user documentation for details on what it does.
49 * A standard Tcl result.
52 * Produces output on a channel.
54 *----------------------------------------------------------------------
59 Tcl_PutsObjCmd(dummy, interp, objc, objv)
60 ClientData dummy; /* Not used. */
61 Tcl_Interp *interp; /* Current interpreter. */
62 int objc; /* Number of arguments. */
63 Tcl_Obj *CONST objv[]; /* Argument objects. */
65 Tcl_Channel chan; /* The channel to puts on. */
66 Tcl_Obj *string; /* String to write. */
67 int newline; /* Add a newline at end? */
68 char *channelId; /* Name of channel for puts. */
69 int result; /* Result of puts operation. */
70 int mode; /* Mode in which channel is opened. */
79 case 3: /* puts -nonewline $x or puts $chan $x */
80 if (strcmp(Tcl_GetString(objv[1]), "-nonewline") == 0) {
85 channelId = Tcl_GetString(objv[1]);
90 case 4: /* puts -nonewline $chan $x or puts $chan $x nonewline */
91 if (strcmp(Tcl_GetString(objv[1]), "-nonewline") == 0) {
92 channelId = Tcl_GetString(objv[2]);
96 * The code below provides backwards compatibility with an
97 * old form of the command that is no longer recommended
104 arg = Tcl_GetStringFromObj(objv[3], &length);
105 if ((length != 9) || (strncmp(arg, "nonewline", (size_t) length) != 0)) {
106 Tcl_AppendResult(interp, "bad argument \"", arg,
107 "\": should be \"nonewline\"",
111 channelId = Tcl_GetString(objv[1]);
117 default: /* puts or puts some bad number of arguments... */
118 Tcl_WrongNumArgs(interp, 1, objv, "?-nonewline? ?channelId? string");
122 chan = Tcl_GetChannel(interp, channelId, &mode);
123 if (chan == (Tcl_Channel) NULL) {
126 if ((mode & TCL_WRITABLE) == 0) {
127 Tcl_AppendResult(interp, "channel \"", channelId,
128 "\" wasn't opened for writing", (char *) NULL);
132 result = Tcl_WriteObj(chan, string);
137 result = Tcl_WriteChars(chan, "\n", 1);
145 Tcl_AppendResult(interp, "error writing \"", channelId, "\": ",
146 Tcl_PosixError(interp), (char *) NULL);
151 *----------------------------------------------------------------------
155 * This procedure is called to process the Tcl "flush" command.
156 * See the user documentation for details on what it does.
159 * A standard Tcl result.
162 * May cause output to appear on the specified channel.
164 *----------------------------------------------------------------------
169 Tcl_FlushObjCmd(dummy, interp, objc, objv)
170 ClientData dummy; /* Not used. */
171 Tcl_Interp *interp; /* Current interpreter. */
172 int objc; /* Number of arguments. */
173 Tcl_Obj *CONST objv[]; /* Argument objects. */
175 Tcl_Channel chan; /* The channel to flush on. */
180 Tcl_WrongNumArgs(interp, 1, objv, "channelId");
183 channelId = Tcl_GetString(objv[1]);
184 chan = Tcl_GetChannel(interp, channelId, &mode);
185 if (chan == (Tcl_Channel) NULL) {
188 if ((mode & TCL_WRITABLE) == 0) {
189 Tcl_AppendResult(interp, "channel \"", channelId,
190 "\" wasn't opened for writing", (char *) NULL);
194 if (Tcl_Flush(chan) != TCL_OK) {
195 Tcl_AppendResult(interp, "error flushing \"", channelId, "\": ",
196 Tcl_PosixError(interp), (char *) NULL);
203 *----------------------------------------------------------------------
207 * This procedure is called to process the Tcl "gets" command.
208 * See the user documentation for details on what it does.
211 * A standard Tcl result.
214 * May consume input from channel.
216 *----------------------------------------------------------------------
221 Tcl_GetsObjCmd(dummy, interp, objc, objv)
222 ClientData dummy; /* Not used. */
223 Tcl_Interp *interp; /* Current interpreter. */
224 int objc; /* Number of arguments. */
225 Tcl_Obj *CONST objv[]; /* Argument objects. */
227 Tcl_Channel chan; /* The channel to read from. */
228 int lineLen; /* Length of line just read. */
229 int mode; /* Mode in which channel is opened. */
231 Tcl_Obj *resultPtr, *linePtr;
233 if ((objc != 2) && (objc != 3)) {
234 Tcl_WrongNumArgs(interp, 1, objv, "channelId ?varName?");
237 name = Tcl_GetString(objv[1]);
238 chan = Tcl_GetChannel(interp, name, &mode);
239 if (chan == (Tcl_Channel) NULL) {
242 if ((mode & TCL_READABLE) == 0) {
243 Tcl_AppendResult(interp, "channel \"", name,
244 "\" wasn't opened for reading", (char *) NULL);
248 linePtr = Tcl_NewObj();
250 lineLen = Tcl_GetsObj(chan, linePtr);
252 if (!Tcl_Eof(chan) && !Tcl_InputBlocked(chan)) {
253 Tcl_DecrRefCount(linePtr);
254 Tcl_ResetResult(interp);
255 Tcl_AppendResult(interp, "error reading \"", name, "\": ",
256 Tcl_PosixError(interp), (char *) NULL);
262 if (Tcl_ObjSetVar2(interp, objv[2], NULL, linePtr,
263 TCL_LEAVE_ERR_MSG) == NULL) {
264 Tcl_DecrRefCount(linePtr);
267 resultPtr = Tcl_GetObjResult(interp);
268 Tcl_SetIntObj(resultPtr, lineLen);
271 Tcl_SetObjResult(interp, linePtr);
277 *----------------------------------------------------------------------
281 * This procedure is invoked to process the Tcl "read" command.
282 * See the user documentation for details on what it does.
285 * A standard Tcl result.
288 * May consume input from channel.
290 *----------------------------------------------------------------------
295 Tcl_ReadObjCmd(dummy, interp, objc, objv)
296 ClientData dummy; /* Not used. */
297 Tcl_Interp *interp; /* Current interpreter. */
298 int objc; /* Number of arguments. */
299 Tcl_Obj *CONST objv[]; /* Argument objects. */
301 Tcl_Channel chan; /* The channel to read from. */
302 int newline, i; /* Discard newline at end? */
303 int toRead; /* How many bytes to read? */
304 int charactersRead; /* How many characters were read? */
305 int mode; /* Mode in which channel is opened. */
309 if ((objc != 2) && (objc != 3)) {
311 Tcl_WrongNumArgs(interp, 1, objv, "channelId ?numChars?");
312 Tcl_AppendResult(interp, " or \"", Tcl_GetString(objv[0]),
313 " ?-nonewline? channelId\"", (char *) NULL);
319 if (strcmp(Tcl_GetString(objv[1]), "-nonewline") == 0) {
328 name = Tcl_GetString(objv[i]);
329 chan = Tcl_GetChannel(interp, name, &mode);
330 if (chan == (Tcl_Channel) NULL) {
333 if ((mode & TCL_READABLE) == 0) {
334 Tcl_AppendResult(interp, "channel \"", name,
335 "\" wasn't opened for reading", (char *) NULL);
338 i++; /* Consumed channel name. */
341 * Compute how many bytes to read, and see whether the final
342 * newline should be dropped.
349 arg = Tcl_GetString(objv[i]);
350 if (isdigit(UCHAR(arg[0]))) { /* INTL: digit */
351 if (Tcl_GetIntFromObj(interp, objv[i], &toRead) != TCL_OK) {
354 } else if (strcmp(arg, "nonewline") == 0) {
357 Tcl_AppendResult(interp, "bad argument \"", arg,
358 "\": should be \"nonewline\"", (char *) NULL);
363 resultPtr = Tcl_NewObj();
364 Tcl_IncrRefCount(resultPtr);
365 charactersRead = Tcl_ReadChars(chan, resultPtr, toRead, 0);
366 if (charactersRead < 0) {
367 Tcl_ResetResult(interp);
368 Tcl_AppendResult(interp, "error reading \"", name, "\": ",
369 Tcl_PosixError(interp), (char *) NULL);
370 Tcl_DecrRefCount(resultPtr);
375 * If requested, remove the last newline in the channel if at EOF.
378 if ((charactersRead > 0) && (newline != 0)) {
382 result = Tcl_GetStringFromObj(resultPtr, &length);
383 if (result[length - 1] == '\n') {
384 Tcl_SetObjLength(resultPtr, length - 1);
387 Tcl_SetObjResult(interp, resultPtr);
388 Tcl_DecrRefCount(resultPtr);
393 *----------------------------------------------------------------------
397 * This procedure is invoked to process the Tcl "seek" command. See
398 * the user documentation for details on what it does.
401 * A standard Tcl result.
404 * Moves the position of the access point on the specified channel.
405 * May flush queued output.
407 *----------------------------------------------------------------------
412 Tcl_SeekObjCmd(clientData, interp, objc, objv)
413 ClientData clientData; /* Not used. */
414 Tcl_Interp *interp; /* Current interpreter. */
415 int objc; /* Number of arguments. */
416 Tcl_Obj *CONST objv[]; /* Argument objects. */
418 Tcl_Channel chan; /* The channel to tell on. */
419 Tcl_WideInt offset; /* Where to seek? */
420 int mode; /* How to seek? */
421 Tcl_WideInt result; /* Of calling Tcl_Seek. */
424 static CONST char *originOptions[] = {
425 "start", "current", "end", (char *) NULL
427 static int modeArray[] = {SEEK_SET, SEEK_CUR, SEEK_END};
429 if ((objc != 3) && (objc != 4)) {
430 Tcl_WrongNumArgs(interp, 1, objv, "channelId offset ?origin?");
433 chanName = Tcl_GetString(objv[1]);
434 chan = Tcl_GetChannel(interp, chanName, NULL);
435 if (chan == (Tcl_Channel) NULL) {
438 if (Tcl_GetWideIntFromObj(interp, objv[2], &offset) != TCL_OK) {
443 if (Tcl_GetIndexFromObj(interp, objv[3], originOptions, "origin", 0,
444 &optionIndex) != TCL_OK) {
447 mode = modeArray[optionIndex];
450 result = Tcl_Seek(chan, offset, mode);
451 if (result == Tcl_LongAsWide(-1)) {
452 Tcl_AppendResult(interp, "error during seek on \"",
453 chanName, "\": ", Tcl_PosixError(interp), (char *) NULL);
460 *----------------------------------------------------------------------
464 * This procedure is invoked to process the Tcl "tell" command.
465 * See the user documentation for details on what it does.
468 * A standard Tcl result.
473 *----------------------------------------------------------------------
478 Tcl_TellObjCmd(clientData, interp, objc, objv)
479 ClientData clientData; /* Not used. */
480 Tcl_Interp *interp; /* Current interpreter. */
481 int objc; /* Number of arguments. */
482 Tcl_Obj *CONST objv[]; /* Argument objects. */
484 Tcl_Channel chan; /* The channel to tell on. */
488 Tcl_WrongNumArgs(interp, 1, objv, "channelId");
492 * Try to find a channel with the right name and permissions in
493 * the IO channel table of this interpreter.
496 chanName = Tcl_GetString(objv[1]);
497 chan = Tcl_GetChannel(interp, chanName, NULL);
498 if (chan == (Tcl_Channel) NULL) {
501 Tcl_SetWideIntObj(Tcl_GetObjResult(interp), Tcl_Tell(chan));
506 *----------------------------------------------------------------------
510 * This procedure is invoked to process the Tcl "close" command.
511 * See the user documentation for details on what it does.
514 * A standard Tcl result.
517 * May discard queued input; may flush queued output.
519 *----------------------------------------------------------------------
524 Tcl_CloseObjCmd(clientData, interp, objc, objv)
525 ClientData clientData; /* Not used. */
526 Tcl_Interp *interp; /* Current interpreter. */
527 int objc; /* Number of arguments. */
528 Tcl_Obj *CONST objv[]; /* Argument objects. */
530 Tcl_Channel chan; /* The channel to close. */
534 Tcl_WrongNumArgs(interp, 1, objv, "channelId");
538 arg = Tcl_GetString(objv[1]);
539 chan = Tcl_GetChannel(interp, arg, NULL);
540 if (chan == (Tcl_Channel) NULL) {
544 if (Tcl_UnregisterChannel(interp, chan) != TCL_OK) {
546 * If there is an error message and it ends with a newline, remove
547 * the newline. This is done for command pipeline channels where the
548 * error output from the subprocesses is stored in interp's result.
550 * NOTE: This is likely to not have any effect on regular error
551 * messages produced by drivers during the closing of a channel,
552 * because the Tcl convention is that such error messages do not
553 * have a terminating newline.
560 resultPtr = Tcl_GetObjResult(interp);
561 string = Tcl_GetStringFromObj(resultPtr, &len);
562 if ((len > 0) && (string[len - 1] == '\n')) {
563 Tcl_SetObjLength(resultPtr, len - 1);
572 *----------------------------------------------------------------------
574 * Tcl_FconfigureObjCmd --
576 * This procedure is invoked to process the Tcl "fconfigure" command.
577 * See the user documentation for details on what it does.
580 * A standard Tcl result.
583 * May modify the behavior of an IO channel.
585 *----------------------------------------------------------------------
590 Tcl_FconfigureObjCmd(clientData, interp, objc, objv)
591 ClientData clientData; /* Not used. */
592 Tcl_Interp *interp; /* Current interpreter. */
593 int objc; /* Number of arguments. */
594 Tcl_Obj *CONST objv[]; /* Argument objects. */
596 char *chanName, *optionName, *valueName;
597 Tcl_Channel chan; /* The channel to set a mode on. */
598 int i; /* Iterate over arg-value pairs. */
599 Tcl_DString ds; /* DString to hold result of
600 * calling Tcl_GetChannelOption. */
602 if ((objc < 2) || (((objc % 2) == 1) && (objc != 3))) {
603 Tcl_WrongNumArgs(interp, 1, objv,
604 "channelId ?optionName? ?value? ?optionName value?...");
607 chanName = Tcl_GetString(objv[1]);
608 chan = Tcl_GetChannel(interp, chanName, NULL);
609 if (chan == (Tcl_Channel) NULL) {
613 Tcl_DStringInit(&ds);
614 if (Tcl_GetChannelOption(interp, chan, (char *) NULL, &ds) != TCL_OK) {
615 Tcl_DStringFree(&ds);
618 Tcl_DStringResult(interp, &ds);
622 Tcl_DStringInit(&ds);
623 optionName = Tcl_GetString(objv[2]);
624 if (Tcl_GetChannelOption(interp, chan, optionName, &ds) != TCL_OK) {
625 Tcl_DStringFree(&ds);
628 Tcl_DStringResult(interp, &ds);
631 for (i = 3; i < objc; i += 2) {
632 optionName = Tcl_GetString(objv[i-1]);
633 valueName = Tcl_GetString(objv[i]);
634 if (Tcl_SetChannelOption(interp, chan, optionName, valueName)
643 *---------------------------------------------------------------------------
647 * This procedure is invoked to process the Tcl "eof" command.
648 * See the user documentation for details on what it does.
651 * A standard Tcl result.
654 * Sets interp's result to boolean true or false depending on whether
655 * the specified channel has an EOF condition.
657 *---------------------------------------------------------------------------
662 Tcl_EofObjCmd(unused, interp, objc, objv)
663 ClientData unused; /* Not used. */
664 Tcl_Interp *interp; /* Current interpreter. */
665 int objc; /* Number of arguments. */
666 Tcl_Obj *CONST objv[]; /* Argument objects. */
673 Tcl_WrongNumArgs(interp, 1, objv, "channelId");
677 arg = Tcl_GetString(objv[1]);
678 chan = Tcl_GetChannel(interp, arg, &dummy);
683 Tcl_SetBooleanObj(Tcl_GetObjResult(interp), Tcl_Eof(chan));
688 *----------------------------------------------------------------------
692 * This procedure is invoked to process the "exec" Tcl command.
693 * See the user documentation for details on what it does.
696 * A standard Tcl result.
699 * See the user documentation.
701 *----------------------------------------------------------------------
706 Tcl_ExecObjCmd(dummy, interp, objc, objv)
707 ClientData dummy; /* Not used. */
708 Tcl_Interp *interp; /* Current interpreter. */
709 int objc; /* Number of arguments. */
710 Tcl_Obj *CONST objv[]; /* Argument objects. */
714 Tcl_AppendResult(interp, "exec not implemented under Mac OS",
721 * This procedure generates an argv array for the string arguments. It
722 * starts out with stack-allocated space but uses dynamically-allocated
731 CONST char *argStorage[NUM_ARGS];
732 int argc, background, i, index, keepNewline, result, skip, length;
733 static CONST char *options[] = {
734 "-keepnewline", "--", NULL
737 EXEC_KEEPNEWLINE, EXEC_LAST
741 * Check for a leading "-keepnewline" argument.
745 for (skip = 1; skip < objc; skip++) {
746 string = Tcl_GetString(objv[skip]);
747 if (string[0] != '-') {
750 if (Tcl_GetIndexFromObj(interp, objv[skip], options, "switch",
751 TCL_EXACT, &index) != TCL_OK) {
754 if (index == EXEC_KEEPNEWLINE) {
762 Tcl_WrongNumArgs(interp, 1, objv, "?switches? arg ?arg ...?");
767 * See if the command is to be run in background.
771 string = Tcl_GetString(objv[objc - 1]);
772 if ((string[0] == '&') && (string[1] == '\0')) {
778 * Create the string argument array "argv". Make sure argv is large
779 * enough to hold the argc arguments plus 1 extra for the zero
785 if ((argc + 1) > sizeof(argv) / sizeof(argv[0])) {
786 argv = (CONST char **) ckalloc((unsigned)(argc + 1) * sizeof(char *));
790 * Copy the string conversions of each (post option) object into the
794 for (i = 0; i < argc; i++) {
795 argv[i] = Tcl_GetString(objv[i + skip]);
798 chan = Tcl_OpenCommandChannel(interp, argc, argv,
799 (background ? 0 : TCL_STDOUT | TCL_STDERR));
802 * Free the argv array if malloc'ed storage was used.
805 if (argv != argStorage) {
806 ckfree((char *)argv);
809 if (chan == (Tcl_Channel) NULL) {
815 * Store the list of PIDs from the pipeline in interp's result and
816 * detach the PIDs (instead of waiting for them).
819 TclGetAndDetachPids(interp, chan);
820 if (Tcl_Close(interp, chan) != TCL_OK) {
826 resultPtr = Tcl_NewObj();
827 if (Tcl_GetChannelHandle(chan, TCL_READABLE, NULL) == TCL_OK) {
828 if (Tcl_ReadChars(chan, resultPtr, -1, 0) < 0) {
829 Tcl_ResetResult(interp);
830 Tcl_AppendResult(interp, "error reading output from command: ",
831 Tcl_PosixError(interp), (char *) NULL);
832 Tcl_DecrRefCount(resultPtr);
837 * If the process produced anything on stderr, it will have been
838 * returned in the interpreter result. It needs to be appended to
842 result = Tcl_Close(interp, chan);
843 string = Tcl_GetStringFromObj(Tcl_GetObjResult(interp), &length);
844 Tcl_AppendToObj(resultPtr, string, length);
847 * If the last character of the result is a newline, then remove
848 * the newline character.
851 if (keepNewline == 0) {
852 string = Tcl_GetStringFromObj(resultPtr, &length);
853 if ((length > 0) && (string[length - 1] == '\n')) {
854 Tcl_SetObjLength(resultPtr, length - 1);
857 Tcl_SetObjResult(interp, resultPtr);
860 #endif /* !MAC_TCL */
864 *---------------------------------------------------------------------------
866 * Tcl_FblockedObjCmd --
868 * This procedure is invoked to process the Tcl "fblocked" command.
869 * See the user documentation for details on what it does.
872 * A standard Tcl result.
875 * Sets interp's result to boolean true or false depending on whether
876 * the preceeding input operation on the channel would have blocked.
878 *---------------------------------------------------------------------------
883 Tcl_FblockedObjCmd(unused, interp, objc, objv)
884 ClientData unused; /* Not used. */
885 Tcl_Interp *interp; /* Current interpreter. */
886 int objc; /* Number of arguments. */
887 Tcl_Obj *CONST objv[]; /* Argument objects. */
894 Tcl_WrongNumArgs(interp, 1, objv, "channelId");
898 arg = Tcl_GetString(objv[1]);
899 chan = Tcl_GetChannel(interp, arg, &mode);
903 if ((mode & TCL_READABLE) == 0) {
904 Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "channel \"",
905 arg, "\" wasn't opened for reading", (char *) NULL);
909 Tcl_SetBooleanObj(Tcl_GetObjResult(interp), Tcl_InputBlocked(chan));
914 *----------------------------------------------------------------------
918 * This procedure is invoked to process the "open" Tcl command.
919 * See the user documentation for details on what it does.
922 * A standard Tcl result.
925 * See the user documentation.
927 *----------------------------------------------------------------------
932 Tcl_OpenObjCmd(notUsed, interp, objc, objv)
933 ClientData notUsed; /* Not used. */
934 Tcl_Interp *interp; /* Current interpreter. */
935 int objc; /* Number of arguments. */
936 Tcl_Obj *CONST objv[]; /* Argument objects. */
939 char *modeString, *what;
942 if ((objc < 2) || (objc > 4)) {
943 Tcl_WrongNumArgs(interp, 1, objv, "fileName ?access? ?permissions?");
950 modeString = Tcl_GetString(objv[2]);
952 if (Tcl_GetIntFromObj(interp, objv[3], &prot) != TCL_OK) {
959 what = Tcl_GetString(objv[1]);
960 if (what[0] == '|') {
965 * Open the file or create a process pipeline.
969 chan = Tcl_FSOpenFileChannel(interp, objv[1], modeString, prot);
972 Tcl_AppendResult(interp,
973 "command pipelines not supported on Macintosh OS",
977 int mode, seekFlag, cmdObjc;
978 CONST char **cmdArgv;
980 if (Tcl_SplitList(interp, what+1, &cmdObjc, &cmdArgv) != TCL_OK) {
984 mode = TclGetOpenMode(interp, modeString, &seekFlag);
988 int flags = TCL_STDERR | TCL_ENFORCE_MODE;
989 switch (mode & (O_RDONLY | O_WRONLY | O_RDWR)) {
997 flags |= (TCL_STDIN | TCL_STDOUT);
1000 panic("Tcl_OpenCmd: invalid mode value");
1003 chan = Tcl_OpenCommandChannel(interp, cmdObjc, cmdArgv, flags);
1005 ckfree((char *) cmdArgv);
1008 if (chan == (Tcl_Channel) NULL) {
1011 Tcl_RegisterChannel(interp, chan);
1012 Tcl_AppendResult(interp, Tcl_GetChannelName(chan), (char *) NULL);
1017 *----------------------------------------------------------------------
1019 * TcpAcceptCallbacksDeleteProc --
1021 * Assocdata cleanup routine called when an interpreter is being
1022 * deleted to set the interp field of all the accept callback records
1023 * registered with the interpreter to NULL. This will prevent the
1024 * interpreter from being used in the future to eval accept scripts.
1030 * Deallocates memory and sets the interp field of all the accept
1031 * callback records to NULL to prevent this interpreter from being
1032 * used subsequently to eval accept scripts.
1034 *----------------------------------------------------------------------
1039 TcpAcceptCallbacksDeleteProc(clientData, interp)
1040 ClientData clientData; /* Data which was passed when the assocdata
1041 * was registered. */
1042 Tcl_Interp *interp; /* Interpreter being deleted - not used. */
1044 Tcl_HashTable *hTblPtr;
1045 Tcl_HashEntry *hPtr;
1046 Tcl_HashSearch hSearch;
1047 AcceptCallback *acceptCallbackPtr;
1049 hTblPtr = (Tcl_HashTable *) clientData;
1050 for (hPtr = Tcl_FirstHashEntry(hTblPtr, &hSearch);
1051 hPtr != (Tcl_HashEntry *) NULL;
1052 hPtr = Tcl_NextHashEntry(&hSearch)) {
1053 acceptCallbackPtr = (AcceptCallback *) Tcl_GetHashValue(hPtr);
1054 acceptCallbackPtr->interp = (Tcl_Interp *) NULL;
1056 Tcl_DeleteHashTable(hTblPtr);
1057 ckfree((char *) hTblPtr);
1061 *----------------------------------------------------------------------
1063 * RegisterTcpServerInterpCleanup --
1065 * Registers an accept callback record to have its interp
1066 * field set to NULL when the interpreter is deleted.
1072 * When, in the future, the interpreter is deleted, the interp
1073 * field of the accept callback data structure will be set to
1074 * NULL. This will prevent attempts to eval the accept script
1075 * in a deleted interpreter.
1077 *----------------------------------------------------------------------
1081 RegisterTcpServerInterpCleanup(interp, acceptCallbackPtr)
1082 Tcl_Interp *interp; /* Interpreter for which we want to be
1083 * informed of deletion. */
1084 AcceptCallback *acceptCallbackPtr;
1085 /* The accept callback record whose
1086 * interp field we want set to NULL when
1087 * the interpreter is deleted. */
1089 Tcl_HashTable *hTblPtr; /* Hash table for accept callback
1090 * records to smash when the interpreter
1091 * will be deleted. */
1092 Tcl_HashEntry *hPtr; /* Entry for this record. */
1093 int new; /* Is the entry new? */
1095 hTblPtr = (Tcl_HashTable *) Tcl_GetAssocData(interp,
1096 "tclTCPAcceptCallbacks",
1098 if (hTblPtr == (Tcl_HashTable *) NULL) {
1099 hTblPtr = (Tcl_HashTable *) ckalloc((unsigned) sizeof(Tcl_HashTable));
1100 Tcl_InitHashTable(hTblPtr, TCL_ONE_WORD_KEYS);
1101 (void) Tcl_SetAssocData(interp, "tclTCPAcceptCallbacks",
1102 TcpAcceptCallbacksDeleteProc, (ClientData) hTblPtr);
1104 hPtr = Tcl_CreateHashEntry(hTblPtr, (char *) acceptCallbackPtr, &new);
1106 panic("RegisterTcpServerCleanup: damaged accept record table");
1108 Tcl_SetHashValue(hPtr, (ClientData) acceptCallbackPtr);
1112 *----------------------------------------------------------------------
1114 * UnregisterTcpServerInterpCleanupProc --
1116 * Unregister a previously registered accept callback record. The
1117 * interp field of this record will no longer be set to NULL in
1118 * the future when the interpreter is deleted.
1124 * Prevents the interp field of the accept callback record from
1125 * being set to NULL in the future when the interpreter is deleted.
1127 *----------------------------------------------------------------------
1131 UnregisterTcpServerInterpCleanupProc(interp, acceptCallbackPtr)
1132 Tcl_Interp *interp; /* Interpreter in which the accept callback
1133 * record was registered. */
1134 AcceptCallback *acceptCallbackPtr;
1135 /* The record for which to delete the
1138 Tcl_HashTable *hTblPtr;
1139 Tcl_HashEntry *hPtr;
1141 hTblPtr = (Tcl_HashTable *) Tcl_GetAssocData(interp,
1142 "tclTCPAcceptCallbacks", NULL);
1143 if (hTblPtr == (Tcl_HashTable *) NULL) {
1146 hPtr = Tcl_FindHashEntry(hTblPtr, (char *) acceptCallbackPtr);
1147 if (hPtr == (Tcl_HashEntry *) NULL) {
1150 Tcl_DeleteHashEntry(hPtr);
1154 *----------------------------------------------------------------------
1156 * AcceptCallbackProc --
1158 * This callback is invoked by the TCP channel driver when it
1159 * accepts a new connection from a client on a server socket.
1165 * Whatever the script does.
1167 *----------------------------------------------------------------------
1171 AcceptCallbackProc(callbackData, chan, address, port)
1172 ClientData callbackData; /* The data stored when the callback
1173 * was created in the call to
1174 * Tcl_OpenTcpServer. */
1175 Tcl_Channel chan; /* Channel for the newly accepted
1177 char *address; /* Address of client that was
1179 int port; /* Port of client that was accepted. */
1181 AcceptCallback *acceptCallbackPtr;
1184 char portBuf[TCL_INTEGER_SPACE];
1187 acceptCallbackPtr = (AcceptCallback *) callbackData;
1190 * Check if the callback is still valid; the interpreter may have gone
1191 * away, this is signalled by setting the interp field of the callback
1195 if (acceptCallbackPtr->interp != (Tcl_Interp *) NULL) {
1197 script = acceptCallbackPtr->script;
1198 interp = acceptCallbackPtr->interp;
1200 Tcl_Preserve((ClientData) script);
1201 Tcl_Preserve((ClientData) interp);
1203 TclFormatInt(portBuf, port);
1204 Tcl_RegisterChannel(interp, chan);
1207 * Artificially bump the refcount to protect the channel from
1208 * being deleted while the script is being evaluated.
1211 Tcl_RegisterChannel((Tcl_Interp *) NULL, chan);
1213 result = Tcl_VarEval(interp, script, " ", Tcl_GetChannelName(chan),
1214 " ", address, " ", portBuf, (char *) NULL);
1215 if (result != TCL_OK) {
1216 Tcl_BackgroundError(interp);
1217 Tcl_UnregisterChannel(interp, chan);
1221 * Decrement the artificially bumped refcount. After this it is
1222 * not safe anymore to use "chan", because it may now be deleted.
1225 Tcl_UnregisterChannel((Tcl_Interp *) NULL, chan);
1227 Tcl_Release((ClientData) interp);
1228 Tcl_Release((ClientData) script);
1232 * The interpreter has been deleted, so there is no useful
1233 * way to utilize the client socket - just close it.
1236 Tcl_Close((Tcl_Interp *) NULL, chan);
1241 *----------------------------------------------------------------------
1243 * TcpServerCloseProc --
1245 * This callback is called when the TCP server channel for which it
1246 * was registered is being closed. It informs the interpreter in
1247 * which the accept script is evaluated (if that interpreter still
1248 * exists) that this channel no longer needs to be informed if the
1249 * interpreter is deleted.
1255 * In the future, if the interpreter is deleted this channel will
1256 * no longer be informed.
1258 *----------------------------------------------------------------------
1262 TcpServerCloseProc(callbackData)
1263 ClientData callbackData; /* The data passed in the call to
1264 * Tcl_CreateCloseHandler. */
1266 AcceptCallback *acceptCallbackPtr;
1267 /* The actual data. */
1269 acceptCallbackPtr = (AcceptCallback *) callbackData;
1270 if (acceptCallbackPtr->interp != (Tcl_Interp *) NULL) {
1271 UnregisterTcpServerInterpCleanupProc(acceptCallbackPtr->interp,
1274 Tcl_EventuallyFree((ClientData) acceptCallbackPtr->script, TCL_DYNAMIC);
1275 ckfree((char *) acceptCallbackPtr);
1279 *----------------------------------------------------------------------
1281 * Tcl_SocketObjCmd --
1283 * This procedure is invoked to process the "socket" Tcl command.
1284 * See the user documentation for details on what it does.
1287 * A standard Tcl result.
1290 * Creates a socket based channel.
1292 *----------------------------------------------------------------------
1296 Tcl_SocketObjCmd(notUsed, interp, objc, objv)
1297 ClientData notUsed; /* Not used. */
1298 Tcl_Interp *interp; /* Current interpreter. */
1299 int objc; /* Number of arguments. */
1300 Tcl_Obj *CONST objv[]; /* Argument objects. */
1302 static CONST char *socketOptions[] = {
1303 "-async", "-myaddr", "-myport","-server", (char *) NULL
1305 enum socketOptions {
1306 SKT_ASYNC, SKT_MYADDR, SKT_MYPORT, SKT_SERVER
1308 int optionIndex, a, server, port;
1309 char *arg, *copyScript, *host, *script;
1310 char *myaddr = NULL;
1314 AcceptCallback *acceptCallbackPtr;
1319 if (TclpHasSockets(interp) != TCL_OK) {
1323 for (a = 1; a < objc; a++) {
1324 arg = Tcl_GetString(objv[a]);
1325 if (arg[0] != '-') {
1328 if (Tcl_GetIndexFromObj(interp, objv[a], socketOptions,
1329 "option", TCL_EXACT, &optionIndex) != TCL_OK) {
1332 switch ((enum socketOptions) optionIndex) {
1335 Tcl_AppendResult(interp,
1336 "cannot set -async option for server sockets",
1346 Tcl_AppendResult(interp,
1347 "no argument given for -myaddr option",
1351 myaddr = Tcl_GetString(objv[a]);
1358 Tcl_AppendResult(interp,
1359 "no argument given for -myport option",
1363 myPortName = Tcl_GetString(objv[a]);
1364 if (TclSockGetPort(interp, myPortName, "tcp", &myport)
1372 Tcl_AppendResult(interp,
1373 "cannot set -async option for server sockets",
1380 Tcl_AppendResult(interp,
1381 "no argument given for -server option",
1385 script = Tcl_GetString(objv[a]);
1389 panic("Tcl_SocketObjCmd: bad option index to SocketOptions");
1394 host = myaddr; /* NULL implies INADDR_ANY */
1396 Tcl_AppendResult(interp, "Option -myport is not valid for servers",
1400 } else if (a < objc) {
1401 host = Tcl_GetString(objv[a]);
1405 Tcl_AppendResult(interp, "wrong # args: should be either:\n",
1406 Tcl_GetString(objv[0]),
1407 " ?-myaddr addr? ?-myport myport? ?-async? host port\n",
1408 Tcl_GetString(objv[0]),
1409 " -server command ?-myaddr addr? port",
1415 if (TclSockGetPort(interp, Tcl_GetString(objv[a]),
1416 "tcp", &port) != TCL_OK) {
1424 acceptCallbackPtr = (AcceptCallback *) ckalloc((unsigned)
1425 sizeof(AcceptCallback));
1426 copyScript = ckalloc((unsigned) strlen(script) + 1);
1427 strcpy(copyScript, script);
1428 acceptCallbackPtr->script = copyScript;
1429 acceptCallbackPtr->interp = interp;
1430 chan = Tcl_OpenTcpServer(interp, port, host, AcceptCallbackProc,
1431 (ClientData) acceptCallbackPtr);
1432 if (chan == (Tcl_Channel) NULL) {
1434 ckfree((char *) acceptCallbackPtr);
1439 * Register with the interpreter to let us know when the
1440 * interpreter is deleted (by having the callback set the
1441 * acceptCallbackPtr->interp field to NULL). This is to
1442 * avoid trying to eval the script in a deleted interpreter.
1445 RegisterTcpServerInterpCleanup(interp, acceptCallbackPtr);
1448 * Register a close callback. This callback will inform the
1449 * interpreter (if it still exists) that this channel does not
1450 * need to be informed when the interpreter is deleted.
1453 Tcl_CreateCloseHandler(chan, TcpServerCloseProc,
1454 (ClientData) acceptCallbackPtr);
1456 chan = Tcl_OpenTcpClient(interp, port, host, myaddr, myport, async);
1457 if (chan == (Tcl_Channel) NULL) {
1461 Tcl_RegisterChannel(interp, chan);
1462 Tcl_AppendResult(interp, Tcl_GetChannelName(chan), (char *) NULL);
1468 *----------------------------------------------------------------------
1470 * Tcl_FcopyObjCmd --
1472 * This procedure is invoked to process the "fcopy" Tcl command.
1473 * See the user documentation for details on what it does.
1476 * A standard Tcl result.
1479 * Moves data between two channels and possibly sets up a
1480 * background copy handler.
1482 *----------------------------------------------------------------------
1486 Tcl_FcopyObjCmd(dummy, interp, objc, objv)
1487 ClientData dummy; /* Not used. */
1488 Tcl_Interp *interp; /* Current interpreter. */
1489 int objc; /* Number of arguments. */
1490 Tcl_Obj *CONST objv[]; /* Argument objects. */
1492 Tcl_Channel inChan, outChan;
1497 static CONST char* switches[] = { "-size", "-command", NULL };
1498 enum { FcopySize, FcopyCommand };
1500 if ((objc < 3) || (objc > 7) || (objc == 4) || (objc == 6)) {
1501 Tcl_WrongNumArgs(interp, 1, objv,
1502 "input output ?-size size? ?-command callback?");
1507 * Parse the channel arguments and verify that they are readable
1508 * or writable, as appropriate.
1511 arg = Tcl_GetString(objv[1]);
1512 inChan = Tcl_GetChannel(interp, arg, &mode);
1513 if (inChan == (Tcl_Channel) NULL) {
1516 if ((mode & TCL_READABLE) == 0) {
1517 Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "channel \"",
1519 "\" wasn't opened for reading", (char *) NULL);
1522 arg = Tcl_GetString(objv[2]);
1523 outChan = Tcl_GetChannel(interp, arg, &mode);
1524 if (outChan == (Tcl_Channel) NULL) {
1527 if ((mode & TCL_WRITABLE) == 0) {
1528 Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "channel \"",
1530 "\" wasn't opened for writing", (char *) NULL);
1536 for (i = 3; i < objc; i += 2) {
1537 if (Tcl_GetIndexFromObj(interp, objv[i], switches, "switch", 0,
1538 (int *) &index) != TCL_OK) {
1543 if (Tcl_GetIntFromObj(interp, objv[i+1], &toRead) != TCL_OK) {
1553 return TclCopyChannel(interp, inChan, outChan, toRead, cmdPtr);