1/* 2 * ------------------------------------------------------------------------ 3 * PACKAGE: [incr Tcl] 4 * DESCRIPTION: Object-Oriented Extensions to Tcl 5 * 6 * [incr Tcl] provides object-oriented extensions to Tcl, much as 7 * C++ provides object-oriented extensions to C. It provides a means 8 * of encapsulating related procedures together with their shared data 9 * in a local namespace that is hidden from the outside world. It 10 * promotes code re-use through inheritance. More than anything else, 11 * it encourages better organization of Tcl applications through the 12 * object-oriented paradigm, leading to code that is easier to 13 * understand and maintain. 14 * 15 * This part handles ensembles, which support compound commands in Tcl. 16 * The usual "info" command is an ensemble with parts like "info body" 17 * and "info globals". Extension developers can extend commands like 18 * "info" by adding their own parts to the ensemble. 19 * 20 * ======================================================================== 21 * AUTHOR: Michael J. McLennan 22 * Bell Labs Innovations for Lucent Technologies 23 * mmclennan@lucent.com 24 * http://www.tcltk.com/itcl 25 * 26 * RCS: $Id: itcl_ensemble.c,v 1.13 2008/12/15 20:02:58 andreas_kupries Exp $ 27 * ======================================================================== 28 * Copyright (c) 1993-1998 Lucent Technologies, Inc. 29 * ------------------------------------------------------------------------ 30 * See the file "license.terms" for information on usage and redistribution 31 * of this file, and for a DISCLAIMER OF ALL WARRANTIES. 32 */ 33#include "itclInt.h" 34 35/* 36 * Data used to represent an ensemble: 37 */ 38struct Ensemble; 39typedef struct EnsemblePart { 40 char *name; /* name of this part */ 41 int minChars; /* chars needed to uniquely identify part */ 42 Command *cmdPtr; /* command handling this part */ 43 char *usage; /* usage string describing syntax */ 44 struct Ensemble* ensemble; /* ensemble containing this part */ 45} EnsemblePart; 46 47/* 48 * Data used to represent an ensemble: 49 */ 50typedef struct Ensemble { 51 Tcl_Interp *interp; /* interpreter containing this ensemble */ 52 EnsemblePart **parts; /* list of parts in this ensemble */ 53 int numParts; /* number of parts in part list */ 54 int maxParts; /* current size of parts list */ 55 Tcl_Command cmd; /* command representing this ensemble */ 56 EnsemblePart* parent; /* parent part for sub-ensembles 57 * NULL => toplevel ensemble */ 58} Ensemble; 59 60/* 61 * Data shared by ensemble access commands and ensemble parser: 62 */ 63typedef struct EnsembleParser { 64 Tcl_Interp* master; /* master interp containing ensembles */ 65 Tcl_Interp* parser; /* slave interp for parsing */ 66 Ensemble* ensData; /* add parts to this ensemble */ 67} EnsembleParser; 68 69/* 70 * Declarations for local procedures to this file: 71 */ 72static void FreeEnsInvocInternalRep _ANSI_ARGS_((Tcl_Obj *objPtr)); 73static void DupEnsInvocInternalRep _ANSI_ARGS_((Tcl_Obj *objPtr, 74 Tcl_Obj *copyPtr)); 75static void UpdateStringOfEnsInvoc _ANSI_ARGS_((Tcl_Obj *objPtr)); 76static int SetEnsInvocFromAny _ANSI_ARGS_((Tcl_Interp *interp, 77 Tcl_Obj *objPtr)); 78 79/* 80 * This structure defines a Tcl object type that takes the 81 * place of a part name during ensemble invocations. When an 82 * error occurs and the caller tries to print objv[0], it will 83 * get a string that contains a complete path to the ensemble 84 * part. 85 */ 86Tcl_ObjType itclEnsInvocType = { 87 "ensembleInvoc", /* name */ 88 FreeEnsInvocInternalRep, /* freeIntRepProc */ 89 DupEnsInvocInternalRep, /* dupIntRepProc */ 90 UpdateStringOfEnsInvoc, /* updateStringProc */ 91 SetEnsInvocFromAny /* setFromAnyProc */ 92}; 93 94 95/* 96 * Forward declarations for the procedures used in this file. 97 */ 98static void GetEnsembleUsage _ANSI_ARGS_((Ensemble *ensData, 99 Tcl_Obj *objPtr)); 100 101static void GetEnsemblePartUsage _ANSI_ARGS_((EnsemblePart *ensPart, 102 Tcl_Obj *objPtr)); 103 104static int CreateEnsemble _ANSI_ARGS_((Tcl_Interp *interp, 105 Ensemble *parentEnsData, char *ensName)); 106 107static int AddEnsemblePart _ANSI_ARGS_((Tcl_Interp *interp, 108 Ensemble* ensData, CONST char* partName, CONST char* usageInfo, 109 Tcl_ObjCmdProc *objProc, ClientData clientData, 110 Tcl_CmdDeleteProc *deleteProc, EnsemblePart **rVal)); 111 112static void DeleteEnsemble _ANSI_ARGS_((ClientData clientData)); 113 114static int FindEnsemble _ANSI_ARGS_((Tcl_Interp *interp, char **nameArgv, 115 int nameArgc, Ensemble** ensDataPtr)); 116 117static int CreateEnsemblePart _ANSI_ARGS_((Tcl_Interp *interp, 118 Ensemble *ensData, CONST char* partName, EnsemblePart **ensPartPtr)); 119 120static void DeleteEnsemblePart _ANSI_ARGS_((EnsemblePart *ensPart)); 121 122static int FindEnsemblePart _ANSI_ARGS_((Tcl_Interp *interp, 123 Ensemble *ensData, CONST char* partName, EnsemblePart **rensPart)); 124 125static int FindEnsemblePartIndex _ANSI_ARGS_((Ensemble *ensData, 126 CONST char *partName, int *posPtr)); 127 128static void ComputeMinChars _ANSI_ARGS_((Ensemble *ensData, int pos)); 129 130static int HandleEnsemble _ANSI_ARGS_((ClientData clientData, 131 Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])); 132 133static EnsembleParser* GetEnsembleParser _ANSI_ARGS_((Tcl_Interp *interp)); 134 135static void DeleteEnsParser _ANSI_ARGS_((ClientData clientData, 136 Tcl_Interp* interp)); 137 138 139 140/* 141 *---------------------------------------------------------------------- 142 * 143 * Itcl_EnsembleInit -- 144 * 145 * Called when any interpreter is created to make sure that 146 * things are properly set up for ensembles. 147 * 148 * Results: 149 * Returns TCL_OK if successful, and TCL_ERROR if anything goes 150 * wrong. 151 * 152 * Side effects: 153 * On the first call, the "ensemble" object type is registered 154 * with the Tcl compiler. If an error is encountered, an error 155 * is left as the result in the interpreter. 156 * 157 *---------------------------------------------------------------------- 158 */ 159 /* ARGSUSED */ 160int 161Itcl_EnsembleInit(interp) 162 Tcl_Interp *interp; /* interpreter being initialized */ 163{ 164 if (Tcl_GetObjType(itclEnsInvocType.name) == NULL) { 165 Tcl_RegisterObjType(&itclEnsInvocType); 166 } 167 168 Tcl_CreateObjCommand(interp, "::itcl::ensemble", 169 Itcl_EnsembleCmd, (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL); 170 171 return TCL_OK; 172} 173 174 175/* 176 *---------------------------------------------------------------------- 177 * 178 * Itcl_CreateEnsemble -- 179 * 180 * Creates an ensemble command, or adds a sub-ensemble to an 181 * existing ensemble command. The ensemble name is a space- 182 * separated list. The first word in the list is the command 183 * name for the top-level ensemble. Other names do not have 184 * commands associated with them; they are merely sub-ensembles 185 * within the ensemble. So a name like "a::b::foo bar baz" 186 * represents an ensemble command called "foo" in the namespace 187 * "a::b" that has a sub-ensemble "bar", that has a sub-ensemble 188 * "baz". 189 * 190 * If the name is a single word, then this procedure creates 191 * a top-level ensemble and installs an access command for it. 192 * If a command already exists with that name, it is deleted. 193 * 194 * If the name has more than one word, then the leading words 195 * are treated as a path name for an existing ensemble. The 196 * last word is treated as the name for a new sub-ensemble. 197 * If an part already exists with that name, it is an error. 198 * 199 * Results: 200 * Returns TCL_OK if successful, and TCL_ERROR if anything goes 201 * wrong. 202 * 203 * Side effects: 204 * If an error is encountered, an error is left as the result 205 * in the interpreter. 206 * 207 *---------------------------------------------------------------------- 208 */ 209int 210Itcl_CreateEnsemble(interp, ensName) 211 Tcl_Interp *interp; /* interpreter to be updated */ 212 CONST char* ensName; /* name of the new ensemble */ 213{ 214 char **nameArgv = NULL; 215 int nameArgc; 216 Ensemble *parentEnsData; 217 Tcl_DString buffer; 218 219 /* 220 * Split the ensemble name into its path components. 221 */ 222 if (Tcl_SplitList(interp, (CONST84 char *)ensName, &nameArgc, 223 &nameArgv) != TCL_OK) { 224 goto ensCreateFail; 225 } 226 if (nameArgc < 1) { 227 Tcl_AppendResult(interp, 228 "invalid ensemble name \"", ensName, "\"", 229 (char*)NULL); 230 goto ensCreateFail; 231 } 232 233 /* 234 * If there is more than one path component, then follow 235 * the path down to the last component, to find the containing 236 * ensemble. 237 */ 238 parentEnsData = NULL; 239 if (nameArgc > 1) { 240 if (FindEnsemble(interp, nameArgv, nameArgc-1, &parentEnsData) 241 != TCL_OK) { 242 goto ensCreateFail; 243 } 244 245 if (parentEnsData == NULL) { 246 char *pname = Tcl_Merge(nameArgc-1, nameArgv); 247 Tcl_AppendResult(interp, 248 "invalid ensemble name \"", pname, "\"", 249 (char*)NULL); 250 ckfree(pname); 251 goto ensCreateFail; 252 } 253 } 254 255 /* 256 * Create the ensemble. 257 */ 258 if (CreateEnsemble(interp, parentEnsData, nameArgv[nameArgc-1]) 259 != TCL_OK) { 260 goto ensCreateFail; 261 } 262 263 ckfree((char*)nameArgv); 264 return TCL_OK; 265 266ensCreateFail: 267 if (nameArgv) { 268 ckfree((char*)nameArgv); 269 } 270 Tcl_DStringInit(&buffer); 271 Tcl_DStringAppend(&buffer, "\n (while creating ensemble \"", -1); 272 Tcl_DStringAppend(&buffer, ensName, -1); 273 Tcl_DStringAppend(&buffer, "\")", -1); 274 Tcl_AddObjErrorInfo(interp, Tcl_DStringValue(&buffer), -1); 275 Tcl_DStringFree(&buffer); 276 277 return TCL_ERROR; 278} 279 280 281/* 282 *---------------------------------------------------------------------- 283 * 284 * Itcl_AddEnsemblePart -- 285 * 286 * Adds a part to an ensemble which has been created by 287 * Itcl_CreateEnsemble. Ensembles are addressed by name, as 288 * described in Itcl_CreateEnsemble. 289 * 290 * If the ensemble already has a part with the specified name, 291 * this procedure returns an error. Otherwise, it adds a new 292 * part to the ensemble. 293 * 294 * Any client data specified is automatically passed to the 295 * handling procedure whenever the part is invoked. It is 296 * automatically destroyed by the deleteProc when the part is 297 * deleted. 298 * 299 * Results: 300 * Returns TCL_OK if successful, and TCL_ERROR if anything goes 301 * wrong. 302 * 303 * Side effects: 304 * If an error is encountered, an error is left as the result 305 * in the interpreter. 306 * 307 *---------------------------------------------------------------------- 308 */ 309int 310Itcl_AddEnsemblePart(interp, ensName, partName, usageInfo, 311 objProc, clientData, deleteProc) 312 313 Tcl_Interp *interp; /* interpreter to be updated */ 314 CONST char* ensName; /* ensemble containing this part */ 315 CONST char* partName; /* name of the new part */ 316 CONST char* usageInfo; /* usage info for argument list */ 317 Tcl_ObjCmdProc *objProc; /* handling procedure for part */ 318 ClientData clientData; /* client data associated with part */ 319 Tcl_CmdDeleteProc *deleteProc; /* procedure used to destroy client data */ 320{ 321 char **nameArgv = NULL; 322 int nameArgc; 323 Ensemble *ensData; 324 EnsemblePart *ensPart; 325 Tcl_DString buffer; 326 327 /* 328 * Parse the ensemble name and look for a containing ensemble. 329 */ 330 if (Tcl_SplitList(interp, (CONST84 char *)ensName, &nameArgc, 331 &nameArgv) != TCL_OK) { 332 goto ensPartFail; 333 } 334 if (FindEnsemble(interp, nameArgv, nameArgc, &ensData) != TCL_OK) { 335 goto ensPartFail; 336 } 337 338 if (ensData == NULL) { 339 char *pname = Tcl_Merge(nameArgc, nameArgv); 340 Tcl_AppendResult(interp, 341 "invalid ensemble name \"", pname, "\"", 342 (char*)NULL); 343 ckfree(pname); 344 goto ensPartFail; 345 } 346 347 /* 348 * Install the new part into the part list. 349 */ 350 if (AddEnsemblePart(interp, ensData, partName, usageInfo, 351 objProc, clientData, deleteProc, &ensPart) != TCL_OK) { 352 goto ensPartFail; 353 } 354 355 ckfree((char*)nameArgv); 356 return TCL_OK; 357 358ensPartFail: 359 if (nameArgv) { 360 ckfree((char*)nameArgv); 361 } 362 Tcl_DStringInit(&buffer); 363 Tcl_DStringAppend(&buffer, "\n (while adding to ensemble \"", -1); 364 Tcl_DStringAppend(&buffer, ensName, -1); 365 Tcl_DStringAppend(&buffer, "\")", -1); 366 Tcl_AddObjErrorInfo(interp, Tcl_DStringValue(&buffer), -1); 367 Tcl_DStringFree(&buffer); 368 369 return TCL_ERROR; 370} 371 372 373/* 374 *---------------------------------------------------------------------- 375 * 376 * Itcl_GetEnsemblePart -- 377 * 378 * Looks for a part within an ensemble, and returns information 379 * about it. 380 * 381 * Results: 382 * If the ensemble and its part are found, this procedure 383 * loads information about the part into the "infoPtr" structure 384 * and returns 1. Otherwise, it returns 0. 385 * 386 * Side effects: 387 * None. 388 * 389 *---------------------------------------------------------------------- 390 */ 391int 392Itcl_GetEnsemblePart(interp, ensName, partName, infoPtr) 393 Tcl_Interp *interp; /* interpreter to be updated */ 394 CONST char *ensName; /* ensemble containing the part */ 395 CONST char *partName; /* name of the desired part */ 396 Tcl_CmdInfo *infoPtr; /* returns: info associated with part */ 397{ 398 char **nameArgv = NULL; 399 int nameArgc; 400 Ensemble *ensData; 401 EnsemblePart *ensPart; 402 Command *cmdPtr; 403 Itcl_InterpState state; 404 405 /* 406 * Parse the ensemble name and look for a containing ensemble. 407 * Save the interpreter state before we do this. If we get any 408 * errors, we don't want them to affect the interpreter. 409 */ 410 state = Itcl_SaveInterpState(interp, TCL_OK); 411 412 if (Tcl_SplitList(interp, (CONST84 char *)ensName, &nameArgc, 413 &nameArgv) != TCL_OK) { 414 goto ensGetFail; 415 } 416 if (FindEnsemble(interp, nameArgv, nameArgc, &ensData) != TCL_OK) { 417 goto ensGetFail; 418 } 419 if (ensData == NULL) { 420 goto ensGetFail; 421 } 422 423 /* 424 * Look for a part with the desired name. If found, load 425 * its data into the "infoPtr" structure. 426 */ 427 if (FindEnsemblePart(interp, ensData, partName, &ensPart) 428 != TCL_OK || ensPart == NULL) { 429 goto ensGetFail; 430 } 431 432 cmdPtr = ensPart->cmdPtr; 433 infoPtr->isNativeObjectProc = (cmdPtr->objProc != TclInvokeStringCommand); 434 infoPtr->objProc = cmdPtr->objProc; 435 infoPtr->objClientData = cmdPtr->objClientData; 436 infoPtr->proc = cmdPtr->proc; 437 infoPtr->clientData = cmdPtr->clientData; 438 infoPtr->deleteProc = cmdPtr->deleteProc; 439 infoPtr->deleteData = cmdPtr->deleteData; 440 infoPtr->namespacePtr = (Tcl_Namespace*)cmdPtr->nsPtr; 441 442 Itcl_DiscardInterpState(state); 443 return 1; 444 445ensGetFail: 446 Itcl_RestoreInterpState(interp, state); 447 return 0; 448} 449 450 451/* 452 *---------------------------------------------------------------------- 453 * 454 * Itcl_IsEnsemble -- 455 * 456 * Determines whether or not an existing command is an ensemble. 457 * 458 * Results: 459 * Returns non-zero if the command is an ensemble, and zero 460 * otherwise. 461 * 462 * Side effects: 463 * None. 464 * 465 *---------------------------------------------------------------------- 466 */ 467int 468Itcl_IsEnsemble(infoPtr) 469 Tcl_CmdInfo* infoPtr; /* command info from Tcl_GetCommandInfo() */ 470{ 471 if (infoPtr) { 472 return (infoPtr->deleteProc == DeleteEnsemble); 473 } 474 return 0; 475} 476 477 478/* 479 *---------------------------------------------------------------------- 480 * 481 * Itcl_GetEnsembleUsage -- 482 * 483 * Returns a summary of all of the parts of an ensemble and 484 * the meaning of their arguments. Each part is listed on 485 * a separate line. Having this summary is sometimes useful 486 * when building error messages for the "@error" handler in 487 * an ensemble. 488 * 489 * Ensembles are accessed by name, as described in 490 * Itcl_CreateEnsemble. 491 * 492 * Results: 493 * If the ensemble is found, its usage information is appended 494 * onto the object "objPtr", and this procedure returns 495 * non-zero. It is the responsibility of the caller to 496 * initialize and free the object. If anything goes wrong, 497 * this procedure returns 0. 498 * 499 * Side effects: 500 * Object passed in is modified. 501 * 502 *---------------------------------------------------------------------- 503 */ 504int 505Itcl_GetEnsembleUsage(interp, ensName, objPtr) 506 Tcl_Interp *interp; /* interpreter containing the ensemble */ 507 CONST char *ensName; /* name of the ensemble */ 508 Tcl_Obj *objPtr; /* returns: summary of usage info */ 509{ 510 char **nameArgv = NULL; 511 int nameArgc; 512 Ensemble *ensData; 513 Itcl_InterpState state; 514 515 /* 516 * Parse the ensemble name and look for the ensemble. 517 * Save the interpreter state before we do this. If we get 518 * any errors, we don't want them to affect the interpreter. 519 */ 520 state = Itcl_SaveInterpState(interp, TCL_OK); 521 522 if (Tcl_SplitList(interp, (CONST84 char *)ensName, &nameArgc, 523 &nameArgv) != TCL_OK) { 524 goto ensUsageFail; 525 } 526 if (FindEnsemble(interp, nameArgv, nameArgc, &ensData) != TCL_OK) { 527 goto ensUsageFail; 528 } 529 if (ensData == NULL) { 530 goto ensUsageFail; 531 } 532 533 /* 534 * Add a summary of usage information to the return buffer. 535 */ 536 GetEnsembleUsage(ensData, objPtr); 537 538 Itcl_DiscardInterpState(state); 539 return 1; 540 541ensUsageFail: 542 Itcl_RestoreInterpState(interp, state); 543 return 0; 544} 545 546 547/* 548 *---------------------------------------------------------------------- 549 * 550 * Itcl_GetEnsembleUsageForObj -- 551 * 552 * Returns a summary of all of the parts of an ensemble and 553 * the meaning of their arguments. This procedure is just 554 * like Itcl_GetEnsembleUsage, but it determines the desired 555 * ensemble from a command line argument. The argument should 556 * be the first argument on the command line--the ensemble 557 * command or one of its parts. 558 * 559 * Results: 560 * If the ensemble is found, its usage information is appended 561 * onto the object "objPtr", and this procedure returns 562 * non-zero. It is the responsibility of the caller to 563 * initialize and free the object. If anything goes wrong, 564 * this procedure returns 0. 565 * 566 * Side effects: 567 * Object passed in is modified. 568 * 569 *---------------------------------------------------------------------- 570 */ 571int 572Itcl_GetEnsembleUsageForObj(interp, ensObjPtr, objPtr) 573 Tcl_Interp *interp; /* interpreter containing the ensemble */ 574 Tcl_Obj *ensObjPtr; /* argument representing ensemble */ 575 Tcl_Obj *objPtr; /* returns: summary of usage info */ 576{ 577 Ensemble *ensData; 578 Tcl_Obj *chainObj; 579 Tcl_Command cmd; 580 Command *cmdPtr; 581 582 /* 583 * If the argument is an ensemble part, then follow the chain 584 * back to the command word for the entire ensemble. 585 */ 586 chainObj = ensObjPtr; 587 while (chainObj && chainObj->typePtr == &itclEnsInvocType) { 588 chainObj = (Tcl_Obj*)chainObj->internalRep.twoPtrValue.ptr2; 589 } 590 591 if (chainObj) { 592 cmd = Tcl_GetCommandFromObj(interp, chainObj); 593 cmdPtr = (Command*)cmd; 594 if (cmdPtr->deleteProc == DeleteEnsemble) { 595 ensData = (Ensemble*)cmdPtr->objClientData; 596 GetEnsembleUsage(ensData, objPtr); 597 return 1; 598 } 599 } 600 return 0; 601} 602 603 604/* 605 *---------------------------------------------------------------------- 606 * 607 * GetEnsembleUsage -- 608 * 609 * 610 * Returns a summary of all of the parts of an ensemble and 611 * the meaning of their arguments. Each part is listed on 612 * a separate line. This procedure is used internally to 613 * generate usage information for error messages. 614 * 615 * Results: 616 * Appends usage information onto the object in "objPtr". 617 * 618 * Side effects: 619 * None. 620 * 621 *---------------------------------------------------------------------- 622 */ 623static void 624GetEnsembleUsage(ensData, objPtr) 625 Ensemble *ensData; /* ensemble data */ 626 Tcl_Obj *objPtr; /* returns: summary of usage info */ 627{ 628 char *spaces = " "; 629 int isOpenEnded = 0; 630 631 int i; 632 EnsemblePart *ensPart; 633 634 for (i=0; i < ensData->numParts; i++) { 635 ensPart = ensData->parts[i]; 636 637 if (*ensPart->name == '@' && strcmp(ensPart->name,"@error") == 0) { 638 isOpenEnded = 1; 639 } 640 else { 641 Tcl_AppendToObj(objPtr, spaces, -1); 642 GetEnsemblePartUsage(ensPart, objPtr); 643 spaces = "\n "; 644 } 645 } 646 if (isOpenEnded) { 647 Tcl_AppendToObj(objPtr, 648 "\n...and others described on the man page", -1); 649 } 650} 651 652 653/* 654 *---------------------------------------------------------------------- 655 * 656 * GetEnsemblePartUsage -- 657 * 658 * Determines the usage for a single part within an ensemble, 659 * and appends a summary onto a dynamic string. The usage 660 * is a combination of the part name and the argument summary. 661 * It is the caller's responsibility to initialize and free 662 * the dynamic string. 663 * 664 * Results: 665 * Returns usage information in the object "objPtr". 666 * 667 * Side effects: 668 * None. 669 * 670 *---------------------------------------------------------------------- 671 */ 672static void 673GetEnsemblePartUsage(ensPart, objPtr) 674 EnsemblePart *ensPart; /* ensemble part for usage info */ 675 Tcl_Obj *objPtr; /* returns: usage information */ 676{ 677 EnsemblePart *part; 678 Command *cmdPtr; 679 char *name; 680 Itcl_List trail; 681 Itcl_ListElem *elem; 682 Tcl_DString buffer; 683 684 /* 685 * Build the trail of ensemble names leading to this part. 686 */ 687 Tcl_DStringInit(&buffer); 688 Itcl_InitList(&trail); 689 for (part=ensPart; part; part=part->ensemble->parent) { 690 Itcl_InsertList(&trail, (ClientData)part); 691 } 692 693 cmdPtr = (Command*)ensPart->ensemble->cmd; 694 name = Tcl_GetHashKey(cmdPtr->hPtr->tablePtr, cmdPtr->hPtr); 695 Tcl_DStringAppendElement(&buffer, name); 696 697 for (elem=Itcl_FirstListElem(&trail); elem; elem=Itcl_NextListElem(elem)) { 698 part = (EnsemblePart*)Itcl_GetListValue(elem); 699 Tcl_DStringAppendElement(&buffer, part->name); 700 } 701 Itcl_DeleteList(&trail); 702 703 /* 704 * If the part has usage info, use it directly. 705 */ 706 if (ensPart->usage && *ensPart->usage != '\0') { 707 Tcl_DStringAppend(&buffer, " ", 1); 708 Tcl_DStringAppend(&buffer, ensPart->usage, -1); 709 } 710 711 /* 712 * If the part is itself an ensemble, summarize its usage. 713 */ 714 else if (ensPart->cmdPtr && 715 ensPart->cmdPtr->deleteProc == DeleteEnsemble) { 716 Tcl_DStringAppend(&buffer, " option ?arg arg ...?", 21); 717 } 718 719 Tcl_AppendToObj(objPtr, Tcl_DStringValue(&buffer), 720 Tcl_DStringLength(&buffer)); 721 722 Tcl_DStringFree(&buffer); 723} 724 725 726/* 727 *---------------------------------------------------------------------- 728 * 729 * CreateEnsemble -- 730 * 731 * Creates an ensemble command, or adds a sub-ensemble to an 732 * existing ensemble command. Works like Itcl_CreateEnsemble, 733 * except that the ensemble name is a single name, not a path. 734 * If a parent ensemble is specified, then a new ensemble is 735 * added to that parent. If a part already exists with the 736 * same name, it is an error. If a parent ensemble is not 737 * specified, then a top-level ensemble is created. If a 738 * command already exists with the same name, it is deleted. 739 * 740 * Results: 741 * Returns TCL_OK if successful, and TCL_ERROR if anything goes 742 * wrong. 743 * 744 * Side effects: 745 * If an error is encountered, an error is left as the result 746 * in the interpreter. 747 * 748 *---------------------------------------------------------------------- 749 */ 750static int 751CreateEnsemble(interp, parentEnsData, ensName) 752 Tcl_Interp *interp; /* interpreter to be updated */ 753 Ensemble *parentEnsData; /* parent ensemble or NULL */ 754 char *ensName; /* name of the new ensemble */ 755{ 756 Ensemble *ensData; 757 EnsemblePart *ensPart; 758 Command *cmdPtr; 759 Tcl_CmdInfo cmdInfo; 760 761 /* 762 * Create the data associated with the ensemble. 763 */ 764 ensData = (Ensemble*)ckalloc(sizeof(Ensemble)); 765 ensData->interp = interp; 766 ensData->numParts = 0; 767 ensData->maxParts = 10; 768 ensData->parts = (EnsemblePart**)ckalloc( 769 (unsigned)(ensData->maxParts*sizeof(EnsemblePart*)) 770 ); 771 ensData->cmd = NULL; 772 ensData->parent = NULL; 773 774 /* 775 * If there is no parent data, then this is a top-level 776 * ensemble. Create the ensemble by installing its access 777 * command. 778 * 779 * BE CAREFUL: Set the string-based proc to the wrapper 780 * procedure TclInvokeObjectCommand. Otherwise, the 781 * ensemble command may fail. For example, it will fail 782 * when invoked as a hidden command. 783 */ 784 if (parentEnsData == NULL) { 785 ensData->cmd = Tcl_CreateObjCommand(interp, ensName, 786 HandleEnsemble, (ClientData)ensData, DeleteEnsemble); 787 788 if (Tcl_GetCommandInfo(interp, ensName, &cmdInfo)) { 789 cmdInfo.proc = TclInvokeObjectCommand; 790 Tcl_SetCommandInfo(interp, ensName, &cmdInfo); 791 } 792 return TCL_OK; 793 } 794 795 /* 796 * Otherwise, this ensemble is contained within another parent. 797 * Install the new ensemble as a part within its parent. 798 */ 799 if (CreateEnsemblePart(interp, parentEnsData, ensName, &ensPart) 800 != TCL_OK) { 801 DeleteEnsemble((ClientData)ensData); 802 return TCL_ERROR; 803 } 804 805 ensData->cmd = parentEnsData->cmd; 806 ensData->parent = ensPart; 807 808 /* 809 * Initialize non-NULL data only. This allows us to handle the 810 * structure differences between versions better. 811 */ 812 cmdPtr = (Command *) ckalloc(sizeof(Command)); 813 memset((VOID *) cmdPtr, 0, sizeof(Command)); 814 cmdPtr->nsPtr = ((Command *) ensData->cmd)->nsPtr; 815 cmdPtr->objProc = HandleEnsemble; 816 cmdPtr->objClientData = (ClientData)ensData; 817 cmdPtr->deleteProc = DeleteEnsemble; 818 cmdPtr->deleteData = cmdPtr->objClientData; 819 820 ensPart->cmdPtr = cmdPtr; 821 822 return TCL_OK; 823} 824 825 826/* 827 *---------------------------------------------------------------------- 828 * 829 * AddEnsemblePart -- 830 * 831 * Adds a part to an existing ensemble. Works like 832 * Itcl_AddEnsemblePart, but the part name is a single word, 833 * not a path. 834 * 835 * If the ensemble already has a part with the specified name, 836 * this procedure returns an error. Otherwise, it adds a new 837 * part to the ensemble. 838 * 839 * Any client data specified is automatically passed to the 840 * handling procedure whenever the part is invoked. It is 841 * automatically destroyed by the deleteProc when the part is 842 * deleted. 843 * 844 * Results: 845 * Returns TCL_OK if successful, along with a pointer to the 846 * new part. Returns TCL_ERROR if anything goes wrong. 847 * 848 * Side effects: 849 * If an error is encountered, an error is left as the result 850 * in the interpreter. 851 * 852 *---------------------------------------------------------------------- 853 */ 854static int 855AddEnsemblePart(interp, ensData, partName, usageInfo, 856 objProc, clientData, deleteProc, rVal) 857 858 Tcl_Interp *interp; /* interpreter to be updated */ 859 Ensemble* ensData; /* ensemble that will contain this part */ 860 CONST char* partName; /* name of the new part */ 861 CONST char* usageInfo; /* usage info for argument list */ 862 Tcl_ObjCmdProc *objProc; /* handling procedure for part */ 863 ClientData clientData; /* client data associated with part */ 864 Tcl_CmdDeleteProc *deleteProc; /* procedure used to destroy client data */ 865 EnsemblePart **rVal; /* returns: new ensemble part */ 866{ 867 EnsemblePart *ensPart; 868 Command *cmdPtr; 869 870 /* 871 * Install the new part into the part list. 872 */ 873 if (CreateEnsemblePart(interp, ensData, partName, &ensPart) != TCL_OK) { 874 return TCL_ERROR; 875 } 876 877 if (usageInfo) { 878 ensPart->usage = ckalloc((unsigned)(strlen(usageInfo)+1)); 879 strcpy(ensPart->usage, usageInfo); 880 } 881 882 /* 883 * Initialize non-NULL data only. This allows us to handle the 884 * structure differences between versions better. 885 */ 886 cmdPtr = (Command *) ckalloc(sizeof(Command)); 887 memset((VOID *) cmdPtr, 0, sizeof(Command)); 888 cmdPtr->nsPtr = ((Command *) ensData->cmd)->nsPtr; 889 cmdPtr->objProc = objProc; 890 cmdPtr->objClientData = (ClientData)clientData; 891 cmdPtr->deleteProc = deleteProc; 892 cmdPtr->deleteData = (ClientData)clientData; 893 894 ensPart->cmdPtr = cmdPtr; 895 *rVal = ensPart; 896 897 return TCL_OK; 898} 899 900 901/* 902 *---------------------------------------------------------------------- 903 * 904 * DeleteEnsemble -- 905 * 906 * Invoked when the command associated with an ensemble is 907 * destroyed, to delete the ensemble. Destroys all parts 908 * included in the ensemble, and frees all memory associated 909 * with it. 910 * 911 * Results: 912 * None. 913 * 914 * Side effects: 915 * None. 916 * 917 *---------------------------------------------------------------------- 918 */ 919static void 920DeleteEnsemble(clientData) 921 ClientData clientData; /* ensemble data */ 922{ 923 Ensemble* ensData = (Ensemble*)clientData; 924 925 /* 926 * BE CAREFUL: Each ensemble part removes itself from the list. 927 * So keep deleting the first part until all parts are gone. 928 */ 929 while (ensData->numParts > 0) { 930 DeleteEnsemblePart(ensData->parts[0]); 931 } 932 ckfree((char*)ensData->parts); 933 ckfree((char*)ensData); 934} 935 936 937/* 938 *---------------------------------------------------------------------- 939 * 940 * FindEnsemble -- 941 * 942 * Searches for an ensemble command and follows a path to 943 * sub-ensembles. 944 * 945 * Results: 946 * Returns TCL_OK if the ensemble was found, along with a 947 * pointer to the ensemble data in "ensDataPtr". Returns 948 * TCL_ERROR if anything goes wrong. 949 * 950 * Side effects: 951 * If anything goes wrong, this procedure returns an error 952 * message as the result in the interpreter. 953 * 954 *---------------------------------------------------------------------- 955 */ 956static int 957FindEnsemble(interp, nameArgv, nameArgc, ensDataPtr) 958 Tcl_Interp *interp; /* interpreter containing the ensemble */ 959 char **nameArgv; /* path of names leading to ensemble */ 960 int nameArgc; /* number of strings in nameArgv */ 961 Ensemble** ensDataPtr; /* returns: ensemble data */ 962{ 963 int i; 964 Command* cmdPtr; 965 Ensemble *ensData; 966 EnsemblePart *ensPart; 967 968 *ensDataPtr = NULL; /* assume that no data will be found */ 969 970 /* 971 * If there are no names in the path, then return an error. 972 */ 973 if (nameArgc < 1) { 974 Tcl_AppendToObj(Tcl_GetObjResult(interp), 975 "invalid ensemble name \"\"", -1); 976 return TCL_ERROR; 977 } 978 979 /* 980 * Use the first name to find the command for the top-level 981 * ensemble. 982 */ 983 cmdPtr = (Command*) Tcl_FindCommand(interp, nameArgv[0], 984 (Tcl_Namespace*)NULL, TCL_LEAVE_ERR_MSG); 985 986 if (cmdPtr == NULL || cmdPtr->deleteProc != DeleteEnsemble) { 987 Tcl_AppendResult(interp, 988 "command \"", nameArgv[0], "\" is not an ensemble", 989 (char*)NULL); 990 return TCL_ERROR; 991 } 992 ensData = (Ensemble*)cmdPtr->objClientData; 993 994 /* 995 * Follow the trail of sub-ensemble names. 996 */ 997 for (i=1; i < nameArgc; i++) { 998 if (FindEnsemblePart(interp, ensData, nameArgv[i], &ensPart) 999 != TCL_OK) { 1000 return TCL_ERROR; 1001 } 1002 if (ensPart == NULL) { 1003 char *pname = Tcl_Merge(i, nameArgv); 1004 Tcl_AppendResult(interp, 1005 "invalid ensemble name \"", pname, "\"", 1006 (char*)NULL); 1007 ckfree(pname); 1008 return TCL_ERROR; 1009 } 1010 1011 cmdPtr = ensPart->cmdPtr; 1012 if (cmdPtr == NULL || cmdPtr->deleteProc != DeleteEnsemble) { 1013 Tcl_AppendResult(interp, 1014 "part \"", nameArgv[i], "\" is not an ensemble", 1015 (char*)NULL); 1016 return TCL_ERROR; 1017 } 1018 ensData = (Ensemble*)cmdPtr->objClientData; 1019 } 1020 *ensDataPtr = ensData; 1021 1022 return TCL_OK; 1023} 1024 1025 1026/* 1027 *---------------------------------------------------------------------- 1028 * 1029 * CreateEnsemblePart -- 1030 * 1031 * Creates a new part within an ensemble. 1032 * 1033 * Results: 1034 * If successful, this procedure returns TCL_OK, along with a 1035 * pointer to the new part in "ensPartPtr". If a part with the 1036 * same name already exists, this procedure returns TCL_ERROR. 1037 * 1038 * Side effects: 1039 * If anything goes wrong, this procedure returns an error 1040 * message as the result in the interpreter. 1041 * 1042 *---------------------------------------------------------------------- 1043 */ 1044static int 1045CreateEnsemblePart(interp, ensData, partName, ensPartPtr) 1046 Tcl_Interp *interp; /* interpreter containing the ensemble */ 1047 Ensemble *ensData; /* ensemble being modified */ 1048 CONST char* partName; /* name of the new part */ 1049 EnsemblePart **ensPartPtr; /* returns: new ensemble part */ 1050{ 1051 int i, pos, size; 1052 EnsemblePart** partList; 1053 EnsemblePart* part; 1054 1055 /* 1056 * If a matching entry was found, then return an error. 1057 */ 1058 if (FindEnsemblePartIndex(ensData, partName, &pos)) { 1059 Tcl_AppendResult(interp, 1060 "part \"", partName, "\" already exists in ensemble", 1061 (char*)NULL); 1062 return TCL_ERROR; 1063 } 1064 1065 /* 1066 * Otherwise, make room for a new entry. Keep the parts in 1067 * lexicographical order, so we can search them quickly 1068 * later. 1069 */ 1070 if (ensData->numParts >= ensData->maxParts) { 1071 size = ensData->maxParts*sizeof(EnsemblePart*); 1072 partList = (EnsemblePart**)ckalloc((unsigned)2*size); 1073 memcpy((VOID*)partList, (VOID*)ensData->parts, (size_t)size); 1074 ckfree((char*)ensData->parts); 1075 1076 ensData->parts = partList; 1077 ensData->maxParts *= 2; 1078 } 1079 1080 for (i=ensData->numParts; i > pos; i--) { 1081 ensData->parts[i] = ensData->parts[i-1]; 1082 } 1083 ensData->numParts++; 1084 1085 part = (EnsemblePart*)ckalloc(sizeof(EnsemblePart)); 1086 part->name = (char*)ckalloc((unsigned)(strlen(partName)+1)); 1087 strcpy(part->name, partName); 1088 part->cmdPtr = NULL; 1089 part->usage = NULL; 1090 part->ensemble = ensData; 1091 1092 ensData->parts[pos] = part; 1093 1094 /* 1095 * Compare the new part against the one on either side of 1096 * it. Determine how many letters are needed in each part 1097 * to guarantee that an abbreviated form is unique. Update 1098 * the parts on either side as well, since they are influenced 1099 * by the new part. 1100 */ 1101 ComputeMinChars(ensData, pos); 1102 ComputeMinChars(ensData, pos-1); 1103 ComputeMinChars(ensData, pos+1); 1104 1105 *ensPartPtr = part; 1106 return TCL_OK; 1107} 1108 1109 1110/* 1111 *---------------------------------------------------------------------- 1112 * 1113 * DeleteEnsemblePart -- 1114 * 1115 * Deletes a single part from an ensemble. The part must have 1116 * been created previously by CreateEnsemblePart. 1117 * 1118 * If the part has a delete proc, then it is called to free the 1119 * associated client data. 1120 * 1121 * Results: 1122 * None. 1123 * 1124 * Side effects: 1125 * Delete proc is called. 1126 * 1127 *---------------------------------------------------------------------- 1128 */ 1129static void 1130DeleteEnsemblePart(ensPart) 1131 EnsemblePart *ensPart; /* part being destroyed */ 1132{ 1133 int i, pos; 1134 Command *cmdPtr; 1135 Ensemble *ensData; 1136 cmdPtr = ensPart->cmdPtr; 1137 1138 /* 1139 * If this part has a delete proc, then call it to free 1140 * up the client data. 1141 */ 1142 if (cmdPtr->deleteData && cmdPtr->deleteProc) { 1143 (*cmdPtr->deleteProc)(cmdPtr->deleteData); 1144 } 1145 ckfree((char*)cmdPtr); 1146 1147 /* 1148 * Find this part within its ensemble, and remove it from 1149 * the list of parts. 1150 */ 1151 if (FindEnsemblePartIndex(ensPart->ensemble, ensPart->name, &pos)) { 1152 ensData = ensPart->ensemble; 1153 for (i=pos; i < ensData->numParts-1; i++) { 1154 ensData->parts[i] = ensData->parts[i+1]; 1155 } 1156 ensData->numParts--; 1157 } 1158 1159 /* 1160 * Free the memory associated with the part. 1161 */ 1162 if (ensPart->usage) { 1163 ckfree(ensPart->usage); 1164 } 1165 ckfree(ensPart->name); 1166 ckfree((char*)ensPart); 1167} 1168 1169 1170/* 1171 *---------------------------------------------------------------------- 1172 * 1173 * FindEnsemblePart -- 1174 * 1175 * Searches for a part name within an ensemble. Recognizes 1176 * unique abbreviations for part names. 1177 * 1178 * Results: 1179 * If the part name is not a unique abbreviation, this procedure 1180 * returns TCL_ERROR. Otherwise, it returns TCL_OK. If the 1181 * part can be found, "rensPart" returns a pointer to the part. 1182 * Otherwise, it returns NULL. 1183 * 1184 * Side effects: 1185 * If anything goes wrong, this procedure returns an error 1186 * message as the result in the interpreter. 1187 * 1188 *---------------------------------------------------------------------- 1189 */ 1190static int 1191FindEnsemblePart(interp, ensData, partName, rensPart) 1192 Tcl_Interp *interp; /* interpreter containing the ensemble */ 1193 Ensemble *ensData; /* ensemble being searched */ 1194 CONST char* partName; /* name of the desired part */ 1195 EnsemblePart **rensPart; /* returns: pointer to the desired part */ 1196{ 1197 int pos = 0; 1198 int first, last, nlen; 1199 int i, cmp; 1200 1201 *rensPart = NULL; 1202 1203 /* 1204 * Search for the desired part name. 1205 * All parts are in lexicographical order, so use a 1206 * binary search to find the part quickly. Match only 1207 * as many characters as are included in the specified 1208 * part name. 1209 */ 1210 first = 0; 1211 last = ensData->numParts-1; 1212 nlen = strlen(partName); 1213 1214 while (last >= first) { 1215 pos = (first+last)/2; 1216 if (*partName == *ensData->parts[pos]->name) { 1217 cmp = strncmp(partName, ensData->parts[pos]->name, nlen); 1218 if (cmp == 0) { 1219 break; /* found it! */ 1220 } 1221 } 1222 else if (*partName < *ensData->parts[pos]->name) { 1223 cmp = -1; 1224 } 1225 else { 1226 cmp = 1; 1227 } 1228 1229 if (cmp > 0) { 1230 first = pos+1; 1231 } else { 1232 last = pos-1; 1233 } 1234 } 1235 1236 /* 1237 * If a matching entry could not be found, then quit. 1238 */ 1239 if (last < first) { 1240 return TCL_OK; 1241 } 1242 1243 /* 1244 * If a matching entry was found, there may be some ambiguity 1245 * if the user did not specify enough characters. Find the 1246 * top-most match in the list, and see if the part name has 1247 * enough characters. If there are two parts like "foo" 1248 * and "food", this allows us to match "foo" exactly. 1249 */ 1250 if (nlen < ensData->parts[pos]->minChars) { 1251 while (pos > 0) { 1252 pos--; 1253 if (strncmp(partName, ensData->parts[pos]->name, nlen) != 0) { 1254 pos++; 1255 break; 1256 } 1257 } 1258 } 1259 if (nlen < ensData->parts[pos]->minChars) { 1260 Tcl_Obj *resultPtr = Tcl_NewStringObj((char*)NULL, 0); 1261 1262 Tcl_AppendStringsToObj(resultPtr, 1263 "ambiguous option \"", partName, "\": should be one of...", 1264 (char*)NULL); 1265 1266 for (i=pos; i < ensData->numParts; i++) { 1267 if (strncmp(partName, ensData->parts[i]->name, nlen) != 0) { 1268 break; 1269 } 1270 Tcl_AppendToObj(resultPtr, "\n ", 3); 1271 GetEnsemblePartUsage(ensData->parts[i], resultPtr); 1272 } 1273 Tcl_SetObjResult(interp, resultPtr); 1274 return TCL_ERROR; 1275 } 1276 1277 /* 1278 * Found a match. Return the desired part. 1279 */ 1280 *rensPart = ensData->parts[pos]; 1281 return TCL_OK; 1282} 1283 1284 1285/* 1286 *---------------------------------------------------------------------- 1287 * 1288 * FindEnsemblePartIndex -- 1289 * 1290 * Searches for a part name within an ensemble. The part name 1291 * must be an exact match for an existing part name in the 1292 * ensemble. This procedure is useful for managing (i.e., 1293 * creating and deleting) parts in an ensemble. 1294 * 1295 * Results: 1296 * If an exact match is found, this procedure returns 1297 * non-zero, along with the index of the part in posPtr. 1298 * Otherwise, it returns zero, along with an index in posPtr 1299 * indicating where the part should be. 1300 * 1301 * Side effects: 1302 * None. 1303 * 1304 *---------------------------------------------------------------------- 1305 */ 1306static int 1307FindEnsemblePartIndex(ensData, partName, posPtr) 1308 Ensemble *ensData; /* ensemble being searched */ 1309 CONST char *partName; /* name of desired part */ 1310 int *posPtr; /* returns: index for part */ 1311{ 1312 int pos = 0; 1313 int first, last; 1314 int cmp; 1315 1316 /* 1317 * Search for the desired part name. 1318 * All parts are in lexicographical order, so use a 1319 * binary search to find the part quickly. 1320 */ 1321 first = 0; 1322 last = ensData->numParts-1; 1323 1324 while (last >= first) { 1325 pos = (first+last)/2; 1326 if (*partName == *ensData->parts[pos]->name) { 1327 cmp = strcmp(partName, ensData->parts[pos]->name); 1328 if (cmp == 0) { 1329 break; /* found it! */ 1330 } 1331 } 1332 else if (*partName < *ensData->parts[pos]->name) { 1333 cmp = -1; 1334 } 1335 else { 1336 cmp = 1; 1337 } 1338 1339 if (cmp > 0) { 1340 first = pos+1; 1341 } else { 1342 last = pos-1; 1343 } 1344 } 1345 1346 if (last >= first) { 1347 *posPtr = pos; 1348 return 1; 1349 } 1350 *posPtr = first; 1351 return 0; 1352} 1353 1354 1355/* 1356 *---------------------------------------------------------------------- 1357 * 1358 * ComputeMinChars -- 1359 * 1360 * Compares part names on an ensemble's part list and 1361 * determines the minimum number of characters needed for a 1362 * unique abbreviation. The parts on either side of a 1363 * particular part index are compared. As long as there is 1364 * a part on one side or the other, this procedure updates 1365 * the parts to have the proper minimum abbreviations. 1366 * 1367 * Results: 1368 * None. 1369 * 1370 * Side effects: 1371 * Updates three parts within the ensemble to remember 1372 * the minimum abbreviations. 1373 * 1374 *---------------------------------------------------------------------- 1375 */ 1376static void 1377ComputeMinChars(ensData, pos) 1378 Ensemble *ensData; /* ensemble being modified */ 1379 int pos; /* index of part being updated */ 1380{ 1381 int min, max; 1382 char *p, *q; 1383 1384 /* 1385 * If the position is invalid, do nothing. 1386 */ 1387 if (pos < 0 || pos >= ensData->numParts) { 1388 return; 1389 } 1390 1391 /* 1392 * Start by assuming that only the first letter is required 1393 * to uniquely identify this part. Then compare the name 1394 * against each neighboring part to determine the real minimum. 1395 */ 1396 ensData->parts[pos]->minChars = 1; 1397 1398 if (pos-1 >= 0) { 1399 p = ensData->parts[pos]->name; 1400 q = ensData->parts[pos-1]->name; 1401 for (min=1; *p == *q && *p != '\0' && *q != '\0'; min++) { 1402 p++; 1403 q++; 1404 } 1405 if (min > ensData->parts[pos]->minChars) { 1406 ensData->parts[pos]->minChars = min; 1407 } 1408 } 1409 1410 if (pos+1 < ensData->numParts) { 1411 p = ensData->parts[pos]->name; 1412 q = ensData->parts[pos+1]->name; 1413 for (min=1; *p == *q && *p != '\0' && *q != '\0'; min++) { 1414 p++; 1415 q++; 1416 } 1417 if (min > ensData->parts[pos]->minChars) { 1418 ensData->parts[pos]->minChars = min; 1419 } 1420 } 1421 1422 max = strlen(ensData->parts[pos]->name); 1423 if (ensData->parts[pos]->minChars > max) { 1424 ensData->parts[pos]->minChars = max; 1425 } 1426} 1427 1428 1429/* 1430 *---------------------------------------------------------------------- 1431 * 1432 * HandleEnsemble -- 1433 * 1434 * Invoked by Tcl whenever the user issues an ensemble-style 1435 * command. Handles commands of the form: 1436 * 1437 * <ensembleName> <partName> ?<arg> <arg>...? 1438 * 1439 * Looks for the <partName> within the ensemble, and if it 1440 * exists, the procedure transfers control to it. 1441 * 1442 * Results: 1443 * Returns TCL_OK if successful, and TCL_ERROR if anything 1444 * goes wrong. 1445 * 1446 * Side effects: 1447 * If anything goes wrong, this procedure returns an error 1448 * message as the result in the interpreter. 1449 * 1450 *---------------------------------------------------------------------- 1451 */ 1452static int 1453HandleEnsemble(clientData, interp, objc, objv) 1454 ClientData clientData; /* ensemble data */ 1455 Tcl_Interp *interp; /* current interpreter */ 1456 int objc; /* number of arguments */ 1457 Tcl_Obj *CONST objv[]; /* argument objects */ 1458{ 1459 Ensemble *ensData = (Ensemble*)clientData; 1460 1461 int i, result; 1462 Command *cmdPtr; 1463 EnsemblePart *ensPart; 1464 char *partName; 1465 int partNameLen; 1466 Tcl_Obj *cmdlinePtr, *chainObj; 1467 int cmdlinec; 1468 Tcl_Obj **cmdlinev; 1469 1470 /* 1471 * If a part name is not specified, return an error that 1472 * summarizes the usage for this ensemble. 1473 */ 1474 if (objc < 2) { 1475 Tcl_Obj *resultPtr = Tcl_NewStringObj( 1476 "wrong # args: should be one of...\n", -1); 1477 1478 GetEnsembleUsage(ensData, resultPtr); 1479 Tcl_SetObjResult(interp, resultPtr); 1480 return TCL_ERROR; 1481 } 1482 1483 /* 1484 * Lookup the desired part. If an ambiguous abbrevition is 1485 * found, return an error immediately. 1486 */ 1487 partName = Tcl_GetStringFromObj(objv[1], &partNameLen); 1488 if (FindEnsemblePart(interp, ensData, partName, &ensPart) != TCL_OK) { 1489 return TCL_ERROR; 1490 } 1491 1492 /* 1493 * If the part was not found, then look for an "@error" part 1494 * to handle the error. 1495 */ 1496 if (ensPart == NULL) { 1497 if (FindEnsemblePart(interp, ensData, "@error", &ensPart) != TCL_OK) { 1498 return TCL_ERROR; 1499 } 1500 if (ensPart != NULL) { 1501 cmdPtr = (Command*)ensPart->cmdPtr; 1502 result = (*cmdPtr->objProc)(cmdPtr->objClientData, 1503 interp, objc, objv); 1504 return result; 1505 } 1506 } 1507 if (ensPart == NULL) { 1508 return Itcl_EnsembleErrorCmd((ClientData)ensData, 1509 interp, objc-1, objv+1); 1510 } 1511 1512 /* 1513 * Pass control to the part, and return the result. 1514 */ 1515 chainObj = Tcl_NewObj(); 1516 chainObj->bytes = NULL; 1517 chainObj->typePtr = &itclEnsInvocType; 1518 chainObj->internalRep.twoPtrValue.ptr1 = (VOID *) ensPart; 1519 Tcl_IncrRefCount(objv[1]); 1520 chainObj->internalRep.twoPtrValue.ptr2 = (VOID *) objv[0]; 1521 Tcl_IncrRefCount(objv[0]); 1522 1523 cmdlinePtr = Tcl_NewListObj(0, (Tcl_Obj**)NULL); 1524 Tcl_ListObjAppendElement((Tcl_Interp*)NULL, cmdlinePtr, chainObj); 1525 for (i=2; i < objc; i++) { 1526 Tcl_ListObjAppendElement((Tcl_Interp*)NULL, cmdlinePtr, objv[i]); 1527 } 1528 Tcl_IncrRefCount(cmdlinePtr); 1529 1530 result = Tcl_ListObjGetElements((Tcl_Interp*)NULL, cmdlinePtr, 1531 &cmdlinec, &cmdlinev); 1532 1533 if (result == TCL_OK) { 1534 cmdPtr = (Command*)ensPart->cmdPtr; 1535 result = (*cmdPtr->objProc)(cmdPtr->objClientData, interp, 1536 cmdlinec, cmdlinev); 1537 } 1538 Tcl_DecrRefCount(cmdlinePtr); 1539 1540 return result; 1541} 1542 1543 1544/* 1545 *---------------------------------------------------------------------- 1546 * 1547 * Itcl_EnsembleCmd -- 1548 * 1549 * Invoked by Tcl whenever the user issues the "ensemble" 1550 * command to manipulate an ensemble. Handles the following 1551 * syntax: 1552 * 1553 * ensemble <ensName> ?<command> <arg> <arg>...? 1554 * ensemble <ensName> { 1555 * part <partName> <args> <body> 1556 * ensemble <ensName> { 1557 * ... 1558 * } 1559 * } 1560 * 1561 * Finds or creates the ensemble <ensName>, and then executes 1562 * the commands to add parts. 1563 * 1564 * Results: 1565 * Returns TCL_OK if successful, and TCL_ERROR if anything 1566 * goes wrong. 1567 * 1568 * Side effects: 1569 * If anything goes wrong, this procedure returns an error 1570 * message as the result in the interpreter. 1571 * 1572 *---------------------------------------------------------------------- 1573 */ 1574int 1575Itcl_EnsembleCmd(clientData, interp, objc, objv) 1576 ClientData clientData; /* ensemble data */ 1577 Tcl_Interp *interp; /* current interpreter */ 1578 int objc; /* number of arguments */ 1579 Tcl_Obj *CONST objv[]; /* argument objects */ 1580{ 1581 int status; 1582 char *ensName; 1583 EnsembleParser *ensInfo; 1584 Ensemble *ensData, *savedEnsData; 1585 EnsemblePart *ensPart; 1586 Tcl_Command cmd; 1587 Command *cmdPtr; 1588 Tcl_Obj *objPtr; 1589 1590 /* 1591 * Make sure that an ensemble name was specified. 1592 */ 1593 if (objc < 2) { 1594 Tcl_AppendResult(interp, 1595 "wrong # args: should be \"", 1596 Tcl_GetStringFromObj(objv[0], (int*)NULL), 1597 " name ?command arg arg...?\"", 1598 (char*)NULL); 1599 return TCL_ERROR; 1600 } 1601 1602 /* 1603 * If this is the "ensemble" command in the main interpreter, 1604 * then the client data will be null. Otherwise, it is 1605 * the "ensemble" command in the ensemble body parser, and 1606 * the client data indicates which ensemble we are modifying. 1607 */ 1608 if (clientData) { 1609 ensInfo = (EnsembleParser*)clientData; 1610 } else { 1611 ensInfo = GetEnsembleParser(interp); 1612 } 1613 ensData = ensInfo->ensData; 1614 1615 /* 1616 * Find or create the desired ensemble. If an ensemble is 1617 * being built, then this "ensemble" command is enclosed in 1618 * another "ensemble" command. Use the current ensemble as 1619 * the parent, and find or create an ensemble part within it. 1620 */ 1621 ensName = Tcl_GetStringFromObj(objv[1], (int*)NULL); 1622 1623 if (ensData) { 1624 if (FindEnsemblePart(interp, ensData, ensName, &ensPart) != TCL_OK) { 1625 ensPart = NULL; 1626 } 1627 if (ensPart == NULL) { 1628 if (CreateEnsemble(interp, ensData, ensName) != TCL_OK) { 1629 return TCL_ERROR; 1630 } 1631 if (FindEnsemblePart(interp, ensData, ensName, &ensPart) 1632 != TCL_OK) { 1633 Tcl_Panic("Itcl_EnsembleCmd: can't create ensemble"); 1634 } 1635 } 1636 1637 cmdPtr = (Command*)ensPart->cmdPtr; 1638 if (cmdPtr->deleteProc != DeleteEnsemble) { 1639 Tcl_AppendResult(interp, 1640 "part \"", Tcl_GetStringFromObj(objv[1], (int*)NULL), 1641 "\" is not an ensemble", 1642 (char*)NULL); 1643 return TCL_ERROR; 1644 } 1645 ensData = (Ensemble*)cmdPtr->objClientData; 1646 } 1647 1648 /* 1649 * Otherwise, the desired ensemble is a top-level ensemble. 1650 * Find or create the access command for the ensemble, and 1651 * then get its data. 1652 */ 1653 else { 1654 cmd = Tcl_FindCommand(interp, ensName, (Tcl_Namespace*)NULL, 0); 1655 if (cmd == NULL) { 1656 if (CreateEnsemble(interp, (Ensemble*)NULL, ensName) 1657 != TCL_OK) { 1658 return TCL_ERROR; 1659 } 1660 cmd = Tcl_FindCommand(interp, ensName, (Tcl_Namespace*)NULL, 0); 1661 } 1662 cmdPtr = (Command*)cmd; 1663 1664 if (cmdPtr == NULL || cmdPtr->deleteProc != DeleteEnsemble) { 1665 Tcl_AppendResult(interp, 1666 "command \"", Tcl_GetStringFromObj(objv[1], (int*)NULL), 1667 "\" is not an ensemble", 1668 (char*)NULL); 1669 return TCL_ERROR; 1670 } 1671 ensData = (Ensemble*)cmdPtr->objClientData; 1672 } 1673 1674 /* 1675 * At this point, we have the data for the ensemble that is 1676 * being manipulated. Plug this into the parser, and then 1677 * interpret the rest of the arguments in the ensemble parser. 1678 */ 1679 status = TCL_OK; 1680 savedEnsData = ensInfo->ensData; 1681 ensInfo->ensData = ensData; 1682 1683 if (objc == 3) { 1684 status = Tcl_EvalObj(ensInfo->parser, objv[2]); 1685 } 1686 else if (objc > 3) { 1687 objPtr = Tcl_NewListObj(objc-2, objv+2); 1688 Tcl_IncrRefCount(objPtr); /* stop Eval trashing it */ 1689 status = Tcl_EvalObj(ensInfo->parser, objPtr); 1690 Tcl_DecrRefCount(objPtr); /* we're done with the object */ 1691 } 1692 1693 /* 1694 * Copy the result from the parser interpreter to the 1695 * master interpreter. If an error was encountered, 1696 * copy the error info first, and then set the result. 1697 * Otherwise, the offending command is reported twice. 1698 */ 1699 if (status == TCL_ERROR) { 1700 CONST char *errInfo = Tcl_GetVar2(ensInfo->parser, "::errorInfo", 1701 (char*)NULL, TCL_GLOBAL_ONLY); 1702 1703 if (errInfo) { 1704 Tcl_AddObjErrorInfo(interp, (CONST84 char *)errInfo, -1); 1705 } 1706 1707 if (objc == 3) { 1708 char msg[128]; 1709 sprintf(msg, "\n (\"ensemble\" body line %d)", 1710 ERRORLINE(ensInfo->parser)); 1711 Tcl_AddObjErrorInfo(interp, msg, -1); 1712 } 1713 } 1714 Tcl_SetObjResult(interp, Tcl_GetObjResult(ensInfo->parser)); 1715 1716 ensInfo->ensData = savedEnsData; 1717 return status; 1718} 1719 1720 1721/* 1722 *---------------------------------------------------------------------- 1723 * 1724 * GetEnsembleParser -- 1725 * 1726 * Returns the slave interpreter that acts as a parser for 1727 * the body of an "ensemble" definition. The first time that 1728 * this is called for an interpreter, the parser is created 1729 * and registered as associated data. After that, it is 1730 * simply returned. 1731 * 1732 * Results: 1733 * Returns a pointer to the ensemble parser data structure. 1734 * 1735 * Side effects: 1736 * On the first call, the ensemble parser is created and 1737 * registered as "itcl_ensembleParser" with the interpreter. 1738 * 1739 *---------------------------------------------------------------------- 1740 */ 1741static EnsembleParser* 1742GetEnsembleParser(interp) 1743 Tcl_Interp *interp; /* interpreter handling the ensemble */ 1744{ 1745 Namespace *nsPtr; 1746 Tcl_Namespace *childNs; 1747 EnsembleParser *ensInfo; 1748 Tcl_HashEntry *hPtr; 1749 Tcl_HashSearch search; 1750 Tcl_Command cmd; 1751 1752 /* 1753 * Look for an existing ensemble parser. If it is found, 1754 * return it immediately. 1755 */ 1756 ensInfo = (EnsembleParser*) Tcl_GetAssocData(interp, 1757 "itcl_ensembleParser", NULL); 1758 1759 if (ensInfo) { 1760 return ensInfo; 1761 } 1762 1763 /* 1764 * Create a slave interpreter that can be used to parse 1765 * the body of an ensemble definition. 1766 */ 1767 ensInfo = (EnsembleParser*)ckalloc(sizeof(EnsembleParser)); 1768 ensInfo->master = interp; 1769 ensInfo->parser = Tcl_CreateInterp(); 1770 ensInfo->ensData = NULL; 1771 1772 /* 1773 * Remove all namespaces and all normal commands from the 1774 * parser interpreter. 1775 */ 1776 nsPtr = (Namespace*)Tcl_GetGlobalNamespace(ensInfo->parser); 1777 1778 for (hPtr = Tcl_FirstHashEntry(&nsPtr->childTable, &search); 1779 hPtr != NULL; 1780 hPtr = Tcl_FirstHashEntry(&nsPtr->childTable, &search)) { 1781 1782 childNs = (Tcl_Namespace*)Tcl_GetHashValue(hPtr); 1783 Tcl_DeleteNamespace(childNs); 1784 } 1785 1786 for (hPtr = Tcl_FirstHashEntry(&nsPtr->cmdTable, &search); 1787 hPtr != NULL; 1788 hPtr = Tcl_FirstHashEntry(&nsPtr->cmdTable, &search)) { 1789 1790 cmd = (Tcl_Command)Tcl_GetHashValue(hPtr); 1791 Tcl_DeleteCommandFromToken(ensInfo->parser, cmd); 1792 } 1793 1794 /* 1795 * Add the allowed commands to the parser interpreter: 1796 * part, delete, ensemble 1797 */ 1798 Tcl_CreateObjCommand(ensInfo->parser, "part", Itcl_EnsPartCmd, 1799 (ClientData)ensInfo, (Tcl_CmdDeleteProc*)NULL); 1800 1801 Tcl_CreateObjCommand(ensInfo->parser, "option", Itcl_EnsPartCmd, 1802 (ClientData)ensInfo, (Tcl_CmdDeleteProc*)NULL); 1803 1804 Tcl_CreateObjCommand(ensInfo->parser, "ensemble", Itcl_EnsembleCmd, 1805 (ClientData)ensInfo, (Tcl_CmdDeleteProc*)NULL); 1806 1807 /* 1808 * Install the parser data, so we'll have it the next time 1809 * we call this procedure. 1810 */ 1811 (void) Tcl_SetAssocData(interp, "itcl_ensembleParser", 1812 DeleteEnsParser, (ClientData)ensInfo); 1813 1814 return ensInfo; 1815} 1816 1817 1818/* 1819 *---------------------------------------------------------------------- 1820 * 1821 * DeleteEnsParser -- 1822 * 1823 * Called when an interpreter is destroyed to clean up the 1824 * ensemble parser within it. Destroys the slave interpreter 1825 * and frees up the data associated with it. 1826 * 1827 * Results: 1828 * None. 1829 * 1830 * Side effects: 1831 * None. 1832 * 1833 *---------------------------------------------------------------------- 1834 */ 1835 /* ARGSUSED */ 1836static void 1837DeleteEnsParser(clientData, interp) 1838 ClientData clientData; /* client data for ensemble-related commands */ 1839 Tcl_Interp *interp; /* interpreter containing the data */ 1840{ 1841 EnsembleParser* ensInfo = (EnsembleParser*)clientData; 1842 Tcl_DeleteInterp(ensInfo->parser); 1843 ckfree((char*)ensInfo); 1844} 1845 1846 1847/* 1848 *---------------------------------------------------------------------- 1849 * 1850 * Itcl_EnsPartCmd -- 1851 * 1852 * Invoked by Tcl whenever the user issues the "part" command 1853 * to manipulate an ensemble. This command can only be used 1854 * inside the "ensemble" command, which handles ensembles. 1855 * Handles the following syntax: 1856 * 1857 * ensemble <ensName> { 1858 * part <partName> <args> <body> 1859 * } 1860 * 1861 * Adds a new part called <partName> to the ensemble. If a 1862 * part already exists with that name, it is an error. The 1863 * new part is handled just like an ordinary Tcl proc, with 1864 * a list of <args> and a <body> of code to execute. 1865 * 1866 * Results: 1867 * Returns TCL_OK if successful, and TCL_ERROR if anything 1868 * goes wrong. 1869 * 1870 * Side effects: 1871 * If anything goes wrong, this procedure returns an error 1872 * message as the result in the interpreter. 1873 * 1874 *---------------------------------------------------------------------- 1875 */ 1876int 1877Itcl_EnsPartCmd(clientData, interp, objc, objv) 1878 ClientData clientData; /* ensemble data */ 1879 Tcl_Interp *interp; /* current interpreter */ 1880 int objc; /* number of arguments */ 1881 Tcl_Obj *CONST objv[]; /* argument objects */ 1882{ 1883 EnsembleParser *ensInfo = (EnsembleParser*)clientData; 1884 Ensemble *ensData = (Ensemble*)ensInfo->ensData; 1885 1886 int status, varArgs, space; 1887 char *partName, *usage; 1888 Proc *procPtr; 1889 Command *cmdPtr; 1890 CompiledLocal *localPtr; 1891 EnsemblePart *ensPart; 1892 Tcl_DString buffer; 1893 1894 if (objc != 4) { 1895 Tcl_AppendResult(interp, 1896 "wrong # args: should be \"", 1897 Tcl_GetStringFromObj(objv[0], (int*)NULL), 1898 " name args body\"", 1899 (char*)NULL); 1900 return TCL_ERROR; 1901 } 1902 1903 /* 1904 * Create a Tcl-style proc definition using the specified args 1905 * and body. This is not a proc in the usual sense. It belongs 1906 * to the namespace that contains the ensemble, but it is 1907 * accessed through the ensemble, not through a Tcl command. 1908 */ 1909 partName = Tcl_GetStringFromObj(objv[1], (int*)NULL); 1910 cmdPtr = (Command*)ensData->cmd; 1911 1912 if (TclCreateProc(interp, cmdPtr->nsPtr, partName, objv[2], objv[3], 1913 &procPtr) != TCL_OK) { 1914 return TCL_ERROR; 1915 } 1916 1917 /* 1918 * Deduce the usage information from the argument list. 1919 * We'll register this when we create the part, in a moment. 1920 */ 1921 Tcl_DStringInit(&buffer); 1922 varArgs = 0; 1923 space = 0; 1924 1925 for (localPtr=procPtr->firstLocalPtr; 1926 localPtr != NULL; 1927 localPtr=localPtr->nextPtr) { 1928 1929 if (TclIsVarArgument(localPtr)) { 1930 varArgs = 0; 1931 if (strcmp(localPtr->name, "args") == 0) { 1932 varArgs = 1; 1933 } 1934 else if (localPtr->defValuePtr) { 1935 if (space) { 1936 Tcl_DStringAppend(&buffer, " ", 1); 1937 } 1938 Tcl_DStringAppend(&buffer, "?", 1); 1939 Tcl_DStringAppend(&buffer, localPtr->name, -1); 1940 Tcl_DStringAppend(&buffer, "?", 1); 1941 space = 1; 1942 } 1943 else { 1944 if (space) { 1945 Tcl_DStringAppend(&buffer, " ", 1); 1946 } 1947 Tcl_DStringAppend(&buffer, localPtr->name, -1); 1948 space = 1; 1949 } 1950 } 1951 } 1952 if (varArgs) { 1953 if (space) { 1954 Tcl_DStringAppend(&buffer, " ", 1); 1955 } 1956 Tcl_DStringAppend(&buffer, "?arg arg ...?", 13); 1957 } 1958 1959 usage = Tcl_DStringValue(&buffer); 1960 1961 /* 1962 * Create a new part within the ensemble. If successful, 1963 * plug the command token into the proc; we'll need it later 1964 * if we try to compile the Tcl code for the part. If 1965 * anything goes wrong, clean up before bailing out. 1966 */ 1967 status = AddEnsemblePart(interp, ensData, partName, usage, 1968 TclObjInterpProc, (ClientData)procPtr, TclProcDeleteProc, 1969 &ensPart); 1970 1971 if (status == TCL_OK) { 1972 procPtr->cmdPtr = ensPart->cmdPtr; 1973 } else { 1974 TclProcDeleteProc((ClientData)procPtr); 1975 } 1976 Tcl_DStringFree(&buffer); 1977 1978 return status; 1979} 1980 1981 1982/* 1983 *---------------------------------------------------------------------- 1984 * 1985 * Itcl_EnsembleErrorCmd -- 1986 * 1987 * Invoked when the user tries to access an unknown part for 1988 * an ensemble. Acts as the default handler for the "@error" 1989 * part. Generates an error message like: 1990 * 1991 * bad option "foo": should be one of... 1992 * info args procname 1993 * info body procname 1994 * info cmdcount 1995 * ... 1996 * 1997 * Results: 1998 * Always returns TCL_OK. 1999 * 2000 * Side effects: 2001 * Returns the error message as the result in the interpreter. 2002 * 2003 *---------------------------------------------------------------------- 2004 */ 2005 /* ARGSUSED */ 2006int 2007Itcl_EnsembleErrorCmd(clientData, interp, objc, objv) 2008 ClientData clientData; /* ensemble info */ 2009 Tcl_Interp *interp; /* current interpreter */ 2010 int objc; /* number of arguments */ 2011 Tcl_Obj *CONST objv[]; /* argument objects */ 2012{ 2013 Ensemble *ensData = (Ensemble*)clientData; 2014 2015 char *cmdName; 2016 Tcl_Obj *objPtr; 2017 2018 cmdName = Tcl_GetStringFromObj(objv[0], (int*)NULL); 2019 2020 objPtr = Tcl_NewStringObj((char*)NULL, 0); 2021 Tcl_AppendStringsToObj(objPtr, 2022 "bad option \"", cmdName, "\": should be one of...\n", 2023 (char*)NULL); 2024 GetEnsembleUsage(ensData, objPtr); 2025 2026 Tcl_SetObjResult(interp, objPtr); 2027 return TCL_ERROR; 2028} 2029 2030 2031/* 2032 *---------------------------------------------------------------------- 2033 * 2034 * FreeEnsInvocInternalRep -- 2035 * 2036 * Frees the resources associated with an ensembleInvoc object's 2037 * internal representation. 2038 * 2039 * Results: 2040 * None. 2041 * 2042 * Side effects: 2043 * Decrements the ref count of the two objects referenced by 2044 * this object. If there are no more uses, this will free 2045 * the other objects. 2046 * 2047 *---------------------------------------------------------------------- 2048 */ 2049static void 2050FreeEnsInvocInternalRep(objPtr) 2051 register Tcl_Obj *objPtr; /* namespName object with internal 2052 * representation to free */ 2053{ 2054 Tcl_Obj *prevArgObj = (Tcl_Obj*)objPtr->internalRep.twoPtrValue.ptr2; 2055 2056 if (prevArgObj) { 2057 Tcl_DecrRefCount(prevArgObj); 2058 } 2059} 2060 2061 2062/* 2063 *---------------------------------------------------------------------- 2064 * 2065 * DupEnsInvocInternalRep -- 2066 * 2067 * Initializes the internal representation of an ensembleInvoc 2068 * object to a copy of the internal representation of 2069 * another ensembleInvoc object. 2070 * 2071 * This shouldn't be called. Normally, a temporary ensembleInvoc 2072 * object is created while an ensemble call is in progress. 2073 * This object may be converted to string form if an error occurs. 2074 * It does not stay around long, and there is no reason for it 2075 * to be duplicated. 2076 * 2077 * Results: 2078 * None. 2079 * 2080 * Side effects: 2081 * copyPtr's internal rep is set to duplicates of the objects 2082 * pointed to by srcPtr's internal rep. 2083 * 2084 *---------------------------------------------------------------------- 2085 */ 2086static void 2087DupEnsInvocInternalRep(srcPtr, copyPtr) 2088 Tcl_Obj *srcPtr; /* Object with internal rep to copy. */ 2089 register Tcl_Obj *copyPtr; /* Object with internal rep to set. */ 2090{ 2091 EnsemblePart *ensPart = (EnsemblePart*)srcPtr->internalRep.twoPtrValue.ptr1; 2092 Tcl_Obj *prevArgObj = (Tcl_Obj*)srcPtr->internalRep.twoPtrValue.ptr2; 2093 Tcl_Obj *objPtr; 2094 2095 copyPtr->internalRep.twoPtrValue.ptr1 = (VOID *) ensPart; 2096 2097 if (prevArgObj) { 2098 objPtr = Tcl_DuplicateObj(prevArgObj); 2099 Tcl_IncrRefCount(objPtr); 2100 copyPtr->internalRep.twoPtrValue.ptr2 = (VOID *) objPtr; 2101 } 2102} 2103 2104 2105/* 2106 *---------------------------------------------------------------------- 2107 * 2108 * SetEnsInvocFromAny -- 2109 * 2110 * Generates the internal representation for an ensembleInvoc 2111 * object. This conversion really shouldn't take place. 2112 * Normally, a temporary ensembleInvoc object is created while 2113 * an ensemble call is in progress. This object may be converted 2114 * to string form if an error occurs. But there is no reason 2115 * for any other object to be converted to ensembleInvoc form. 2116 * 2117 * Results: 2118 * Always returns TCL_OK. 2119 * 2120 * Side effects: 2121 * The string representation is saved as if it were the 2122 * command line argument for the ensemble invocation. The 2123 * reference to the ensemble part is set to NULL. 2124 * 2125 *---------------------------------------------------------------------- 2126 */ 2127static int 2128SetEnsInvocFromAny(interp, objPtr) 2129 Tcl_Interp *interp; /* Determines the context for 2130 name resolution */ 2131 register Tcl_Obj *objPtr; /* The object to convert */ 2132{ 2133 int length; 2134 char *name; 2135 Tcl_Obj *argObj; 2136 2137 /* 2138 * Get objPtr's string representation. 2139 * Make it up-to-date if necessary. 2140 * THIS FAILS IF THE OBJECT'S STRING REP CONTAINS NULLS. 2141 */ 2142 name = Tcl_GetStringFromObj(objPtr, &length); 2143 2144 /* 2145 * Make an argument object to contain the string, and 2146 * set the ensemble part definition to NULL. At this point, 2147 * we don't know anything about an ensemble, so we'll just 2148 * keep the string around as if it were the command line 2149 * invocation. 2150 */ 2151 argObj = Tcl_NewStringObj(name, length); 2152 2153 /* 2154 * Free the old representation and install a new one. 2155 */ 2156 if (objPtr->typePtr && objPtr->typePtr->freeIntRepProc != NULL) { 2157 (*objPtr->typePtr->freeIntRepProc)(objPtr); 2158 } 2159 objPtr->internalRep.twoPtrValue.ptr1 = (VOID *) NULL; 2160 objPtr->internalRep.twoPtrValue.ptr2 = (VOID *) argObj; 2161 objPtr->typePtr = &itclEnsInvocType; 2162 2163 return TCL_OK; 2164} 2165 2166 2167/* 2168 *---------------------------------------------------------------------- 2169 * 2170 * UpdateStringOfEnsInvoc -- 2171 * 2172 * Updates the string representation for an ensembleInvoc object. 2173 * This is called when an error occurs in an ensemble part, when 2174 * the code tries to print objv[0] as the command name. This 2175 * code automatically chains together all of the names leading 2176 * to the ensemble part, so the error message references the 2177 * entire command, not just the part name. 2178 * 2179 * Note: This procedure does not free an existing old string rep 2180 * so storage will be lost if this has not already been done. 2181 * 2182 * Results: 2183 * None. 2184 * 2185 * Side effects: 2186 * The object's string is set to the full command name for 2187 * the ensemble part. 2188 * 2189 *---------------------------------------------------------------------- 2190 */ 2191static void 2192UpdateStringOfEnsInvoc(objPtr) 2193 register Tcl_Obj *objPtr; /* NamespName obj to update string rep. */ 2194{ 2195 EnsemblePart *ensPart = (EnsemblePart*)objPtr->internalRep.twoPtrValue.ptr1; 2196 Tcl_Obj *prevArgObj = (Tcl_Obj*)objPtr->internalRep.twoPtrValue.ptr2; 2197 2198 Tcl_DString buffer; 2199 int length; 2200 char *name; 2201 2202 Tcl_DStringInit(&buffer); 2203 2204 /* 2205 * Get the string representation for the previous argument. 2206 * This will force each ensembleInvoc argument up the line 2207 * to get its string representation. So we will get the 2208 * original command name, followed by the sub-ensemble, and 2209 * the next sub-ensemble, and so on. Then add the part 2210 * name from the ensPart argument. 2211 */ 2212 if (prevArgObj) { 2213 name = Tcl_GetStringFromObj(prevArgObj, &length); 2214 Tcl_DStringAppend(&buffer, name, length); 2215 } 2216 2217 if (ensPart) { 2218 Tcl_DStringAppendElement(&buffer, ensPart->name); 2219 } 2220 2221 /* 2222 * The following allocates an empty string on the heap if name is "" 2223 * (e.g., if the internal rep is NULL). 2224 */ 2225 name = Tcl_DStringValue(&buffer); 2226 length = strlen(name); 2227 objPtr->bytes = (char *) ckalloc((unsigned) (length + 1)); 2228 memcpy((VOID *) objPtr->bytes, (VOID *) name, (unsigned) length); 2229 objPtr->bytes[length] = '\0'; 2230 objPtr->length = length; 2231} 2232