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

通讯编程

开发平台:

Visual C++

  1. /*
  2.  * Copyright (c) 1993-1995 Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer.
  10.  * 2. Redistributions in binary form must reproduce the above copyright
  11.  *    notice, this list of conditions and the following disclaimer in the
  12.  *    documentation and/or other materials provided with the distribution.
  13.  * 3. All advertising materials mentioning features or use of this software
  14.  *    must display the following acknowledgement:
  15.  *      This product includes software developed by the University of
  16.  *      California, Berkeley and the Network Research Group at
  17.  *      Lawrence Berkeley Laboratory.
  18.  * 4. Neither the name of the University nor of the Laboratory may be used
  19.  *    to endorse or promote products derived from this software without
  20.  *    specific prior written permission.
  21.  *
  22.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  23.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  24.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  25.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  26.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  27.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  28.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  29.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  30.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  31.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  32.  * SUCH DAMAGE.
  33.  */
  34. #ifndef lint
  35. static const char rcsid[] =
  36.     "@(#) $Header: /cvsroot/otcl-tclcl/tclcl/Tcl.cc,v 1.76 2007/02/04 01:46:43 tom_henderson Exp $ (LBL)";
  37. #endif
  38. #include "config.h"
  39. #include <stdio.h>
  40. #include <stdlib.h>
  41. #include <string.h>
  42. #include <stdarg.h>
  43. #include <ctype.h>
  44. #include <tcl.h>
  45. #include "tclcl.h"
  46. #include "tclcl-config.h"
  47. #include "tclcl-internal.h"
  48. #include <sys/types.h>
  49. #include <assert.h>
  50. #include "tracedvar.h"
  51. /* WIN32: Moved tk.h to a point after tclcl.h since tclcl.h includes
  52.  * windows.h which grumbles if I load tk.h before it
  53.  */
  54. #ifndef NO_TK
  55. #include <tk.h>
  56. #endif
  57. #define MAX_CODE_TO_DUMP (8*1024)
  58. class InstVar {
  59. protected:
  60. InstVar(const char* name);
  61. public:
  62. virtual ~InstVar();
  63. InstVar* next_;
  64. virtual void set(const char*) = 0;
  65. virtual const char* snget(char *wrk, int wrklen) = 0;
  66. void init(const char* var);
  67. inline const char* name() { return name_; }
  68. inline TracedVar* tracedvar() { return tracedvar_; }
  69. inline void tracedvar(TracedVar* v) { tracedvar_ = v; }
  70. static double time_atof(const char* s);
  71. static double bw_atof(const char* s);
  72. private:
  73. static char* catch_var(ClientData, Tcl_Interp* tcl,
  74.        CONST84 char* name1, CONST84 char* name2, int flags);
  75. protected:
  76. void catch_read(const char* name1, const char* name2);
  77. void catch_write(const char* name1, const char* name2);
  78. void catch_destroy(const char* name1, const char* name2);
  79. const char* name_;
  80. TracedVar* tracedvar_;
  81. };
  82. #define WRK_SMALL_SIZE 32
  83. #define WRK_MEDIUM_SIZE 256
  84. class TracedVarTcl : public TracedVar {
  85. public:
  86. TracedVarTcl(const char* name);
  87. virtual ~TracedVarTcl();
  88. virtual char* value(char* buf, int buflen);
  89. private:
  90. static char* catch_var(ClientData, Tcl_Interp* tcl,
  91.        CONST84 char* name1, CONST84 char* name2, int flags);
  92. protected:
  93. void catch_write(const char* name1, const char*);
  94. void catch_destroy(const char* name1, const char*);
  95. CONST84 char* value_;
  96. };
  97. Tcl Tcl::instance_;
  98. Tcl::Tcl() :
  99. tcl_(0),
  100. tkmain_(0),
  101. application_(0)
  102. {
  103. Tcl_InitHashTable(&objs_, TCL_STRING_KEYS);
  104. bp_ = buffer_;
  105. }
  106. void Tcl::init(const char* application)
  107. {
  108. init(Tcl_CreateInterp(), application);
  109. }
  110. extern EmbeddedTcl et_tclobject;
  111. void Tcl::init(Tcl_Interp* tcl, const char* application)
  112. {
  113. instance_.tcl_ = tcl;
  114. instance_.application_ = application;
  115. et_tclobject.load();
  116. TclClass::init();
  117. }
  118. TclObject* Tcl::lookup(const char* name)
  119. {
  120. /*XXX use tcl hash table */
  121. Tcl_HashEntry* he = Tcl_FindHashEntry(&objs_, (char*)name);
  122. if (he != 0)
  123. return ((TclObject*)Tcl_GetHashValue(he));
  124. return (0);
  125. }
  126. void Tcl::enter(TclObject* o)
  127. {
  128. int nw;
  129. Tcl_HashEntry* he = Tcl_CreateHashEntry(&objs_, (char*)o->name(),
  130. (int*)&nw);
  131. Tcl_SetHashValue(he, (char*)o);
  132. }
  133. void Tcl::remove(TclObject* o)
  134. {
  135. Tcl_HashEntry* he = Tcl_FindHashEntry(&objs_, (char*)o->name());
  136. if (he == 0)
  137. abort();
  138. Tcl_DeleteHashEntry(he);
  139. }
  140. void Tcl::evalc(const char* s)
  141. {
  142. unsigned int n = strlen(s) + 1;
  143. if (n < sizeof(buffer_) - (bp_ - buffer_)) {
  144. char* const p = bp_;
  145. bp_ += n;
  146. strcpy(p, s);
  147. eval(p);
  148. bp_ = p;
  149. } else {
  150. char* p = new char[n + 1];
  151. strcpy(p, s);
  152. eval(p);
  153. delete[] p;
  154. }
  155. }
  156. void Tcl::eval(char* s)
  157. {
  158. int st = Tcl_GlobalEval(tcl_, s);
  159. if (st != TCL_OK) {
  160. int n = strlen(application_) + strlen(s);
  161. if (n > MAX_CODE_TO_DUMP) {
  162. s = "n[code omitted because of length]n";
  163. n = strlen(application_) + strlen(s);
  164. };
  165. char* wrk = new char[n + 80];
  166. sprintf(wrk, "tkerror {%s: %s}", application_, s);
  167. if (Tcl_GlobalEval(tcl_, wrk) != TCL_OK) {
  168. fprintf(stderr, "%s: tcl error on eval of: %sn",
  169. application_, s);
  170. exit(1);
  171. }
  172. delete[] wrk;
  173. //exit(1);
  174. }
  175. }
  176. void Tcl::eval()
  177. {
  178. char* p = bp_;
  179. bp_ = p + strlen(p) + 1;
  180. /*XXX*/
  181. if (bp_ >= &buffer_[1024]) {
  182. fprintf(stderr, "bailing in Tcl::evaln");
  183. assert(0);
  184. exit(1);
  185. }
  186. eval(p);
  187. bp_ = p;
  188. }
  189. void Tcl::error(const char* s)
  190. {
  191. if (strlen(s) > MAX_CODE_TO_DUMP) {
  192. s = "n[code omitted because of length]n";
  193. };
  194. fprintf(stderr, "%s: "%s": %sn", application_, s, tcl_->result);
  195. exit(1);
  196. }
  197. /*XXX should be driven from tcl not C...*/
  198. #ifdef notdef
  199. void Tcl::add_option(const char* name, const char* value)
  200. {
  201. bp_[0] = toupper(application_[0]);
  202. sprintf(&bp_[1], "%s.%s", application_ + 1, name);
  203. Tk_AddOption(tkmain_, bp_, (char*)value, TK_USER_DEFAULT_PRIO + 1);
  204. }
  205. void Tcl::add_default(const char* name, const char* value)
  206. {
  207. bp_[0] = toupper(application_[0]);
  208. sprintf(&bp_[1], "%s.%s", application_ + 1, name);
  209. Tk_AddOption(tkmain_, bp_, (char*)value, TK_STARTUP_FILE_PRIO + 1);
  210. }
  211. const char* Tcl::attr(const char* attr) const
  212. {
  213. bp_[0] = toupper(application_[0]);
  214. strcpy(&bp_[1], application_ + 1);
  215. const char* cp = Tk_GetOption(tkmain_, (char*)attr, bp_);
  216. if (cp != 0 && *cp == 0)
  217. cp = 0;
  218. return (cp);
  219. }
  220. #endif
  221. TclObject::TclObject() : instvar_(0), tracedvar_(0)
  222. {
  223. #if 0
  224. name_[0] = 0;
  225. #else /* ! 0 */
  226. name_ = NULL;
  227. #endif /* 0 */
  228. }
  229. TclObject::~TclObject()
  230. {
  231. delete[] name_;
  232. }
  233. int TclObject::dispatch_static_proc(ClientData clientData,
  234.     Tcl_Interp * /*interp*/,
  235.     int argc, char *argv[])
  236. {
  237. int (*proc)(int argc, const char * const *argv);
  238. proc = (int (*) (int, const char * const *)) clientData;
  239. return ((*proc)(argc - 2, argv + 2));
  240. }
  241. void TclObject::insert(InstVar* p)
  242. {
  243. p->next_ = instvar_;
  244. instvar_ = p;
  245. }
  246. void TclObject::insert(TracedVar* var)
  247. {
  248. var->owner(this);
  249. var->next_ = tracedvar_;
  250. tracedvar_ = var;
  251. }
  252. #ifdef notdef
  253. int TclObject::callback(ClientData cd, Tcl_Interp*, int ac, char** av)
  254. {
  255. TclObject* tc = (TclObject*)cd;
  256. return (tc->command(ac, (const char*const*)av));
  257. }
  258. #endif
  259. void TclObject::name(const char* s)
  260. {
  261. #if 0
  262. // if TCLCL_NAME_LEN we should allow 10^10-1 (almost a billion) objects
  263. if (strlen(s) >= TCLCL_NAME_LEN)
  264. abort();
  265. strcpy(name_, s);
  266. #else /* ! 0 */
  267. delete[] name_;
  268. name_ = new char[strlen(s) + 1];
  269. strcpy(name_, s);
  270. #endif /* 0 */
  271. }
  272. int TclObject::command(int argc, const char*const* argv)
  273. {
  274. #ifdef notdef
  275. Tcl& t = Tcl::instance();
  276. char* cp = t.buffer();
  277. sprintf(cp, "%s: ", t.application());
  278. cp += strlen(cp);
  279. const char* cmd = argv[0];
  280. if (cmd[0] == '_' && cmd[1] == 'o' && class_name_ != 0)
  281. sprintf(cp, ""%s" (%s): ", class_name_, cmd);
  282. else
  283. sprintf(cp, "%s: ", cmd);
  284. cp += strlen(cp);
  285. if (argc >= 2)
  286. sprintf(cp, "no such method (%s)", argv[1]);
  287. else
  288. sprintf(cp, "requires additional args");
  289. t.result(t.buffer());
  290. #endif
  291. if (argc > 2) {
  292. if (strcmp(argv[1], "trace") == 0) {
  293. TclObject* tracer = this;
  294. if (argc > 3)
  295. tracer = TclObject::lookup(argv[3]);
  296. return traceVar(argv[2], tracer);
  297. }
  298. }
  299. return (TCL_ERROR);
  300. }
  301. void
  302. TclObject::create_instvar(const char* var)
  303. {
  304. /*
  305.  * XXX can't use tcl.evalf() because it uses Tcl_GlobalEval
  306.  * and we need to run in the context of the method.
  307.  */
  308. char wrk[256];
  309. sprintf(wrk, "$self instvar %s", var);
  310. Tcl_Eval(Tcl::instance().interp(), wrk);
  311. }
  312. int
  313. TclObject::create_framevar(const char *localName)
  314. {
  315. /*
  316.  * XXX can't use tcl.evalf() because it uses Tcl_GlobalEval
  317.  * and we need to run in the context of the method.
  318.  * 
  319.  * XXX Should add a check to see if we already have this framevar.
  320.  * If so, don't do the following set stuff, otherwise it'll change
  321.  * the correct value to 0.
  322.  */
  323. Tcl_Interp* tcl = Tcl::instance().interp();
  324. CONST84 char *v = (char *) Tcl_GetVar(tcl, (CONST84 char*)localName, 0);
  325. if (v != 0)
  326. return (TCL_OK);
  327. char wrk[WRK_MEDIUM_SIZE];
  328. if (-1 == snprintf(wrk, WRK_MEDIUM_SIZE, "set %s 0", localName))
  329. return TCL_ERROR;
  330. return Tcl_Eval(Tcl::instance().interp(), wrk);
  331. }
  332. // Enumerating through all traced variables, but still use the trace() 
  333. // callback. 
  334. int TclObject::enum_tracedVars()
  335. {
  336. for (InstVar* p = instvar_; p != 0; p = p->next_) {
  337. if (p->tracedvar() && p->tracedvar()->tracer())
  338. p->tracedvar()->tracer()->trace(p->tracedvar());
  339. }
  340. TracedVar* var = tracedvar_;
  341. for ( ;  var != 0;  var = var->next_) 
  342. if (var->tracer()) 
  343. var->tracer()->trace(var);
  344. return TCL_OK;
  345. }
  346. int TclObject::traceVar(const char* varName, TclObject* tracer)
  347. {
  348. // first check for delay-bound variables
  349. int e = delay_bind_dispatch(varName, varName, tracer);
  350. if (e == TCL_OK)  // was delay-bound and is now taken care of
  351. return e;
  352. // now handle regular bound variables
  353. for (InstVar* p = instvar_; p != 0; p = p->next_) {
  354. if (strcmp(p->name(), varName) == 0) {
  355. if (p->tracedvar()) {
  356. p->tracedvar()->tracer(tracer);
  357. tracer->trace(p->tracedvar());
  358. return TCL_OK;
  359. }
  360. Tcl& tcl = Tcl::instance();
  361. tcl.resultf("trace: %s is not a TracedVar", varName);
  362. return TCL_ERROR;
  363. }
  364. }
  365. TracedVar* var = tracedvar_;
  366. for ( ;  var != 0;  var = var->next_) {
  367. if (strcmp(var->name(), varName) == 0) {
  368. var->tracer(tracer);
  369. tracer->trace(var);
  370. return TCL_OK;
  371. }
  372. }
  373. // XXX Introduce the var into OTcl scope before tracing it
  374. OTclObject *otcl_object = 
  375. OTclGetObject(Tcl::instance().interp(), name_);
  376. int result = OTclOInstVarOne(otcl_object, Tcl::instance().interp(),
  377.      "1", (char *)varName, (char *)varName, 0);
  378. if (result == TCL_OK) {
  379. var = new TracedVarTcl(varName);
  380. insert(var);
  381. var->tracer(tracer);
  382. tracer->trace(var);
  383. }
  384. return result;
  385. }
  386. void TclObject::trace(TracedVar*)
  387. {
  388. fprintf(stderr, "SplitObject::trace called in the base class of %sn",
  389. name_);
  390. }
  391. void TclObject::msg_abort(const char *fmt, ...)
  392. {
  393. if (fmt != NULL) {
  394. va_list ap;
  395. va_start(ap, fmt);
  396. vprintf(fmt, ap);
  397. }
  398. ::abort();
  399. }
  400. TclClass* TclClass::all_;
  401. TclClass::TclClass(const char* classname) : class_(0), classname_(classname)
  402. {
  403. if (Tcl::instance().interp()!=NULL) {
  404. // the interpreter already exists!
  405. // this can happen only (?) if the class is created as part
  406. // of a dynamic library
  407. bind();
  408. } else {
  409. // the interpreter doesn't yet exist
  410. // add this class to a linked list that is traversed when
  411. // the interpreter is created
  412. next_ = all_;
  413. all_ = this;
  414. }
  415. }
  416. TclClass::~TclClass()
  417. {
  418. TclClass** p = &all_;
  419. while (*p != this && *p != NULL)
  420. p = &(*p)->next_;
  421. if (*p != NULL) {
  422. *p = (*p)->next_;
  423. }
  424. }
  425. void TclClass::init()
  426. {
  427. for (TclClass* p = all_; p != 0; p = p->next_)
  428. p->bind();
  429. }
  430. int TclClass::dispatch_cmd(ClientData clientData, Tcl_Interp *,
  431.    int argc, CONST84 char *argv[])
  432. {
  433. TclObject* o = (TclObject*)clientData;
  434. return (o->command(argc - 3, argv + 3));
  435. }
  436. int TclClass::create_shadow(ClientData clientData, Tcl_Interp *interp,
  437.     int argc, CONST84 char *argv[])
  438. {
  439. TclClass* p = (TclClass*)clientData;
  440. TclObject* o = p->create(argc, argv);
  441. Tcl& tcl = Tcl::instance();
  442. if (o != 0) {
  443. o->name(argv[0]);
  444. tcl.enter(o);
  445. if (o->init(argc - 2, argv + 2) == TCL_ERROR) {
  446. tcl.remove(o);
  447. delete o;
  448. return (TCL_ERROR);
  449. }
  450. tcl.result(o->name());
  451. OTclAddPMethod(OTclGetObject(interp, argv[0]), "cmd",
  452.        (Tcl_CmdProc *) dispatch_cmd, (ClientData)o, 0);
  453. OTclAddPMethod(OTclGetObject(interp, argv[0]), "instvar",
  454.        (Tcl_CmdProc *) dispatch_instvar, (ClientData)o, 0);
  455. o->delay_bind_init_all();
  456. return (TCL_OK);
  457. } else {
  458. tcl.resultf("new failed while creating object of class %s",
  459.     p->classname_);
  460. return (TCL_ERROR);
  461. }
  462. }
  463. /*
  464.  * Sigh.  Much of this routine duplicates
  465.  * OTclOInstVarMethod.  I did that rather than
  466.  * build and eval the appropriate Tcl code to get
  467.  * the control I wanted over calling my superclass's instvar.
  468.  */
  469. int
  470. TclClass::dispatch_instvar(ClientData /*cd*/, Tcl_Interp* in,
  471.    int argc, CONST84 char *argv[])
  472. {
  473. int i;
  474. int result;
  475. Tcl& tcl = Tcl::instance();
  476. // XXX:
  477. // There seems to be something silly about these next two lines.
  478. // Maybe a TclObject should have a method to return the OTclObject?
  479. OTclObject *otcl_object = OTclGetObject(in, argv[0]);
  480. TclObject* tcl_object = tcl.lookup(argv[0]);
  481. int need_parse = 0;
  482. for (i = 4; i < argc; i++) {
  483. int ac;
  484. CONST84 char **av;
  485. CONST84 char *varName, *localName;
  486. if (strcmp(argv[i], "-parse-part1") == 0) {
  487. need_parse = 1;
  488. continue;
  489. };
  490. result = Tcl_SplitList(in, argv[i], &ac, (const char ***) &av);
  491. if (result != TCL_OK) break;
  492. if (ac == 1) {
  493. varName = localName = av[0];
  494. } else if (ac == 2) {
  495. varName = av[0];
  496. localName = av[1];
  497. } else {
  498. Tcl_ResetResult(in);
  499. Tcl_AppendResult(in, "expected ?inst/local? or ?inst? ?local? but got ",
  500.  argv[i]);
  501. ckfree((char*)av);
  502. result = TCL_ERROR;
  503. break;
  504. };
  505. // handle arrays in instvars if -parse-part1 was specified.
  506. if (need_parse) {
  507. const char *p = strchr (localName, '(');
  508. if (p)
  509. ((char*) localName)[p-localName] = '';
  510. };
  511. if (TCL_OK != (result = tcl_object->delay_bind_dispatch(varName, localName, NULL)))
  512. result = OTclOInstVarOne(otcl_object, in, "1", varName, localName, 0);
  513. ckfree((char*)av);
  514. }
  515. return result;
  516. }
  517. int TclClass::delete_shadow(ClientData, Tcl_Interp*,
  518.    int argc, CONST84 char *argv[])
  519. {
  520. Tcl& tcl = Tcl::instance();
  521. if (argc != 4) {
  522. tcl.result("XXX delete-shadow");
  523. return (TCL_ERROR);
  524. }
  525. TclObject* o = tcl.lookup(argv[0]);
  526. /*
  527.  * Delete shadow if it exists.  Shadow might not exist
  528.  * because of error condition in constructor.
  529.  */
  530. if (o != 0) {
  531. tcl.remove(o);
  532. delete o;
  533. }
  534. return (TCL_OK);
  535. }
  536. void TclClass::bind()
  537. {
  538. Tcl& tcl = Tcl::instance();
  539. tcl.evalf("SplitObject register %s", classname_);
  540. class_ = OTclGetClass(tcl.interp(), (char*)classname_);
  541. OTclAddIMethod(class_, "create-shadow",
  542.        (Tcl_CmdProc *) create_shadow, (ClientData)this, 0);
  543. OTclAddIMethod(class_, "delete-shadow",
  544.        (Tcl_CmdProc *) delete_shadow, (ClientData)this, 0);
  545. otcl_mappings();
  546. }
  547. int TclClass::method(int, const char*const*)
  548. {
  549. /*XXX*/
  550. return (TCL_ERROR);
  551. }
  552. void TclClass::add_method(const char* name)
  553. {
  554. OTclAddPMethod((OTclObject*)class_, (char*)name,
  555.        (Tcl_CmdProc *) dispatch_method, (ClientData)this, 0);
  556. }
  557. int TclClass::dispatch_method(ClientData cd, Tcl_Interp*, int ac, CONST84 char** av)
  558. {
  559. TclClass* tc = (TclClass*)cd;
  560. return (tc->method(ac, (const char*const*)av));
  561. }
  562. void EmbeddedTcl::load()
  563. {
  564. Tcl::instance().evalc(code_);
  565. }
  566. int EmbeddedTcl::load(Tcl_Interp* interp)
  567. {
  568. return Tcl_Eval(interp, (char*)code_);
  569. }
  570. TclCommand::TclCommand(const char* cmd) : name_(cmd)
  571. {
  572. Tcl::instance().CreateCommand(cmd, (Tcl_CmdProc *) dispatch_cmd, (ClientData)this, 0);
  573. }
  574. TclCommand::~TclCommand()
  575. {
  576. Tcl::instance().DeleteCommand(name_);
  577. }
  578. int TclCommand::dispatch_cmd(ClientData clientData, Tcl_Interp*,
  579.    int argc, CONST84 char *argv[])
  580. {
  581. TclCommand* o = (TclCommand*)clientData;
  582. return (o->command(argc, argv));
  583. }
  584. TracedVarTcl::TracedVarTcl(const char* name) : TracedVar(), value_(0)
  585. {
  586. char* s = new char[strlen(name) + 1];
  587. strcpy(s, name);
  588. name_ = s;
  589. Tcl& tcl = Tcl::instance();
  590. Tcl_TraceVar(tcl.interp(), (char*)name,
  591.      TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  592.      (Tcl_VarTraceProc *) catch_var, (ClientData)this);
  593. }
  594. TracedVarTcl::~TracedVarTcl()
  595. {
  596. delete[] (char*)name_;
  597. }
  598. char* TracedVarTcl::value(char* buf, int buflen)
  599. {
  600. if (buf) {
  601. if (value_ != NULL)
  602. strncpy(buf, value_, buflen);
  603. else 
  604. buf[0] = 0;
  605. }
  606. return buf;
  607. }
  608. void TracedVarTcl::catch_write(const char* name1, const char*)
  609. {
  610. if (tracer() == 0)
  611. return;
  612. Tcl_Interp* tcl = Tcl::instance().interp();
  613. value_ = (char *) Tcl_GetVar(tcl, (CONST84 char*)name1, 0);
  614. if (value_ != 0)
  615. tracer()->trace(this);
  616. }
  617. void TracedVarTcl::catch_destroy(const char* /*name1*/, const char*)
  618. {
  619. delete this;
  620. }
  621. char* TracedVarTcl::catch_var(ClientData clientData, Tcl_Interp*,
  622.       CONST84 char* name1, CONST84 char* name2, int flags)
  623. {
  624. TracedVarTcl* p = (TracedVarTcl*)clientData;
  625. if (flags & TCL_TRACE_WRITES)
  626. p->catch_write(name1, name2);
  627. else if ((flags & TCL_TRACE_UNSETS) && (flags & TCL_TRACE_DESTROYED))
  628. p->catch_destroy(name1, name2);
  629. return (0);
  630. }
  631. /*XXX should be easy to extend to arrays*/
  632. char* InstVar::catch_var(ClientData clientData, Tcl_Interp*,
  633.  CONST84 char* name1, CONST84 char* name2, int flags)
  634. {
  635. InstVar* p = (InstVar*)clientData;
  636. if (flags & TCL_TRACE_WRITES)
  637. p->catch_write(name1, name2);
  638. else if (flags & TCL_TRACE_READS)
  639. p->catch_read(name1, name2);
  640. else if ((flags & TCL_TRACE_UNSETS) && (flags & TCL_TRACE_DESTROYED))
  641. p->catch_destroy(name1, name2);
  642. return (0);
  643. }
  644. void InstVar::catch_read(const char* name1, const char* name2)
  645. {
  646. char wrk[WRK_SMALL_SIZE];
  647. Tcl_Interp* tcl = Tcl::instance().interp();
  648. // tcl will copy the value out of wrk
  649. (void)Tcl_SetVar2(tcl, (char*)name1, (char*)name2, (char*)snget(wrk, WRK_SMALL_SIZE), 0);
  650. }
  651. void InstVar::catch_write(const char* name1, const char*)
  652. {
  653. Tcl_Interp* tcl = Tcl::instance().interp();
  654. const char* v = (char *) Tcl_GetVar(tcl, (CONST84 char*)name1, 0);
  655. if (v != 0)
  656. set(v);
  657. }
  658. /*
  659.  * catch_destroy only gets called for instvars allocated on the stack
  660.  * (classinstvars).  Regular instvars are deallocated by TclObject.
  661.  * Gee, I hope there's not a race there.
  662.  */
  663. void InstVar::catch_destroy(const char* /*name1*/, const char*)
  664. {
  665. delete this;
  666. }
  667. InstVar::InstVar(const char* name) : name_(name), tracedvar_(0)
  668. {
  669. Tcl& tcl = Tcl::instance();
  670. Tcl_TraceVar(tcl.interp(), (CONST84 char*)name,
  671.      TCL_TRACE_READS|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  672.      (Tcl_VarTraceProc *) catch_var, (ClientData)this);
  673. }
  674. InstVar::~InstVar()
  675. {
  676. /*XXX do an untrace?*/
  677. }
  678. /*
  679.  * Initialize the instance variable to the value stored in its class;
  680.  * this way we can easily create defaults for each instance
  681.  * variable that used by C.  We call this routine after the
  682.  * trace is set up so that the trace is invoked and the
  683.  * C variable is initialized.
  684.  */
  685. void InstVar::init(const char* var)
  686. {
  687. char wrk[256];
  688. sprintf(wrk, "$self init-instvar %s", var);
  689. if (Tcl_Eval(Tcl::instance().interp(), wrk) != TCL_OK) {
  690. /*XXX can only happy if TclObject::init-instvar broken */
  691. Tcl::instance().evalf("puts stderr "init-instvar: $errorInfo"");
  692. exit(1);
  693. }
  694. }
  695. class InstVarTclObject : public InstVar {
  696. public:
  697.   InstVarTclObject(const char* name, TclObject** val) 
  698.     : InstVar(name), val_(val) {}
  699.   
  700.   const char* snget(char *wrk, int wrklen) {
  701.     if (-1 == snprintf(wrk, wrklen, "%s", (*val_)->name()))
  702.       abort();
  703.     return (wrk);
  704.   }
  705.   void set(const char* s) {
  706.     *val_ = TclObject::lookup(s);
  707.   }
  708. protected:
  709.   TclObject** val_;
  710. };
  711. class InstVarReal : public InstVar {
  712.  public:
  713.   InstVarReal(const char* name, double* val)
  714. : InstVar(name), val_(val) {}
  715. const char* snget(char *wrk, int wrklen) {
  716. if (-1 == snprintf(wrk, wrklen, "%.17g", *val_))
  717. abort();
  718. return (wrk);
  719. }
  720. void set(const char* s) {
  721. *val_ = atof(s);
  722. }
  723.  protected:
  724. double* val_;
  725. };
  726. class InstVarBandwidth : public InstVarReal {
  727.  public:
  728. InstVarBandwidth(const char* name, double* val)
  729. : InstVarReal(name, val) { }
  730. void set(const char* s) {
  731. *val_ = bw_atof(s);
  732. }
  733. };
  734. class InstVarTime : public InstVarReal {
  735.  public:
  736. InstVarTime(const char* name, double* val)
  737. : InstVarReal(name, val) { }
  738. void set(const char* s) {
  739. *val_ = time_atof(s);
  740. }
  741. };
  742. class InstVarInt : public InstVar {
  743.  public:
  744. InstVarInt(const char* name, int* val)
  745. : InstVar(name), val_(val) {}
  746. const char* snget(char *wrk, int wrklen) {
  747. if (-1 == snprintf(wrk, wrklen, "%d", *val_))
  748. abort();
  749. return (wrk);
  750. }
  751. void set(const char* s) {
  752. *val_ = strtol(s, (char**)0, 0);
  753. }
  754.  protected:
  755. int* val_;
  756. };
  757. class InstVarUInt : public InstVar {
  758.  public:
  759.         InstVarUInt(const char* name, unsigned int* val)
  760. : InstVar(name), val_(val) {}
  761. const char* snget(char *wrk, int wrklen) {
  762. if (-1 == snprintf(wrk, wrklen, "%u", *val_))
  763. abort();
  764. return (wrk);
  765. }
  766. void set(const char* s) {
  767. *val_ = strtoul(s, (char**)0, 0);
  768. }
  769.  protected:
  770. unsigned int* val_;
  771. };
  772. #if defined(HAVE_INT64)
  773. class InstVarInt64 : public InstVar {
  774.  public:
  775. InstVarInt64(const char* name, int64_t* val) 
  776. : InstVar(name), val_(val) {}
  777. const char* snget(char *wrk, int wrklen) {
  778. if (-1 == snprintf(wrk, wrklen, 
  779.    STRTOI64_FMTSTR, *val_))
  780. abort();
  781. return (wrk);
  782. }
  783. void set(const char* s) {
  784. *val_ = STRTOI64(s, (char**)0, 0);
  785. }
  786.  protected:
  787. int64_t* val_;
  788. };
  789. #endif
  790. class InstVarBool : public InstVarInt {
  791.  public:
  792. InstVarBool(const char* var, int* val) : InstVarInt(var, val) {}
  793. void set(const char* s) {
  794. int v;
  795. if (isdigit(*s))
  796. v = atoi(s);
  797. else switch (*s) {
  798. case 't':
  799. case 'T':
  800. v =  1;
  801. break;
  802. default:
  803. v = 0;
  804. break;
  805. }
  806. *val_ = v;
  807. }
  808. };
  809. class InstVarError : public InstVar {
  810.  public:
  811. InstVarError(const char* name, const char* errmsg)
  812. : InstVar(name), errmsg_(errmsg) {}
  813. const char* snget(char *wrk, int wrklen) {
  814. fprintf(stderr, "nERROR: %snn", errmsg_);
  815. abort();
  816. // To make MSVC happy
  817. return NULL;
  818. }
  819. void set(const char* s) {
  820. fprintf(stderr, "nERROR: %snn", errmsg_);
  821. abort();
  822. }
  823.  protected:
  824. const char* errmsg_;
  825. };
  826. class InstVarTracedInt : public InstVar {
  827.  public:
  828. InstVarTracedInt(const char* name, TracedInt* val) : InstVar(name), val_(val) {
  829. tracedvar(val);
  830. }
  831. const char* snget(char *wrk, int wrklen) {
  832. return (val_->value(wrk, wrklen));
  833. }
  834. void set(const char* s) {
  835. *val_ = strtol(s, (char**)0, 0);
  836. }
  837.  protected:
  838. TracedInt* val_;
  839. };
  840. class InstVarTracedReal : public InstVar {
  841.  public:
  842. InstVarTracedReal(const char* name, TracedDouble* val) : InstVar(name), val_(val) { 
  843. tracedvar(val);
  844. }
  845. const char* snget(char *wrk, int wrklen) {
  846. return (val_->value(wrk, wrklen));
  847. }
  848. void set(const char* s) {
  849. *val_ = atof(s);
  850. }
  851.  protected:
  852. TracedDouble* val_;
  853. };
  854. double InstVar::bw_atof(const char* s) 
  855. {
  856. char wrk[32];
  857. char* cp = wrk;
  858. while (isdigit(*s) || *s == 'e' || *s == '+' ||
  859.        *s == '-' || *s == '.')
  860. *cp++ = *s++;
  861. *cp = 0;
  862. double v = atof(wrk);
  863. switch (s[0]) {
  864. case 'k':
  865. case 'K':
  866. v *= 1e3;
  867. break;
  868. case 'm':
  869. case 'M':
  870. v *= 1e6;
  871. break;
  872. case 'g':
  873. case 'G':
  874. v *= 1e9;
  875. break;
  876. case 't':
  877. case 'T':
  878. v *= 1e12;
  879. break;
  880. case 'p':
  881. case 'P':
  882. v *= 1e15;
  883. break;
  884. }
  885. if (s[0] != 0 && s[1] == 'B')
  886. v *= 8;
  887. return (v);
  888. }
  889. double InstVar::time_atof(const char* s)
  890. {
  891. char wrk[32];
  892. char* cp = wrk;
  893. while (isdigit(*s) || *s == 'e' || *s == '+' || *s == '-' || *s == '.')
  894. *cp++ = *s++;
  895. *cp = 0;
  896. double v = atof(wrk);
  897. switch (*s) {
  898. case 'm':
  899. v *= 1e-3;
  900. break;
  901. case 'u':
  902. v *= 1e-6;
  903. break;
  904. case 'n':
  905. v *= 1e-9;
  906. break;
  907. case 'p':
  908. v *= 1e-12;
  909. break;
  910. }
  911. return (v);
  912. }
  913. void TclObject::init(InstVar* v, const char* var)
  914. {
  915. insert(v);
  916. v->init(var);
  917. }
  918. #define TOB(FUNCTION, C_TYPE, INSTVAR_TYPE, OTHER_STUFF) 
  919. void TclObject::FUNCTION(const char* var, C_TYPE* val) 
  920.   create_instvar(var); 
  921.   OTHER_STUFF; 
  922.     init(new INSTVAR_TYPE(var, val), var); 
  923. }
  924. TOB(bind, double, InstVarReal, ;)
  925. TOB(bind_bw, double, InstVarBandwidth, ;)
  926. TOB(bind_time, double, InstVarTime, ;)
  927. TOB(bind, int, InstVarInt, ;)
  928. TOB(bind, unsigned int, InstVarUInt, ;)
  929. TOB(bind_bool, int, InstVarBool, ;)
  930. TOB(bind, TclObject*, InstVarTclObject, ;)
  931. TOB(bind, TracedInt, InstVarTracedInt, val->name(var); val->owner(this);)
  932. TOB(bind, TracedDouble, InstVarTracedReal, val->name(var); val->owner(this);)
  933. void TclObject::bind_error(const char* name, const char* errmsg) {
  934. create_instvar(name);
  935. insert(new InstVarError(name, errmsg));
  936. }
  937. #if defined(HAVE_INT64)
  938. TOB(bind, int64_t, InstVarInt64, )
  939. #endif
  940. int
  941. TclObject::delay_bind_dispatch(const char* /*varName*/, const char* /*localName*/, TclObject * /*tracer*/)
  942. {
  943. return TCL_ERROR;  // terminate search
  944. }
  945. void
  946. TclObject::delay_bind_init_all()
  947. {
  948. }
  949. /*
  950.  * sigh... I'd like to call both these functions delay_bind_init,
  951.  * but gcc 2.7.2.3 apparently isn't
  952.  * distinguishing the two based on signature.
  953.  */
  954. void
  955. TclObject::delay_bind_init_one(const char *varName)
  956. {
  957. char wrk[WRK_MEDIUM_SIZE];
  958. if (-1 == snprintf(wrk, WRK_MEDIUM_SIZE, "$self init-instvar %s", varName))
  959. abort();
  960. if (Tcl_Eval(Tcl::instance().interp(), wrk) != TCL_OK)
  961. abort();
  962. }
  963. void
  964. TclObject::not_a_TracedVar(const char *varName)
  965. {
  966. fprintf(stderr, "TclObject: %s is not a TracedVar.n", varName);
  967. abort();
  968. }
  969. void
  970. TclObject::handle_TracedVar(const char *name, TracedVar *val, TclObject *tracer)
  971. {
  972. /*
  973.  * Remember what the variable is called.
  974.  * We assume name is a pointer to static storage
  975.  * and so won't free it.
  976.  */
  977. val->name(name);
  978. // It's not clear that owner is ever used, but we set it anyway
  979. // for compability.
  980. val->owner(this);
  981. // hook the traced var into the tracing system
  982. val->tracer(tracer);
  983. tracer->trace(val);
  984. }
  985. /*
  986.  * Traced vars and delay_binding:
  987.  *
  988.  * Without delay-binding, tracevars end up linked into
  989.  * the object's instvar_ and tracedvar_ chains as part of bind().
  990.  * This info is then used in two places:
  991.  * - TclObject::enum_tracedVars (to list them all)
  992.  * - TclObject::traceVar (to see if a given thing we're trying to trace
  993.  * shoud be, called from TclObject::command's "trace" cmd
  994.  *
  995.  * With delay binding this approach doesn't work, since we don't
  996.  * have InstVar structures in existance at all times.  (This is good---
  997.  * it saves memory.)  Instead, we call delay_bind_dispatch() to
  998.  * search the object hierarchy for a delay-bound variable when a script
  999.  * does "instvar foo_".  This is the only way to search the hierarchy,
  1000.  * so we make it also work the trace command.
  1001.  *
  1002.  * (Design aside:  it would be more elegant to put all the class
  1003.  * variables (i.e., the delay_bound ones) in a class-wide hash and do
  1004.  * a quick lookup on them through the hash table, like what happens
  1005.  * for objects.  Unfortuantely, we can't do this, primarily because we
  1006.  * need offsets into objects (like in Xt), but C++ doesn't allow void
  1007.  * O::* pointers just typed O::*'s, and C++ O::* pointers are REALLY
  1008.  * ugly because they have to handle * multiple inheritance.)
  1009.  *
  1010.  *
  1011.  * There's probably a better way to do this with templates
  1012.  * (but we don't allow templates currently for portability).
  1013.  *
  1014.  * TclObject::delay_bind returns a boolean if it was handled our not
  1015.  * see ~ns-2/agent.cc for an example of how to use delay_bind_init_all
  1016.  * and delay_bind_dispatch.
  1017.  */
  1018. #define TODB(FUNCTION, C_TYPE, INSTVAR_TYPE, TRACEDVAR_CODE) 
  1019. bool 
  1020. TclObject:: FUNCTION (const char* varName, const char* localName, 
  1021.       const char* thisVarName, 
  1022.       C_TYPE *val, TclObject *tracer) 
  1023. if (strcmp(varName, thisVarName) != 0) return false; 
  1024. if (tracer) { 
  1025. /* traced var request */ 
  1026. TRACEDVAR_CODE; 
  1027. }  else { 
  1028. /* just a binding */ 
  1029. if (TCL_OK != create_framevar(localName)) abort(); 
  1030. (void) (new INSTVAR_TYPE (localName, val)); 
  1031. }; 
  1032. return true; 
  1033. }
  1034. // These macros are quite ugly in that they the xxx_TracedVars
  1035. // reference params of the function above...
  1036. TODB(delay_bind, double, InstVarReal, not_a_TracedVar(thisVarName))
  1037. TODB(delay_bind_bw, double, InstVarBandwidth, not_a_TracedVar(thisVarName))
  1038. TODB(delay_bind_time, double, InstVarTime, not_a_TracedVar(thisVarName))
  1039. TODB(delay_bind, int, InstVarInt, not_a_TracedVar(thisVarName))
  1040. TODB(delay_bind, unsigned int, InstVarUInt, not_a_TracedVar(thisVarName))
  1041. TODB(delay_bind_bool, int, InstVarBool, not_a_TracedVar(thisVarName))
  1042. TODB(delay_bind, TracedInt, InstVarTracedInt, handle_TracedVar(thisVarName,val,tracer))
  1043. TODB(delay_bind, TracedDouble, InstVarTracedReal, handle_TracedVar(thisVarName,val,tracer))
  1044. #if defined(HAVE_INT64)
  1045. TODB(delay_bind, int64_t, InstVarInt64, not_a_TracedVar(thisVarName))
  1046. #endif
  1047. TclObject *
  1048. TclObject::New(const char *className, const char * arg1, ...)
  1049. {
  1050. Tcl_DString buf;
  1051. const char *string;
  1052. Tcl &tcl = Tcl::instance();
  1053. int result;
  1054. va_list ap;
  1055. va_start(ap, arg1);
  1056. Tcl_DStringInit(&buf);
  1057. Tcl_DStringAppendElement(&buf, "new");
  1058. Tcl_DStringAppendElement(&buf, (char*)className);
  1059. string = arg1;
  1060. while (string!=NULL) {
  1061. Tcl_DStringAppendElement(&buf, (char*)string);
  1062. string = va_arg(ap, const char *);
  1063. }
  1064. va_end(ap);
  1065. result = Tcl_Eval(tcl.interp(), Tcl_DStringValue(&buf));
  1066. Tcl_DStringFree(&buf);
  1067. if (result==TCL_ERROR) {
  1068. return NULL;
  1069. }
  1070. else {
  1071. return tcl.lookup(tcl.result());
  1072. }
  1073. }
  1074. int
  1075. TclObject::Delete(TclObject *object)
  1076. {
  1077. Tcl &tcl = Tcl::instance();
  1078. if (object->name()==NULL) {
  1079. // this object does not have a corresponding OTcl object
  1080. delete object;
  1081. tcl.result("");
  1082. return TCL_OK;
  1083. }
  1084. return Tcl_VarEval(tcl.interp(), "delete ", object->name(), NULL);
  1085. }
  1086. int
  1087. TclObject::Invoke(const char *method, ...)
  1088. {
  1089. Tcl_DString buf;
  1090. const char *string;
  1091. Tcl &tcl = Tcl::instance();
  1092. int result;
  1093. va_list ap;
  1094. if (name()==NULL) {
  1095. // this object does not have a corresponding OTcl object
  1096. tcl.result("no otcl object associated with C++ TclObject");
  1097. tcl.add_error("ninvoked from withing TclObject::invoke()");
  1098. return TCL_ERROR;
  1099. }
  1100. va_start(ap, method);
  1101. Tcl_DStringInit(&buf);
  1102. Tcl_DStringAppendElement(&buf, (char*) name());
  1103. Tcl_DStringAppendElement(&buf, (char*) method);
  1104. while ( (string = va_arg(ap, const char *))!=NULL ) {
  1105. Tcl_DStringAppendElement(&buf, (char*) string);
  1106. }
  1107. va_end(ap);
  1108. result = Tcl_Eval(tcl.interp(), Tcl_DStringValue(&buf));
  1109. Tcl_DStringFree(&buf);
  1110. return result;
  1111. }
  1112. int
  1113. TclObject::Invokef(const char *format, ...)
  1114. {
  1115. static char buffer[1024]; /* XXX: individual command should not be
  1116.    * larger than 1023 */
  1117. sprintf(buffer, "%s ", name());
  1118. va_list ap;
  1119. va_start(ap, format);
  1120. vsprintf(&buffer[strlen(buffer)], format, ap);
  1121. return Tcl_Eval(Tcl::instance().interp(), buffer);
  1122. }
  1123. int
  1124. TclArguments::next(const char *&arg)
  1125. {
  1126. if (!more_args()) {
  1127. Tcl::instance().result("too few arguments");
  1128. add_error();
  1129. return TCL_ERROR;
  1130. }
  1131. arg = argv_[current_++];
  1132. if (arg==NULL) {
  1133. Tcl::instance().result("null argument");
  1134. add_error();
  1135. return TCL_ERROR;
  1136. }
  1137. return TCL_OK;
  1138. }
  1139. void
  1140. TclArguments::add_error() const
  1141. {
  1142. Tcl::instance().add_errorf("ninvoked from within '%s %s'",
  1143.    argv_[0], argv_[1]);
  1144. }
  1145. int
  1146. TclArguments::arg(int &value)
  1147. {
  1148. Tcl &tcl = Tcl::instance();
  1149. const char *arg;
  1150. if (next(arg)==TCL_ERROR) {
  1151. return TCL_ERROR;
  1152. }
  1153. if (Tcl_GetInt(tcl.interp(), (char*)arg, &value)==TCL_ERROR) {
  1154. add_error();
  1155. return TCL_ERROR;
  1156. }
  1157. return TCL_OK;
  1158. }
  1159. int
  1160. TclArguments::arg(unsigned int &value)
  1161. {
  1162. int iValue;
  1163. if (arg(iValue)==TCL_ERROR) return TCL_ERROR;
  1164. value = (unsigned int) iValue;
  1165. return TCL_OK;
  1166. }
  1167. int
  1168. TclArguments::arg(unsigned short &value)
  1169. {
  1170. int iValue;
  1171. if (arg(iValue)==TCL_ERROR) return TCL_ERROR;
  1172. value = (unsigned short) iValue;
  1173. return TCL_OK;
  1174. }
  1175. int
  1176. TclArguments::arg(double &value)
  1177. {
  1178. Tcl &tcl = Tcl::instance();
  1179. const char *arg;
  1180. if (next(arg)==TCL_ERROR) {
  1181. return TCL_ERROR;
  1182. }
  1183. if (Tcl_GetDouble(tcl.interp(), (char*) arg, &value)==TCL_ERROR) {
  1184. add_error();
  1185. return TCL_ERROR;
  1186. }
  1187. return TCL_OK;
  1188. }
  1189. int
  1190. TclArguments::arg(TclObject *&value)
  1191. {
  1192. Tcl &tcl = Tcl::instance();
  1193. const char *arg;
  1194. if (next(arg)==TCL_ERROR) {
  1195. return TCL_ERROR;
  1196. }
  1197. value = tcl.lookup(arg);
  1198. if (value==NULL) {
  1199. tcl.resultf("Invalid object name '%s'", arg);
  1200. add_error();
  1201. return TCL_ERROR;
  1202. }
  1203. return TCL_OK;
  1204. }
  1205. int
  1206. TclArguments::arg(const char *&value)
  1207. {
  1208. if (next(value)==TCL_ERROR) {
  1209. return TCL_ERROR;
  1210. }
  1211. return TCL_OK;
  1212. }