/[dtapublic]/projs/trunk/shared_source/c_tcl_base_7_5_w_mods/tclcmdil.c
ViewVC logotype

Annotation of /projs/trunk/shared_source/c_tcl_base_7_5_w_mods/tclcmdil.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 66 - (hide annotations) (download)
Sun Oct 30 21:57:38 2016 UTC (7 years, 11 months ago) by dashley
File MIME type: text/plain
File size: 92208 byte(s)
Header and footer cleanup.
1 dashley 64 /* $Header$ */
2 dashley 25 /*
3     * tclCmdIL.c --
4     *
5     * This file contains the top-level command routines for most of
6     * the Tcl built-in commands whose names begin with the letters
7     * I through L. It contains only commands in the generic core
8     * (i.e. those that don't depend much upon UNIX facilities).
9     *
10     * Copyright (c) 1987-1993 The Regents of the University of California.
11     * Copyright (c) 1993-1997 Lucent Technologies.
12     * Copyright (c) 1994-1997 Sun Microsystems, Inc.
13     * Copyright (c) 1998-1999 by Scriptics Corporation.
14     *
15     * See the file "license.terms" for information on usage and redistribution
16     * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
17     *
18     * RCS: @(#) $Id: tclcmdil.c,v 1.1.1.1 2001/06/13 04:34:54 dtashley Exp $
19     */
20    
21     #include "tclInt.h"
22     #include "tclPort.h"
23     #include "tclCompile.h"
24     #include "tclRegexp.h"
25    
26     /*
27     * During execution of the "lsort" command, structures of the following
28     * type are used to arrange the objects being sorted into a collection
29     * of linked lists.
30     */
31    
32     typedef struct SortElement {
33     Tcl_Obj *objPtr; /* Object being sorted. */
34     int count; /* number of same elements in list */
35     struct SortElement *nextPtr; /* Next element in the list, or
36     * NULL for end of list. */
37     } SortElement;
38    
39     /*
40     * The "lsort" command needs to pass certain information down to the
41     * function that compares two list elements, and the comparison function
42     * needs to pass success or failure information back up to the top-level
43     * "lsort" command. The following structure is used to pass this
44     * information.
45     */
46    
47     typedef struct SortInfo {
48     int isIncreasing; /* Nonzero means sort in increasing order. */
49     int sortMode; /* The sort mode. One of SORTMODE_*
50     * values defined below */
51     Tcl_Obj *compareCmdPtr; /* The Tcl comparison command when sortMode
52     * is SORTMODE_COMMAND. Pre-initialized to
53     * hold base of command.*/
54     int index; /* If the -index option was specified, this
55     * holds the index of the list element
56     * to extract for comparison. If -index
57     * wasn't specified, this is -1. */
58     Tcl_Interp *interp; /* The interpreter in which the sortis
59     * being done. */
60     int resultCode; /* Completion code for the lsort command.
61     * If an error occurs during the sort this
62     * is changed from TCL_OK to TCL_ERROR. */
63     } SortInfo;
64    
65     /*
66     * The "sortMode" field of the SortInfo structure can take on any of the
67     * following values.
68     */
69    
70     #define SORTMODE_ASCII 0
71     #define SORTMODE_INTEGER 1
72     #define SORTMODE_REAL 2
73     #define SORTMODE_COMMAND 3
74     #define SORTMODE_DICTIONARY 4
75    
76     /*
77     * Forward declarations for procedures defined in this file:
78     */
79    
80     static void AppendLocals _ANSI_ARGS_((Tcl_Interp *interp,
81     Tcl_Obj *listPtr, char *pattern,
82     int includeLinks));
83     static int DictionaryCompare _ANSI_ARGS_((char *left,
84     char *right));
85     static int InfoArgsCmd _ANSI_ARGS_((ClientData dummy,
86     Tcl_Interp *interp, int objc,
87     Tcl_Obj *CONST objv[]));
88     static int InfoBodyCmd _ANSI_ARGS_((ClientData dummy,
89     Tcl_Interp *interp, int objc,
90     Tcl_Obj *CONST objv[]));
91     static int InfoCmdCountCmd _ANSI_ARGS_((ClientData dummy,
92     Tcl_Interp *interp, int objc,
93     Tcl_Obj *CONST objv[]));
94     static int InfoCommandsCmd _ANSI_ARGS_((ClientData dummy,
95     Tcl_Interp *interp, int objc,
96     Tcl_Obj *CONST objv[]));
97     static int InfoCompleteCmd _ANSI_ARGS_((ClientData dummy,
98     Tcl_Interp *interp, int objc,
99     Tcl_Obj *CONST objv[]));
100     static int InfoDefaultCmd _ANSI_ARGS_((ClientData dummy,
101     Tcl_Interp *interp, int objc,
102     Tcl_Obj *CONST objv[]));
103     static int InfoExistsCmd _ANSI_ARGS_((ClientData dummy,
104     Tcl_Interp *interp, int objc,
105     Tcl_Obj *CONST objv[]));
106     static int InfoGlobalsCmd _ANSI_ARGS_((ClientData dummy,
107     Tcl_Interp *interp, int objc,
108     Tcl_Obj *CONST objv[]));
109     static int InfoHostnameCmd _ANSI_ARGS_((ClientData dummy,
110     Tcl_Interp *interp, int objc,
111     Tcl_Obj *CONST objv[]));
112     static int InfoLevelCmd _ANSI_ARGS_((ClientData dummy,
113     Tcl_Interp *interp, int objc,
114     Tcl_Obj *CONST objv[]));
115     static int InfoLibraryCmd _ANSI_ARGS_((ClientData dummy,
116     Tcl_Interp *interp, int objc,
117     Tcl_Obj *CONST objv[]));
118     static int InfoLoadedCmd _ANSI_ARGS_((ClientData dummy,
119     Tcl_Interp *interp, int objc,
120     Tcl_Obj *CONST objv[]));
121     static int InfoLocalsCmd _ANSI_ARGS_((ClientData dummy,
122     Tcl_Interp *interp, int objc,
123     Tcl_Obj *CONST objv[]));
124     static int InfoNameOfExecutableCmd _ANSI_ARGS_((
125     ClientData dummy, Tcl_Interp *interp, int objc,
126     Tcl_Obj *CONST objv[]));
127     static int InfoPatchLevelCmd _ANSI_ARGS_((ClientData dummy,
128     Tcl_Interp *interp, int objc,
129     Tcl_Obj *CONST objv[]));
130     static int InfoProcsCmd _ANSI_ARGS_((ClientData dummy,
131     Tcl_Interp *interp, int objc,
132     Tcl_Obj *CONST objv[]));
133     static int InfoScriptCmd _ANSI_ARGS_((ClientData dummy,
134     Tcl_Interp *interp, int objc,
135     Tcl_Obj *CONST objv[]));
136     static int InfoSharedlibCmd _ANSI_ARGS_((ClientData dummy,
137     Tcl_Interp *interp, int objc,
138     Tcl_Obj *CONST objv[]));
139     static int InfoTclVersionCmd _ANSI_ARGS_((ClientData dummy,
140     Tcl_Interp *interp, int objc,
141     Tcl_Obj *CONST objv[]));
142     static int InfoVarsCmd _ANSI_ARGS_((ClientData dummy,
143     Tcl_Interp *interp, int objc,
144     Tcl_Obj *CONST objv[]));
145     static SortElement * MergeSort _ANSI_ARGS_((SortElement *headPt,
146     SortInfo *infoPtr));
147     static SortElement * MergeLists _ANSI_ARGS_((SortElement *leftPtr,
148     SortElement *rightPtr, SortInfo *infoPtr));
149     static int SortCompare _ANSI_ARGS_((Tcl_Obj *firstPtr,
150     Tcl_Obj *second, SortInfo *infoPtr));
151    
152     /*
153     *----------------------------------------------------------------------
154     *
155     * Tcl_IfObjCmd --
156     *
157     * This procedure is invoked to process the "if" Tcl command.
158     * See the user documentation for details on what it does.
159     *
160     * With the bytecode compiler, this procedure is only called when
161     * a command name is computed at runtime, and is "if" or the name
162     * to which "if" was renamed: e.g., "set z if; $z 1 {puts foo}"
163     *
164     * Results:
165     * A standard Tcl result.
166     *
167     * Side effects:
168     * See the user documentation.
169     *
170     *----------------------------------------------------------------------
171     */
172    
173     /* ARGSUSED */
174     int
175     Tcl_IfObjCmd(dummy, interp, objc, objv)
176     ClientData dummy; /* Not used. */
177     Tcl_Interp *interp; /* Current interpreter. */
178     int objc; /* Number of arguments. */
179     Tcl_Obj *CONST objv[]; /* Argument objects. */
180     {
181     int thenScriptIndex = 0; /* then script to be evaled after syntax check */
182     int i, result, value;
183     char *clause;
184     i = 1;
185     while (1) {
186     /*
187     * At this point in the loop, objv and objc refer to an expression
188     * to test, either for the main expression or an expression
189     * following an "elseif". The arguments after the expression must
190     * be "then" (optional) and a script to execute if the expression is
191     * true.
192     */
193    
194     if (i >= objc) {
195     clause = Tcl_GetString(objv[i-1]);
196     Tcl_AppendResult(interp, "wrong # args: no expression after \"",
197     clause, "\" argument", (char *) NULL);
198     return TCL_ERROR;
199     }
200     if (!thenScriptIndex) {
201     result = Tcl_ExprBooleanObj(interp, objv[i], &value);
202     if (result != TCL_OK) {
203     return result;
204     }
205     }
206     i++;
207     if (i >= objc) {
208     missingScript:
209     clause = Tcl_GetString(objv[i-1]);
210     Tcl_AppendResult(interp, "wrong # args: no script following \"",
211     clause, "\" argument", (char *) NULL);
212     return TCL_ERROR;
213     }
214     clause = Tcl_GetString(objv[i]);
215     if ((i < objc) && (strcmp(clause, "then") == 0)) {
216     i++;
217     }
218     if (i >= objc) {
219     goto missingScript;
220     }
221     if (value) {
222     thenScriptIndex = i;
223     value = 0;
224     }
225    
226     /*
227     * The expression evaluated to false. Skip the command, then
228     * see if there is an "else" or "elseif" clause.
229     */
230    
231     i++;
232     if (i >= objc) {
233     if (thenScriptIndex) {
234     return Tcl_EvalObjEx(interp, objv[thenScriptIndex], 0);
235     }
236     return TCL_OK;
237     }
238     clause = Tcl_GetString(objv[i]);
239     if ((clause[0] == 'e') && (strcmp(clause, "elseif") == 0)) {
240     i++;
241     continue;
242     }
243     break;
244     }
245    
246     /*
247     * Couldn't find a "then" or "elseif" clause to execute. Check now
248     * for an "else" clause. We know that there's at least one more
249     * argument when we get here.
250     */
251    
252     if (strcmp(clause, "else") == 0) {
253     i++;
254     if (i >= objc) {
255     Tcl_AppendResult(interp,
256     "wrong # args: no script following \"else\" argument",
257     (char *) NULL);
258     return TCL_ERROR;
259     }
260     }
261     if (i < objc - 1) {
262     Tcl_AppendResult(interp,
263     "wrong # args: extra words after \"else\" clause in \"if\" command",
264     (char *) NULL);
265     return TCL_ERROR;
266     }
267     if (thenScriptIndex) {
268     return Tcl_EvalObjEx(interp, objv[thenScriptIndex], 0);
269     }
270     return Tcl_EvalObjEx(interp, objv[i], 0);
271     }
272    
273     /*
274     *----------------------------------------------------------------------
275     *
276     * Tcl_IncrObjCmd --
277     *
278     * This procedure is invoked to process the "incr" Tcl command.
279     * See the user documentation for details on what it does.
280     *
281     * With the bytecode compiler, this procedure is only called when
282     * a command name is computed at runtime, and is "incr" or the name
283     * to which "incr" was renamed: e.g., "set z incr; $z i -1"
284     *
285     * Results:
286     * A standard Tcl result.
287     *
288     * Side effects:
289     * See the user documentation.
290     *
291     *----------------------------------------------------------------------
292     */
293    
294     /* ARGSUSED */
295     int
296     Tcl_IncrObjCmd(dummy, interp, objc, objv)
297     ClientData dummy; /* Not used. */
298     Tcl_Interp *interp; /* Current interpreter. */
299     int objc; /* Number of arguments. */
300     Tcl_Obj *CONST objv[]; /* Argument objects. */
301     {
302     long incrAmount;
303     Tcl_Obj *newValuePtr;
304    
305     if ((objc != 2) && (objc != 3)) {
306     Tcl_WrongNumArgs(interp, 1, objv, "varName ?increment?");
307     return TCL_ERROR;
308     }
309    
310     /*
311     * Calculate the amount to increment by.
312     */
313    
314     if (objc == 2) {
315     incrAmount = 1;
316     } else {
317     if (Tcl_GetLongFromObj(interp, objv[2], &incrAmount) != TCL_OK) {
318     Tcl_AddErrorInfo(interp, "\n (reading increment)");
319     return TCL_ERROR;
320     }
321     }
322    
323     /*
324     * Increment the variable's value.
325     */
326    
327     newValuePtr = TclIncrVar2(interp, objv[1], (Tcl_Obj *) NULL, incrAmount,
328     TCL_LEAVE_ERR_MSG);
329     if (newValuePtr == NULL) {
330     return TCL_ERROR;
331     }
332    
333     /*
334     * Set the interpreter's object result to refer to the variable's new
335     * value object.
336     */
337    
338     Tcl_SetObjResult(interp, newValuePtr);
339     return TCL_OK;
340     }
341    
342     /*
343     *----------------------------------------------------------------------
344     *
345     * Tcl_InfoObjCmd --
346     *
347     * This procedure is invoked to process the "info" Tcl command.
348     * See the user documentation for details on what it does.
349     *
350     * Results:
351     * A standard Tcl result.
352     *
353     * Side effects:
354     * See the user documentation.
355     *
356     *----------------------------------------------------------------------
357     */
358    
359     /* ARGSUSED */
360     int
361     Tcl_InfoObjCmd(clientData, interp, objc, objv)
362     ClientData clientData; /* Arbitrary value passed to the command. */
363     Tcl_Interp *interp; /* Current interpreter. */
364     int objc; /* Number of arguments. */
365     Tcl_Obj *CONST objv[]; /* Argument objects. */
366     {
367     static char *subCmds[] = {
368     "args", "body", "cmdcount", "commands",
369     "complete", "default", "exists", "globals",
370     "hostname", "level", "library", "loaded",
371     "locals", "nameofexecutable", "patchlevel", "procs",
372     "script", "sharedlibextension", "tclversion", "vars",
373     (char *) NULL};
374     enum ISubCmdIdx {
375     IArgsIdx, IBodyIdx, ICmdCountIdx, ICommandsIdx,
376     ICompleteIdx, IDefaultIdx, IExistsIdx, IGlobalsIdx,
377     IHostnameIdx, ILevelIdx, ILibraryIdx, ILoadedIdx,
378     ILocalsIdx, INameOfExecutableIdx, IPatchLevelIdx, IProcsIdx,
379     IScriptIdx, ISharedLibExtensionIdx, ITclVersionIdx, IVarsIdx
380     };
381     int index, result;
382    
383     if (objc < 2) {
384     Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?");
385     return TCL_ERROR;
386     }
387    
388     result = Tcl_GetIndexFromObj(interp, objv[1], subCmds, "option", 0,
389     (int *) &index);
390     if (result != TCL_OK) {
391     return result;
392     }
393    
394     switch (index) {
395     case IArgsIdx:
396     result = InfoArgsCmd(clientData, interp, objc, objv);
397     break;
398     case IBodyIdx:
399     result = InfoBodyCmd(clientData, interp, objc, objv);
400     break;
401     case ICmdCountIdx:
402     result = InfoCmdCountCmd(clientData, interp, objc, objv);
403     break;
404     case ICommandsIdx:
405     result = InfoCommandsCmd(clientData, interp, objc, objv);
406     break;
407     case ICompleteIdx:
408     result = InfoCompleteCmd(clientData, interp, objc, objv);
409     break;
410     case IDefaultIdx:
411     result = InfoDefaultCmd(clientData, interp, objc, objv);
412     break;
413     case IExistsIdx:
414     result = InfoExistsCmd(clientData, interp, objc, objv);
415     break;
416     case IGlobalsIdx:
417     result = InfoGlobalsCmd(clientData, interp, objc, objv);
418     break;
419     case IHostnameIdx:
420     result = InfoHostnameCmd(clientData, interp, objc, objv);
421     break;
422     case ILevelIdx:
423     result = InfoLevelCmd(clientData, interp, objc, objv);
424     break;
425     case ILibraryIdx:
426     result = InfoLibraryCmd(clientData, interp, objc, objv);
427     break;
428     case ILoadedIdx:
429     result = InfoLoadedCmd(clientData, interp, objc, objv);
430     break;
431     case ILocalsIdx:
432     result = InfoLocalsCmd(clientData, interp, objc, objv);
433     break;
434     case INameOfExecutableIdx:
435     result = InfoNameOfExecutableCmd(clientData, interp, objc, objv);
436     break;
437     case IPatchLevelIdx:
438     result = InfoPatchLevelCmd(clientData, interp, objc, objv);
439     break;
440     case IProcsIdx:
441     result = InfoProcsCmd(clientData, interp, objc, objv);
442     break;
443     case IScriptIdx:
444     result = InfoScriptCmd(clientData, interp, objc, objv);
445     break;
446     case ISharedLibExtensionIdx:
447     result = InfoSharedlibCmd(clientData, interp, objc, objv);
448     break;
449     case ITclVersionIdx:
450     result = InfoTclVersionCmd(clientData, interp, objc, objv);
451     break;
452     case IVarsIdx:
453     result = InfoVarsCmd(clientData, interp, objc, objv);
454     break;
455     }
456     return result;
457     }
458    
459     /*
460     *----------------------------------------------------------------------
461     *
462     * InfoArgsCmd --
463     *
464     * Called to implement the "info args" command that returns the
465     * argument list for a procedure. Handles the following syntax:
466     *
467     * info args procName
468     *
469     * Results:
470     * Returns TCL_OK if successful and TCL_ERROR if there is an error.
471     *
472     * Side effects:
473     * Returns a result in the interpreter's result object. If there is
474     * an error, the result is an error message.
475     *
476     *----------------------------------------------------------------------
477     */
478    
479     static int
480     InfoArgsCmd(dummy, interp, objc, objv)
481     ClientData dummy; /* Not used. */
482     Tcl_Interp *interp; /* Current interpreter. */
483     int objc; /* Number of arguments. */
484     Tcl_Obj *CONST objv[]; /* Argument objects. */
485     {
486     register Interp *iPtr = (Interp *) interp;
487     char *name;
488     Proc *procPtr;
489     CompiledLocal *localPtr;
490     Tcl_Obj *listObjPtr;
491    
492     if (objc != 3) {
493     Tcl_WrongNumArgs(interp, 2, objv, "procname");
494     return TCL_ERROR;
495     }
496    
497     name = Tcl_GetString(objv[2]);
498     procPtr = TclFindProc(iPtr, name);
499     if (procPtr == NULL) {
500     Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
501     "\"", name, "\" isn't a procedure", (char *) NULL);
502     return TCL_ERROR;
503     }
504    
505     /*
506     * Build a return list containing the arguments.
507     */
508    
509     listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
510     for (localPtr = procPtr->firstLocalPtr; localPtr != NULL;
511     localPtr = localPtr->nextPtr) {
512     if (TclIsVarArgument(localPtr)) {
513     Tcl_ListObjAppendElement(interp, listObjPtr,
514     Tcl_NewStringObj(localPtr->name, -1));
515     }
516     }
517     Tcl_SetObjResult(interp, listObjPtr);
518     return TCL_OK;
519     }
520    
521     /*
522     *----------------------------------------------------------------------
523     *
524     * InfoBodyCmd --
525     *
526     * Called to implement the "info body" command that returns the body
527     * for a procedure. Handles the following syntax:
528     *
529     * info body procName
530     *
531     * Results:
532     * Returns TCL_OK if successful and TCL_ERROR if there is an error.
533     *
534     * Side effects:
535     * Returns a result in the interpreter's result object. If there is
536     * an error, the result is an error message.
537     *
538     *----------------------------------------------------------------------
539     */
540    
541     static int
542     InfoBodyCmd(dummy, interp, objc, objv)
543     ClientData dummy; /* Not used. */
544     Tcl_Interp *interp; /* Current interpreter. */
545     int objc; /* Number of arguments. */
546     Tcl_Obj *CONST objv[]; /* Argument objects. */
547     {
548     register Interp *iPtr = (Interp *) interp;
549     char *name;
550     Proc *procPtr;
551     Tcl_Obj *bodyPtr, *resultPtr;
552    
553     if (objc != 3) {
554     Tcl_WrongNumArgs(interp, 2, objv, "procname");
555     return TCL_ERROR;
556     }
557    
558     name = Tcl_GetString(objv[2]);
559     procPtr = TclFindProc(iPtr, name);
560     if (procPtr == NULL) {
561     Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
562     "\"", name, "\" isn't a procedure", (char *) NULL);
563     return TCL_ERROR;
564     }
565    
566     /*
567     * We should not return a bytecompiled body. If it is precompiled,
568     * then the bodyPtr's string representation is bogus, since sources
569     * are not available. If it was just a bytecompiled body, then it
570     * is likely to not be of any use to the caller, as it was compiled
571     * for a separate procedure context [Bug: 3412], and noone else can
572     * reasonably use it.
573     * In order to make sure that later manipulations of the object do not
574     * invalidate the internal representation, we make a copy of the string
575     * representation and return that one, instead.
576     */
577    
578     bodyPtr = procPtr->bodyPtr;
579     resultPtr = bodyPtr;
580     if (bodyPtr->typePtr == &tclByteCodeType) {
581     resultPtr = Tcl_NewStringObj(bodyPtr->bytes, bodyPtr->length);
582     }
583    
584     Tcl_SetObjResult(interp, resultPtr);
585     return TCL_OK;
586     }
587    
588     /*
589     *----------------------------------------------------------------------
590     *
591     * InfoCmdCountCmd --
592     *
593     * Called to implement the "info cmdcount" command that returns the
594     * number of commands that have been executed. Handles the following
595     * syntax:
596     *
597     * info cmdcount
598     *
599     * Results:
600     * Returns TCL_OK if successful and TCL_ERROR if there is an error.
601     *
602     * Side effects:
603     * Returns a result in the interpreter's result object. If there is
604     * an error, the result is an error message.
605     *
606     *----------------------------------------------------------------------
607     */
608    
609     static int
610     InfoCmdCountCmd(dummy, interp, objc, objv)
611     ClientData dummy; /* Not used. */
612     Tcl_Interp *interp; /* Current interpreter. */
613     int objc; /* Number of arguments. */
614     Tcl_Obj *CONST objv[]; /* Argument objects. */
615     {
616     Interp *iPtr = (Interp *) interp;
617    
618     if (objc != 2) {
619     Tcl_WrongNumArgs(interp, 2, objv, NULL);
620     return TCL_ERROR;
621     }
622    
623     Tcl_SetIntObj(Tcl_GetObjResult(interp), iPtr->cmdCount);
624     return TCL_OK;
625     }
626    
627     /*
628     *----------------------------------------------------------------------
629     *
630     * InfoCommandsCmd --
631     *
632     * Called to implement the "info commands" command that returns the
633     * list of commands in the interpreter that match an optional pattern.
634     * The pattern, if any, consists of an optional sequence of namespace
635     * names separated by "::" qualifiers, which is followed by a
636     * glob-style pattern that restricts which commands are returned.
637     * Handles the following syntax:
638     *
639     * info commands ?pattern?
640     *
641     * Results:
642     * Returns TCL_OK if successful and TCL_ERROR if there is an error.
643     *
644     * Side effects:
645     * Returns a result in the interpreter's result object. If there is
646     * an error, the result is an error message.
647     *
648     *----------------------------------------------------------------------
649     */
650    
651     static int
652     InfoCommandsCmd(dummy, interp, objc, objv)
653     ClientData dummy; /* Not used. */
654     Tcl_Interp *interp; /* Current interpreter. */
655     int objc; /* Number of arguments. */
656     Tcl_Obj *CONST objv[]; /* Argument objects. */
657     {
658     char *cmdName, *pattern, *simplePattern;
659     register Tcl_HashEntry *entryPtr;
660     Tcl_HashSearch search;
661     Namespace *nsPtr;
662     Namespace *globalNsPtr = (Namespace *) Tcl_GetGlobalNamespace(interp);
663     Namespace *currNsPtr = (Namespace *) Tcl_GetCurrentNamespace(interp);
664     Tcl_Obj *listPtr, *elemObjPtr;
665     int specificNsInPattern = 0; /* Init. to avoid compiler warning. */
666     Tcl_Command cmd;
667    
668     /*
669     * Get the pattern and find the "effective namespace" in which to
670     * list commands.
671     */
672    
673     if (objc == 2) {
674     simplePattern = NULL;
675     nsPtr = currNsPtr;
676     specificNsInPattern = 0;
677     } else if (objc == 3) {
678     /*
679     * From the pattern, get the effective namespace and the simple
680     * pattern (no namespace qualifiers or ::'s) at the end. If an
681     * error was found while parsing the pattern, return it. Otherwise,
682     * if the namespace wasn't found, just leave nsPtr NULL: we will
683     * return an empty list since no commands there can be found.
684     */
685    
686     Namespace *dummy1NsPtr, *dummy2NsPtr;
687    
688    
689     pattern = Tcl_GetString(objv[2]);
690     TclGetNamespaceForQualName(interp, pattern, (Namespace *) NULL,
691     /*flags*/ 0, &nsPtr, &dummy1NsPtr, &dummy2NsPtr, &simplePattern);
692    
693     if (nsPtr != NULL) { /* we successfully found the pattern's ns */
694     specificNsInPattern = (strcmp(simplePattern, pattern) != 0);
695     }
696     } else {
697     Tcl_WrongNumArgs(interp, 2, objv, "?pattern?");
698     return TCL_ERROR;
699     }
700    
701     /*
702     * Scan through the effective namespace's command table and create a
703     * list with all commands that match the pattern. If a specific
704     * namespace was requested in the pattern, qualify the command names
705     * with the namespace name.
706     */
707    
708     listPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
709    
710     if (nsPtr != NULL) {
711     entryPtr = Tcl_FirstHashEntry(&nsPtr->cmdTable, &search);
712     while (entryPtr != NULL) {
713     cmdName = Tcl_GetHashKey(&nsPtr->cmdTable, entryPtr);
714     if ((simplePattern == NULL)
715     || Tcl_StringMatch(cmdName, simplePattern)) {
716     if (specificNsInPattern) {
717     cmd = (Tcl_Command) Tcl_GetHashValue(entryPtr);
718     elemObjPtr = Tcl_NewObj();
719     Tcl_GetCommandFullName(interp, cmd, elemObjPtr);
720     } else {
721     elemObjPtr = Tcl_NewStringObj(cmdName, -1);
722     }
723     Tcl_ListObjAppendElement(interp, listPtr, elemObjPtr);
724     }
725     entryPtr = Tcl_NextHashEntry(&search);
726     }
727    
728     /*
729     * If the effective namespace isn't the global :: namespace, and a
730     * specific namespace wasn't requested in the pattern, then add in
731     * all global :: commands that match the simple pattern. Of course,
732     * we add in only those commands that aren't hidden by a command in
733     * the effective namespace.
734     */
735    
736     if ((nsPtr != globalNsPtr) && !specificNsInPattern) {
737     entryPtr = Tcl_FirstHashEntry(&globalNsPtr->cmdTable, &search);
738     while (entryPtr != NULL) {
739     cmdName = Tcl_GetHashKey(&globalNsPtr->cmdTable, entryPtr);
740     if ((simplePattern == NULL)
741     || Tcl_StringMatch(cmdName, simplePattern)) {
742     if (Tcl_FindHashEntry(&nsPtr->cmdTable, cmdName) == NULL) {
743     Tcl_ListObjAppendElement(interp, listPtr,
744     Tcl_NewStringObj(cmdName, -1));
745     }
746     }
747     entryPtr = Tcl_NextHashEntry(&search);
748     }
749     }
750     }
751    
752     Tcl_SetObjResult(interp, listPtr);
753     return TCL_OK;
754     }
755    
756     /*
757     *----------------------------------------------------------------------
758     *
759     * InfoCompleteCmd --
760     *
761     * Called to implement the "info complete" command that determines
762     * whether a string is a complete Tcl command. Handles the following
763     * syntax:
764     *
765     * info complete command
766     *
767     * Results:
768     * Returns TCL_OK if successful and TCL_ERROR if there is an error.
769     *
770     * Side effects:
771     * Returns a result in the interpreter's result object. If there is
772     * an error, the result is an error message.
773     *
774     *----------------------------------------------------------------------
775     */
776    
777     static int
778     InfoCompleteCmd(dummy, interp, objc, objv)
779     ClientData dummy; /* Not used. */
780     Tcl_Interp *interp; /* Current interpreter. */
781     int objc; /* Number of arguments. */
782     Tcl_Obj *CONST objv[]; /* Argument objects. */
783     {
784     if (objc != 3) {
785     Tcl_WrongNumArgs(interp, 2, objv, "command");
786     return TCL_ERROR;
787     }
788    
789     if (TclObjCommandComplete(objv[2])) {
790     Tcl_SetIntObj(Tcl_GetObjResult(interp), 1);
791     } else {
792     Tcl_SetIntObj(Tcl_GetObjResult(interp), 0);
793     }
794    
795     return TCL_OK;
796     }
797    
798     /*
799     *----------------------------------------------------------------------
800     *
801     * InfoDefaultCmd --
802     *
803     * Called to implement the "info default" command that returns the
804     * default value for a procedure argument. Handles the following
805     * syntax:
806     *
807     * info default procName arg varName
808     *
809     * Results:
810     * Returns TCL_OK if successful and TCL_ERROR if there is an error.
811     *
812     * Side effects:
813     * Returns a result in the interpreter's result object. If there is
814     * an error, the result is an error message.
815     *
816     *----------------------------------------------------------------------
817     */
818    
819     static int
820     InfoDefaultCmd(dummy, interp, objc, objv)
821     ClientData dummy; /* Not used. */
822     Tcl_Interp *interp; /* Current interpreter. */
823     int objc; /* Number of arguments. */
824     Tcl_Obj *CONST objv[]; /* Argument objects. */
825     {
826     Interp *iPtr = (Interp *) interp;
827     char *procName, *argName, *varName;
828     Proc *procPtr;
829     CompiledLocal *localPtr;
830     Tcl_Obj *valueObjPtr;
831    
832     if (objc != 5) {
833     Tcl_WrongNumArgs(interp, 2, objv, "procname arg varname");
834     return TCL_ERROR;
835     }
836    
837     procName = Tcl_GetString(objv[2]);
838     argName = Tcl_GetString(objv[3]);
839    
840     procPtr = TclFindProc(iPtr, procName);
841     if (procPtr == NULL) {
842     Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
843     "\"", procName, "\" isn't a procedure", (char *) NULL);
844     return TCL_ERROR;
845     }
846    
847     for (localPtr = procPtr->firstLocalPtr; localPtr != NULL;
848     localPtr = localPtr->nextPtr) {
849     if (TclIsVarArgument(localPtr)
850     && (strcmp(argName, localPtr->name) == 0)) {
851     if (localPtr->defValuePtr != NULL) {
852     valueObjPtr = Tcl_ObjSetVar2(interp, objv[4], NULL,
853     localPtr->defValuePtr, 0);
854     if (valueObjPtr == NULL) {
855     defStoreError:
856     varName = Tcl_GetString(objv[4]);
857     Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
858     "couldn't store default value in variable \"",
859     varName, "\"", (char *) NULL);
860     return TCL_ERROR;
861     }
862     Tcl_SetIntObj(Tcl_GetObjResult(interp), 1);
863     } else {
864     Tcl_Obj *nullObjPtr = Tcl_NewObj();
865     valueObjPtr = Tcl_ObjSetVar2(interp, objv[4], NULL,
866     nullObjPtr, 0);
867     if (valueObjPtr == NULL) {
868     Tcl_DecrRefCount(nullObjPtr); /* free unneeded obj */
869     goto defStoreError;
870     }
871     Tcl_SetIntObj(Tcl_GetObjResult(interp), 0);
872     }
873     return TCL_OK;
874     }
875     }
876    
877     Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
878     "procedure \"", procName, "\" doesn't have an argument \"",
879     argName, "\"", (char *) NULL);
880     return TCL_ERROR;
881     }
882    
883     /*
884     *----------------------------------------------------------------------
885     *
886     * InfoExistsCmd --
887     *
888     * Called to implement the "info exists" command that determines
889     * whether a variable exists. Handles the following syntax:
890     *
891     * info exists varName
892     *
893     * Results:
894     * Returns TCL_OK if successful and TCL_ERROR if there is an error.
895     *
896     * Side effects:
897     * Returns a result in the interpreter's result object. If there is
898     * an error, the result is an error message.
899     *
900     *----------------------------------------------------------------------
901     */
902    
903     static int
904     InfoExistsCmd(dummy, interp, objc, objv)
905     ClientData dummy; /* Not used. */
906     Tcl_Interp *interp; /* Current interpreter. */
907     int objc; /* Number of arguments. */
908     Tcl_Obj *CONST objv[]; /* Argument objects. */
909     {
910     char *varName;
911     Var *varPtr;
912    
913     if (objc != 3) {
914     Tcl_WrongNumArgs(interp, 2, objv, "varName");
915     return TCL_ERROR;
916     }
917    
918     varName = Tcl_GetString(objv[2]);
919     varPtr = TclVarTraceExists(interp, varName);
920     if ((varPtr != NULL) && !TclIsVarUndefined(varPtr)) {
921     Tcl_SetIntObj(Tcl_GetObjResult(interp), 1);
922     } else {
923     Tcl_SetIntObj(Tcl_GetObjResult(interp), 0);
924     }
925     return TCL_OK;
926     }
927    
928     /*
929     *----------------------------------------------------------------------
930     *
931     * InfoGlobalsCmd --
932     *
933     * Called to implement the "info globals" command that returns the list
934     * of global variables matching an optional pattern. Handles the
935     * following syntax:
936     *
937     * info globals ?pattern?
938     *
939     * Results:
940     * Returns TCL_OK if successful and TCL_ERROR if there is an error.
941     *
942     * Side effects:
943     * Returns a result in the interpreter's result object. If there is
944     * an error, the result is an error message.
945     *
946     *----------------------------------------------------------------------
947     */
948    
949     static int
950     InfoGlobalsCmd(dummy, interp, objc, objv)
951     ClientData dummy; /* Not used. */
952     Tcl_Interp *interp; /* Current interpreter. */
953     int objc; /* Number of arguments. */
954     Tcl_Obj *CONST objv[]; /* Argument objects. */
955     {
956     char *varName, *pattern;
957     Namespace *globalNsPtr = (Namespace *) Tcl_GetGlobalNamespace(interp);
958     register Tcl_HashEntry *entryPtr;
959     Tcl_HashSearch search;
960     Var *varPtr;
961     Tcl_Obj *listPtr;
962    
963     if (objc == 2) {
964     pattern = NULL;
965     } else if (objc == 3) {
966     pattern = Tcl_GetString(objv[2]);
967     } else {
968     Tcl_WrongNumArgs(interp, 2, objv, "?pattern?");
969     return TCL_ERROR;
970     }
971    
972     /*
973     * Scan through the global :: namespace's variable table and create a
974     * list of all global variables that match the pattern.
975     */
976    
977     listPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
978     for (entryPtr = Tcl_FirstHashEntry(&globalNsPtr->varTable, &search);
979     entryPtr != NULL;
980     entryPtr = Tcl_NextHashEntry(&search)) {
981     varPtr = (Var *) Tcl_GetHashValue(entryPtr);
982     if (TclIsVarUndefined(varPtr)) {
983     continue;
984     }
985     varName = Tcl_GetHashKey(&globalNsPtr->varTable, entryPtr);
986     if ((pattern == NULL) || Tcl_StringMatch(varName, pattern)) {
987     Tcl_ListObjAppendElement(interp, listPtr,
988     Tcl_NewStringObj(varName, -1));
989     }
990     }
991     Tcl_SetObjResult(interp, listPtr);
992     return TCL_OK;
993     }
994    
995     /*
996     *----------------------------------------------------------------------
997     *
998     * InfoHostnameCmd --
999     *
1000     * Called to implement the "info hostname" command that returns the
1001     * host name. Handles the following syntax:
1002     *
1003     * info hostname
1004     *
1005     * Results:
1006     * Returns TCL_OK if successful and TCL_ERROR if there is an error.
1007     *
1008     * Side effects:
1009     * Returns a result in the interpreter's result object. If there is
1010     * an error, the result is an error message.
1011     *
1012     *----------------------------------------------------------------------
1013     */
1014    
1015     static int
1016     InfoHostnameCmd(dummy, interp, objc, objv)
1017     ClientData dummy; /* Not used. */
1018     Tcl_Interp *interp; /* Current interpreter. */
1019     int objc; /* Number of arguments. */
1020     Tcl_Obj *CONST objv[]; /* Argument objects. */
1021     {
1022     char *name;
1023     if (objc != 2) {
1024     Tcl_WrongNumArgs(interp, 2, objv, NULL);
1025     return TCL_ERROR;
1026     }
1027    
1028     name = Tcl_GetHostName();
1029     if (name) {
1030     Tcl_SetStringObj(Tcl_GetObjResult(interp), name, -1);
1031     return TCL_OK;
1032     } else {
1033     Tcl_SetStringObj(Tcl_GetObjResult(interp),
1034     "unable to determine name of host", -1);
1035     return TCL_ERROR;
1036     }
1037     }
1038    
1039     /*
1040     *----------------------------------------------------------------------
1041     *
1042     * InfoLevelCmd --
1043     *
1044     * Called to implement the "info level" command that returns
1045     * information about the call stack. Handles the following syntax:
1046     *
1047     * info level ?number?
1048     *
1049     * Results:
1050     * Returns TCL_OK if successful and TCL_ERROR if there is an error.
1051     *
1052     * Side effects:
1053     * Returns a result in the interpreter's result object. If there is
1054     * an error, the result is an error message.
1055     *
1056     *----------------------------------------------------------------------
1057     */
1058    
1059     static int
1060     InfoLevelCmd(dummy, interp, objc, objv)
1061     ClientData dummy; /* Not used. */
1062     Tcl_Interp *interp; /* Current interpreter. */
1063     int objc; /* Number of arguments. */
1064     Tcl_Obj *CONST objv[]; /* Argument objects. */
1065     {
1066     Interp *iPtr = (Interp *) interp;
1067     int level;
1068     CallFrame *framePtr;
1069     Tcl_Obj *listPtr;
1070    
1071     if (objc == 2) { /* just "info level" */
1072     if (iPtr->varFramePtr == NULL) {
1073     Tcl_SetIntObj(Tcl_GetObjResult(interp), 0);
1074     } else {
1075     Tcl_SetIntObj(Tcl_GetObjResult(interp), iPtr->varFramePtr->level);
1076     }
1077     return TCL_OK;
1078     } else if (objc == 3) {
1079     if (Tcl_GetIntFromObj(interp, objv[2], &level) != TCL_OK) {
1080     return TCL_ERROR;
1081     }
1082     if (level <= 0) {
1083     if (iPtr->varFramePtr == NULL) {
1084     levelError:
1085     Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
1086     "bad level \"",
1087     Tcl_GetString(objv[2]),
1088     "\"", (char *) NULL);
1089     return TCL_ERROR;
1090     }
1091     level += iPtr->varFramePtr->level;
1092     }
1093     for (framePtr = iPtr->varFramePtr; framePtr != NULL;
1094     framePtr = framePtr->callerVarPtr) {
1095     if (framePtr->level == level) {
1096     break;
1097     }
1098     }
1099     if (framePtr == NULL) {
1100     goto levelError;
1101     }
1102    
1103     listPtr = Tcl_NewListObj(framePtr->objc, framePtr->objv);
1104     Tcl_SetObjResult(interp, listPtr);
1105     return TCL_OK;
1106     }
1107    
1108     Tcl_WrongNumArgs(interp, 2, objv, "?number?");
1109     return TCL_ERROR;
1110     }
1111    
1112     /*
1113     *----------------------------------------------------------------------
1114     *
1115     * InfoLibraryCmd --
1116     *
1117     * Called to implement the "info library" command that returns the
1118     * library directory for the Tcl installation. Handles the following
1119     * syntax:
1120     *
1121     * info library
1122     *
1123     * Results:
1124     * Returns TCL_OK if successful and TCL_ERROR if there is an error.
1125     *
1126     * Side effects:
1127     * Returns a result in the interpreter's result object. If there is
1128     * an error, the result is an error message.
1129     *
1130     *----------------------------------------------------------------------
1131     */
1132    
1133     static int
1134     InfoLibraryCmd(dummy, interp, objc, objv)
1135     ClientData dummy; /* Not used. */
1136     Tcl_Interp *interp; /* Current interpreter. */
1137     int objc; /* Number of arguments. */
1138     Tcl_Obj *CONST objv[]; /* Argument objects. */
1139     {
1140     char *libDirName;
1141    
1142     if (objc != 2) {
1143     Tcl_WrongNumArgs(interp, 2, objv, NULL);
1144     return TCL_ERROR;
1145     }
1146    
1147     libDirName = Tcl_GetVar(interp, "tcl_library", TCL_GLOBAL_ONLY);
1148     if (libDirName != NULL) {
1149     Tcl_SetStringObj(Tcl_GetObjResult(interp), libDirName, -1);
1150     return TCL_OK;
1151     }
1152     Tcl_SetStringObj(Tcl_GetObjResult(interp),
1153     "no library has been specified for Tcl", -1);
1154     return TCL_ERROR;
1155     }
1156    
1157     /*
1158     *----------------------------------------------------------------------
1159     *
1160     * InfoLoadedCmd --
1161     *
1162     * Called to implement the "info loaded" command that returns the
1163     * packages that have been loaded into an interpreter. Handles the
1164     * following syntax:
1165     *
1166     * info loaded ?interp?
1167     *
1168     * Results:
1169     * Returns TCL_OK if successful and TCL_ERROR if there is an error.
1170     *
1171     * Side effects:
1172     * Returns a result in the interpreter's result object. If there is
1173     * an error, the result is an error message.
1174     *
1175     *----------------------------------------------------------------------
1176     */
1177    
1178     static int
1179     InfoLoadedCmd(dummy, interp, objc, objv)
1180     ClientData dummy; /* Not used. */
1181     Tcl_Interp *interp; /* Current interpreter. */
1182     int objc; /* Number of arguments. */
1183     Tcl_Obj *CONST objv[]; /* Argument objects. */
1184     {
1185     char *interpName;
1186     int result;
1187    
1188     if ((objc != 2) && (objc != 3)) {
1189     Tcl_WrongNumArgs(interp, 2, objv, "?interp?");
1190     return TCL_ERROR;
1191     }
1192    
1193     if (objc == 2) { /* get loaded pkgs in all interpreters */
1194     interpName = NULL;
1195     } else { /* get pkgs just in specified interp */
1196     interpName = Tcl_GetString(objv[2]);
1197     }
1198     result = TclGetLoadedPackages(interp, interpName);
1199     return result;
1200     }
1201    
1202     /*
1203     *----------------------------------------------------------------------
1204     *
1205     * InfoLocalsCmd --
1206     *
1207     * Called to implement the "info locals" command to return a list of
1208     * local variables that match an optional pattern. Handles the
1209     * following syntax:
1210     *
1211     * info locals ?pattern?
1212     *
1213     * Results:
1214     * Returns TCL_OK if successful and TCL_ERROR if there is an error.
1215     *
1216     * Side effects:
1217     * Returns a result in the interpreter's result object. If there is
1218     * an error, the result is an error message.
1219     *
1220     *----------------------------------------------------------------------
1221     */
1222    
1223     static int
1224     InfoLocalsCmd(dummy, interp, objc, objv)
1225     ClientData dummy; /* Not used. */
1226     Tcl_Interp *interp; /* Current interpreter. */
1227     int objc; /* Number of arguments. */
1228     Tcl_Obj *CONST objv[]; /* Argument objects. */
1229     {
1230     Interp *iPtr = (Interp *) interp;
1231     char *pattern;
1232     Tcl_Obj *listPtr;
1233    
1234     if (objc == 2) {
1235     pattern = NULL;
1236     } else if (objc == 3) {
1237     pattern = Tcl_GetString(objv[2]);
1238     } else {
1239     Tcl_WrongNumArgs(interp, 2, objv, "?pattern?");
1240     return TCL_ERROR;
1241     }
1242    
1243     if (iPtr->varFramePtr == NULL || !iPtr->varFramePtr->isProcCallFrame) {
1244     return TCL_OK;
1245     }
1246    
1247     /*
1248     * Return a list containing names of first the compiled locals (i.e. the
1249     * ones stored in the call frame), then the variables in the local hash
1250     * table (if one exists).
1251     */
1252    
1253     listPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
1254     AppendLocals(interp, listPtr, pattern, 0);
1255     Tcl_SetObjResult(interp, listPtr);
1256     return TCL_OK;
1257     }
1258    
1259     /*
1260     *----------------------------------------------------------------------
1261     *
1262     * AppendLocals --
1263     *
1264     * Append the local variables for the current frame to the
1265     * specified list object.
1266     *
1267     * Results:
1268     * None.
1269     *
1270     * Side effects:
1271     * None.
1272     *
1273     *----------------------------------------------------------------------
1274     */
1275    
1276     static void
1277     AppendLocals(interp, listPtr, pattern, includeLinks)
1278     Tcl_Interp *interp; /* Current interpreter. */
1279     Tcl_Obj *listPtr; /* List object to append names to. */
1280     char *pattern; /* Pattern to match against. */
1281     int includeLinks; /* 1 if upvars should be included, else 0. */
1282     {
1283     Interp *iPtr = (Interp *) interp;
1284     CompiledLocal *localPtr;
1285     Var *varPtr;
1286     int i, localVarCt;
1287     char *varName;
1288     Tcl_HashTable *localVarTablePtr;
1289     register Tcl_HashEntry *entryPtr;
1290     Tcl_HashSearch search;
1291    
1292     localPtr = iPtr->varFramePtr->procPtr->firstLocalPtr;
1293     localVarCt = iPtr->varFramePtr->numCompiledLocals;
1294     varPtr = iPtr->varFramePtr->compiledLocals;
1295     localVarTablePtr = iPtr->varFramePtr->varTablePtr;
1296    
1297     for (i = 0; i < localVarCt; i++) {
1298     /*
1299     * Skip nameless (temporary) variables and undefined variables
1300     */
1301    
1302     if (!TclIsVarTemporary(localPtr) && !TclIsVarUndefined(varPtr)) {
1303     varName = varPtr->name;
1304     if ((pattern == NULL) || Tcl_StringMatch(varName, pattern)) {
1305     Tcl_ListObjAppendElement(interp, listPtr,
1306     Tcl_NewStringObj(varName, -1));
1307     }
1308     }
1309     varPtr++;
1310     localPtr = localPtr->nextPtr;
1311     }
1312    
1313     if (localVarTablePtr != NULL) {
1314     for (entryPtr = Tcl_FirstHashEntry(localVarTablePtr, &search);
1315     entryPtr != NULL;
1316     entryPtr = Tcl_NextHashEntry(&search)) {
1317     varPtr = (Var *) Tcl_GetHashValue(entryPtr);
1318     if (!TclIsVarUndefined(varPtr)
1319     && (includeLinks || !TclIsVarLink(varPtr))) {
1320     varName = Tcl_GetHashKey(localVarTablePtr, entryPtr);
1321     if ((pattern == NULL)
1322     || Tcl_StringMatch(varName, pattern)) {
1323     Tcl_ListObjAppendElement(interp, listPtr,
1324     Tcl_NewStringObj(varName, -1));
1325     }
1326     }
1327     }
1328     }
1329     }
1330    
1331     /*
1332     *----------------------------------------------------------------------
1333     *
1334     * InfoNameOfExecutableCmd --
1335     *
1336     * Called to implement the "info nameofexecutable" command that returns
1337     * the name of the binary file running this application. Handles the
1338     * following syntax:
1339     *
1340     * info nameofexecutable
1341     *
1342     * Results:
1343     * Returns TCL_OK if successful and TCL_ERROR if there is an error.
1344     *
1345     * Side effects:
1346     * Returns a result in the interpreter's result object. If there is
1347     * an error, the result is an error message.
1348     *
1349     *----------------------------------------------------------------------
1350     */
1351    
1352     static int
1353     InfoNameOfExecutableCmd(dummy, interp, objc, objv)
1354     ClientData dummy; /* Not used. */
1355     Tcl_Interp *interp; /* Current interpreter. */
1356     int objc; /* Number of arguments. */
1357     Tcl_Obj *CONST objv[]; /* Argument objects. */
1358     {
1359     CONST char *nameOfExecutable;
1360    
1361     if (objc != 2) {
1362     Tcl_WrongNumArgs(interp, 2, objv, NULL);
1363     return TCL_ERROR;
1364     }
1365    
1366     nameOfExecutable = Tcl_GetNameOfExecutable();
1367    
1368     if (nameOfExecutable != NULL) {
1369     Tcl_SetStringObj(Tcl_GetObjResult(interp), (char *)nameOfExecutable, -1);
1370     }
1371     return TCL_OK;
1372     }
1373    
1374     /*
1375     *----------------------------------------------------------------------
1376     *
1377     * InfoPatchLevelCmd --
1378     *
1379     * Called to implement the "info patchlevel" command that returns the
1380     * default value for an argument to a procedure. Handles the following
1381     * syntax:
1382     *
1383     * info patchlevel
1384     *
1385     * Results:
1386     * Returns TCL_OK if successful and TCL_ERROR if there is an error.
1387     *
1388     * Side effects:
1389     * Returns a result in the interpreter's result object. If there is
1390     * an error, the result is an error message.
1391     *
1392     *----------------------------------------------------------------------
1393     */
1394    
1395     static int
1396     InfoPatchLevelCmd(dummy, interp, objc, objv)
1397     ClientData dummy; /* Not used. */
1398     Tcl_Interp *interp; /* Current interpreter. */
1399     int objc; /* Number of arguments. */
1400     Tcl_Obj *CONST objv[]; /* Argument objects. */
1401     {
1402     char *patchlevel;
1403    
1404     if (objc != 2) {
1405     Tcl_WrongNumArgs(interp, 2, objv, NULL);
1406     return TCL_ERROR;
1407     }
1408    
1409     patchlevel = Tcl_GetVar(interp, "tcl_patchLevel",
1410     (TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG));
1411     if (patchlevel != NULL) {
1412     Tcl_SetStringObj(Tcl_GetObjResult(interp), patchlevel, -1);
1413     return TCL_OK;
1414     }
1415     return TCL_ERROR;
1416     }
1417    
1418     /*
1419     *----------------------------------------------------------------------
1420     *
1421     * InfoProcsCmd --
1422     *
1423     * Called to implement the "info procs" command that returns the
1424     * list of procedures in the interpreter that match an optional pattern.
1425     * The pattern, if any, consists of an optional sequence of namespace
1426     * names separated by "::" qualifiers, which is followed by a
1427     * glob-style pattern that restricts which commands are returned.
1428     * Handles the following syntax:
1429     *
1430     * info procs ?pattern?
1431     *
1432     * Results:
1433     * Returns TCL_OK if successful and TCL_ERROR if there is an error.
1434     *
1435     * Side effects:
1436     * Returns a result in the interpreter's result object. If there is
1437     * an error, the result is an error message.
1438     *
1439     *----------------------------------------------------------------------
1440     */
1441    
1442     static int
1443     InfoProcsCmd(dummy, interp, objc, objv)
1444     ClientData dummy; /* Not used. */
1445     Tcl_Interp *interp; /* Current interpreter. */
1446     int objc; /* Number of arguments. */
1447     Tcl_Obj *CONST objv[]; /* Argument objects. */
1448     {
1449     char *cmdName, *pattern, *simplePattern;
1450     Namespace *nsPtr;
1451     #ifdef INFO_PROCS_SEARCH_GLOBAL_NS
1452     Namespace *globalNsPtr = (Namespace *) Tcl_GetGlobalNamespace(interp);
1453     #endif
1454     Namespace *currNsPtr = (Namespace *) Tcl_GetCurrentNamespace(interp);
1455     Tcl_Obj *listPtr, *elemObjPtr;
1456     int specificNsInPattern = 0; /* Init. to avoid compiler warning. */
1457     register Tcl_HashEntry *entryPtr;
1458     Tcl_HashSearch search;
1459     Command *cmdPtr, *realCmdPtr;
1460    
1461     /*
1462     * Get the pattern and find the "effective namespace" in which to
1463     * list procs.
1464     */
1465    
1466     if (objc == 2) {
1467     simplePattern = NULL;
1468     nsPtr = currNsPtr;
1469     specificNsInPattern = 0;
1470     } else if (objc == 3) {
1471     /*
1472     * From the pattern, get the effective namespace and the simple
1473     * pattern (no namespace qualifiers or ::'s) at the end. If an
1474     * error was found while parsing the pattern, return it. Otherwise,
1475     * if the namespace wasn't found, just leave nsPtr NULL: we will
1476     * return an empty list since no commands there can be found.
1477     */
1478    
1479     Namespace *dummy1NsPtr, *dummy2NsPtr;
1480    
1481     pattern = Tcl_GetString(objv[2]);
1482     TclGetNamespaceForQualName(interp, pattern, (Namespace *) NULL,
1483     /*flags*/ 0, &nsPtr, &dummy1NsPtr, &dummy2NsPtr,
1484     &simplePattern);
1485    
1486     if (nsPtr != NULL) { /* we successfully found the pattern's ns */
1487     specificNsInPattern = (strcmp(simplePattern, pattern) != 0);
1488     }
1489     } else {
1490     Tcl_WrongNumArgs(interp, 2, objv, "?pattern?");
1491     return TCL_ERROR;
1492     }
1493    
1494     /*
1495     * Scan through the effective namespace's command table and create a
1496     * list with all procs that match the pattern. If a specific
1497     * namespace was requested in the pattern, qualify the command names
1498     * with the namespace name.
1499     */
1500    
1501     listPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
1502     if (nsPtr != NULL) {
1503     entryPtr = Tcl_FirstHashEntry(&nsPtr->cmdTable, &search);
1504     while (entryPtr != NULL) {
1505     cmdName = Tcl_GetHashKey(&nsPtr->cmdTable, entryPtr);
1506     if ((simplePattern == NULL)
1507     || Tcl_StringMatch(cmdName, simplePattern)) {
1508     cmdPtr = (Command *) Tcl_GetHashValue(entryPtr);
1509    
1510     if (specificNsInPattern) {
1511     elemObjPtr = Tcl_NewObj();
1512     Tcl_GetCommandFullName(interp, (Tcl_Command) cmdPtr,
1513     elemObjPtr);
1514     } else {
1515     elemObjPtr = Tcl_NewStringObj(cmdName, -1);
1516     }
1517    
1518     realCmdPtr = (Command *)
1519     TclGetOriginalCommand((Tcl_Command) cmdPtr);
1520    
1521     if (TclIsProc(cmdPtr)
1522     || ((realCmdPtr != NULL) && TclIsProc(realCmdPtr))) {
1523     Tcl_ListObjAppendElement(interp, listPtr, elemObjPtr);
1524     }
1525     }
1526     entryPtr = Tcl_NextHashEntry(&search);
1527     }
1528    
1529     /*
1530     * If the effective namespace isn't the global :: namespace, and a
1531     * specific namespace wasn't requested in the pattern, then add in
1532     * all global :: procs that match the simple pattern. Of course,
1533     * we add in only those procs that aren't hidden by a proc in
1534     * the effective namespace.
1535     */
1536    
1537     #ifdef INFO_PROCS_SEARCH_GLOBAL_NS
1538     /*
1539     * If "info procs" worked like "info commands", returning the
1540     * commands also seen in the global namespace, then you would
1541     * include this code. As this could break backwards compatibilty
1542     * with 8.0-8.2, we decided not to "fix" it in 8.3, leaving the
1543     * behavior slightly different.
1544     */
1545     if ((nsPtr != globalNsPtr) && !specificNsInPattern) {
1546     entryPtr = Tcl_FirstHashEntry(&globalNsPtr->cmdTable, &search);
1547     while (entryPtr != NULL) {
1548     cmdName = Tcl_GetHashKey(&globalNsPtr->cmdTable, entryPtr);
1549     if ((simplePattern == NULL)
1550     || Tcl_StringMatch(cmdName, simplePattern)) {
1551     if (Tcl_FindHashEntry(&nsPtr->cmdTable, cmdName) == NULL) {
1552     cmdPtr = (Command *) Tcl_GetHashValue(entryPtr);
1553     realCmdPtr = (Command *) TclGetOriginalCommand(
1554     (Tcl_Command) cmdPtr);
1555    
1556     if (TclIsProc(cmdPtr) || ((realCmdPtr != NULL)
1557     && TclIsProc(realCmdPtr))) {
1558     Tcl_ListObjAppendElement(interp, listPtr,
1559     Tcl_NewStringObj(cmdName, -1));
1560     }
1561     }
1562     }
1563     entryPtr = Tcl_NextHashEntry(&search);
1564     }
1565     }
1566     #endif
1567     }
1568    
1569     Tcl_SetObjResult(interp, listPtr);
1570     return TCL_OK;
1571     }
1572    
1573     /*
1574     *----------------------------------------------------------------------
1575     *
1576     * InfoScriptCmd --
1577     *
1578     * Called to implement the "info script" command that returns the
1579     * script file that is currently being evaluated. Handles the
1580     * following syntax:
1581     *
1582     * info script
1583     *
1584     * Results:
1585     * Returns TCL_OK if successful and TCL_ERROR if there is an error.
1586     *
1587     * Side effects:
1588     * Returns a result in the interpreter's result object. If there is
1589     * an error, the result is an error message.
1590     *
1591     *----------------------------------------------------------------------
1592     */
1593    
1594     static int
1595     InfoScriptCmd(dummy, interp, objc, objv)
1596     ClientData dummy; /* Not used. */
1597     Tcl_Interp *interp; /* Current interpreter. */
1598     int objc; /* Number of arguments. */
1599     Tcl_Obj *CONST objv[]; /* Argument objects. */
1600     {
1601     Interp *iPtr = (Interp *) interp;
1602     if (objc != 2) {
1603     Tcl_WrongNumArgs(interp, 2, objv, NULL);
1604     return TCL_ERROR;
1605     }
1606    
1607     if (iPtr->scriptFile != NULL) {
1608     Tcl_SetStringObj(Tcl_GetObjResult(interp), iPtr->scriptFile, -1);
1609     }
1610     return TCL_OK;
1611     }
1612    
1613     /*
1614     *----------------------------------------------------------------------
1615     *
1616     * InfoSharedlibCmd --
1617     *
1618     * Called to implement the "info sharedlibextension" command that
1619     * returns the file extension used for shared libraries. Handles the
1620     * following syntax:
1621     *
1622     * info sharedlibextension
1623     *
1624     * Results:
1625     * Returns TCL_OK if successful and TCL_ERROR if there is an error.
1626     *
1627     * Side effects:
1628     * Returns a result in the interpreter's result object. If there is
1629     * an error, the result is an error message.
1630     *
1631     *----------------------------------------------------------------------
1632     */
1633    
1634     static int
1635     InfoSharedlibCmd(dummy, interp, objc, objv)
1636     ClientData dummy; /* Not used. */
1637     Tcl_Interp *interp; /* Current interpreter. */
1638     int objc; /* Number of arguments. */
1639     Tcl_Obj *CONST objv[]; /* Argument objects. */
1640     {
1641     if (objc != 2) {
1642     Tcl_WrongNumArgs(interp, 2, objv, NULL);
1643     return TCL_ERROR;
1644     }
1645    
1646     #ifdef TCL_SHLIB_EXT
1647     Tcl_SetStringObj(Tcl_GetObjResult(interp), TCL_SHLIB_EXT, -1);
1648     #endif
1649     return TCL_OK;
1650     }
1651    
1652     /*
1653     *----------------------------------------------------------------------
1654     *
1655     * InfoTclVersionCmd --
1656     *
1657     * Called to implement the "info tclversion" command that returns the
1658     * version number for this Tcl library. Handles the following syntax:
1659     *
1660     * info tclversion
1661     *
1662     * Results:
1663     * Returns TCL_OK if successful and TCL_ERROR if there is an error.
1664     *
1665     * Side effects:
1666     * Returns a result in the interpreter's result object. If there is
1667     * an error, the result is an error message.
1668     *
1669     *----------------------------------------------------------------------
1670     */
1671    
1672     static int
1673     InfoTclVersionCmd(dummy, interp, objc, objv)
1674     ClientData dummy; /* Not used. */
1675     Tcl_Interp *interp; /* Current interpreter. */
1676     int objc; /* Number of arguments. */
1677     Tcl_Obj *CONST objv[]; /* Argument objects. */
1678     {
1679     char *version;
1680    
1681     if (objc != 2) {
1682     Tcl_WrongNumArgs(interp, 2, objv, NULL);
1683     return TCL_ERROR;
1684     }
1685    
1686     version = Tcl_GetVar(interp, "tcl_version",
1687     (TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG));
1688     if (version != NULL) {
1689     Tcl_SetStringObj(Tcl_GetObjResult(interp), version, -1);
1690     return TCL_OK;
1691     }
1692     return TCL_ERROR;
1693     }
1694    
1695     /*
1696     *----------------------------------------------------------------------
1697     *
1698     * InfoVarsCmd --
1699     *
1700     * Called to implement the "info vars" command that returns the
1701     * list of variables in the interpreter that match an optional pattern.
1702     * The pattern, if any, consists of an optional sequence of namespace
1703     * names separated by "::" qualifiers, which is followed by a
1704     * glob-style pattern that restricts which variables are returned.
1705     * Handles the following syntax:
1706     *
1707     * info vars ?pattern?
1708     *
1709     * Results:
1710     * Returns TCL_OK if successful and TCL_ERROR if there is an error.
1711     *
1712     * Side effects:
1713     * Returns a result in the interpreter's result object. If there is
1714     * an error, the result is an error message.
1715     *
1716     *----------------------------------------------------------------------
1717     */
1718    
1719     static int
1720     InfoVarsCmd(dummy, interp, objc, objv)
1721     ClientData dummy; /* Not used. */
1722     Tcl_Interp *interp; /* Current interpreter. */
1723     int objc; /* Number of arguments. */
1724     Tcl_Obj *CONST objv[]; /* Argument objects. */
1725     {
1726     Interp *iPtr = (Interp *) interp;
1727     char *varName, *pattern, *simplePattern;
1728     register Tcl_HashEntry *entryPtr;
1729     Tcl_HashSearch search;
1730     Var *varPtr;
1731     Namespace *nsPtr;
1732     Namespace *globalNsPtr = (Namespace *) Tcl_GetGlobalNamespace(interp);
1733     Namespace *currNsPtr = (Namespace *) Tcl_GetCurrentNamespace(interp);
1734     Tcl_Obj *listPtr, *elemObjPtr;
1735     int specificNsInPattern = 0; /* Init. to avoid compiler warning. */
1736    
1737     /*
1738     * Get the pattern and find the "effective namespace" in which to
1739     * list variables. We only use this effective namespace if there's
1740     * no active Tcl procedure frame.
1741     */
1742    
1743     if (objc == 2) {
1744     simplePattern = NULL;
1745     nsPtr = currNsPtr;
1746     specificNsInPattern = 0;
1747     } else if (objc == 3) {
1748     /*
1749     * From the pattern, get the effective namespace and the simple
1750     * pattern (no namespace qualifiers or ::'s) at the end. If an
1751     * error was found while parsing the pattern, return it. Otherwise,
1752     * if the namespace wasn't found, just leave nsPtr NULL: we will
1753     * return an empty list since no variables there can be found.
1754     */
1755    
1756     Namespace *dummy1NsPtr, *dummy2NsPtr;
1757    
1758     pattern = Tcl_GetString(objv[2]);
1759     TclGetNamespaceForQualName(interp, pattern, (Namespace *) NULL,
1760     /*flags*/ 0, &nsPtr, &dummy1NsPtr, &dummy2NsPtr,
1761     &simplePattern);
1762    
1763     if (nsPtr != NULL) { /* we successfully found the pattern's ns */
1764     specificNsInPattern = (strcmp(simplePattern, pattern) != 0);
1765     }
1766     } else {
1767     Tcl_WrongNumArgs(interp, 2, objv, "?pattern?");
1768     return TCL_ERROR;
1769     }
1770    
1771     /*
1772     * If the namespace specified in the pattern wasn't found, just return.
1773     */
1774    
1775     if (nsPtr == NULL) {
1776     return TCL_OK;
1777     }
1778    
1779     listPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
1780    
1781     if ((iPtr->varFramePtr == NULL)
1782     || !iPtr->varFramePtr->isProcCallFrame
1783     || specificNsInPattern) {
1784     /*
1785     * There is no frame pointer, the frame pointer was pushed only
1786     * to activate a namespace, or we are in a procedure call frame
1787     * but a specific namespace was specified. Create a list containing
1788     * only the variables in the effective namespace's variable table.
1789     */
1790    
1791     entryPtr = Tcl_FirstHashEntry(&nsPtr->varTable, &search);
1792     while (entryPtr != NULL) {
1793     varPtr = (Var *) Tcl_GetHashValue(entryPtr);
1794     if (!TclIsVarUndefined(varPtr)
1795     || (varPtr->flags & VAR_NAMESPACE_VAR)) {
1796     varName = Tcl_GetHashKey(&nsPtr->varTable, entryPtr);
1797     if ((simplePattern == NULL)
1798     || Tcl_StringMatch(varName, simplePattern)) {
1799     if (specificNsInPattern) {
1800     elemObjPtr = Tcl_NewObj();
1801     Tcl_GetVariableFullName(interp, (Tcl_Var) varPtr,
1802     elemObjPtr);
1803     } else {
1804     elemObjPtr = Tcl_NewStringObj(varName, -1);
1805     }
1806     Tcl_ListObjAppendElement(interp, listPtr, elemObjPtr);
1807     }
1808     }
1809     entryPtr = Tcl_NextHashEntry(&search);
1810     }
1811    
1812     /*
1813     * If the effective namespace isn't the global :: namespace, and a
1814     * specific namespace wasn't requested in the pattern (i.e., the
1815     * pattern only specifies variable names), then add in all global ::
1816     * variables that match the simple pattern. Of course, add in only
1817     * those variables that aren't hidden by a variable in the effective
1818     * namespace.
1819     */
1820    
1821     if ((nsPtr != globalNsPtr) && !specificNsInPattern) {
1822     entryPtr = Tcl_FirstHashEntry(&globalNsPtr->varTable, &search);
1823     while (entryPtr != NULL) {
1824     varPtr = (Var *) Tcl_GetHashValue(entryPtr);
1825     if (!TclIsVarUndefined(varPtr)
1826     || (varPtr->flags & VAR_NAMESPACE_VAR)) {
1827     varName = Tcl_GetHashKey(&globalNsPtr->varTable,
1828     entryPtr);
1829     if ((simplePattern == NULL)
1830     || Tcl_StringMatch(varName, simplePattern)) {
1831     if (Tcl_FindHashEntry(&nsPtr->varTable, varName) == NULL) {
1832     Tcl_ListObjAppendElement(interp, listPtr,
1833     Tcl_NewStringObj(varName, -1));
1834     }
1835     }
1836     }
1837     entryPtr = Tcl_NextHashEntry(&search);
1838     }
1839     }
1840     } else if (((Interp *)interp)->varFramePtr->procPtr != NULL) {
1841     AppendLocals(interp, listPtr, simplePattern, 1);
1842     }
1843    
1844     Tcl_SetObjResult(interp, listPtr);
1845     return TCL_OK;
1846     }
1847    
1848     /*
1849     *----------------------------------------------------------------------
1850     *
1851     * Tcl_JoinObjCmd --
1852     *
1853     * This procedure is invoked to process the "join" Tcl command.
1854     * See the user documentation for details on what it does.
1855     *
1856     * Results:
1857     * A standard Tcl object result.
1858     *
1859     * Side effects:
1860     * See the user documentation.
1861     *
1862     *----------------------------------------------------------------------
1863     */
1864    
1865     /* ARGSUSED */
1866     int
1867     Tcl_JoinObjCmd(dummy, interp, objc, objv)
1868     ClientData dummy; /* Not used. */
1869     Tcl_Interp *interp; /* Current interpreter. */
1870     int objc; /* Number of arguments. */
1871     Tcl_Obj *CONST objv[]; /* The argument objects. */
1872     {
1873     char *joinString, *bytes;
1874     int joinLength, listLen, length, i, result;
1875     Tcl_Obj **elemPtrs;
1876     Tcl_Obj *resObjPtr;
1877    
1878     if (objc == 2) {
1879     joinString = " ";
1880     joinLength = 1;
1881     } else if (objc == 3) {
1882     joinString = Tcl_GetStringFromObj(objv[2], &joinLength);
1883     } else {
1884     Tcl_WrongNumArgs(interp, 1, objv, "list ?joinString?");
1885     return TCL_ERROR;
1886     }
1887    
1888     /*
1889     * Make sure the list argument is a list object and get its length and
1890     * a pointer to its array of element pointers.
1891     */
1892    
1893     result = Tcl_ListObjGetElements(interp, objv[1], &listLen, &elemPtrs);
1894     if (result != TCL_OK) {
1895     return result;
1896     }
1897    
1898     /*
1899     * Now concatenate strings to form the "joined" result. We append
1900     * directly into the interpreter's result object.
1901     */
1902    
1903     resObjPtr = Tcl_GetObjResult(interp);
1904    
1905     for (i = 0; i < listLen; i++) {
1906     bytes = Tcl_GetStringFromObj(elemPtrs[i], &length);
1907     if (i > 0) {
1908     Tcl_AppendToObj(resObjPtr, joinString, joinLength);
1909     }
1910     Tcl_AppendToObj(resObjPtr, bytes, length);
1911     }
1912     return TCL_OK;
1913     }
1914    
1915     /*
1916     *----------------------------------------------------------------------
1917     *
1918     * Tcl_LindexObjCmd --
1919     *
1920     * This object-based procedure is invoked to process the "lindex" Tcl
1921     * command. See the user documentation for details on what it does.
1922     *
1923     * Results:
1924     * A standard Tcl object result.
1925     *
1926     * Side effects:
1927     * See the user documentation.
1928     *
1929     *----------------------------------------------------------------------
1930     */
1931    
1932     /* ARGSUSED */
1933     int
1934     Tcl_LindexObjCmd(dummy, interp, objc, objv)
1935     ClientData dummy; /* Not used. */
1936     Tcl_Interp *interp; /* Current interpreter. */
1937     int objc; /* Number of arguments. */
1938     Tcl_Obj *CONST objv[]; /* Argument objects. */
1939     {
1940     Tcl_Obj *listPtr;
1941     Tcl_Obj **elemPtrs;
1942     int listLen, index, result;
1943    
1944     if (objc != 3) {
1945     Tcl_WrongNumArgs(interp, 1, objv, "list index");
1946     return TCL_ERROR;
1947     }
1948    
1949     /*
1950     * Convert the first argument to a list if necessary.
1951     */
1952    
1953     listPtr = objv[1];
1954     result = Tcl_ListObjGetElements(interp, listPtr, &listLen, &elemPtrs);
1955     if (result != TCL_OK) {
1956     return result;
1957     }
1958    
1959     /*
1960     * Get the index from objv[2].
1961     */
1962    
1963     result = TclGetIntForIndex(interp, objv[2], /*endValue*/ (listLen - 1),
1964     &index);
1965     if (result != TCL_OK) {
1966     return result;
1967     }
1968     if ((index < 0) || (index >= listLen)) {
1969     /*
1970     * The index is out of range: the result is an empty string object.
1971     */
1972    
1973     return TCL_OK;
1974     }
1975    
1976     /*
1977     * Make sure listPtr still refers to a list object. It might have been
1978     * converted to an int above if the argument objects were shared.
1979     */
1980    
1981     if (listPtr->typePtr != &tclListType) {
1982     result = Tcl_ListObjGetElements(interp, listPtr, &listLen,
1983     &elemPtrs);
1984     if (result != TCL_OK) {
1985     return result;
1986     }
1987     }
1988    
1989     /*
1990     * Set the interpreter's object result to the index-th list element.
1991     */
1992    
1993     Tcl_SetObjResult(interp, elemPtrs[index]);
1994     return TCL_OK;
1995     }
1996    
1997     /*
1998     *----------------------------------------------------------------------
1999     *
2000     * Tcl_LinsertObjCmd --
2001     *
2002     * This object-based procedure is invoked to process the "linsert" Tcl
2003     * command. See the user documentation for details on what it does.
2004     *
2005     * Results:
2006     * A new Tcl list object formed by inserting zero or more elements
2007     * into a list.
2008     *
2009     * Side effects:
2010     * See the user documentation.
2011     *
2012     *----------------------------------------------------------------------
2013     */
2014    
2015     /* ARGSUSED */
2016     int
2017     Tcl_LinsertObjCmd(dummy, interp, objc, objv)
2018     ClientData dummy; /* Not used. */
2019     Tcl_Interp *interp; /* Current interpreter. */
2020     register int objc; /* Number of arguments. */
2021     Tcl_Obj *CONST objv[]; /* Argument objects. */
2022     {
2023     Tcl_Obj *listPtr, *resultPtr;
2024     Tcl_ObjType *typePtr;
2025     int index, isDuplicate, len, result;
2026    
2027     if (objc < 4) {
2028     Tcl_WrongNumArgs(interp, 1, objv, "list index element ?element ...?");
2029     return TCL_ERROR;
2030     }
2031    
2032     /*
2033     * Get the index first since, if a conversion to int is needed, it
2034     * will invalidate the list's internal representation.
2035     */
2036    
2037     result = Tcl_ListObjLength(interp, objv[1], &len);
2038     if (result != TCL_OK) {
2039     return result;
2040     }
2041    
2042     result = TclGetIntForIndex(interp, objv[2], /*endValue*/ len, &index);
2043     if (result != TCL_OK) {
2044     return result;
2045     }
2046    
2047     /*
2048     * If the list object is unshared we can modify it directly. Otherwise
2049     * we create a copy to modify: this is "copy on write". We create the
2050     * duplicate directly in the interpreter's object result.
2051     */
2052    
2053     listPtr = objv[1];
2054     isDuplicate = 0;
2055     if (Tcl_IsShared(listPtr)) {
2056     /*
2057     * The following code must reflect the logic in Tcl_DuplicateObj()
2058     * except that it must duplicate the list object directly into the
2059     * interpreter's result.
2060     */
2061    
2062     Tcl_ResetResult(interp);
2063     resultPtr = Tcl_GetObjResult(interp);
2064     typePtr = listPtr->typePtr;
2065     if (listPtr->bytes == NULL) {
2066     resultPtr->bytes = NULL;
2067     } else if (listPtr->bytes != tclEmptyStringRep) {
2068     len = listPtr->length;
2069     TclInitStringRep(resultPtr, listPtr->bytes, len);
2070     }
2071     if (typePtr != NULL) {
2072     if (typePtr->dupIntRepProc == NULL) {
2073     resultPtr->internalRep = listPtr->internalRep;
2074     resultPtr->typePtr = typePtr;
2075     } else {
2076     (*typePtr->dupIntRepProc)(listPtr, resultPtr);
2077     }
2078     }
2079     listPtr = resultPtr;
2080     isDuplicate = 1;
2081     }
2082    
2083     if ((objc == 4) && (index == INT_MAX)) {
2084     /*
2085     * Special case: insert one element at the end of the list.
2086     */
2087    
2088     result = Tcl_ListObjAppendElement(interp, listPtr, objv[3]);
2089     } else if (objc > 3) {
2090     result = Tcl_ListObjReplace(interp, listPtr, index, 0,
2091     (objc-3), &(objv[3]));
2092     }
2093     if (result != TCL_OK) {
2094     return result;
2095     }
2096    
2097     /*
2098     * Set the interpreter's object result.
2099     */
2100    
2101     if (!isDuplicate) {
2102     Tcl_SetObjResult(interp, listPtr);
2103     }
2104     return TCL_OK;
2105     }
2106    
2107     /*
2108     *----------------------------------------------------------------------
2109     *
2110     * Tcl_ListObjCmd --
2111     *
2112     * This procedure is invoked to process the "list" Tcl command.
2113     * See the user documentation for details on what it does.
2114     *
2115     * Results:
2116     * A standard Tcl object result.
2117     *
2118     * Side effects:
2119     * See the user documentation.
2120     *
2121     *----------------------------------------------------------------------
2122     */
2123    
2124     /* ARGSUSED */
2125     int
2126     Tcl_ListObjCmd(dummy, interp, objc, objv)
2127     ClientData dummy; /* Not used. */
2128     Tcl_Interp *interp; /* Current interpreter. */
2129     register int objc; /* Number of arguments. */
2130     register Tcl_Obj *CONST objv[]; /* The argument objects. */
2131     {
2132     /*
2133     * If there are no list elements, the result is an empty object.
2134     * Otherwise modify the interpreter's result object to be a list object.
2135     */
2136    
2137     if (objc > 1) {
2138     Tcl_SetListObj(Tcl_GetObjResult(interp), (objc-1), &(objv[1]));
2139     }
2140     return TCL_OK;
2141     }
2142    
2143     /*
2144     *----------------------------------------------------------------------
2145     *
2146     * Tcl_LlengthObjCmd --
2147     *
2148     * This object-based procedure is invoked to process the "llength" Tcl
2149     * command. See the user documentation for details on what it does.
2150     *
2151     * Results:
2152     * A standard Tcl object result.
2153     *
2154     * Side effects:
2155     * See the user documentation.
2156     *
2157     *----------------------------------------------------------------------
2158     */
2159    
2160     /* ARGSUSED */
2161     int
2162     Tcl_LlengthObjCmd(dummy, interp, objc, objv)
2163     ClientData dummy; /* Not used. */
2164     Tcl_Interp *interp; /* Current interpreter. */
2165     int objc; /* Number of arguments. */
2166     register Tcl_Obj *CONST objv[]; /* Argument objects. */
2167     {
2168     int listLen, result;
2169    
2170     if (objc != 2) {
2171     Tcl_WrongNumArgs(interp, 1, objv, "list");
2172     return TCL_ERROR;
2173     }
2174    
2175     result = Tcl_ListObjLength(interp, objv[1], &listLen);
2176     if (result != TCL_OK) {
2177     return result;
2178     }
2179    
2180     /*
2181     * Set the interpreter's object result to an integer object holding the
2182     * length.
2183     */
2184    
2185     Tcl_SetIntObj(Tcl_GetObjResult(interp), listLen);
2186     return TCL_OK;
2187     }
2188    
2189     /*
2190     *----------------------------------------------------------------------
2191     *
2192     * Tcl_LrangeObjCmd --
2193     *
2194     * This procedure is invoked to process the "lrange" Tcl command.
2195     * See the user documentation for details on what it does.
2196     *
2197     * Results:
2198     * A standard Tcl object result.
2199     *
2200     * Side effects:
2201     * See the user documentation.
2202     *
2203     *----------------------------------------------------------------------
2204     */
2205    
2206     /* ARGSUSED */
2207     int
2208     Tcl_LrangeObjCmd(notUsed, interp, objc, objv)
2209     ClientData notUsed; /* Not used. */
2210     Tcl_Interp *interp; /* Current interpreter. */
2211     int objc; /* Number of arguments. */
2212     register Tcl_Obj *CONST objv[]; /* Argument objects. */
2213     {
2214     Tcl_Obj *listPtr;
2215     Tcl_Obj **elemPtrs;
2216     int listLen, first, last, numElems, result;
2217    
2218     if (objc != 4) {
2219     Tcl_WrongNumArgs(interp, 1, objv, "list first last");
2220     return TCL_ERROR;
2221     }
2222    
2223     /*
2224     * Make sure the list argument is a list object and get its length and
2225     * a pointer to its array of element pointers.
2226     */
2227    
2228     listPtr = objv[1];
2229     result = Tcl_ListObjGetElements(interp, listPtr, &listLen, &elemPtrs);
2230     if (result != TCL_OK) {
2231     return result;
2232     }
2233    
2234     /*
2235     * Get the first and last indexes.
2236     */
2237    
2238     result = TclGetIntForIndex(interp, objv[2], /*endValue*/ (listLen - 1),
2239     &first);
2240     if (result != TCL_OK) {
2241     return result;
2242     }
2243     if (first < 0) {
2244     first = 0;
2245     }
2246    
2247     result = TclGetIntForIndex(interp, objv[3], /*endValue*/ (listLen - 1),
2248     &last);
2249     if (result != TCL_OK) {
2250     return result;
2251     }
2252     if (last >= listLen) {
2253     last = (listLen - 1);
2254     }
2255    
2256     if (first > last) {
2257     return TCL_OK; /* the result is an empty object */
2258     }
2259    
2260     /*
2261     * Make sure listPtr still refers to a list object. It might have been
2262     * converted to an int above if the argument objects were shared.
2263     */
2264    
2265     if (listPtr->typePtr != &tclListType) {
2266     result = Tcl_ListObjGetElements(interp, listPtr, &listLen,
2267     &elemPtrs);
2268     if (result != TCL_OK) {
2269     return result;
2270     }
2271     }
2272    
2273     /*
2274     * Extract a range of fields. We modify the interpreter's result object
2275     * to be a list object containing the specified elements.
2276     */
2277    
2278     numElems = (last - first + 1);
2279     Tcl_SetListObj(Tcl_GetObjResult(interp), numElems, &(elemPtrs[first]));
2280     return TCL_OK;
2281     }
2282    
2283     /*
2284     *----------------------------------------------------------------------
2285     *
2286     * Tcl_LreplaceObjCmd --
2287     *
2288     * This object-based procedure is invoked to process the "lreplace"
2289     * Tcl command. See the user documentation for details on what it does.
2290     *
2291     * Results:
2292     * A new Tcl list object formed by replacing zero or more elements of
2293     * a list.
2294     *
2295     * Side effects:
2296     * See the user documentation.
2297     *
2298     *----------------------------------------------------------------------
2299     */
2300    
2301     /* ARGSUSED */
2302     int
2303     Tcl_LreplaceObjCmd(dummy, interp, objc, objv)
2304     ClientData dummy; /* Not used. */
2305     Tcl_Interp *interp; /* Current interpreter. */
2306     int objc; /* Number of arguments. */
2307     Tcl_Obj *CONST objv[]; /* Argument objects. */
2308     {
2309     register Tcl_Obj *listPtr;
2310     int createdNewObj, first, last, listLen, numToDelete;
2311     int firstArgLen, result;
2312     char *firstArg;
2313    
2314     if (objc < 4) {
2315     Tcl_WrongNumArgs(interp, 1, objv,
2316     "list first last ?element element ...?");
2317     return TCL_ERROR;
2318     }
2319    
2320     /*
2321     * If the list object is unshared we can modify it directly, otherwise
2322     * we create a copy to modify: this is "copy on write".
2323     */
2324    
2325     listPtr = objv[1];
2326     createdNewObj = 0;
2327     if (Tcl_IsShared(listPtr)) {
2328     listPtr = Tcl_DuplicateObj(listPtr);
2329     createdNewObj = 1;
2330     }
2331     result = Tcl_ListObjLength(interp, listPtr, &listLen);
2332     if (result != TCL_OK) {
2333     errorReturn:
2334     if (createdNewObj) {
2335     Tcl_DecrRefCount(listPtr); /* free unneeded obj */
2336     }
2337     return result;
2338     }
2339    
2340     /*
2341     * Get the first and last indexes.
2342     */
2343    
2344     result = TclGetIntForIndex(interp, objv[2], /*endValue*/ (listLen - 1),
2345     &first);
2346     if (result != TCL_OK) {
2347     goto errorReturn;
2348     }
2349     firstArg = Tcl_GetStringFromObj(objv[2], &firstArgLen);
2350    
2351     result = TclGetIntForIndex(interp, objv[3], /*endValue*/ (listLen - 1),
2352     &last);
2353     if (result != TCL_OK) {
2354     goto errorReturn;
2355     }
2356    
2357     if (first < 0) {
2358     first = 0;
2359     }
2360     if ((first >= listLen) && (listLen > 0)
2361     && (strncmp(firstArg, "end", (unsigned) firstArgLen) != 0)) {
2362     Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
2363     "list doesn't contain element ",
2364     Tcl_GetString(objv[2]), (int *) NULL);
2365     result = TCL_ERROR;
2366     goto errorReturn;
2367     }
2368     if (last >= listLen) {
2369     last = (listLen - 1);
2370     }
2371     if (first <= last) {
2372     numToDelete = (last - first + 1);
2373     } else {
2374     numToDelete = 0;
2375     }
2376    
2377     if (objc > 4) {
2378     result = Tcl_ListObjReplace(interp, listPtr, first, numToDelete,
2379     (objc-4), &(objv[4]));
2380     } else {
2381     result = Tcl_ListObjReplace(interp, listPtr, first, numToDelete,
2382     0, NULL);
2383     }
2384     if (result != TCL_OK) {
2385     goto errorReturn;
2386     }
2387    
2388     /*
2389     * Set the interpreter's object result.
2390     */
2391    
2392     Tcl_SetObjResult(interp, listPtr);
2393     return TCL_OK;
2394     }
2395    
2396     /*
2397     *----------------------------------------------------------------------
2398     *
2399     * Tcl_LsearchObjCmd --
2400     *
2401     * This procedure is invoked to process the "lsearch" Tcl command.
2402     * See the user documentation for details on what it does.
2403     *
2404     * Results:
2405     * A standard Tcl result.
2406     *
2407     * Side effects:
2408     * See the user documentation.
2409     *
2410     *----------------------------------------------------------------------
2411     */
2412    
2413     int
2414     Tcl_LsearchObjCmd(clientData, interp, objc, objv)
2415     ClientData clientData; /* Not used. */
2416     Tcl_Interp *interp; /* Current interpreter. */
2417     int objc; /* Number of arguments. */
2418     Tcl_Obj *CONST objv[]; /* Argument values. */
2419     {
2420     char *bytes, *patternBytes;
2421     int i, match, mode, index, result, listc, length, elemLen;
2422     Tcl_Obj *patObj, **listv;
2423     static char *options[] = {
2424     "-exact", "-glob", "-regexp", NULL
2425     };
2426     enum options {
2427     LSEARCH_EXACT, LSEARCH_GLOB, LSEARCH_REGEXP
2428     };
2429    
2430     mode = LSEARCH_GLOB;
2431     if (objc == 4) {
2432     if (Tcl_GetIndexFromObj(interp, objv[1], options, "search mode", 0,
2433     &mode) != TCL_OK) {
2434     return TCL_ERROR;
2435     }
2436     } else if (objc != 3) {
2437     Tcl_WrongNumArgs(interp, 1, objv, "?mode? list pattern");
2438     return TCL_ERROR;
2439     }
2440    
2441     /*
2442     * Make sure the list argument is a list object and get its length and
2443     * a pointer to its array of element pointers.
2444     */
2445    
2446     result = Tcl_ListObjGetElements(interp, objv[objc - 2], &listc, &listv);
2447     if (result != TCL_OK) {
2448     return result;
2449     }
2450    
2451     patObj = objv[objc - 1];
2452     patternBytes = Tcl_GetStringFromObj(patObj, &length);
2453    
2454     index = -1;
2455     for (i = 0; i < listc; i++) {
2456     match = 0;
2457     switch ((enum options) mode) {
2458     case LSEARCH_EXACT: {
2459     bytes = Tcl_GetStringFromObj(listv[i], &elemLen);
2460     if (length == elemLen) {
2461     match = (memcmp(bytes, patternBytes,
2462     (size_t) length) == 0);
2463     }
2464     break;
2465     }
2466     case LSEARCH_GLOB: {
2467     match = Tcl_StringMatch(Tcl_GetString(listv[i]), patternBytes);
2468     break;
2469     }
2470     case LSEARCH_REGEXP: {
2471     match = Tcl_RegExpMatchObj(interp, listv[i], patObj);
2472     if (match < 0) {
2473     return TCL_ERROR;
2474     }
2475     break;
2476     }
2477     }
2478     if (match != 0) {
2479     index = i;
2480     break;
2481     }
2482     }
2483     Tcl_SetIntObj(Tcl_GetObjResult(interp), index);
2484     return TCL_OK;
2485     }
2486    
2487     /*
2488     *----------------------------------------------------------------------
2489     *
2490     * Tcl_LsortObjCmd --
2491     *
2492     * This procedure is invoked to process the "lsort" Tcl command.
2493     * See the user documentation for details on what it does.
2494     *
2495     * Results:
2496     * A standard Tcl result.
2497     *
2498     * Side effects:
2499     * See the user documentation.
2500     *
2501     *----------------------------------------------------------------------
2502     */
2503    
2504     int
2505     Tcl_LsortObjCmd(clientData, interp, objc, objv)
2506     ClientData clientData; /* Not used. */
2507     Tcl_Interp *interp; /* Current interpreter. */
2508     int objc; /* Number of arguments. */
2509     Tcl_Obj *CONST objv[]; /* Argument values. */
2510     {
2511     int i, index, unique;
2512     Tcl_Obj *resultPtr;
2513     int length;
2514     Tcl_Obj *cmdPtr, **listObjPtrs;
2515     SortElement *elementArray;
2516     SortElement *elementPtr;
2517     SortInfo sortInfo; /* Information about this sort that
2518     * needs to be passed to the
2519     * comparison function */
2520     static char *switches[] = {
2521     "-ascii", "-command", "-decreasing", "-dictionary", "-increasing",
2522     "-index", "-integer", "-real", "-unique", (char *) NULL
2523     };
2524    
2525     resultPtr = Tcl_GetObjResult(interp);
2526     if (objc < 2) {
2527     Tcl_WrongNumArgs(interp, 1, objv, "?options? list");
2528     return TCL_ERROR;
2529     }
2530    
2531     /*
2532     * Parse arguments to set up the mode for the sort.
2533     */
2534    
2535     sortInfo.isIncreasing = 1;
2536     sortInfo.sortMode = SORTMODE_ASCII;
2537     sortInfo.index = -1;
2538     sortInfo.interp = interp;
2539     sortInfo.resultCode = TCL_OK;
2540     cmdPtr = NULL;
2541     unique = 0;
2542     for (i = 1; i < objc-1; i++) {
2543     if (Tcl_GetIndexFromObj(interp, objv[i], switches, "option", 0, &index)
2544     != TCL_OK) {
2545     return TCL_ERROR;
2546     }
2547     switch (index) {
2548     case 0: /* -ascii */
2549     sortInfo.sortMode = SORTMODE_ASCII;
2550     break;
2551     case 1: /* -command */
2552     if (i == (objc-2)) {
2553     Tcl_AppendToObj(resultPtr,
2554     "\"-command\" option must be followed by comparison command",
2555     -1);
2556     return TCL_ERROR;
2557     }
2558     sortInfo.sortMode = SORTMODE_COMMAND;
2559     cmdPtr = objv[i+1];
2560     i++;
2561     break;
2562     case 2: /* -decreasing */
2563     sortInfo.isIncreasing = 0;
2564     break;
2565     case 3: /* -dictionary */
2566     sortInfo.sortMode = SORTMODE_DICTIONARY;
2567     break;
2568     case 4: /* -increasing */
2569     sortInfo.isIncreasing = 1;
2570     break;
2571     case 5: /* -index */
2572     if (i == (objc-2)) {
2573     Tcl_AppendToObj(resultPtr,
2574     "\"-index\" option must be followed by list index",
2575     -1);
2576     return TCL_ERROR;
2577     }
2578     if (TclGetIntForIndex(interp, objv[i+1], -2, &sortInfo.index)
2579     != TCL_OK) {
2580     return TCL_ERROR;
2581     }
2582     cmdPtr = objv[i+1];
2583     i++;
2584     break;
2585     case 6: /* -integer */
2586     sortInfo.sortMode = SORTMODE_INTEGER;
2587     break;
2588     case 7: /* -real */
2589     sortInfo.sortMode = SORTMODE_REAL;
2590     break;
2591     case 8: /* -unique */
2592     unique = 1;
2593     break;
2594     }
2595     }
2596     if (sortInfo.sortMode == SORTMODE_COMMAND) {
2597     /*
2598     * The existing command is a list. We want to flatten it, append
2599     * two dummy arguments on the end, and replace these arguments
2600     * later.
2601     */
2602    
2603     Tcl_Obj *newCommandPtr = Tcl_DuplicateObj(cmdPtr);
2604     Tcl_Obj *newObjPtr = Tcl_NewObj();
2605    
2606     Tcl_IncrRefCount(newCommandPtr);
2607     if (Tcl_ListObjAppendElement(interp, newCommandPtr, newObjPtr)
2608     != TCL_OK) {
2609     Tcl_DecrRefCount(newCommandPtr);
2610     Tcl_IncrRefCount(newObjPtr);
2611     Tcl_DecrRefCount(newObjPtr);
2612     return TCL_ERROR;
2613     }
2614     Tcl_ListObjAppendElement(interp, newCommandPtr, Tcl_NewObj());
2615     sortInfo.compareCmdPtr = newCommandPtr;
2616     }
2617    
2618     sortInfo.resultCode = Tcl_ListObjGetElements(interp, objv[objc-1],
2619     &length, &listObjPtrs);
2620     if (sortInfo.resultCode != TCL_OK) {
2621     goto done;
2622     }
2623     if (length <= 0) {
2624     return TCL_OK;
2625     }
2626     elementArray = (SortElement *) ckalloc(length * sizeof(SortElement));
2627     for (i=0; i < length; i++){
2628     elementArray[i].objPtr = listObjPtrs[i];
2629     elementArray[i].count = 0;
2630     elementArray[i].nextPtr = &elementArray[i+1];
2631     }
2632     elementArray[length-1].nextPtr = NULL;
2633     elementPtr = MergeSort(elementArray, &sortInfo);
2634     if (sortInfo.resultCode == TCL_OK) {
2635     /*
2636     * Note: must clear the interpreter's result object: it could
2637     * have been set by the -command script.
2638     */
2639    
2640     Tcl_ResetResult(interp);
2641     resultPtr = Tcl_GetObjResult(interp);
2642     if (unique) {
2643     for (; elementPtr != NULL; elementPtr = elementPtr->nextPtr){
2644     if (elementPtr->count == 0) {
2645     Tcl_ListObjAppendElement(interp, resultPtr,
2646     elementPtr->objPtr);
2647     }
2648     }
2649     } else {
2650     for (; elementPtr != NULL; elementPtr = elementPtr->nextPtr){
2651     Tcl_ListObjAppendElement(interp, resultPtr,
2652     elementPtr->objPtr);
2653     }
2654     }
2655     }
2656     ckfree((char*) elementArray);
2657    
2658     done:
2659     if (sortInfo.sortMode == SORTMODE_COMMAND) {
2660     Tcl_DecrRefCount(sortInfo.compareCmdPtr);
2661     sortInfo.compareCmdPtr = NULL;
2662     }
2663     return sortInfo.resultCode;
2664     }
2665    
2666     /*
2667     *----------------------------------------------------------------------
2668     *
2669     * MergeSort -
2670     *
2671     * This procedure sorts a linked list of SortElement structures
2672     * use the merge-sort algorithm.
2673     *
2674     * Results:
2675     * A pointer to the head of the list after sorting is returned.
2676     *
2677     * Side effects:
2678     * None, unless a user-defined comparison command does something
2679     * weird.
2680     *
2681     *----------------------------------------------------------------------
2682     */
2683    
2684     static SortElement *
2685     MergeSort(headPtr, infoPtr)
2686     SortElement *headPtr; /* First element on the list */
2687     SortInfo *infoPtr; /* Information needed by the
2688     * comparison operator */
2689     {
2690     /*
2691     * The subList array below holds pointers to temporary lists built
2692     * during the merge sort. Element i of the array holds a list of
2693     * length 2**i.
2694     */
2695    
2696     # define NUM_LISTS 30
2697     SortElement *subList[NUM_LISTS];
2698     SortElement *elementPtr;
2699     int i;
2700    
2701     for(i = 0; i < NUM_LISTS; i++){
2702     subList[i] = NULL;
2703     }
2704     while (headPtr != NULL) {
2705     elementPtr = headPtr;
2706     headPtr = headPtr->nextPtr;
2707     elementPtr->nextPtr = 0;
2708     for (i = 0; (i < NUM_LISTS) && (subList[i] != NULL); i++){
2709     elementPtr = MergeLists(subList[i], elementPtr, infoPtr);
2710     subList[i] = NULL;
2711     }
2712     if (i >= NUM_LISTS) {
2713     i = NUM_LISTS-1;
2714     }
2715     subList[i] = elementPtr;
2716     }
2717     elementPtr = NULL;
2718     for (i = 0; i < NUM_LISTS; i++){
2719     elementPtr = MergeLists(subList[i], elementPtr, infoPtr);
2720     }
2721     return elementPtr;
2722     }
2723    
2724     /*
2725     *----------------------------------------------------------------------
2726     *
2727     * MergeLists -
2728     *
2729     * This procedure combines two sorted lists of SortElement structures
2730     * into a single sorted list.
2731     *
2732     * Results:
2733     * The unified list of SortElement structures.
2734     *
2735     * Side effects:
2736     * None, unless a user-defined comparison command does something
2737     * weird.
2738     *
2739     *----------------------------------------------------------------------
2740     */
2741    
2742     static SortElement *
2743     MergeLists(leftPtr, rightPtr, infoPtr)
2744     SortElement *leftPtr; /* First list to be merged; may be
2745     * NULL. */
2746     SortElement *rightPtr; /* Second list to be merged; may be
2747     * NULL. */
2748     SortInfo *infoPtr; /* Information needed by the
2749     * comparison operator. */
2750     {
2751     SortElement *headPtr;
2752     SortElement *tailPtr;
2753     int cmp;
2754    
2755     if (leftPtr == NULL) {
2756     return rightPtr;
2757     }
2758     if (rightPtr == NULL) {
2759     return leftPtr;
2760     }
2761     cmp = SortCompare(leftPtr->objPtr, rightPtr->objPtr, infoPtr);
2762     if (cmp > 0) {
2763     tailPtr = rightPtr;
2764     rightPtr = rightPtr->nextPtr;
2765     } else {
2766     if (cmp == 0) {
2767     leftPtr->count++;
2768     }
2769     tailPtr = leftPtr;
2770     leftPtr = leftPtr->nextPtr;
2771     }
2772     headPtr = tailPtr;
2773     while ((leftPtr != NULL) && (rightPtr != NULL)) {
2774     cmp = SortCompare(leftPtr->objPtr, rightPtr->objPtr, infoPtr);
2775     if (cmp > 0) {
2776     tailPtr->nextPtr = rightPtr;
2777     tailPtr = rightPtr;
2778     rightPtr = rightPtr->nextPtr;
2779     } else {
2780     if (cmp == 0) {
2781     leftPtr->count++;
2782     }
2783     tailPtr->nextPtr = leftPtr;
2784     tailPtr = leftPtr;
2785     leftPtr = leftPtr->nextPtr;
2786     }
2787     }
2788     if (leftPtr != NULL) {
2789     tailPtr->nextPtr = leftPtr;
2790     } else {
2791     tailPtr->nextPtr = rightPtr;
2792     }
2793     return headPtr;
2794     }
2795    
2796     /*
2797     *----------------------------------------------------------------------
2798     *
2799     * SortCompare --
2800     *
2801     * This procedure is invoked by MergeLists to determine the proper
2802     * ordering between two elements.
2803     *
2804     * Results:
2805     * A negative results means the the first element comes before the
2806     * second, and a positive results means that the second element
2807     * should come first. A result of zero means the two elements
2808     * are equal and it doesn't matter which comes first.
2809     *
2810     * Side effects:
2811     * None, unless a user-defined comparison command does something
2812     * weird.
2813     *
2814     *----------------------------------------------------------------------
2815     */
2816    
2817     static int
2818     SortCompare(objPtr1, objPtr2, infoPtr)
2819     Tcl_Obj *objPtr1, *objPtr2; /* Values to be compared. */
2820     SortInfo *infoPtr; /* Information passed from the
2821     * top-level "lsort" command */
2822     {
2823     int order, listLen, index;
2824     Tcl_Obj *objPtr;
2825     char buffer[TCL_INTEGER_SPACE];
2826    
2827     order = 0;
2828     if (infoPtr->resultCode != TCL_OK) {
2829     /*
2830     * Once an error has occurred, skip any future comparisons
2831     * so as to preserve the error message in sortInterp->result.
2832     */
2833    
2834     return order;
2835     }
2836     if (infoPtr->index != -1) {
2837     /*
2838     * The "-index" option was specified. Treat each object as a
2839     * list, extract the requested element from each list, and
2840     * compare the elements, not the lists. The special index "end"
2841     * is signaled here with a large negative index.
2842     */
2843    
2844     if (Tcl_ListObjLength(infoPtr->interp, objPtr1, &listLen) != TCL_OK) {
2845     infoPtr->resultCode = TCL_ERROR;
2846     return order;
2847     }
2848     if (infoPtr->index < -1) {
2849     index = listLen - 1;
2850     } else {
2851     index = infoPtr->index;
2852     }
2853    
2854     if (Tcl_ListObjIndex(infoPtr->interp, objPtr1, index, &objPtr)
2855     != TCL_OK) {
2856     infoPtr->resultCode = TCL_ERROR;
2857     return order;
2858     }
2859     if (objPtr == NULL) {
2860     objPtr = objPtr1;
2861     missingElement:
2862     TclFormatInt(buffer, infoPtr->index);
2863     Tcl_AppendStringsToObj(Tcl_GetObjResult(infoPtr->interp),
2864     "element ", buffer, " missing from sublist \"",
2865     Tcl_GetString(objPtr), "\"", (char *) NULL);
2866     infoPtr->resultCode = TCL_ERROR;
2867     return order;
2868     }
2869     objPtr1 = objPtr;
2870    
2871     if (Tcl_ListObjLength(infoPtr->interp, objPtr2, &listLen) != TCL_OK) {
2872     infoPtr->resultCode = TCL_ERROR;
2873     return order;
2874     }
2875     if (infoPtr->index < -1) {
2876     index = listLen - 1;
2877     } else {
2878     index = infoPtr->index;
2879     }
2880    
2881     if (Tcl_ListObjIndex(infoPtr->interp, objPtr2, index, &objPtr)
2882     != TCL_OK) {
2883     infoPtr->resultCode = TCL_ERROR;
2884     return order;
2885     }
2886     if (objPtr == NULL) {
2887     objPtr = objPtr2;
2888     goto missingElement;
2889     }
2890     objPtr2 = objPtr;
2891     }
2892     if (infoPtr->sortMode == SORTMODE_ASCII) {
2893     order = strcmp(Tcl_GetString(objPtr1), Tcl_GetString(objPtr2));
2894     } else if (infoPtr->sortMode == SORTMODE_DICTIONARY) {
2895     order = DictionaryCompare(
2896     Tcl_GetString(objPtr1), Tcl_GetString(objPtr2));
2897     } else if (infoPtr->sortMode == SORTMODE_INTEGER) {
2898     long a, b;
2899    
2900     if ((Tcl_GetLongFromObj(infoPtr->interp, objPtr1, &a) != TCL_OK)
2901     || (Tcl_GetLongFromObj(infoPtr->interp, objPtr2, &b)
2902     != TCL_OK)) {
2903     infoPtr->resultCode = TCL_ERROR;
2904     return order;
2905     }
2906     if (a > b) {
2907     order = 1;
2908     } else if (b > a) {
2909     order = -1;
2910     }
2911     } else if (infoPtr->sortMode == SORTMODE_REAL) {
2912     double a, b;
2913    
2914     if ((Tcl_GetDoubleFromObj(infoPtr->interp, objPtr1, &a) != TCL_OK)
2915     || (Tcl_GetDoubleFromObj(infoPtr->interp, objPtr2, &b)
2916     != TCL_OK)) {
2917     infoPtr->resultCode = TCL_ERROR;
2918     return order;
2919     }
2920     if (a > b) {
2921     order = 1;
2922     } else if (b > a) {
2923     order = -1;
2924     }
2925     } else {
2926     Tcl_Obj **objv, *paramObjv[2];
2927     int objc;
2928    
2929     paramObjv[0] = objPtr1;
2930     paramObjv[1] = objPtr2;
2931    
2932     /*
2933     * We made space in the command list for the two things to
2934     * compare. Replace them and evaluate the result.
2935     */
2936    
2937     Tcl_ListObjLength(infoPtr->interp, infoPtr->compareCmdPtr, &objc);
2938     Tcl_ListObjReplace(infoPtr->interp, infoPtr->compareCmdPtr, objc - 2,
2939     2, 2, paramObjv);
2940     Tcl_ListObjGetElements(infoPtr->interp, infoPtr->compareCmdPtr,
2941     &objc, &objv);
2942    
2943     infoPtr->resultCode = Tcl_EvalObjv(infoPtr->interp, objc, objv, 0);
2944    
2945     if (infoPtr->resultCode != TCL_OK) {
2946     Tcl_AddErrorInfo(infoPtr->interp,
2947     "\n (-compare command)");
2948     return order;
2949     }
2950    
2951     /*
2952     * Parse the result of the command.
2953     */
2954    
2955     if (Tcl_GetIntFromObj(infoPtr->interp,
2956     Tcl_GetObjResult(infoPtr->interp), &order) != TCL_OK) {
2957     Tcl_ResetResult(infoPtr->interp);
2958     Tcl_AppendToObj(Tcl_GetObjResult(infoPtr->interp),
2959     "-compare command returned non-numeric result", -1);
2960     infoPtr->resultCode = TCL_ERROR;
2961     return order;
2962     }
2963     }
2964     if (!infoPtr->isIncreasing) {
2965     order = -order;
2966     }
2967     return order;
2968     }
2969    
2970     /*
2971     *----------------------------------------------------------------------
2972     *
2973     * DictionaryCompare
2974     *
2975     * This function compares two strings as if they were being used in
2976     * an index or card catalog. The case of alphabetic characters is
2977     * ignored, except to break ties. Thus "B" comes before "b" but
2978     * after "a". Also, integers embedded in the strings compare in
2979     * numerical order. In other words, "x10y" comes after "x9y", not
2980     * before it as it would when using strcmp().
2981     *
2982     * Results:
2983     * A negative result means that the first element comes before the
2984     * second, and a positive result means that the second element
2985     * should come first. A result of zero means the two elements
2986     * are equal and it doesn't matter which comes first.
2987     *
2988     * Side effects:
2989     * None.
2990     *
2991     *----------------------------------------------------------------------
2992     */
2993    
2994     static int
2995     DictionaryCompare(left, right)
2996     char *left, *right; /* The strings to compare */
2997     {
2998     Tcl_UniChar uniLeft, uniRight, uniLeftLower, uniRightLower;
2999     int diff, zeros;
3000     int secondaryDiff = 0;
3001    
3002     while (1) {
3003     if (isdigit(UCHAR(*right)) /* INTL: digit */
3004     && isdigit(UCHAR(*left))) { /* INTL: digit */
3005     /*
3006     * There are decimal numbers embedded in the two
3007     * strings. Compare them as numbers, rather than
3008     * strings. If one number has more leading zeros than
3009     * the other, the number with more leading zeros sorts
3010     * later, but only as a secondary choice.
3011     */
3012    
3013     zeros = 0;
3014     while ((*right == '0') && (isdigit(UCHAR(right[1])))) {
3015     right++;
3016     zeros--;
3017     }
3018     while ((*left == '0') && (isdigit(UCHAR(left[1])))) {
3019     left++;
3020     zeros++;
3021     }
3022     if (secondaryDiff == 0) {
3023     secondaryDiff = zeros;
3024     }
3025    
3026     /*
3027     * The code below compares the numbers in the two
3028     * strings without ever converting them to integers. It
3029     * does this by first comparing the lengths of the
3030     * numbers and then comparing the digit values.
3031     */
3032    
3033     diff = 0;
3034     while (1) {
3035     if (diff == 0) {
3036     diff = UCHAR(*left) - UCHAR(*right);
3037     }
3038     right++;
3039     left++;
3040     if (!isdigit(UCHAR(*right))) { /* INTL: digit */
3041     if (isdigit(UCHAR(*left))) { /* INTL: digit */
3042     return 1;
3043     } else {
3044     /*
3045     * The two numbers have the same length. See
3046     * if their values are different.
3047     */
3048    
3049     if (diff != 0) {
3050     return diff;
3051     }
3052     break;
3053     }
3054     } else if (!isdigit(UCHAR(*left))) { /* INTL: digit */
3055     return -1;
3056     }
3057     }
3058     continue;
3059     }
3060    
3061     /*
3062     * Convert character to Unicode for comparison purposes. If either
3063     * string is at the terminating null, do a byte-wise comparison and
3064     * bail out immediately.
3065     */
3066    
3067     if ((*left != '\0') && (*right != '\0')) {
3068     left += Tcl_UtfToUniChar(left, &uniLeft);
3069     right += Tcl_UtfToUniChar(right, &uniRight);
3070     /*
3071     * Convert both chars to lower for the comparison, because
3072     * dictionary sorts are case insensitve. Covert to lower, not
3073     * upper, so chars between Z and a will sort before A (where most
3074     * other interesting punctuations occur)
3075     */
3076     uniLeftLower = Tcl_UniCharToLower(uniLeft);
3077     uniRightLower = Tcl_UniCharToLower(uniRight);
3078     } else {
3079     diff = UCHAR(*left) - UCHAR(*right);
3080     break;
3081     }
3082    
3083     diff = uniLeftLower - uniRightLower;
3084     if (diff) {
3085     return diff;
3086     } else if (secondaryDiff == 0) {
3087     if (Tcl_UniCharIsUpper(uniLeft) &&
3088     Tcl_UniCharIsLower(uniRight)) {
3089     secondaryDiff = -1;
3090     } else if (Tcl_UniCharIsUpper(uniRight)
3091     && Tcl_UniCharIsLower(uniLeft)) {
3092     secondaryDiff = 1;
3093     }
3094     }
3095     }
3096     if (diff == 0) {
3097     diff = secondaryDiff;
3098     }
3099     return diff;
3100     }
3101    
3102 dashley 66 /* End of tclcmdil.c */

Properties

Name Value
svn:keywords Header

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25