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