/* *---------------------------------------------------------------------- * * DeleteImportedCmd -> deleteImportedCmd * * Invoked by Tcl whenever an imported command is deleted. The "real" * command keeps a list of all the imported commands that refer to it, * so those imported commands can be deleted when the real command is * deleted. This procedure removes the imported command reference from * the real command's list, and frees up the memory associated with * the imported command. * * Results: * None. * * Side effects: * Removes the imported command from the real command's import list. * *---------------------------------------------------------------------- */ internal static void deleteImportedCmd(ImportedCmdData data) // The data object for this imported command { WrappedCommand realCmd = data.realCmd; WrappedCommand self = data.self; ImportRef ref_Renamed, prev; prev = null; for (ref_Renamed = realCmd.importRef; ref_Renamed != null; ref_Renamed = ref_Renamed.next) { if (ref_Renamed.importedCmd == self) { // Remove ref from real command's list of imported commands // that refer to it. if (prev == null) { // ref is first in list realCmd.importRef = ref_Renamed.next; } else { prev.next = ref_Renamed.next; } ref_Renamed = null; data = null; return ; } prev = ref_Renamed; } throw new TclRuntimeError("DeleteImportedCmd: did not find cmd in real cmd's list of import references"); }
/* *---------------------------------------------------------------------- * * Tcl_Import -> importList * * Imports all of the commands matching a pattern into the namespace * specified by namespace (or the current namespace if namespace * is null). This is done by creating a new command (the "imported * command") that points to the real command in its original namespace. * * If matching commands are on the autoload path but haven't been * loaded yet, this command forces them to be loaded, then creates * the links to them. * * Results: * Returns if successful, raises TclException if something goes wrong. * * Side effects: * Creates new commands in the importing namespace. These indirect * calls back to the real command and are deleted if the real commands * are deleted. * *---------------------------------------------------------------------- */ internal static void importList(Interp interp, Namespace namespace_Renamed, string pattern, bool allowOverwrite) { Namespace ns, importNs; Namespace currNs = getCurrentNamespace(interp); string simplePattern, cmdName; IEnumerator search; WrappedCommand cmd, realCmd; ImportRef ref_Renamed; WrappedCommand autoCmd, importedCmd; ImportedCmdData data; bool wasExported; int i, result; // If the specified namespace is null, use the current namespace. if (namespace_Renamed == null) { ns = currNs; } else { ns = namespace_Renamed; } // First, invoke the "auto_import" command with the pattern // being imported. This command is part of the Tcl library. // It looks for imported commands in autoloaded libraries and // loads them in. That way, they will be found when we try // to create links below. autoCmd = findCommand(interp, "auto_import", null, TCL.VarFlag.GLOBAL_ONLY); if (autoCmd != null) { TclObject[] objv = new TclObject[2]; objv[0] = TclString.newInstance("auto_import"); objv[0].preserve(); objv[1] = TclString.newInstance(pattern); objv[1].preserve(); cmd = autoCmd; try { // Invoke the command with the arguments cmd.cmd.cmdProc(interp, objv); } finally { objv[0].release(); objv[1].release(); } interp.resetResult(); } // From the pattern, find the namespace from which we are importing // and get the simple pattern (no namespace qualifiers or ::'s) at // the end. if (pattern.Length == 0) { throw new TclException(interp, "empty import pattern"); } // Java does not support passing an address so we pass // an array of size 1 and then assign arr[0] to the value Namespace[] importNsArr = new Namespace[1]; Namespace[] dummyArr = new Namespace[1]; string[] simplePatternArr = new string[1]; getNamespaceForQualName(interp, pattern, ns, TCL.VarFlag.LEAVE_ERR_MSG, importNsArr, dummyArr, dummyArr, simplePatternArr); importNs = importNsArr[0]; simplePattern = simplePatternArr[0]; if (importNs == null) { throw new TclException(interp, "unknown namespace in import pattern \"" + pattern + "\""); } if (importNs == ns) { if ((System.Object) pattern == (System.Object) simplePattern) { throw new TclException(interp, "no namespace specified in import pattern \"" + pattern + "\""); } else { throw new TclException(interp, "import pattern \"" + pattern + "\" tries to import from namespace \"" + importNs.name + "\" into itself"); } } // Scan through the command table in the source namespace and look for // exported commands that match the string pattern. Create an "imported // command" in the current namespace for each imported command; these // commands redirect their invocations to the "real" command. for (search = importNs.cmdTable.Keys.GetEnumerator(); search.MoveNext(); ) { cmdName = ((string) search.Current); if (Util.stringMatch(cmdName, simplePattern)) { // The command cmdName in the source namespace matches the // pattern. Check whether it was exported. If it wasn't, // we ignore it. wasExported = false; for (i = 0; i < importNs.numExportPatterns; i++) { if (Util.stringMatch(cmdName, importNs.exportArray[i])) { wasExported = true; break; } } if (!wasExported) { continue; } // Unless there is a name clash, create an imported command // in the current namespace that refers to cmdPtr. if ((ns.cmdTable[cmdName] == null) || allowOverwrite) { // Create the imported command and its client data. // To create the new command in the current namespace, // generate a fully qualified name for it. System.Text.StringBuilder ds; ds = new System.Text.StringBuilder(); ds.Append(ns.fullName); if (ns != interp.globalNs) { ds.Append("::"); } ds.Append(cmdName); // Check whether creating the new imported command in the // current namespace would create a cycle of imported->real // command references that also would destroy an existing // "real" command already in the current namespace. cmd = (WrappedCommand) importNs.cmdTable[cmdName]; if (cmd.cmd is ImportedCmdData) { // This is actually an imported command, find // the real command it references realCmd = getOriginalCommand(cmd); if ((realCmd != null) && (realCmd.ns == currNs) && (currNs.cmdTable[cmdName] != null)) { throw new TclException(interp, "import pattern \"" + pattern + "\" would create a loop containing command \"" + ds.ToString() + "\""); } } data = new ImportedCmdData(); // Create the imported command inside the interp interp.createCommand(ds.ToString(), data); // Lookup in the namespace for the new WrappedCommand importedCmd = findCommand(interp, ds.ToString(), ns, (TCL.VarFlag.NAMESPACE_ONLY | TCL.VarFlag.LEAVE_ERR_MSG)); data.realCmd = cmd; data.self = importedCmd; // Create an ImportRef structure describing this new import // command and add it to the import ref list in the "real" // command. ref_Renamed = new ImportRef(); ref_Renamed.importedCmd = importedCmd; ref_Renamed.next = cmd.importRef; cmd.importRef = ref_Renamed; } else { throw new TclException(interp, "can't import command \"" + cmdName + "\": already exists"); } } } return ; }
/* *---------------------------------------------------------------------- * * InvokeImportedCmd -> invokeImportedCmd * * Invoked by Tcl whenever the user calls an imported command that * was created by Tcl_Import. Finds the "real" command (in another * namespace), and passes control to it. * * Results: * Returns if successful, raises TclException if something goes wrong. * * Side effects: * Returns a result in the interpreter's result object. If anything * goes wrong, the result object is set to an error message. * *---------------------------------------------------------------------- */ internal static void invokeImportedCmd(Interp interp, ImportedCmdData data, TclObject[] objv) { WrappedCommand realCmd = data.realCmd; realCmd.cmd.cmdProc(interp, objv); }