tcltimer.c

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

C
1,143
字号
     * if it doesn't already exist.  Associate it with the command too,     * so that it will be passed in as the ClientData argument in the     * future.     */    if (assocPtr == NULL) {	assocPtr = (AfterAssocData *) ckalloc(sizeof(AfterAssocData));	assocPtr->interp = interp;	assocPtr->firstAfterPtr = NULL;	Tcl_SetAssocData(interp, "tclAfter", AfterCleanupProc,		(ClientData) assocPtr);	cmdInfo.proc = NULL;	cmdInfo.clientData = (ClientData) NULL;	cmdInfo.objProc = Tcl_AfterObjCmd;	cmdInfo.objClientData = (ClientData) assocPtr;	cmdInfo.deleteProc = NULL;	cmdInfo.deleteData = (ClientData) assocPtr;	Tcl_SetCommandInfo(interp, Tcl_GetStringFromObj(objv[0], &length),		&cmdInfo);    }    /*     * First lets see if the command was passed a number as the first argument.     */    if (objv[1]->typePtr == &tclIntType) {	ms = (int) objv[1]->internalRep.longValue;	goto processInteger;    }    argString = Tcl_GetStringFromObj(objv[1], &length);    if (isdigit(UCHAR(argString[0]))) {	/* INTL: digit */	if (Tcl_GetIntFromObj(interp, objv[1], &ms) != TCL_OK) {	    return TCL_ERROR;	}processInteger:	if (ms < 0) {	    ms = 0;	}	if (objc == 2) {	    Tcl_Sleep(ms);	    return TCL_OK;	}	afterPtr = (AfterInfo *) ckalloc((unsigned) (sizeof(AfterInfo)));	afterPtr->assocPtr = assocPtr;	if (objc == 3) {	    afterPtr->commandPtr = objv[2];	} else { 	    afterPtr->commandPtr = Tcl_ConcatObj(objc-2, objv+2);	}	Tcl_IncrRefCount(afterPtr->commandPtr);	/*	 * The variable below is used to generate unique identifiers for	 * after commands.  This id can wrap around, which can potentially	 * cause problems.  However, there are not likely to be problems	 * in practice, because after commands can only be requested to	 * about a month in the future, and wrap-around is unlikely to	 * occur in less than about 1-10 years.  Thus it's unlikely that	 * any old ids will still be around when wrap-around occurs.	 */	afterPtr->id = tsdPtr->afterId;	tsdPtr->afterId += 1;	afterPtr->token = Tcl_CreateTimerHandler(ms, AfterProc,		(ClientData) afterPtr);	afterPtr->nextPtr = assocPtr->firstAfterPtr;	assocPtr->firstAfterPtr = afterPtr;	sprintf(buf, "after#%d", afterPtr->id);	Tcl_AppendResult(interp, buf, (char *) NULL);	return TCL_OK;    }    /*     * If it's not a number it must be a subcommand.     */    if (Tcl_GetIndexFromObj(NULL, objv[1], afterSubCmds, "argument",            0, &index) != TCL_OK) {	Tcl_AppendResult(interp, "bad argument \"", argString,		"\": must be cancel, idle, info, or a number",		(char *) NULL);	return TCL_ERROR;    }    switch ((enum afterSubCmds) index) {        case AFTER_CANCEL: {	    Tcl_Obj *commandPtr;	    char *command, *tempCommand;	    int tempLength;	    if (objc < 3) {		Tcl_WrongNumArgs(interp, 2, objv, "id|command");		return TCL_ERROR;	    }	    if (objc == 3) {		commandPtr = objv[2];	    } else {		commandPtr = Tcl_ConcatObj(objc-2, objv+2);;	    }	    command = Tcl_GetStringFromObj(commandPtr, &length);	    for (afterPtr = assocPtr->firstAfterPtr;  afterPtr != NULL;		    afterPtr = afterPtr->nextPtr) {		tempCommand = Tcl_GetStringFromObj(afterPtr->commandPtr,			&tempLength);		if ((length == tempLength)		        && (memcmp((void*) command, (void*) tempCommand,			        (unsigned) length) == 0)) {		    break;		}	    }	    if (afterPtr == NULL) {		afterPtr = GetAfterEvent(assocPtr, commandPtr);	    }	    if (objc != 3) {		Tcl_DecrRefCount(commandPtr);	    }	    if (afterPtr != NULL) {		if (afterPtr->token != NULL) {		    Tcl_DeleteTimerHandler(afterPtr->token);		} else {		    Tcl_CancelIdleCall(AfterProc, (ClientData) afterPtr);		}		FreeAfterPtr(afterPtr);	    }	    break;	}	case AFTER_IDLE:	    if (objc < 3) {		Tcl_WrongNumArgs(interp, 2, objv, "script script ...");		return TCL_ERROR;	    }	    afterPtr = (AfterInfo *) ckalloc((unsigned) (sizeof(AfterInfo)));	    afterPtr->assocPtr = assocPtr;	    if (objc == 3) { 		afterPtr->commandPtr = objv[2];	    } else {		afterPtr->commandPtr = Tcl_ConcatObj(objc-2, objv+2);	    }	    Tcl_IncrRefCount(afterPtr->commandPtr);	    afterPtr->id = tsdPtr->afterId;	    tsdPtr->afterId += 1;	    afterPtr->token = NULL;	    afterPtr->nextPtr = assocPtr->firstAfterPtr;	    assocPtr->firstAfterPtr = afterPtr;	    Tcl_DoWhenIdle(AfterProc, (ClientData) afterPtr);	    sprintf(buf, "after#%d", afterPtr->id);	    Tcl_AppendResult(interp, buf, (char *) NULL);	    break;	case AFTER_INFO: {	    Tcl_Obj *resultListPtr;	    if (objc == 2) {		for (afterPtr = assocPtr->firstAfterPtr; afterPtr != NULL;		     afterPtr = afterPtr->nextPtr) {		    if (assocPtr->interp == interp) {			sprintf(buf, "after#%d", afterPtr->id);			Tcl_AppendElement(interp, buf);		    }		}		return TCL_OK;	    }	    if (objc != 3) {		Tcl_WrongNumArgs(interp, 2, objv, "?id?");		return TCL_ERROR;	    }	    afterPtr = GetAfterEvent(assocPtr, objv[2]);	    if (afterPtr == NULL) {		Tcl_AppendResult(interp, "event \"", Tcl_GetString(objv[2]),			"\" doesn't exist", (char *) NULL);		return TCL_ERROR;	    }	    resultListPtr = Tcl_GetObjResult(interp); 	    Tcl_ListObjAppendElement(interp, resultListPtr, afterPtr->commandPtr); 	    Tcl_ListObjAppendElement(interp, resultListPtr, Tcl_NewStringObj( 		(afterPtr->token == NULL) ? "idle" : "timer", -1));	    Tcl_SetObjResult(interp, resultListPtr);	    break;	}	default: {	    panic("Tcl_AfterObjCmd: bad subcommand index to afterSubCmds");	}    }    return TCL_OK;}/* *---------------------------------------------------------------------- * * GetAfterEvent -- * *	This procedure parses an "after" id such as "after#4" and *	returns a pointer to the AfterInfo structure. * * Results: *	The return value is either a pointer to an AfterInfo structure, *	if one is found that corresponds to "cmdString" and is for interp, *	or NULL if no corresponding after event can be found. * * Side effects: *	None. * *---------------------------------------------------------------------- */static AfterInfo *GetAfterEvent(assocPtr, commandPtr)    AfterAssocData *assocPtr;	/* Points to "after"-related information for				 * this interpreter. */    Tcl_Obj *commandPtr;{    char *cmdString;		/* Textual identifier for after event, such				 * as "after#6". */    AfterInfo *afterPtr;    int id;    char *end;    cmdString = Tcl_GetString(commandPtr);    if (strncmp(cmdString, "after#", 6) != 0) {	return NULL;    }    cmdString += 6;    id = strtoul(cmdString, &end, 10);    if ((end == cmdString) || (*end != 0)) {	return NULL;    }    for (afterPtr = assocPtr->firstAfterPtr; afterPtr != NULL;	    afterPtr = afterPtr->nextPtr) {	if (afterPtr->id == id) {	    return afterPtr;	}    }    return NULL;}/* *---------------------------------------------------------------------- * * AfterProc -- * *	Timer callback to execute commands registered with the *	"after" command. * * Results: *	None. * * Side effects: *	Executes whatever command was specified.  If the command *	returns an error, then the command "bgerror" is invoked *	to process the error;  if bgerror fails then information *	about the error is output on stderr. * *---------------------------------------------------------------------- */static voidAfterProc(clientData)    ClientData clientData;	/* Describes command to execute. */{    AfterInfo *afterPtr = (AfterInfo *) clientData;    AfterAssocData *assocPtr = afterPtr->assocPtr;    AfterInfo *prevPtr;    int result;    Tcl_Interp *interp;    char *script;    int numBytes;    /*     * First remove the callback from our list of callbacks;  otherwise     * someone could delete the callback while it's being executed, which     * could cause a core dump.     */    if (assocPtr->firstAfterPtr == afterPtr) {	assocPtr->firstAfterPtr = afterPtr->nextPtr;    } else {	for (prevPtr = assocPtr->firstAfterPtr; prevPtr->nextPtr != afterPtr;		prevPtr = prevPtr->nextPtr) {	    /* Empty loop body. */	}	prevPtr->nextPtr = afterPtr->nextPtr;    }    /*     * Execute the callback.     */    interp = assocPtr->interp;    Tcl_Preserve((ClientData) interp);    script = Tcl_GetStringFromObj(afterPtr->commandPtr, &numBytes);    result = Tcl_EvalEx(interp, script, numBytes, TCL_EVAL_GLOBAL);    if (result != TCL_OK) {	Tcl_AddErrorInfo(interp, "\n    (\"after\" script)");	Tcl_BackgroundError(interp);    }    Tcl_Release((ClientData) interp);        /*     * Free the memory for the callback.     */    Tcl_DecrRefCount(afterPtr->commandPtr);    ckfree((char *) afterPtr);}/* *---------------------------------------------------------------------- * * FreeAfterPtr -- * *	This procedure removes an "after" command from the list of *	those that are pending and frees its resources.  This procedure *	does *not* cancel the timer handler;  if that's needed, the *	caller must do it. * * Results: *	None. * * Side effects: *	The memory associated with afterPtr is released. * *---------------------------------------------------------------------- */static voidFreeAfterPtr(afterPtr)    AfterInfo *afterPtr;		/* Command to be deleted. */{    AfterInfo *prevPtr;    AfterAssocData *assocPtr = afterPtr->assocPtr;    if (assocPtr->firstAfterPtr == afterPtr) {	assocPtr->firstAfterPtr = afterPtr->nextPtr;    } else {	for (prevPtr = assocPtr->firstAfterPtr; prevPtr->nextPtr != afterPtr;		prevPtr = prevPtr->nextPtr) {	    /* Empty loop body. */	}	prevPtr->nextPtr = afterPtr->nextPtr;    }    Tcl_DecrRefCount(afterPtr->commandPtr);    ckfree((char *) afterPtr);}/* *---------------------------------------------------------------------- * * AfterCleanupProc -- * *	This procedure is invoked whenever an interpreter is deleted *	to cleanup the AssocData for "tclAfter". * * Results: *	None. * * Side effects: *	After commands are removed. * *---------------------------------------------------------------------- */	/* ARGSUSED */static voidAfterCleanupProc(clientData, interp)    ClientData clientData;	/* Points to AfterAssocData for the				 * interpreter. */    Tcl_Interp *interp;		/* Interpreter that is being deleted. */{    AfterAssocData *assocPtr = (AfterAssocData *) clientData;    AfterInfo *afterPtr;    while (assocPtr->firstAfterPtr != NULL) {	afterPtr = assocPtr->firstAfterPtr;	assocPtr->firstAfterPtr = afterPtr->nextPtr;	if (afterPtr->token != NULL) {	    Tcl_DeleteTimerHandler(afterPtr->token);	} else {	    Tcl_CancelIdleCall(AfterProc, (ClientData) afterPtr);	}	Tcl_DecrRefCount(afterPtr->commandPtr);	ckfree((char *) afterPtr);    }    ckfree((char *) assocPtr);}

⌨️ 快捷键说明

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