PageRenderTime 79ms CodeModel.GetById 33ms RepoModel.GetById 1ms app.codeStats 0ms

/TCL/src/commands/NamespaceCmd.cs

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