otcl.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:65k
源码类别:

通讯编程

开发平台:

Visual C++

  1. /* -*- Mode: c++ -*-
  2.  *
  3.  *  $Id: otcl.c,v 1.23 2005/09/07 04:44:18 tom_henderson Exp $
  4.  *  
  5.  *  Copyright 1993 Massachusetts Institute of Technology
  6.  * 
  7.  *  Permission to use, copy, modify, distribute, and sell this software and its
  8.  *  documentation for any purpose is hereby granted without fee, provided that
  9.  *  the above copyright notice appear in all copies and that both that
  10.  *  copyright notice and this permission notice appear in supporting
  11.  *  documentation, and that the name of M.I.T. not be used in advertising or
  12.  *  publicity pertaining to distribution of the software without specific,
  13.  *  written prior permission.  M.I.T. makes no representations about the
  14.  *  suitability of this software for any purpose.  It is provided "as is"
  15.  *  without express or implied warranty.
  16.  * 
  17.  */
  18. #include <stdlib.h>
  19. #include <string.h>
  20. #include <tclInt.h>
  21. #include <otcl.h> 
  22. /*
  23.  * compatibility definitions to bridge 7.x -> 7.5
  24.  */
  25. #if TCL_MAJOR_VERSION < 7
  26.   #error Tcl distribution is TOO OLD
  27. #elif TCL_MAJOR_VERSION == 7 && TCL_MINOR_VERSION <= 3
  28.   typedef char* Tcl_Command;
  29.   static char* Tcl_GetCommandName(Tcl_Interp* in, Tcl_Command id) {
  30.     return id;
  31.   }
  32.   static int Tcl_UpVar(Tcl_Interp* in, char* lvl, char* l, char* g, int flg){
  33.     char* args[4];
  34.     args[0] = "uplevel"; args[1] = "1";
  35.     args[2]=l; args[3]=g;
  36.     return Tcl_UpvarCmd(0, in, 4, args);
  37.   }
  38.   #define Tcl_CreateCommand(A,B,C,D,E)     
  39.     strcpy((char*)ckalloc(strlen(B)+1), B);
  40.     Tcl_CreateCommand(A,B,C,D,E)
  41. #endif
  42. #if TCL_MAJOR_VERSION <= 7
  43. #define TclIsVarUndefined(varPtr) 
  44.     ((varPtr)->flags == VAR_UNDEFINED)
  45. #endif
  46. #if TCL_MAJOR_VERSION < 8
  47. #define ObjVarTablePtr(OBJ) (&(OBJ)->variables.varTable)
  48. #define compat_Tcl_AddObjErrorInfo(a,b,c) Tcl_AddErrorInfo(a,b)
  49. #else
  50. #define ObjVarTablePtr(OBJ) ((OBJ)->variables.varTablePtr)
  51. #define compat_Tcl_AddObjErrorInfo(a,b,c) Tcl_AddObjErrorInfo(a,b,c)
  52. #endif
  53. /*
  54.  * object and class internals
  55.  */
  56. typedef struct OTclObject {
  57.   Tcl_Command id;
  58.   Tcl_Interp* teardown;
  59.   struct OTclClass* cl;
  60.   struct OTclClass* type;
  61.   Tcl_HashTable* procs;
  62.   CallFrame variables;
  63. } OTclObject;
  64. typedef struct OTclClass {
  65.   struct OTclObject object;
  66.   struct OTclClasses* super;
  67.   struct OTclClasses* sub;
  68.   int color;
  69.   struct OTclClasses* order;
  70.   struct OTclClass* parent;
  71.   Tcl_HashTable instprocs;
  72.   Tcl_HashTable instances;
  73.   Tcl_HashTable* objectdata;
  74. } OTclClass;
  75. typedef struct OTclClasses {
  76.   struct OTclClass* cl;
  77.   struct OTclClasses* next;
  78. } OTclClasses;
  79. /*
  80.  * definitions of the main otcl objects
  81.  */
  82. static Tcl_HashTable* theObjects = 0;
  83. static Tcl_HashTable* theClasses = 0;
  84. static Tcl_CmdProc* ProcInterpId = 0;
  85. /*
  86.  * error return functions
  87.  */
  88. static int
  89. OTclErrMsg(Tcl_Interp *in, char* msg, Tcl_FreeProc* type) {
  90.   Tcl_SetResult(in, msg, type);
  91.   return TCL_ERROR;
  92. }
  93. static int
  94. OTclErrArgCnt(Tcl_Interp *in, CONST84 char *cmdname, char *arglist) {
  95.   Tcl_ResetResult(in);
  96.   Tcl_AppendResult(in, "wrong # args: should be {", cmdname, 0);
  97.   if (arglist != 0) Tcl_AppendResult(in, " ", arglist, 0);
  98.   Tcl_AppendResult(in, "}", 0);
  99.   return TCL_ERROR;
  100. }
  101. static int
  102. OTclErrBadVal(Tcl_Interp *in, char *expected, CONST84 char *value) {
  103.   Tcl_ResetResult(in);
  104.   Tcl_AppendResult(in, "expected ", expected, " but got", 0);
  105.   Tcl_AppendElement(in, value);
  106.   return TCL_ERROR;
  107. }
  108. static int
  109. OTclErrType(Tcl_Interp *in, CONST84 char* nm, char* wt) {
  110.   Tcl_ResetResult(in);
  111.   Tcl_AppendResult(in,"type check failed: ",nm," is not of type ",wt,0);
  112.   return TCL_ERROR;
  113. }
  114. /*
  115.  * precedence ordering functions
  116.  */
  117. enum colors { WHITE, GRAY, BLACK };
  118. static int
  119. TopoSort(OTclClass* cl, OTclClass* base, OTclClasses* (*next)(OTclClass*)) {
  120.   OTclClasses* sl = (*next)(cl);
  121.   OTclClasses* pl;
  122.   /*
  123.    * careful to reset the color of unreported classes to
  124.    * white in case we unwind with error, and on final exit
  125.    * reset color of reported classes to white
  126.    */
  127.   cl->color = GRAY;
  128.   for (; sl != 0; sl = sl->next) {
  129.     OTclClass* sc = sl->cl;
  130.     if (sc->color==GRAY) { cl->color = WHITE; return 0; }
  131.     if (sc->color==WHITE && !TopoSort(sc, base, next)) {
  132.       cl->color=WHITE;
  133.       if (cl == base) {
  134. OTclClasses* pc = cl->order;
  135. while (pc != 0) { pc->cl->color = WHITE; pc = pc->next; }
  136.       }
  137.       return 0;
  138.     }
  139.   }
  140.   cl->color = BLACK;
  141.   pl = (OTclClasses*)ckalloc(sizeof(OTclClasses));
  142.   pl->cl = cl;
  143.   pl->next = base->order;
  144.   base->order = pl;
  145.   if (cl == base) {
  146.     OTclClasses* pc = cl->order;
  147.     while (pc != 0) { pc->cl->color = WHITE; pc = pc->next; }
  148.   }
  149.   return 1;
  150. }
  151. static void
  152. RC(OTclClasses* sl) {
  153.   while (sl != 0) {
  154.     OTclClasses* n = sl->next;
  155.     ckfree((char*)sl); sl = n;
  156.   }
  157. static OTclClasses* Super(OTclClass* cl) { return cl->super; }
  158. static OTclClasses*
  159. ComputePrecedence(OTclClass* cl) {
  160.   if (!cl->order) {
  161.     int ok = TopoSort(cl, cl, Super);
  162.     if (!ok) { RC(cl->order); cl->order = 0; }
  163.   }
  164.   return cl->order;
  165. }
  166. static OTclClasses* Sub(OTclClass* cl) { return cl->sub; }
  167. static OTclClasses*
  168. ComputeDependents(OTclClass* cl) {
  169.   if (!cl->order) {
  170.     int ok = TopoSort(cl, cl, Sub);
  171.     if (!ok) { RC(cl->order); cl->order = 0; }
  172.   }
  173.   return cl->order;
  174. }
  175. static void
  176. FlushPrecedences(OTclClass* cl) {
  177.   OTclClasses* pc;
  178.   RC(cl->order); cl->order = 0;
  179.   pc = ComputeDependents(cl);
  180.   /*
  181.    * ordering doesn't matter here - we're just using toposort
  182.    * to find all lower classes so we can flush their caches
  183.    */
  184.   if (pc) pc = pc->next;
  185.   while (pc != 0) {
  186.     RC(pc->cl->order); pc->cl->order = 0;
  187.     pc = pc->next;
  188.   }  
  189.   RC(cl->order); cl->order = 0;
  190. }
  191. static void
  192. AddInstance(OTclObject* obj, OTclClass* cl) {
  193.   obj->cl = cl;
  194.   if (cl != 0) {
  195.     int nw;
  196.     (void) Tcl_CreateHashEntry(&cl->instances, (char*)obj, &nw);
  197.   }
  198. }
  199. static int
  200. RemoveInstance(OTclObject* obj, OTclClass* cl) {
  201.   if (cl != 0) {
  202.     Tcl_HashEntry *hPtr = Tcl_FindHashEntry(&cl->instances, (char*)obj);
  203.     if (hPtr) { Tcl_DeleteHashEntry(hPtr); return 1; }
  204.   }
  205.   return 0;
  206. }
  207. /*
  208.  * superclass/subclass list maintenance
  209.  */
  210. static void
  211. AS(OTclClass* cl, OTclClass* s, OTclClasses** sl) {
  212.   OTclClasses* l = *sl;
  213.   while (l &&  l->cl != s) l = l->next;
  214.   if (!l) {
  215.     OTclClasses* sc = (OTclClasses*)ckalloc(sizeof(OTclClasses));
  216.     sc->cl = s; sc->next = *sl; *sl = sc;
  217.   }
  218. }
  219. static void
  220. AddSuper(OTclClass* cl, OTclClass* super) {
  221.   if (cl && super) {
  222.     
  223.     /*
  224.      * keep corresponding sub in step with super
  225.      */ 
  226.     AS(cl, super, &cl->super);
  227.     AS(super, cl, &super->sub);
  228.   }
  229. }
  230. static int
  231. RS(OTclClass* cl, OTclClass* s, OTclClasses** sl) {
  232.   OTclClasses* l = *sl;
  233.   if (!l) return 0;
  234.   if (l->cl == s) {
  235.     *sl = l->next;
  236.     ckfree((char*)l);
  237.     return 1;
  238.   }
  239.   while (l->next && l->next->cl != s) l = l->next;
  240.   if (l->next) {
  241.     OTclClasses* n = l->next->next;
  242.     ckfree((char*)(l->next));
  243.     l->next = n;
  244.     return 1;
  245.   }
  246.   return 0;
  247. }
  248. static int
  249. RemoveSuper(OTclClass* cl, OTclClass* super) {
  250.   /*
  251.    * keep corresponding sub in step with super
  252.    */ 
  253.   int sp = RS(cl, super, &cl->super);
  254.   int sb = RS(super, cl, &super->sub);
  255.   return (sp && sb);
  256. }
  257. /*
  258.  * internal type checking
  259.  */
  260. static OTclClass*
  261. InObject(Tcl_Interp* in) {
  262.   Tcl_HashEntry* hp = Tcl_FindHashEntry(theObjects, (char*)in);
  263.   if (hp != 0) return (OTclClass*)Tcl_GetHashValue(hp);
  264.   return 0;
  265. }
  266. static OTclClass*
  267. InClass(Tcl_Interp* in) {
  268.   Tcl_HashEntry* hp = Tcl_FindHashEntry(theClasses, (char*)in);
  269.   if (hp != 0) return (OTclClass*)Tcl_GetHashValue(hp);
  270.   return 0;
  271. }
  272. static int
  273. IsType(OTclObject* obj, OTclClass* type) {
  274.   OTclClass* t = obj ? obj->type : 0;
  275.   while (t && t!=type) t = t->parent;
  276.   return (t != 0);
  277. }
  278. /*
  279.  * methods lookup and dispatch
  280.  */
  281. static int
  282. LookupMethod(Tcl_HashTable* methods, CONST84 char* nm, Tcl_CmdProc** pr,
  283.      ClientData* cd, Tcl_CmdDeleteProc** dp) 
  284. {
  285.   Tcl_HashEntry *hPtr = Tcl_FindHashEntry(methods, nm);
  286.   if (hPtr != 0) {
  287.     Tcl_CmdInfo* co = (Tcl_CmdInfo*)Tcl_GetHashValue(hPtr);
  288.     if (pr != 0) *pr = co->proc;
  289.     if (cd != 0) *cd = co->clientData;
  290.     if (dp != 0) *dp = co->deleteProc;
  291.     return 1;
  292.   }
  293.   return 0;
  294. }
  295. static OTclClass*
  296. SearchCMethod(OTclClasses* pl, CONST84 char* nm, Tcl_CmdProc** pr,
  297.       ClientData* cd, Tcl_CmdDeleteProc** dp)
  298.   while (pl != 0) {
  299.     Tcl_HashTable* cm = &pl->cl->instprocs;
  300.     if (LookupMethod(cm, nm, pr, cd, 0) != 0) break;
  301.     pl = pl->next;
  302.   }
  303.   return pl ? pl->cl : 0;
  304. }
  305. #define OTCLSMALLARGS 8
  306. static int 
  307. OTclDispatch(ClientData cd, Tcl_Interp* in, int argc, CONST84 char* argv[]) {
  308.   OTclObject* self = (OTclObject*)cd;
  309.   Tcl_CmdProc* proc = 0;
  310.   ClientData cp = 0;
  311.   OTclClass* cl = 0;
  312.   if (argc < 2) return OTclErrArgCnt(in, argv[0], "message ?args...?");
  313.   /*
  314.    * try for local methods first, then up the class heirarchy
  315.    */ 
  316.   if (!self->procs || !LookupMethod(self->procs, argv[1], &proc, &cp, 0))
  317.     cl = SearchCMethod(ComputePrecedence(self->cl),argv[1],&proc,&cp,0);
  318.   if (proc) {
  319.     CONST84 char* sargs[OTCLSMALLARGS];
  320.     CONST84 char** args = sargs;
  321.     int result;
  322.     int i;
  323.     
  324.     /*
  325.      * permute args to be:  self self class method <rest>
  326.      * and, if method has no clientdata, pass an object pointer.
  327.      */ 
  328.     cp = (cp != 0) ? cp : cd;
  329.     if (argc+2 > OTCLSMALLARGS)
  330.       args = (CONST84 char**)ckalloc((argc+2)*sizeof(char*));
  331.     args[0] = argv[0]; 
  332.     args[1] = argv[0];
  333.     args[2] = cl ? (char *) Tcl_GetCommandName(in, cl->object.id) : "";
  334.     for (i = 1; i < argc; i++) args[i+2] = argv[i];
  335.     /*
  336.     printf("%d ", argc);
  337.     for (i = 0; i < argc; i++)
  338.       printf("%s ", argv[i]);
  339.     printf("n");
  340.     */
  341.     /*
  342.     for (i = 0; i < argc + 2; i++)
  343.       printf("%s ", args[i]);
  344.     printf("n");
  345.     */
  346.     result = (*proc)(cp, in, argc+2, (const char **) args);
  347.     /* this adds to the stack trace */
  348.     if (result == TCL_ERROR) {
  349.     char msg[150];
  350.     /* old_args2 is because args[2] was getting
  351.      * clobbered sometimes => seg fault.
  352.      * ---johnh
  353.      */
  354.     CONST84 char *old_args2 = cl ? (char *) Tcl_GetCommandName(in, cl->object.id) : argv[0];
  355.     sprintf(msg, "n    (%.40s %.40s line %d)",
  356.     old_args2, argv[1], in->errorLine);
  357.     compat_Tcl_AddObjErrorInfo(in, msg, -1);
  358.     }
  359.     if (argc+2 > OTCLSMALLARGS) { ckfree((char*)args); args = 0; }
  360.     return result;
  361.   }
  362.   /*
  363.    * back off and try unknown
  364.    */
  365.   if (!self->procs || !LookupMethod(self->procs, "unknown", &proc, &cp, 0))
  366.     cl = SearchCMethod(ComputePrecedence(self->cl),"unknown",&proc,&cp,0);
  367.   
  368.   if (proc) {
  369.     CONST84 char* sargs[OTCLSMALLARGS];
  370.     CONST84 char** args = sargs;
  371.     int result;
  372.     int i;
  373.     
  374.     /*
  375.      * permute args to be:  self self class method <rest>
  376.      * and, if method has no clientdata, pass an object pointer.
  377.      */ 
  378.     cp = (cp != 0) ? cp : cd;
  379.     if (argc+3 > OTCLSMALLARGS)
  380.       args = (CONST84 char**)ckalloc((argc+3)*sizeof(char*));
  381.     args[0] = argv[0]; 
  382.     args[1] = argv[0];
  383.     args[2] = cl ? (char *) Tcl_GetCommandName(in, cl->object.id) : "";
  384.     args[3] = "unknown";
  385.     for (i = 1; i < argc; i++) args[i+3] = argv[i];
  386.     result = (*proc)(cp, in, argc+3, (const char **) args);
  387.     if (result == TCL_ERROR) {
  388.     char msg[100];
  389.     sprintf(msg, "n    (%.30s unknown line %d)",
  390.     cl ? args[2] : argv[0], in->errorLine);
  391.     compat_Tcl_AddObjErrorInfo(in, msg, -1);
  392.     }
  393.     if (argc+3 > OTCLSMALLARGS) { ckfree((char*)args); args = 0; }
  394.     return result;
  395.   }  
  396.   /*
  397.    * and if that fails too, error out
  398.    */
  399.   Tcl_ResetResult(in);
  400.   Tcl_AppendResult(in, argv[0], ": unable to dispatch method ", argv[1], 0);
  401.   return TCL_ERROR;
  402. }
  403. /*
  404.  * autoloading
  405.  */
  406. static void
  407. AutoLoaderDP(ClientData cd) {
  408.   ckfree((char*)cd);
  409. }
  410. static int
  411. AutoLoader(ClientData cd, Tcl_Interp* in, int argc, CONST84 char*argv[]) {
  412.   /*
  413.    * cd is a script to evaluate; object context reconstructed from argv
  414.    */
  415.   OTclObject* obj = OTclGetObject(in, argv[1]);
  416.   OTclClass* cl = argv[2][0] ? OTclGetClass(in, argv[2]) : 0;
  417.   CONST84 char* clname = cl ? argv[2] : "{}"; 
  418.   Tcl_CmdProc* proc = 0;
  419.   ClientData cp = 0;
  420.   if (Tcl_Eval(in, (char*)cd) != TCL_OK) {
  421.     Tcl_AppendResult(in, " during autoloading (object=", argv[1],
  422.      ", class=", clname, ", proc=", argv[3],")", 0);
  423.     return TCL_ERROR;
  424.   }
  425.   /*
  426.    * the above eval should have displaced this procedure from the object,
  427.    * so check by looking at our old spot in the table, and if successful
  428.    * continue dispatch with the right clientdata.
  429.    */ 
  430.   if (cl)
  431.     (void) LookupMethod(&cl->instprocs, argv[3], &proc, &cp, 0);
  432.   else if (obj->procs)
  433.     (void) LookupMethod(obj->procs, argv[3], &proc, &cp, 0);
  434.   if (proc && proc != (Tcl_CmdProc *) AutoLoader) {
  435.     ClientData cdata = (cp != 0) ? cp : (ClientData)obj;
  436.     return (*proc)(cdata, in, argc, (const char **) argv);
  437.   }
  438.   
  439.   Tcl_ResetResult(in);
  440.   Tcl_AppendResult(in, "no new proc during autoloading (object=", argv[1],
  441.    ", class=", clname, ", proc=", argv[3],")", 0);
  442.   return TCL_ERROR;
  443. }
  444. int
  445. MakeAuto(Tcl_CmdInfo* proc, CONST84 char* loader) {
  446.   proc->proc = (Tcl_CmdProc *) AutoLoader;
  447.   proc->deleteProc = AutoLoaderDP;
  448.   proc->clientData = (ClientData)strcpy(ckalloc(strlen(loader)+1), loader);
  449.   return (proc->clientData != 0);
  450. }
  451. /*
  452.  * creating, installing, listing and removing procs
  453.  */
  454. static void
  455. AddMethod(Tcl_HashTable* methods, CONST84 char* nm, Tcl_CmdProc* pr,
  456.   ClientData cd, Tcl_CmdDeleteProc* dp, ClientData dd)
  457.   int nw = 0;
  458.   Tcl_HashEntry *hPtr = Tcl_CreateHashEntry(methods, nm, &nw);
  459.   Tcl_CmdInfo* co = (Tcl_CmdInfo*)ckalloc(sizeof(Tcl_CmdInfo));
  460.   co->proc = pr;
  461.   co->clientData = cd;
  462.   co->deleteProc = dp;
  463.   co->deleteData = dd;
  464.   Tcl_SetHashValue(hPtr, (ClientData)co);
  465. }
  466. static int
  467. RemoveMethod(Tcl_HashTable* methods, CONST84 char* nm, ClientData cd) { 
  468.   Tcl_HashEntry *hPtr = Tcl_FindHashEntry(methods, nm);
  469.   if (hPtr != 0) {
  470.     Tcl_CmdInfo* co = (Tcl_CmdInfo*)Tcl_GetHashValue(hPtr);
  471.     if (co->deleteProc != 0) (*co->deleteProc)(co->deleteData);
  472.     ckfree((char*)co);
  473.     Tcl_DeleteHashEntry(hPtr);
  474.     return 1;
  475.   }
  476.   return 0;
  477. }
  478. #if TCL_MAJOR_VERSION >= 8
  479. typedef struct {
  480. Tcl_Interp* interp;
  481. int procUid;
  482. } OTclDeleteProcData;
  483. static int s_ProcUid=0; 
  484. static const char s_otclProcPrefix[] = "::otcl::p";
  485. static char s_otclProcName[sizeof(s_otclProcPrefix) + 8];
  486. const char* GetProcName(int index)
  487. {
  488. sprintf(s_otclProcName, "%s%d", s_otclProcPrefix, index);
  489. return s_otclProcName;
  490. }
  491. static void
  492. OTclDeleteProc(ClientData cd)
  493. {
  494.   OTclDeleteProcData* pdpd = (OTclDeleteProcData*)cd;
  495.   /* cleanup, ignore any errors */
  496.   Tcl_Command cmd;
  497.   cmd = Tcl_FindCommand(pdpd->interp, (char*)GetProcName(pdpd->procUid),
  498. (Tcl_Namespace*)NULL, 0);
  499.   if (cmd) 
  500.   Tcl_DeleteCommandFromToken(pdpd->interp, cmd);
  501.   ckfree((char*)pdpd);
  502. }
  503. #endif  
  504. int
  505. MakeProc(Tcl_CmdInfo* proc, Tcl_Interp* in, int argc, CONST84 char* argv[]) {    
  506.   CONST84 char* name = argv[1];
  507.   CONST84 char* oargs = argv[2];
  508.   CONST84 char* nargs = (CONST84 char*)ckalloc(strlen("self class proc ")+strlen(argv[2])+1);
  509.   int ok = 0;
  510.   CONST84 char* id;
  511. #if TCL_MAJOR_VERSION >= 8
  512.   Tcl_Obj **objv;
  513.   int i;
  514.   id= (char*)GetProcName(++s_ProcUid);
  515. #else
  516.   id= "__OTclProc__";
  517. #endif
  518.   /*
  519.    * add the standard method args automatically
  520.    */
  521.   argv[1] = id;
  522.   (void)strcpy((char *)nargs, "self class proc ");
  523.   if (argv[2][0] != 0) (void) strcat((char *)nargs, argv[2]);
  524.   argv[2] = nargs;
  525. #if TCL_MAJOR_VERSION >= 8
  526.   objv = (Tcl_Obj **)ckalloc(argc * sizeof(Tcl_Obj *));
  527.   for (i = 0; i < argc; i++) {
  528.     objv[i] = Tcl_NewStringObj(argv[i], -1); /* let strlen() decide length */
  529.     Tcl_IncrRefCount(objv[i]);
  530.   }
  531.   /*
  532.    * use standard Tcl_ProcCmd to digest, and fish result out of interp
  533.    */
  534.   if (Tcl_ProcObjCmd(0, in, argc, objv) == TCL_OK) {
  535.     if (Tcl_GetCommandInfo(in, id, proc) && proc->proc == ProcInterpId) {
  536.       OTclDeleteProcData* pData =
  537.         (OTclDeleteProcData*)(ckalloc(sizeof(OTclDeleteProcData)));
  538.       pData->procUid = s_ProcUid;
  539.       pData->interp = in;
  540.       /* set the delete procedure to be OTclDeleteProc, which will
  541.        * remove the procedure, the deleteProc will be called in, for example,
  542.        * RemoveMethod, note that we are changing a copy of proc, the original
  543.        * proc structure still has the right deleteProc */
  544.       proc->deleteProc = OTclDeleteProc;
  545.       proc->deleteData = (ClientData)pData;
  546.       ok = 1;
  547.     }
  548.   }
  549.   for (i = 0; i < argc; i++)
  550.     Tcl_DecrRefCount(objv[i]);
  551.   ckfree((char *)objv);
  552.   
  553. #else /* TCL_MAJOR_VERSION < 8 */
  554.   
  555.   if (Tcl_ProcCmd(0, in, argc, argv) == TCL_OK) {
  556.     if (Tcl_GetCommandInfo(in, id, proc) && proc->proc == ProcInterpId) {
  557.       Tcl_CmdDeleteProc* dp = proc->deleteProc;
  558.       proc->deleteProc = 0;
  559.       if (Tcl_SetCommandInfo(in, id, proc))
  560.        (void)Tcl_DeleteCommand(in, id);
  561.       proc->deleteProc = dp;
  562.       ok = 1;
  563.     }
  564.   }
  565. #endif /* TCL_MAJOR_VERSION < 8 */
  566.   ckfree((char*)nargs);
  567.   argv[1] = name;
  568.   argv[2] = oargs;
  569.   
  570.   return ok;
  571. }
  572. static void
  573. ListKeys(Tcl_Interp* in, Tcl_HashTable* table, CONST84 char* pattern) {
  574.   Tcl_HashSearch hSrch;
  575.   Tcl_HashEntry* hPtr = table ? Tcl_FirstHashEntry(table, &hSrch) : 0;
  576.   Tcl_ResetResult(in);
  577.   for (; hPtr != 0; hPtr = Tcl_NextHashEntry(&hSrch)) {
  578.     char* key = Tcl_GetHashKey(table, hPtr);
  579.     if (!pattern || Tcl_StringMatch(key, pattern))
  580.       Tcl_AppendElement(in, key);
  581.   }
  582. }
  583. static void
  584. ListInstanceKeys(Tcl_Interp* in, Tcl_HashTable* table, CONST84 char* pattern) {
  585.   Tcl_HashSearch hSrch;
  586.   Tcl_HashEntry* hPtr = table ? Tcl_FirstHashEntry(table, &hSrch) : 0;
  587.   Tcl_ResetResult(in);
  588.   for (; hPtr != 0; hPtr = Tcl_NextHashEntry(&hSrch)) {
  589.     OTclObject* obj = (OTclObject*)Tcl_GetHashKey(table, hPtr);
  590.     CONST84 char* name = (char *) Tcl_GetCommandName(in, obj->id);
  591.     if (!pattern || Tcl_StringMatch(name, pattern))
  592.       Tcl_AppendElement(in, name);
  593.   }
  594. }
  595. static void
  596. ListProcKeys(Tcl_Interp* in, Tcl_HashTable* table, CONST84 char* pattern) {
  597.   Tcl_HashSearch hSrch;
  598.   Tcl_HashEntry* hPtr = table ? Tcl_FirstHashEntry(table, &hSrch) : 0;
  599.   Tcl_ResetResult(in);
  600.   for (; hPtr != 0; hPtr = Tcl_NextHashEntry(&hSrch)) {
  601.     CONST84 char* key = Tcl_GetHashKey(table, hPtr);
  602.     Tcl_CmdProc* proc = ((Tcl_CmdInfo*)Tcl_GetHashValue(hPtr))->proc;
  603.     if (pattern && !Tcl_StringMatch(key, pattern)) continue;
  604.     
  605.     /*
  606.      * also counts anything to be autoloaded as a proc
  607.      */
  608.     if (proc!=(Tcl_CmdProc *) AutoLoader && proc!=ProcInterpId) continue; 
  609.     Tcl_AppendElement(in, key);
  610.   }
  611. }
  612. static Proc*
  613. FindProc(Tcl_HashTable* table, CONST84 char* name) {
  614.   Tcl_HashEntry* hPtr = table ? Tcl_FindHashEntry(table, name) : 0;
  615.   if (hPtr) {
  616.     Tcl_CmdInfo* co = (Tcl_CmdInfo*)Tcl_GetHashValue(hPtr);
  617.     if (co->proc == ProcInterpId)
  618.       return (Proc*)co->clientData;
  619.   }
  620.   return 0;
  621. }
  622.  
  623. static int
  624. ListProcArgs(Tcl_Interp* in, Tcl_HashTable* table, CONST84 char* name) {
  625.   Proc* proc = FindProc(table, name);
  626.   if (proc) {
  627. #if TCL_MAJOR_VERSION == 7
  628.     Arg* args = proc->argPtr;
  629. #else
  630.     CompiledLocal* args = proc->firstLocalPtr;
  631. #endif
  632.     int i = 0;
  633.     /*
  634.      * skip over hidden self, class, proc args 
  635.      */
  636.     for (; args!=0 && i<3; args = args->nextPtr, i++) ;
  637.     Tcl_ResetResult(in);
  638.     while (args != 0) {
  639. #if TCL_MAJOR_VERSION >= 8
  640. /*#if TCL_RELEASE_SERIAL >= 3*/
  641. #if ((TCL_MINOR_VERSION == 0) && (TCL_RELEASE_SERIAL >= 3)) || (TCL_MINOR_VERSION > 0)
  642.     if (TclIsVarArgument(args))
  643. #else
  644.     if (args->isArg)
  645. #endif
  646. #endif
  647.     Tcl_AppendElement(in, args->name);
  648.     args = args->nextPtr;
  649.     }
  650.     return TCL_OK;
  651.   }
  652.   return OTclErrBadVal(in, "a tcl method name", name);
  653. }
  654. static int
  655. ListProcDefault(Tcl_Interp* in, Tcl_HashTable* table,
  656. CONST84 char* name, CONST84 char* arg, CONST84 char* var)
  657. {
  658.   /*
  659.    * code snarfed from tcl info default
  660.    */
  661.   Proc* proc = FindProc(table, name);
  662.   if (proc) {
  663. #if TCL_MAJOR_VERSION < 8
  664.     Arg *ap;
  665.     for (ap = proc->argPtr; ap != 0; ap = ap->nextPtr) {
  666.       if (strcmp(arg, ap->name) != 0) continue;
  667.       if (ap->defValue != 0) {
  668. if (Tcl_SetVar(in, var, ap->defValue, 0) == 0) {
  669. #else 
  670.     CompiledLocal *ap;
  671.     for (ap = proc->firstLocalPtr; ap != 0; ap = ap->nextPtr) {
  672.       if (strcmp(arg, ap->name) != 0) continue;
  673.       if (ap->defValuePtr != 0) {
  674. if (Tcl_SetVar(in, 
  675.        var, 
  676. #if TCL_MINOR_VERSION == 0
  677.        TclGetStringFromObj(ap->defValuePtr, 
  678.    (int *) NULL),
  679. #else
  680.        TclGetString(ap->defValuePtr),
  681. #endif
  682.        0) == NULL) {
  683. #endif
  684.   Tcl_ResetResult(in);
  685.   Tcl_AppendResult(in, "couldn't store default value in variable "",
  686.    var, """, (char *) 0);
  687.   return TCL_ERROR;
  688. }
  689. Tcl_SetResult(in, "1", TCL_STATIC);
  690.       } else {
  691. if (Tcl_SetVar(in, var, "", 0) == 0) {
  692.   Tcl_AppendResult(in, "couldn't store default value in variable "",
  693.    var, """, (char *) 0);
  694.   return TCL_ERROR;
  695. }
  696. Tcl_SetResult(in, "0", TCL_STATIC);
  697.       }
  698.       return TCL_OK;
  699.     }
  700.     Tcl_ResetResult(in);
  701.     Tcl_AppendResult(in, "procedure "", name,
  702.      "" doesn't have an argument "", arg, """, (char *) 0);
  703.     return TCL_ERROR;
  704.   }
  705.   return OTclErrBadVal(in, "a tcl method name", name);
  706. }
  707. static int
  708. ListProcBody(Tcl_Interp* in, Tcl_HashTable* table, CONST84 char* name) {
  709.   Proc* proc = FindProc(table, name);
  710.   if (proc) {
  711.     Tcl_ResetResult(in);
  712. #if TCL_MAJOR_VERSION< 8
  713.     Tcl_AppendResult(in, proc->command, 0);
  714. #else 
  715.     Tcl_AppendResult(in, 
  716. #if TCL_MINOR_VERSION == 0
  717.      TclGetStringFromObj(proc->bodyPtr, (int *)NULL),
  718. #else
  719.      TclGetString(proc->bodyPtr),
  720. #endif
  721.      0);
  722. #endif
  723.     return TCL_OK;
  724.   }
  725.   return OTclErrBadVal(in, "a tcl method name", name);
  726. }
  727.  
  728. /*
  729.  * object creation
  730.  */
  731. static void
  732. PrimitiveOInit(void* mem, Tcl_Interp* in, CONST84 char* name, OTclClass* cl) {
  733.   OTclObject* obj = (OTclObject*)mem;
  734.   obj->teardown = in;
  735.   AddInstance(obj, cl);
  736.   obj->type = InObject(in);
  737.   obj->procs = 0;
  738.   
  739.   /*
  740.    * fake callframe needed to interface to tcl variable
  741.    * manipulations. looks like one below global 
  742.    */
  743.   Tcl_InitHashTable(ObjVarTablePtr(obj), TCL_STRING_KEYS);
  744.   obj->variables.level = 1;
  745. #if TCL_MAJOR_VERSION < 8
  746.   obj->variables.argc = 0;
  747.   obj->variables.argv = 0;
  748. #else
  749.   obj->variables.numCompiledLocals = 0;
  750.   obj->variables.compiledLocals = 0;
  751. #endif
  752.   obj->variables.callerPtr = 0;
  753.   obj->variables.callerVarPtr = 0;
  754. #if TCL_MAJOR_VERSION >= 8
  755.   /* we need to deal with new members in CallFrame in Tcl8.0 */
  756.   obj->variables.isProcCallFrame = 1;
  757.   /* XXX: is it correct to assign global namespace here? */
  758.   obj->variables.nsPtr = ((Interp *)in)->globalNsPtr; 
  759.   obj->variables.objc = 0;
  760.   obj->variables.objv = NULL; /* we don't want byte codes for now */
  761.   obj->variables.procPtr = (Proc *) ckalloc(sizeof(Proc));
  762.   obj->variables.procPtr->iPtr = (Interp *)in;
  763.   obj->variables.procPtr->refCount = 1;
  764.   /* XXX it correct to assign global namespace here? */
  765.   obj->variables.procPtr->cmdPtr = NULL;
  766.   obj->variables.procPtr->bodyPtr = NULL;
  767.   obj->variables.procPtr->numArgs  = 0; /* actual argument count is set below. */
  768.   obj->variables.procPtr->numCompiledLocals = 0;
  769.   obj->variables.procPtr->firstLocalPtr = NULL;
  770.   obj->variables.procPtr->lastLocalPtr = NULL;
  771. #endif
  772. }
  773. static void PrimitiveODestroyNoFree(ClientData cd);
  774. static void 
  775. PrimitiveODestroy(ClientData cd) {
  776.   PrimitiveODestroyNoFree(cd);   
  777.   ckfree((char*)cd);
  778. }
  779. static void
  780. PrimitiveODestroyNoFree(ClientData cd) {
  781.   OTclObject* obj = (OTclObject*)cd;
  782.   Tcl_HashSearch hs;
  783.   Tcl_HashEntry* hp;
  784.   Tcl_HashSearch hs2;
  785.   Tcl_HashEntry* hp2;
  786.   Tcl_Interp* in;
  787.   /*
  788.    * check and latch against recurrent calls with obj->teardown
  789.    */
  790.   if (!obj || !obj->teardown) return;
  791.   in = obj->teardown; obj->teardown = 0;
  792.   /*
  793.    * call and latch user destroy with obj->id if we haven't
  794.    */
  795.   if (obj->id) {
  796.     CONST84 char* args[2] = { "", "destroy" };
  797.     Tcl_CmdInfo info;
  798.     /*
  799.      * but under 7.4p1 it is too late, so check with info
  800.      */
  801.     args[0] = (char *) Tcl_GetCommandName(in, obj->id);
  802.     if (Tcl_GetCommandInfo(in, args[0], &info))
  803.       (void) OTclDispatch(cd, in, 2, args);
  804.     obj->id = 0;
  805.   }
  806.   /*
  807.    * resume the primitive teardown for procs and variables.
  808.    * variables unset here were lost from user destroy, and
  809.    * any trace error messages will be swallowed.
  810.    */
  811.   hp = Tcl_FirstHashEntry(ObjVarTablePtr(obj), &hs);
  812.   while (hp != 0) {
  813.     for (;;) {
  814.       Var* vp = (Var*)Tcl_GetHashValue(hp);
  815.       if (!TclIsVarUndefined(vp)) break;
  816.       hp = Tcl_NextHashEntry(&hs);
  817.       if (hp == 0)
  818.       goto done;
  819.     }
  820.     if (hp != 0) {
  821.       char* name = Tcl_GetHashKey(ObjVarTablePtr(obj), hp);
  822.       (void)OTclUnsetInstVar(obj, in, name, TCL_LEAVE_ERR_MSG);
  823.     }
  824.     hp = Tcl_FirstHashEntry(ObjVarTablePtr(obj), &hs);
  825.   }
  826. done:
  827.   hp = Tcl_FirstHashEntry(ObjVarTablePtr(obj), &hs);
  828.   while (hp != 0) {
  829.    /*
  830.     * We delete the hash table below so disassociate
  831.     * each remaining (undefined) var from its hash table entry.
  832.     * (Otherwise, tcl will later try to delete
  833.     * the already-freed hash table entry.)
  834.     */
  835.     Var* vp = (Var*)Tcl_GetHashValue(hp);
  836.     vp->hPtr = 0;
  837.     hp = Tcl_NextHashEntry(&hs);
  838.   }
  839.   Tcl_DeleteHashTable(ObjVarTablePtr(obj));
  840.   hp2 = obj->procs ? Tcl_FirstHashEntry(obj->procs, &hs2) : 0; 
  841.   for (; hp2 != 0; hp2 = Tcl_NextHashEntry(&hs2)) {
  842.     Tcl_CmdInfo* co = (Tcl_CmdInfo*)Tcl_GetHashValue(hp2);
  843.     ClientData cdest = cd;
  844.     if (co->clientData != 0) cdest = co->clientData;
  845.     if (co->deleteProc != 0) (*co->deleteProc)(co->deleteData);
  846.     ckfree((char*)co);
  847.   }
  848.   if (obj->procs) {
  849.     Tcl_DeleteHashTable(obj->procs); ckfree((char*)(obj->procs));
  850.   }
  851.   (void)RemoveInstance(obj, obj->cl);
  852. #if TCL_MAJOR_VERSION >= 8
  853.   ckfree((char*)(obj->variables.procPtr));
  854.   ckfree((char*)(obj->variables.varTablePtr));
  855. #endif
  856. }
  857. static OTclObject*
  858. PrimitiveOCreate(Tcl_Interp* in, CONST84 char* name, OTclClass* cl) {
  859.   OTclObject* obj = (OTclObject*)ckalloc(sizeof(OTclObject)); 
  860. #if TCL_MAJOR_VERSION < 8
  861.   if (obj != 0) {
  862.     PrimitiveOInit(obj, in, name, cl);
  863.     obj->id = Tcl_CreateCommand(in, name, OTclDispatch, (ClientData)obj,
  864. PrimitiveODestroy);
  865.   }
  866. #else
  867.   obj->variables.varTablePtr = (Tcl_HashTable *)ckalloc(sizeof(Tcl_HashTable));
  868.   if (obj != 0)
  869.     if (obj->variables.varTablePtr != 0) {
  870.       PrimitiveOInit(obj, in, name, cl);
  871.       obj->id = Tcl_CreateCommand(in, name, (Tcl_CmdProc *) OTclDispatch, 
  872.   (ClientData)obj, PrimitiveODestroy);
  873.     } else {
  874.       ckfree((char *)obj);
  875.       obj = NULL;
  876.     }
  877. #endif
  878.   return obj;
  879. }
  880. static void
  881. PrimitiveCInit(void* mem, Tcl_Interp* in, CONST84 char* name, OTclClass* class) {
  882.   OTclObject* obj = (OTclObject*)mem;
  883.   OTclClass* cl = (OTclClass*)mem;
  884.   obj->type = InClass(in);
  885.   cl->super = 0;
  886.   cl->sub = 0;
  887.   AddSuper(cl, InObject(in));
  888.   cl->parent = InObject(in);
  889.   cl->color = WHITE;
  890.   cl->order = 0;
  891.   Tcl_InitHashTable(&cl->instprocs, TCL_STRING_KEYS);
  892.   Tcl_InitHashTable(&cl->instances, TCL_ONE_WORD_KEYS);
  893.   cl->objectdata = 0;
  894. }
  895. static void
  896. PrimitiveCDestroy(ClientData cd) {
  897.   OTclClass* cl = (OTclClass*)cd;
  898.   OTclObject* obj = (OTclObject*)cd;
  899.   Tcl_HashSearch hSrch;
  900.   Tcl_HashEntry* hPtr;
  901.   Tcl_Interp* in;
  902.   /*
  903.    * check and latch against recurrent calls with obj->teardown
  904.    */
  905.   
  906.   if (!obj || !obj->teardown) return;
  907.   in = obj->teardown; obj->teardown = 0;
  908.   /*
  909.    * call and latch user destroy with obj->id if we haven't
  910.    */
  911.   if (obj->id) {
  912.     CONST84 char* args[2] = { "", "destroy" };
  913.     Tcl_CmdInfo info;
  914.     /*
  915.      * but under 7.4p1 it is too late, so check with info
  916.      */
  917.     args[0] = (char *) Tcl_GetCommandName(in, obj->id);
  918.     if (Tcl_GetCommandInfo(in, args[0], &info))
  919.       (void) OTclDispatch(cd, in, 2, args);
  920.     obj->id = 0;
  921.   }
  922.   /*
  923.    * resume the primitive teardown for instances and instprocs
  924.    */ 
  925.   hPtr = Tcl_FirstHashEntry(&cl->instances, &hSrch);
  926.   while (hPtr) {
  927.     /*
  928.      * allow circularity for meta-classes
  929.      */
  930.     OTclObject* inst;
  931.     for (;;) {
  932.       inst = (OTclObject*)Tcl_GetHashKey(&cl->instances, hPtr);
  933.       if (inst != (OTclObject*)cl) {
  934.         CONST84 char* name = (char *) Tcl_GetCommandName(inst->teardown, inst->id);
  935. (void)Tcl_DeleteCommand(inst->teardown, name);
  936. break;
  937.       }
  938.       hPtr = Tcl_NextHashEntry(&hSrch);
  939.       if (hPtr == 0)
  940. goto done;
  941.     }
  942.     hPtr = Tcl_FirstHashEntry(&cl->instances, &hSrch);
  943.   }
  944. done:
  945.   hPtr = Tcl_FirstHashEntry(&cl->instprocs, &hSrch); 
  946.   for (; hPtr != 0; hPtr = Tcl_NextHashEntry(&hSrch)) {
  947.     /* for version 8 the instprocs are registered, so no need to delete them (?) */
  948.     Tcl_CmdInfo* co = (Tcl_CmdInfo*)Tcl_GetHashValue(hPtr);
  949.     ClientData cdest = cd;
  950.     if (co->clientData != 0) cdest = co->clientData;
  951.     if (co->deleteProc != 0) (*co->deleteProc)(co->deleteData);
  952.     ckfree((char*)co);
  953.   }
  954.   Tcl_DeleteHashTable(&cl->instprocs);
  955.   if (cl->objectdata) {
  956.     Tcl_DeleteHashTable(cl->objectdata);
  957.     ckfree((char*)(cl->objectdata)); cl->objectdata = 0;
  958.   }
  959.   /*
  960.    * flush all caches, unlink superclasses
  961.    */ 
  962.   FlushPrecedences(cl);
  963.   while (cl->super) (void)RemoveSuper(cl, cl->super->cl);
  964.   while (cl->sub) (void)RemoveSuper(cl->sub->cl, cl);
  965.   /*
  966.    * handoff the primitive teardown
  967.    */
  968.   obj->teardown = in;  
  969.   /* don't want to free the memory since we need to 
  970.    * delete the hash table later, because we want the 
  971.    * PrimitiveODestory to destory the hash entries first */
  972.   PrimitiveODestroyNoFree(cd);
  973.   Tcl_DeleteHashTable(&cl->instances);
  974.   ckfree((char*)cd);
  975. }
  976. static OTclClass*
  977. PrimitiveCCreate(Tcl_Interp* in, CONST84 char* name, OTclClass* class){
  978.   OTclClass* cl = (OTclClass*)ckalloc(sizeof(OTclClass));
  979. #if TCL_MAJOR_VERSION < 8
  980.   if (cl != 0) {
  981.     OTclObject* obj = (OTclObject*)cl;
  982.     PrimitiveOInit(obj, in, name, class);
  983.     PrimitiveCInit(cl, in, name, class);
  984.     obj->id = Tcl_CreateCommand(in, name, OTclDispatch, (ClientData)cl,
  985. PrimitiveCDestroy);
  986.   }
  987. #else
  988.   cl->object.variables.varTablePtr = (Tcl_HashTable *)ckalloc(sizeof(Tcl_HashTable));
  989.   if (cl != 0) 
  990.     if (cl->object.variables.varTablePtr != 0) {
  991.       OTclObject* obj = (OTclObject*)cl;
  992.       PrimitiveOInit(obj, in, name, class);
  993.       PrimitiveCInit(cl, in, name, class);
  994.       obj->id = Tcl_CreateCommand(in, name, (Tcl_CmdProc *) OTclDispatch, 
  995.   (ClientData)cl, PrimitiveCDestroy);
  996.     } else {
  997.       ckfree((char *)cl);
  998.       cl = NULL;
  999.     }
  1000. #endif
  1001.   return cl;
  1002. }
  1003. /*
  1004.  * object method implementations
  1005.  */
  1006. static int
  1007. OTclOAllocMethod(ClientData cd, Tcl_Interp* in, int argc, CONST84 char*argv[]) {
  1008.   OTclClass* cl = OTclAsClass(in, cd);
  1009.   OTclObject* newobj;
  1010.   int i;
  1011.   if (!cl) return OTclErrType(in, argv[0], "Class");
  1012.   if (argc < 5) return OTclErrArgCnt(in, argv[0], "alloc <obj> ?args?");
  1013.   newobj = PrimitiveOCreate(in, argv[4], cl);
  1014.   if (newobj == 0) return OTclErrMsg(in,"Object alloc failed", TCL_STATIC);
  1015.   
  1016.   /*
  1017.    * this alloc doesn't process any extra args, so return them all
  1018.    */
  1019.   Tcl_ResetResult(in);
  1020.   for (i = 5; i < argc; i++) Tcl_AppendElement(in, argv[i]);
  1021.   return TCL_OK;
  1022. }
  1023. static int
  1024. OTclOInitMethod(ClientData cd, Tcl_Interp* in, int argc, CONST84 char*argv[]) {
  1025.   OTclObject* obj = OTclAsObject(in, cd);
  1026.   int i;
  1027.   if (!obj) return OTclErrType(in, argv[0], "Object");
  1028.   if (argc < 4) return OTclErrArgCnt(in, argv[0], "init ?args?");
  1029.   if (argc & 1) return OTclErrMsg(in, "uneven number of args", TCL_STATIC);
  1030.   for (i=4; i<argc; i+=2) {
  1031.     int result;
  1032.     CONST84 char* args[3];
  1033.     args[0] = argv[0];
  1034.     args[1] = argv[i]; if (args[1][0] == '-') args[1]++;
  1035.     args[2] = argv[i+1];
  1036.     result = OTclDispatch(cd, in, 3, args);
  1037.     if (result != TCL_OK) {
  1038.       Tcl_AppendResult(in, " during {", args[0], "} {",
  1039.        args[1], "} {", args[2], "}", 0);
  1040.       return result;
  1041.     }
  1042.   }
  1043.   Tcl_ResetResult(in);
  1044.   return TCL_OK;
  1045. }
  1046. static int
  1047. OTclODestroyMethod(ClientData cd, Tcl_Interp* in, int argc, CONST84 char*argv[]) {
  1048.   OTclObject* obj = OTclAsObject(in, cd);
  1049.   Tcl_HashSearch hs;
  1050.   Tcl_HashEntry* hp;
  1051.   Tcl_Command oid;
  1052.   int result = TCL_OK;
  1053.   if (!obj) return OTclErrType(in, argv[0], "Object");
  1054.   if (argc != 4) return OTclErrArgCnt(in, argv[0], "destroy");
  1055.   /*
  1056.    * unset variables here, while it may not be too late
  1057.    * to deal with trace error messages
  1058.    */
  1059.   hp = Tcl_FirstHashEntry(ObjVarTablePtr(obj), &hs);
  1060.   while (hp != 0) {
  1061.     for (;;) {
  1062.       Var* vp = (Var*)Tcl_GetHashValue(hp);
  1063.       if (!TclIsVarUndefined(vp)) break;
  1064.       hp = Tcl_NextHashEntry(&hs);
  1065.       if (hp == 0)
  1066.         goto done;
  1067.     }
  1068.     if (hp != 0) {
  1069.       CONST84 char* name = Tcl_GetHashKey(ObjVarTablePtr(obj), hp);
  1070.       result = OTclUnsetInstVar(obj, in, name, TCL_LEAVE_ERR_MSG);
  1071.       if (result != TCL_OK) break;
  1072.     }
  1073.     hp = Tcl_FirstHashEntry(ObjVarTablePtr(obj), &hs);
  1074.   }
  1075.   if (hp != 0) return TCL_ERROR;
  1076. done:
  1077.   /*
  1078.    * latch, and call delete command if not already in progress
  1079.    */
  1080.   oid = obj->id; obj->id = 0;
  1081.   if (obj->teardown != 0) {
  1082.     CONST84 char* name = (char *) Tcl_GetCommandName(in, oid);
  1083.     return (Tcl_DeleteCommand(in, name) == 0) ? TCL_OK : TCL_ERROR;
  1084.   }
  1085.   
  1086.   Tcl_ResetResult(in);
  1087.   return TCL_OK;
  1088. }
  1089. static int
  1090. OTclOClassMethod(ClientData cd, Tcl_Interp* in, int argc, CONST84 char*argv[]) {
  1091.   OTclObject* obj = OTclAsObject(in, cd);
  1092.   OTclClass* cl;
  1093.   if (!obj) return OTclErrType(in, argv[0], "Object");
  1094.   if (argc != 5) return OTclErrArgCnt(in, argv[0], "class <class>");
  1095.   /*
  1096.    * allow a change to any class; type system enforces safety later
  1097.    */
  1098.   cl = OTclGetClass(in, argv[4]);
  1099.   if (!cl) return OTclErrBadVal(in, "a class", argv[4]);
  1100.   (void)RemoveInstance(obj, obj->cl);
  1101.   AddInstance(obj, cl);
  1102.   return TCL_OK;
  1103. }
  1104. static int OTclCInfoMethod(ClientData, Tcl_Interp*, int, CONST84 char*[]);
  1105. static int
  1106. OTclOInfoMethod(ClientData cd, Tcl_Interp* in, int argc, CONST84 char*argv[]) {
  1107.   OTclObject* obj = OTclAsObject(in, cd);
  1108.   if (!obj) return OTclErrType(in, argv[0], "Object");
  1109.   if (argc < 5) return OTclErrArgCnt(in,argv[0],"info <opt> ?args?");
  1110.   
  1111.   if (!strcmp(argv[4], "class")) {
  1112.     if (argc > 6) return OTclErrArgCnt(in,argv[0],"info class ?class?");
  1113.     if (argc == 5) {
  1114.       Tcl_SetResult(in, (char *)Tcl_GetCommandName(in, obj->cl->object.id),
  1115.     TCL_VOLATILE);
  1116.     } else {
  1117.       int result;
  1118.       CONST84 char* saved = argv[4];
  1119.       argv[4] = "superclass";
  1120.       result = OTclCInfoMethod((ClientData)obj->cl, in, argc, argv);
  1121.       argv[4] = saved;
  1122.       return result;
  1123.     }
  1124.   } else if (!strcmp(argv[4], "commands")) {
  1125.     if (argc > 6) return OTclErrArgCnt(in,argv[0],"info commands ?pat?");
  1126.     ListKeys(in, obj->procs, (argc == 6) ? argv[5] : 0);
  1127.   } else if (!strcmp(argv[4], "procs")) {
  1128.     if (argc > 6) return OTclErrArgCnt(in,argv[0],"info procs ?pat?");
  1129.     ListProcKeys(in, obj->procs, (argc == 6) ? argv[5] : 0);
  1130.   } else if (!strcmp(argv[4], "args")) {
  1131.     if (argc != 6) return OTclErrArgCnt(in,argv[0],"info args <proc>");
  1132.     return ListProcArgs(in, obj->procs, argv[5]);
  1133.   } else if (!strcmp(argv[4], "default")) {
  1134.     if (argc != 8)
  1135.       return OTclErrArgCnt(in,argv[0],"info default <proc> <arg> <var>");
  1136.     return ListProcDefault(in, obj->procs, argv[5], argv[6], argv[7]);
  1137.   } else if (!strcmp(argv[4], "body")) {
  1138.     if (argc != 6) return OTclErrArgCnt(in,argv[0],"info body <proc>");
  1139.     return ListProcBody(in, obj->procs, argv[5]);
  1140.   } else if (!strcmp(argv[4], "vars")) {
  1141.     if (argc > 6) return OTclErrArgCnt(in,argv[0],"info vars ?pat?");
  1142.     ListKeys(in, ObjVarTablePtr(obj), (argc == 6) ? argv[5] : 0);
  1143.   } else {
  1144.     return OTclErrBadVal(in, "an info option", argv[4]);
  1145.   }
  1146.   return TCL_OK;
  1147. }
  1148. static int
  1149. OTclOProcMethod(ClientData cd, Tcl_Interp* in, int argc, CONST84 char*argv[]) {
  1150.   OTclObject* obj = OTclAsObject(in, cd);
  1151.   Tcl_CmdInfo proc;
  1152.   int op;
  1153.   if (!obj) return OTclErrType(in, argv[0], "Object");
  1154.   if (argc != 7) return OTclErrArgCnt(in,argv[0],"proc name args body");
  1155.   /*
  1156.    * if the args list is "auto", the body is a script to load the proc
  1157.    */
  1158.   if (!strcmp("auto", argv[5])) op = MakeAuto(&proc, argv[6]);
  1159.   else if (argv[5][0]==0 && argv[6][0]==0) op = -1;
  1160.   else op = MakeProc(&proc,in, argc-3, argv+3);
  1161.   if (!obj->procs) {
  1162.     obj->procs = (Tcl_HashTable*)ckalloc(sizeof(Tcl_HashTable));
  1163.     Tcl_InitHashTable(obj->procs, TCL_STRING_KEYS);
  1164.   }
  1165.   (void)RemoveMethod(obj->procs, argv[4], (ClientData)obj);
  1166.   if (op == 1) AddMethod(obj->procs, argv[4], proc.proc,
  1167.  proc.clientData, proc.deleteProc, proc.deleteData);
  1168.   
  1169.   return (op != 0) ? TCL_OK : TCL_ERROR;
  1170. }
  1171. static int
  1172. OTclONextMethod(ClientData cd, Tcl_Interp* in, int argc, CONST84 char*argv[]) {
  1173.   OTclObject* obj = OTclAsObject(in, cd);
  1174.   CONST84 char* class = (char *) Tcl_GetVar(in, "class",0);
  1175.   CONST84 char* method = (char *) Tcl_GetVar(in, "proc",0);
  1176.   if (!obj) return OTclErrType(in, argv[0], "Object");
  1177.   if (argc < 4) return OTclErrArgCnt(in, argv[0], "next ?args?");
  1178.   if (!method||!class) return OTclErrMsg(in,"no executing proc", TCL_STATIC);
  1179.   argv[2] = class;
  1180.   argv[3] = method;
  1181.   return OTclNextMethod(obj, in, argc, argv);
  1182. }
  1183. static int
  1184. OTclOSetMethod(ClientData cd, Tcl_Interp* in, int argc, CONST84 char*argv[]) {
  1185.   OTclObject* obj = OTclAsObject(in, cd);
  1186.   CONST84 char* result;
  1187.   if (!obj) return OTclErrType(in, argv[0], "Object");
  1188.   if (argc<5 || argc>6) return OTclErrArgCnt(in, argv[0], "set var ?value?");
  1189.   if (argc == 6)
  1190.     result = OTclSetInstVar(obj, in, argv[4], argv[5], TCL_LEAVE_ERR_MSG);
  1191.   else
  1192.     result = OTclGetInstVar(obj, in, argv[4], TCL_LEAVE_ERR_MSG);
  1193.   if (result != 0) Tcl_SetResult(in, (char *)result, TCL_VOLATILE); 
  1194.   return (result != 0) ? TCL_OK : TCL_ERROR;
  1195. }
  1196. static int
  1197. OTclOUnsetMethod(ClientData cd, Tcl_Interp* in, int argc, CONST84 char*argv[]) {
  1198.   OTclObject* obj = OTclAsObject(in, cd);
  1199.   int result = TCL_ERROR;
  1200.   int i;
  1201.   if (!obj) return OTclErrType(in, argv[0], "Object");
  1202.   if (argc < 5) return OTclErrArgCnt(in, argv[0], "unset ?vars?");
  1203.   for (i=4; i<argc; i++) {
  1204.     result = OTclUnsetInstVar(obj, in, argv[i], TCL_LEAVE_ERR_MSG);
  1205.     if (result != TCL_OK) break;
  1206.   }
  1207.   return result;
  1208. }
  1209. /*
  1210.  * This (fairly low-level) routine is exported to allow tclcl
  1211.  * to avoid generating/evaling tcl code to do its instvar.
  1212.  */
  1213. int
  1214. OTclOInstVarOne(OTclObject* obj, Tcl_Interp *in, char *frameName, CONST84 char *varName, CONST84 char *localName, int flags)
  1215. {
  1216.   Interp* iPtr = (Interp*)in;
  1217.   int result = TCL_ERROR;
  1218.   /*
  1219.    * Fake things as if the caller's stack frame is just over
  1220.    * the object, then use UpVar to suck the object's variable
  1221.    * into the caller.
  1222.    *
  1223.    * Patched for global instvar by Orion Hodson <O.Hodson@cs.ucl.ac.uk>
  1224.    */
  1225.   if (iPtr->varFramePtr) {
  1226.    CallFrame* saved = iPtr->varFramePtr->callerVarPtr;
  1227.    int level = iPtr->varFramePtr->level;
  1228.   iPtr->varFramePtr->callerVarPtr = &obj->variables;
  1229.    iPtr->varFramePtr->level = obj->variables.level+1;
  1230.    result = Tcl_UpVar(in, frameName, varName, localName, flags);
  1231.    iPtr->varFramePtr->callerVarPtr = saved;
  1232.    iPtr->varFramePtr->level = level;
  1233.   } else {
  1234.    Tcl_SetResult(in, "no instvar in global :: scope", TCL_STATIC);
  1235.   }
  1236.   return result;
  1237. }
  1238. static int
  1239. OTclOInstVarMethod(ClientData cd, Tcl_Interp* in, int argc, CONST84 char*argv[])
  1240. {
  1241.   OTclObject* obj = OTclAsObject(in, cd);
  1242.   int i;
  1243.   int result = TCL_ERROR;
  1244.   if (!obj) return OTclErrType(in, argv[0], "Object");
  1245.   if (argc < 5) return OTclErrArgCnt(in, argv[0], "instvar ?vars?");
  1246.   for (i=4; i<argc; i++) {
  1247.     int ac;
  1248.     CONST84 char **av;
  1249.     result = Tcl_SplitList(in, argv[i], &ac, (const char ***) &av);
  1250.     if (result != TCL_OK) break;
  1251.     if (ac == 1) {
  1252.       result = OTclOInstVarOne(obj, in, "1", av[0], av[0], 0);
  1253.     } else if (ac == 2) {
  1254.       result = OTclOInstVarOne(obj, in, "1", av[0], av[1], 0);
  1255.     } else {
  1256.       result = TCL_ERROR;
  1257.       Tcl_ResetResult(in);
  1258.       Tcl_AppendResult(in, "expected ?inst/local? or ?inst? ?local? but got ",
  1259.        argv[i]);
  1260.     }
  1261.     ckfree((char*)av);
  1262.     if (result != TCL_OK) break;
  1263.   }
  1264.   return result;
  1265. }
  1266. /*
  1267.  * class method implementations
  1268.  */
  1269. static int
  1270. OTclCAllocMethod(ClientData cd, Tcl_Interp* in, int argc, CONST84 char*argv[]) {
  1271.   OTclClass* cl = OTclAsClass(in, cd);
  1272.   OTclClass* newcl;
  1273.   int i;
  1274.   if (!cl) return OTclErrType(in, argv[0], "Class");
  1275.   if (argc < 5) return OTclErrArgCnt(in, argv[0], "alloc <cl> ?args?");
  1276.   newcl = PrimitiveCCreate(in, argv[4], cl);
  1277.   if (newcl == 0) return OTclErrMsg(in,"Class alloc failed", TCL_STATIC);
  1278.   /*
  1279.    * this alloc doesn't process any extra args, so return them all
  1280.    */
  1281.   Tcl_ResetResult(in);
  1282.   for (i = 5; i < argc; i++) Tcl_AppendElement(in, argv[i]);
  1283.   return TCL_OK;
  1284. }
  1285. static int
  1286. OTclCCreateMethod(ClientData cd, Tcl_Interp* in, int argc, CONST84 char*argv[]) {
  1287.   OTclClass* cl = OTclAsClass(in, cd);
  1288.   OTclObject* obj;
  1289.   Tcl_CmdProc* proc = 0;
  1290.   ClientData cp = 0;
  1291.   OTclClasses* pl;
  1292.   CONST84 char* args[4];
  1293.   int result;
  1294.   int i;
  1295.   if (!cl) return OTclErrType(in, argv[0], "Class");
  1296.   if (argc < 5) return OTclErrArgCnt(in, argv[0], "create <obj> ?args?");
  1297.   for (pl = ComputePrecedence(cl); pl != 0; pl = pl->next) {
  1298.     Tcl_HashTable* procs = pl->cl->object.procs;
  1299.     if (procs && LookupMethod(procs,"alloc",&proc,&cp,0)) break;
  1300.   }
  1301.   if (!pl) return OTclErrMsg(in, "no reachable alloc", TCL_STATIC);
  1302.   
  1303.   for (i=0; i<4; i++) args[i] = argv[i];
  1304.   argv[0] = (char *) Tcl_GetCommandName(in, pl->cl->object.id);
  1305.   argv[1] = argv[0];
  1306.   argv[2] = "";
  1307.   argv[3] = "alloc";
  1308.   cp = (cp != 0) ? cp : (ClientData)pl->cl;
  1309.   result = (*proc)(cp, in, argc, (const char **) argv);
  1310.   for (i=0; i<4; i++) argv[i] = args[i];
  1311.   if (result != TCL_OK) return result;
  1312.   obj = OTclGetObject(in, argv[4]);
  1313.   if (obj == 0) 
  1314.   return OTclErrMsg(in, "couldn't find result of alloc", TCL_STATIC);
  1315.   (void)RemoveInstance(obj, obj->cl);
  1316.   AddInstance(obj, cl);
  1317.   result = Tcl_VarEval(in, argv[4], " init ", in->result, 0);
  1318.   if (result != TCL_OK) return result;
  1319.   Tcl_SetResult(in, (char *)argv[4], TCL_VOLATILE);
  1320.   return TCL_OK;
  1321. }
  1322. static int
  1323. OTclCSuperClassMethod(ClientData cd, Tcl_Interp* in, int argc, CONST84 char*argv[]) {
  1324.   OTclClass* cl = OTclAsClass(in, cd);
  1325.   OTclClasses* osl = 0;
  1326.   int ac = 0;
  1327.   CONST84 char** av = 0;
  1328.   OTclClass** scl = 0;
  1329.   int reversed = 0;
  1330.   int i, j;
  1331.   if (!cl) return OTclErrType(in, argv[0], "Class");
  1332.   if (argc != 5) return OTclErrArgCnt(in, argv[0], "superclass <classes>");
  1333.   if (Tcl_SplitList(in, argv[4], &ac, (const char ***) &av) != TCL_OK)
  1334.     return TCL_ERROR;
  1335.   scl = (OTclClass**)ckalloc(ac*sizeof(OTclClass*));
  1336.   for (i = 0; i < ac; i++) {
  1337.     scl[i] = OTclGetClass(in, av[i]);
  1338.     if (!scl[i]) {
  1339.       /*
  1340.        * try to force autoloading if we can't resolve a class name
  1341.        */
  1342.       int loaded = 0;
  1343.       char* args = (char*)ckalloc(strlen("auto_load ")+strlen(av[i])+1);
  1344.       (void)strcpy(args, "auto_load ");
  1345.       (void) strcat(args, av[i]);
  1346.       if (Tcl_Eval(in, args) == TCL_OK) {
  1347. scl[i] = OTclGetClass(in, av[i]);
  1348. loaded = (scl[i] != 0);
  1349.       }
  1350.       ckfree(args);
  1351.       if (!loaded) {
  1352. ckfree((char*)av);
  1353. ckfree((char*)scl);
  1354. return OTclErrBadVal(in, "a list of classes", argv[4]);
  1355.       }
  1356.     }
  1357.   }
  1358.   /*
  1359.    * check that superclasses don't precede their classes
  1360.    */
  1361.   for (i = 0; i < ac; i++) {
  1362.     if (reversed != 0) break;
  1363.     for (j = i+1; j < ac; j++) {
  1364.       OTclClasses* dl = ComputePrecedence(scl[j]);
  1365.       if (reversed != 0) break;
  1366.       while (dl != 0) {
  1367. if (dl->cl == scl[i]) break;
  1368. dl = dl->next;
  1369.       }
  1370.       if (dl != 0) reversed = 1;
  1371.     }
  1372.   }
  1373.   
  1374.   if (reversed != 0) {
  1375.     ckfree((char*)av);
  1376.     ckfree((char*)scl);
  1377.     return OTclErrBadVal(in, "classes in dependence order", argv[4]);
  1378.   }
  1379.   
  1380.   while (cl->super != 0) {
  1381.     
  1382.     /*
  1383.      * build up an old superclass list in case we need to revert
  1384.      */ 
  1385.     OTclClass* sc = cl->super->cl;
  1386.     OTclClasses* l = osl;
  1387.     osl = (OTclClasses*)ckalloc(sizeof(OTclClasses));
  1388.     osl->cl = sc;
  1389.     osl->next = l;
  1390.     (void)RemoveSuper(cl, cl->super->cl);
  1391.   }
  1392.   for (i = 0; i < ac; i++)
  1393.     AddSuper(cl, scl[i]);
  1394.   ckfree((char*)av);
  1395.   ckfree((char*)scl);
  1396.   FlushPrecedences(cl);
  1397.   
  1398.   if (!ComputePrecedence(cl)) {
  1399.     /*
  1400.      * cycle in the superclass graph, backtrack
  1401.      */ 
  1402.     OTclClasses* l;
  1403.     while (cl->super != 0) (void)RemoveSuper(cl, cl->super->cl);
  1404.     for (l = osl; l != 0; l = l->next) AddSuper(cl, l->cl);
  1405.     RC(osl);
  1406.     return OTclErrBadVal(in, "a cycle-free graph", argv[4]);
  1407.   }
  1408.   RC(osl);
  1409.   Tcl_ResetResult(in);
  1410.   return TCL_OK;
  1411. }
  1412. static int
  1413. OTclCInfoMethod(ClientData cd, Tcl_Interp* in, int argc, CONST84 char*argv[]) {
  1414.   OTclClass* cl = OTclAsClass(in, cd);
  1415.   if (!cl) return OTclErrType(in, argv[0], "Class");
  1416.   if (argc < 5) return OTclErrArgCnt(in,argv[0],"info <opt> ?args?");
  1417.   if (!strcmp(argv[4], "superclass")) {
  1418.     if (argc > 6) return OTclErrArgCnt(in,argv[0],"info superclass ?class?");
  1419.     if (argc == 5) {
  1420.       OTclClasses* sl = cl->super;
  1421.       OTclClasses* sc = 0;
  1422.       
  1423.       /*
  1424.        * reverse the list to obtain presentation order
  1425.        */ 
  1426.       
  1427.       Tcl_ResetResult(in);
  1428.       while (sc != sl) {
  1429. OTclClasses* nl = sl;
  1430. while (nl->next != sc) nl = nl->next;
  1431. Tcl_AppendElement(in, Tcl_GetCommandName(in, nl->cl->object.id));
  1432. sc = nl;
  1433.       }
  1434.     } else {
  1435.       OTclClass* isc = OTclGetClass(in, argv[5]);
  1436.       OTclClasses* pl;
  1437.       if (isc == 0) return OTclErrBadVal(in, "a class", argv[5]);
  1438.       pl = ComputePrecedence(cl);
  1439.       
  1440.       /*
  1441.        * search precedence to see if we're related or not
  1442.        */
  1443.       while (pl != 0) {
  1444. if (pl->cl == isc) {
  1445.   Tcl_SetResult(in, "1", TCL_STATIC);
  1446.   break;
  1447. }
  1448. pl = pl->next;
  1449.       }
  1450.       if (pl == 0) Tcl_SetResult(in, "0", TCL_STATIC);
  1451.     }
  1452.   } else if (!strcmp(argv[4], "subclass")) {
  1453.     if (argc > 6) return OTclErrArgCnt(in,argv[0],"info subclass ?class?");
  1454.     if (argc == 5) {
  1455.       OTclClasses* sl = cl->sub;
  1456.       OTclClasses* sc = 0;
  1457.       
  1458.       /*
  1459.        * order unimportant
  1460.        */ 
  1461.       
  1462.       Tcl_ResetResult(in);
  1463.       for (sc = sl; sc != 0; sc = sc->next)
  1464. Tcl_AppendElement(in, Tcl_GetCommandName(in, sc->cl->object.id));
  1465.     } else {
  1466.       OTclClass* isc = OTclGetClass(in, argv[5]);
  1467.       OTclClasses* pl;
  1468.       OTclClasses* saved;
  1469.       if (isc == 0) return OTclErrBadVal(in, "a class", argv[5]);
  1470.       saved = cl->order; cl->order = 0;
  1471.       pl = ComputeDependents(cl);
  1472.       
  1473.       /*
  1474.        * search precedence to see if we're related or not
  1475.        */
  1476.       while (pl != 0) {
  1477. if (pl->cl == isc) {
  1478.   Tcl_SetResult(in, "1", TCL_STATIC);
  1479.   break;
  1480. }
  1481. pl = pl->next;
  1482.       }
  1483.       if (pl == 0) Tcl_SetResult(in, "0", TCL_STATIC);
  1484.       RC(cl->order); cl->order = saved;
  1485.     }
  1486.   } else if (!strcmp(argv[4], "heritage")) {
  1487.     OTclClasses* pl = ComputePrecedence(cl);
  1488.     CONST84 char* pattern = (argc == 6) ? argv[5] : 0;
  1489.     if (argc > 6) return OTclErrArgCnt(in,argv[0],"info heritage ?pat?");
  1490.     if (pl) pl = pl->next;
  1491.     Tcl_ResetResult(in);
  1492.     for (; pl != 0; pl = pl->next) {
  1493.       CONST84 char* name = (char *) Tcl_GetCommandName(in, pl->cl->object.id);
  1494.       if (pattern && !Tcl_StringMatch(name, pattern)) continue;
  1495.       Tcl_AppendElement(in, name);
  1496.     }
  1497.   } else if (!strcmp(argv[4], "instances")) {
  1498.     if (argc > 6) return OTclErrArgCnt(in,argv[0],"info instances ?pat?");
  1499.     ListInstanceKeys(in, &cl->instances, (argc == 6) ? argv[5] : 0);
  1500.   } else if (!strcmp(argv[4], "instcommands")) {
  1501.     if (argc > 6) return OTclErrArgCnt(in,argv[0],"info instcommands ?pat?");
  1502.     ListKeys(in, &cl->instprocs, (argc == 6) ? argv[5] : 0);
  1503.   } else if (!strcmp(argv[4], "instprocs")) {
  1504.     if (argc > 6) return OTclErrArgCnt(in,argv[0],"info instprocs ?pat?");
  1505.     ListProcKeys(in, &cl->instprocs, (argc == 6) ? argv[5] : 0);
  1506.   } else if (!strcmp(argv[4], "instargs")) {
  1507.     if (argc != 6) return OTclErrArgCnt(in,argv[0],"info instargs <instproc>");
  1508.     return ListProcArgs(in, &cl->instprocs, argv[5]);
  1509.   } else if (!strcmp(argv[4], "instdefault")) {
  1510.     if (argc != 8)
  1511.       return OTclErrArgCnt(in,argv[0],
  1512.    "info instdefault <instproc> <arg> <var>");
  1513.     return ListProcDefault(in, &cl->instprocs, argv[5], argv[6], argv[7]);
  1514.   } else if (!strcmp(argv[4], "instbody")) {
  1515.     if (argc != 6) return OTclErrArgCnt(in,argv[0],"info instbody <instproc>");
  1516.     return ListProcBody(in, &cl->instprocs, argv[5]);
  1517.   } else {
  1518.     return OTclOInfoMethod(cd, in, argc, argv);
  1519.   }
  1520.   return TCL_OK;
  1521. }
  1522. static int
  1523. OTclCInstProcMethod(ClientData cd, Tcl_Interp* in, int argc, CONST84 char*argv[]) {
  1524.   OTclClass* cl = OTclAsClass(in, cd);
  1525.   Tcl_CmdInfo proc;
  1526.   int op;
  1527.   if (!cl) return OTclErrType(in, argv[0], "Class");
  1528.   if (argc != 7) return OTclErrArgCnt(in,argv[0],"instproc name args body");
  1529.   
  1530.   /*
  1531.    * if the args list is "auto", the body is a script to load the proc
  1532.    */
  1533.   if (!strcmp("auto", argv[5])) op = MakeAuto(&proc, argv[6]);
  1534.   else if (argv[5][0]==0 && argv[6][0]==0) op = -1;
  1535.   else op = MakeProc(&proc,in, argc-3, argv+3);
  1536.   (void)RemoveMethod(&cl->instprocs, argv[4], (ClientData)cl);
  1537.   if (op == 1) AddMethod(&cl->instprocs, argv[4], proc.proc,
  1538.  proc.clientData, proc.deleteProc, proc.deleteData);
  1539.   
  1540.   return (op != 0) ? TCL_OK : TCL_ERROR;
  1541. }
  1542. /*
  1543.  * C interface routines for manipulating objects and classes
  1544.  */
  1545. extern OTclObject*
  1546. OTclAsObject(Tcl_Interp* in, ClientData cd) {
  1547.   OTclObject* obj = (OTclObject*)cd;
  1548.   return IsType(obj, InObject(in)) ? obj : 0;
  1549. }
  1550. extern OTclClass*
  1551. OTclAsClass(Tcl_Interp* in, ClientData cd) {
  1552.   OTclClass* cl = (OTclClass*)cd;
  1553.   return IsType((OTclObject*)cl, InClass(in)) ? cl : 0;
  1554. }
  1555. extern OTclObject*
  1556. OTclGetObject(Tcl_Interp* in, CONST84 char* name) {
  1557.   Tcl_CmdInfo info;
  1558.   OTclObject* obj = 0;
  1559.   if (Tcl_GetCommandInfo(in, name, &info))
  1560.     if (info.proc == (Tcl_CmdProc *) OTclDispatch)
  1561.       obj = OTclAsObject(in, info.clientData);
  1562.   return obj;
  1563. }
  1564. extern OTclClass*
  1565. OTclGetClass(Tcl_Interp* in, CONST84 char* name) {
  1566.   Tcl_CmdInfo info;
  1567.   OTclClass* cl = 0;
  1568.   if (Tcl_GetCommandInfo(in, name, &info))
  1569.     if (info.proc == (Tcl_CmdProc *) OTclDispatch)
  1570.       cl = OTclAsClass(in, info.clientData);
  1571.   return cl;
  1572. }
  1573. extern OTclObject*
  1574. OTclCreateObject(Tcl_Interp* in, CONST84 char* name, OTclClass* cl) {
  1575.   CONST84 char* args[3];
  1576.   args[0] = (char *) Tcl_GetCommandName(in, cl->object.id);
  1577.   args[1] = "create";
  1578.   args[2] = name;
  1579.   if (OTclDispatch((ClientData)cl,in,3,args) != TCL_OK) return 0;
  1580.   return OTclGetObject(in, name);
  1581. }
  1582. extern OTclClass*
  1583. OTclCreateClass(Tcl_Interp* in, CONST84 char* name, OTclClass* cl){
  1584.   CONST84 char* args[3];
  1585.   args[0] = (char *) Tcl_GetCommandName(in, cl->object.id);
  1586.   args[1] = "create";
  1587.   args[2] = name;
  1588.   if (OTclDispatch((ClientData)cl,in,3,args) != TCL_OK) return 0;
  1589.   return OTclGetClass(in, name);
  1590. }
  1591. extern int
  1592. OTclDeleteObject(Tcl_Interp* in, OTclObject* obj) {
  1593.   CONST84 char* args[2];
  1594.   args[0] = (char *) Tcl_GetCommandName(in, obj->id);
  1595.   args[1] = "destroy";
  1596.   return OTclDispatch((ClientData)obj, in, 2, args);
  1597. }
  1598. extern int
  1599. OTclDeleteClass(Tcl_Interp* in, OTclClass* cl) {
  1600.   CONST84 char* args[2];
  1601.   args[0] = (char *) Tcl_GetCommandName(in, cl->object.id);
  1602.   args[1] = "destroy";
  1603.   return OTclDispatch((ClientData)cl, in, 2, args);
  1604. }
  1605. extern void
  1606. OTclAddPMethod(OTclObject* obj, char* nm, Tcl_CmdProc* proc,
  1607.        ClientData cd, Tcl_CmdDeleteProc* dp)
  1608. {
  1609.   if (obj->procs)
  1610.     (void)RemoveMethod(obj->procs, nm, (ClientData)obj);
  1611.   else {
  1612.     obj->procs = (Tcl_HashTable*)ckalloc(sizeof(Tcl_HashTable));
  1613.     Tcl_InitHashTable(obj->procs, TCL_STRING_KEYS);
  1614.   }
  1615.   AddMethod(obj->procs, nm, proc, cd, dp, cd);
  1616. }
  1617. extern void
  1618. OTclAddIMethod(OTclClass* cl, char* nm, Tcl_CmdProc* proc,
  1619.        ClientData cd, Tcl_CmdDeleteProc* dp)
  1620. {
  1621.   (void)RemoveMethod(&cl->instprocs, nm, (ClientData)cl);
  1622.   AddMethod(&cl->instprocs, nm, proc, cd, dp, cd);
  1623. }
  1624. extern int
  1625. OTclRemovePMethod(OTclObject* obj, char* nm) {
  1626.   if (obj->procs) return RemoveMethod(obj->procs, nm, (ClientData)obj);
  1627.   else return 0;
  1628. }
  1629. extern int
  1630. OTclRemoveIMethod(OTclClass* cl, char* nm) {
  1631.   return RemoveMethod(&cl->instprocs, nm, (ClientData)cl);
  1632. }
  1633. extern int
  1634. OTclNextMethod(OTclObject* obj, Tcl_Interp* in, int argc, CONST84 char*argv[]) {
  1635.   OTclClass* cl = 0;
  1636.   OTclClass* ncl;
  1637.   OTclClasses* pl;
  1638.   Tcl_CmdProc* proc = 0;
  1639.   ClientData cp = 0;
  1640.   CONST84 char* class = argv[2];
  1641.   int result = TCL_OK;
  1642.   if (class[0]){
  1643.     cl = OTclGetClass(in, class);
  1644.     if (!cl) return OTclErrBadVal(in, "a class", class);
  1645.   }  
  1646.   /*
  1647.    * if we are already in the precedence ordering, then advance
  1648.    * past our last point; otherwise (if cl==0) start from the start
  1649.    */
  1650.   pl = ComputePrecedence(obj->cl);
  1651.   while (pl && cl) {
  1652.     if (pl->cl == cl) cl = 0;
  1653.     pl = pl->next;
  1654.   }
  1655.   /*
  1656.    * search for a further class method and patch args before launching.
  1657.    * if no further method, return without error.
  1658.    */
  1659.   ncl = SearchCMethod(pl, argv[3], &proc, &cp, 0);  
  1660.   if (proc != 0) {
  1661.     cp = (cp != 0) ? cp : (ClientData)obj;
  1662.     argv[2] = (char *) Tcl_GetCommandName(in, ncl->object.id);
  1663.     result = (*proc)(cp, in, argc, (const char **) argv);
  1664.     argv[2] = class;
  1665.   }
  1666.   return result;
  1667. }
  1668. extern CONST84_RETURN char*
  1669. OTclSetInstVar(OTclObject* obj,Tcl_Interp* in, 
  1670.        CONST84 char* name, CONST84 char* value,int flgs){
  1671.   Interp* iPtr = (Interp*)in;
  1672.   CallFrame* saved = iPtr->varFramePtr;
  1673.   CONST84 char* result;
  1674.   iPtr->varFramePtr = &obj->variables;
  1675.   result = (char *) Tcl_SetVar(in, name, value, flgs);
  1676.   iPtr->varFramePtr = saved;
  1677.   return result;
  1678. }
  1679. extern CONST84_RETURN char*
  1680. OTclGetInstVar(OTclObject* obj, Tcl_Interp* in, CONST84 char* name, int flgs){
  1681.   Interp* iPtr = (Interp*)in;
  1682.   CallFrame* saved = iPtr->varFramePtr;
  1683.   CONST84 char* result;
  1684.   iPtr->varFramePtr = &obj->variables;
  1685.   result = (char *) Tcl_GetVar(in, name, flgs);
  1686.   iPtr->varFramePtr = saved;
  1687.   return result;
  1688. }
  1689. extern int
  1690. OTclUnsetInstVar(OTclObject* obj, Tcl_Interp* in, CONST84 char* name, int flgs) {
  1691.   Interp* iPtr = (Interp*)in;
  1692.   CallFrame* saved = iPtr->varFramePtr;
  1693.   int result;
  1694.   iPtr->varFramePtr = &obj->variables;
  1695.   result = Tcl_UnsetVar(in, name, flgs);
  1696.   iPtr->varFramePtr = saved;
  1697.   return result;
  1698. }
  1699. extern void
  1700. OTclSetObjectData(OTclObject* obj, OTclClass* cl, ClientData data) {
  1701.   Tcl_HashEntry *hPtr;
  1702.   int nw;
  1703.   if (!cl->objectdata) {
  1704.     cl->objectdata = (Tcl_HashTable*)ckalloc(sizeof(Tcl_HashTable));
  1705.     Tcl_InitHashTable(cl->objectdata, TCL_ONE_WORD_KEYS);
  1706.   }
  1707.   hPtr = Tcl_CreateHashEntry(cl->objectdata, (char*)obj, &nw);
  1708.   Tcl_SetHashValue(hPtr, data);
  1709. }
  1710. extern int
  1711. OTclGetObjectData(OTclObject* obj, OTclClass* cl, ClientData* data) {
  1712.   Tcl_HashEntry *hPtr;
  1713.   if (!cl->objectdata) return 0;
  1714.   hPtr = Tcl_FindHashEntry(cl->objectdata, (char*)obj);
  1715.   if (data) *data = hPtr ? Tcl_GetHashValue(hPtr) : 0;
  1716.   return (hPtr != 0);
  1717. }
  1718. extern int
  1719. OTclUnsetObjectData(OTclObject* obj, OTclClass* cl) {
  1720.   Tcl_HashEntry *hPtr;
  1721.   if (!cl->objectdata) return 0;
  1722.   hPtr = Tcl_FindHashEntry(cl->objectdata, (char*)obj);
  1723.   if (hPtr) Tcl_DeleteHashEntry(hPtr);
  1724.   return (hPtr != 0);
  1725. }
  1726. /*
  1727.  * Tcl extension initialization routine
  1728.  */
  1729. #define MAXTCLPROC 4096
  1730. extern int
  1731. Otcl_Init(Tcl_Interp* in) {
  1732.   OTclClass* theobj = 0;
  1733.   OTclClass* thecls = 0;
  1734.   Tcl_HashEntry* hp1;
  1735.   Tcl_HashEntry* hp2;
  1736.   int nw1;
  1737.   int nw2;
  1738.   char tm[MAXTCLPROC];
  1739. #if TCL_MAJOR_VERSION >= 8
  1740.   Tcl_Namespace *namespacePtr;
  1741. #endif
  1742.   
  1743.   /*
  1744.    * discover Tcl's hidden proc interpreter
  1745.    */
  1746.   
  1747.   if (ProcInterpId == 0) {
  1748.     char* args[4];
  1749. #if TCL_MAJOR_VERSION >= 8
  1750.     int i;
  1751.     int res = 0;
  1752.     Tcl_Obj* objv[4];
  1753. #endif
  1754.     args[0]="proc"; args[1]="_fake_"; args[2]=""; args[3]="return";
  1755. #if TCL_MAJOR_VERSION < 8
  1756.     if (Tcl_ProcCmd(0, in, 4, args) == TCL_OK) {
  1757.       Tcl_CmdInfo info;
  1758.       if (Tcl_GetCommandInfo(in, args[1], &info)) {
  1759. ProcInterpId = info.proc;
  1760. (void)Tcl_DeleteCommand(in, args[1]);
  1761.       } else return OTclErrMsg(in, "proc failed", TCL_STATIC);
  1762.     } else return TCL_ERROR;
  1763. #else /*TCL_MAJOR_VERSION >= 8*/
  1764.     for (i = 0; i < 4; i++) {
  1765.       objv[i] = Tcl_NewStringObj(args[i], -1);
  1766.       Tcl_IncrRefCount(objv[i]);
  1767.     }
  1768.     if (Tcl_ProcObjCmd(0, in, 4, objv) == TCL_OK) {
  1769.       Tcl_CmdInfo info;
  1770.       if (Tcl_GetCommandInfo(in, args[1], &info)) {
  1771. ProcInterpId = info.proc;
  1772. (void)Tcl_DeleteCommand(in, args[1]);
  1773.       } else 
  1774. res = 1;
  1775.     } else 
  1776.       res = 2;
  1777.     for (i = 0; i < 4; i++)
  1778.       Tcl_DecrRefCount(objv[i]);
  1779.     switch (res) {
  1780.     case 1: return OTclErrMsg(in, "proc failed", TCL_STATIC);
  1781.     case 2: return TCL_ERROR;
  1782.     }
  1783. #endif  /*TCL_MAJOR_VERSION >= 8*/
  1784.   }
  1785.   /*
  1786.    * bootstrap the tables of base objects and classes
  1787.    */
  1788.   
  1789.   if (theObjects == 0) {
  1790.     theObjects = (Tcl_HashTable*)ckalloc(sizeof(Tcl_HashTable));
  1791.     if (!theObjects) return OTclErrMsg(in, "Object table failed", TCL_STATIC);
  1792.     Tcl_InitHashTable(theObjects, TCL_ONE_WORD_KEYS);
  1793.   }
  1794.   if (theClasses == 0) {
  1795.     theClasses = (Tcl_HashTable*)ckalloc(sizeof(Tcl_HashTable));
  1796.     if (!theClasses) return OTclErrMsg(in, "Class table failed", TCL_STATIC);
  1797.     Tcl_InitHashTable(theClasses, TCL_ONE_WORD_KEYS);
  1798.   }
  1799.     
  1800.   hp1 = Tcl_CreateHashEntry(theObjects, (char*)in, &nw1);
  1801.   if (nw1) theobj = PrimitiveCCreate(in, "Object", 0);
  1802.   hp2 = Tcl_CreateHashEntry(theClasses, (char*)in, &nw2);
  1803.   if (nw2) thecls = PrimitiveCCreate(in, "Class", 0);
  1804.   if (!nw1 && !nw2) {
  1805.     Tcl_SetResult(in, "0", TCL_STATIC);
  1806.     return TCL_OK;
  1807.   } else if (!theobj || !thecls) {
  1808.     if (theobj) PrimitiveCDestroy((ClientData)theobj);
  1809.     if (thecls) PrimitiveCDestroy((ClientData)thecls);
  1810.     return OTclErrMsg(in, "Object/Class failed", TCL_STATIC);
  1811.   }
  1812.   Tcl_SetHashValue(hp1, (char*)theobj);
  1813.   Tcl_SetHashValue(hp2, (char*)thecls);
  1814.     
  1815.   theobj->object.type = thecls;
  1816.   theobj->parent = 0;
  1817.   thecls->object.type = thecls;
  1818.   thecls->parent = theobj;
  1819.   AddInstance((OTclObject*)theobj, thecls);
  1820.   AddInstance((OTclObject*)thecls, thecls);
  1821.   AddSuper(thecls, theobj);
  1822. #if TCL_MAJOR_VERSION >= 8
  1823.   /* create the otcl namespace of otcl instprocs and procs */
  1824.   namespacePtr = Tcl_CreateNamespace(in, "otcl", (ClientData) NULL,
  1825.      (Tcl_NamespaceDeleteProc *) NULL);  
  1826.   if (namespacePtr==NULL)
  1827.     return OTclErrMsg(in, "creation of name space failed", TCL_STATIC);
  1828. #endif
  1829.   
  1830.   /*
  1831.    * and fill them with functionality
  1832.    */
  1833.   
  1834.   OTclAddPMethod((OTclObject*)theobj, "alloc", (Tcl_CmdProc *) OTclOAllocMethod, 0, 0);
  1835.   OTclAddIMethod(theobj, "init", (Tcl_CmdProc *) OTclOInitMethod, 0, 0);
  1836.   OTclAddIMethod(theobj, "destroy", (Tcl_CmdProc *) OTclODestroyMethod, 0, 0);
  1837.   OTclAddIMethod(theobj, "class", (Tcl_CmdProc *) OTclOClassMethod, 0, 0);
  1838.   OTclAddIMethod(theobj, "info", (Tcl_CmdProc *) OTclOInfoMethod, 0, 0);
  1839.   OTclAddIMethod(theobj, "proc", (Tcl_CmdProc *) OTclOProcMethod, 0, 0);
  1840.   OTclAddIMethod(theobj, "next", (Tcl_CmdProc *) OTclONextMethod, 0, 0);
  1841.   OTclAddIMethod(theobj, "set", (Tcl_CmdProc *) OTclOSetMethod, 0, 0);
  1842.   OTclAddIMethod(theobj, "unset", (Tcl_CmdProc *) OTclOUnsetMethod, 0, 0);
  1843.   OTclAddIMethod(theobj, "instvar", (Tcl_CmdProc *) OTclOInstVarMethod, 0, 0);
  1844.   OTclAddPMethod((OTclObject*)thecls, "alloc", (Tcl_CmdProc *) OTclCAllocMethod, 0, 0);
  1845.   OTclAddIMethod(thecls, "create", (Tcl_CmdProc *) OTclCCreateMethod, 0, 0);
  1846.   OTclAddIMethod(thecls, "superclass", (Tcl_CmdProc *) OTclCSuperClassMethod, 0, 0);
  1847.   OTclAddIMethod(thecls, "info", (Tcl_CmdProc *) OTclCInfoMethod, 0, 0);
  1848.   OTclAddIMethod(thecls, "instproc", (Tcl_CmdProc *) OTclCInstProcMethod, 0, 0);
  1849.   /*
  1850.    * with some methods and library procs in tcl - they could go in a
  1851.    * otcl.tcl file, but they're embedded here with Tcl_Eval to avoid
  1852.    * the need to carry around a separate library.
  1853.    */
  1854.   (void)strcpy(tm, "Object instproc array {opt ary args} {             n");
  1855.   (void)strcat(tm, "  $self instvar $ary                               n");
  1856.   (void)strcat(tm, "  eval array [list $opt] [list $ary] $args         n");
  1857.   (void)strcat(tm, "}                                                  n");
  1858.   if (Tcl_Eval(in, tm) != TCL_OK) return TCL_ERROR;
  1859.   
  1860.   (void)strcpy(tm, "Class instproc unknown {m args} {                  n");
  1861.   (void)strcat(tm, "  if {$m == {create}} then {                       n");
  1862.   (void)strcat(tm, "    error "$self: unable to dispatch $m"         n");
  1863.   (void)strcat(tm, "  }                                                n");
  1864.   (void)strcat(tm, "  eval [list $self] create [list $m] $args         n");
  1865.   (void)strcat(tm, "}                                                  n");
  1866.   if (Tcl_Eval(in, tm) != TCL_OK) return TCL_ERROR;
  1867.   
  1868.   (void)strcpy(tm, "proc otcl_load {obj file} {                        n");
  1869.   (void)strcat(tm, "   global auto_index                               n");
  1870.   (void)strcat(tm, "   source $file                                    n");
  1871.   (void)strcat(tm, "   foreach i [array names auto_index             \n");
  1872.   (void)strcat(tm, "       [list $obj *proc *]] {                      n");
  1873.   (void)strcat(tm, "     set type [lindex $i 1]                        n");
  1874.   (void)strcat(tm, "     set meth [lindex $i 2]                        n");
  1875.   (void)strcat(tm, "     if {[$obj info ${type}s $meth] == {}} then {  n");
  1876.   (void)strcat(tm, "       $obj $type $meth {auto} $auto_index($i)     n");
  1877.   (void)strcat(tm, "     }                                             n");
  1878.   (void)strcat(tm, "   }                                               n");
  1879.   (void)strcat(tm, " }                                                 n");
  1880.   if (Tcl_Eval(in, tm) != TCL_OK) return TCL_ERROR;
  1881.   (void)strcpy(tm, "proc otcl_mkindex {meta dir args} {                n");
  1882.   (void)strcat(tm, "  set sp {[  ]+}                            n");
  1883.   (void)strcat(tm, "  set st {^[  ]*}                            n");
  1884.   (void)strcat(tm, "  set wd {([^  ]+)}                           n");
  1885.   (void)strcat(tm, "  foreach creator $meta {                          n");
  1886.   (void)strcat(tm, "    lappend cp "$st$creator${sp}create$sp$wd"    n");
  1887.   (void)strcat(tm, "    lappend ap "$st$creator$sp$wd"               n");
  1888.   (void)strcat(tm, "  }                                                n");
  1889.   (void)strcat(tm, "  foreach method {proc instproc} {                 n");
  1890.   (void)strcat(tm, "    lappend mp "$st$wd${sp}($method)$sp$wd"      n");
  1891.   (void)strcat(tm, "  }                                                n");
  1892.   (void)strcat(tm, "  foreach cl [concat Class [Class info heritage]] {n");
  1893.   (void)strcat(tm, "    eval lappend meths [$cl info instcommands]     n");
  1894.   (void)strcat(tm, "  }                                                n");
  1895.   (void)strcat(tm, "  set old [pwd]                                    n");
  1896.   (void)strcat(tm, "  cd $dir                                          n");
  1897.   (void)strcat(tm, "  append idx "# Tcl autoload index file, "       n");
  1898.   (void)strcat(tm, "  append idx "version 2.0\n"                    n");
  1899.   (void)strcat(tm, "  append idx "# otcl additions generated with "  n");
  1900.   (void)strcat(tm, "  append idx "\"otcl_mkindex [list $meta] "    n");
  1901.   (void)strcat(tm, "  append idx "[list $dir] $args\"\n"          n");
  1902.   (void)strcat(tm, "  set oc 0                                         n");
  1903.   (void)strcat(tm, "  set mc 0                                         n");
  1904.   (void)strcat(tm, "  foreach file [eval glob -nocomplain -- $args] {  n");
  1905.   (void)strcat(tm, "    if {[catch {set f [open $file]} msg]} then {   n");
  1906.   (void)strcat(tm, "      catch {close $f}                             n");
  1907.   (void)strcat(tm, "      cd $old                                      n");
  1908.   (void)strcat(tm, "      error $msg                                   n");
  1909.   (void)strcat(tm, "    }                                              n");
  1910.   (void)strcat(tm, "    while {[gets $f line] >= 0} {                  n");
  1911.   (void)strcat(tm, "      foreach c $cp {                              n");
  1912.   (void)strcat(tm, "     if {[regexp $c $line x obj]==1 &&          n");
  1913.   (void)strcat(tm, "         [string index $obj 0]!={$}} then {     n");
  1914.   (void)strcat(tm, "       incr oc                                  n");
  1915.   (void)strcat(tm, "       append idx "set auto_index($obj) "     n");
  1916.   (void)strcat(tm, "       append idx "\"otcl_load $obj "       n");
  1917.   (void)strcat(tm, "          append idx "\$dir/$file\"\n"       n");
  1918.   (void)strcat(tm, "     }                                          n");
  1919.   (void)strcat(tm, "   }                                            n");
  1920.   (void)strcat(tm, "      foreach a $ap {                              n");
  1921.   (void)strcat(tm, "     if {[regexp $a $line x obj]==1 &&          n");
  1922.   (void)strcat(tm, "         [string index $obj 0]!={$} &&          n");
  1923.   (void)strcat(tm, "         [lsearch -exact $meths $obj]==-1} {    n");
  1924.   (void)strcat(tm, "       incr oc                                  n");
  1925.   (void)strcat(tm, "       append idx "set auto_index($obj) "     n");
  1926.   (void)strcat(tm, "       append idx "\"otcl_load $obj "       n");
  1927.   (void)strcat(tm, "          append idx "\$dir/$file\"\n"       n");
  1928.   (void)strcat(tm, "     }                                          n");
  1929.   (void)strcat(tm, "   }                                            n");
  1930.   (void)strcat(tm, "      foreach m $mp {                              n");
  1931.   (void)strcat(tm, "     if {[regexp $m $line x obj ty pr]==1 &&    n");
  1932.   (void)strcat(tm, "         [string index $obj 0]!={$} &&          n");
  1933.   (void)strcat(tm, "         [string index $pr 0]!={$}} then {      n");
  1934.   (void)strcat(tm, "         incr mc                                n");
  1935.   (void)strcat(tm, "         append idx "set \{auto_index($obj " n");
  1936.   (void)strcat(tm, "         append idx "$ty $pr)\} \"source " n");
  1937.   (void)strcat(tm, "         append idx "\$dir/$file\"\n"     n");
  1938.   (void)strcat(tm, "     }                                          n");
  1939.   (void)strcat(tm, "      }                                            n");
  1940.   (void)strcat(tm, "    }                                              n");
  1941.   (void)strcat(tm, "    close $f                                       n");
  1942.   (void)strcat(tm, "  }                                                n");
  1943.   (void)strcat(tm, "  set t [open tclIndex a+]                         n");
  1944.   (void)strcat(tm, "  puts $t $idx nonewline                           n");
  1945.   (void)strcat(tm, "  close $t                                         n");
  1946.   (void)strcat(tm, "  cd $old                                          n");
  1947.   (void)strcat(tm, "  return "$oc objects, $mc methods"              n");
  1948.   (void)strcat(tm, "}                                                  n");
  1949.   if (Tcl_Eval(in, tm) != TCL_OK) return TCL_ERROR;
  1950.   Tcl_SetResult(in, "1", TCL_STATIC);
  1951.   return TCL_OK;
  1952. }
  1953. /*
  1954.  * Otcl strangness:  why isn't c listed?
  1955.  *    dash> otclsh
  1956.  *    % Class Foo
  1957.  *    Foo
  1958.  *    % Foo instproc a a {}
  1959.  *    % Foo instproc b {} { }
  1960.  *    % Foo instproc c {} {}
  1961.  *    % Foo info instprocs
  1962.  *    a b
  1963.  * -johnh, 30-Jun-98
  1964.  */