📄 tclio.c
字号:
* output on closed channels. Removes any channeEvent handlers that were * registered in this interpreter. * *---------------------------------------------------------------------- */static voidDeleteChannelTable(clientData, interp) ClientData clientData; /* The per-interpreter data structure. */ Tcl_Interp *interp; /* The interpreter being deleted. */{ Tcl_HashTable *hTblPtr; /* The hash table. */ Tcl_HashSearch hSearch; /* Search variable. */ Tcl_HashEntry *hPtr; /* Search variable. */ Channel *chanPtr; /* Channel being deleted. */ EventScriptRecord *sPtr, *prevPtr, *nextPtr; /* Variables to loop over all channel events * registered, to delete the ones that refer * to the interpreter being deleted. */ /* * Delete all the registered channels - this will close channels whose * refcount reaches zero. */ hTblPtr = (Tcl_HashTable *) clientData; for (hPtr = Tcl_FirstHashEntry(hTblPtr, &hSearch); hPtr != (Tcl_HashEntry *) NULL; hPtr = Tcl_FirstHashEntry(hTblPtr, &hSearch)) { chanPtr = (Channel *) Tcl_GetHashValue(hPtr); /* * Remove any fileevents registered in this interpreter. */ for (sPtr = chanPtr->scriptRecordPtr, prevPtr = (EventScriptRecord *) NULL; sPtr != (EventScriptRecord *) NULL; sPtr = nextPtr) { nextPtr = sPtr->nextPtr; if (sPtr->interp == interp) { if (prevPtr == (EventScriptRecord *) NULL) { chanPtr->scriptRecordPtr = nextPtr; } else { prevPtr->nextPtr = nextPtr; } Tcl_DeleteChannelHandler((Tcl_Channel) chanPtr, ChannelEventScriptInvoker, (ClientData) sPtr); ckfree(sPtr->script); ckfree((char *) sPtr); } else { prevPtr = sPtr; } } /* * Cannot call Tcl_UnregisterChannel because that procedure calls * Tcl_GetAssocData to get the channel table, which might already * be inaccessible from the interpreter structure. Instead, we * emulate the behavior of Tcl_UnregisterChannel directly here. */ Tcl_DeleteHashEntry(hPtr); chanPtr->refCount--; if (chanPtr->refCount <= 0) { if (!(chanPtr->flags & BG_FLUSH_SCHEDULED)) { (void) Tcl_Close(interp, (Tcl_Channel) chanPtr); } } } Tcl_DeleteHashTable(hTblPtr); ckfree((char *) hTblPtr);}/* *---------------------------------------------------------------------- * * CheckForStdChannelsBeingClosed -- * * Perform special handling for standard channels being closed. When * given a standard channel, if the refcount is now 1, it means that * the last reference to the standard channel is being explicitly * closed. Now bump the refcount artificially down to 0, to ensure the * normal handling of channels being closed will occur. Also reset the * static pointer to the channel to NULL, to avoid dangling references. * * Results: * None. * * Side effects: * Manipulates the refcount on standard channels. May smash the global * static pointer to a standard channel. * *---------------------------------------------------------------------- */static voidCheckForStdChannelsBeingClosed(chan) Tcl_Channel chan;{ Channel *chanPtr = (Channel *) chan; if ((chan == stdinChannel) && (stdinInitialized)) { if (chanPtr->refCount < 2) { chanPtr->refCount = 0; stdinChannel = NULL; return; } } else if ((chan == stdoutChannel) && (stdoutInitialized)) { if (chanPtr->refCount < 2) { chanPtr->refCount = 0; stdoutChannel = NULL; return; } } else if ((chan == stderrChannel) && (stderrInitialized)) { if (chanPtr->refCount < 2) { chanPtr->refCount = 0; stderrChannel = NULL; return; } }}/* *---------------------------------------------------------------------- * * Tcl_UnregisterChannel -- * * Deletes the hash entry for a channel associated with an interpreter. * If the interpreter given as argument is NULL, it only decrements the * reference count. * * Results: * A standard Tcl result. * * Side effects: * Deletes the hash entry for a channel associated with an interpreter. * *---------------------------------------------------------------------- */intTcl_UnregisterChannel(interp, chan) Tcl_Interp *interp; /* Interpreter in which channel is defined. */ Tcl_Channel chan; /* Channel to delete. */{ Tcl_HashTable *hTblPtr; /* Hash table of channels. */ Tcl_HashEntry *hPtr; /* Search variable. */ Channel *chanPtr; /* The real IO channel. */ chanPtr = (Channel *) chan; if (interp != (Tcl_Interp *) NULL) { hTblPtr = (Tcl_HashTable *) Tcl_GetAssocData(interp, "tclIO", NULL); if (hTblPtr == (Tcl_HashTable *) NULL) { return TCL_OK; } hPtr = Tcl_FindHashEntry(hTblPtr, chanPtr->channelName); if (hPtr == (Tcl_HashEntry *) NULL) { return TCL_OK; } if ((Channel *) Tcl_GetHashValue(hPtr) != chanPtr) { return TCL_OK; } Tcl_DeleteHashEntry(hPtr); /* * Remove channel handlers that refer to this interpreter, so that they * will not be present if the actual close is delayed and more events * happen on the channel. This may occur if the channel is shared * between several interpreters, or if the channel has async * flushing active. */ CleanupChannelHandlers(interp, chanPtr); } chanPtr->refCount--; /* * Perform special handling for standard channels being closed. If the * refCount is now 1 it means that the last reference to the standard * channel is being explicitly closed, so bump the refCount down * artificially to 0. This will ensure that the channel is actually * closed, below. Also set the static pointer to NULL for the channel. */ CheckForStdChannelsBeingClosed(chan); /* * If the refCount reached zero, close the actual channel. */ if (chanPtr->refCount <= 0) { /* * Ensure that if there is another buffer, it gets flushed * whether or not we are doing a background flush. */ if ((chanPtr->curOutPtr != NULL) && (chanPtr->curOutPtr->nextAdded > chanPtr->curOutPtr->nextRemoved)) { chanPtr->flags |= BUFFER_READY; } chanPtr->flags |= CHANNEL_CLOSED; if (!(chanPtr->flags & BG_FLUSH_SCHEDULED)) { if (Tcl_Close(interp, chan) != TCL_OK) { return TCL_ERROR; } } } return TCL_OK;}/* *---------------------------------------------------------------------- * * Tcl_RegisterChannel -- * * Adds an already-open channel to the channel table of an interpreter. * If the interpreter passed as argument is NULL, it only increments * the channel refCount. * * Results: * None. * * Side effects: * May increment the reference count of a channel. * *---------------------------------------------------------------------- */voidTcl_RegisterChannel(interp, chan) Tcl_Interp *interp; /* Interpreter in which to add the channel. */ Tcl_Channel chan; /* The channel to add to this interpreter * channel table. */{ Tcl_HashTable *hTblPtr; /* Hash table of channels. */ Tcl_HashEntry *hPtr; /* Search variable. */ int new; /* Is the hash entry new or does it exist? */ Channel *chanPtr; /* The actual channel. */ chanPtr = (Channel *) chan; if (chanPtr->channelName == (char *) NULL) { panic("Tcl_RegisterChannel: channel without name"); } if (interp != (Tcl_Interp *) NULL) { hTblPtr = GetChannelTable(interp); hPtr = Tcl_CreateHashEntry(hTblPtr, chanPtr->channelName, &new); if (new == 0) { if (chan == (Tcl_Channel) Tcl_GetHashValue(hPtr)) { return; } panic("Tcl_RegisterChannel: duplicate channel names"); } Tcl_SetHashValue(hPtr, (ClientData) chanPtr); } chanPtr->refCount++;}/* *---------------------------------------------------------------------- * * Tcl_GetChannel -- * * Finds an existing Tcl_Channel structure by name in a given * interpreter. This function is public because it is used by * channel-type-specific functions. * * Results: * A Tcl_Channel or NULL on failure. If failed, interp->result * contains an error message. It also returns, in modePtr, the * modes in which the channel is opened. * * Side effects: * None. * *---------------------------------------------------------------------- */Tcl_ChannelTcl_GetChannel(interp, chanName, modePtr) Tcl_Interp *interp; /* Interpreter in which to find or create * the channel. */ char *chanName; /* The name of the channel. */ int *modePtr; /* Where to store the mode in which the * channel was opened? Will contain an ORed * combination of TCL_READABLE and * TCL_WRITABLE, if non-NULL. */{ Channel *chanPtr; /* The actual channel. */ Tcl_HashTable *hTblPtr; /* Hash table of channels. */ Tcl_HashEntry *hPtr; /* Search variable. */ char *name; /* Translated name. */ /* * Substitute "stdin", etc. Note that even though we immediately * find the channel using Tcl_GetStdChannel, we still need to look * it up in the specified interpreter to ensure that it is present * in the channel table. Otherwise, safe interpreters would always * have access to the standard channels. */ name = chanName; if ((chanName[0] == 's') && (chanName[1] == 't')) { chanPtr = NULL; if (strcmp(chanName, "stdin") == 0) { chanPtr = (Channel *)Tcl_GetStdChannel(TCL_STDIN); } else if (strcmp(chanName, "stdout") == 0) { chanPtr = (Channel *)Tcl_GetStdChannel(TCL_STDOUT); } else if (strcmp(chanName, "stderr") == 0) { chanPtr = (Channel *)Tcl_GetStdChannel(TCL_STDERR); } if (chanPtr != NULL) { name = chanPtr->channelName; } } hTblPtr = GetChannelTable(interp); hPtr = Tcl_FindHashEntry(hTblPtr, name); if (hPtr == (Tcl_HashEntry *) NULL) { Tcl_AppendResult(interp, "can not find channel named \"", chanName, "\"", (char *) NULL); return NULL; } chanPtr = (Channel *) Tcl_GetHashValue(hPtr); if (modePtr != NULL) { *modePtr = (chanPtr->flags & (TCL_READABLE|TCL_WRITABLE)); } return (Tcl_Channel) chanPtr;}/* *---------------------------------------------------------------------- * * Tcl_CreateChannel -- * * Creates a new entry in the hash table for a Tcl_Channel * record. * * Results: * Returns the new Tcl_Channel. * * Side effects: * Creates a new Tcl_Channel instance and inserts it into the * hash table. * *---------------------------------------------------------------------- */Tcl_ChannelTcl_CreateChannel(typePtr, chanName, instanceData, mask) Tcl_ChannelType *typePtr; /* The channel type record. */ char *chanName; /* Name of channel to record. */ ClientData instanceData; /* Instance specific data. */ int mask; /* TCL_READABLE & TCL_WRITABLE to indicate * if the channel is readable, writable. */{ Channel *chanPtr; /* The channel structure newly created. */ chanPtr = (Channel *) ckalloc((unsigned) sizeof(Channel)); if (chanName != (char *) NULL) { chanPtr->channelName = ckalloc((unsigned) (strlen(chanName) + 1)); strcpy(chanPtr->channelName, chanName); } else { panic("Tcl_CreateChannel: NULL channel name"); } chanPtr->flags = mask; /* * Set the channel up initially in AUTO input translation mode to * accept "\n", "\r" and "\r\n". Output translation mode is set to * a platform specific default value. The eofChar is set to 0 for both * input and output, so that Tcl does not look for an in-file EOF * indicator (e.g. ^Z) and does not append an EOF indicator to files. */ chanPtr->inputTranslation = TCL_TRANSLATE_AUTO; chanPtr->outputTranslation = TCL_PLATFORM_TRANSLATION; chanPtr->inEofChar = 0; chanPtr->outEofChar = 0; chanPtr->unreportedError = 0; chanPtr->instanceData = instanceData; chanPtr->typePtr = typePtr; chanPtr->refCount = 0; chanPtr->closeCbPtr = (CloseCallback *) NULL; chanPtr->curOutPtr = (ChannelBuffer *) NULL; chanPtr->outQueueHead = (ChannelBuffer *) NULL; chanPtr->outQueueTail = (ChannelBuffer *) NULL; chanPtr->saveInBufPtr = (ChannelBuffer *) NULL; chanPtr->inQueueHead = (ChannelBuffer *) NULL; chanPtr->inQueueTail = (ChannelBuffer *) NULL; chanPtr->chPtr = (ChannelHandler *) NULL; chanPtr->interestMask = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -