home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1993 #3 / NN_1993_3.iso / spool / comp / lang / tcl / 2475 < prev    next >
Encoding:
Text File  |  1993-01-27  |  49.9 KB  |  1,712 lines

  1. Newsgroups: comp.lang.tcl
  2. Path: sparky!uunet!eco.twg.com!twg.com!news
  3. From: "David Herron" <david@twg.com>
  4. Subject: interp module source
  5. Message-ID: <1993Jan26.183003.6215@twg.com>
  6. Sensitivity: Personal
  7. Encoding:  1692 TEXT , 4 TEXT 
  8. Sender: news@twg.com (USENET News System)
  9. Conversion: Prohibited
  10. Organization: The Wollongong Group, Inc., Palo Alto, CA
  11. Conversion-With-Loss: Prohibited
  12. Date: Tue, 26 Jan 1993 18:26:04 GMT
  13. Lines: 1697
  14.  
  15. Since a couple people have asked for this (and a couple others have signed
  16. up to the mailing list..) here it is.
  17.  
  18. NOTE: The code is also available by sending mail to services@davids.mmdf.com.
  19. The commands are:
  20.  
  21.     To: services@davids.mmdf.com
  22.     Subject: archive-request files
  23.  
  24. Returns a list of all the files there.
  25.  
  26.     To: services@davids.mmdf.com
  27.     Subject: archive-request interp/interp.[ch] interp/README.interp
  28. interp/fileBrowserC.tcl
  29.  
  30. Returns the interp source.
  31.  
  32. The server uses either the quoted printable or base64 encodings from MIME
  33. for safe transmission.  Other software is available from the server
  34. to decode either encoding.
  35.  
  36. qpdecode.sed
  37. qpdecode.help
  38.  
  39.     A quoted-printable decoder written in sed.  This is incomplete
  40.     but is enough for bootstrapping the following...
  41.  
  42. mimencode.tar.Z.uue
  43.  
  44.     A quoted-printable & base 64 decoder written in C.  The individual
  45.     source files for this are separately retrievable as well.
  46.  
  47. Again, signing up to the mailing list is done with e-mail doing the
  48. following:
  49.  
  50.     To: services@davids.mmdf.com
  51.     Subject: listserv subscribe interp
  52.  
  53. #! /bin/sh
  54. # This is a shell archive, meaning:
  55. # 1. Remove everything above the #! /bin/sh line.
  56. # 2. Save the resulting text in a file.
  57. # 3. Execute the file with /bin/sh (not csh) to create the files:
  58. #    README.interp
  59. #    interp.c
  60. #    interp.h
  61. #    fileBrowserC.tcl
  62. # This archive created: Tue Jan 26 10:24:51 1993
  63. export PATH; PATH=/bin:$PATH
  64. echo shar: extracting "'README.interp'" '(7025 characters)'
  65. if test -f 'README.interp'
  66. then
  67.     echo shar: will not over-write existing file "'README.interp'"
  68. else
  69. sed 's/^    X//' << \SHAR_EOF > 'README.interp'
  70.     X.ds interp \fIinterp\fP
  71.     X.ds tcl TCL
  72.     X.TL
  73.     XMultiple Interpreters in TCL
  74.     X.br
  75.     Xand
  76.     X.br
  77.     XObject Oriented Extensions to TCL
  78.     X.AU
  79.     XDavid S. Herron
  80.     X.br
  81.     X<david@davids.mmdf.com>
  82.     X.DA
  83.     X.AB
  84.     X.LP
  85.     XIn one of the Usenix papers delivered by Dr. Ousterhout is a
  86.     Xstatement to the effect that \*QInterpreters are light weight
  87.     Xthings and programmers should feel free to make as many
  88.     Xas s/he likes\*U.  Unfortunately there was no facilities in the
  89.     XTCL language for manipulating interpreters.  Instead the only
  90.     Xtools to manipulate them are C functions in the library.
  91.     X
  92.     XThis module, called \*[interp], provides access to multiple
  93.     Xinterpreters from \*[tcl].  With it \*[tcl] programmers can create and
  94.     Xdelete interpreters, and can execute commands or access variables in
  95.     Xother interpreters.
  96.     X
  97.     XIn addition facilities are provided for doing Object Oriented
  98.     XProgramming in \*[tcl].  Guidance in designing the facilities were
  99.     Xtaken from \fIObject Oriented Design \s-2with applications\s+2\fP by
  100.     XGrady Booch.
  101.     X.AE
  102.     X
  103.     X.SH
  104.     XCreating, deleting and interacting with interpreters
  105.     X.LP
  106.     X
  107.     XCreating an interp is easy, simply type:
  108.     X
  109.     X.ce 1
  110.     Xset var [interp new interpName]
  111.     X
  112.     XWhich creates a new interp (includes an interpretor) which can be
  113.     Xreferred to either as `interpName' or `$var' (whichever strikes your
  114.     Xfancy; `interp new' returns the name of the new interp structure).
  115.     XThe object created is discussed more in fuller detail below.  This
  116.     Xis just an introduction.
  117.     X
  118.     XThe name of the interp is registered as a command in, at least,
  119.     Xthe namespace of the interpretor which created it.  This command
  120.     Xevaluates the remainder of its command line and returns whatever
  121.     Xthe result was.  For instance:
  122.     X
  123.     X.DS
  124.     X\ \ \ \ \ interp new newInterp
  125.     X\ \ \ \ \ newInterp set pwd \e[pwd\e]
  126.     X\ \ \ \ \ newInterp set dl \e[split $pwd "/"\e]
  127.     X\ \ \ \ \ set dl [newInterp -getVar dl ""]
  128.     X.DE
  129.     X
  130.     XA bit nonsensical, but showed a couple of important points.
  131.     XCreate a new interp and execute "set pwd [pwd]" over there.
  132.     XBut there's \e's quoting the brackets.  This is to avoid
  133.     Xexecuting the `pwd' command in the \fIcurrent\fP interpretor,
  134.     Xbut to leave it for \fInewInterp\fP.  Quoting becomes very
  135.     Ximportant and using the `{}' construct sometimes is important.
  136.     XAn equivalent sequence is:
  137.     X
  138.     X.DS
  139.     X\ \ \ \ \ interp new newInterp
  140.     X\ \ \ \ \ newInterp {
  141.     X\ \ \ \ \ \ \ \ \ \ set pwd [pwd]
  142.     X\ \ \ \ \ \ \ \ \ \ set dl [split $pwd "/"]
  143.     X\ \ \ \ \ }
  144.     X\ \ \ \ \ set dl [newInterp -getVar dl ""]
  145.     X.DE
  146.     X
  147.     XSome commands are created in the new interpretor.  One of particular
  148.     Ximportance is that \fIexit\fP is replaced with one which destroys this
  149.     Xinterpretor rather than exiting the process.
  150.     X
  151.     X.SH
  152.     XIntroduction to O\-O extensions
  153.     X.LP
  154.     XMuch ado has been made about \fIObject Oriented Programming\fP
  155.     Xrecently so there are many books on the subject.  The one I have been
  156.     Xstudying is \fIObject Oriented Design \s-2with applications\s+2\fP by
  157.     XGrady Booch.  It appears to be very good giving a very in depth
  158.     Xdiscussion of what it is and how to use it.  Unfortunately it doesn't
  159.     Xexplain the statement I've heard:
  160.     X.CD
  161.     X\fIObjects are poor mans closures.\fP
  162.     X.DE
  163.     XBe that as it may.  Early in the book is listed the conceptual
  164.     Xframework necessary for an object oriented system.  These are:
  165.     X\fBAbstraction\fP,
  166.     X\fBEncapsulation\fP,
  167.     X\fBModularity\fP,
  168.     X\fBHierarchy\fP,
  169.     X\fBTyping\fP,
  170.     X\fBConcurrency\fP, and
  171.     X\fBPersistence\fP.
  172.     XOf these \*[interp] only implements the first four.
  173.     X
  174.     XThese terms mean:
  175.     X
  176.     X.IP Abstraction
  177.     XIs the essential characteristics of an object which distinguish it
  178.     Xfrom all others.  Provides crisply defined conceptual boundries.
  179.     XPrimarily the actions the object performs rather than the data it
  180.     Xholds.
  181.     X
  182.     X.IP Encapsulation
  183.     XThis is supposed to be Information Hiding.  That is, holding details
  184.     Xof implementation close to the chest so that other bits of software
  185.     Xdon't depend on internal details.  Encapsulation is complementary to
  186.     Xabstraction.  Abstraction is the view from outside the object while
  187.     Xencapsulation is from the inside.  Things to be hidden are those which
  188.     Xdon't contribute to its essential characteristics.
  189.     X
  190.     XUnfortunately \*[interp] and \*[tcl] do not allow for this to be
  191.     Xcompletely carried out properly.  While it is more difficult to access
  192.     Xthings in an interpreter, everything \fBis\fP available.
  193.     X
  194.     X.IP Modularity
  195.     XThis is the unit of \fIphysical decomposition\fP.  That is, what
  196.     Xpieces you break the problem down to.  Which is what \*[interp]
  197.     Xis all about!
  198.     X
  199.     X.IP Hierarchy
  200.     XThis builds on existing structure to create new things.  There
  201.     Xare two sorts of inheritance, and both require a \fIclass\fP
  202.     Xsystem to be present.  These are:
  203.     X
  204.     X.TS
  205.     Xexpand allbox;
  206.     Xl l.
  207.     XSort of relationship    Sort of hierarchy
  208.     X=
  209.     XT{
  210.     X\fIkind of\fP == \fIclass structure\fP
  211.     XT}    T{
  212.     Xinheritance - build on existing object
  213.     XT}
  214.     XT{
  215.     X\fIpart of\fP == \fIobject structure\fP
  216.     XT}    T{
  217.     Xaggregation - build from many objects
  218.     XT}
  219.     X.TE
  220.     X
  221.     XWith \*[interp] one can create either sort of hierarchy.
  222.     X
  223.     X\fBInheritance: \fP The \fI-chainCommand\fP command creates
  224.     Xa command in the interpretor executing it which pulls the same
  225.     Xcommand from the other interpretor but executes that command
  226.     Xwithin the local interpretor.  OK, that was probably a bit
  227.     Xconfusing.  You have two interpretors, \fBcaller\fP and \fBtarget\fP.
  228.     XIn caller you execute
  229.     X
  230.     X    -chainCommand target command-name
  231.     X
  232.     XThis creates \fIcommand-name\fP in caller.  When it executes, it is
  233.     Xdone within caller's context (interp), but the actual function is
  234.     Xretrieved from target's context (interp).
  235.     X
  236.     XThe intent is to create an interpretor which holds the definition
  237.     Xfor objects of a particular class.  To create new instantiations
  238.     Xof the class, a command \fInew\fP should be created.  It creates
  239.     Xa new interpretor and then uses -chainCommand to link the necessary
  240.     Xprocedures into the new interp.  If there are any values to create
  241.     Xthere, then do so in \fInew\fP.
  242.     X
  243.     X\fBAggregation: \fP  This is when you make an object which contains
  244.     Xother objects.  For instance, ...example...
  245.     X
  246.     X.IP Typing
  247.     XNot necessarily the same concept as \fIclass\fP.  Definds what kinds
  248.     Xof objects can be interchanged, and how it is done.
  249.     X
  250.     XSince TCL has no concept of data typing in the first place this
  251.     Xdoesn't seem to have a place here.  Further, the objects one would
  252.     Xcreate with \*[interp] are likely to be \fIlarge\fP, larger than
  253.     Xone would normally do assignments with (for instance).
  254.     X
  255.     X.IP Concurrency
  256.     XMulti threading ability.  Not entirely relavent to object oriented
  257.     Xprogramming, but multi threading can make OOPing easier to accomplish.
  258.     XThis feature distinguishes active objects from inactive ones.
  259.     X
  260.     XIt might be interesting to, at some time, fix a way to let each
  261.     Xinterpretor be its own thread.  Some nifty/interesting abilities
  262.     Xshould be possible that way.  We'll leave that to the future right
  263.     Xnow.
  264.     X
  265.     X.IP Persistence
  266.     XLive long and persist.  Persistent objects survive even after the
  267.     Xprogram which created & held them exits.
  268.     X
  269.     XBut \*[interp] does not provide any facilities for persistent objects.
  270.     XThe object itself may do it, that is up to the object.
  271.     X
  272.     X.SH
  273.     X\fIinterp\fP command
  274.     X.LP
  275.     X
  276.     X.SH
  277.     XCommands in created interpretor
  278.     X.LP
  279.     X
  280.     X.SH
  281.     XCreating classes and objects
  282.     X.LP
  283.     X
  284. SHAR_EOF
  285. if test 7025 -ne "`wc -c < 'README.interp'`"
  286. then
  287.     echo shar: error transmitting "'README.interp'" '(should have been 7025
  288. characters)'
  289. fi
  290. fi # end of overwriting check
  291. echo shar: extracting "'interp.c'" '(27100 characters)'
  292. if test -f 'interp.c'
  293. then
  294.     echo shar: will not over-write existing file "'interp.c'"
  295. else
  296. sed 's/^    X//' << \SHAR_EOF > 'interp.c'
  297.     X/* $Id: interp.c,v 1.1 1993/01/25 06:32:12 david Exp $
  298.     X *
  299.     X * interp.c -- TCL Commands to create/delete/manipulate interpretors.
  300.     X *
  301.     X * AUTHOR:    David Herron <david@davids.mmdf.com> (home)
  302.     X *                 <david@twg.com>         (work)
  303.     X *
  304.     X * INTRO:
  305.     X *
  306.     X * In one of the Usenix papers delivered by Dr. Ousterhout is a
  307.     X * statement to the effect that "Interpretors are light weight
  308.     X * things and programmers should feel free to make as many
  309.     X * as s/he likes".  Unfortunately there was no facilities in the
  310.     X * TCL language for manipulating interpretors.  Instead the attributes
  311.     X * of interpretors are not described very well, and the only
  312.     X * tools to manipulate them are C functions in the library.
  313.     X *
  314.     X * This module is a first attempt at putting an interpretor facility
  315.     X * into TCL.  It keeps a hash table of of {interp,name,destroyHook}
  316.     X * tuples and provides a few TCL commands for manipulating them.
  317.     X *
  318.     X * Since each interpretor has its own namespace this is useful for
  319.     X *
  320.     X *    Creating `modules' with an pseudo-exported interface.
  321.     X *
  322.     X *    Allowing multiple Tk applications to be resident in
  323.     X *    the same core image and have them all think their
  324.     X *    main window is `.'.
  325.     X *
  326.     X *    A form of light-weight processes but with no scheduler
  327.     X *    or preemption by other light weight processes.  This works
  328.     X *    best with the `multiple Tk applications' concept above.
  329.     X *
  330.     X * My initial inspiration for creating this module was the
  331.     X * multiple Tk applications in one core image.  It was thought
  332.     X * up during a brainstorming session on a potential port of
  333.     X * TCL/Tk to MS-DOG using X/DOS.  X/DOS creates a DOS program
  334.     X * which makes the VGA screen look like an X session, but the
  335.     X * X session only lasts for the existance of that one program.
  336.     X *
  337.     X * So the thought was to have a Tk script be a sort of application
  338.     X * launcher.  It would need to be able to create new interpretors
  339.     X * so that each launched application did not interfere with others
  340.     X * and had `.' as its main window.  etc.
  341.     X *
  342.     X * Then an obvious application to have running is a `ps' which is
  343.     X * constantly updated Tk scripts come and go.
  344.     X *
  345.     X * Another is a procedure/variable editor which pokes into other
  346.     X * interpretors and allows the user to get/view/edit variables
  347.     X * or procedures in other interpreters.  Of course you then have
  348.     X * the problem of saving the procedures to disk when you're
  349.     X * satisfied with 'em...
  350.     X *
  351.     X * As they say: The possibilities are endless ...
  352.     X *
  353.     X * PROBLEMS:
  354.     X *
  355.     X * The `interp eval' command prints the result directly to the users
  356.     X * terminal AS WELL AS returning its value.  That is:
  357.     X *
  358.     X *    set val [otherInterp "set val"]
  359.     X *
  360.     X * Workaround: set val [otherInterp {return val}]
  361.     X *
  362.     X * is intended to retrieve `val' from otherInterp.  It does so, but
  363.     X * the value of val is also printed on the screen.  This is probably
  364.     X * because of the behaviour that a top-level `set' prints the value
  365.     X * on the screen.  A possibility is to define a new command `setReturn'
  366.     X * (or some such name) which all it does is set the interp's value.
  367.     X *
  368.     X * The new interpretor is stripped bare.  It isn't clear the
  369.     X * right way to add commands which're C-coded application extensions to
  370.     X * the interpretor.  Adding TCL code is trivial with the createHook.
  371.     X *
  372.     X * A possibility is to provide a library function for retrieving
  373.     X * an interpretor info structure given its name.  Then a function
  374.     X * can be written for each module of extensions which adds the
  375.     X * C commands to that interpretor.  It might be hard, however, to
  376.     X * find which interpretor has the C command in it which can add
  377.     X * the C commands for a module to any interpretor.
  378.     X *
  379.     X * There is no possibility of sharing data or TCL commands between
  380.     X * interpretors other than by passing things through `interp eval'.
  381.     X *
  382.     X * It is not clear that simply replacing the `exit' command
  383.     X * will make things safe.  Nor that this is exactly the right
  384.     X * thing to do.
  385.     X *
  386.     X * We are using TCL_STATIC for some return values below.  It isn't
  387.     X * clear that this is safe.
  388.     X *
  389.     X * The following sequence core dumps:
  390.     X *
  391.     X *    interp manage goober; exit
  392.     X *    -- Type any command.
  393.     X *
  394.     X * Workaround:
  395.     X *
  396.     X *    rename exit _exit; interp manage goober; rename _exit exit
  397.     X *
  398.     X *
  399.     X * If an `interp' is encapsulating some TK widgets.  When the widgets
  400.     X * are destroyed there isn't a clean way to get rid of the interp's.
  401.     X * It would be helpful if widgets had a destroy callback.
  402.     X *
  403.     X * FUTURE:
  404.     X *
  405.     X * This implementation requires no changes to the `core' of TCL.
  406.     X *
  407.     X * COMMANDS:
  408.     X *
  409.     X * interp new name
  410.     X *
  411.     X *    Create a new interpretor, giving it the handle `name'.  The
  412.     X *    interpreter is given the following attributes:
  413.     X *
  414.     X *        The `interp' command.
  415.     X *
  416.     X *        Its `exit' command replaced to simply delete itself
  417.     X *        rather than exit the process.
  418.     X *
  419.     X *        Global variable `thisInterpretor' holding the name
  420.     X *        of the interpretor.
  421.     X *
  422.     X *        A `-destroyHook' command.  This takes a command-string
  423.     X *        which is executed at the beginning of the destruction
  424.     X *        sequence.
  425.     X *
  426.     X *        If a CreateHook has been defined then it is executed.
  427.     X *
  428.     X *
  429.     X *    All interpreter's interp commands have access to the same list
  430.     X *    of interpretors.
  431.     X *
  432.     X * interp MainInterp
  433.     X *
  434.     X *    Registers the current interpreter as the "Main Interpreter".
  435.     X *    The meaning is not currently implemented but ...
  436.     X *
  437.     X * interp result name
  438.     X *
  439.     X *    Returns the current result in the interpretor.  `eval' also
  440.     X *    passes the result back.
  441.     X *
  442.     X * interp list
  443.     X *
  444.     X *    Return TCL list of all the current interpretors.
  445.     X *
  446.     X * interp exists name
  447.     X *
  448.     X *    Tests for existance of an interpretor by that name.  If it
  449.     X *    exists, `1' is returned, otherwise `0'.  There can be only one
  450.     X *    interpreter of a particular name.
  451.     X *
  452.     X * interp createHook command
  453.     X *
  454.     X *    Defines `command' to be the createHook which is executed
  455.     X *    for every new interpretor.  If `command' is empty then
  456.     X *    the createHook is forgotten.
  457.     X *
  458.     X * Some timing results:
  459.     X *
  460.     X * 1. Executing a command in local interpretor versus in a remote interp
  461.     X *    versus in a remote interp but that interp relying on `unknown'
  462.     X *    to find the command.
  463.     X *
  464.     X *
  465.     X * setup:
  466.     X *
  467.     X *    wish: interp new nn
  468.     X *    wish: interp MainInterp
  469.     X *    wish: proc t {} { return }
  470.     X *
  471.     X * Establish baseline, execute command locally:
  472.     X *
  473.     X *    wish: time { for { set i 0 } {$i < 100000} {incr i} { t } }
  474.     X *    21394279 microseconds per iteration
  475.     X *
  476.     X * Execute command remotely but rely on `unknown':
  477.     X *
  478.     X *    wish: time { for { set i 0 } {$i < 100000} {incr i} { nn t } }
  479.     X *    33272622 microseconds per iteration
  480.     X *
  481.     X * Execute command remotely with `t' in there:
  482.     X *
  483.     X *    wish: nn { proc t {} { return } }
  484.     X *    wish: time { for { set i 0 } {$i < 100000} {incr i} { nn t } }
  485.     X *    27732659 microseconds per iteration
  486.     X *
  487.     X * Executing commands in the other interp costs 6.338 seconds extra (29%).
  488.     X * Using `unknown' adds another 5.54 seconds beyond that (19%).
  489.     X *
  490.     X * (All this is as-of Jan 4, 1993) (This indicates we need to do
  491.     X * some profile'ing to see what's going on).
  492.     X *
  493.     X * $Log: interp.c,v $
  494.     X * Revision 1.1  1993/01/25  06:32:12  david
  495.     X * Initial revisions of the interp module, documentation, and file browser.
  496.     X *
  497.     X *
  498.     X */
  499.     X
  500.     X#include <stdlib.h>
  501.     X#include <malloc.h>
  502.     X#include <tclInt.h>
  503.     X#include <tclHash.h>
  504.     X#include "interp.h"
  505.     X
  506.     Xstatic int cmdInterp         _ANSI_ARGS_((ClientData *, Tcl_Interp *, int, char
  507. **));
  508.     Xstatic int cmdExitInterpCMD  _ANSI_ARGS_((ClientData *, Tcl_Interp *, int,
  509. char **));
  510.     Xstatic int cmdDoCMD         _ANSI_ARGS_((ClientData *, Tcl_Interp *, int, char
  511. **));
  512.     Xstatic int cmdDestroyHookCMD _ANSI_ARGS_((ClientData *, Tcl_Interp *, int,
  513. char **));
  514.     X/* static int cmdImportStuffCMD _ANSI_ARGS_((ClientData *, Tcl_Interp *, int,
  515. char **)); */
  516.     Xstatic int cmdChainCommand   _ANSI_ARGS_((ClientData *, Tcl_Interp *, int,
  517. char **));
  518.     Xstatic int cmdChainCommandHelper _ANSI_ARGS_((ClientData *, Tcl_Interp *, int,
  519. char **));
  520.     Xstatic int cmdImportGSetVar  _ANSI_ARGS_((ClientData *, Tcl_Interp *, int,
  521. char **));
  522.     Xstatic int cmdInterp         _ANSI_ARGS_((ClientData *, Tcl_Interp *, int,
  523. char **));
  524.     Xstatic int cmdUnknown        _ANSI_ARGS_((ClientData *, Tcl_Interp *, int,
  525. char **));
  526.     Xstatic int cmdSetParent      _ANSI_ARGS_((ClientData *, Tcl_Interp *, int,
  527. char **));
  528.     X
  529.     Xextern Command *TclFindCmd _ANSI_ARGS_((    /* tclCmdQuery.c */
  530.     X    Tcl_Interp *,         /* Interpreter in which to look. */
  531.     X    char *));                /* Name of desired command. */
  532.     X
  533.     Xextern ClientData         TclClientData _ANSI_ARGS_((Command *));
  534.     Xextern Tcl_CmdDeleteProc *TclDeleteProc _ANSI_ARGS_((Command *));
  535.     Xextern Tcl_CmdProc       *TclCmdProc    _ANSI_ARGS_((Command *));
  536.     X
  537.     X
  538.     Xstatic Tcl_HashTable iList;
  539.     Xstatic short iListInited = (1==0);
  540.     X
  541.     Xstatic char *createHookText = (char *)NULL;
  542.     X
  543.     Xstatic Tcl_Interp *main_interp = (Tcl_Interp *)NULL;
  544.     Xstatic struct interpInfo main_interp_info;
  545.     X
  546.     Xstatic char *nm_MainInterp = "MainInterp";
  547.     X
  548.     Xvoid init_interp(interp)
  549.     XTcl_Interp *interp;
  550.     X{
  551.     X    Tcl_CreateCommand(interp, "interp", cmdInterp, (ClientData) NULL,
  552.     X            (Tcl_CmdDeleteProc *) NULL);
  553.     X    if (!iListInited) {
  554.     X        memset(&main_interp_info, 0, sizeof(main_interp_info));
  555.     X        Tcl_InitHashTable(&iList, TCL_STRING_KEYS);
  556.     X        iListInited = (1==1);
  557.     X    }
  558.     X}
  559.     X
  560.     X/*
  561.     X * Int_CreateInterp() -- Create one of our special interpreters.
  562.     X */
  563.     Xstruct interpInfo *Int_CreateInterp(interp, new_interp, name)
  564.     XTcl_Interp *interp, *new_interp;
  565.     Xchar *name;
  566.     X{
  567.     X    int new;
  568.     X    Tcl_HashEntry *hPtr;
  569.     X    struct interpInfo *info;
  570.     X    char constCmd[128];
  571.     X
  572.     X    hPtr = Tcl_CreateHashEntry(&iList, name, &new);
  573.     X    if (!new) {
  574.     X        Tcl_AppendResult(interp, "ERROR: Already an interpretor named ",
  575.     X                      name, ".", NULL);
  576.     X        return (struct interpInfo *)NULL;
  577.     X    }
  578.     X
  579.     X    if (!new_interp)
  580.     X        new_interp = Tcl_CreateInterp();
  581.     X    if (!new_interp) {
  582.     X        Tcl_AppendResult(interp,
  583.     X            "ERROR: Could not create interpretor for ", name, NULL);
  584.     X        return (struct interpInfo *)NULL;
  585.     X    }
  586.     X
  587.     X    info = (struct interpInfo *)malloc(sizeof(*info));
  588.     X    memset(info, 0, sizeof(*info));
  589.     X    Tcl_SetHashValue(hPtr, info);
  590.     X    info->interp = new_interp;
  591.     X    strncpy(info->name, name, sizeof(info->name));
  592.     X
  593.     X    /*
  594.     X     * Give the new interpreter an `interp' command.
  595.     X     * Also replace its exit command so we can free 
  596.     X     * everything up alright.
  597.     X     */
  598.     X/*
  599.     X * Do we really need to do this?
  600.     X *
  601.     X    {
  602.     X    Tcl_HashEntry *hp;
  603.     X    Tcl_HashSearch hs;
  604.     X    struct interpInfo *iinfo;
  605.     X    for (hp = Tcl_FirstHashEntry(&iList, &hs);
  606.     X         hp;
  607.     X         hp = Tcl_NextHashEntry(&hs)) {
  608.     X        iinfo = (struct interpInfo *)Tcl_GetHashValue(hp);
  609.     X        Tcl_CreateCommand(iinfo->interp, info->name, cmdDoCMD,
  610.     X                 (ClientData) info,
  611.     X                     (Tcl_CmdDeleteProc *) NULL);
  612.     X    }
  613.     X    }
  614.     X*/
  615.     X    Tcl_CreateCommand(interp, info->name, cmdDoCMD,
  616.     X             (ClientData) info,
  617.     X                 (Tcl_CmdDeleteProc *) NULL);
  618.     X    Tcl_CreateCommand(info->interp, info->name, cmdDoCMD,
  619.     X             (ClientData) info,
  620.     X                 (Tcl_CmdDeleteProc *) NULL);
  621.     X    if (main_interp)
  622.     X        Tcl_CreateCommand(main_interp, info->name, cmdDoCMD,
  623.     X                 (ClientData) info,
  624.     X                     (Tcl_CmdDeleteProc *) NULL);
  625.     X
  626.     X    Tcl_CreateCommand(info->interp, nm_MainInterp, cmdDoCMD,
  627.     X             (ClientData) nm_MainInterp, NULL);
  628.     X
  629.     X    Tcl_CreateCommand(info->interp, "interp", cmdInterp,
  630.     X             (ClientData) info,
  631.     X                 (Tcl_CmdDeleteProc *) NULL);
  632.     X    Tcl_CreateCommand(info->interp, "exit", cmdExitInterpCMD,
  633.     X             (ClientData) info,
  634.     X                 (Tcl_CmdDeleteProc *) NULL);
  635.     X    Tcl_CreateCommand(info->interp, "-destroyHook", cmdDestroyHookCMD,
  636.     X             (ClientData) info,
  637.     X                 (Tcl_CmdDeleteProc *) NULL);
  638.     X    Tcl_CreateCommand(info->interp, "-getVar", cmdImportGSetVar,
  639.     X             (ClientData) info,
  640.     X                 (Tcl_CmdDeleteProc *) NULL);
  641.     X    Tcl_CreateCommand(info->interp, "-setVar", cmdImportGSetVar,
  642.     X             (ClientData) info,
  643.     X                 (Tcl_CmdDeleteProc *) NULL);
  644.     X    Tcl_CreateCommand(info->interp, "-unsetVar", cmdImportGSetVar,
  645.     X             (ClientData) info,
  646.     X                 (Tcl_CmdDeleteProc *) NULL);
  647.     X
  648.     X/*
  649.     X *    Tcl_CreateCommand(info->interp, "-import", cmdImportStuffCMD,
  650.     X *             (ClientData) info,
  651.     X *                 (Tcl_CmdDeleteProc *) NULL);
  652.     X */
  653.     X    Tcl_CreateCommand(info->interp, "-chainCommand", cmdChainCommand,
  654.     X             (ClientData) info,
  655.     X                 (Tcl_CmdDeleteProc *) NULL);
  656.     X
  657.     X    Tcl_CreateCommand(info->interp, "-parentInterp", cmdSetParent,
  658.     X             (ClientData) info,
  659.     X                 (Tcl_CmdDeleteProc *) NULL);
  660.     X
  661.     X    Tcl_CreateCommand(info->interp, "unknown", cmdUnknown,
  662.     X             (ClientData) info,
  663.     X                 (Tcl_CmdDeleteProc *) NULL);
  664.     X
  665.     X    /*
  666.     X     * Set up the name of the interpretor.  Make it read-only.
  667.     X     */
  668.     X    Tcl_SetVar(interp, "thisInterpretor", info->name, TCL_GLOBAL_ONLY);
  669.     X    sprintf(constCmd,
  670.     X"set thisInterpretor {%s}; trace variable thisInterpretor w \"set
  671. thisInterpretor {%s}\"; list",
  672.     X        info->name, info->name);
  673.     X    Tcl_GlobalEval(info->interp, constCmd, 0, (char *)NULL);
  674.     X
  675.     X    /*
  676.     X     * The intention of the createHook is so the
  677.     X     * new interpreters can be customized.
  678.     X     */
  679.     X    if (isstr(createHookText)) {
  680.     X        Tcl_GlobalEval(info->interp, createHookText, 0,(char *)NULL);
  681.     X    }
  682.     X
  683.     X    return info;
  684.     X}
  685.     X
  686.     X/*
  687.     X * Int_findInterp() -- Find the named interpretor info structure if
  688.     X *    it exists.
  689.     X *
  690.     X * HACK: We recognize `MainInterp' as a special name and fake up
  691.     X *     an info structure for it.
  692.     X */
  693.     Xstruct interpInfo *Int_findInterp(name)
  694.     Xchar *name;
  695.     X{
  696.     X    Tcl_HashEntry *hPtr;
  697.     X    struct interpInfo *info;
  698.     X
  699.     X    if (!isstr(name))
  700.     X        return (struct interpInfo *)NULL;
  701.     X
  702.     X    if (strcmp(name, nm_MainInterp) == 0 && main_interp) {
  703.     X        main_interp_info.interp = main_interp;
  704.     X        return &main_interp_info;
  705.     X    }
  706.     X
  707.     X    hPtr = Tcl_FindHashEntry(&iList, name);
  708.     X    if (!hPtr)
  709.     X        return (struct interpInfo *)NULL;
  710.     X    info = (struct interpInfo *)Tcl_GetHashValue(hPtr);
  711.     X    return info;
  712.     X}
  713.     X
  714.     X/*
  715.     X * Int_whoIs() -- Find the name for a particular interpretor.
  716.     X */
  717.     Xstruct interpInfo *Int_whoIs(interp)
  718.     XTcl_Interp *interp;
  719.     X{
  720.     X    Tcl_HashEntry *hPtr;
  721.     X    Tcl_HashSearch hs;
  722.     X    struct interpInfo *info;
  723.     X
  724.     X    for (hPtr = Tcl_FirstHashEntry(&iList, &hs);
  725.     X         hPtr;
  726.     X         hPtr = Tcl_NextHashEntry(&hs)) {
  727.     X        info = (struct interpInfo *)Tcl_GetHashValue(hPtr);
  728.     X        if (info->interp == interp)
  729.     X            return info;
  730.     X    }
  731.     X    return (struct interpInfo *)NULL;
  732.     X}
  733.     X
  734.     X/*
  735.     X * Int_delInterp() -- Clean up after an interpretor.
  736.     X */
  737.     Xvoid Int_delInterp(info)
  738.     Xstruct interpInfo *info;
  739.     X{
  740.     X    Tcl_HashEntry *hPtr;
  741.     X
  742.     X    hPtr = Tcl_FindHashEntry(&iList, info->name);
  743.     X
  744.     X    {
  745.     X    Tcl_HashEntry *hp;
  746.     X    Tcl_HashSearch hs;
  747.     X    struct interpInfo *iinfo;
  748.     X    for (hp = Tcl_FirstHashEntry(&iList, &hs);
  749.     X         hp;
  750.     X         hp = Tcl_NextHashEntry(&hs)) {
  751.     X        iinfo = (struct interpInfo *)Tcl_GetHashValue(hp);
  752.     X        Tcl_DeleteCommand(iinfo->interp, info->name);
  753.     X    }
  754.     X    }
  755.     X
  756.     X    if (isstr(info->destroyHook)) {
  757.     X        Tcl_GlobalEval(info->interp, info->destroyHook, 0, (char *)NULL);
  758.     X        free(info->destroyHook);
  759.     X    }
  760.     X
  761.     X    Tcl_DeleteInterp(info->interp);
  762.     X    memset(info, 0, sizeof(*info));
  763.     X    free(info);
  764.     X    Tcl_DeleteHashEntry(hPtr);
  765.     X}
  766.     X
  767.     X/*
  768.     X * Int_ExecCommand() -- Execute a command (already parsed into argc/argv)
  769.     X *    in another interpretor.  If we cannot find the command over there
  770.     X *    attempt to execute `unknown' over there.
  771.     X *
  772.     X * HEAVILY derived from Tcl_Eval(), but we cannot call trace procedures.
  773.     X */
  774.     Xint Int_ExecCommand(this, other, argc, argv)
  775.     XTcl_Interp *this, *other;
  776.     Xint   argc;
  777.     Xchar *argv[];
  778.     X{
  779.     X    int result = TCL_OK;                         /* Return value. */
  780.     X    register Interp *iPtr = (Interp *) this;
  781.     X    Command *cmdPtr;
  782.     X    char **new_argv = (char **)NULL;
  783.     X    int    new_argc = -1;
  784.     X
  785.     X    if (!other) {
  786.     X        Tcl_AppendResult(this,
  787.     X"ERROR: Int_ExecCommand() called with NULL `other' interpretor.", NULL);
  788.     X        return TCL_ERROR;
  789.     X    }
  790.     X
  791.     X    /*
  792.     X     * This is taken from tclBasic.c:Tcl_Eval().
  793.     X     *//*
  794.     X         * Find the procedure to execute this command.  If there isn't
  795.     X         * one, then see if there is a command "unknown".  If so,
  796.     X         * invoke it instead, passing it the words of the original
  797.     X         * command as arguments.
  798.     X         */
  799.     X    cmdPtr = TclFindCmd(other, argv[0]);
  800.     X    if (cmdPtr == (Command *)NULL) {
  801.     X        int i;
  802.     X 
  803.     X        cmdPtr = TclFindCmd(this, "unknown");
  804.     X        if (cmdPtr == (Command *)NULL) {
  805.     X            Tcl_ResetResult(this);
  806.     X            Tcl_AppendResult(this, "ERROR: invalid command name: \"",
  807.     X                argv[0], "\"", (char *) NULL);
  808.     X            result = TCL_ERROR;
  809.     X            goto done;
  810.     X        }
  811.     X        new_argc = argc + 1;
  812.     X        new_argv = (char **)malloc((new_argc+1) * sizeof(char *));
  813.     X        new_argv[0] = "unknown";
  814.     X        for (i = 0; i < new_argc; i++) {
  815.     X            new_argv[i+1] = argv[i];
  816.     X        }
  817.     X        }
  818.     X
  819.     X    /*
  820.     X     * Call trace procedures, if any.
  821.     X     *//*
  822.     X     * ... We can't do trace procedures since the original
  823.     X     * ... command line isn't available.
  824.     X     */
  825.     X 
  826.     X        /*
  827.     X         * At long last, invoke the command procedure.  Reset the
  828.     X         * result to its default empty value first (it could have
  829.     X         * gotten changed by earlier commands in the same command
  830.     X         * string).
  831.     X         */
  832.     X        iPtr->cmdCount++;
  833.     X        Tcl_FreeResult(iPtr);
  834.     X        iPtr->result = iPtr->resultSpace;
  835.     X        iPtr->resultSpace[0] = 0;
  836.     X        result = (*(TclCmdProc(cmdPtr)))(TclClientData(cmdPtr), this,
  837.     X                    new_argv ? new_argc : argc,
  838.     X                    new_argv ? new_argv : argv);
  839.     X    /* fall through */
  840.     Xdone:
  841.     X    if (new_argv) free(new_argv);
  842.     X    return result;
  843.     X}
  844.     X
  845.     Xvoid Int_copyErrorInfo(src, dest)
  846.     Xstruct interpInfo *src;
  847.     XTcl_Interp *dest;
  848.     X{
  849.     X    char msbuf[60];
  850.     X    char *errorInfo = Tcl_GetVar(src->interp, "errorInfo", TCL_LEAVE_ERR_MSG);
  851.     X    if (!isstr(errorInfo)) return;
  852.     X
  853.     X    sprintf(msbuf, "    ----> Copied from interpretor %s\n", src->name);
  854.     X    Tcl_AddErrorInfo(dest, msbuf);
  855.     X    Tcl_AddErrorInfo(dest, errorInfo);
  856.     X    sprintf(msbuf, "\n    <---- End of copy from interpretor %s", src->name);
  857.     X    Tcl_AddErrorInfo(dest, msbuf);
  858.     X}
  859.     X
  860.     Xstatic int cmdExitInterpCMD(clientData, interp, argc, argv)
  861.     X    ClientData *clientData;
  862.     X    Tcl_Interp *interp;
  863.     X    int argc;
  864.     X    char *argv[];
  865.     X{
  866.     X    Tcl_DeleteCommand(interp, ((struct interpInfo *)clientData)->name);
  867.     X    Int_delInterp((struct interpInfo *)clientData);
  868.     X    return TCL_OK;
  869.     X}
  870.     X
  871.     X/*
  872.     X *----------------------------------------------------------------------
  873.     X *
  874.     X * cmdDoCMD --
  875.     X *
  876.     X *      Given a variable number of string arguments, concatenate them
  877.     X *      all together and execute the result as a Tcl command in the
  878.     X *    interpreter passed in clientData.  This routine is taken
  879.     X *    DIRECTLY from tclBasic.c:Tcl_VarEval().
  880.     X *
  881.     X *    It would be nice if TCL internals offered a Tcl_xxxEval()
  882.     X *    which took an argv/argc.  That way we could avoid some
  883.     X *    needless malloc()ing & parsing.
  884.     X *
  885.     X * Results:
  886.     X *      A standard Tcl return result.  An error message or other
  887.     X *      result may be left in interp->result.
  888.     X *
  889.     X * Side effects:
  890.     X *      Depends on what was done by the command.
  891.     X *
  892.     X *----------------------------------------------------------------------
  893.     X */
  894.     Xstatic int cmdDoCMD(clientData, interp, argc, argv)
  895.     X    ClientData *clientData;
  896.     X    Tcl_Interp *interp;
  897.     X    int argc;
  898.     X    char *argv[];
  899.     X{
  900.     X    struct interpInfo *info;
  901.     X    char *cmd;
  902.     X    int result;
  903.     X
  904.     X    if (clientData != nm_MainInterp)
  905.     X        info = (struct interpInfo *)clientData;
  906.     X    else
  907.     X        info = Int_findInterp(nm_MainInterp);
  908.     X
  909.     X    cmd = Tcl_Merge(argc - 1, &(argv[1]));
  910.     X    if (cmd && cmd[0] == '{') {
  911.     X
  912.     X        /*
  913.     X         * Remove the {}'s if they're there.
  914.     X         */
  915.     X        register char *p;
  916.     X        for (p = cmd; p[0] != '\0'; p++)
  917.     X            p[0] = p[1];
  918.     X        if (p > cmd) {
  919.     X            while (p[0] == '\0') p--;
  920.     X            while (p[0] == ' ' || p[0] == '\t') {
  921.     X                p[0] = '\0';
  922.     X                p--;
  923.     X            }
  924.     X            if (p[0] == '}') p[0] = '\0';
  925.     X        }
  926.     X    }
  927.     X
  928.     X    result = Tcl_Eval(info->interp, cmd, 0, (char **)NULL);
  929.     X    if (cmd) ckfree(cmd);
  930.     X
  931.     X    /*
  932.     X     * If the Eval deleted the interpretor then
  933.     X     * info->interp will be NULL.
  934.     X     */
  935.     X    if (info->interp) {
  936.     X        if (result != TCL_OK)
  937.     X            Int_copyErrorInfo(info, interp);
  938.     X        Tcl_SetResult(interp, info->interp->result, TCL_STATIC);
  939.     X    }
  940.     X
  941.     X    return result;
  942.     X}
  943.     X
  944.     X/*
  945.     X * USAGE:    -destroyHook ?command?
  946.     X *
  947.     X * Manipulates the command string executed just before
  948.     X * destruction of the interpreter.
  949.     X */
  950.     Xstatic int cmdDestroyHookCMD(clientData, interp, argc, argv)
  951.     XClientData *clientData;
  952.     XTcl_Interp *interp;
  953.     Xint argc;
  954.     Xchar *argv[];
  955.     X{
  956.     X    struct interpInfo *info = (struct interpInfo *)clientData;
  957.     X
  958.     X    if (isstr(info->destroyHook)) free(info->destroyHook);
  959.     X    info->destroyHook = (char *)NULL;
  960.     X    if (isstr(argv[1])) info->destroyHook = strdup(argv[1]);
  961.     X
  962.     X    return TCL_OK;
  963.     X}
  964.     X
  965.     X/*
  966.     X * USAGE:    -getVar name1 name2
  967.     X *
  968.     X * Gets the value of a variable from another interpretor.
  969.     X *
  970.     X * USAGE:    -setVar name1 name2 value
  971.     X *
  972.     X * Sets the value of a variable in another interpretor.
  973.     X *
  974.     X * USAGE:    -unsetVar name1 name2
  975.     X *
  976.     X * Gets rid of (unset) a variable in another interpretor.
  977.     X */
  978.     Xstatic int cmdImportGSetVar(clientData, interp, argc, argv)
  979.     XClientData *clientData;
  980.     XTcl_Interp *interp;
  981.     Xint argc;
  982.     Xchar *argv[];
  983.     X{
  984.     X    struct interpInfo *info = (struct interpInfo *)clientData;
  985.     X    char *res;
  986.     X
  987.     X    switch (argv[0][1]) {
  988.     X    case 'g':
  989.     X        if (argc != 3) {
  990.     X            Tcl_AppendResult(interp, "USAGE: -getVar name1 name2", NULL);
  991.     X            return TCL_ERROR;
  992.     X        }
  993.     X        res = Tcl_GetVar2(info->interp, argv[1], argv[2], TCL_LEAVE_ERR_MSG);
  994.     X        break;
  995.     X
  996.     X    case 's':
  997.     X        if (argc != 4) {
  998.     X            Tcl_AppendResult(interp, "USAGE: -setVar name1 name2 value", NULL);
  999.     X            return TCL_ERROR;
  1000.     X        }
  1001.     X        res = Tcl_SetVar2(info->interp, argv[1], argv[2], argv[3],
  1002. TCL_LEAVE_ERR_MSG);
  1003.     X        break;
  1004.     X
  1005.     X    case 'u':
  1006.     X        if (argc != 4) {
  1007.     X            Tcl_AppendResult(interp, "USAGE: -unsetVar name1 name2", NULL);
  1008.     X            return TCL_ERROR;
  1009.     X        }
  1010.     X        return Tcl_UnsetVar2(info->interp, argv[1], argv[2], TCL_LEAVE_ERR_MSG);
  1011.     X        break;
  1012.     X    default:
  1013.     X    }
  1014.     X
  1015.     X
  1016.     X    if (isstr(res)) {
  1017.     X        Tcl_SetResult(interp, res, TCL_VOLATILE);
  1018.     X        return TCL_OK;
  1019.     X    }
  1020.     X    else
  1021.     X        return TCL_ERROR;
  1022.     X}
  1023.     X
  1024.     X/*
  1025.     X * USAGE:    -importCommand otherInterp function
  1026.     X *
  1027.     X * Imports copies of objects from other interpretors.
  1028.     X *
  1029.     X * IMPLEMENTATION: Look up the named interpreter.  We need to
  1030.     X * know internals.  Step through its hash table and duplicate
  1031.     X * everything we see.  As a side effect this should replace
  1032.     X * anything things already existing locally but with names
  1033.     X * conflicting with what is imported.
  1034.     X *
  1035.     X * Once we've duplicated the thing, it is completely independant
  1036.     X * from the original.
  1037.     X */
  1038.     X/*
  1039.     X * static int cmdImportStuffCMD(clientData, interp, argc, argv)
  1040.     X * ClientData *clientData;
  1041.     X * Tcl_Interp *interp;
  1042.     X * int argc;
  1043.     X * char *argv[];
  1044.     X * {
  1045.     X *     return TCL_OK;
  1046.     X * }
  1047.     X */
  1048.     X
  1049.     X/*
  1050.     X * cmdChainCommandHelper() -- Do the work of executing the command
  1051.     X *    in the interpretor named in the -chainCommand command.
  1052.     X *
  1053.     X * Also used to support MainInterp command.
  1054.     X */
  1055.     Xstatic int cmdChainCommandHelper(clientData, interp, argc, argv)
  1056.     XClientData *clientData;
  1057.     XTcl_Interp *interp;
  1058.     Xint argc;
  1059.     Xchar *argv[];
  1060.     X{
  1061.     X    struct interpInfo *info;
  1062.     X
  1063.     X    info = Int_findInterp((char *)clientData);
  1064.     X    if (!info) {
  1065.     X        Tcl_AppendResult(interp, "ERROR: Could not find interpretor '",
  1066.     X            (char *)clientData, "'.", NULL);
  1067.     X        return TCL_ERROR;
  1068.     X    }
  1069.     X    return Int_ExecCommand(interp, info->interp, argc, argv);
  1070.     X}
  1071.     X
  1072.     X/*
  1073.     X * USAGE:    -chainCommand otherInterp function
  1074.     X *
  1075.     X * Makes a link to command in another interpretor.
  1076.     X *
  1077.     X * IMPLEMENTATION: Looks up the named interpretor.  We must
  1078.     X * know the internals.  Steps through the hash table and for
  1079.     X * every object does:
  1080.     X *
  1081.     X *    FUNCTION: Creates a command pointing at a C function
  1082.     X *        to be written in this module.  The function looks
  1083.     X *        up the interpreter then eval's the command over
  1084.     X *        there.  It returns the ->result of that interpreter.
  1085.     X *
  1086.     X */
  1087.     Xstatic int cmdChainCommand(clientData, interp, argc, argv)
  1088.     XClientData *clientData;
  1089.     XTcl_Interp *interp;
  1090.     Xint argc;
  1091.     Xchar *argv[];
  1092.     X{
  1093.     X/*    struct interpInfo *info = (struct interpInfo *)clientData;*/
  1094.     X    struct interpInfo *other = (struct interpInfo *)NULL;
  1095.     X
  1096.     X    if (argc != 3) {
  1097.     X        Tcl_AppendResult(interp, "USAGE: -chainCommand otherInterp function",
  1098.     X            NULL);
  1099.     X        return TCL_ERROR;
  1100.     X    }
  1101.     X
  1102.     X    other = Int_findInterp(argv[1]);
  1103.     X    if (!other) {
  1104.     X        Tcl_AddErrorInfo(interp, "    While executing '-chainCommand'");
  1105.     X        return TCL_ERROR;
  1106.     X    }
  1107.     X
  1108.     X    Tcl_CreateCommand(interp, argv[2], cmdChainCommandHelper,
  1109.     X            strdup(argv[1]), free);
  1110.     X    return TCL_OK;
  1111.     X}
  1112.     X
  1113.     X
  1114.     X/*
  1115.     X * USAGE:    unknown command
  1116.     X *
  1117.     X * Version of `unknown' which
  1118.     X *
  1119.     X *
  1120.     X */
  1121.     Xstatic int cmdUnknown(clientData, interp, argc, argv)
  1122.     XClientData *clientData;
  1123.     XTcl_Interp *interp;
  1124.     Xint argc;
  1125.     Xchar *argv[];
  1126.     X{
  1127.     X    struct interpInfo *info = (struct interpInfo *)clientData;
  1128.     X    struct interpInfo *other = (struct interpInfo *)NULL;
  1129.     X
  1130.     X    if (isstr(info->parent)) {
  1131.     X        other = Int_findInterp(info->parent);
  1132.     X        if (TclFindCmd(other->interp, argv[1]))
  1133.     X            return Int_ExecCommand(interp, other->interp, argc-1, argv+1);
  1134.     X    }
  1135.     X
  1136.     X    if (main_interp && TclFindCmd(main_interp, argv[1]))
  1137.     X        return Int_ExecCommand(interp, main_interp, argc-1, argv+1);
  1138.     X
  1139.     X    Tcl_ResetResult(interp);
  1140.     X    Tcl_AppendResult(interp, "ERROR: Cannot pass command \"",
  1141.     X                argv[1], "\" to any interpretor.", (char *) NULL);
  1142.     X    return TCL_ERROR;
  1143.     X}
  1144.     X
  1145.     X
  1146.     X/*
  1147.     X * USAGE:    -parentInterp name
  1148.     X *
  1149.     X */
  1150.     Xstatic int cmdSetParent(clientData, interp, argc, argv)
  1151.     XClientData *clientData;
  1152.     XTcl_Interp *interp;
  1153.     Xint argc;
  1154.     Xchar *argv[];
  1155.     X{
  1156.     X    struct interpInfo *info = (struct interpInfo *)clientData;
  1157.     X
  1158.     X    if (argc < 1 || argc > 2) {
  1159.     X        Tcl_AppendResult(interp, "USAGE: -parentInterp name", NULL);
  1160.     X        return TCL_ERROR;
  1161.     X    }
  1162.     X
  1163.     X    /*
  1164.     X     * If there's only one arg they're asking for the name of the parent.
  1165.     X     */
  1166.     X    if (argc == 1) {
  1167.     X        Tcl_SetResult(interp, info->parent, TCL_STATIC);
  1168.     X        return TCL_OK;
  1169.     X    }
  1170.     X
  1171.     X    /*
  1172.     X     * If it's a NULL string they wanna dissasociate them.
  1173.     X     */
  1174.     X    if (!isstr(argv[1]))
  1175.     X        info->parent[0] = '\0';
  1176.     X    else
  1177.     X        strncpy(info->parent, argv[1], sizeof(info->parent));
  1178.     X
  1179.     X    return TCL_OK;
  1180.     X}
  1181.     X
  1182.     Xstatic int cmdInterp(clientData, interp, argc, argv)
  1183.     XClientData *clientData;
  1184.     XTcl_Interp *interp;
  1185.     Xint argc;
  1186.     Xchar *argv[];
  1187.     X{
  1188.     X    Tcl_HashEntry *hPtr;
  1189.     X    struct interpInfo *info;
  1190.     X
  1191.     X
  1192.     X
  1193.     X    Tcl_ResetResult(interp);
  1194.     X
  1195.     X    if (argc < 2) {
  1196.     X        Tcl_AppendResult(interp, "USAGE: interp
  1197. new|MainInterp|exists|list|createHook",
  1198.     X            NULL);
  1199.     X        return TCL_ERROR;
  1200.     X    }
  1201.     X
  1202.     X    /*
  1203.     X     * interp new name
  1204.     X     */
  1205.     X    if (argv[1][0] == 'n' && strcmp(argv[1], "new") == 0) {
  1206.     X        if (!isstr(argv[2])) goto no_interp_name_given;
  1207.     X        info = Int_CreateInterp(interp, (Tcl_Interp *)NULL, argv[2]);
  1208.     X        if (!info) {
  1209.     X            Tcl_AddErrorInfo(interp, "   While executing 'interp new'");
  1210.     X            return TCL_ERROR;
  1211.     X        }
  1212.     X        else return TCL_OK;
  1213.     X    }
  1214.     X
  1215.     X    /*
  1216.     X     * interp mainInterp
  1217.     X     */
  1218.     X    if (argv[1][0] == nm_MainInterp[0] && strcmp(argv[1], nm_MainInterp) == 0) {
  1219.     X        main_interp = interp;
  1220.     X        return TCL_OK;
  1221.     X    }
  1222.     X
  1223.     X    /*
  1224.     X     * interp exists name
  1225.     X     */
  1226.     X    else if (argv[1][0] == 'e' && strcmp(argv[1], "exists") == 0) {
  1227.     X        if (!isstr(argv[2])) goto no_interp_name_given;
  1228.     X        info = Int_findInterp(argv[2]);
  1229.     X        if (info)
  1230.     X            Tcl_SetResult(interp, "1", TCL_STATIC);
  1231.     X        else
  1232.     X            Tcl_SetResult(interp, "0", TCL_STATIC);
  1233.     X        return TCL_OK;
  1234.     X    }
  1235.     X
  1236.     X    /*
  1237.     X     * interp list
  1238.     X     */
  1239.     X    else if (argv[1][0] == 'l' && strcmp(argv[1], "list") == 0) {
  1240.     X        Tcl_HashSearch hs;
  1241.     X        for (hPtr = Tcl_FirstHashEntry(&iList, &hs);
  1242.     X             hPtr;
  1243.     X             hPtr = Tcl_NextHashEntry(&hs)) {
  1244.     X            Tcl_AppendElement(interp, Tcl_GetHashKey(&iList, hPtr), 0);
  1245.     X        }
  1246.     X        if (main_interp)
  1247.     X            Tcl_AppendElement(interp, "MainInterp");
  1248.     X        return TCL_OK;
  1249.     X    }
  1250.     X
  1251.     X    /*
  1252.     X     * interp createHook command
  1253.     X     */
  1254.     X    else if (argv[1][0] == 'c' && strcmp(argv[1], "createHook") == 0) {
  1255.     X        if (createHookText) free(createHookText);
  1256.     X        if (isstr(argv[2]))
  1257.     X            createHookText = strdup(argv[2]);
  1258.     X        else
  1259.     X            createHookText = (char *)NULL;
  1260.     X        return TCL_OK;
  1261.     X    }
  1262.     X
  1263.     X    /*
  1264.     X     * interp result name
  1265.     X     */
  1266.     X    else if (argv[1][0] == 'r' && strcmp(argv[1], "result") == 0) {
  1267.     X        if (!isstr(argv[2])) goto no_interp_name_given;
  1268.     X        info = Int_findInterp(argv[2]);
  1269.     X        if (!info) {
  1270.     X            Tcl_AppendResult(interp, "ERROR: Could not find interpreter '",
  1271.     X                argv[2], "'.", NULL);
  1272.     X            return TCL_ERROR;
  1273.     X        }
  1274.     X
  1275.     X        Tcl_SetResult(interp, info->interp->result, TCL_STATIC);
  1276.     X        return TCL_OK;
  1277.     X    }
  1278.     X
  1279.     X    Tcl_AppendResult(interp, "ERROR: Unknown interp subcommand: ",
  1280.     X                argv[1], NULL);
  1281.     X    return TCL_ERROR;
  1282.     X
  1283.     Xno_interp_name_given:
  1284.     X    Tcl_AppendResult(interp, "USAGE: ", argv[0], " ", argv[1], " interp-name",
  1285.     X                    NULL);
  1286.     X    return TCL_ERROR;
  1287.     X}
  1288.     X
  1289. SHAR_EOF
  1290. if test 27100 -ne "`wc -c < 'interp.c'`"
  1291. then
  1292.     echo shar: error transmitting "'interp.c'" '(should have been 27100
  1293. characters)'
  1294. fi
  1295. fi # end of overwriting check
  1296. echo shar: extracting "'interp.h'" '(1550 characters)'
  1297. if test -f 'interp.h'
  1298. then
  1299.     echo shar: will not over-write existing file "'interp.h'"
  1300. else
  1301. sed 's/^    X//' << \SHAR_EOF > 'interp.h'
  1302.     X/* $Id: interp.h,v 1.1 1993/01/25 06:32:11 david Exp $
  1303.     X * interp.h -- Definitions for interp module.
  1304.     X *
  1305.     X * PUBLIC FUNCTIONS:
  1306.     X *
  1307.     X * Int_CreateInterp(interp, name) -- Create a new one of these interpretors.
  1308.     X *    Returns `struct interpInfo' or NULL if it could not be created.
  1309.     X *
  1310.     X * Int_findInterp(name) -- Finds the interpInfo matching the name.  If
  1311.     X *    none, then return NULL;
  1312.     X *
  1313.     X * Int_whoIs(interp) -- Finds the interpInfo matching a TCL interpretor.
  1314.     X *
  1315.     X * Int_delInterp(info) -- Deletes all the information associated with
  1316.     X *    one of these interpretors.  First runs the destroyHook if
  1317.     X *    there is one.
  1318.     X *
  1319.     X *
  1320.     X * $Log: interp.h,v $
  1321.     X * Revision 1.1  1993/01/25  06:32:11  david
  1322.     X * Initial revisions of the interp module, documentation, and file browser.
  1323.     X *
  1324.     X *
  1325.     X */
  1326.     X
  1327.     X#ifndef __INTERP_H__DSH__
  1328.     X#define __INTERP_H__DSH__
  1329.     X
  1330.     X
  1331.     X#ifndef isstr
  1332.     X#define isstr(s) ((s) && (s)[0])
  1333.     X#endif
  1334.     X
  1335.     X#define INT_NAME_LEN    20
  1336.     X
  1337.     Xstruct interpInfo {
  1338.     X    Tcl_Interp *interp;
  1339.     X    char        name[INT_NAME_LEN];
  1340.     X    char        parent[INT_NAME_LEN];
  1341.     X    char       *destroyHook;
  1342.     X};
  1343.     X
  1344.     Xextern void init_interp _ANSI_ARGS_((Tcl_Interp *));
  1345.     Xextern struct interpInfo *Int_CreateInterp _ANSI_ARGS_((Tcl_Interp *,
  1346. Tcl_Interp *, char *));
  1347.     Xextern struct interpInfo *Int_findInterp _ANSI_ARGS_((char *));
  1348.     Xextern struct interpInfo *Int_whoIs _ANSI_ARGS_((Tcl_Interp *));
  1349.     Xextern void Int_delInterp _ANSI_ARGS_((Tcl_Interp *));
  1350.     Xextern int Int_ExecCommand _ANSI_ARGS_((Tcl_Interp *, Tcl_Interp *, int, char
  1351. *));
  1352.     Xextern void Int_copyErrorInfo _ANSI_ARGS_((struct interpInfo *, Tcl_Interp
  1353. *));
  1354.     X
  1355.     X#endif /* __INTERP_H__DSH__ */
  1356. SHAR_EOF
  1357. if test 1550 -ne "`wc -c < 'interp.h'`"
  1358. then
  1359.     echo shar: error transmitting "'interp.h'" '(should have been 1550 characters)'
  1360. fi
  1361. fi # end of overwriting check
  1362. echo shar: extracting "'fileBrowserC.tcl'" '(8616 characters)'
  1363. if test -f 'fileBrowserC.tcl'
  1364. then
  1365.     echo shar: will not over-write existing file "'fileBrowserC.tcl'"
  1366. else
  1367. sed 's/^    X//' << \SHAR_EOF > 'fileBrowserC.tcl'
  1368.     X
  1369.     X# $Id: fileBrowserC.tcl,v 1.1 1993/01/25 06:32:14 david Exp $
  1370.     X# fileBrowserC.tcl - File Browser class definition.
  1371.     X#
  1372.     X# AUTHOR: David Herron <david@davids.mmdf.com (home)>, <david@twg.com (work)>
  1373.     X#
  1374.     X# $Log: fileBrowserC.tcl,v $
  1375.     X# Revision 1.1  1993/01/25  06:32:14  david
  1376.     X# Initial revisions of the interp module, documentation, and file browser.
  1377.     X#
  1378.     X#
  1379.     X#
  1380.     X# The file browser continually presents the contents of a particular
  1381.     X# directory, with the goal of selecting a file.  The user is able to
  1382.     X# change the current directory at will.  The current list of files can
  1383.     X# be limited with a pattern, and the pattern can be modified at any
  1384.     X# time by the user.  Once a file is selected the browser goes away,
  1385.     X# and calls the okCommand.  The cancel button calls cancelCommand, and
  1386.     X# the help button calls helpCommand.
  1387.     X#
  1388.     X# Each place where a path name is shown there are two entry
  1389.     X# boxes.  One for the path component, and the other for
  1390.     X# the file component.  Two such places are shown, one for
  1391.     X# the current directory and file pattern.  The other for
  1392.     X# the last selected file.
  1393.     X#
  1394.     X# METHODS:
  1395.     X#
  1396.     X# new
  1397.     X#
  1398.     X#    Create a new fileBrowser instance.
  1399.     X#
  1400.     X# delete
  1401.     X#
  1402.     X#    Delete a fileBrowser.
  1403.     X#
  1404.     X# MakeWidgets
  1405.     X#
  1406.     X#    Create the visual components.
  1407.     X#
  1408.     X# setDirectory dirString
  1409.     X#
  1410.     X#    Change directory to the named one.  If dirString ends in ".."
  1411.     X#    then go to the parent.
  1412.     X#
  1413.     X# changeDirectory
  1414.     X#
  1415.     X#    Changes directory to the one stored in $dirEntry.  Finds
  1416.     X#    the files matching the pattern in $patEntry.  Displays
  1417.     X#    all directories there in the directory list, and all matching
  1418.     X#    files in the file list.
  1419.     X#
  1420.     X# setPattern newpat
  1421.     X#
  1422.     X#    Sets the text in $patEntry.
  1423.     X#
  1424.     X# setFile file
  1425.     X#
  1426.     X#    Sets the selected file to be the path from the current
  1427.     X#    directory, and the file name passed in.
  1428.     X#
  1429.     X#
  1430.     X
  1431.     Xif ![interp exists FileBrowserClass] {
  1432.     X
  1433.     Xinterp new FileBrowserClass
  1434.     X
  1435.     XFileBrowserClass {
  1436.     X
  1437.     Xproc new {} {
  1438.     X    global fileb_count
  1439.     X    if ![info exists fileb_count] {set fileb_count 0}
  1440.     X    incr fileb_count
  1441.     X    set name "fileb$fileb_count"
  1442.     X    interp new $name
  1443.     X
  1444.     X    foreach cmd {    new delete MakeWidgets isModal setDirectory
  1445.     X            changeDirectory
  1446.     X            setPattern setFile rescan getDirectory
  1447.     X            getPattern getFile doubleCommand okCommand
  1448.     X            cancelCommand helpCommand 
  1449.     X        } { $name -chainCommand FileBrowserClass $cmd }
  1450.     X
  1451.     X    return $name
  1452.     X}
  1453.     X
  1454.     Xproc delete {} { exit }
  1455.     X
  1456.     Xproc MakeWidgets top {
  1457.     X    global topFrame patFrame lstFrame filFrame cmdFrame \
  1458.     X        patLabel dirEntry slashLabel patEntry \
  1459.     X        dirList dirScroll filList filScroll \
  1460.     X        filLabel pathEntry filslashLabel filEntry \
  1461.     X        okBtn canBtn travBtn hlpBtn
  1462.     X
  1463.     X    global thisInterpretor
  1464.     X
  1465.     X    set topFrame ${top}
  1466.     X    set patFrame ${top}.pat
  1467.     X    set lstFrame ${top}.lst
  1468.     X    set filFrame ${top}.fil
  1469.     X    set cmdFrame ${top}.cmd
  1470.     X
  1471.     X    MainInterp frame $topFrame
  1472.     X    MainInterp frame $patFrame
  1473.     X    MainInterp frame $lstFrame
  1474.     X    MainInterp frame $filFrame
  1475.     X    MainInterp frame $cmdFrame
  1476.     X    MainInterp pack append  $topFrame \
  1477.     X                $patFrame {top fillx} \
  1478.     X                $lstFrame {top fill expand} \
  1479.     X                $filFrame {top fillx} \
  1480.     X                $cmdFrame {top fillx}
  1481.     X
  1482.     X    set patLabel   ${patFrame}.l
  1483.     X    set dirEntry   ${patFrame}.dir
  1484.     X    set slashLabel ${patFrame}.slash
  1485.     X    set patEntry   ${patFrame}.pat
  1486.     X
  1487.     X    MainInterp label $patLabel -text "Pattern"
  1488.     X    MainInterp entry $dirEntry
  1489.     X    MainInterp label $slashLabel -text "/"
  1490.     X    MainInterp entry $patEntry
  1491.     X    MainInterp pack append  $patFrame \
  1492.     X                $patLabel   {left fillx} \
  1493.     X                $dirEntry   {left fillx expand} \
  1494.     X                $slashLabel {left fillx} \
  1495.     X                $patEntry   {left fillx expand}
  1496.     X
  1497.     X    set dirList    ${lstFrame}.dl
  1498.     X    set dirScroll    ${lstFrame}.ds
  1499.     X    set filList    ${lstFrame}.fl
  1500.     X    set filScroll    ${lstFrame}.fs
  1501.     X
  1502.     X    MainInterp scrollbar $dirScroll -command        "$dirList   yview"
  1503.     X    MainInterp listbox   $dirList     -yscrollcommand "$dirScroll set" 
  1504.     X    MainInterp scrollbar $filScroll -command        "$filList   yview"
  1505.     X    MainInterp listbox   $filList     -yscrollcommand "$filScroll set" 
  1506.     X
  1507.     X    MainInterp pack append  $lstFrame \
  1508.     X                $dirList   {left fill expand} \
  1509.     X                $dirScroll {left filly} \
  1510.     X                $filList   {left fill expand} \
  1511.     X                $filScroll {left filly}
  1512.     X
  1513.     X    set filLabel ${filFrame}.l
  1514.     X    set pathEntry ${filFrame}.p
  1515.     X    set filslashLabel ${filFrame}.sl
  1516.     X    set filEntry ${filFrame}.e
  1517.     X
  1518.     X    MainInterp label $filLabel -text "File"
  1519.     X    MainInterp entry $pathEntry
  1520.     X    MainInterp label $filslashLabel -text "/"
  1521.     X    MainInterp entry $filEntry
  1522.     X    MainInterp pack append  $filFrame \
  1523.     X                $filLabel {left fillx} \
  1524.     X                $pathEntry {left fillx expand} \
  1525.     X                $filslashLabel {left fillx} \
  1526.     X                $filEntry {left fillx expand}
  1527.     X
  1528.     X    set okBtn   ${cmdFrame}.ok
  1529.     X    set canBtn  ${cmdFrame}.can
  1530.     X    set travBtn ${cmdFrame}.trav
  1531.     X    set hlpBtn  ${cmdFrame}.hlp
  1532.     X
  1533.     X    MainInterp button $okBtn   -text "OK" \
  1534.     X                   -command "$thisInterpretor okCommand"
  1535.     X    MainInterp button $canBtn  -text "Cancel" \
  1536.     X                   -command "$thisInterpretor cancelCommand"
  1537.     X    MainInterp button $travBtn -text "Change Directory" \
  1538.     X                   -command "$thisInterpretor changeDirectory"
  1539.     X    MainInterp button $hlpBtn  -text "Help" \
  1540.     X                   -command "$thisInterpretor helpCommand"
  1541.     X
  1542.     X    MainInterp pack append  $cmdFrame \
  1543.     X                $okBtn   {left fillx expand} \
  1544.     X                $canBtn  {left fillx expand} \
  1545.     X                $travBtn {left fillx expand} \
  1546.     X                $hlpBtn  {left fillx expand}
  1547.     X
  1548.     X
  1549.     X    bind $dirEntry <Return> "$thisInterpretor {
  1550.     X            setDirectory \[$dirEntry get\]
  1551.     X            changeDirectory
  1552.     X        }
  1553.     X        $travBtn flash
  1554.     X        "
  1555.     X    bind $patEntry <Return> \
  1556.     X        "$thisInterpretor changeDirectory; $travBtn flash"
  1557.     X
  1558.     X    bind $filEntry <Return> \
  1559.     X        "$okBtn flash; update; $thisInterpretor okCommand"
  1560.     X
  1561.     X
  1562.     X    # Override the unaddorned <1> bindings so that we get
  1563.     X    # notified of any clicks.  This unfortunately means that
  1564.     X    # if the default binding were to change we'd have to be
  1565.     X    # aware of that and change it here.
  1566.     X
  1567.     X    MainInterp bind $dirList <1> "
  1568.     X        %W select from \[%W nearest %y\]
  1569.     X        $thisInterpretor setDirectory \
  1570.     X            \[%W get \[lindex \[%W curselection\] 0\]\]
  1571.     X    "
  1572.     X    MainInterp bind $dirList <Double-Button-1> "
  1573.     X        %W select from \[%W nearest %y\]
  1574.     X        $thisInterpretor setDirectory \
  1575.     X            \[%W get \[lindex \[%W curselection\] 0\]\]
  1576.     X        $thisInterpretor changeDirectory
  1577.     X        $travBtn flash
  1578.     X    "
  1579.     X    MainInterp bind $filList <1> "
  1580.     X        %W select from \[%W nearest %y\]
  1581.     X        $thisInterpretor setFile \
  1582.     X            \[%W get \[lindex \[%W curselection\] 0\]\]
  1583.     X    "
  1584.     X    MainInterp bind $filList <Double-Button-1> "
  1585.     X        %W select from \[%W nearest %y\]
  1586.     X        $thisInterpretor setFile \
  1587.     X            \[%W get \[lindex \[%W curselection\] 0\]\]
  1588.     X        $thisInterpretor okCommand
  1589.     X    "
  1590.     X
  1591.     X    return $topFrame
  1592.     X}
  1593.     X
  1594.     X# setDirectory - Set the given directory into $dirEntry.  If the last
  1595.     X# component is ".." then strip it & its parent off.  If the length of
  1596.     X# the whole thing is too short when stripping away the ".."  then assume
  1597.     X# we've gone to/through the root and change to `/'.
  1598.     X#
  1599.     X# If the first component is "." then we expand that to be [pwd].
  1600.     X#
  1601.     X# BUG(let): If the string is something weird (like `a/..') then
  1602.     X# the result is `/'.  
  1603.     X
  1604.     X
  1605.     Xproc setDirectory dir {
  1606.     X    global dirEntry
  1607.     X catch {MainInterp $dirEntry delete 0 end}
  1608.     X    set dl [split $dir "/"]
  1609.     X    if {[lindex $dl 0] == "."} {
  1610.     X        set s [split [pwd] "/"]
  1611.     X        foreach d [lrange $dl 1 end] {lappend s $d}
  1612.     X        set dl $s
  1613.     X        set dlen [llength $dl]
  1614.     X        set dir "/[join [lrange $dl 1 [expr $dlen-1]] /]"
  1615.     X    } else {
  1616.     X        set dlen [llength $dl]
  1617.     X    }
  1618.     X    if {[lindex $dl [expr $dlen-1]] == ".."} {
  1619.     X        if {$dlen <= 3} {
  1620.     X            set dir "/"
  1621.     X        } else {
  1622.     X            set dir "/[join [lrange $dl 1 [expr $dlen-3]] /]"
  1623.     X        }
  1624.     X    }
  1625.     X    MainInterp $dirEntry insert end $dir
  1626.     X}
  1627.     X
  1628.     Xproc changeDirectory {} {
  1629.     X    global dirEntry patEntry dirList filList
  1630.     X
  1631.     X    set newDir  [MainInterp $dirEntry get]
  1632.     X    set pattern [MainInterp $patEntry get]
  1633.     X
  1634.     X    if {[catch {set list [glob "${newDir}/*"]}] != 0} {
  1635.     X            set list ""
  1636.     X    }
  1637.     X    if {$newDir == "/"} {
  1638.     X        set dirs [list "/.."]
  1639.     X    } else {
  1640.     X        set dirs [list "$newDir/.."]
  1641.     X    }
  1642.     X    set files ""
  1643.     X    foreach f $list {
  1644.     X        if {[file isdirectory $f]}      {
  1645.     X            lappend dirs  $f
  1646.     X            continue
  1647.     X        }
  1648.     X        if {[string match $pattern $f]} {
  1649.     X            set fl [split $f "/"]
  1650.     X            # This should've been just [lindex $fl end]
  1651.     X            set end [expr [llength $fl]-1]
  1652.     X            lappend files [lindex $fl $end]
  1653.     X        }
  1654.     X    }
  1655.     X
  1656.     X    catch         {MainInterp $dirList delete 0 end}
  1657.     X    foreach d $dirs  {MainInterp $dirList insert end $d}
  1658.     X    catch          {MainInterp $filList delete 0 end}
  1659.     X    foreach f $files {MainInterp $filList insert end $f}
  1660.     X}
  1661.     X
  1662.     Xproc setPattern newpat {
  1663.     X    global patEntry
  1664.     X catch {MainInterp $patEntry delete 0 end}
  1665.     X    MainInterp $patEntry insert end $newpat
  1666.     X    changeDirectory
  1667.     X}
  1668.     X
  1669.     Xproc setFile file {
  1670.     X    global filEntry pathEntry dirEntry
  1671.     X catch {MainInterp $filEntry delete 0 end}
  1672.     X    MainInterp $filEntry insert end $file
  1673.     X catch {MainInterp $pathEntry delete 0 end}
  1674.     X    MainInterp $pathEntry insert end [MainInterp $dirEntry get]
  1675.     X}
  1676.     X
  1677.     X# proc getDirectory {} {
  1678.     X# }
  1679.     X
  1680.     X# proc getPattern {} {
  1681.     X# }
  1682.     X
  1683.     X# proc getFile {} {
  1684.     X# }
  1685.     X
  1686.     Xproc okCommand {} {
  1687.     X}
  1688.     X
  1689.     Xproc cancelCommand {} {
  1690.     X}
  1691.     X
  1692.     Xproc helpCommand {} {
  1693.     X}
  1694.     X
  1695.     X}
  1696.     X}
  1697.     X# END: if ![interp exists FileBrowserClass]
  1698. SHAR_EOF
  1699. if test 8616 -ne "`wc -c < 'fileBrowserC.tcl'`"
  1700. then
  1701.     echo shar: error transmitting "'fileBrowserC.tcl'" '(should have been 8616
  1702. characters)'
  1703. fi
  1704. fi # end of overwriting check
  1705. #    End of shell archive
  1706. exit 0
  1707.  
  1708. <- David Herron <david@twg.com> (work) <david@davids.mmdf.com> (home)
  1709. <-
  1710. <- "That's our advantage at Microsoft; we set the standards and we can change them."
  1711. <- Karen Hargrove of Microsoft quoted in the Feb 1993 Unix Review editorial.
  1712.