📄 itcl_ensemble.c
字号:
* Side effects: * If an error is encountered, an error is left as the result * in the interpreter. * *---------------------------------------------------------------------- */static intAddEnsemblePart(interp, ensData, partName, usageInfo, objProc, clientData, deleteProc, rVal) Tcl_Interp *interp; /* interpreter to be updated */ Ensemble* ensData; /* ensemble that will contain this part */ char* partName; /* name of the new part */ char* usageInfo; /* usage info for argument list */ Tcl_ObjCmdProc *objProc; /* handling procedure for part */ ClientData clientData; /* client data associated with part */ Tcl_CmdDeleteProc *deleteProc; /* procedure used to destroy client data */ EnsemblePart **rVal; /* returns: new ensemble part */{ EnsemblePart *ensPart; Command *cmdPtr; /* * Install the new part into the part list. */ if (CreateEnsemblePart(interp, ensData, partName, &ensPart) != TCL_OK) { return TCL_ERROR; } if (usageInfo) { ensPart->usage = ckalloc((unsigned)(strlen(usageInfo)+1)); strcpy(ensPart->usage, usageInfo); } cmdPtr = (Command*)ckalloc(sizeof(Command)); cmdPtr->hPtr = NULL; cmdPtr->nsPtr = ((Command*)ensData->cmd)->nsPtr; cmdPtr->refCount = 0; cmdPtr->cmdEpoch = 0; cmdPtr->compileProc = NULL; cmdPtr->objProc = objProc; cmdPtr->objClientData = (ClientData)clientData; cmdPtr->proc = NULL; cmdPtr->clientData = NULL; cmdPtr->deleteProc = deleteProc; cmdPtr->deleteData = (ClientData)clientData; cmdPtr->deleted = 0; cmdPtr->importRefPtr = NULL; ensPart->cmdPtr = cmdPtr; *rVal = ensPart; return TCL_OK;}/* *---------------------------------------------------------------------- * * DeleteEnsemble -- * * Invoked when the command associated with an ensemble is * destroyed, to delete the ensemble. Destroys all parts * included in the ensemble, and frees all memory associated * with it. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */static voidDeleteEnsemble(clientData) ClientData clientData; /* ensemble data */{ Ensemble* ensData = (Ensemble*)clientData; /* * BE CAREFUL: Each ensemble part removes itself from the list. * So keep deleting the first part until all parts are gone. */ while (ensData->numParts > 0) { DeleteEnsemblePart(ensData->parts[0]); } ckfree((char*)ensData->parts); ckfree((char*)ensData);}/* *---------------------------------------------------------------------- * * FindEnsemble -- * * Searches for an ensemble command and follows a path to * sub-ensembles. * * Results: * Returns TCL_OK if the ensemble was found, along with a * pointer to the ensemble data in "ensDataPtr". Returns * TCL_ERROR if anything goes wrong. * * Side effects: * If anything goes wrong, this procedure returns an error * message as the result in the interpreter. * *---------------------------------------------------------------------- */static intFindEnsemble(interp, nameArgv, nameArgc, ensDataPtr) Tcl_Interp *interp; /* interpreter containing the ensemble */ char **nameArgv; /* path of names leading to ensemble */ int nameArgc; /* number of strings in nameArgv */ Ensemble** ensDataPtr; /* returns: ensemble data */{ int i; Command* cmdPtr; Ensemble *ensData; EnsemblePart *ensPart; *ensDataPtr = NULL; /* assume that no data will be found */ /* * If there are no names in the path, then return an error. */ if (nameArgc < 1) { Tcl_AppendToObj(Tcl_GetObjResult(interp), "invalid ensemble name \"\"", -1); return TCL_ERROR; } /* * Use the first name to find the command for the top-level * ensemble. */ cmdPtr = (Command*) Tcl_FindCommand(interp, nameArgv[0], (Tcl_Namespace*)NULL, TCL_LEAVE_ERR_MSG); if (cmdPtr == NULL || cmdPtr->deleteProc != DeleteEnsemble) { Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "command \"", nameArgv[0], "\" is not an ensemble", (char*)NULL); return TCL_ERROR; } ensData = (Ensemble*)cmdPtr->objClientData; /* * Follow the trail of sub-ensemble names. */ for (i=1; i < nameArgc; i++) { if (FindEnsemblePart(interp, ensData, nameArgv[i], &ensPart) != TCL_OK) { return TCL_ERROR; } if (ensPart == NULL) { char *pname = Tcl_Merge(i, nameArgv); Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "invalid ensemble name \"", pname, "\"", (char*)NULL); ckfree(pname); return TCL_ERROR; } cmdPtr = ensPart->cmdPtr; if (cmdPtr == NULL || cmdPtr->deleteProc != DeleteEnsemble) { Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "part \"", nameArgv[i], "\" is not an ensemble", (char*)NULL); return TCL_ERROR; } ensData = (Ensemble*)cmdPtr->objClientData; } *ensDataPtr = ensData; return TCL_OK;}/* *---------------------------------------------------------------------- * * CreateEnsemblePart -- * * Creates a new part within an ensemble. * * Results: * If successful, this procedure returns TCL_OK, along with a * pointer to the new part in "ensPartPtr". If a part with the * same name already exists, this procedure returns TCL_ERROR. * * Side effects: * If anything goes wrong, this procedure returns an error * message as the result in the interpreter. * *---------------------------------------------------------------------- */static intCreateEnsemblePart(interp, ensData, partName, ensPartPtr) Tcl_Interp *interp; /* interpreter containing the ensemble */ Ensemble *ensData; /* ensemble being modified */ char* partName; /* name of the new part */ EnsemblePart **ensPartPtr; /* returns: new ensemble part */{ int i, pos, size; EnsemblePart** partList; EnsemblePart* part; /* * If a matching entry was found, then return an error. */ if (FindEnsemblePartIndex(ensData, partName, &pos)) { Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "part \"", partName, "\" already exists in ensemble", (char*)NULL); return TCL_ERROR; } /* * Otherwise, make room for a new entry. Keep the parts in * lexicographical order, so we can search them quickly * later. */ if (ensData->numParts >= ensData->maxParts) { size = ensData->maxParts*sizeof(EnsemblePart*); partList = (EnsemblePart**)ckalloc((unsigned)2*size); memcpy((VOID*)partList, (VOID*)ensData->parts, (size_t)size); ckfree((char*)ensData->parts); ensData->parts = partList; ensData->maxParts *= 2; } for (i=ensData->numParts; i > pos; i--) { ensData->parts[i] = ensData->parts[i-1]; } ensData->numParts++; part = (EnsemblePart*)ckalloc(sizeof(EnsemblePart)); part->name = (char*)ckalloc((unsigned)(strlen(partName)+1)); strcpy(part->name, partName); part->cmdPtr = NULL; part->usage = NULL; part->ensemble = ensData; ensData->parts[pos] = part; /* * Compare the new part against the one on either side of * it. Determine how many letters are needed in each part * to guarantee that an abbreviated form is unique. Update * the parts on either side as well, since they are influenced * by the new part. */ ComputeMinChars(ensData, pos); ComputeMinChars(ensData, pos-1); ComputeMinChars(ensData, pos+1); *ensPartPtr = part; return TCL_OK;}/* *---------------------------------------------------------------------- * * DeleteEnsemblePart -- * * Deletes a single part from an ensemble. The part must have * been created previously by CreateEnsemblePart. * * If the part has a delete proc, then it is called to free the * associated client data. * * Results: * None. * * Side effects: * Delete proc is called. * *---------------------------------------------------------------------- */static voidDeleteEnsemblePart(ensPart) EnsemblePart *ensPart; /* part being destroyed */{ int i, pos; Command *cmdPtr; Ensemble *ensData; cmdPtr = ensPart->cmdPtr; /* * If this part has a delete proc, then call it to free * up the client data. */ if (cmdPtr->deleteData && cmdPtr->deleteProc) { (*cmdPtr->deleteProc)(cmdPtr->deleteData); } ckfree((char*)cmdPtr); /* * Find this part within its ensemble, and remove it from * the list of parts. */ if (FindEnsemblePartIndex(ensPart->ensemble, ensPart->name, &pos)) { ensData = ensPart->ensemble; for (i=pos; i < ensData->numParts-1; i++) { ensData->parts[i] = ensData->parts[i+1]; } ensData->numParts--; } /* * Free the memory associated with the part. */ if (ensPart->usage) { ckfree(ensPart->usage); } ckfree(ensPart->name); ckfree((char*)ensPart);}/* *---------------------------------------------------------------------- * * FindEnsemblePart -- * * Searches for a part name within an ensemble. Recognizes * unique abbreviations for part names. * * Results: * If the part name is not a unique abbreviation, this procedure * returns TCL_ERROR. Otherwise, it returns TCL_OK. If the * part can be found, "rensPart" returns a pointer to the part. * Otherwise, it returns NULL. * * Side effects: * If anything goes wrong, this procedure returns an error * message as the result in the interpreter. * *---------------------------------------------------------------------- */static intFindEnsemblePart(interp, ensData, partName, rensPart) Tcl_Interp *interp; /* interpreter containing the ensemble */ Ensemble *ensData; /* ensemble being searched */ char* partName; /* name of the desired part */ EnsemblePart **rensPart; /* returns: pointer to the desired part */{ int pos = 0; int first, last, nlen; int i, cmp; *rensPart = NULL; /* * Search for the desired part name. * All parts are in lexicographical order, so use a * binary search to find the part quickly. Match only * as many characters as are included in the specified * part name. */ first = 0; last = ensData->numParts-1; nlen = strlen(partName); while (last >= first) { pos = (first+last)/2; if (*partName == *ensData->parts[pos]->name) { cmp = strncmp(partName, ensData->parts[pos]->name, nlen); if (cmp == 0) { break; /* found it! */ } } else if (*partName < *ensData->parts[pos]->name) { cmp = -1; } else { cmp = 1; } if (cmp > 0) { first = pos+1; } else { last = pos-1; } } /* * If a matching entry could not be found, then quit. */ if (last < first) { return TCL_OK; } /* * If a matching entry was found, there may be some ambiguity * if the user did not specify enough characters. Find the * top-most match in the list, and see if the part name has * enough characters. If there are two parts like "foo" * and "food", this allows us to match "foo" exactly. */ if (nlen < ensData->parts[pos]->minChars) { while (pos > 0) { pos--; if (strncmp(partName, ensData->parts[pos]->name, nlen) != 0) { pos++; break; } } } if (nlen < ensData->parts[pos]->minChars) { Tcl_Obj *resultPtr = Tcl_NewStringObj((char*)NULL, 0); Tcl_AppendStringsToObj(resultPtr, "ambiguous option \"", partName, "\": should be one of...", (char*)NULL); for (i=pos; i < ensData->numParts; i++) { if (strncmp(partName, ensData->parts[i]->name, nlen) != 0) { break; } Tcl_AppendToObj(resultPtr, "\n ", 3);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -