PageRenderTime 138ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 1ms

/trunk/managed/csharpsqlite/TCL/src/commands/NamespaceCmd.cs

https://bitbucket.org/KyanhaLLC/opensim-libs
C# | 3658 lines | 1632 code | 560 blank | 1466 comment | 432 complexity | 12fb30405e0ed62db687a6b89fe50b51 MD5 | raw file
Possible License(s): Apache-2.0, BSD-2-Clause, MIT, LGPL-2.1, LGPL-3.0, GPL-2.0, CC-BY-SA-3.0, GPL-3.0, BSD-3-Clause
  1. #undef DEBUG
  2. /*
  3. * NamespaceCmd.java
  4. *
  5. * Copyright (c) 1993-1997 Lucent Technologies.
  6. * Copyright (c) 1997 Sun Microsystems, Inc.
  7. * Copyright (c) 1998-1999 by Scriptics Corporation.
  8. * Copyright (c) 1999 Moses DeJong
  9. *
  10. * Originally implemented by
  11. * Michael J. McLennan
  12. * Bell Labs Innovations for Lucent Technologies
  13. * mmclennan@lucent.com
  14. *
  15. * See the file "license.terms" for information on usage and
  16. * redistribution of this file, and for a DISCLAIMER OF ALL
  17. * WARRANTIES.
  18. *
  19. * Included in SQLite3 port to C# for use in testharness only; 2008 Noah B Hart
  20. * $Header$
  21. * RCS @(#) $Id: NamespaceCmd.java,v 1.12 2001/05/05 22:38:13 mdejong Exp $
  22. */
  23. using System;
  24. using System.Collections;
  25. namespace tcl.lang
  26. {
  27. /// <summary> This class implements the built-in "namespace" command in Tcl.
  28. /// See the user documentation for details on what it does.
  29. /// </summary>
  30. public class NamespaceCmd : InternalRep, Command
  31. {
  32. // Flag passed to getNamespaceForQualName to indicate that it should
  33. // search for a namespace rather than a command or variable inside a
  34. // namespace. Note that this flag's value must not conflict with the values
  35. // of TCL.VarFlag.GLOBAL_ONLY, TCL.VarFlag.NAMESPACE_ONLY, or TCL.VarFlag.CREATE_NS_IF_UNKNOWN.
  36. // Initial size of stack allocated space for tail list - used when resetting
  37. // shadowed command references in the functin: TclResetShadowedCmdRefs.
  38. //private static final int NUM_TRAIL_ELEMS = 5;
  39. // Count of the number of namespaces created. This value is used as a
  40. // unique id for each namespace.
  41. private static long numNsCreated = 0;
  42. private static Object nsMutex;
  43. //
  44. // Flags used to represent the status of a namespace:
  45. //
  46. // NS_DYING - 1 means deleteNamespace has been called to delete the
  47. // namespace but there are still active call frames on the Tcl
  48. // stack that refer to the namespace. When the last call frame
  49. // referring to it has been popped, it's variables and command
  50. // will be destroyed and it will be marked "dead" (NS_DEAD).
  51. // The namespace can no longer be looked up by name.
  52. // NS_DEAD - 1 means deleteNamespace has been called to delete the
  53. // namespace and no call frames still refer to it. Its
  54. // variables and command have already been destroyed. This bit
  55. // allows the namespace resolution code to recognize that the
  56. // namespace is "deleted". When the last namespaceName object
  57. // in any byte code code unit that refers to the namespace has
  58. // been freed (i.e., when the namespace's refCount is 0), the
  59. // namespace's storage will be freed.
  60. internal const int NS_DYING = 0x01;
  61. internal const int NS_DEAD = 0x02;
  62. // Flag passed to getNamespaceForQualName to have it create all namespace
  63. // components of a namespace-qualified name that cannot be found. The new
  64. // namespaces are created within their specified parent. Note that this
  65. // flag's value must not conflict with the values of the flags
  66. // TCL.VarFlag.GLOBAL_ONLY, TCL.VarFlag.NAMESPACE_ONLY, and TCL.VarFlag.FIND_ONLY_NS
  67. // internal const int TCL.VarFlag.CREATE_NS_IF_UNKNOWN = 0x800;
  68. // This value corresponds to the Tcl_Obj.otherValuePtr pointer used
  69. // in the C version of Tcl 8.1. Use it to keep track of a ResolvedNsName.
  70. private ResolvedNsName otherValue = null;
  71. /*
  72. *----------------------------------------------------------------------
  73. *
  74. * Tcl_GetCurrentNamespace -> getCurrentNamespace
  75. *
  76. * Returns a reference to an interpreter's currently active namespace.
  77. *
  78. * Results:
  79. * Returns a reference to the interpreter's current namespace.
  80. *
  81. * Side effects:
  82. * None.
  83. *
  84. *----------------------------------------------------------------------
  85. */
  86. internal static Namespace getCurrentNamespace(Interp interp)
  87. {
  88. if (interp.varFrame != null)
  89. {
  90. return interp.varFrame.ns;
  91. }
  92. else
  93. {
  94. return interp.globalNs;
  95. }
  96. }
  97. /*
  98. *----------------------------------------------------------------------
  99. *
  100. * Tcl_GetGlobalNamespace -> getGlobalNamespace
  101. *
  102. * Returns a reference to an interpreter's global :: namespace.
  103. *
  104. * Results:
  105. * Returns a reference to the specified interpreter's global namespace.
  106. *
  107. * Side effects:
  108. * None.
  109. *
  110. *----------------------------------------------------------------------
  111. */
  112. internal static Namespace getGlobalNamespace(Interp interp)
  113. {
  114. return interp.globalNs;
  115. }
  116. /*
  117. *----------------------------------------------------------------------
  118. *
  119. * Tcl_PushCallFrame -> pushCallFrame
  120. *
  121. * Pushes a new call frame onto the interpreter's Tcl call stack.
  122. * Called when executing a Tcl procedure or a "namespace eval" or
  123. * "namespace inscope" command.
  124. *
  125. * Results:
  126. * Returns if successful, raises TclException if something goes wrong.
  127. *
  128. * Side effects:
  129. * Modifies the interpreter's Tcl call stack.
  130. *
  131. *----------------------------------------------------------------------
  132. */
  133. internal static void pushCallFrame(Interp interp, CallFrame frame, Namespace namespace_Renamed, bool isProcCallFrame)
  134. // If true, the frame represents a
  135. // called Tcl procedure and may have local
  136. // vars. Vars will ordinarily be looked up
  137. // in the frame. If new variables are
  138. // created, they will be created in the
  139. // frame. If false, the frame is for a
  140. // "namespace eval" or "namespace inscope"
  141. // command and var references are treated
  142. // as references to namespace variables.
  143. {
  144. Namespace ns;
  145. if (namespace_Renamed == null)
  146. {
  147. ns = getCurrentNamespace(interp);
  148. }
  149. else
  150. {
  151. ns = namespace_Renamed;
  152. if ((ns.flags & NS_DEAD) != 0)
  153. {
  154. throw new TclRuntimeError("Trying to push call frame for dead namespace");
  155. }
  156. }
  157. ns.activationCount++;
  158. frame.ns = ns;
  159. frame.isProcCallFrame = isProcCallFrame;
  160. frame.objv = null;
  161. frame.caller = interp.frame;
  162. frame.callerVar = interp.varFrame;
  163. if (interp.varFrame != null)
  164. {
  165. frame.level = (interp.varFrame.level + 1);
  166. }
  167. else
  168. {
  169. frame.level = 1;
  170. }
  171. // FIXME : does Jacl need a procPtr in the CallFrame class?
  172. //frame.procPtr = null; // no called procedure
  173. frame.varTable = null; // and no local variables
  174. // Compiled locals are not part of Jacl's CallFrame
  175. // Push the new call frame onto the interpreter's stack of procedure
  176. // call frames making it the current frame.
  177. interp.frame = frame;
  178. interp.varFrame = frame;
  179. }
  180. /*
  181. *----------------------------------------------------------------------
  182. *
  183. * Tcl_PopCallFrame -> popCallFrame
  184. *
  185. * Removes a call frame from the Tcl call stack for the interpreter.
  186. * Called to remove a frame previously pushed by Tcl_PushCallFrame.
  187. *
  188. * Results:
  189. * None.
  190. *
  191. * Side effects:
  192. * Modifies the call stack of the interpreter. Resets various fields of
  193. * the popped call frame. If a namespace has been deleted and
  194. * has no more activations on the call stack, the namespace is
  195. * destroyed.
  196. *
  197. *----------------------------------------------------------------------
  198. */
  199. internal static void popCallFrame(Interp interp)
  200. {
  201. CallFrame frame = interp.frame;
  202. int saveErrFlag;
  203. Namespace ns;
  204. // It's important to remove the call frame from the interpreter's stack
  205. // of call frames before deleting local variables, so that traces
  206. // invoked by the variable deletion don't see the partially-deleted
  207. // frame.
  208. interp.frame = frame.caller;
  209. interp.varFrame = frame.callerVar;
  210. // Delete the local variables. As a hack, we save then restore the
  211. // ERR_IN_PROGRESS flag in the interpreter. The problem is that there
  212. // could be unset traces on the variables, which cause scripts to be
  213. // evaluated. This will clear the ERR_IN_PROGRESS flag, losing stack
  214. // trace information if the procedure was exiting with an error. The
  215. // code below preserves the flag. Unfortunately, that isn't really
  216. // enough: we really should preserve the errorInfo variable too
  217. // (otherwise a nested error in the trace script will trash errorInfo).
  218. // What's really needed is a general-purpose mechanism for saving and
  219. // restoring interpreter state.
  220. saveErrFlag = (interp.flags & Parser.ERR_IN_PROGRESS);
  221. if (frame.varTable != null)
  222. {
  223. Var.deleteVars(interp, frame.varTable);
  224. frame.varTable = null;
  225. }
  226. interp.flags |= saveErrFlag;
  227. // Decrement the namespace's count of active call frames. If the
  228. // namespace is "dying" and there are no more active call frames,
  229. // call Tcl_DeleteNamespace to destroy it.
  230. ns = frame.ns;
  231. ns.activationCount--;
  232. if (((ns.flags & NS_DYING) != 0) && (ns.activationCount == 0))
  233. {
  234. deleteNamespace(ns);
  235. }
  236. frame.ns = null;
  237. }
  238. /*
  239. *----------------------------------------------------------------------
  240. *
  241. * Tcl_CreateNamespace --
  242. *
  243. * Creates a new namespace with the given name. If there is no
  244. * active namespace (i.e., the interpreter is being initialized),
  245. * the global :: namespace is created and returned.
  246. *
  247. * Results:
  248. * Returns a reference to the new namespace if successful. If the
  249. * namespace already exists or if another error occurs, this routine
  250. * returns null, along with an error message in the interpreter's
  251. * result object.
  252. *
  253. * Side effects:
  254. * If the name contains "::" qualifiers and a parent namespace does
  255. * not already exist, it is automatically created.
  256. *
  257. *----------------------------------------------------------------------
  258. */
  259. internal static Namespace createNamespace(Interp interp, string name, DeleteProc deleteProc)
  260. {
  261. Namespace ns, ancestor;
  262. Namespace parent;
  263. Namespace globalNs = getGlobalNamespace(interp);
  264. string simpleName;
  265. System.Text.StringBuilder buffer1, buffer2;
  266. // If there is no active namespace, the interpreter is being
  267. // initialized.
  268. if ((globalNs == null) && (interp.varFrame == null))
  269. {
  270. // Treat this namespace as the global namespace, and avoid
  271. // looking for a parent.
  272. parent = null;
  273. simpleName = "";
  274. }
  275. else if (name.Length == 0)
  276. {
  277. /*
  278. TclObject tobj = interp.getResult();
  279. // FIXME : is there a test case to check this error result?
  280. TclString.append(tobj,
  281. "can't create namespace \"\": only global namespace can have empty name");
  282. */
  283. // FIXME : is there a test case to check this error result?
  284. interp.setResult("can't create namespace \"\": only global namespace can have empty name");
  285. return null;
  286. }
  287. else
  288. {
  289. // Find the parent for the new namespace.
  290. // Java does not support passing an address so we pass
  291. // an array of size 1 and then assign arr[0] to the value
  292. Namespace[] parentArr = new Namespace[1];
  293. Namespace[] dummyArr = new Namespace[1];
  294. string[] simpleArr = new string[1];
  295. getNamespaceForQualName(interp, name, null, (TCL.VarFlag.CREATE_NS_IF_UNKNOWN | TCL.VarFlag.LEAVE_ERR_MSG), parentArr, dummyArr, dummyArr, simpleArr);
  296. // Get the values out of the arrays!
  297. parent = parentArr[0];
  298. simpleName = simpleArr[0];
  299. // If the unqualified name at the end is empty, there were trailing
  300. // "::"s after the namespace's name which we ignore. The new
  301. // namespace was already (recursively) created and is referenced
  302. // by parent.
  303. if (simpleName.Length == 0)
  304. {
  305. return parent;
  306. }
  307. // Check for a bad namespace name and make sure that the name
  308. // does not already exist in the parent namespace.
  309. if (parent.childTable[simpleName] != null)
  310. {
  311. /*
  312. TclObject tobj = interp.getResult();
  313. // FIXME : is there a test case to check this error result?
  314. TclString.append(tobj,
  315. "can't create namespace \"" + name + "\": already exists");
  316. */
  317. // FIXME : is there a test case to check this error result?
  318. interp.setResult("can't create namespace \"" + name + "\": already exists");
  319. return null;
  320. }
  321. }
  322. // Create the new namespace and root it in its parent. Increment the
  323. // count of namespaces created.
  324. ns = new Namespace();
  325. ns.name = simpleName;
  326. ns.fullName = null; // set below
  327. //ns.clientData = clientData;
  328. ns.deleteProc = deleteProc;
  329. ns.parent = parent;
  330. ns.childTable = new Hashtable();
  331. lock (nsMutex)
  332. {
  333. numNsCreated++;
  334. ns.nsId = numNsCreated;
  335. }
  336. ns.interp = interp;
  337. ns.flags = 0;
  338. ns.activationCount = 0;
  339. // FIXME : there was a problem with the refcount because
  340. // when the namespace was deleted the refocount was 0.
  341. // We avoid this by just using a refcount of 1 for now.
  342. // We can do ignore the refCount because GC will reclaim mem.
  343. //ns.refCount = 0;
  344. ns.refCount = 1;
  345. ns.cmdTable = new Hashtable();
  346. ns.varTable = new Hashtable();
  347. ns.exportArray = null;
  348. ns.numExportPatterns = 0;
  349. ns.maxExportPatterns = 0;
  350. // Jacl does not use these tcl compiler specific members
  351. //ns.cmdRefEpoch = 0;
  352. //ns.resolverEpoch = 0;
  353. ns.resolver = null;
  354. if (parent != null)
  355. {
  356. SupportClass.PutElement(parent.childTable, simpleName, ns);
  357. }
  358. // Build the fully qualified name for this namespace.
  359. buffer1 = new System.Text.StringBuilder();
  360. buffer2 = new System.Text.StringBuilder();
  361. for (ancestor = ns; ancestor != null; ancestor = ancestor.parent)
  362. {
  363. if (ancestor != globalNs)
  364. {
  365. buffer1.Append("::");
  366. buffer1.Append(ancestor.name);
  367. }
  368. buffer1.Append(buffer2);
  369. buffer2.Length = 0;
  370. buffer2.Append(buffer1);
  371. buffer1.Length = 0;
  372. }
  373. name = buffer2.ToString();
  374. ns.fullName = name;
  375. // Return a reference to the new namespace.
  376. return ns;
  377. }
  378. /*
  379. *----------------------------------------------------------------------
  380. *
  381. * Tcl_DeleteNamespace -> deleteNamespace
  382. *
  383. * Deletes a namespace and all of the commands, variables, and other
  384. * namespaces within it.
  385. *
  386. * Results:
  387. * None.
  388. *
  389. * Side effects:
  390. * When a namespace is deleted, it is automatically removed as a
  391. * child of its parent namespace. Also, all its commands, variables
  392. * and child namespaces are deleted.
  393. *
  394. *----------------------------------------------------------------------
  395. */
  396. internal static void deleteNamespace(Namespace namespace_Renamed)
  397. {
  398. Namespace ns = namespace_Renamed;
  399. Interp interp = ns.interp;
  400. Namespace globalNs = getGlobalNamespace(interp);
  401. // If the namespace is on the call frame stack, it is marked as "dying"
  402. // (NS_DYING is OR'd into its flags): the namespace can't be looked up
  403. // by name but its commands and variables are still usable by those
  404. // active call frames. When all active call frames referring to the
  405. // namespace have been popped from the Tcl stack, popCallFrame will
  406. // call this procedure again to delete everything in the namespace.
  407. // If no nsName objects refer to the namespace (i.e., if its refCount
  408. // is zero), its commands and variables are deleted and the storage for
  409. // its namespace structure is freed. Otherwise, if its refCount is
  410. // nonzero, the namespace's commands and variables are deleted but the
  411. // structure isn't freed. Instead, NS_DEAD is OR'd into the structure's
  412. // flags to allow the namespace resolution code to recognize that the
  413. // namespace is "deleted".
  414. if (ns.activationCount > 0)
  415. {
  416. ns.flags |= NS_DYING;
  417. if (ns.parent != null)
  418. {
  419. ns.parent.childTable.Remove(ns.name);
  420. }
  421. ns.parent = null;
  422. }
  423. else
  424. {
  425. // Delete the namespace and everything in it. If this is the global
  426. // namespace, then clear it but don't free its storage unless the
  427. // interpreter is being torn down.
  428. teardownNamespace(ns);
  429. if ((ns != globalNs) || ((interp.flags & Parser.DELETED) != 0))
  430. {
  431. // If this is the global namespace, then it may have residual
  432. // "errorInfo" and "errorCode" variables for errors that
  433. // occurred while it was being torn down. Try to clear the
  434. // variable list one last time.
  435. Var.deleteVars(ns.interp, ns.varTable);
  436. ns.childTable.Clear();
  437. ns.cmdTable.Clear();
  438. // If the reference count is 0, then discard the namespace.
  439. // Otherwise, mark it as "dead" so that it can't be used.
  440. if (ns.refCount == 0)
  441. {
  442. free(ns);
  443. }
  444. else
  445. {
  446. ns.flags |= NS_DEAD;
  447. }
  448. }
  449. }
  450. }
  451. /*
  452. *----------------------------------------------------------------------
  453. *
  454. * TclTeardownNamespace -> teardownNamespace
  455. *
  456. * Used internally to dismantle and unlink a namespace when it is
  457. * deleted. Divorces the namespace from its parent, and deletes all
  458. * commands, variables, and child namespaces.
  459. *
  460. * This is kept separate from Tcl_DeleteNamespace so that the global
  461. * namespace can be handled specially. Global variables like
  462. * "errorInfo" and "errorCode" need to remain intact while other
  463. * namespaces and commands are torn down, in case any errors occur.
  464. *
  465. * Results:
  466. * None.
  467. *
  468. * Side effects:
  469. * Removes this namespace from its parent's child namespace hashtable.
  470. * Deletes all commands, variables and namespaces in this namespace.
  471. * If this is the global namespace, the "errorInfo" and "errorCode"
  472. * variables are left alone and deleted later.
  473. *
  474. *----------------------------------------------------------------------
  475. */
  476. internal static void teardownNamespace(Namespace ns)
  477. {
  478. Interp interp = ns.interp;
  479. IEnumerator search;
  480. Namespace globalNs = getGlobalNamespace(interp);
  481. int i;
  482. // Start by destroying the namespace's variable table,
  483. // since variables might trigger traces.
  484. if (ns == globalNs)
  485. {
  486. // This is the global namespace, so be careful to preserve the
  487. // "errorInfo" and "errorCode" variables. These might be needed
  488. // later on if errors occur while deleting commands. We are careful
  489. // to destroy and recreate the "errorInfo" and "errorCode"
  490. // variables, in case they had any traces on them.
  491. string errorInfoStr, errorCodeStr;
  492. try
  493. {
  494. errorInfoStr = interp.getVar("errorInfo", TCL.VarFlag.GLOBAL_ONLY).ToString();
  495. }
  496. catch (TclException e)
  497. {
  498. errorInfoStr = null;
  499. }
  500. try
  501. {
  502. errorCodeStr = interp.getVar("errorCode", TCL.VarFlag.GLOBAL_ONLY).ToString();
  503. }
  504. catch (TclException e)
  505. {
  506. errorCodeStr = null;
  507. }
  508. Var.deleteVars(interp, ns.varTable);
  509. if ((System.Object) errorInfoStr != null)
  510. {
  511. try
  512. {
  513. interp.setVar("errorInfo", errorInfoStr, TCL.VarFlag.GLOBAL_ONLY);
  514. }
  515. catch (TclException e)
  516. {
  517. // ignore an exception while setting this var
  518. }
  519. }
  520. if ((System.Object) errorCodeStr != null)
  521. {
  522. try
  523. {
  524. interp.setVar("errorCode", errorCodeStr, TCL.VarFlag.GLOBAL_ONLY);
  525. }
  526. catch (TclException e)
  527. {
  528. // ignore an exception while setting this var
  529. }
  530. }
  531. }
  532. else
  533. {
  534. // Variable table should be cleared.
  535. Var.deleteVars(interp, ns.varTable);
  536. }
  537. // Remove the namespace from its parent's child hashtable.
  538. if (ns.parent != null)
  539. {
  540. ns.parent.childTable.Remove(ns.name);
  541. }
  542. ns.parent = null;
  543. // Delete all the child namespaces.
  544. //
  545. // BE CAREFUL: When each child is deleted, it will divorce
  546. // itself from its parent. You can't traverse a hash table
  547. // properly if its elements are being deleted. We use only
  548. // the Tcl_FirstHashEntry function to be safe.
  549. foreach (Namespace childNs in new ArrayList(ns.childTable.Values))
  550. {
  551. deleteNamespace(childNs);
  552. }
  553. // Delete all commands in this namespace. Be careful when traversing the
  554. // hash table: when each command is deleted, it removes itself from the
  555. // command table.
  556. // FIXME : double check that using an enumeration for a hashtable
  557. // that changes is ok in Java! Also call deleteCommand... correctly!
  558. foreach(WrappedCommand cmd in new ArrayList(ns.cmdTable.Values))
  559. {
  560. interp.deleteCommandFromToken(cmd);
  561. }
  562. ns.cmdTable.Clear();
  563. // Free the namespace's export pattern array.
  564. if (ns.exportArray != null)
  565. {
  566. ns.exportArray = null;
  567. ns.numExportPatterns = 0;
  568. ns.maxExportPatterns = 0;
  569. }
  570. // Callback invoked when namespace is deleted
  571. if (ns.deleteProc != null)
  572. {
  573. ns.deleteProc.delete();
  574. }
  575. ns.deleteProc = null;
  576. // Reset the namespace's id field to ensure that this namespace won't
  577. // be interpreted as valid by, e.g., the cache validation code for
  578. // cached command references in Tcl_GetCommandFromObj.
  579. ns.nsId = 0;
  580. }
  581. /*
  582. *----------------------------------------------------------------------
  583. *
  584. * NamespaceFree -> free
  585. *
  586. * Called after a namespace has been deleted, when its
  587. * reference count reaches 0. Frees the data structure
  588. * representing the namespace.
  589. *
  590. * Results:
  591. * None.
  592. *
  593. * Side effects:
  594. * None.
  595. *
  596. *----------------------------------------------------------------------
  597. */
  598. internal static void free(Namespace ns)
  599. {
  600. // Most of the namespace's contents are freed when the namespace is
  601. // deleted by Tcl_DeleteNamespace. All that remains is to free its names
  602. // (for error messages), and the structure itself.
  603. ns.name = null;
  604. ns.fullName = null;
  605. }
  606. /*
  607. *----------------------------------------------------------------------
  608. *
  609. * Tcl_Export -> exportList
  610. *
  611. * Makes all the commands matching a pattern available to later be
  612. * imported from the namespace specified by namespace (or the
  613. * current namespace if namespace is null). The specified pattern is
  614. * appended onto the namespace's export pattern list, which is
  615. * optionally cleared beforehand.
  616. *
  617. * Results:
  618. * Returns if successful, raises TclException if something goes wrong.
  619. *
  620. * Side effects:
  621. * Appends the export pattern onto the namespace's export list.
  622. * Optionally reset the namespace's export pattern list.
  623. *
  624. *----------------------------------------------------------------------
  625. */
  626. internal static void exportList(Interp interp, Namespace namespace_Renamed, string pattern, bool resetListFirst)
  627. {
  628. int INIT_EXPORT_PATTERNS = 5;
  629. Namespace ns, exportNs;
  630. Namespace currNs = getCurrentNamespace(interp);
  631. string simplePattern, patternCpy;
  632. int neededElems, len, i;
  633. // If the specified namespace is null, use the current namespace.
  634. if (namespace_Renamed == null)
  635. {
  636. ns = currNs;
  637. }
  638. else
  639. {
  640. ns = namespace_Renamed;
  641. }
  642. // If resetListFirst is true (nonzero), clear the namespace's export
  643. // pattern list.
  644. if (resetListFirst)
  645. {
  646. if (ns.exportArray != null)
  647. {
  648. for (i = 0; i < ns.numExportPatterns; i++)
  649. {
  650. ns.exportArray[i] = null;
  651. }
  652. ns.exportArray = null;
  653. ns.numExportPatterns = 0;
  654. ns.maxExportPatterns = 0;
  655. }
  656. }
  657. // Check that the pattern doesn't have namespace qualifiers.
  658. // Java does not support passing an address so we pass
  659. // an array of size 1 and then assign arr[0] to the value
  660. Namespace[] exportNsArr = new Namespace[1];
  661. Namespace[] dummyArr = new Namespace[1];
  662. string[] simplePatternArr = new string[1];
  663. getNamespaceForQualName(interp, pattern, ns, TCL.VarFlag.LEAVE_ERR_MSG, exportNsArr, dummyArr, dummyArr, simplePatternArr);
  664. // get the values out of the arrays
  665. exportNs = exportNsArr[0];
  666. simplePattern = simplePatternArr[0];
  667. if ((exportNs != ns) || (pattern.CompareTo(simplePattern) != 0))
  668. {
  669. throw new TclException(interp, "invalid export pattern \"" + pattern + "\": pattern can't specify a namespace");
  670. }
  671. // Make sure there is room in the namespace's pattern array for the
  672. // new pattern.
  673. neededElems = ns.numExportPatterns + 1;
  674. if (ns.exportArray == null)
  675. {
  676. ns.exportArray = new string[INIT_EXPORT_PATTERNS];
  677. ns.numExportPatterns = 0;
  678. ns.maxExportPatterns = INIT_EXPORT_PATTERNS;
  679. }
  680. else if (neededElems > ns.maxExportPatterns)
  681. {
  682. int numNewElems = 2 * ns.maxExportPatterns;
  683. string[] newArray = new string[numNewElems];
  684. Array.Copy((System.Array) ns.exportArray, 0, (System.Array) newArray, 0, ns.numExportPatterns);
  685. ns.exportArray = newArray;
  686. ns.maxExportPatterns = numNewElems;
  687. }
  688. // Add the pattern to the namespace's array of export patterns.
  689. ns.exportArray[ns.numExportPatterns] = pattern;
  690. ns.numExportPatterns++;
  691. return ;
  692. }
  693. /*
  694. *----------------------------------------------------------------------
  695. *
  696. * Tcl_AppendExportList -> appendExportList
  697. *
  698. * Appends onto the argument object the list of export patterns for the
  699. * specified namespace.
  700. *
  701. * Results:
  702. * The method will return when successful; in this case the object
  703. * referenced by obj has each export pattern appended to it. If an
  704. * error occurs, an exception and the interpreter's result
  705. * holds an error message.
  706. *
  707. * Side effects:
  708. * If necessary, the object referenced by obj is converted into
  709. * a list object.
  710. *
  711. *----------------------------------------------------------------------
  712. */
  713. internal static void appendExportList(Interp interp, Namespace namespace_Renamed, TclObject obj)
  714. {
  715. Namespace ns;
  716. int i;
  717. // If the specified namespace is null, use the current namespace.
  718. if (namespace_Renamed == null)
  719. {
  720. ns = getCurrentNamespace(interp);
  721. }
  722. else
  723. {
  724. ns = namespace_Renamed;
  725. }
  726. // Append the export pattern list onto objPtr.
  727. for (i = 0; i < ns.numExportPatterns; i++)
  728. {
  729. TclList.append(interp, obj, TclString.newInstance(ns.exportArray[i]));
  730. }
  731. return ;
  732. }
  733. /*
  734. *----------------------------------------------------------------------
  735. *
  736. * Tcl_Import -> importList
  737. *
  738. * Imports all of the commands matching a pattern into the namespace
  739. * specified by namespace (or the current namespace if namespace
  740. * is null). This is done by creating a new command (the "imported
  741. * command") that points to the real command in its original namespace.
  742. *
  743. * If matching commands are on the autoload path but haven't been
  744. * loaded yet, this command forces them to be loaded, then creates
  745. * the links to them.
  746. *
  747. * Results:
  748. * Returns if successful, raises TclException if something goes wrong.
  749. *
  750. * Side effects:
  751. * Creates new commands in the importing namespace. These indirect
  752. * calls back to the real command and are deleted if the real commands
  753. * are deleted.
  754. *
  755. *----------------------------------------------------------------------
  756. */
  757. internal static void importList(Interp interp, Namespace namespace_Renamed, string pattern, bool allowOverwrite)
  758. {
  759. Namespace ns, importNs;
  760. Namespace currNs = getCurrentNamespace(interp);
  761. string simplePattern, cmdName;
  762. IEnumerator search;
  763. WrappedCommand cmd, realCmd;
  764. ImportRef ref_Renamed;
  765. WrappedCommand autoCmd, importedCmd;
  766. ImportedCmdData data;
  767. bool wasExported;
  768. int i, result;
  769. // If the specified namespace is null, use the current namespace.
  770. if (namespace_Renamed == null)
  771. {
  772. ns = currNs;
  773. }
  774. else
  775. {
  776. ns = namespace_Renamed;
  777. }
  778. // First, invoke the "auto_import" command with the pattern
  779. // being imported. This command is part of the Tcl library.
  780. // It looks for imported commands in autoloaded libraries and
  781. // loads them in. That way, they will be found when we try
  782. // to create links below.
  783. autoCmd = findCommand(interp, "auto_import", null, TCL.VarFlag.GLOBAL_ONLY);
  784. if (autoCmd != null)
  785. {
  786. TclObject[] objv = new TclObject[2];
  787. objv[0] = TclString.newInstance("auto_import");
  788. objv[0].preserve();
  789. objv[1] = TclString.newInstance(pattern);
  790. objv[1].preserve();
  791. cmd = autoCmd;
  792. try
  793. {
  794. // Invoke the command with the arguments
  795. cmd.cmd.cmdProc(interp, objv);
  796. }
  797. finally
  798. {
  799. objv[0].release();
  800. objv[1].release();
  801. }
  802. interp.resetResult();
  803. }
  804. // From the pattern, find the namespace from which we are importing
  805. // and get the simple pattern (no namespace qualifiers or ::'s) at
  806. // the end.
  807. if (pattern.Length == 0)
  808. {
  809. throw new TclException(interp, "empty import pattern");
  810. }
  811. // Java does not support passing an address so we pass
  812. // an array of size 1 and then assign arr[0] to the value
  813. Namespace[] importNsArr = new Namespace[1];
  814. Namespace[] dummyArr = new Namespace[1];
  815. string[] simplePatternArr = new string[1];
  816. getNamespaceForQualName(interp, pattern, ns, TCL.VarFlag.LEAVE_ERR_MSG, importNsArr, dummyArr, dummyArr, simplePatternArr);
  817. importNs = importNsArr[0];
  818. simplePattern = simplePatternArr[0];
  819. if (importNs == null)
  820. {
  821. throw new TclException(interp, "unknown namespace in import pattern \"" + pattern + "\"");
  822. }
  823. if (importNs == ns)
  824. {
  825. if ((System.Object) pattern == (System.Object) simplePattern)
  826. {
  827. throw new TclException(interp, "no namespace specified in import pattern \"" + pattern + "\"");
  828. }
  829. else
  830. {
  831. throw new TclException(interp, "import pattern \"" + pattern + "\" tries to import from namespace \"" + importNs.name + "\" into itself");
  832. }
  833. }
  834. // Scan through the command table in the source namespace and look for
  835. // exported commands that match the string pattern. Create an "imported
  836. // command" in the current namespace for each imported command; these
  837. // commands redirect their invocations to the "real" command.
  838. for (search = importNs.cmdTable.Keys.GetEnumerator(); search.MoveNext(); )
  839. {
  840. cmdName = ((string) search.Current);
  841. if (Util.stringMatch(cmdName, simplePattern))
  842. {
  843. // The command cmdName in the source namespace matches the
  844. // pattern. Check whether it was exported. If it wasn't,
  845. // we ignore it.
  846. wasExported = false;
  847. for (i = 0; i < importNs.numExportPatterns; i++)
  848. {
  849. if (Util.stringMatch(cmdName, importNs.exportArray[i]))
  850. {
  851. wasExported = true;
  852. break;
  853. }
  854. }
  855. if (!wasExported)
  856. {
  857. continue;
  858. }
  859. // Unless there is a name clash, create an imported command
  860. // in the current namespace that refers to cmdPtr.
  861. if ((ns.cmdTable[cmdName] == null) || allowOverwrite)
  862. {
  863. // Create the imported command and its client data.
  864. // To create the new command in the current namespace,
  865. // generate a fully qualified name for it.
  866. System.Text.StringBuilder ds;
  867. ds = new System.Text.StringBuilder();
  868. ds.Append(ns.fullName);
  869. if (ns != interp.globalNs)
  870. {
  871. ds.Append("::");
  872. }
  873. ds.Append(cmdName);
  874. // Check whether creating the new imported command in the
  875. // current namespace would create a cycle of imported->real
  876. // command references that also would destroy an existing
  877. // "real" command already in the current namespace.
  878. cmd = (WrappedCommand) importNs.cmdTable[cmdName];
  879. if (cmd.cmd is ImportedCmdData)
  880. {
  881. // This is actually an imported command, find
  882. // the real command it references
  883. realCmd = getOriginalCommand(cmd);
  884. if ((realCmd != null) && (realCmd.ns == currNs) && (currNs.cmdTable[cmdName] != null))
  885. {
  886. throw new TclException(interp, "import pattern \"" + pattern + "\" would create a loop containing command \"" + ds.ToString() + "\"");
  887. }
  888. }
  889. data = new ImportedCmdData();
  890. // Create the imported command inside the interp
  891. interp.createCommand(ds.ToString(), data);
  892. // Lookup in the namespace for the new WrappedCommand
  893. importedCmd = findCommand(interp, ds.ToString(), ns, (TCL.VarFlag.NAMESPACE_ONLY | TCL.VarFlag.LEAVE_ERR_MSG));
  894. data.realCmd = cmd;
  895. data.self = importedCmd;
  896. // Create an ImportRef structure describing this new import
  897. // command and add it to the import ref list in the "real"
  898. // command.
  899. ref_Renamed = new ImportRef();
  900. ref_Renamed.importedCmd = importedCmd;
  901. ref_Renamed.next = cmd.importRef;
  902. cmd.importRef = ref_Renamed;
  903. }
  904. else
  905. {
  906. throw new TclException(interp, "can't import command \"" + cmdName + "\": already exists");
  907. }
  908. }
  909. }
  910. return ;
  911. }
  912. /*
  913. *----------------------------------------------------------------------
  914. *
  915. * Tcl_ForgetImport -> forgetImport
  916. *
  917. * Deletes previously imported commands. Given a pattern that may
  918. * include the name of an exporting namespace, this procedure first
  919. * finds all matching exported commands. It then looks in the namespace
  920. * specified by namespace for any corresponding previously imported
  921. * commands, which it deletes. If namespace is null, commands are
  922. * deleted from the current namespace.
  923. *
  924. * Results:
  925. * Returns if successful, raises TclException if something goes wrong.
  926. *
  927. * Side effects:
  928. * May delete commands.
  929. *
  930. *----------------------------------------------------------------------
  931. */
  932. internal static void forgetImport(Interp interp, Namespace namespace_Renamed, string pattern)
  933. {
  934. Namespace ns, importNs, actualCtx;
  935. string simplePattern, cmdName;
  936. IEnumerator search;
  937. WrappedCommand cmd;
  938. // If the specified namespace is null, use the current namespace.
  939. if (namespace_Renamed == null)
  940. {
  941. ns = getCurrentNamespace(interp);
  942. }
  943. else
  944. {
  945. ns = namespace_Renamed;
  946. }
  947. // From the pattern, find the namespace from which we are importing
  948. // and get the simple pattern (no namespace qualifiers or ::'s) at
  949. // the end.
  950. // Java does not support passing an address so we pass
  951. // an array of size 1 and then assign arr[0] to the value
  952. Namespace[] importNsArr = new Namespace[1];
  953. Namespace[] dummyArr = new Namespace[1];
  954. Namespace[] actualCtxArr = new Namespace[1];
  955. string[] simplePatternArr = new string[1];
  956. getNamespaceForQualName(interp, pattern, ns, TCL.VarFlag.LEAVE_ERR_MSG, importNsArr, dummyArr, actualCtxArr, simplePatternArr);
  957. // get the values out of the arrays
  958. importNs = importNsArr[0];
  959. actualCtx = actualCtxArr[0];
  960. simplePattern = simplePatternArr[0];
  961. // FIXME : the above call passes TCL.VarFlag.LEAVE_ERR_MSG, but
  962. // it seems like this will be a problem when exception is raised!
  963. if (importNs == null)
  964. {
  965. throw new TclException(interp, "unknown namespace in namespace forget pattern \"" + pattern + "\"");
  966. }
  967. // Scan through the command table in the source namespace and look for
  968. // exported commands that match the string pattern. If the current
  969. // namespace has an imported command that refers to one of those real
  970. // commands, delete it.
  971. for (search = importNs.cmdTable.Keys.GetEnumerator(); search.MoveNext(); )
  972. {
  973. cmdName = ((string) search.Current);
  974. if (Util.stringMatch(cmdName, simplePattern))
  975. {
  976. cmd = (WrappedCommand) ns.cmdTable[cmdName];
  977. if (cmd != null)
  978. {
  979. // cmd of same name in current namespace
  980. if (cmd.cmd is ImportedCmdData)
  981. {
  982. interp.deleteCommandFromToken(cmd);
  983. }
  984. }
  985. }
  986. }
  987. return ;
  988. }
  989. /*
  990. *----------------------------------------------------------------------
  991. *
  992. * TclGetOriginalCommand -> getOriginalCommand
  993. *
  994. * An imported command is created in a namespace when a "real" command
  995. * is imported from another namespace. If the specified command is an
  996. * imported command, this procedure returns the original command it
  997. * refers to.
  998. *
  999. * Results:
  1000. * If the command was imported into a sequence of namespaces a, b,...,n
  1001. * where each successive namespace just imports the command from the
  1002. * previous namespace, this procedure returns the Tcl_Command token in
  1003. * the first namespace, a. Otherwise, if the specified command is not
  1004. * an imported command, the procedure returns null.
  1005. *
  1006. * Side effects:
  1007. * None.
  1008. *
  1009. *----------------------------------------------------------------------
  1010. */
  1011. internal static WrappedCommand getOriginalCommand(WrappedCommand command)
  1012. {
  1013. WrappedCommand cmd = command;
  1014. ImportedCmdData data;
  1015. if (!(cmd.cmd is ImportedCmdData))
  1016. {
  1017. return null;
  1018. }
  1019. while (cmd.cmd is ImportedCmdData)
  1020. {
  1021. data = (ImportedCmdData) cmd.cmd;
  1022. cmd = data.realCmd;
  1023. }
  1024. return cmd;
  1025. }
  1026. /*
  1027. *----------------------------------------------------------------------
  1028. *
  1029. * InvokeImportedCmd -> invokeImportedCmd
  1030. *
  1031. * Invoked by Tcl whenever the user calls an imported command that
  1032. * was created by Tcl_Import. Finds the "real" command (in another
  1033. * namespace), and passes control to it.
  1034. *
  1035. * Results:
  1036. * Returns if successful, raises TclException if something goes wrong.
  1037. *
  1038. * Side effects:
  1039. * Returns a result in the interpreter's result object. If anything
  1040. * goes wrong, the result object is set to an error message.
  1041. *
  1042. *----------------------------------------------------------------------
  1043. */
  1044. internal static void invokeImportedCmd(Interp interp, ImportedCmdData data, TclObject[] objv)
  1045. {
  1046. WrappedCommand realCmd = data.realCmd;
  1047. realCmd.cmd.cmdProc(interp, objv);
  1048. }
  1049. /*
  1050. *----------------------------------------------------------------------
  1051. *
  1052. * DeleteImportedCmd -> deleteImportedCmd
  1053. *
  1054. * Invoked by Tcl whenever an imported command is deleted. The "real"
  1055. * command keeps a list of all the imported commands that refer to it,
  1056. * so those imported commands can be deleted when the real command is
  1057. * deleted. This procedure removes the imported command reference from
  1058. * the real command's list, and frees up the memory associated with
  1059. * the imported command.
  1060. *
  1061. * Results:
  1062. * None.
  1063. *
  1064. * Side effects:
  1065. * Removes the imported command from the real command's import list.
  1066. *
  1067. *----------------------------------------------------------------------
  1068. */
  1069. internal static void deleteImportedCmd(ImportedCmdData data)
  1070. // The data object for this imported command
  1071. {
  1072. WrappedCommand realCmd = data.realCmd;
  1073. WrappedCommand self = data.self;
  1074. ImportRef ref_Renamed, prev;
  1075. prev = null;
  1076. for (ref_Renamed = realCmd.importRef; ref_Renamed != null; ref_Renamed = ref_Renamed.next)
  1077. {
  1078. if (ref_Renamed.importedCmd == self)
  1079. {
  1080. // Remove ref from real command's list of imported commands
  1081. // that refer to it.
  1082. if (prev == null)
  1083. {
  1084. // ref is first in list
  1085. realCmd.importRef = ref_Renamed.next;
  1086. }
  1087. else
  1088. {
  1089. prev.next = ref_Renamed.next;
  1090. }
  1091. ref_Renamed = null;
  1092. data = null;
  1093. return ;
  1094. }
  1095. prev = ref_Renamed;
  1096. }
  1097. throw new TclRuntimeError("DeleteImportedCmd: did not find cmd in real cmd's list of import references");
  1098. }
  1099. /*
  1100. *----------------------------------------------------------------------
  1101. *
  1102. * TclGetNamespaceForQualName -> getNamespaceForQualName
  1103. *
  1104. * Given a qualified name specifying a command, variable, or namespace,
  1105. * and a namespace in which to resolve the name, this procedure returns
  1106. * a pointer to the namespace that contains the item. A qualified name
  1107. * consists of the "simple" name of an item qualified by the names of
  1108. * an arbitrary number of containing namespace separated by "::"s. If
  1109. * the qualified name starts with "::", it is interpreted absolutely
  1110. * from the global namespace. Otherwise, it is interpreted relative to
  1111. * the namespace specified by cxtNsPtr if it is non-null. If cxtNsPtr
  1112. * is null, the name is interpreted relative to the current namespace.
  1113. *
  1114. * A relative name like "foo::bar::x" can be found starting in either
  1115. * the current namespace or in the global namespace. So each search
  1116. * usually follows two tracks, and two possible namespaces are
  1117. * returned. If the procedure sets either nsPtrPtr[0] or altNsPtrPtr[0] to
  1118. * null, then that path failed.
  1119. *
  1120. * If "flags" contains TCL.VarFlag.GLOBAL_ONLY, the relative qualified name is
  1121. * sought only in the global :: namespace. The alternate search
  1122. * (also) starting from the global namespace is ignored and
  1123. * altNsPtrPtr[0] is set null.
  1124. *
  1125. * If "flags" contains TCL.VarFlag.NAMESPACE_ONLY, the relative qualified
  1126. * name is sought only in the namespace specified by cxtNsPtr. The
  1127. * alternate search starting from the global namespace is ignored and
  1128. * altNsPtrPtr[0] is set null. If both TCL.VarFlag.GLOBAL_ONLY and
  1129. * TCL.VarFlag.NAMESPACE_ONLY are specified, TCL.VarFlag.GLOBAL_ONLY is ignored and
  1130. * the search starts from the namespace specified by cxtNsPtr.
  1131. *
  1132. * If "flags" contains TCL.VarFlag.CREATE_NS_IF_UNKNOWN, all namespace
  1133. * components of the qualified name that cannot be found are
  1134. * automatically created within their specified parent. This makes sure
  1135. * that functions like Tcl_CreateCommand always succeed. There is no
  1136. * alternate search path, so altNsPtrPtr[0] is set null.
  1137. *
  1138. * If "flags" contains TCL.VarFlag.FIND_ONLY_NS, the qualified name is treated as a
  1139. * reference to a namespace, and the entire qualified name is
  1140. * followed. If the name is relative, the namespace is looked up only
  1141. * in the current namespace. A pointer to the namespace is stored in
  1142. * nsPtrPtr[0] and null is stored in simpleNamePtr[0]. Otherwise, if
  1143. * TCL.VarFlag.FIND_ONLY_NS is not specified, only the leading components are
  1144. * treated as namespace names, and a pointer to the simple name of the
  1145. * final component is stored in simpleNamePtr[0].
  1146. *
  1147. * Results:
  1148. * It sets nsPtrPtr[0] and altNsPtrPtr[0] to point to the two possible
  1149. * namespaces which represent the last (containing) namespace in the
  1150. * qualified name. If the procedure sets either nsPtrPtr[0] or altNsPtrPtr[0]
  1151. * to null, then the search along that path failed. The procedure also
  1152. * stores a pointer to the simple name of the final component in
  1153. * simpleNamePtr[0]. If the qualified name is "::" or was treated as a
  1154. * namespace reference (TCL.VarFlag.FIND_ONLY_NS), the procedure stores a pointer
  1155. * to the namespace in nsPtrPtr[0], null in altNsPtrPtr[0], and sets
  1156. * simpleNamePtr[0] to an empty string.
  1157. *
  1158. * If there is an error, this procedure returns TCL_ERROR. If "flags"
  1159. * contains TCL_LEAVE_ERR_MSG, an error message is returned in the
  1160. * interpreter's result object. Otherwise, the interpreter's result
  1161. * object is left unchanged.
  1162. *
  1163. * actualCxtPtrPtr[0] is set to the actual context namespace. It is
  1164. * set to the input context namespace pointer in cxtNsPtr. If cxtNsPtr
  1165. * is null, it is set to the current namespace context.
  1166. *
  1167. * Side effects:
  1168. * If "flags" contains TCL.VarFlag.CREATE_NS_IF_UNKNOWN, new namespaces may be
  1169. * created.
  1170. *
  1171. *----------------------------------------------------------------------
  1172. */
  1173. internal static void getNamespaceForQualName(Interp interp, string qualName, Namespace cxtNsPtr, TCL.VarFlag flags, Namespace[] nsPtrPtr, Namespace[] altNsPtrPtr, Namespace[] actualCxtPtrPtr, string[] simpleNamePtr)
  1174. {
  1175. // FIXME : remove extra method call checks when we are sure this works!
  1176. if (true)
  1177. {
  1178. // check invariants
  1179. if ((nsPtrPtr == null) || (nsPtrPtr.Length != 1))
  1180. {
  1181. throw new System.SystemException("nsPtrPtr " + nsPtrPtr);
  1182. }
  1183. if ((altNsPtrPtr == null) || (altNsPtrPtr.Length != 1))
  1184. {
  1185. throw new System.SystemException("altNsPtrPtr " + altNsPtrPtr);
  1186. }
  1187. if ((actualCxtPtrPtr == null) || (actualCxtPtrPtr.Length != 1))
  1188. {
  1189. throw new System.SystemException("actualCxtPtrPtr " + actualCxtPtrPtr);
  1190. }
  1191. if ((simpleNamePtr == null) || (simpleNamePtr.Length != 1))
  1192. {
  1193. throw new System.SystemException("simpleNamePtr " + simpleNamePtr);
  1194. }
  1195. }
  1196. Namespace ns = cxtNsPtr;
  1197. Namespace altNs;
  1198. Namespace globalNs = getGlobalNamespace(interp);
  1199. Namespace entryNs;
  1200. string start, end;
  1201. string nsName;
  1202. int len;
  1203. int start_ind, end_ind, name_len;
  1204. // Determine the context namespace ns in which to start the primary
  1205. // search. If TCL.VarFlag.NAMESPACE_ONLY or TCL.VarFlag.FIND_ONLY_NS was specified, search
  1206. // from the current namespace. If the qualName name starts with a "::"
  1207. // or TCL.VarFlag.GLOBAL_ONLY was specified, search from the global
  1208. // namespace. Otherwise, use the given namespace given in cxtNsPtr, or
  1209. // if that is null, use the current namespace context. Note that we
  1210. // always treat two or more adjacent ":"s as a namespace separator.
  1211. if ((flags & (TCL.VarFlag.NAMESPACE_ONLY | TCL.VarFlag.FIND_ONLY_NS)) != 0)
  1212. {
  1213. ns = getCurrentNamespace(interp);
  1214. }
  1215. else if ((flags & TCL.VarFlag.GLOBAL_ONLY) != 0)
  1216. {
  1217. ns = globalNs;
  1218. }
  1219. else if (ns == null)
  1220. {
  1221. if (interp.varFrame != null)
  1222. {
  1223. ns = interp.varFrame.ns;
  1224. }
  1225. else
  1226. {
  1227. ns = interp.globalNs;
  1228. }
  1229. }
  1230. start_ind = 0;
  1231. name_len = qualName.Length;
  1232. if ((name_len >= 2) && (qualName[0] == ':') && (qualName[1] == ':'))
  1233. {
  1234. start_ind = 2; // skip over the initial ::
  1235. while ((start_ind < name_len) && (qualName[start_ind] == ':'))
  1236. {
  1237. start_ind++; // skip over a subsequent :
  1238. }
  1239. ns = globalNs;
  1240. if (start_ind >= name_len)
  1241. {
  1242. // qualName is just two or more ":"s
  1243. nsPtrPtr[0] = globalNs;
  1244. altNsPtrPtr[0] = null;
  1245. actualCxtPtrPtr[0] = globalNs;
  1246. simpleNamePtr[0] = ""; // points to empty string
  1247. return ;
  1248. }
  1249. }
  1250. actualCxtPtrPtr[0] = ns;
  1251. // Start an alternate search path starting with the global namespace.
  1252. // However, if the starting context is the global namespace, or if the
  1253. // flag is set to search only the namespace cxtNs, ignore the
  1254. // alternate search path.
  1255. altNs = globalNs;
  1256. if ((ns == globalNs) || ((flags & (TCL.VarFlag.NAMESPACE_ONLY | TCL.VarFlag.FIND_ONLY_NS)) != 0))
  1257. {
  1258. altNs = null;
  1259. }
  1260. // Loop to resolve each namespace qualifier in qualName.
  1261. end_ind = start_ind;
  1262. while (start_ind < name_len)
  1263. {
  1264. // Find the next namespace qualifier (i.e., a name ending in "::")
  1265. // or the end of the qualified name (i.e., a name ending in "\0").
  1266. // Set len to the number of characters, starting from start,
  1267. // in the name; set end to point after the "::"s or at the "\0".
  1268. len = 0;
  1269. for (end_ind = start_ind; end_ind < name_len; end_ind++)
  1270. {
  1271. if (((name_len - end_ind) > 1) && (qualName[end_ind] == ':') && (qualName[end_ind + 1] == ':'))
  1272. {
  1273. end_ind += 2; // skip over the initial ::
  1274. while ((end_ind < name_len) && (qualName[end_ind] == ':'))
  1275. {
  1276. end_ind++; // skip over a subsequent :
  1277. }
  1278. break;
  1279. }
  1280. len++;
  1281. }
  1282. if ((end_ind == name_len) && !((end_ind - start_ind >= 2) && ((qualName[end_ind - 1] == ':') && (qualName[end_ind - 2] == ':'))))
  1283. {
  1284. // qualName ended with a simple name at start. If TCL.VarFlag.FIND_ONLY_NS
  1285. // was specified, look this up as a namespace. Otherwise,
  1286. // start is the name of a cmd or var and we are done.
  1287. if ((flags & TCL.VarFlag.FIND_ONLY_NS) != 0)
  1288. {
  1289. // assign the string from start_ind to the end of the name string
  1290. nsName = qualName.Substring(start_ind);
  1291. }
  1292. else
  1293. {
  1294. nsPtrPtr[0] = ns;
  1295. altNsPtrPtr[0] = altNs;
  1296. simpleNamePtr[0] = qualName.Substring(start_ind);
  1297. return ;
  1298. }
  1299. }
  1300. else
  1301. {
  1302. // start points to the beginning of a namespace qualifier ending
  1303. // in "::". Create new string with the namespace qualifier.
  1304. nsName = qualName.Substring(start_ind, (start_ind + len) - (start_ind));
  1305. }
  1306. // Look up the namespace qualifier nsName in the current namespace
  1307. // context. If it isn't found but TCL.VarFlag.CREATE_NS_IF_UNKNOWN is set,
  1308. // create that qualifying namespace. This is needed for procedures
  1309. // like Tcl_CreateCommand that cannot fail.
  1310. if (ns != null)
  1311. {
  1312. entryNs = (Namespace) ns.childTable[nsName];
  1313. if (entryNs != null)
  1314. {
  1315. ns = entryNs;
  1316. }
  1317. else if ((flags & TCL.VarFlag.CREATE_NS_IF_UNKNOWN) != 0)
  1318. {
  1319. CallFrame frame = interp.newCallFrame();
  1320. pushCallFrame(interp, frame, ns, false);
  1321. ns = createNamespace(interp, nsName, null);
  1322. popCallFrame(interp);
  1323. if (ns == null)
  1324. {
  1325. throw new System.SystemException("Could not create namespace " + nsName);
  1326. }
  1327. }
  1328. else
  1329. {
  1330. ns = null; // namespace not found and wasn't created
  1331. }
  1332. }
  1333. // Look up the namespace qualifier in the alternate search path too.
  1334. if (altNs != null)
  1335. {
  1336. altNs = (Namespace) altNs.childTable[nsName];
  1337. }
  1338. // If both search paths have failed, return null results.
  1339. if ((ns == null) && (altNs == null))
  1340. {
  1341. nsPtrPtr[0] = null;
  1342. altNsPtrPtr[0] = null;
  1343. simpleNamePtr[0] = null;
  1344. return ;
  1345. }
  1346. start_ind = end_ind;
  1347. }
  1348. // We ignore trailing "::"s in a namespace name, but in a command or
  1349. // variable name, trailing "::"s refer to the cmd or var named {}.
  1350. if (((flags & TCL.VarFlag.FIND_ONLY_NS) != 0) || ((end_ind > start_ind) && (qualName[end_ind - 1] != ':')))
  1351. {
  1352. simpleNamePtr[0] = null; // found namespace name
  1353. }
  1354. else
  1355. {
  1356. // FIXME : make sure this does not throw exception when end_ind is at the end of the string
  1357. simpleNamePtr[0] = qualName.Substring(end_ind); // found cmd/var: points to empty string
  1358. }
  1359. // As a special case, if we are looking for a namespace and qualName
  1360. // is "" and the current active namespace (ns) is not the global
  1361. // namespace, return null (no namespace was found). This is because
  1362. // namespaces can not have empty names except for the global namespace.
  1363. if (((flags & TCL.VarFlag.FIND_ONLY_NS) != 0) && (name_len == 0) && (ns != globalNs))
  1364. {
  1365. ns = null;
  1366. }
  1367. nsPtrPtr[0] = ns;
  1368. altNsPtrPtr[0] = altNs;
  1369. return ;
  1370. }
  1371. /*
  1372. *----------------------------------------------------------------------
  1373. *
  1374. * Tcl_FindNamespace -> findNamespace
  1375. *
  1376. * Searches for a namespace.
  1377. *
  1378. * Results:T
  1379. * Returns a reference to the namespace if it is found. Otherwise,
  1380. * returns null and leaves an error message in the interpreter's
  1381. * result object if "flags" contains TCL.VarFlag.LEAVE_ERR_MSG.
  1382. *
  1383. * Side effects:
  1384. * None.
  1385. *
  1386. *----------------------------------------------------------------------
  1387. */
  1388. internal static Namespace findNamespace(Interp interp, string name, Namespace contextNs, TCL.VarFlag flags)
  1389. {
  1390. Namespace ns;
  1391. // Java does not support passing an address so we pass
  1392. // an array of size 1 and then assign arr[0] to the value
  1393. Namespace[] nsArr = new Namespace[1];
  1394. Namespace[] dummy1Arr = new Namespace[1];
  1395. string[] dummy2Arr = new string[1];
  1396. // Find the namespace(s) that contain the specified namespace name.
  1397. // Add the TCL.VarFlag.FIND_ONLY_NS flag to resolve the name all the way down
  1398. // to its last component, a namespace.
  1399. getNamespaceForQualName(interp, name, contextNs, (flags | TCL.VarFlag.FIND_ONLY_NS), nsArr, dummy1Arr, dummy1Arr, dummy2Arr);
  1400. // Get the values out of the arrays!
  1401. ns = nsArr[0];
  1402. if (ns != null)
  1403. {
  1404. return ns;
  1405. }
  1406. else if ((flags & TCL.VarFlag.LEAVE_ERR_MSG) != 0)
  1407. {
  1408. /*
  1409. interp.resetResult();
  1410. TclString.append(interp.getResult(), "unknown namespace \"" + name + "\"");
  1411. */
  1412. // FIXME : is there a test case for this error?
  1413. interp.setResult("unknown namespace \"" + name + "\"");
  1414. }
  1415. return null;
  1416. }
  1417. /*
  1418. *----------------------------------------------------------------------
  1419. *
  1420. * Tcl_FindCommand -> findCommand
  1421. *
  1422. * Searches for a command.
  1423. *
  1424. * Results:
  1425. * Returns a token for the command if it is found. Otherwise, if it
  1426. * can't be found or there is an error, returns null and leaves an
  1427. * error message in the interpreter's result object if "flags"
  1428. * contains TCL.VarFlag.LEAVE_ERR_MSG.
  1429. *
  1430. * Side effects:
  1431. * None.
  1432. *
  1433. *----------------------------------------------------------------------
  1434. */
  1435. internal static WrappedCommand findCommand(Interp interp, string name, Namespace contextNs, TCL.VarFlag flags)
  1436. {
  1437. Interp.ResolverScheme res;
  1438. Namespace cxtNs;
  1439. Namespace[] ns = new Namespace[2];
  1440. string simpleName;
  1441. int search;
  1442. //int result;
  1443. WrappedCommand cmd;
  1444. // If this namespace has a command resolver, then give it first
  1445. // crack at the command resolution. If the interpreter has any
  1446. // command resolvers, consult them next. The command resolver
  1447. // procedures may return a Tcl_Command value, they may signal
  1448. // to continue onward, or they may signal an error.
  1449. if ((flags & TCL.VarFlag.GLOBAL_ONLY) != 0)
  1450. {
  1451. cxtNs = getGlobalNamespace(interp);
  1452. }
  1453. else if (contextNs != null)
  1454. {
  1455. cxtNs = contextNs;
  1456. }
  1457. else
  1458. {
  1459. cxtNs = getCurrentNamespace(interp);
  1460. }
  1461. if (cxtNs.resolver != null || interp.resolvers != null)
  1462. {
  1463. try
  1464. {
  1465. if (cxtNs.resolver != null)
  1466. {
  1467. cmd = cxtNs.resolver.resolveCmd(interp, name, cxtNs, flags);
  1468. }
  1469. else
  1470. {
  1471. cmd = null;
  1472. }
  1473. if (cmd == null && interp.resolvers != null)
  1474. {
  1475. IEnumerator enum_Renamed = interp.resolvers.GetEnumerator();
  1476. while (cmd == null && enum_Renamed.MoveNext())
  1477. {
  1478. res = (Interp.ResolverScheme) enum_Renamed.Current;
  1479. cmd = res.resolver.resolveCmd(interp, name, cxtNs, flags);
  1480. }
  1481. }
  1482. if (cmd != null)
  1483. {
  1484. return cmd;
  1485. }
  1486. }
  1487. catch (TclException e)
  1488. {
  1489. return null;
  1490. }
  1491. }
  1492. // Java does not support passing an address so we pass
  1493. // an array of size 1 and then assign arr[0] to the value
  1494. Namespace[] ns0Arr = new Namespace[1];
  1495. Namespace[] ns1Arr = new Namespace[1];
  1496. Namespace[] cxtNsArr = new Namespace[1];
  1497. string[] simpleNameArr = new string[1];
  1498. // Find the namespace(s) that contain the command.
  1499. getNamespaceForQualName(interp, name, contextNs, flags, ns0Arr, ns1Arr, cxtNsArr, simpleNameArr);
  1500. // Get the values out of the arrays!
  1501. ns[0] = ns0Arr[0];
  1502. ns[1] = ns1Arr[0];
  1503. cxtNs = cxtNsArr[0];
  1504. simpleName = simpleNameArr[0];
  1505. // Look for the command in the command table of its namespace.
  1506. // Be sure to check both possible search paths: from the specified
  1507. // namespace context and from the global namespace.
  1508. cmd = null;
  1509. for (search = 0; (search < 2) && (cmd == null); search++)
  1510. {
  1511. if ((ns[search] != null) && ((System.Object) simpleName != null))
  1512. {
  1513. cmd = (WrappedCommand) ns[search].cmdTable[simpleName];
  1514. }
  1515. }
  1516. if (cmd != null)
  1517. {
  1518. return cmd;
  1519. }
  1520. else if ((flags & TCL.VarFlag.LEAVE_ERR_MSG) != 0)
  1521. {
  1522. throw new TclException(interp, "unknown command \"" + name + "\"");
  1523. }
  1524. return null;
  1525. }
  1526. /*
  1527. *----------------------------------------------------------------------
  1528. *
  1529. * Tcl_FindNamespaceVar -> findNamespaceVar
  1530. *
  1531. * Searches for a namespace variable, a variable not local to a
  1532. * procedure. The variable can be either a scalar or an array, but
  1533. * may not be an element of an array.
  1534. *
  1535. * Results:
  1536. * Returns a token for the variable if it is found. Otherwise, if it
  1537. * can't be found or there is an error, returns null and leaves an
  1538. * error message in the interpreter's result object if "flags"
  1539. * contains TCL.VarFlag.LEAVE_ERR_MSG.
  1540. *
  1541. * Side effects:
  1542. * None.
  1543. *
  1544. *----------------------------------------------------------------------
  1545. */
  1546. internal static Var findNamespaceVar(Interp interp, string name, Namespace contextNs,TCL.VarFlag flags)
  1547. {
  1548. Interp.ResolverScheme res;
  1549. Namespace cxtNs;
  1550. Namespace[] ns = new Namespace[2];
  1551. string simpleName;
  1552. int search;
  1553. //int result;
  1554. Var var;
  1555. // If this namespace has a variable resolver, then give it first
  1556. // crack at the variable resolution. It may return a Tcl_Var
  1557. // value, it may signal to continue onward, or it may signal
  1558. // an error.
  1559. if ((flags & TCL.VarFlag.GLOBAL_ONLY) != 0)
  1560. {
  1561. cxtNs = getGlobalNamespace(interp);
  1562. }
  1563. else if (contextNs != null)
  1564. {
  1565. cxtNs = contextNs;
  1566. }
  1567. else
  1568. {
  1569. cxtNs = getCurrentNamespace(interp);
  1570. }
  1571. if (cxtNs.resolver != null || interp.resolvers != null)
  1572. {
  1573. try
  1574. {
  1575. if (cxtNs.resolver != null)
  1576. {
  1577. var = cxtNs.resolver.resolveVar(interp, name, cxtNs, flags);
  1578. }
  1579. else
  1580. {
  1581. var = null;
  1582. }
  1583. if (var == null && interp.resolvers != null)
  1584. {
  1585. IEnumerator enum_Renamed = interp.resolvers.GetEnumerator();
  1586. while (var == null && enum_Renamed.MoveNext())
  1587. {
  1588. res = (Interp.ResolverScheme) enum_Renamed.Current;
  1589. var = res.resolver.resolveVar(interp, name, cxtNs, flags);
  1590. }
  1591. }
  1592. if (var != null)
  1593. {
  1594. return var;
  1595. }
  1596. }
  1597. catch (TclException e)
  1598. {
  1599. return null;
  1600. }
  1601. }
  1602. // Java does not support passing an address so we pass
  1603. // an array of size 1 and then assign arr[0] to the value
  1604. Namespace[] ns0Arr = new Namespace[1];
  1605. Namespace[] ns1Arr = new Namespace[1];
  1606. Namespace[] cxtNsArr = new Namespace[1];
  1607. string[] simpleNameArr = new string[1];
  1608. // Find the namespace(s) that contain the variable.
  1609. getNamespaceForQualName(interp, name, contextNs, flags, ns0Arr, ns1Arr, cxtNsArr, simpleNameArr);
  1610. // Get the values out of the arrays!
  1611. ns[0] = ns0Arr[0];
  1612. ns[1] = ns1Arr[0];
  1613. cxtNs = cxtNsArr[0];
  1614. simpleName = simpleNameArr[0];
  1615. // Look for the variable in the variable table of its namespace.
  1616. // Be sure to check both possible search paths: from the specified
  1617. // namespace context and from the global namespace.
  1618. var = null;
  1619. for (search = 0; (search < 2) && (var == null); search++)
  1620. {
  1621. if ((ns[search] != null) && ((System.Object) simpleName != null))
  1622. {
  1623. var = (Var) ns[search].varTable[simpleName];
  1624. }
  1625. }
  1626. if (var != null)
  1627. {
  1628. return var;
  1629. }
  1630. else if ((flags & TCL.VarFlag.LEAVE_ERR_MSG) != 0)
  1631. {
  1632. /*
  1633. interp.resetResult();
  1634. TclString.append(interp.getResult(), "unknown variable \"" + name + "\"");
  1635. */
  1636. // FIXME : is there a test case for this error?
  1637. interp.setResult("unknown variable \"" + name + "\"");
  1638. }
  1639. return null;
  1640. }
  1641. /*
  1642. *----------------------------------------------------------------------
  1643. *
  1644. * GetNamespaceFromObj -> getNamespaceFromObj
  1645. *
  1646. * Returns the namespace specified by the name in a TclObject.
  1647. *
  1648. * Results:
  1649. * This method will return the Namespace object whose name
  1650. * is stored in the obj argument. If the namespace can't be found,
  1651. * a TclException is raised.
  1652. *
  1653. * Side effects:
  1654. * May update the internal representation for the object, caching the
  1655. * namespace reference. The next time this procedure is called, the
  1656. * namespace value can be found quickly.
  1657. *
  1658. * If anything goes wrong, an error message is left in the
  1659. * interpreter's result object.
  1660. *
  1661. *----------------------------------------------------------------------
  1662. */
  1663. internal static Namespace getNamespaceFromObj(Interp interp, TclObject obj)
  1664. {
  1665. ResolvedNsName resName;
  1666. Namespace ns;
  1667. Namespace currNs = getCurrentNamespace(interp);
  1668. int result;
  1669. // Get the internal representation, converting to a namespace type if
  1670. // needed. The internal representation is a ResolvedNsName that points
  1671. // to the actual namespace.
  1672. // FIXME : if NamespaceCmd is not the internal rep this needs to be changed!
  1673. if (!(obj.InternalRep is NamespaceCmd))
  1674. {
  1675. setNsNameFromAny(interp, obj);
  1676. }
  1677. resName = ((NamespaceCmd) obj.InternalRep).otherValue;
  1678. // Check the context namespace of the resolved symbol to make sure that
  1679. // it is fresh. If not, then force another conversion to the namespace
  1680. // type, to discard the old rep and create a new one. Note that we
  1681. // verify that the namespace id of the cached namespace is the same as
  1682. // the id when we cached it; this insures that the namespace wasn't
  1683. // deleted and a new one created at the same address.
  1684. ns = null;
  1685. if ((resName != null) && (resName.refNs == currNs) && (resName.nsId == resName.ns.nsId))
  1686. {
  1687. ns = resName.ns;
  1688. if ((ns.flags & NS_DEAD) != 0)
  1689. {
  1690. ns = null;
  1691. }
  1692. }
  1693. if (ns == null)
  1694. {
  1695. // try again
  1696. setNsNameFromAny(interp, obj);
  1697. resName = ((NamespaceCmd) obj.InternalRep).otherValue;
  1698. if (resName != null)
  1699. {
  1700. ns = resName.ns;
  1701. if ((ns.flags & NS_DEAD) != 0)
  1702. {
  1703. ns = null;
  1704. }
  1705. }
  1706. }
  1707. return ns;
  1708. }
  1709. /// <summary>----------------------------------------------------------------------
  1710. ///
  1711. /// Tcl_SetNamespaceResolvers -> setNamespaceResolver
  1712. ///
  1713. /// Sets the command/variable resolution object for a namespace,
  1714. /// thereby changing the way that command/variable names are
  1715. /// interpreted. This allows extension writers to support different
  1716. /// name resolution schemes, such as those for object-oriented
  1717. /// packages.
  1718. ///
  1719. /// Command resolution is handled by the following method:
  1720. ///
  1721. /// resolveCmd (Interp interp, String name,
  1722. /// NamespaceCmd.Namespace context, int flags)
  1723. /// throws TclException;
  1724. ///
  1725. /// Whenever a command is executed or NamespaceCmd.findCommand is invoked
  1726. /// within the namespace, this method is called to resolve the
  1727. /// command name. If this method is able to resolve the name,
  1728. /// it should return the corresponding WrappedCommand. Otherwise,
  1729. /// the procedure can return null, and the command will
  1730. /// be treated under the usual name resolution rules. Or, it can
  1731. /// throw a TclException, and the command will be considered invalid.
  1732. ///
  1733. /// Variable resolution is handled by the following method:
  1734. ///
  1735. /// resolveVar (Interp interp, String name,
  1736. /// NamespaceCmd.Namespace context, int flags)
  1737. /// throws TclException;
  1738. ///
  1739. /// If this method is able to resolve the name, it should return
  1740. /// the variable as Var object. The method may also
  1741. /// return null, and the variable will be treated under the usual
  1742. /// name resolution rules. Or, it can throw a TclException,
  1743. /// and the variable will be considered invalid.
  1744. ///
  1745. /// Results:
  1746. /// See above.
  1747. ///
  1748. /// Side effects:
  1749. /// None.
  1750. ///
  1751. /// ----------------------------------------------------------------------
  1752. /// </summary>
  1753. internal static void setNamespaceResolver(Namespace namespace_Renamed, Resolver resolver)
  1754. // command and variable resolution
  1755. {
  1756. // Plug in the new command resolver.
  1757. namespace_Renamed.resolver = resolver;
  1758. }
  1759. /// <summary>----------------------------------------------------------------------
  1760. ///
  1761. /// Tcl_GetNamespaceResolvers -> getNamespaceResolver
  1762. ///
  1763. /// Returns the current command/variable resolution object
  1764. /// for a namespace. By default, these objects are null.
  1765. /// New objects can be installed by calling setNamespaceResolver,
  1766. /// to provide new name resolution rules.
  1767. ///
  1768. /// Results:
  1769. /// Returns the esolver object assigned to this namespace.
  1770. /// Returns null otherwise.
  1771. ///
  1772. /// Side effects:
  1773. /// None.
  1774. ///
  1775. /// ----------------------------------------------------------------------
  1776. /// </summary>
  1777. internal static Resolver getNamespaceResolver(Namespace namespace_Renamed)
  1778. // Namespace whose resolution rules
  1779. // are being queried.
  1780. {
  1781. return namespace_Renamed.resolver;
  1782. }
  1783. /*
  1784. *----------------------------------------------------------------------
  1785. *
  1786. * Tcl_NamespaceObjCmd -> cmdProc
  1787. *
  1788. * Invoked to implement the "namespace" command that creates, deletes,
  1789. * or manipulates Tcl namespaces. Handles the following syntax:
  1790. *
  1791. * namespace children ?name? ?pattern?
  1792. * namespace code arg
  1793. * namespace current
  1794. * namespace delete ?name name...?
  1795. * namespace eval name arg ?arg...?
  1796. * namespace export ?-clear? ?pattern pattern...?
  1797. * namespace forget ?pattern pattern...?
  1798. * namespace import ?-force? ?pattern pattern...?
  1799. * namespace inscope name arg ?arg...?
  1800. * namespace origin name
  1801. * namespace parent ?name?
  1802. * namespace qualifiers string
  1803. * namespace tail string
  1804. * namespace which ?-command? ?-variable? name
  1805. *
  1806. * Results:
  1807. * Returns if the command is successful. Raises Exception if
  1808. * anything goes wrong.
  1809. *
  1810. * Side effects:
  1811. * Based on the subcommand name (e.g., "import"), this procedure
  1812. * dispatches to a corresponding member commands in this class.
  1813. * This method's side effects depend on whatever that subcommand does.
  1814. *----------------------------------------------------------------------
  1815. */
  1816. private static readonly string[] validCmds = new string[]{"children", "code", "current", "delete", "eval", "export", "forget", "import", "inscope", "origin", "parent", "qualifiers", "tail", "which"};
  1817. private const int OPT_CHILDREN = 0;
  1818. private const int OPT_CODE = 1;
  1819. private const int OPT_CURRENT = 2;
  1820. private const int OPT_DELETE = 3;
  1821. private const int OPT_EVAL = 4;
  1822. private const int OPT_EXPORT = 5;
  1823. private const int OPT_FORGET = 6;
  1824. private const int OPT_IMPORT = 7;
  1825. private const int OPT_INSCOPE = 8;
  1826. private const int OPT_ORIGIN = 9;
  1827. private const int OPT_PARENT = 10;
  1828. private const int OPT_QUALIFIERS = 11;
  1829. private const int OPT_TAIL = 12;
  1830. private const int OPT_WHICH = 13;
  1831. public TCL.CompletionCode cmdProc(Interp interp, TclObject[] objv)
  1832. {
  1833. int i, opt;
  1834. if (objv.Length < 2)
  1835. {
  1836. throw new TclNumArgsException(interp, 1, objv, "subcommand ?arg ...?");
  1837. }
  1838. opt = TclIndex.get(interp, objv[1], validCmds, "option", 0);
  1839. switch (opt)
  1840. {
  1841. case OPT_CHILDREN: {
  1842. childrenCmd(interp, objv);
  1843. return TCL.CompletionCode.RETURN;
  1844. }
  1845. case OPT_CODE: {
  1846. codeCmd(interp, objv);
  1847. return TCL.CompletionCode.RETURN;
  1848. }
  1849. case OPT_CURRENT: {
  1850. currentCmd(interp, objv);
  1851. return TCL.CompletionCode.RETURN;
  1852. }
  1853. case OPT_DELETE: {
  1854. deleteCmd(interp, objv);
  1855. return TCL.CompletionCode.RETURN;
  1856. }
  1857. case OPT_EVAL: {
  1858. evalCmd(interp, objv);
  1859. return TCL.CompletionCode.RETURN;
  1860. }
  1861. case OPT_EXPORT: {
  1862. exportCmd(interp, objv);
  1863. return TCL.CompletionCode.RETURN;
  1864. }
  1865. case OPT_FORGET: {
  1866. forgetCmd(interp, objv);
  1867. return TCL.CompletionCode.RETURN;
  1868. }
  1869. case OPT_IMPORT: {
  1870. importCmd(interp, objv);
  1871. return TCL.CompletionCode.RETURN;
  1872. }
  1873. case OPT_INSCOPE: {
  1874. inscopeCmd(interp, objv);
  1875. return TCL.CompletionCode.RETURN;
  1876. }
  1877. case OPT_ORIGIN: {
  1878. originCmd(interp, objv);
  1879. return TCL.CompletionCode.RETURN;
  1880. }
  1881. case OPT_PARENT: {
  1882. parentCmd(interp, objv);
  1883. return TCL.CompletionCode.RETURN;
  1884. }
  1885. case OPT_QUALIFIERS: {
  1886. qualifiersCmd(interp, objv);
  1887. return TCL.CompletionCode.RETURN;
  1888. }
  1889. case OPT_TAIL: {
  1890. tailCmd(interp, objv);
  1891. return TCL.CompletionCode.RETURN;
  1892. }
  1893. case OPT_WHICH: {
  1894. whichCmd(interp, objv);
  1895. return TCL.CompletionCode.RETURN;
  1896. }
  1897. } // end switch(opt)
  1898. return TCL.CompletionCode.RETURN;
  1899. }
  1900. /*
  1901. *----------------------------------------------------------------------
  1902. *
  1903. * NamespaceChildrenCmd -> childrenCmd
  1904. *
  1905. * Invoked to implement the "namespace children" command that returns a
  1906. * list containing the fully-qualified names of the child namespaces of
  1907. * a given namespace. Handles the following syntax:
  1908. *
  1909. * namespace children ?name? ?pattern?
  1910. *
  1911. * Results:
  1912. * Nothing.
  1913. *
  1914. * Side effects:
  1915. * Returns a result in the interpreter's result object. If anything
  1916. * goes wrong, the result is an error message.
  1917. *
  1918. *----------------------------------------------------------------------
  1919. */
  1920. private static void childrenCmd(Interp interp, TclObject[] objv)
  1921. {
  1922. Namespace namespace_Renamed;
  1923. Namespace ns;
  1924. Namespace globalNs = getGlobalNamespace(interp);
  1925. string pattern = null;
  1926. System.Text.StringBuilder buffer;
  1927. IEnumerator search;
  1928. TclObject list, elem;
  1929. // Get a pointer to the specified namespace, or the current namespace.
  1930. if (objv.Length == 2)
  1931. {
  1932. ns = getCurrentNamespace(interp);
  1933. }
  1934. else if ((objv.Length == 3) || (objv.Length == 4))
  1935. {
  1936. ns = getNamespaceFromObj(interp, objv[2]);
  1937. if (ns == null)
  1938. {
  1939. throw new TclException(interp, "unknown namespace \"" + objv[2].ToString() + "\" in namespace children command");
  1940. }
  1941. }
  1942. else
  1943. {
  1944. throw new TclNumArgsException(interp, 2, objv, "?name? ?pattern?");
  1945. }
  1946. // Get the glob-style pattern, if any, used to narrow the search.
  1947. buffer = new System.Text.StringBuilder();
  1948. if (objv.Length == 4)
  1949. {
  1950. string name = objv[3].ToString();
  1951. if (name.StartsWith("::"))
  1952. {
  1953. pattern = name;
  1954. }
  1955. else
  1956. {
  1957. buffer.Append(ns.fullName);
  1958. if (ns != globalNs)
  1959. {
  1960. buffer.Append("::");
  1961. }
  1962. buffer.Append(name);
  1963. pattern = buffer.ToString();
  1964. }
  1965. }
  1966. // Create a list containing the full names of all child namespaces
  1967. // whose names match the specified pattern, if any.
  1968. list = TclList.newInstance();
  1969. foreach (Namespace childNs in ns.childTable.Values) {
  1970. if (((System.Object) pattern == null) || Util.stringMatch(childNs.fullName, pattern))
  1971. {
  1972. elem = TclString.newInstance(childNs.fullName);
  1973. TclList.append(interp, list, elem);
  1974. }
  1975. }
  1976. interp.setResult(list);
  1977. return ;
  1978. }
  1979. /*
  1980. *----------------------------------------------------------------------
  1981. *
  1982. * NamespaceCodeCmd -> codeCmd
  1983. *
  1984. * Invoked to implement the "namespace code" command to capture the
  1985. * namespace context of a command. Handles the following syntax:
  1986. *
  1987. * namespace code arg
  1988. *
  1989. * Here "arg" can be a list. "namespace code arg" produces a result
  1990. * equivalent to that produced by the command
  1991. *
  1992. * list namespace inscope [namespace current] $arg
  1993. *
  1994. * However, if "arg" is itself a scoped value starting with
  1995. * "namespace inscope", then the result is just "arg".
  1996. *
  1997. * Results:
  1998. * Nothing.
  1999. *
  2000. * Side effects:
  2001. * If anything goes wrong, this procedure returns an error
  2002. * message as the result in the interpreter's result object.
  2003. *
  2004. *----------------------------------------------------------------------
  2005. */
  2006. private static void codeCmd(Interp interp, TclObject[] objv)
  2007. {
  2008. Namespace currNs;
  2009. TclObject list, obj;
  2010. string arg, p;
  2011. int length;
  2012. int p_ind;
  2013. if (objv.Length != 3)
  2014. {
  2015. throw new TclNumArgsException(interp, 2, objv, "arg");
  2016. }
  2017. // If "arg" is already a scoped value, then return it directly.
  2018. arg = objv[2].ToString();
  2019. length = arg.Length;
  2020. // FIXME : we need a test for this inscope code if there is not one already!
  2021. if ((length > 17) && (arg[0] == 'n') && arg.StartsWith("namespace"))
  2022. {
  2023. for (p_ind = 9; (p_ind < length) && (arg[p_ind] == ' '); p_ind++)
  2024. {
  2025. // empty body: skip over spaces
  2026. }
  2027. if (((length - p_ind) >= 7) && (arg[p_ind] == 'i') && arg.Substring(p_ind).StartsWith("inscope"))
  2028. {
  2029. interp.setResult(objv[2]);
  2030. return ;
  2031. }
  2032. }
  2033. // Otherwise, construct a scoped command by building a list with
  2034. // "namespace inscope", the full name of the current namespace, and
  2035. // the argument "arg". By constructing a list, we ensure that scoped
  2036. // commands are interpreted properly when they are executed later,
  2037. // by the "namespace inscope" command.
  2038. list = TclList.newInstance();
  2039. TclList.append(interp, list, TclString.newInstance("namespace"));
  2040. TclList.append(interp, list, TclString.newInstance("inscope"));
  2041. currNs = getCurrentNamespace(interp);
  2042. if (currNs == getGlobalNamespace(interp))
  2043. {
  2044. obj = TclString.newInstance("::");
  2045. }
  2046. else
  2047. {
  2048. obj = TclString.newInstance(currNs.fullName);
  2049. }
  2050. TclList.append(interp, list, obj);
  2051. TclList.append(interp, list, objv[2]);
  2052. interp.setResult(list);
  2053. return ;
  2054. }
  2055. /*
  2056. *----------------------------------------------------------------------
  2057. *
  2058. * NamespaceCurrentCmd -> currentCmd
  2059. *
  2060. * Invoked to implement the "namespace current" command which returns
  2061. * the fully-qualified name of the current namespace. Handles the
  2062. * following syntax:
  2063. *
  2064. * namespace current
  2065. *
  2066. * Results:
  2067. * Returns if successful, raises TclException if something goes wrong.
  2068. *
  2069. * Side effects:
  2070. * Returns a result in the interpreter's result object. If anything
  2071. * goes wrong, the result is an error message.
  2072. *
  2073. *----------------------------------------------------------------------
  2074. */
  2075. private static void currentCmd(Interp interp, TclObject[] objv)
  2076. {
  2077. Namespace currNs;
  2078. if (objv.Length != 2)
  2079. {
  2080. throw new TclNumArgsException(interp, 2, objv, null);
  2081. }
  2082. // The "real" name of the global namespace ("::") is the null string,
  2083. // but we return "::" for it as a convenience to programmers. Note that
  2084. // "" and "::" are treated as synonyms by the namespace code so that it
  2085. // is still easy to do things like:
  2086. //
  2087. // namespace [namespace current]::bar { ... }
  2088. currNs = getCurrentNamespace(interp);
  2089. if (currNs == getGlobalNamespace(interp))
  2090. {
  2091. // FIXME : appending to te result really screws everything up!
  2092. // need to figure out how to disallow this!
  2093. //TclString.append(interp.getResult(), "::");
  2094. interp.setResult("::");
  2095. }
  2096. else
  2097. {
  2098. //TclString.append(interp.getResult(), currNs.fullName);
  2099. interp.setResult(currNs.fullName);
  2100. }
  2101. }
  2102. /*
  2103. *----------------------------------------------------------------------
  2104. *
  2105. * NamespaceDeleteCmd -> deleteCmd
  2106. *
  2107. * Invoked to implement the "namespace delete" command to delete
  2108. * namespace(s). Handles the following syntax:
  2109. *
  2110. * namespace delete ?name name...?
  2111. *
  2112. * Each name identifies a namespace. It may include a sequence of
  2113. * namespace qualifiers separated by "::"s. If a namespace is found, it
  2114. * is deleted: all variables and procedures contained in that namespace
  2115. * are deleted. If that namespace is being used on the call stack, it
  2116. * is kept alive (but logically deleted) until it is removed from the
  2117. * call stack: that is, it can no longer be referenced by name but any
  2118. * currently executing procedure that refers to it is allowed to do so
  2119. * until the procedure returns. If the namespace can't be found, this
  2120. * procedure returns an error. If no namespaces are specified, this
  2121. * command does nothing.
  2122. *
  2123. * Results:
  2124. * Returns if successful, raises TclException if something goes wrong.
  2125. *
  2126. * Side effects:
  2127. * Deletes the specified namespaces. If anything goes wrong, this
  2128. * procedure returns an error message in the interpreter's
  2129. * result object.
  2130. *
  2131. *----------------------------------------------------------------------
  2132. */
  2133. private static void deleteCmd(Interp interp, TclObject[] objv)
  2134. {
  2135. Namespace namespace_Renamed;
  2136. string name;
  2137. int i;
  2138. if (objv.Length < 2)
  2139. {
  2140. throw new TclNumArgsException(interp, 2, objv, "?name name...?");
  2141. }
  2142. // Destroying one namespace may cause another to be destroyed. Break
  2143. // this into two passes: first check to make sure that all namespaces on
  2144. // the command line are valid, and report any errors.
  2145. for (i = 2; i < objv.Length; i++)
  2146. {
  2147. name = objv[i].ToString();
  2148. namespace_Renamed = findNamespace(interp, name, null, 0);
  2149. if (namespace_Renamed == null)
  2150. {
  2151. throw new TclException(interp, "unknown namespace \"" + objv[i].ToString() + "\" in namespace delete command");
  2152. }
  2153. }
  2154. // Okay, now delete each namespace.
  2155. for (i = 2; i < objv.Length; i++)
  2156. {
  2157. name = objv[i].ToString();
  2158. namespace_Renamed = findNamespace(interp, name, null, 0);
  2159. if (namespace_Renamed != null)
  2160. {
  2161. deleteNamespace(namespace_Renamed);
  2162. }
  2163. }
  2164. }
  2165. /*
  2166. *----------------------------------------------------------------------
  2167. *
  2168. * NamespaceEvalCmd -> evalCmd
  2169. *
  2170. * Invoked to implement the "namespace eval" command. Executes
  2171. * commands in a namespace. If the namespace does not already exist,
  2172. * it is created. Handles the following syntax:
  2173. *
  2174. * namespace eval name arg ?arg...?
  2175. *
  2176. * If more than one arg argument is specified, the command that is
  2177. * executed is the result of concatenating the arguments together with
  2178. * a space between each argument.
  2179. *
  2180. * Results:
  2181. * Returns if successful, raises TclException if something goes wrong.
  2182. *
  2183. * Side effects:
  2184. * Returns the result of the command in the interpreter's result
  2185. * object. If anything goes wrong, this procedure returns an error
  2186. * message as the result.
  2187. *
  2188. *----------------------------------------------------------------------
  2189. */
  2190. private static void evalCmd(Interp interp, TclObject[] objv)
  2191. {
  2192. Namespace namespace_Renamed;
  2193. CallFrame frame;
  2194. string cmd;
  2195. string name;
  2196. int length;
  2197. if (objv.Length < 4)
  2198. {
  2199. throw new TclNumArgsException(interp, 2, objv, "name arg ?arg...?");
  2200. }
  2201. // Try to resolve the namespace reference, caching the result in the
  2202. // namespace object along the way.
  2203. namespace_Renamed = getNamespaceFromObj(interp, objv[2]);
  2204. // If the namespace wasn't found, try to create it.
  2205. if (namespace_Renamed == null)
  2206. {
  2207. name = objv[2].ToString();
  2208. namespace_Renamed = createNamespace(interp, name, null);
  2209. if (namespace_Renamed == null)
  2210. {
  2211. // FIXME : result hack, we get the interp result and throw it!
  2212. throw new TclException(interp, interp.getResult().ToString());
  2213. }
  2214. }
  2215. // Make the specified namespace the current namespace and evaluate
  2216. // the command(s).
  2217. frame = interp.newCallFrame();
  2218. pushCallFrame(interp, frame, namespace_Renamed, false);
  2219. try
  2220. {
  2221. if (objv.Length == 4)
  2222. {
  2223. interp.eval(objv[3], 0);
  2224. }
  2225. else
  2226. {
  2227. cmd = Util.concat(3, objv.Length, objv);
  2228. // eval() will delete the object when it decrements its
  2229. // refcount after eval'ing it.
  2230. interp.eval(cmd); // do not pass TCL_EVAL_DIRECT, for compiler only
  2231. }
  2232. }
  2233. catch (TclException ex)
  2234. {
  2235. if (ex.getCompletionCode() == TCL.CompletionCode.ERROR)
  2236. {
  2237. interp.addErrorInfo("\n (in namespace eval \"" + namespace_Renamed.fullName + "\" script line " + interp.errorLine + ")");
  2238. }
  2239. throw ex;
  2240. }
  2241. finally
  2242. {
  2243. popCallFrame(interp);
  2244. }
  2245. return ;
  2246. }
  2247. /*
  2248. *----------------------------------------------------------------------
  2249. *
  2250. * NamespaceExportCmd -> exportCmd
  2251. *
  2252. * Invoked to implement the "namespace export" command that specifies
  2253. * which commands are exported from a namespace. The exported commands
  2254. * are those that can be imported into another namespace using
  2255. * "namespace import". Both commands defined in a namespace and
  2256. * commands the namespace has imported can be exported by a
  2257. * namespace. This command has the following syntax:
  2258. *
  2259. * namespace export ?-clear? ?pattern pattern...?
  2260. *
  2261. * Each pattern may contain "string match"-style pattern matching
  2262. * special characters, but the pattern may not include any namespace
  2263. * qualifiers: that is, the pattern must specify commands in the
  2264. * current (exporting) namespace. The specified patterns are appended
  2265. * onto the namespace's list of export patterns.
  2266. *
  2267. * To reset the namespace's export pattern list, specify the "-clear"
  2268. * flag.
  2269. *
  2270. * If there are no export patterns and the "-clear" flag isn't given,
  2271. * this command returns the namespace's current export list.
  2272. *
  2273. * Results:
  2274. * Returns if successful, raises TclException if something goes wrong.
  2275. *
  2276. * Side effects:
  2277. * Returns a result in the interpreter's result object. If anything
  2278. * goes wrong, the result is an error message.
  2279. *
  2280. *----------------------------------------------------------------------
  2281. */
  2282. private static void exportCmd(Interp interp, TclObject[] objv)
  2283. {
  2284. Namespace currNs = getCurrentNamespace(interp);
  2285. string pattern, inString;
  2286. bool resetListFirst = false;
  2287. int firstArg, patternCt, i;
  2288. if (objv.Length < 2)
  2289. {
  2290. throw new TclNumArgsException(interp, 2, objv, "?-clear? ?pattern pattern...?");
  2291. }
  2292. // Process the optional "-clear" argument.
  2293. firstArg = 2;
  2294. if (firstArg < objv.Length)
  2295. {
  2296. inString = objv[firstArg].ToString();
  2297. if (inString.Equals("-clear"))
  2298. {
  2299. resetListFirst = true;
  2300. firstArg++;
  2301. }
  2302. }
  2303. // If no pattern arguments are given, and "-clear" isn't specified,
  2304. // return the namespace's current export pattern list.
  2305. patternCt = (objv.Length - firstArg);
  2306. if (patternCt == 0)
  2307. {
  2308. if (firstArg > 2)
  2309. {
  2310. return ;
  2311. }
  2312. else
  2313. {
  2314. // create list with export patterns
  2315. TclObject list = TclList.newInstance();
  2316. appendExportList(interp, currNs, list);
  2317. interp.setResult(list);
  2318. return ;
  2319. }
  2320. }
  2321. // Add each pattern to the namespace's export pattern list.
  2322. for (i = firstArg; i < objv.Length; i++)
  2323. {
  2324. pattern = objv[i].ToString();
  2325. exportList(interp, currNs, pattern, ((i == firstArg)?resetListFirst:false));
  2326. }
  2327. return ;
  2328. }
  2329. /*
  2330. *----------------------------------------------------------------------
  2331. *
  2332. * NamespaceForgetCmd -> forgetCmd
  2333. *
  2334. * Invoked to implement the "namespace forget" command to remove
  2335. * imported commands from a namespace. Handles the following syntax:
  2336. *
  2337. * namespace forget ?pattern pattern...?
  2338. *
  2339. * Each pattern is a name like "foo::*" or "a::b::x*". That is, the
  2340. * pattern may include the special pattern matching characters
  2341. * recognized by the "string match" command, but only in the command
  2342. * name at the end of the qualified name; the special pattern
  2343. * characters may not appear in a namespace name. All of the commands
  2344. * that match that pattern are checked to see if they have an imported
  2345. * command in the current namespace that refers to the matched
  2346. * command. If there is an alias, it is removed.
  2347. *
  2348. * Results:
  2349. * Returns if successful, raises TclException if something goes wrong.
  2350. *
  2351. * Side effects:
  2352. * Imported commands are removed from the current namespace. If
  2353. * anything goes wrong, this procedure returns an error message in the
  2354. * interpreter's result object.
  2355. *
  2356. *----------------------------------------------------------------------
  2357. */
  2358. private static void forgetCmd(Interp interp, TclObject[] objv)
  2359. {
  2360. string pattern;
  2361. int i;
  2362. if (objv.Length < 2)
  2363. {
  2364. throw new TclNumArgsException(interp, 2, objv, "?pattern pattern...?");
  2365. }
  2366. for (i = 2; i < objv.Length; i++)
  2367. {
  2368. pattern = objv[i].ToString();
  2369. forgetImport(interp, null, pattern);
  2370. }
  2371. return ;
  2372. }
  2373. /*
  2374. *----------------------------------------------------------------------
  2375. *
  2376. * NamespaceImportCmd -> importCmd
  2377. *
  2378. * Invoked to implement the "namespace import" command that imports
  2379. * commands into a namespace. Handles the following syntax:
  2380. *
  2381. * namespace import ?-force? ?pattern pattern...?
  2382. *
  2383. * Each pattern is a namespace-qualified name like "foo::*",
  2384. * "a::b::x*", or "bar::p". That is, the pattern may include the
  2385. * special pattern matching characters recognized by the "string match"
  2386. * command, but only in the command name at the end of the qualified
  2387. * name; the special pattern characters may not appear in a namespace
  2388. * name. All of the commands that match the pattern and which are
  2389. * exported from their namespace are made accessible from the current
  2390. * namespace context. This is done by creating a new "imported command"
  2391. * in the current namespace that points to the real command in its
  2392. * original namespace; when the imported command is called, it invokes
  2393. * the real command.
  2394. *
  2395. * If an imported command conflicts with an existing command, it is
  2396. * treated as an error. But if the "-force" option is included, then
  2397. * existing commands are overwritten by the imported commands.
  2398. *
  2399. * Results:
  2400. * Returns if successful, raises TclException if something goes wrong.
  2401. *
  2402. * Side effects:
  2403. * Adds imported commands to the current namespace. If anything goes
  2404. * wrong, this procedure returns an error message in the interpreter's
  2405. * result object.
  2406. *
  2407. *----------------------------------------------------------------------
  2408. */
  2409. private static void importCmd(Interp interp, TclObject[] objv)
  2410. {
  2411. bool allowOverwrite = false;
  2412. string inString, pattern;
  2413. int i;
  2414. int firstArg;
  2415. if (objv.Length < 2)
  2416. {
  2417. throw new TclNumArgsException(interp, 2, objv, "?-force? ?pattern pattern...?");
  2418. }
  2419. // Skip over the optional "-force" as the first argument.
  2420. firstArg = 2;
  2421. if (firstArg < objv.Length)
  2422. {
  2423. inString = objv[firstArg].ToString();
  2424. if (inString.Equals("-force"))
  2425. {
  2426. allowOverwrite = true;
  2427. firstArg++;
  2428. }
  2429. }
  2430. // Handle the imports for each of the patterns.
  2431. for (i = firstArg; i < objv.Length; i++)
  2432. {
  2433. pattern = objv[i].ToString();
  2434. importList(interp, null, pattern, allowOverwrite);
  2435. }
  2436. return ;
  2437. }
  2438. /*
  2439. *----------------------------------------------------------------------
  2440. *
  2441. * NamespaceInscopeCmd -> inscopeCmd
  2442. *
  2443. * Invoked to implement the "namespace inscope" command that executes a
  2444. * script in the context of a particular namespace. This command is not
  2445. * expected to be used directly by programmers; calls to it are
  2446. * generated implicitly when programs use "namespace code" commands
  2447. * to register callback scripts. Handles the following syntax:
  2448. *
  2449. * namespace inscope name arg ?arg...?
  2450. *
  2451. * The "namespace inscope" command is much like the "namespace eval"
  2452. * command except that it has lappend semantics and the namespace must
  2453. * already exist. It treats the first argument as a list, and appends
  2454. * any arguments after the first onto the end as proper list elements.
  2455. * For example,
  2456. *
  2457. * namespace inscope ::foo a b c d
  2458. *
  2459. * is equivalent to
  2460. *
  2461. * namespace eval ::foo [concat a [list b c d]]
  2462. *
  2463. * This lappend semantics is important because many callback scripts
  2464. * are actually prefixes.
  2465. *
  2466. * Results:
  2467. * Returns if successful, raises TclException if something goes wrong.
  2468. *
  2469. * Side effects:
  2470. * Returns a result in the Tcl interpreter's result object.
  2471. *
  2472. *----------------------------------------------------------------------
  2473. */
  2474. private static void inscopeCmd(Interp interp, TclObject[] objv)
  2475. {
  2476. Namespace namespace_Renamed;
  2477. CallFrame frame;
  2478. int i, result;
  2479. if (objv.Length < 4)
  2480. {
  2481. throw new TclNumArgsException(interp, 2, objv, "name arg ?arg...?");
  2482. }
  2483. // Resolve the namespace reference.
  2484. namespace_Renamed = getNamespaceFromObj(interp, objv[2]);
  2485. if (namespace_Renamed == null)
  2486. {
  2487. throw new TclException(interp, "unknown namespace \"" + objv[2].ToString() + "\" in inscope namespace command");
  2488. }
  2489. // Make the specified namespace the current namespace.
  2490. frame = interp.newCallFrame();
  2491. pushCallFrame(interp, frame, namespace_Renamed, false);
  2492. // Execute the command. If there is just one argument, just treat it as
  2493. // a script and evaluate it. Otherwise, create a list from the arguments
  2494. // after the first one, then concatenate the first argument and the list
  2495. // of extra arguments to form the command to evaluate.
  2496. try
  2497. {
  2498. if (objv.Length == 4)
  2499. {
  2500. interp.eval(objv[3], 0);
  2501. }
  2502. else
  2503. {
  2504. TclObject[] concatObjv = new TclObject[2];
  2505. TclObject list;
  2506. string cmd;
  2507. list = TclList.newInstance();
  2508. for (i = 4; i < objv.Length; i++)
  2509. {
  2510. try
  2511. {
  2512. TclList.append(interp, list, objv[i]);
  2513. }
  2514. catch (TclException ex)
  2515. {
  2516. list.release(); // free unneeded obj
  2517. throw ex;
  2518. }
  2519. }
  2520. concatObjv[0] = objv[3];
  2521. concatObjv[1] = list;
  2522. cmd = Util.concat(0, 1, concatObjv);
  2523. interp.eval(cmd); // do not pass TCL_EVAL_DIRECT, for compiler only
  2524. list.release(); // we're done with the list object
  2525. }
  2526. }
  2527. catch (TclException ex)
  2528. {
  2529. if (ex.getCompletionCode() == TCL.CompletionCode.ERROR)
  2530. {
  2531. interp.addErrorInfo("\n (in namespace inscope \"" + namespace_Renamed.fullName + "\" script line " + interp.errorLine + ")");
  2532. }
  2533. throw ex;
  2534. }
  2535. finally
  2536. {
  2537. popCallFrame(interp);
  2538. }
  2539. return ;
  2540. }
  2541. /*
  2542. *----------------------------------------------------------------------
  2543. *
  2544. * NamespaceOriginCmd -> originCmd
  2545. *
  2546. * Invoked to implement the "namespace origin" command to return the
  2547. * fully-qualified name of the "real" command to which the specified
  2548. * "imported command" refers. Handles the following syntax:
  2549. *
  2550. * namespace origin name
  2551. *
  2552. * Results:
  2553. * An imported command is created in an namespace when that namespace
  2554. * imports a command from another namespace. If a command is imported
  2555. * into a sequence of namespaces a, b,...,n where each successive
  2556. * namespace just imports the command from the previous namespace, this
  2557. * command returns the fully-qualified name of the original command in
  2558. * the first namespace, a. If "name" does not refer to an alias, its
  2559. * fully-qualified name is returned. The returned name is stored in the
  2560. * interpreter's result object. This procedure returns TCL_OK if
  2561. * successful, and TCL_ERROR if anything goes wrong.
  2562. *
  2563. * Side effects:
  2564. * If anything goes wrong, this procedure returns an error message in
  2565. * the interpreter's result object.
  2566. *
  2567. *----------------------------------------------------------------------
  2568. */
  2569. private static void originCmd(Interp interp, TclObject[] objv)
  2570. {
  2571. WrappedCommand command, origCommand;
  2572. if (objv.Length != 3)
  2573. {
  2574. throw new TclNumArgsException(interp, 2, objv, "name");
  2575. }
  2576. // FIXME : is this the right way to search for a command?
  2577. //command = Tcl_GetCommandFromObj(interp, objv[2]);
  2578. command = NamespaceCmd.findCommand(interp, objv[2].ToString(), null, 0);
  2579. if (command == null)
  2580. {
  2581. throw new TclException(interp, "invalid command name \"" + objv[2].ToString() + "\"");
  2582. }
  2583. origCommand = getOriginalCommand(command);
  2584. if (origCommand == null)
  2585. {
  2586. // The specified command isn't an imported command. Return the
  2587. // command's name qualified by the full name of the namespace it
  2588. // was defined in.
  2589. interp.setResult(interp.getCommandFullName(command));
  2590. }
  2591. else
  2592. {
  2593. interp.setResult(interp.getCommandFullName(origCommand));
  2594. }
  2595. return ;
  2596. }
  2597. /*
  2598. *----------------------------------------------------------------------
  2599. *
  2600. * NamespaceParentCmd -> parentCmd
  2601. *
  2602. * Invoked to implement the "namespace parent" command that returns the
  2603. * fully-qualified name of the parent namespace for a specified
  2604. * namespace. Handles the following syntax:
  2605. *
  2606. * namespace parent ?name?
  2607. *
  2608. * Results:
  2609. * Returns if successful, raises TclException if something goes wrong.
  2610. *
  2611. * Side effects:
  2612. * Returns a result in the interpreter's result object. If anything
  2613. * goes wrong, the result is an error message.
  2614. *
  2615. *----------------------------------------------------------------------
  2616. */
  2617. private static void parentCmd(Interp interp, TclObject[] objv)
  2618. {
  2619. Namespace ns;
  2620. if (objv.Length == 2)
  2621. {
  2622. ns = getCurrentNamespace(interp);
  2623. }
  2624. else if (objv.Length == 3)
  2625. {
  2626. ns = getNamespaceFromObj(interp, objv[2]);
  2627. if (ns == null)
  2628. {
  2629. throw new TclException(interp, "unknown namespace \"" + objv[2].ToString() + "\" in namespace parent command");
  2630. }
  2631. }
  2632. else
  2633. {
  2634. throw new TclNumArgsException(interp, 2, objv, "?name?");
  2635. }
  2636. // Report the parent of the specified namespace.
  2637. if (ns.parent != null)
  2638. {
  2639. interp.setResult(ns.parent.fullName);
  2640. }
  2641. }
  2642. /*
  2643. *----------------------------------------------------------------------
  2644. *
  2645. * NamespaceQualifiersCmd -> qualifiersCmd
  2646. *
  2647. * Invoked to implement the "namespace qualifiers" command that returns
  2648. * any leading namespace qualifiers in a string. These qualifiers are
  2649. * namespace names separated by "::"s. For example, for "::foo::p" this
  2650. * command returns "::foo", and for "::" it returns "". This command
  2651. * is the complement of the "namespace tail" command. Note that this
  2652. * command does not check whether the "namespace" names are, in fact,
  2653. * the names of currently defined namespaces. Handles the following
  2654. * syntax:
  2655. *
  2656. * namespace qualifiers string
  2657. *
  2658. * Results:
  2659. * Returns if successful, raises TclException if something goes wrong.
  2660. *
  2661. * Side effects:
  2662. * Returns a result in the interpreter's result object. If anything
  2663. * goes wrong, the result is an error message.
  2664. *
  2665. *----------------------------------------------------------------------
  2666. */
  2667. private static void qualifiersCmd(Interp interp, TclObject[] objv)
  2668. {
  2669. string name;
  2670. int p;
  2671. if (objv.Length != 3)
  2672. {
  2673. throw new TclNumArgsException(interp, 2, objv, "string");
  2674. }
  2675. // Find the end of the string, then work backward and find
  2676. // the start of the last "::" qualifier.
  2677. name = objv[2].ToString();
  2678. p = name.Length;
  2679. while (--p >= 0)
  2680. {
  2681. if ((name[p] == ':') && (p > 0) && (name[p - 1] == ':'))
  2682. {
  2683. p -= 2; // back up over the ::
  2684. while ((p >= 0) && (name[p] == ':'))
  2685. {
  2686. p--; // back up over the preceeding :
  2687. }
  2688. break;
  2689. }
  2690. }
  2691. if (p >= 0)
  2692. {
  2693. interp.setResult(name.Substring(0, (p + 1) - (0)));
  2694. }
  2695. // When no result is set the empty string is the result
  2696. return ;
  2697. }
  2698. /*
  2699. *----------------------------------------------------------------------
  2700. *
  2701. * NamespaceTailCmd -> tailCmd
  2702. *
  2703. * Invoked to implement the "namespace tail" command that returns the
  2704. * trailing name at the end of a string with "::" namespace
  2705. * qualifiers. These qualifiers are namespace names separated by
  2706. * "::"s. For example, for "::foo::p" this command returns "p", and for
  2707. * "::" it returns "". This command is the complement of the "namespace
  2708. * qualifiers" command. Note that this command does not check whether
  2709. * the "namespace" names are, in fact, the names of currently defined
  2710. * namespaces. Handles the following syntax:
  2711. *
  2712. * namespace tail string
  2713. *
  2714. * Results:
  2715. * Returns if successful, raises TclException if something goes wrong.
  2716. *
  2717. * Side effects:
  2718. * Returns a result in the interpreter's result object. If anything
  2719. * goes wrong, the result is an error message.
  2720. *
  2721. *----------------------------------------------------------------------
  2722. */
  2723. private static void tailCmd(Interp interp, TclObject[] objv)
  2724. {
  2725. string name;
  2726. int p;
  2727. if (objv.Length != 3)
  2728. {
  2729. throw new TclNumArgsException(interp, 2, objv, "string");
  2730. }
  2731. // Find the end of the string, then work backward and find the
  2732. // last "::" qualifier.
  2733. name = objv[2].ToString();
  2734. p = name.Length;
  2735. while (--p > 0)
  2736. {
  2737. if ((name[p] == ':') && (name[p - 1] == ':'))
  2738. {
  2739. p++; // just after the last "::"
  2740. break;
  2741. }
  2742. }
  2743. if (p >= 0)
  2744. {
  2745. interp.setResult(name.Substring(p));
  2746. }
  2747. return ;
  2748. }
  2749. /*
  2750. *----------------------------------------------------------------------
  2751. *
  2752. * NamespaceWhichCmd -> whichCmd
  2753. *
  2754. * Invoked to implement the "namespace which" command that returns the
  2755. * fully-qualified name of a command or variable. If the specified
  2756. * command or variable does not exist, it returns "". Handles the
  2757. * following syntax:
  2758. *
  2759. * namespace which ?-command? ?-variable? name
  2760. *
  2761. * Results:
  2762. * Returns if successful, raises TclException if something goes wrong.
  2763. *
  2764. * Side effects:
  2765. * Returns a result in the interpreter's result object. If anything
  2766. * goes wrong, the result is an error message.
  2767. *
  2768. *----------------------------------------------------------------------
  2769. */
  2770. private static void whichCmd(Interp interp, TclObject[] objv)
  2771. {
  2772. string arg;
  2773. WrappedCommand cmd;
  2774. Var variable;
  2775. int argIndex, lookup;
  2776. if (objv.Length < 3)
  2777. {
  2778. throw new TclNumArgsException(interp, 2, objv, "?-command? ?-variable? name");
  2779. }
  2780. // Look for a flag controlling the lookup.
  2781. argIndex = 2;
  2782. lookup = 0; // assume command lookup by default
  2783. arg = objv[2].ToString();
  2784. if ((arg.Length > 1) && (arg[0] == '-'))
  2785. {
  2786. if (arg.Equals("-command"))
  2787. {
  2788. lookup = 0;
  2789. }
  2790. else if (arg.Equals("-variable"))
  2791. {
  2792. lookup = 1;
  2793. }
  2794. else
  2795. {
  2796. throw new TclNumArgsException(interp, 2, objv, "?-command? ?-variable? name");
  2797. }
  2798. argIndex = 3;
  2799. }
  2800. if (objv.Length != (argIndex + 1))
  2801. {
  2802. throw new TclNumArgsException(interp, 2, objv, "?-command? ?-variable? name");
  2803. }
  2804. // FIXME : check that this implementation works!
  2805. switch (lookup)
  2806. {
  2807. case 0:
  2808. arg = objv[argIndex].ToString();
  2809. // FIXME : is this the right way to lookup a Command token?
  2810. //cmd = Tcl_GetCommandFromObj(interp, objv[argIndex]);
  2811. cmd = NamespaceCmd.findCommand(interp, arg, null, 0);
  2812. if (cmd == null)
  2813. {
  2814. return ; // cmd not found, just return (no error)
  2815. }
  2816. interp.setResult(interp.getCommandFullName(cmd));
  2817. return ;
  2818. case 1:
  2819. arg = objv[argIndex].ToString();
  2820. variable = NamespaceCmd.findNamespaceVar(interp, arg, null, 0);
  2821. if (variable != null)
  2822. {
  2823. interp.setResult(Var.getVariableFullName(interp, variable));
  2824. }
  2825. return ;
  2826. }
  2827. return ;
  2828. }
  2829. /*
  2830. *----------------------------------------------------------------------
  2831. *
  2832. * FreeNsNameInternalRep -> dispose
  2833. *
  2834. * Frees the resources associated with a object's internal
  2835. * representation. See src/tcljava/tcl/lang/InternalRep.java
  2836. *
  2837. * Results:
  2838. * None.
  2839. *
  2840. * Side effects:
  2841. * Decrements the ref count of any Namespace structure pointed
  2842. * to by the nsName's internal representation. If there are no more
  2843. * references to the namespace, it's structure will be freed.
  2844. *
  2845. *----------------------------------------------------------------------
  2846. */
  2847. public void dispose()
  2848. {
  2849. bool debug;
  2850. System.Diagnostics.Debug.WriteLine("dispose() called for namespace object " + (otherValue == null?null:otherValue.ns));
  2851. ResolvedNsName resName = otherValue;
  2852. Namespace ns;
  2853. // Decrement the reference count of the namespace. If there are no
  2854. // more references, free it up.
  2855. if (resName != null)
  2856. {
  2857. resName.refCount--;
  2858. if (resName.refCount == 0)
  2859. {
  2860. // Decrement the reference count for the cached namespace. If
  2861. // the namespace is dead, and there are no more references to
  2862. // it, free it.
  2863. ns = resName.ns;
  2864. ns.refCount--;
  2865. if ((ns.refCount == 0) && ((ns.flags & NS_DEAD) != 0))
  2866. {
  2867. free(ns);
  2868. }
  2869. otherValue = null;
  2870. }
  2871. }
  2872. }
  2873. /*
  2874. *----------------------------------------------------------------------
  2875. *
  2876. * DupNsNameInternalRep -> duplicate
  2877. *
  2878. * Get a copy of this Object for copy-on-write
  2879. * operations. We just increment its useCount and return the same
  2880. * ReflectObject because ReflectObject's cannot be modified, so
  2881. * they don't need copy-on-write protections.
  2882. *
  2883. * Results:
  2884. * None.
  2885. *
  2886. * Side effects:
  2887. * None.
  2888. *
  2889. *----------------------------------------------------------------------
  2890. */
  2891. public InternalRep duplicate()
  2892. {
  2893. System.Diagnostics.Debug.WriteLine("duplicate() called for namespace object " + (otherValue == null?null:otherValue.ns));
  2894. ResolvedNsName resName = otherValue;
  2895. if (resName != null)
  2896. {
  2897. resName.refCount++;
  2898. }
  2899. return this;
  2900. }
  2901. /*
  2902. *----------------------------------------------------------------------
  2903. *
  2904. * SetNsNameFromAny -> setNsNameFromAny
  2905. *
  2906. * Attempt to generate a nsName internal representation for a
  2907. * TclObject.
  2908. *
  2909. * Results:
  2910. * Returns if the value could be converted to a proper
  2911. * namespace reference. Otherwise, raises TclException.
  2912. *
  2913. * Side effects:
  2914. * If successful, the object is made a nsName object. Its internal rep
  2915. * is set to point to a ResolvedNsName, which contains a cached pointer
  2916. * to the Namespace. Reference counts are kept on both the
  2917. * ResolvedNsName and the Namespace, so we can keep track of their
  2918. * usage and free them when appropriate.
  2919. *
  2920. *----------------------------------------------------------------------
  2921. */
  2922. private static void setNsNameFromAny(Interp interp, TclObject tobj)
  2923. {
  2924. string name;
  2925. Namespace ns;
  2926. ResolvedNsName resName;
  2927. // Java does not support passing an address so we pass
  2928. // an array of size 1 and then assign arr[0] to the value
  2929. Namespace[] nsArr = new Namespace[1];
  2930. Namespace[] dummy1Arr = new Namespace[1];
  2931. string[] dummy2Arr = new string[1];
  2932. // Get the string representation.
  2933. name = tobj.ToString();
  2934. // Look for the namespace "name" in the current namespace. If there is
  2935. // an error parsing the (possibly qualified) name, return an error.
  2936. // If the namespace isn't found, we convert the object to an nsName
  2937. // object with a null ResolvedNsName internal rep.
  2938. getNamespaceForQualName(interp, name, null, TCL.VarFlag.FIND_ONLY_NS, nsArr, dummy1Arr, dummy1Arr, dummy2Arr);
  2939. // Get the values out of the arrays!
  2940. ns = nsArr[0];
  2941. // If we found a namespace, then create a new ResolvedNsName structure
  2942. // that holds a reference to it.
  2943. if (ns != null)
  2944. {
  2945. Namespace currNs = getCurrentNamespace(interp);
  2946. ns.refCount++;
  2947. resName = new ResolvedNsName();
  2948. resName.ns = ns;
  2949. resName.nsId = ns.nsId;
  2950. resName.refNs = currNs;
  2951. resName.refCount = 1;
  2952. }
  2953. else
  2954. {
  2955. resName = null;
  2956. }
  2957. // By setting the new internal rep we free up the old one.
  2958. // FIXME : should a NamespaceCmd wrap a ResolvedNsName?
  2959. // this is confusing because it seems like the C code uses
  2960. // a ResolvedNsName like it is the InternalRep.
  2961. NamespaceCmd wrap = new NamespaceCmd();
  2962. wrap.otherValue = resName;
  2963. tobj.InternalRep = wrap;
  2964. return ;
  2965. }
  2966. /*
  2967. *----------------------------------------------------------------------
  2968. *
  2969. * UpdateStringOfNsName -> toString
  2970. *
  2971. * Return the string representation for a nsName object.
  2972. * This method is called only by TclObject.toString()
  2973. * when TclObject.stringRep is null.
  2974. *
  2975. * Results:
  2976. * None.
  2977. *
  2978. * Side effects:
  2979. * None.
  2980. *
  2981. *----------------------------------------------------------------------
  2982. */
  2983. public override string ToString()
  2984. {
  2985. bool debug;
  2986. System.Diagnostics.Debug.WriteLine("toString() called for namespace object " + (otherValue == null?null:otherValue.ns));
  2987. ResolvedNsName resName = otherValue;
  2988. Namespace ns;
  2989. string name = "";
  2990. if ((resName != null) && (resName.nsId == resName.ns.nsId))
  2991. {
  2992. ns = resName.ns;
  2993. if ((ns.flags & NS_DEAD) != 0)
  2994. {
  2995. ns = null;
  2996. }
  2997. if (ns != null)
  2998. {
  2999. name = ns.fullName;
  3000. }
  3001. }
  3002. return name;
  3003. }
  3004. // This interface is used to provide a callback when a namespace is deleted
  3005. // (ported Tcl_NamespaceDeleteProc to NamespaceCmd.DeleteProc)
  3006. internal interface DeleteProc
  3007. {
  3008. void delete();
  3009. }
  3010. // This structure contains a cached pointer to a namespace that is the
  3011. // result of resolving the namespace's name in some other namespace. It is
  3012. // the internal representation for a nsName object. It contains the
  3013. // pointer along with some information that is used to check the cached
  3014. // pointer's validity. (ported Tcl_Namespace to NamespaceCmd.Namespace)
  3015. public class Namespace
  3016. {
  3017. internal string name; // The namespace's simple (unqualified)
  3018. // name. This contains no ::'s. The name of
  3019. // the global namespace is "" although "::"
  3020. // is an synonym.
  3021. internal string fullName; // The namespace's fully qualified name.
  3022. // This starts with ::.
  3023. internal DeleteProc deleteProc; // method to invoke when namespace is deleted
  3024. internal Namespace parent; // reference to the namespace that contains
  3025. // this one. null is this is the global namespace.
  3026. internal Hashtable childTable; // Contains any child namespaces. Indexed
  3027. // by strings; values are references to
  3028. // Namespace objects
  3029. internal long nsId; // Unique id for the namespace.
  3030. internal Interp interp; // The interpreter containing this namespace.
  3031. internal int flags; // OR-ed combination of the namespace
  3032. // status flags NS_DYING and NS_DEAD (listed below)
  3033. internal int activationCount; // Number of "activations" or active call
  3034. // frames for this namespace that are on
  3035. // the Tcl call stack. The namespace won't
  3036. // be freed until activationCount becomes zero.
  3037. internal int refCount; // Count of references by nsName
  3038. // objects. The namespace can't be freed
  3039. // until refCount becomes zero.
  3040. internal Hashtable cmdTable; // Contains all the commands currently
  3041. // registered in the namespace. Indexed by
  3042. // strings; values have type (WrappedCommand).
  3043. // Commands imported by Tcl_Import have
  3044. // Command structures that point (via an
  3045. // ImportedCmdRef structure) to the
  3046. // Command structure in the source
  3047. // namespace's command table.
  3048. internal Hashtable varTable; // Contains all the (global) variables
  3049. // currently in this namespace. Indexed
  3050. // by strings; values have type (Var).
  3051. internal string[] exportArray; // Reference to an array of string patterns
  3052. // specifying which commands are exported.
  3053. // A pattern may include "string match"
  3054. // style wildcard characters to specify
  3055. // multiple commands; however, no namespace
  3056. // qualifiers are allowed. null if no
  3057. // export patterns are registered.
  3058. internal int numExportPatterns; // Number of export patterns currently
  3059. // registered using "namespace export".
  3060. internal int maxExportPatterns; // Mumber of export patterns for which
  3061. // space is currently allocated.
  3062. internal Resolver resolver;
  3063. // If non-null, this object overrides the
  3064. // usual command and variable resolution
  3065. // mechanism in Tcl. This procedure is invoked
  3066. // within findCommand and findNamespaceVar to
  3067. // resolve all command and variable references
  3068. // within the namespace.
  3069. // When printing out a Namespace use the full namespace name string
  3070. public override string ToString()
  3071. {
  3072. return fullName;
  3073. }
  3074. }
  3075. // (ported ResolvedNsName to NamespaceCmd.ResolvedNsName)
  3076. internal class ResolvedNsName
  3077. {
  3078. internal Namespace ns; // reference to namespace object
  3079. internal long nsId; // sPtr's unique namespace id. Used to
  3080. // verify that ns is still valid
  3081. // (e.g., it's possible that the namespace
  3082. // was deleted and a new one created at
  3083. // the same address).
  3084. internal Namespace refNs; // reference to the namespace containing the
  3085. // reference (not the namespace that
  3086. // contains the referenced namespace).
  3087. internal int refCount; // Reference count: 1 for each nsName
  3088. // object that has a pointer to this
  3089. // ResolvedNsName structure as its internal
  3090. // rep. This structure can be freed when
  3091. // refCount becomes zero.
  3092. }
  3093. static NamespaceCmd()
  3094. {
  3095. nsMutex = new System.Object();
  3096. }
  3097. }
  3098. }