tclio.c

来自「tcl是工具命令语言」· C语言 代码 · 共 2,110 行 · 第 1/5 页

C
2,110
字号
    ChannelState *statePtr;    CloseCallback *cbPtr;    statePtr = ((Channel *) chan)->state;    cbPtr = (CloseCallback *) ckalloc((unsigned) sizeof(CloseCallback));    cbPtr->proc = proc;    cbPtr->clientData = clientData;    cbPtr->nextPtr = statePtr->closeCbPtr;    statePtr->closeCbPtr = cbPtr;}/* *---------------------------------------------------------------------- * * Tcl_DeleteCloseHandler -- * *	Removes a callback that would have been called on closing *	the channel. If there is no matching callback then this *	function has no effect. * * Results: *	None. * * Side effects: *	The callback will not be called in the future when the channel *	is eventually closed. * *---------------------------------------------------------------------- */voidTcl_DeleteCloseHandler(chan, proc, clientData)    Tcl_Channel chan;		/* The channel for which to cancel the                                 * close callback. */    Tcl_CloseProc *proc;	/* The procedure for the callback to                                 * remove. */    ClientData clientData;	/* The callback data for the callback                                 * to remove. */{    ChannelState *statePtr;    CloseCallback *cbPtr, *cbPrevPtr;    statePtr = ((Channel *) chan)->state;    for (cbPtr = statePtr->closeCbPtr, cbPrevPtr = (CloseCallback *) NULL;	 cbPtr != (CloseCallback *) NULL;	 cbPtr = cbPtr->nextPtr) {        if ((cbPtr->proc == proc) && (cbPtr->clientData == clientData)) {            if (cbPrevPtr == (CloseCallback *) NULL) {                statePtr->closeCbPtr = cbPtr->nextPtr;            }            ckfree((char *) cbPtr);            break;        } else {            cbPrevPtr = cbPtr;        }    }}/* *---------------------------------------------------------------------- * * GetChannelTable -- * *	Gets and potentially initializes the channel table for an *	interpreter. If it is initializing the table it also inserts *	channels for stdin, stdout and stderr if the interpreter is *	trusted. * * Results: *	A pointer to the hash table created, for use by the caller. * * Side effects: *	Initializes the channel table for an interpreter. May create *	channels for stdin, stdout and stderr. * *---------------------------------------------------------------------- */static Tcl_HashTable *GetChannelTable(interp)    Tcl_Interp *interp;{    Tcl_HashTable *hTblPtr;	/* Hash table of channels. */    Tcl_Channel stdinChan, stdoutChan, stderrChan;    hTblPtr = (Tcl_HashTable *) Tcl_GetAssocData(interp, "tclIO", NULL);    if (hTblPtr == (Tcl_HashTable *) NULL) {        hTblPtr = (Tcl_HashTable *) ckalloc((unsigned) sizeof(Tcl_HashTable));        Tcl_InitHashTable(hTblPtr, TCL_STRING_KEYS);        (void) Tcl_SetAssocData(interp, "tclIO",                (Tcl_InterpDeleteProc *) DeleteChannelTable,                (ClientData) hTblPtr);        /*         * If the interpreter is trusted (not "safe"), insert channels         * for stdin, stdout and stderr (possibly creating them in the         * process).         */        if (Tcl_IsSafe(interp) == 0) {            stdinChan = Tcl_GetStdChannel(TCL_STDIN);            if (stdinChan != NULL) {                Tcl_RegisterChannel(interp, stdinChan);            }            stdoutChan = Tcl_GetStdChannel(TCL_STDOUT);            if (stdoutChan != NULL) {                Tcl_RegisterChannel(interp, stdoutChan);            }            stderrChan = Tcl_GetStdChannel(TCL_STDERR);            if (stderrChan != NULL) {                Tcl_RegisterChannel(interp, stderrChan);            }        }    }    return hTblPtr;}/* *---------------------------------------------------------------------- * * DeleteChannelTable -- * *	Deletes the channel table for an interpreter, closing any open *	channels whose refcount reaches zero. This procedure is invoked *	when an interpreter is deleted, via the AssocData cleanup *	mechanism. * * Results: *	None. * * Side effects: *	Deletes the hash table of channels. May close channels. May flush *	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. */    ChannelState *statePtr;	/* State of 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);	statePtr = chanPtr->state;        /*         * Remove any fileevents registered in this interpreter.         */                for (sPtr = statePtr->scriptRecordPtr,                 prevPtr = (EventScriptRecord *) NULL;	     sPtr != (EventScriptRecord *) NULL;	     sPtr = nextPtr) {            nextPtr = sPtr->nextPtr;            if (sPtr->interp == interp) {                if (prevPtr == (EventScriptRecord *) NULL) {                    statePtr->scriptRecordPtr = nextPtr;                } else {                    prevPtr->nextPtr = nextPtr;                }                Tcl_DeleteChannelHandler((Tcl_Channel) chanPtr,                        TclChannelEventScriptInvoker, (ClientData) sPtr);		Tcl_DecrRefCount(sPtr->scriptPtr);                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);        statePtr->refCount--;        if (statePtr->refCount <= 0) {            if (!(statePtr->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;{    ChannelState *statePtr = ((Channel *) chan)->state;    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);    if ((chan == tsdPtr->stdinChannel) && (tsdPtr->stdinInitialized)) {        if (statePtr->refCount < 2) {            statePtr->refCount = 0;            tsdPtr->stdinChannel = NULL;            return;        }    } else if ((chan == tsdPtr->stdoutChannel)	    && (tsdPtr->stdoutInitialized)) {        if (statePtr->refCount < 2) {            statePtr->refCount = 0;            tsdPtr->stdoutChannel = NULL;            return;        }    } else if ((chan == tsdPtr->stderrChannel)	    && (tsdPtr->stderrInitialized)) {        if (statePtr->refCount < 2) {            statePtr->refCount = 0;            tsdPtr->stderrChannel = NULL;            return;        }    }}/* *---------------------------------------------------------------------- * * Tcl_IsStandardChannel -- * *	Test if the given channel is a standard channel.  No attempt *	is made to check if the channel or the standard channels *	are initialized or otherwise valid. * * Results: *	Returns 1 if true, 0 if false. * * Side effects: *      None. * *---------------------------------------------------------------------- */int Tcl_IsStandardChannel(chan)    Tcl_Channel chan;		/* Channel to check. */{    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);    if ((chan == tsdPtr->stdinChannel) 	|| (chan == tsdPtr->stdoutChannel)	|| (chan == tsdPtr->stderrChannel)) {	return 1;    } else {	return 0;    }}/* *---------------------------------------------------------------------- * * 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. */    ChannelState *statePtr;	/* State of the actual channel. */    /*     * Always (un)register bottom-most channel in the stack.  This makes     * management of the channel list easier because no manipulation is     * necessary during (un)stack operation.     */    chanPtr = ((Channel *) chan)->state->bottomChanPtr;    statePtr = chanPtr->state;    if (statePtr->channelName == (CONST char *) NULL) {        panic("Tcl_RegisterChannel: channel without name");    }    if (interp != (Tcl_Interp *) NULL) {        hTblPtr = GetChannelTable(interp);        hPtr = Tcl_CreateHashEntry(hTblPtr, statePtr->channelName, &new);        if (new == 0) {            if (chan == (Tcl_Channel) Tcl_GetHashValue(hPtr)) {                return;            }	    panic("Tcl_RegisterChannel: duplicate channel names");        }        Tcl_SetHashValue(hPtr, (ClientData) chanPtr);    }    statePtr->refCount++;}/* *---------------------------------------------------------------------- * * 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.  (This all happens in the Tcl_DetachChannel helper *	function). *	 *	Finally, if the reference count of the channel drops to zero, *	it is deleted. * * Results: *	A standard Tcl result. * * Side effects: *	Calls Tcl_DetachChannel which deletes the hash entry for a channel  *	associated with an interpreter. *	 *	May delete the channel, which can have a variety of consequences, *	especially if we are forced to close the channel. * *---------------------------------------------------------------------- */intTcl_UnregisterChannel(interp, chan)    Tcl_Interp *interp;		/* Interpreter in which channel is defined. */    Tcl_Channel chan;		/* Channel to delete. */{    ChannelState *statePtr;	/* State of the real channel. */    if (DetachChannel(interp, chan) != TCL_OK) {        return TCL_OK;    }        statePtr = ((Channel *) chan)->state->bottomChanPtr->state;    /*     * 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 (statePtr->refCount <= 0) {        /*         * Ensure that if there is another buffer, it gets flushed         * whether or not we are doing a background flush.         */        if ((statePtr->curOutPtr != NULL) &&                (statePtr->curOutPtr->nextAdded >                        statePtr->curOutPtr->nextRemoved)) {            statePtr->flags |= BUFFER_READY;        }	Tcl_Preserve((ClientData)statePtr);        if (!(statePtr->flags & BG_FLUSH_SCHEDULED)) {

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?