os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/generic/tclCmdIL.c
author sl
Tue, 10 Jun 2014 14:32:02 +0200
changeset 1 260cb5ec6c19
permissions -rw-r--r--
Update contrib.
     1 /* 
     2  * tclCmdIL.c --
     3  *
     4  *	This file contains the top-level command routines for most of
     5  *	the Tcl built-in commands whose names begin with the letters
     6  *	I through L.  It contains only commands in the generic core
     7  *	(i.e. those that don't depend much upon UNIX facilities).
     8  *
     9  * Copyright (c) 1987-1993 The Regents of the University of California.
    10  * Copyright (c) 1993-1997 Lucent Technologies.
    11  * Copyright (c) 1994-1997 Sun Microsystems, Inc.
    12  * Copyright (c) 1998-1999 by Scriptics Corporation.
    13  * Copyright (c) 2001 by Kevin B. Kenny.  All rights reserved.
    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.47.2.11 2007/03/10 14:57:38 dkf Exp $
    19  */
    20 
    21 #include "tclInt.h"
    22 #include "tclPort.h"
    23 #include "tclRegexp.h"
    24 
    25 /*
    26  * During execution of the "lsort" command, structures of the following
    27  * type are used to arrange the objects being sorted into a collection
    28  * of linked lists.
    29  */
    30 
    31 typedef struct SortElement {
    32     Tcl_Obj *objPtr;			/* Object being sorted. */
    33     int count;				/* number of same elements in list */
    34     struct SortElement *nextPtr;        /* Next element in the list, or
    35 					 * NULL for end of list. */
    36 } SortElement;
    37 
    38 /*
    39  * The "lsort" command needs to pass certain information down to the
    40  * function that compares two list elements, and the comparison function
    41  * needs to pass success or failure information back up to the top-level
    42  * "lsort" command.  The following structure is used to pass this
    43  * information.
    44  */
    45 
    46 typedef struct SortInfo {
    47     int isIncreasing;		/* Nonzero means sort in increasing order. */
    48     int sortMode;		/* The sort mode.  One of SORTMODE_*
    49 				 * values defined below */
    50     Tcl_Obj *compareCmdPtr;     /* The Tcl comparison command when sortMode
    51 				 * is SORTMODE_COMMAND.  Pre-initialized to
    52 				 * hold base of command.*/
    53     int index;			/* If the -index option was specified, this
    54 				 * holds the index of the list element
    55 				 * to extract for comparison.  If -index
    56 				 * wasn't specified, this is -1. */
    57     Tcl_Interp *interp;		/* The interpreter in which the sortis
    58 				 * being done. */
    59     int resultCode;		/* Completion code for the lsort command.
    60 				 * If an error occurs during the sort this
    61 				 * is changed from TCL_OK to  TCL_ERROR. */
    62 } SortInfo;
    63 
    64 /*
    65  * The "sortMode" field of the SortInfo structure can take on any of the
    66  * following values.
    67  */
    68 
    69 #define SORTMODE_ASCII      0
    70 #define SORTMODE_INTEGER    1
    71 #define SORTMODE_REAL       2
    72 #define SORTMODE_COMMAND    3
    73 #define SORTMODE_DICTIONARY 4
    74 
    75 /*
    76  * Magic values for the index field of the SortInfo structure.
    77  * Note that the index "end-1" will be translated to SORTIDX_END-1, etc.
    78  */
    79 #define SORTIDX_NONE	-1		/* Not indexed; use whole value. */
    80 #define SORTIDX_END	-2		/* Indexed from end. */
    81 
    82 /*
    83  * Forward declarations for procedures defined in this file:
    84  */
    85 
    86 static void		AppendLocals _ANSI_ARGS_((Tcl_Interp *interp,
    87 			    Tcl_Obj *listPtr, CONST char *pattern,
    88 			    int includeLinks));
    89 static int		DictionaryCompare _ANSI_ARGS_((char *left,
    90 			    char *right));
    91 static int		InfoArgsCmd _ANSI_ARGS_((ClientData dummy,
    92 			    Tcl_Interp *interp, int objc,
    93 			    Tcl_Obj *CONST objv[]));
    94 static int		InfoBodyCmd _ANSI_ARGS_((ClientData dummy,
    95 			    Tcl_Interp *interp, int objc,
    96 			    Tcl_Obj *CONST objv[]));
    97 static int		InfoCmdCountCmd _ANSI_ARGS_((ClientData dummy,
    98 			    Tcl_Interp *interp, int objc,
    99 			    Tcl_Obj *CONST objv[]));
   100 static int		InfoCommandsCmd _ANSI_ARGS_((ClientData dummy,
   101 			    Tcl_Interp *interp, int objc,
   102 			    Tcl_Obj *CONST objv[]));
   103 static int		InfoCompleteCmd _ANSI_ARGS_((ClientData dummy,
   104 			    Tcl_Interp *interp, int objc,
   105 			    Tcl_Obj *CONST objv[]));
   106 static int		InfoDefaultCmd _ANSI_ARGS_((ClientData dummy,
   107 			    Tcl_Interp *interp, int objc,
   108 			    Tcl_Obj *CONST objv[]));
   109 static int		InfoExistsCmd _ANSI_ARGS_((ClientData dummy,
   110 			    Tcl_Interp *interp, int objc,
   111 			    Tcl_Obj *CONST objv[]));
   112 #ifdef TCL_TIP280
   113 /* TIP #280 - New 'info' subcommand 'frame' */
   114 static int		InfoFrameCmd _ANSI_ARGS_((ClientData dummy,
   115 			    Tcl_Interp *interp, int objc,
   116 			    Tcl_Obj *CONST objv[]));
   117 #endif
   118 static int		InfoFunctionsCmd _ANSI_ARGS_((ClientData dummy,
   119 			    Tcl_Interp *interp, int objc,
   120 			    Tcl_Obj *CONST objv[]));
   121 static int		InfoGlobalsCmd _ANSI_ARGS_((ClientData dummy,
   122 			    Tcl_Interp *interp, int objc,
   123 			    Tcl_Obj *CONST objv[]));
   124 static int		InfoHostnameCmd _ANSI_ARGS_((ClientData dummy,
   125 			    Tcl_Interp *interp, int objc,
   126 			    Tcl_Obj *CONST objv[]));
   127 static int		InfoLevelCmd _ANSI_ARGS_((ClientData dummy,
   128 			    Tcl_Interp *interp, int objc,
   129 			    Tcl_Obj *CONST objv[]));
   130 static int		InfoLibraryCmd _ANSI_ARGS_((ClientData dummy,
   131 			    Tcl_Interp *interp, int objc,
   132 			    Tcl_Obj *CONST objv[]));
   133 static int		InfoLoadedCmd _ANSI_ARGS_((ClientData dummy,
   134 			    Tcl_Interp *interp, int objc,
   135 			    Tcl_Obj *CONST objv[]));
   136 static int		InfoLocalsCmd _ANSI_ARGS_((ClientData dummy,
   137 			    Tcl_Interp *interp, int objc,
   138 			    Tcl_Obj *CONST objv[]));
   139 static int		InfoNameOfExecutableCmd _ANSI_ARGS_((
   140 			    ClientData dummy, Tcl_Interp *interp, int objc,
   141 			    Tcl_Obj *CONST objv[]));
   142 static int		InfoPatchLevelCmd _ANSI_ARGS_((ClientData dummy,
   143 			    Tcl_Interp *interp, int objc,
   144 			    Tcl_Obj *CONST objv[]));
   145 static int		InfoProcsCmd _ANSI_ARGS_((ClientData dummy,
   146 			    Tcl_Interp *interp, int objc,
   147 			    Tcl_Obj *CONST objv[]));
   148 static int		InfoScriptCmd _ANSI_ARGS_((ClientData dummy,
   149 			    Tcl_Interp *interp, int objc,
   150 			    Tcl_Obj *CONST objv[]));
   151 static int		InfoSharedlibCmd _ANSI_ARGS_((ClientData dummy,
   152 			    Tcl_Interp *interp, int objc,
   153 			    Tcl_Obj *CONST objv[]));
   154 static int		InfoTclVersionCmd _ANSI_ARGS_((ClientData dummy,
   155 			    Tcl_Interp *interp, int objc,
   156 			    Tcl_Obj *CONST objv[]));
   157 static int		InfoVarsCmd _ANSI_ARGS_((ClientData dummy,
   158 			    Tcl_Interp *interp, int objc,
   159 			    Tcl_Obj *CONST objv[]));
   160 static SortElement *    MergeSort _ANSI_ARGS_((SortElement *headPt,
   161 			    SortInfo *infoPtr));
   162 static SortElement *    MergeLists _ANSI_ARGS_((SortElement *leftPtr,
   163 			    SortElement *rightPtr, SortInfo *infoPtr));
   164 static int		SortCompare _ANSI_ARGS_((Tcl_Obj *firstPtr,
   165 			    Tcl_Obj *second, SortInfo *infoPtr));
   166 
   167 /*
   168  *----------------------------------------------------------------------
   169  *
   170  * Tcl_IfObjCmd --
   171  *
   172  *	This procedure is invoked to process the "if" Tcl command.
   173  *	See the user documentation for details on what it does.
   174  *
   175  *	With the bytecode compiler, this procedure is only called when
   176  *	a command name is computed at runtime, and is "if" or the name
   177  *	to which "if" was renamed: e.g., "set z if; $z 1 {puts foo}"
   178  *
   179  * Results:
   180  *	A standard Tcl result.
   181  *
   182  * Side effects:
   183  *	See the user documentation.
   184  *
   185  *----------------------------------------------------------------------
   186  */
   187 
   188 	/* ARGSUSED */
   189 int
   190 Tcl_IfObjCmd(dummy, interp, objc, objv)
   191     ClientData dummy;			/* Not used. */
   192     Tcl_Interp *interp;			/* Current interpreter. */
   193     int objc;				/* Number of arguments. */
   194     Tcl_Obj *CONST objv[];		/* Argument objects. */
   195 {
   196     int thenScriptIndex = 0;	/* then script to be evaled after syntax check */
   197 #ifdef TCL_TIP280
   198     Interp* iPtr = (Interp*) interp;
   199 #endif
   200     int i, result, value;
   201     char *clause;
   202     i = 1;
   203     while (1) {
   204 	/*
   205 	 * At this point in the loop, objv and objc refer to an expression
   206 	 * to test, either for the main expression or an expression
   207 	 * following an "elseif".  The arguments after the expression must
   208 	 * be "then" (optional) and a script to execute if the expression is
   209 	 * true.
   210 	 */
   211 
   212 	if (i >= objc) {
   213 	    clause = Tcl_GetString(objv[i-1]);
   214 	    Tcl_AppendResult(interp, "wrong # args: no expression after \"",
   215 		    clause, "\" argument", (char *) NULL);
   216 	    return TCL_ERROR;
   217 	}
   218 	if (!thenScriptIndex) {
   219 	    result = Tcl_ExprBooleanObj(interp, objv[i], &value);
   220 	    if (result != TCL_OK) {
   221 		return result;
   222 	    }
   223 	}
   224 	i++;
   225 	if (i >= objc) {
   226 	    missingScript:
   227 	    clause = Tcl_GetString(objv[i-1]);
   228 	    Tcl_AppendResult(interp, "wrong # args: no script following \"",
   229 		    clause, "\" argument", (char *) NULL);
   230 	    return TCL_ERROR;
   231 	}
   232 	clause = Tcl_GetString(objv[i]);
   233 	if ((i < objc) && (strcmp(clause, "then") == 0)) {
   234 	    i++;
   235 	}
   236 	if (i >= objc) {
   237 	    goto missingScript;
   238 	}
   239 	if (value) {
   240 	    thenScriptIndex = i;
   241 	    value = 0;
   242 	}
   243 	
   244 	/*
   245 	 * The expression evaluated to false.  Skip the command, then
   246 	 * see if there is an "else" or "elseif" clause.
   247 	 */
   248 
   249 	i++;
   250 	if (i >= objc) {
   251 	    if (thenScriptIndex) {
   252 #ifndef TCL_TIP280
   253 		return Tcl_EvalObjEx(interp, objv[thenScriptIndex], 0);
   254 #else
   255 		/* TIP #280. Make invoking context available to branch */
   256 		return TclEvalObjEx(interp, objv[thenScriptIndex], 0,
   257 				    iPtr->cmdFramePtr,thenScriptIndex);
   258 #endif
   259 	    }
   260 	    return TCL_OK;
   261 	}
   262 	clause = Tcl_GetString(objv[i]);
   263 	if ((clause[0] == 'e') && (strcmp(clause, "elseif") == 0)) {
   264 	    i++;
   265 	    continue;
   266 	}
   267 	break;
   268     }
   269 
   270     /*
   271      * Couldn't find a "then" or "elseif" clause to execute.  Check now
   272      * for an "else" clause.  We know that there's at least one more
   273      * argument when we get here.
   274      */
   275 
   276     if (strcmp(clause, "else") == 0) {
   277 	i++;
   278 	if (i >= objc) {
   279 	    Tcl_AppendResult(interp,
   280 		    "wrong # args: no script following \"else\" argument",
   281 		    (char *) NULL);
   282 	    return TCL_ERROR;
   283 	}
   284     }
   285     if (i < objc - 1) {
   286 	Tcl_AppendResult(interp,
   287 		"wrong # args: extra words after \"else\" clause in \"if\" command",
   288 		(char *) NULL);
   289 	return TCL_ERROR;
   290     }
   291     if (thenScriptIndex) {
   292 #ifndef TCL_TIP280
   293 	return Tcl_EvalObjEx(interp, objv[thenScriptIndex], 0);
   294 #else
   295 	/* TIP #280. Make invoking context available to branch/else */
   296 	return TclEvalObjEx(interp, objv[thenScriptIndex], 0,
   297 			    iPtr->cmdFramePtr,thenScriptIndex);
   298 #endif
   299     }
   300 #ifndef TCL_TIP280
   301     return Tcl_EvalObjEx(interp, objv[i], 0);
   302 #else
   303     return TclEvalObjEx(interp, objv[i], 0, iPtr->cmdFramePtr,i);
   304 #endif
   305 }
   306 
   307 /*
   308  *----------------------------------------------------------------------
   309  *
   310  * Tcl_IncrObjCmd --
   311  *
   312  *	This procedure is invoked to process the "incr" Tcl command.
   313  *	See the user documentation for details on what it does.
   314  *
   315  *	With the bytecode compiler, this procedure is only called when
   316  *	a command name is computed at runtime, and is "incr" or the name
   317  *	to which "incr" was renamed: e.g., "set z incr; $z i -1"
   318  *
   319  * Results:
   320  *	A standard Tcl result.
   321  *
   322  * Side effects:
   323  *	See the user documentation.
   324  *
   325  *----------------------------------------------------------------------
   326  */
   327 
   328     /* ARGSUSED */
   329 int
   330 Tcl_IncrObjCmd(dummy, interp, objc, objv)
   331     ClientData dummy;			/* Not used. */
   332     Tcl_Interp *interp;			/* Current interpreter. */
   333     int objc;				/* Number of arguments. */
   334     Tcl_Obj *CONST objv[];		/* Argument objects. */
   335 {
   336     long incrAmount;
   337     Tcl_Obj *newValuePtr;
   338     
   339     if ((objc != 2) && (objc != 3)) {
   340         Tcl_WrongNumArgs(interp, 1, objv, "varName ?increment?");
   341 	return TCL_ERROR;
   342     }
   343 
   344     /*
   345      * Calculate the amount to increment by.
   346      */
   347     
   348     if (objc == 2) {
   349 	incrAmount = 1;
   350     } else {
   351 	if (Tcl_GetLongFromObj(interp, objv[2], &incrAmount) != TCL_OK) {
   352 	    Tcl_AddErrorInfo(interp, "\n    (reading increment)");
   353 	    return TCL_ERROR;
   354 	}
   355 	/*
   356 	 * Need to be a bit cautious to ensure that [expr]-like rules
   357 	 * are enforced for interpretation of wide integers, despite
   358 	 * the fact that the underlying API itself is a 'long' only one.
   359 	 */
   360 	if (objv[2]->typePtr == &tclIntType) {
   361 	    incrAmount = objv[2]->internalRep.longValue;
   362 	} else if (objv[2]->typePtr == &tclWideIntType) {
   363 	    TclGetLongFromWide(incrAmount,objv[2]);
   364 	} else {
   365 	    Tcl_WideInt wide;
   366 
   367 	    if (Tcl_GetWideIntFromObj(interp, objv[2], &wide) != TCL_OK) {
   368 		Tcl_AddErrorInfo(interp, "\n    (reading increment)");
   369 		return TCL_ERROR;
   370 	    }
   371 	    incrAmount = Tcl_WideAsLong(wide);
   372 	    if ((wide <= Tcl_LongAsWide(LONG_MAX))
   373 		    && (wide >= Tcl_LongAsWide(LONG_MIN))) {
   374 		objv[2]->typePtr = &tclIntType;
   375 		objv[2]->internalRep.longValue = incrAmount;
   376 	    }
   377 	}
   378     }
   379     
   380     /*
   381      * Increment the variable's value.
   382      */
   383 
   384     newValuePtr = TclIncrVar2(interp, objv[1], (Tcl_Obj *) NULL, incrAmount,
   385 	    TCL_LEAVE_ERR_MSG);
   386     if (newValuePtr == NULL) {
   387 	return TCL_ERROR;
   388     }
   389 
   390     /*
   391      * Set the interpreter's object result to refer to the variable's new
   392      * value object.
   393      */
   394 
   395     Tcl_SetObjResult(interp, newValuePtr);
   396     return TCL_OK; 
   397 }
   398 
   399 /*
   400  *----------------------------------------------------------------------
   401  *
   402  * Tcl_InfoObjCmd --
   403  *
   404  *	This procedure is invoked to process the "info" Tcl command.
   405  *	See the user documentation for details on what it does.
   406  *
   407  * Results:
   408  *	A standard Tcl result.
   409  *
   410  * Side effects:
   411  *	See the user documentation.
   412  *
   413  *----------------------------------------------------------------------
   414  */
   415 
   416 	/* ARGSUSED */
   417 int
   418 Tcl_InfoObjCmd(clientData, interp, objc, objv)
   419     ClientData clientData;	/* Arbitrary value passed to the command. */
   420     Tcl_Interp *interp;		/* Current interpreter. */
   421     int objc;			/* Number of arguments. */
   422     Tcl_Obj *CONST objv[];	/* Argument objects. */
   423 {
   424     static CONST char *subCmds[] = {
   425 	     "args", "body", "cmdcount", "commands",
   426 	     "complete", "default", "exists",
   427 #ifdef TCL_TIP280
   428 	     "frame",
   429 #endif
   430 	     "functions",
   431 	     "globals", "hostname", "level", "library", "loaded",
   432 	     "locals", "nameofexecutable", "patchlevel", "procs",
   433 	     "script", "sharedlibextension", "tclversion", "vars",
   434 	     (char *) NULL};
   435     enum ISubCmdIdx {
   436 	    IArgsIdx, IBodyIdx, ICmdCountIdx, ICommandsIdx,
   437 	    ICompleteIdx, IDefaultIdx, IExistsIdx,
   438 #ifdef TCL_TIP280
   439 	    IFrameIdx,
   440 #endif
   441 	    IFunctionsIdx,
   442 	    IGlobalsIdx, IHostnameIdx, ILevelIdx, ILibraryIdx, ILoadedIdx,
   443 	    ILocalsIdx, INameOfExecutableIdx, IPatchLevelIdx, IProcsIdx,
   444 	    IScriptIdx, ISharedLibExtensionIdx, ITclVersionIdx, IVarsIdx
   445     };
   446     int index, result;
   447 
   448     if (objc < 2) {
   449         Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?");
   450         return TCL_ERROR;
   451     }
   452     
   453     result = Tcl_GetIndexFromObj(interp, objv[1], subCmds, "option", 0,
   454 	    (int *) &index);
   455     if (result != TCL_OK) {
   456 	return result;
   457     }
   458 
   459     switch (index) {
   460         case IArgsIdx:
   461 	    result = InfoArgsCmd(clientData, interp, objc, objv);
   462             break;
   463 	case IBodyIdx:
   464 	    result = InfoBodyCmd(clientData, interp, objc, objv);
   465 	    break;
   466 	case ICmdCountIdx:
   467 	    result = InfoCmdCountCmd(clientData, interp, objc, objv);
   468 	    break;
   469         case ICommandsIdx:
   470 	    result = InfoCommandsCmd(clientData, interp, objc, objv);
   471 	    break;
   472         case ICompleteIdx:
   473 	    result = InfoCompleteCmd(clientData, interp, objc, objv);
   474 	    break;
   475 	case IDefaultIdx:
   476 	    result = InfoDefaultCmd(clientData, interp, objc, objv);
   477 	    break;
   478 	case IExistsIdx:
   479 	    result = InfoExistsCmd(clientData, interp, objc, objv);
   480 	    break;
   481 #ifdef TCL_TIP280
   482 	case IFrameIdx:
   483 	    /* TIP #280 - New method 'frame' */
   484 	    result = InfoFrameCmd(clientData, interp, objc, objv);
   485 	    break;
   486 #endif
   487 	case IFunctionsIdx:
   488 	    result = InfoFunctionsCmd(clientData, interp, objc, objv);
   489 	    break;
   490         case IGlobalsIdx:
   491 	    result = InfoGlobalsCmd(clientData, interp, objc, objv);
   492 	    break;
   493         case IHostnameIdx:
   494 	    result = InfoHostnameCmd(clientData, interp, objc, objv);
   495 	    break;
   496 	case ILevelIdx:
   497 	    result = InfoLevelCmd(clientData, interp, objc, objv);
   498 	    break;
   499 	case ILibraryIdx:
   500 	    result = InfoLibraryCmd(clientData, interp, objc, objv);
   501 	    break;
   502         case ILoadedIdx:
   503 	    result = InfoLoadedCmd(clientData, interp, objc, objv);
   504 	    break;
   505         case ILocalsIdx:
   506 	    result = InfoLocalsCmd(clientData, interp, objc, objv);
   507 	    break;
   508 	case INameOfExecutableIdx:
   509 	    result = InfoNameOfExecutableCmd(clientData, interp, objc, objv);
   510 	    break;
   511 	case IPatchLevelIdx:
   512 	    result = InfoPatchLevelCmd(clientData, interp, objc, objv);
   513 	    break;
   514         case IProcsIdx:
   515 	    result = InfoProcsCmd(clientData, interp, objc, objv);
   516 	    break;
   517         case IScriptIdx:
   518 	    result = InfoScriptCmd(clientData, interp, objc, objv);
   519 	    break;
   520 	case ISharedLibExtensionIdx:
   521 	    result = InfoSharedlibCmd(clientData, interp, objc, objv);
   522 	    break;
   523 	case ITclVersionIdx:
   524 	    result = InfoTclVersionCmd(clientData, interp, objc, objv);
   525 	    break;
   526 	case IVarsIdx:
   527 	    result = InfoVarsCmd(clientData, interp, objc, objv);
   528 	    break;
   529     }
   530     return result;
   531 }
   532 
   533 /*
   534  *----------------------------------------------------------------------
   535  *
   536  * InfoArgsCmd --
   537  *
   538  *      Called to implement the "info args" command that returns the
   539  *      argument list for a procedure. Handles the following syntax:
   540  *
   541  *          info args procName
   542  *
   543  * Results:
   544  *      Returns TCL_OK if successful and TCL_ERROR if there is an error.
   545  *
   546  * Side effects:
   547  *      Returns a result in the interpreter's result object. If there is
   548  *	an error, the result is an error message.
   549  *
   550  *----------------------------------------------------------------------
   551  */
   552 
   553 static int
   554 InfoArgsCmd(dummy, interp, objc, objv)
   555     ClientData dummy;		/* Not used. */
   556     Tcl_Interp *interp;		/* Current interpreter. */
   557     int objc;			/* Number of arguments. */
   558     Tcl_Obj *CONST objv[];	/* Argument objects. */
   559 {
   560     register Interp *iPtr = (Interp *) interp;
   561     char *name;
   562     Proc *procPtr;
   563     CompiledLocal *localPtr;
   564     Tcl_Obj *listObjPtr;
   565 
   566     if (objc != 3) {
   567         Tcl_WrongNumArgs(interp, 2, objv, "procname");
   568         return TCL_ERROR;
   569     }
   570 
   571     name = Tcl_GetString(objv[2]);
   572     procPtr = TclFindProc(iPtr, name);
   573     if (procPtr == NULL) {
   574         Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
   575                 "\"", name, "\" isn't a procedure", (char *) NULL);
   576         return TCL_ERROR;
   577     }
   578 
   579     /*
   580      * Build a return list containing the arguments.
   581      */
   582     
   583     listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
   584     for (localPtr = procPtr->firstLocalPtr;  localPtr != NULL;
   585             localPtr = localPtr->nextPtr) {
   586         if (TclIsVarArgument(localPtr)) {
   587             Tcl_ListObjAppendElement(interp, listObjPtr,
   588 		    Tcl_NewStringObj(localPtr->name, -1));
   589         }
   590     }
   591     Tcl_SetObjResult(interp, listObjPtr);
   592     return TCL_OK;
   593 }
   594 
   595 /*
   596  *----------------------------------------------------------------------
   597  *
   598  * InfoBodyCmd --
   599  *
   600  *      Called to implement the "info body" command that returns the body
   601  *      for a procedure. Handles the following syntax:
   602  *
   603  *          info body procName
   604  *
   605  * Results:
   606  *      Returns TCL_OK if successful and TCL_ERROR if there is an error.
   607  *
   608  * Side effects:
   609  *      Returns a result in the interpreter's result object. If there is
   610  *	an error, the result is an error message.
   611  *
   612  *----------------------------------------------------------------------
   613  */
   614 
   615 static int
   616 InfoBodyCmd(dummy, interp, objc, objv)
   617     ClientData dummy;		/* Not used. */
   618     Tcl_Interp *interp;		/* Current interpreter. */
   619     int objc;			/* Number of arguments. */
   620     Tcl_Obj *CONST objv[];	/* Argument objects. */
   621 {
   622     register Interp *iPtr = (Interp *) interp;
   623     char *name;
   624     Proc *procPtr;
   625     Tcl_Obj *bodyPtr, *resultPtr;
   626     
   627     if (objc != 3) {
   628         Tcl_WrongNumArgs(interp, 2, objv, "procname");
   629         return TCL_ERROR;
   630     }
   631 
   632     name = Tcl_GetString(objv[2]);
   633     procPtr = TclFindProc(iPtr, name);
   634     if (procPtr == NULL) {
   635         Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
   636 		"\"", name, "\" isn't a procedure", (char *) NULL);
   637         return TCL_ERROR;
   638     }
   639 
   640     /* 
   641      * Here we used to return procPtr->bodyPtr, except when the body was
   642      * bytecompiled - in that case, the return was a copy of the body's
   643      * string rep. In order to better isolate the implementation details
   644      * of the compiler/engine subsystem, we now always return a copy of 
   645      * the string rep. It is important to return a copy so that later 
   646      * manipulations of the object do not invalidate the internal rep.
   647      */
   648 
   649     bodyPtr = procPtr->bodyPtr;
   650     if (bodyPtr->bytes == NULL) {
   651 	/*
   652 	 * The string rep might not be valid if the procedure has
   653 	 * never been run before.  [Bug #545644]
   654 	 */
   655 	(void) Tcl_GetString(bodyPtr);
   656     }
   657     resultPtr = Tcl_NewStringObj(bodyPtr->bytes, bodyPtr->length);
   658     
   659     Tcl_SetObjResult(interp, resultPtr);
   660     return TCL_OK;
   661 }
   662 
   663 /*
   664  *----------------------------------------------------------------------
   665  *
   666  * InfoCmdCountCmd --
   667  *
   668  *      Called to implement the "info cmdcount" command that returns the
   669  *      number of commands that have been executed. Handles the following
   670  *      syntax:
   671  *
   672  *          info cmdcount
   673  *
   674  * Results:
   675  *      Returns TCL_OK if successful and TCL_ERROR if there is an error.
   676  *
   677  * Side effects:
   678  *      Returns a result in the interpreter's result object. If there is
   679  *	an error, the result is an error message.
   680  *
   681  *----------------------------------------------------------------------
   682  */
   683 
   684 static int
   685 InfoCmdCountCmd(dummy, interp, objc, objv)
   686     ClientData dummy;		/* Not used. */
   687     Tcl_Interp *interp;		/* Current interpreter. */
   688     int objc;			/* Number of arguments. */
   689     Tcl_Obj *CONST objv[];	/* Argument objects. */
   690 {
   691     Interp *iPtr = (Interp *) interp;
   692     
   693     if (objc != 2) {
   694         Tcl_WrongNumArgs(interp, 2, objv, NULL);
   695         return TCL_ERROR;
   696     }
   697 
   698     Tcl_SetIntObj(Tcl_GetObjResult(interp), iPtr->cmdCount);
   699     return TCL_OK;
   700 }
   701 
   702 /*
   703  *----------------------------------------------------------------------
   704  *
   705  * InfoCommandsCmd --
   706  *
   707  *	Called to implement the "info commands" command that returns the
   708  *	list of commands in the interpreter that match an optional pattern.
   709  *	The pattern, if any, consists of an optional sequence of namespace
   710  *	names separated by "::" qualifiers, which is followed by a
   711  *	glob-style pattern that restricts which commands are returned.
   712  *	Handles the following syntax:
   713  *
   714  *          info commands ?pattern?
   715  *
   716  * Results:
   717  *      Returns TCL_OK if successful and TCL_ERROR if there is an error.
   718  *
   719  * Side effects:
   720  *      Returns a result in the interpreter's result object. If there is
   721  *	an error, the result is an error message.
   722  *
   723  *----------------------------------------------------------------------
   724  */
   725 
   726 static int
   727 InfoCommandsCmd(dummy, interp, objc, objv)
   728     ClientData dummy;		/* Not used. */
   729     Tcl_Interp *interp;		/* Current interpreter. */
   730     int objc;			/* Number of arguments. */
   731     Tcl_Obj *CONST objv[];	/* Argument objects. */
   732 {
   733     char *cmdName, *pattern;
   734     CONST char *simplePattern;
   735     register Tcl_HashEntry *entryPtr;
   736     Tcl_HashSearch search;
   737     Namespace *nsPtr;
   738     Namespace *globalNsPtr = (Namespace *) Tcl_GetGlobalNamespace(interp);
   739     Namespace *currNsPtr   = (Namespace *) Tcl_GetCurrentNamespace(interp);
   740     Tcl_Obj *listPtr, *elemObjPtr;
   741     int specificNsInPattern = 0;  /* Init. to avoid compiler warning. */
   742     Tcl_Command cmd;
   743 
   744     /*
   745      * Get the pattern and find the "effective namespace" in which to
   746      * list commands.
   747      */
   748 
   749     if (objc == 2) {
   750         simplePattern = NULL;
   751 	nsPtr = currNsPtr;
   752 	specificNsInPattern = 0;
   753     } else if (objc == 3) {
   754 	/*
   755 	 * From the pattern, get the effective namespace and the simple
   756 	 * pattern (no namespace qualifiers or ::'s) at the end. If an
   757 	 * error was found while parsing the pattern, return it. Otherwise,
   758 	 * if the namespace wasn't found, just leave nsPtr NULL: we will
   759 	 * return an empty list since no commands there can be found.
   760 	 */
   761 
   762 	Namespace *dummy1NsPtr, *dummy2NsPtr;
   763 	
   764 
   765 	pattern = Tcl_GetString(objv[2]);
   766 	TclGetNamespaceForQualName(interp, pattern, (Namespace *) NULL,
   767            /*flags*/ 0, &nsPtr, &dummy1NsPtr, &dummy2NsPtr, &simplePattern);
   768 
   769 	if (nsPtr != NULL) {	/* we successfully found the pattern's ns */
   770 	    specificNsInPattern = (strcmp(simplePattern, pattern) != 0);
   771 	}
   772     } else {
   773         Tcl_WrongNumArgs(interp, 2, objv, "?pattern?");
   774         return TCL_ERROR;
   775     }
   776 
   777     /*
   778      * Exit as quickly as possible if we couldn't find the namespace.
   779      */
   780 
   781     if (nsPtr == NULL) {
   782 	return TCL_OK;
   783     }
   784 
   785     /*
   786      * Scan through the effective namespace's command table and create a
   787      * list with all commands that match the pattern. If a specific
   788      * namespace was requested in the pattern, qualify the command names
   789      * with the namespace name.
   790      */
   791 
   792     listPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
   793 
   794     if (simplePattern != NULL && TclMatchIsTrivial(simplePattern)) {
   795 	/*
   796 	 * Special case for when the pattern doesn't include any of
   797 	 * glob's special characters. This lets us avoid scans of any
   798 	 * hash tables.
   799 	 */
   800 	entryPtr = Tcl_FindHashEntry(&nsPtr->cmdTable, simplePattern);
   801 	if (entryPtr != NULL) {
   802 	    if (specificNsInPattern) {
   803 		cmd = (Tcl_Command) Tcl_GetHashValue(entryPtr);
   804 		elemObjPtr = Tcl_NewObj();
   805 		Tcl_GetCommandFullName(interp, cmd, elemObjPtr);
   806 	    } else {
   807 		cmdName = Tcl_GetHashKey(&nsPtr->cmdTable, entryPtr);
   808 		elemObjPtr = Tcl_NewStringObj(cmdName, -1);
   809 	    }
   810 	    Tcl_ListObjAppendElement(interp, listPtr, elemObjPtr);
   811 	} else if ((nsPtr != globalNsPtr) && !specificNsInPattern) {
   812 	    entryPtr = Tcl_FindHashEntry(&globalNsPtr->cmdTable,
   813 		    simplePattern);
   814 	    if (entryPtr != NULL) {
   815 		cmdName = Tcl_GetHashKey(&globalNsPtr->cmdTable, entryPtr);
   816 		Tcl_ListObjAppendElement(interp, listPtr,
   817 			Tcl_NewStringObj(cmdName, -1));
   818 	    }
   819 	}
   820     } else {
   821 	entryPtr = Tcl_FirstHashEntry(&nsPtr->cmdTable, &search);
   822 	while (entryPtr != NULL) {
   823 	    cmdName = Tcl_GetHashKey(&nsPtr->cmdTable, entryPtr);
   824 	    if ((simplePattern == NULL)
   825 	            || Tcl_StringMatch(cmdName, simplePattern)) {
   826 		if (specificNsInPattern) {
   827 		    cmd = (Tcl_Command) Tcl_GetHashValue(entryPtr);
   828 		    elemObjPtr = Tcl_NewObj();
   829 		    Tcl_GetCommandFullName(interp, cmd, elemObjPtr);
   830 		} else {
   831 		    elemObjPtr = Tcl_NewStringObj(cmdName, -1);
   832 		}
   833 		Tcl_ListObjAppendElement(interp, listPtr, elemObjPtr);
   834 	    }
   835 	    entryPtr = Tcl_NextHashEntry(&search);
   836 	}
   837 
   838 	/*
   839 	 * If the effective namespace isn't the global :: namespace, and a
   840 	 * specific namespace wasn't requested in the pattern, then add in
   841 	 * all global :: commands that match the simple pattern. Of course,
   842 	 * we add in only those commands that aren't hidden by a command in
   843 	 * the effective namespace.
   844 	 */
   845 	
   846 	if ((nsPtr != globalNsPtr) && !specificNsInPattern) {
   847 	    entryPtr = Tcl_FirstHashEntry(&globalNsPtr->cmdTable, &search);
   848 	    while (entryPtr != NULL) {
   849 		cmdName = Tcl_GetHashKey(&globalNsPtr->cmdTable, entryPtr);
   850 		if ((simplePattern == NULL)
   851 	                || Tcl_StringMatch(cmdName, simplePattern)) {
   852 		    if (Tcl_FindHashEntry(&nsPtr->cmdTable, cmdName) == NULL) {
   853 			Tcl_ListObjAppendElement(interp, listPtr,
   854 				Tcl_NewStringObj(cmdName, -1));
   855 		    }
   856 		}
   857 		entryPtr = Tcl_NextHashEntry(&search);
   858 	    }
   859 	}
   860     }
   861     
   862     Tcl_SetObjResult(interp, listPtr);
   863     return TCL_OK;
   864 }
   865 
   866 /*
   867  *----------------------------------------------------------------------
   868  *
   869  * InfoCompleteCmd --
   870  *
   871  *      Called to implement the "info complete" command that determines
   872  *      whether a string is a complete Tcl command. Handles the following
   873  *      syntax:
   874  *
   875  *          info complete command
   876  *
   877  * Results:
   878  *      Returns TCL_OK if successful and TCL_ERROR if there is an error.
   879  *
   880  * Side effects:
   881  *      Returns a result in the interpreter's result object. If there is
   882  *	an error, the result is an error message.
   883  *
   884  *----------------------------------------------------------------------
   885  */
   886 
   887 static int
   888 InfoCompleteCmd(dummy, interp, objc, objv)
   889     ClientData dummy;		/* Not used. */
   890     Tcl_Interp *interp;		/* Current interpreter. */
   891     int objc;			/* Number of arguments. */
   892     Tcl_Obj *CONST objv[];	/* Argument objects. */
   893 {
   894     if (objc != 3) {
   895         Tcl_WrongNumArgs(interp, 2, objv, "command");
   896         return TCL_ERROR;
   897     }
   898 
   899     if (TclObjCommandComplete(objv[2])) {
   900 	Tcl_SetIntObj(Tcl_GetObjResult(interp), 1);
   901     } else {
   902 	Tcl_SetIntObj(Tcl_GetObjResult(interp), 0);
   903     }
   904 
   905     return TCL_OK;
   906 }
   907 
   908 /*
   909  *----------------------------------------------------------------------
   910  *
   911  * InfoDefaultCmd --
   912  *
   913  *      Called to implement the "info default" command that returns the
   914  *      default value for a procedure argument. Handles the following
   915  *      syntax:
   916  *
   917  *          info default procName arg varName
   918  *
   919  * Results:
   920  *      Returns TCL_OK if successful and TCL_ERROR if there is an error.
   921  *
   922  * Side effects:
   923  *      Returns a result in the interpreter's result object. If there is
   924  *	an error, the result is an error message.
   925  *
   926  *----------------------------------------------------------------------
   927  */
   928 
   929 static int
   930 InfoDefaultCmd(dummy, interp, objc, objv)
   931     ClientData dummy;		/* Not used. */
   932     Tcl_Interp *interp;		/* Current interpreter. */
   933     int objc;			/* Number of arguments. */
   934     Tcl_Obj *CONST objv[];	/* Argument objects. */
   935 {
   936     Interp *iPtr = (Interp *) interp;
   937     char *procName, *argName, *varName;
   938     Proc *procPtr;
   939     CompiledLocal *localPtr;
   940     Tcl_Obj *valueObjPtr;
   941 
   942     if (objc != 5) {
   943         Tcl_WrongNumArgs(interp, 2, objv, "procname arg varname");
   944         return TCL_ERROR;
   945     }
   946 
   947     procName = Tcl_GetString(objv[2]);
   948     argName = Tcl_GetString(objv[3]);
   949 
   950     procPtr = TclFindProc(iPtr, procName);
   951     if (procPtr == NULL) {
   952 	Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
   953 		"\"", procName, "\" isn't a procedure", (char *) NULL);
   954         return TCL_ERROR;
   955     }
   956 
   957     for (localPtr = procPtr->firstLocalPtr;  localPtr != NULL;
   958             localPtr = localPtr->nextPtr) {
   959         if (TclIsVarArgument(localPtr)
   960 		&& (strcmp(argName, localPtr->name) == 0)) {
   961             if (localPtr->defValuePtr != NULL) {
   962 		valueObjPtr = Tcl_ObjSetVar2(interp, objv[4], NULL,
   963 			localPtr->defValuePtr, 0);
   964                 if (valueObjPtr == NULL) {
   965                     defStoreError:
   966 		    varName = Tcl_GetString(objv[4]);
   967 		    Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
   968 	                    "couldn't store default value in variable \"",
   969 			    varName, "\"", (char *) NULL);
   970                     return TCL_ERROR;
   971                 }
   972 		Tcl_SetIntObj(Tcl_GetObjResult(interp), 1);
   973             } else {
   974                 Tcl_Obj *nullObjPtr = Tcl_NewObj();
   975 		Tcl_IncrRefCount(nullObjPtr);
   976                 valueObjPtr = Tcl_ObjSetVar2(interp, objv[4], NULL,
   977 			nullObjPtr, 0);
   978 		Tcl_DecrRefCount(nullObjPtr); /* free unneeded obj */
   979                 if (valueObjPtr == NULL) {
   980                     goto defStoreError;
   981                 }
   982 		Tcl_SetIntObj(Tcl_GetObjResult(interp), 0);
   983             }
   984             return TCL_OK;
   985         }
   986     }
   987 
   988     Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
   989 	    "procedure \"", procName, "\" doesn't have an argument \"",
   990 	    argName, "\"", (char *) NULL);
   991     return TCL_ERROR;
   992 }
   993 
   994 /*
   995  *----------------------------------------------------------------------
   996  *
   997  * InfoExistsCmd --
   998  *
   999  *      Called to implement the "info exists" command that determines
  1000  *      whether a variable exists. Handles the following syntax:
  1001  *
  1002  *          info exists varName
  1003  *
  1004  * Results:
  1005  *      Returns TCL_OK if successful and TCL_ERROR if there is an error.
  1006  *
  1007  * Side effects:
  1008  *      Returns a result in the interpreter's result object. If there is
  1009  *	an error, the result is an error message.
  1010  *
  1011  *----------------------------------------------------------------------
  1012  */
  1013 
  1014 static int
  1015 InfoExistsCmd(dummy, interp, objc, objv)
  1016     ClientData dummy;		/* Not used. */
  1017     Tcl_Interp *interp;		/* Current interpreter. */
  1018     int objc;			/* Number of arguments. */
  1019     Tcl_Obj *CONST objv[];	/* Argument objects. */
  1020 {
  1021     char *varName;
  1022     Var *varPtr;
  1023 
  1024     if (objc != 3) {
  1025         Tcl_WrongNumArgs(interp, 2, objv, "varName");
  1026         return TCL_ERROR;
  1027     }
  1028 
  1029     varName = Tcl_GetString(objv[2]);
  1030     varPtr = TclVarTraceExists(interp, varName);
  1031     if ((varPtr != NULL) && !TclIsVarUndefined(varPtr)) {
  1032         Tcl_SetIntObj(Tcl_GetObjResult(interp), 1);
  1033     } else {
  1034         Tcl_SetIntObj(Tcl_GetObjResult(interp), 0);
  1035     }
  1036     return TCL_OK;
  1037 }
  1038 
  1039 #ifdef TCL_TIP280
  1040 /*
  1041  *----------------------------------------------------------------------
  1042  *
  1043  * InfoFrameCmd --
  1044  *	TIP #280
  1045  *
  1046  *      Called to implement the "info frame" command that returns the
  1047  *      location of either the currently executing command, or its caller.
  1048  *      Handles the following syntax:
  1049  *
  1050  *          info frame ?number?
  1051  *
  1052  * Results:
  1053  *      Returns TCL_OK if successful and TCL_ERROR if there is an error.
  1054  *
  1055  * Side effects:
  1056  *      Returns a result in the interpreter's result object. If there is
  1057  *	an error, the result is an error message.
  1058  *
  1059  *----------------------------------------------------------------------
  1060  */
  1061 
  1062 static int
  1063 InfoFrameCmd(dummy, interp, objc, objv)
  1064      ClientData dummy;		/* Not used. */
  1065      Tcl_Interp *interp;		/* Current interpreter. */
  1066      int objc;			/* Number of arguments. */
  1067      Tcl_Obj *CONST objv[];	/* Argument objects. */
  1068 {
  1069     Interp *iPtr = (Interp *) interp;
  1070 
  1071     if (objc == 2) {
  1072 	/* just "info frame" */
  1073         int levels = (iPtr->cmdFramePtr == NULL
  1074 		      ? 0
  1075 		      : iPtr->cmdFramePtr->level);
  1076 
  1077         Tcl_SetIntObj(Tcl_GetObjResult(interp), levels);
  1078         return TCL_OK;
  1079 
  1080     } else if (objc == 3) {
  1081 	/* "info frame level" */
  1082         int       level;
  1083 	CmdFrame *framePtr;
  1084 
  1085         if (Tcl_GetIntFromObj(interp, objv[2], &level) != TCL_OK) {
  1086             return TCL_ERROR;
  1087         }
  1088         if (level <= 0) {
  1089 	    /* Relative adressing */
  1090 
  1091             if (iPtr->cmdFramePtr == NULL) {
  1092                 levelError:
  1093 		Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
  1094 			"bad level \"",
  1095 			Tcl_GetString(objv[2]),
  1096 			"\"", (char *) NULL);
  1097                 return TCL_ERROR;
  1098             }
  1099             /* Convert to absolute. */
  1100 
  1101             level += iPtr->cmdFramePtr->level;
  1102         }
  1103         for (framePtr = iPtr->cmdFramePtr;
  1104 	     framePtr != NULL;
  1105 	     framePtr = framePtr->nextPtr) {
  1106 
  1107 	    if (framePtr->level == level) {
  1108                 break;
  1109             }
  1110         }
  1111         if (framePtr == NULL) {
  1112             goto levelError;
  1113         }
  1114 
  1115 	/*
  1116 	 * Pull the information and construct the dictionary to return, as
  1117 	 * list. Regarding use of the CmdFrame fields see tclInt.h, and its
  1118 	 * definition.
  1119 	 */
  1120 
  1121 	{
  1122 	    Tcl_Obj* lv [20]; /* Keep uptodate when more keys are added to the dict */
  1123 	    int      lc = 0;
  1124 
  1125 	    /* This array is indexed by the TCL_LOCATION_... values, except
  1126 	     * for _LAST.
  1127 	     */
  1128 
  1129 	    static CONST char* typeString [TCL_LOCATION_LAST] = {
  1130 	       "eval", "eval", "eval", "precompiled", "source", "proc"
  1131 	    };
  1132 
  1133 	    switch (framePtr->type) {
  1134 	    case TCL_LOCATION_EVAL:
  1135 	        /* Evaluation, dynamic script. Type, line, cmd, the latter
  1136 		 * through str. */
  1137 
  1138 	        lv [lc ++] = Tcl_NewStringObj ("type",-1);
  1139 		lv [lc ++] = Tcl_NewStringObj (typeString [framePtr->type],-1);
  1140 		lv [lc ++] = Tcl_NewStringObj ("line",-1);
  1141 		lv [lc ++] = Tcl_NewIntObj    (framePtr->line[0]);
  1142 		lv [lc ++] = Tcl_NewStringObj ("cmd",-1);
  1143 		lv [lc ++] = Tcl_NewStringObj (framePtr->cmd.str.cmd,
  1144 					       framePtr->cmd.str.len);
  1145 		break;
  1146 
  1147 	    case TCL_LOCATION_EVAL_LIST:
  1148 	        /* List optimized evaluation. Type, line, cmd, the latter
  1149 		 * through listPtr, possibly a frame. */
  1150 
  1151 	        lv [lc ++] = Tcl_NewStringObj ("type",-1);
  1152 		lv [lc ++] = Tcl_NewStringObj (typeString [framePtr->type],-1);
  1153 		lv [lc ++] = Tcl_NewStringObj ("line",-1);
  1154 		lv [lc ++] = Tcl_NewIntObj    (framePtr->line[0]);
  1155 
  1156 		/* We put a duplicate of the command list obj into the result
  1157 		 * to ensure that the 'pure List'-property of the command
  1158 		 * itself is not destroyed. Otherwise the query here would
  1159 		 * disable the list optimization path in Tcl_EvalObjEx.
  1160 		 */
  1161 
  1162 		lv [lc ++] =  Tcl_NewStringObj ("cmd",-1);
  1163 		lv [lc ++] =  Tcl_DuplicateObj (framePtr->cmd.listPtr);
  1164 		break;
  1165 
  1166 	    case TCL_LOCATION_PREBC:
  1167 	        /* Precompiled. Result contains the type as signal, nothing
  1168 		 * else */
  1169 
  1170 	        lv [lc ++] = Tcl_NewStringObj ("type",-1);
  1171 		lv [lc ++] = Tcl_NewStringObj (typeString [framePtr->type],-1);
  1172 		break;
  1173 
  1174 	    case TCL_LOCATION_BC: {
  1175 	        /* Execution of bytecode. Talk to the BC engine to fill out
  1176 		 * the frame. */
  1177 
  1178 	        CmdFrame f       =  *framePtr;
  1179 	        Proc*    procPtr = f.framePtr ? f.framePtr->procPtr : NULL;
  1180 
  1181 		/* Note: Type BC => f.data.eval.path    is not used.
  1182 		 *                  f.data.tebc.codePtr is used instead.
  1183 		 */
  1184 
  1185 	        TclGetSrcInfoForPc (&f);
  1186 		/* Now filled:        cmd.str.(cmd,len), line */
  1187 		/* Possibly modified: type, path! */
  1188 
  1189 	        lv [lc ++] = Tcl_NewStringObj ("type",-1);
  1190 		lv [lc ++] = Tcl_NewStringObj (typeString [f.type],-1);
  1191 		lv [lc ++] = Tcl_NewStringObj ("line",-1);
  1192 		lv [lc ++] = Tcl_NewIntObj    (f.line[0]);
  1193 
  1194 		if (f.type == TCL_LOCATION_SOURCE) {
  1195 		    lv [lc ++] = Tcl_NewStringObj ("file",-1);
  1196 		    lv [lc ++] = f.data.eval.path;
  1197 		    /* Death of reference by TclGetSrcInfoForPc */
  1198 		    Tcl_DecrRefCount (f.data.eval.path);
  1199 		}
  1200 
  1201 		lv [lc ++] = Tcl_NewStringObj ("cmd",-1);
  1202 		lv [lc ++] = Tcl_NewStringObj (f.cmd.str.cmd, f.cmd.str.len);
  1203 
  1204 		if (procPtr != NULL) {
  1205 		    Tcl_HashEntry* namePtr  = procPtr->cmdPtr->hPtr;
  1206 		    char*          procName = Tcl_GetHashKey (namePtr->tablePtr, namePtr);
  1207 		    char*          nsName   = procPtr->cmdPtr->nsPtr->fullName;
  1208 
  1209 		    lv [lc ++] = Tcl_NewStringObj ("proc",-1);
  1210 		    lv [lc ++] = Tcl_NewStringObj (nsName,-1);
  1211 
  1212 		    if (strcmp (nsName, "::") != 0) {
  1213 		        Tcl_AppendToObj (lv [lc-1], "::", -1);
  1214 		    }
  1215 		    Tcl_AppendToObj (lv [lc-1], procName, -1);
  1216 		}
  1217 	        break;
  1218 	    }
  1219 
  1220 	    case TCL_LOCATION_SOURCE:
  1221 	        /* Evaluation of a script file */
  1222 
  1223 	        lv [lc ++] = Tcl_NewStringObj ("type",-1);
  1224 		lv [lc ++] = Tcl_NewStringObj (typeString [framePtr->type],-1);
  1225 		lv [lc ++] = Tcl_NewStringObj ("line",-1);
  1226 		lv [lc ++] = Tcl_NewIntObj    (framePtr->line[0]);
  1227 		lv [lc ++] = Tcl_NewStringObj ("file",-1);
  1228 		lv [lc ++] = framePtr->data.eval.path;
  1229 		/* Refcount framePtr->data.eval.path goes up when lv
  1230 		 * is converted into the result list object.
  1231 		 */
  1232 		lv [lc ++] = Tcl_NewStringObj ("cmd",-1);
  1233 		lv [lc ++] = Tcl_NewStringObj (framePtr->cmd.str.cmd,
  1234 					       framePtr->cmd.str.len);
  1235 		break;
  1236 
  1237 	    case TCL_LOCATION_PROC:
  1238 		Tcl_Panic ("TCL_LOCATION_PROC found in standard frame");
  1239 		break;
  1240 	    }
  1241 
  1242 
  1243 	    /* 'level'. Common to all frame types. Conditional on having an
  1244 	     * associated _visible_ CallFrame */
  1245 
  1246 	    if ((framePtr->framePtr != NULL) && (iPtr->varFramePtr != NULL)) {
  1247 	        CallFrame* current = framePtr->framePtr;
  1248 		CallFrame* top     = iPtr->varFramePtr;
  1249 		CallFrame* idx;
  1250 
  1251 		for (idx = top;
  1252 		     idx != NULL;
  1253 		     idx = idx->callerVarPtr) {
  1254 		    if (idx == current) {
  1255 		        int c = framePtr->framePtr->level;
  1256 			int t = iPtr->varFramePtr->level;
  1257 
  1258 			lv [lc ++] = Tcl_NewStringObj ("level",-1);
  1259 			lv [lc ++] = Tcl_NewIntObj (t - c);
  1260 			break;
  1261 		    }
  1262 		}
  1263 	    }
  1264 
  1265 	    Tcl_SetObjResult(interp, Tcl_NewListObj (lc, lv));
  1266 	    return TCL_OK;
  1267 	}
  1268     }
  1269 
  1270     Tcl_WrongNumArgs(interp, 2, objv, "?number?");
  1271 
  1272     return TCL_ERROR;
  1273 }
  1274 #endif
  1275 
  1276 /*
  1277  *----------------------------------------------------------------------
  1278  *
  1279  * InfoFunctionsCmd --
  1280  *
  1281  *      Called to implement the "info functions" command that returns the
  1282  *      list of math functions matching an optional pattern. Handles the
  1283  *      following syntax:
  1284  *
  1285  *          info functions ?pattern?
  1286  *
  1287  * Results:
  1288  *      Returns TCL_OK if successful and TCL_ERROR if there is an error.
  1289  *
  1290  * Side effects:
  1291  *      Returns a result in the interpreter's result object. If there is
  1292  *	an error, the result is an error message.
  1293  *
  1294  *----------------------------------------------------------------------
  1295  */
  1296 
  1297 static int
  1298 InfoFunctionsCmd(dummy, interp, objc, objv)
  1299     ClientData dummy;		/* Not used. */
  1300     Tcl_Interp *interp;		/* Current interpreter. */
  1301     int objc;			/* Number of arguments. */
  1302     Tcl_Obj *CONST objv[];	/* Argument objects. */
  1303 {
  1304     char *pattern;
  1305     Tcl_Obj *listPtr;
  1306 
  1307     if (objc == 2) {
  1308         pattern = NULL;
  1309     } else if (objc == 3) {
  1310         pattern = Tcl_GetString(objv[2]);
  1311     } else {
  1312         Tcl_WrongNumArgs(interp, 2, objv, "?pattern?");
  1313         return TCL_ERROR;
  1314     }
  1315 
  1316     listPtr = Tcl_ListMathFuncs(interp, pattern);
  1317     if (listPtr == NULL) {
  1318 	return TCL_ERROR;
  1319     }
  1320     Tcl_SetObjResult(interp, listPtr);
  1321     return TCL_OK;
  1322 }
  1323 
  1324 /*
  1325  *----------------------------------------------------------------------
  1326  *
  1327  * InfoGlobalsCmd --
  1328  *
  1329  *      Called to implement the "info globals" command that returns the list
  1330  *      of global variables matching an optional pattern. Handles the
  1331  *      following syntax:
  1332  *
  1333  *          info globals ?pattern?
  1334  *
  1335  * Results:
  1336  *      Returns TCL_OK if successful and TCL_ERROR if there is an error.
  1337  *
  1338  * Side effects:
  1339  *      Returns a result in the interpreter's result object. If there is
  1340  *	an error, the result is an error message.
  1341  *
  1342  *----------------------------------------------------------------------
  1343  */
  1344 
  1345 static int
  1346 InfoGlobalsCmd(dummy, interp, objc, objv)
  1347     ClientData dummy;		/* Not used. */
  1348     Tcl_Interp *interp;		/* Current interpreter. */
  1349     int objc;			/* Number of arguments. */
  1350     Tcl_Obj *CONST objv[];	/* Argument objects. */
  1351 {
  1352     char *varName, *pattern;
  1353     Namespace *globalNsPtr = (Namespace *) Tcl_GetGlobalNamespace(interp);
  1354     register Tcl_HashEntry *entryPtr;
  1355     Tcl_HashSearch search;
  1356     Var *varPtr;
  1357     Tcl_Obj *listPtr;
  1358 
  1359     if (objc == 2) {
  1360         pattern = NULL;
  1361     } else if (objc == 3) {
  1362 	pattern = Tcl_GetString(objv[2]);
  1363 	/*
  1364 	 * Strip leading global-namespace qualifiers. [Bug 1057461]
  1365 	 */
  1366 	if (pattern[0] == ':' && pattern[1] == ':') {
  1367 	    while (*pattern == ':') {
  1368 		pattern++;
  1369 	    }
  1370 	}
  1371     } else {
  1372         Tcl_WrongNumArgs(interp, 2, objv, "?pattern?");
  1373         return TCL_ERROR;
  1374     }
  1375 
  1376     /*
  1377      * Scan through the global :: namespace's variable table and create a
  1378      * list of all global variables that match the pattern.
  1379      */
  1380     
  1381     listPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
  1382     if (pattern != NULL && TclMatchIsTrivial(pattern)) {
  1383 	entryPtr = Tcl_FindHashEntry(&globalNsPtr->varTable, pattern);
  1384  	if (entryPtr != NULL) {
  1385 	    varPtr = (Var *) Tcl_GetHashValue(entryPtr);
  1386 	    if (!TclIsVarUndefined(varPtr)) {
  1387 		Tcl_ListObjAppendElement(interp, listPtr,
  1388 			Tcl_NewStringObj(pattern, -1));
  1389 	    }
  1390 	}
  1391     } else {
  1392 	for (entryPtr = Tcl_FirstHashEntry(&globalNsPtr->varTable, &search);
  1393 		entryPtr != NULL;
  1394 		entryPtr = Tcl_NextHashEntry(&search)) {
  1395 	    varPtr = (Var *) Tcl_GetHashValue(entryPtr);
  1396 	    if (TclIsVarUndefined(varPtr)) {
  1397 		continue;
  1398 	    }
  1399 	    varName = Tcl_GetHashKey(&globalNsPtr->varTable, entryPtr);
  1400 	    if ((pattern == NULL) || Tcl_StringMatch(varName, pattern)) {
  1401 		Tcl_ListObjAppendElement(interp, listPtr,
  1402 			Tcl_NewStringObj(varName, -1));
  1403 	    }
  1404 	}
  1405     }
  1406     Tcl_SetObjResult(interp, listPtr);
  1407     return TCL_OK;
  1408 }
  1409 
  1410 /*
  1411  *----------------------------------------------------------------------
  1412  *
  1413  * InfoHostnameCmd --
  1414  *
  1415  *      Called to implement the "info hostname" command that returns the
  1416  *      host name. Handles the following syntax:
  1417  *
  1418  *          info hostname
  1419  *
  1420  * Results:
  1421  *      Returns TCL_OK if successful and TCL_ERROR if there is an error.
  1422  *
  1423  * Side effects:
  1424  *      Returns a result in the interpreter's result object. If there is
  1425  *	an error, the result is an error message.
  1426  *
  1427  *----------------------------------------------------------------------
  1428  */
  1429 
  1430 static int
  1431 InfoHostnameCmd(dummy, interp, objc, objv)
  1432     ClientData dummy;		/* Not used. */
  1433     Tcl_Interp *interp;		/* Current interpreter. */
  1434     int objc;			/* Number of arguments. */
  1435     Tcl_Obj *CONST objv[];	/* Argument objects. */
  1436 {
  1437     CONST char *name;
  1438     if (objc != 2) {
  1439         Tcl_WrongNumArgs(interp, 2, objv, NULL);
  1440         return TCL_ERROR;
  1441     }
  1442 
  1443     name = Tcl_GetHostName();
  1444     if (name) {
  1445 	Tcl_SetStringObj(Tcl_GetObjResult(interp), name, -1);
  1446 	return TCL_OK;
  1447     } else {
  1448 	Tcl_SetStringObj(Tcl_GetObjResult(interp),
  1449 		"unable to determine name of host", -1);
  1450 	return TCL_ERROR;
  1451     }
  1452 }
  1453 
  1454 /*
  1455  *----------------------------------------------------------------------
  1456  *
  1457  * InfoLevelCmd --
  1458  *
  1459  *      Called to implement the "info level" command that returns
  1460  *      information about the call stack. Handles the following syntax:
  1461  *
  1462  *          info level ?number?
  1463  *
  1464  * Results:
  1465  *      Returns TCL_OK if successful and TCL_ERROR if there is an error.
  1466  *
  1467  * Side effects:
  1468  *      Returns a result in the interpreter's result object. If there is
  1469  *	an error, the result is an error message.
  1470  *
  1471  *----------------------------------------------------------------------
  1472  */
  1473 
  1474 static int
  1475 InfoLevelCmd(dummy, interp, objc, objv)
  1476     ClientData dummy;		/* Not used. */
  1477     Tcl_Interp *interp;		/* Current interpreter. */
  1478     int objc;			/* Number of arguments. */
  1479     Tcl_Obj *CONST objv[];	/* Argument objects. */
  1480 {
  1481     Interp *iPtr = (Interp *) interp;
  1482     int level;
  1483     CallFrame *framePtr;
  1484     Tcl_Obj *listPtr;
  1485 
  1486     if (objc == 2) {		/* just "info level" */
  1487         if (iPtr->varFramePtr == NULL) {
  1488             Tcl_SetIntObj(Tcl_GetObjResult(interp), 0);
  1489         } else {
  1490             Tcl_SetIntObj(Tcl_GetObjResult(interp), iPtr->varFramePtr->level);
  1491         }
  1492         return TCL_OK;
  1493     } else if (objc == 3) {
  1494         if (Tcl_GetIntFromObj(interp, objv[2], &level) != TCL_OK) {
  1495             return TCL_ERROR;
  1496         }
  1497         if (level <= 0) {
  1498             if (iPtr->varFramePtr == NULL) {
  1499                 levelError:
  1500 		Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
  1501 			"bad level \"",
  1502 			Tcl_GetString(objv[2]),
  1503 			"\"", (char *) NULL);
  1504                 return TCL_ERROR;
  1505             }
  1506             level += iPtr->varFramePtr->level;
  1507         }
  1508         for (framePtr = iPtr->varFramePtr;  framePtr != NULL;
  1509                 framePtr = framePtr->callerVarPtr) {
  1510             if (framePtr->level == level) {
  1511                 break;
  1512             }
  1513         }
  1514         if (framePtr == NULL) {
  1515             goto levelError;
  1516         }
  1517 
  1518         listPtr = Tcl_NewListObj(framePtr->objc, framePtr->objv);
  1519         Tcl_SetObjResult(interp, listPtr);
  1520         return TCL_OK;
  1521     }
  1522 
  1523     Tcl_WrongNumArgs(interp, 2, objv, "?number?");
  1524     return TCL_ERROR;
  1525 }
  1526 
  1527 /*
  1528  *----------------------------------------------------------------------
  1529  *
  1530  * InfoLibraryCmd --
  1531  *
  1532  *      Called to implement the "info library" command that returns the
  1533  *      library directory for the Tcl installation. Handles the following
  1534  *      syntax:
  1535  *
  1536  *          info library
  1537  *
  1538  * Results:
  1539  *      Returns TCL_OK if successful and TCL_ERROR if there is an error.
  1540  *
  1541  * Side effects:
  1542  *      Returns a result in the interpreter's result object. If there is
  1543  *	an error, the result is an error message.
  1544  *
  1545  *----------------------------------------------------------------------
  1546  */
  1547 
  1548 static int
  1549 InfoLibraryCmd(dummy, interp, objc, objv)
  1550     ClientData dummy;		/* Not used. */
  1551     Tcl_Interp *interp;		/* Current interpreter. */
  1552     int objc;			/* Number of arguments. */
  1553     Tcl_Obj *CONST objv[];	/* Argument objects. */
  1554 {
  1555     CONST char *libDirName;
  1556 
  1557     if (objc != 2) {
  1558         Tcl_WrongNumArgs(interp, 2, objv, NULL);
  1559         return TCL_ERROR;
  1560     }
  1561 
  1562     libDirName = Tcl_GetVar(interp, "tcl_library", TCL_GLOBAL_ONLY);
  1563     if (libDirName != NULL) {
  1564         Tcl_SetStringObj(Tcl_GetObjResult(interp), libDirName, -1);
  1565         return TCL_OK;
  1566     }
  1567     Tcl_SetStringObj(Tcl_GetObjResult(interp), 
  1568             "no library has been specified for Tcl", -1);
  1569     return TCL_ERROR;
  1570 }
  1571 
  1572 /*
  1573  *----------------------------------------------------------------------
  1574  *
  1575  * InfoLoadedCmd --
  1576  *
  1577  *      Called to implement the "info loaded" command that returns the
  1578  *      packages that have been loaded into an interpreter. Handles the
  1579  *      following syntax:
  1580  *
  1581  *          info loaded ?interp?
  1582  *
  1583  * Results:
  1584  *      Returns TCL_OK if successful and TCL_ERROR if there is an error.
  1585  *
  1586  * Side effects:
  1587  *      Returns a result in the interpreter's result object. If there is
  1588  *	an error, the result is an error message.
  1589  *
  1590  *----------------------------------------------------------------------
  1591  */
  1592 
  1593 static int
  1594 InfoLoadedCmd(dummy, interp, objc, objv)
  1595     ClientData dummy;		/* Not used. */
  1596     Tcl_Interp *interp;		/* Current interpreter. */
  1597     int objc;			/* Number of arguments. */
  1598     Tcl_Obj *CONST objv[];	/* Argument objects. */
  1599 {
  1600     char *interpName;
  1601     int result;
  1602 
  1603     if ((objc != 2) && (objc != 3)) {
  1604         Tcl_WrongNumArgs(interp, 2, objv, "?interp?");
  1605         return TCL_ERROR;
  1606     }
  1607 
  1608     if (objc == 2) {		/* get loaded pkgs in all interpreters */
  1609 	interpName = NULL;
  1610     } else {			/* get pkgs just in specified interp */
  1611 	interpName = Tcl_GetString(objv[2]);
  1612     }
  1613     result = TclGetLoadedPackages(interp, interpName);
  1614     return result;
  1615 }
  1616 
  1617 /*
  1618  *----------------------------------------------------------------------
  1619  *
  1620  * InfoLocalsCmd --
  1621  *
  1622  *      Called to implement the "info locals" command to return a list of
  1623  *      local variables that match an optional pattern. Handles the
  1624  *      following syntax:
  1625  *
  1626  *          info locals ?pattern?
  1627  *
  1628  * Results:
  1629  *      Returns TCL_OK if successful and TCL_ERROR if there is an error.
  1630  *
  1631  * Side effects:
  1632  *      Returns a result in the interpreter's result object. If there is
  1633  *	an error, the result is an error message.
  1634  *
  1635  *----------------------------------------------------------------------
  1636  */
  1637 
  1638 static int
  1639 InfoLocalsCmd(dummy, interp, objc, objv)
  1640     ClientData dummy;		/* Not used. */
  1641     Tcl_Interp *interp;		/* Current interpreter. */
  1642     int objc;			/* Number of arguments. */
  1643     Tcl_Obj *CONST objv[];	/* Argument objects. */
  1644 {
  1645     Interp *iPtr = (Interp *) interp;
  1646     char *pattern;
  1647     Tcl_Obj *listPtr;
  1648 
  1649     if (objc == 2) {
  1650         pattern = NULL;
  1651     } else if (objc == 3) {
  1652         pattern = Tcl_GetString(objv[2]);
  1653     } else {
  1654         Tcl_WrongNumArgs(interp, 2, objv, "?pattern?");
  1655         return TCL_ERROR;
  1656     }
  1657     
  1658     if (iPtr->varFramePtr == NULL || !iPtr->varFramePtr->isProcCallFrame) {
  1659         return TCL_OK;
  1660     }
  1661 
  1662     /*
  1663      * Return a list containing names of first the compiled locals (i.e. the
  1664      * ones stored in the call frame), then the variables in the local hash
  1665      * table (if one exists).
  1666      */
  1667     
  1668     listPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
  1669     AppendLocals(interp, listPtr, pattern, 0);
  1670     Tcl_SetObjResult(interp, listPtr);
  1671     return TCL_OK;
  1672 }
  1673 
  1674 /*
  1675  *----------------------------------------------------------------------
  1676  *
  1677  * AppendLocals --
  1678  *
  1679  *	Append the local variables for the current frame to the
  1680  *	specified list object.
  1681  *
  1682  * Results:
  1683  *	None.
  1684  *
  1685  * Side effects:
  1686  *	None.
  1687  *
  1688  *----------------------------------------------------------------------
  1689  */
  1690 
  1691 static void
  1692 AppendLocals(interp, listPtr, pattern, includeLinks)
  1693     Tcl_Interp *interp;		/* Current interpreter. */
  1694     Tcl_Obj *listPtr;		/* List object to append names to. */
  1695     CONST char *pattern;	/* Pattern to match against. */
  1696     int includeLinks;		/* 1 if upvars should be included, else 0. */
  1697 {
  1698     Interp *iPtr = (Interp *) interp;
  1699     CompiledLocal *localPtr;
  1700     Var *varPtr;
  1701     int i, localVarCt;
  1702     char *varName;
  1703     Tcl_HashTable *localVarTablePtr;
  1704     register Tcl_HashEntry *entryPtr;
  1705     Tcl_HashSearch search;
  1706 
  1707     localPtr = iPtr->varFramePtr->procPtr->firstLocalPtr;
  1708     localVarCt = iPtr->varFramePtr->numCompiledLocals;
  1709     varPtr = iPtr->varFramePtr->compiledLocals;
  1710     localVarTablePtr = iPtr->varFramePtr->varTablePtr;
  1711 
  1712     for (i = 0; i < localVarCt; i++) {
  1713 	/*
  1714 	 * Skip nameless (temporary) variables and undefined variables
  1715 	 */
  1716 
  1717 	if (!TclIsVarTemporary(localPtr) && !TclIsVarUndefined(varPtr)
  1718 	        && (includeLinks || !TclIsVarLink(varPtr))) {
  1719 	    varName = varPtr->name;
  1720 	    if ((pattern == NULL) || Tcl_StringMatch(varName, pattern)) {
  1721 		Tcl_ListObjAppendElement(interp, listPtr,
  1722 		        Tcl_NewStringObj(varName, -1));
  1723 	    }
  1724         }
  1725 	varPtr++;
  1726 	localPtr = localPtr->nextPtr;
  1727     }
  1728     
  1729     if (localVarTablePtr != NULL) {
  1730 	for (entryPtr = Tcl_FirstHashEntry(localVarTablePtr, &search);
  1731 	        entryPtr != NULL;
  1732                 entryPtr = Tcl_NextHashEntry(&search)) {
  1733 	    varPtr = (Var *) Tcl_GetHashValue(entryPtr);
  1734 	    if (!TclIsVarUndefined(varPtr)
  1735 		    && (includeLinks || !TclIsVarLink(varPtr))) {
  1736 		varName = Tcl_GetHashKey(localVarTablePtr, entryPtr);
  1737 		if ((pattern == NULL)
  1738 		        || Tcl_StringMatch(varName, pattern)) {
  1739 		    Tcl_ListObjAppendElement(interp, listPtr,
  1740 			    Tcl_NewStringObj(varName, -1));
  1741 		}
  1742 	    }
  1743 	}
  1744     }
  1745 }
  1746 
  1747 /*
  1748  *----------------------------------------------------------------------
  1749  *
  1750  * InfoNameOfExecutableCmd --
  1751  *
  1752  *      Called to implement the "info nameofexecutable" command that returns
  1753  *      the name of the binary file running this application. Handles the
  1754  *      following syntax:
  1755  *
  1756  *          info nameofexecutable
  1757  *
  1758  * Results:
  1759  *      Returns TCL_OK if successful and TCL_ERROR if there is an error.
  1760  *
  1761  * Side effects:
  1762  *      Returns a result in the interpreter's result object. If there is
  1763  *	an error, the result is an error message.
  1764  *
  1765  *----------------------------------------------------------------------
  1766  */
  1767 
  1768 static int
  1769 InfoNameOfExecutableCmd(dummy, interp, objc, objv)
  1770     ClientData dummy;		/* Not used. */
  1771     Tcl_Interp *interp;		/* Current interpreter. */
  1772     int objc;			/* Number of arguments. */
  1773     Tcl_Obj *CONST objv[];	/* Argument objects. */
  1774 {
  1775     CONST char *nameOfExecutable;
  1776 
  1777     if (objc != 2) {
  1778         Tcl_WrongNumArgs(interp, 2, objv, NULL);
  1779         return TCL_ERROR;
  1780     }
  1781 
  1782     nameOfExecutable = Tcl_GetNameOfExecutable();
  1783     
  1784     if (nameOfExecutable != NULL) {
  1785 	Tcl_SetStringObj(Tcl_GetObjResult(interp), nameOfExecutable, -1);
  1786     }
  1787     return TCL_OK;
  1788 }
  1789 
  1790 /*
  1791  *----------------------------------------------------------------------
  1792  *
  1793  * InfoPatchLevelCmd --
  1794  *
  1795  *      Called to implement the "info patchlevel" command that returns the
  1796  *      default value for an argument to a procedure. Handles the following
  1797  *      syntax:
  1798  *
  1799  *          info patchlevel
  1800  *
  1801  * Results:
  1802  *      Returns TCL_OK if successful and TCL_ERROR if there is an error.
  1803  *
  1804  * Side effects:
  1805  *      Returns a result in the interpreter's result object. If there is
  1806  *	an error, the result is an error message.
  1807  *
  1808  *----------------------------------------------------------------------
  1809  */
  1810 
  1811 static int
  1812 InfoPatchLevelCmd(dummy, interp, objc, objv)
  1813     ClientData dummy;		/* Not used. */
  1814     Tcl_Interp *interp;		/* Current interpreter. */
  1815     int objc;			/* Number of arguments. */
  1816     Tcl_Obj *CONST objv[];	/* Argument objects. */
  1817 {
  1818     CONST char *patchlevel;
  1819 
  1820     if (objc != 2) {
  1821         Tcl_WrongNumArgs(interp, 2, objv, NULL);
  1822         return TCL_ERROR;
  1823     }
  1824 
  1825     patchlevel = Tcl_GetVar(interp, "tcl_patchLevel",
  1826             (TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG));
  1827     if (patchlevel != NULL) {
  1828         Tcl_SetStringObj(Tcl_GetObjResult(interp), patchlevel, -1);
  1829         return TCL_OK;
  1830     }
  1831     return TCL_ERROR;
  1832 }
  1833 
  1834 /*
  1835  *----------------------------------------------------------------------
  1836  *
  1837  * InfoProcsCmd --
  1838  *
  1839  *	Called to implement the "info procs" command that returns the
  1840  *	list of procedures in the interpreter that match an optional pattern.
  1841  *	The pattern, if any, consists of an optional sequence of namespace
  1842  *	names separated by "::" qualifiers, which is followed by a
  1843  *	glob-style pattern that restricts which commands are returned.
  1844  *	Handles the following syntax:
  1845  *
  1846  *          info procs ?pattern?
  1847  *
  1848  * Results:
  1849  *      Returns TCL_OK if successful and TCL_ERROR if there is an error.
  1850  *
  1851  * Side effects:
  1852  *      Returns a result in the interpreter's result object. If there is
  1853  *	an error, the result is an error message.
  1854  *
  1855  *----------------------------------------------------------------------
  1856  */
  1857 
  1858 static int
  1859 InfoProcsCmd(dummy, interp, objc, objv)
  1860     ClientData dummy;		/* Not used. */
  1861     Tcl_Interp *interp;		/* Current interpreter. */
  1862     int objc;			/* Number of arguments. */
  1863     Tcl_Obj *CONST objv[];	/* Argument objects. */
  1864 {
  1865     char *cmdName, *pattern;
  1866     CONST char *simplePattern;
  1867     Namespace *nsPtr;
  1868 #ifdef INFO_PROCS_SEARCH_GLOBAL_NS
  1869     Namespace *globalNsPtr = (Namespace *) Tcl_GetGlobalNamespace(interp);
  1870 #endif
  1871     Namespace *currNsPtr   = (Namespace *) Tcl_GetCurrentNamespace(interp);
  1872     Tcl_Obj *listPtr, *elemObjPtr;
  1873     int specificNsInPattern = 0;  /* Init. to avoid compiler warning. */
  1874     register Tcl_HashEntry *entryPtr;
  1875     Tcl_HashSearch search;
  1876     Command *cmdPtr, *realCmdPtr;
  1877 
  1878     /*
  1879      * Get the pattern and find the "effective namespace" in which to
  1880      * list procs.
  1881      */
  1882 
  1883     if (objc == 2) {
  1884 	simplePattern = NULL;
  1885 	nsPtr = currNsPtr;
  1886 	specificNsInPattern = 0;
  1887     } else if (objc == 3) {
  1888 	/*
  1889 	 * From the pattern, get the effective namespace and the simple
  1890 	 * pattern (no namespace qualifiers or ::'s) at the end. If an
  1891 	 * error was found while parsing the pattern, return it. Otherwise,
  1892 	 * if the namespace wasn't found, just leave nsPtr NULL: we will
  1893 	 * return an empty list since no commands there can be found.
  1894 	 */
  1895 
  1896 	Namespace *dummy1NsPtr, *dummy2NsPtr;
  1897 
  1898 	pattern = Tcl_GetString(objv[2]);
  1899 	TclGetNamespaceForQualName(interp, pattern, (Namespace *) NULL,
  1900 		/*flags*/ 0, &nsPtr, &dummy1NsPtr, &dummy2NsPtr,
  1901 		&simplePattern);
  1902 
  1903 	if (nsPtr != NULL) {	/* we successfully found the pattern's ns */
  1904 	    specificNsInPattern = (strcmp(simplePattern, pattern) != 0);
  1905 	}
  1906     } else {
  1907         Tcl_WrongNumArgs(interp, 2, objv, "?pattern?");
  1908         return TCL_ERROR;
  1909     }
  1910 
  1911     if (nsPtr == NULL) {
  1912 	return TCL_OK;
  1913     }
  1914 
  1915     /*
  1916      * Scan through the effective namespace's command table and create a
  1917      * list with all procs that match the pattern. If a specific
  1918      * namespace was requested in the pattern, qualify the command names
  1919      * with the namespace name.
  1920      */
  1921 
  1922     listPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
  1923 #ifndef INFO_PROCS_SEARCH_GLOBAL_NS
  1924     if (simplePattern != NULL && TclMatchIsTrivial(simplePattern)) {
  1925 	entryPtr = Tcl_FindHashEntry(&nsPtr->cmdTable, simplePattern);
  1926 	if (entryPtr != NULL) {
  1927 	    cmdPtr = (Command *) Tcl_GetHashValue(entryPtr);
  1928 
  1929 	    if (!TclIsProc(cmdPtr)) {
  1930 		realCmdPtr = (Command *)
  1931 			TclGetOriginalCommand((Tcl_Command) cmdPtr);
  1932 		if (realCmdPtr != NULL && TclIsProc(realCmdPtr)) {
  1933 		    goto simpleProcOK;
  1934 		}
  1935 	    } else {
  1936 	      simpleProcOK:
  1937 		if (specificNsInPattern) {
  1938 		    elemObjPtr = Tcl_NewObj();
  1939 		    Tcl_GetCommandFullName(interp, (Tcl_Command) cmdPtr,
  1940 			    elemObjPtr);
  1941 		} else {
  1942 		    elemObjPtr = Tcl_NewStringObj(simplePattern, -1);
  1943 		}
  1944 		Tcl_ListObjAppendElement(interp, listPtr, elemObjPtr);
  1945 	    }
  1946 	}
  1947     } else
  1948 #endif /* !INFO_PROCS_SEARCH_GLOBAL_NS */
  1949     {
  1950 	entryPtr = Tcl_FirstHashEntry(&nsPtr->cmdTable, &search);
  1951 	while (entryPtr != NULL) {
  1952 	    cmdName = Tcl_GetHashKey(&nsPtr->cmdTable, entryPtr);
  1953 	    if ((simplePattern == NULL)
  1954 	            || Tcl_StringMatch(cmdName, simplePattern)) {
  1955 		cmdPtr = (Command *) Tcl_GetHashValue(entryPtr);
  1956 
  1957 		if (!TclIsProc(cmdPtr)) {
  1958 		    realCmdPtr = (Command *)
  1959 			    TclGetOriginalCommand((Tcl_Command) cmdPtr);
  1960 		    if (realCmdPtr != NULL && TclIsProc(realCmdPtr)) {
  1961 			goto procOK;
  1962 		    }
  1963 		} else {
  1964 		  procOK:
  1965 		    if (specificNsInPattern) {
  1966 			elemObjPtr = Tcl_NewObj();
  1967 			Tcl_GetCommandFullName(interp, (Tcl_Command) cmdPtr,
  1968 				elemObjPtr);
  1969 		    } else {
  1970 			elemObjPtr = Tcl_NewStringObj(cmdName, -1);
  1971 		    }
  1972 		    Tcl_ListObjAppendElement(interp, listPtr, elemObjPtr);
  1973 		}
  1974 	    }
  1975 	    entryPtr = Tcl_NextHashEntry(&search);
  1976 	}
  1977 
  1978 	/*
  1979 	 * If the effective namespace isn't the global :: namespace, and a
  1980 	 * specific namespace wasn't requested in the pattern, then add in
  1981 	 * all global :: procs that match the simple pattern. Of course,
  1982 	 * we add in only those procs that aren't hidden by a proc in
  1983 	 * the effective namespace.
  1984 	 */
  1985 
  1986 #ifdef INFO_PROCS_SEARCH_GLOBAL_NS
  1987 	/*
  1988 	 * If "info procs" worked like "info commands", returning the
  1989 	 * commands also seen in the global namespace, then you would
  1990 	 * include this code.  As this could break backwards compatibilty
  1991 	 * with 8.0-8.2, we decided not to "fix" it in 8.3, leaving the
  1992 	 * behavior slightly different.
  1993 	 */
  1994 	if ((nsPtr != globalNsPtr) && !specificNsInPattern) {
  1995 	    entryPtr = Tcl_FirstHashEntry(&globalNsPtr->cmdTable, &search);
  1996 	    while (entryPtr != NULL) {
  1997 		cmdName = Tcl_GetHashKey(&globalNsPtr->cmdTable, entryPtr);
  1998 		if ((simplePattern == NULL)
  1999 	                || Tcl_StringMatch(cmdName, simplePattern)) {
  2000 		    if (Tcl_FindHashEntry(&nsPtr->cmdTable, cmdName) == NULL) {
  2001 			cmdPtr = (Command *) Tcl_GetHashValue(entryPtr);
  2002 			realCmdPtr = (Command *) TclGetOriginalCommand(
  2003 			        (Tcl_Command) cmdPtr);
  2004 
  2005 			if (TclIsProc(cmdPtr) || ((realCmdPtr != NULL)
  2006 				&& TclIsProc(realCmdPtr))) {
  2007 			    Tcl_ListObjAppendElement(interp, listPtr,
  2008 			            Tcl_NewStringObj(cmdName, -1));
  2009 			}
  2010 		    }
  2011 		}
  2012 		entryPtr = Tcl_NextHashEntry(&search);
  2013 	    }
  2014 	}
  2015 #endif
  2016     }
  2017 
  2018     Tcl_SetObjResult(interp, listPtr);
  2019     return TCL_OK;
  2020 }
  2021 
  2022 /*
  2023  *----------------------------------------------------------------------
  2024  *
  2025  * InfoScriptCmd --
  2026  *
  2027  *      Called to implement the "info script" command that returns the
  2028  *      script file that is currently being evaluated. Handles the
  2029  *      following syntax:
  2030  *
  2031  *          info script ?newName?
  2032  *
  2033  *	If newName is specified, it will set that as the internal name.
  2034  *
  2035  * Results:
  2036  *      Returns TCL_OK if successful and TCL_ERROR if there is an error.
  2037  *
  2038  * Side effects:
  2039  *      Returns a result in the interpreter's result object. If there is
  2040  *	an error, the result is an error message.  It may change the
  2041  *	internal script filename.
  2042  *
  2043  *----------------------------------------------------------------------
  2044  */
  2045 
  2046 static int
  2047 InfoScriptCmd(dummy, interp, objc, objv)
  2048     ClientData dummy;		/* Not used. */
  2049     Tcl_Interp *interp;		/* Current interpreter. */
  2050     int objc;			/* Number of arguments. */
  2051     Tcl_Obj *CONST objv[];	/* Argument objects. */
  2052 {
  2053     Interp *iPtr = (Interp *) interp;
  2054     if ((objc != 2) && (objc != 3)) {
  2055         Tcl_WrongNumArgs(interp, 2, objv, "?filename?");
  2056         return TCL_ERROR;
  2057     }
  2058 
  2059     if (objc == 3) {
  2060 	if (iPtr->scriptFile != NULL) {
  2061 	    Tcl_DecrRefCount(iPtr->scriptFile);
  2062 	}
  2063 	iPtr->scriptFile = objv[2];
  2064 	Tcl_IncrRefCount(iPtr->scriptFile);
  2065     }
  2066     if (iPtr->scriptFile != NULL) {
  2067         Tcl_SetObjResult(interp, iPtr->scriptFile);
  2068     }
  2069     return TCL_OK;
  2070 }
  2071 
  2072 /*
  2073  *----------------------------------------------------------------------
  2074  *
  2075  * InfoSharedlibCmd --
  2076  *
  2077  *      Called to implement the "info sharedlibextension" command that
  2078  *      returns the file extension used for shared libraries. Handles the
  2079  *      following syntax:
  2080  *
  2081  *          info sharedlibextension
  2082  *
  2083  * Results:
  2084  *      Returns TCL_OK if successful and TCL_ERROR if there is an error.
  2085  *
  2086  * Side effects:
  2087  *      Returns a result in the interpreter's result object. If there is
  2088  *	an error, the result is an error message.
  2089  *
  2090  *----------------------------------------------------------------------
  2091  */
  2092 
  2093 static int
  2094 InfoSharedlibCmd(dummy, interp, objc, objv)
  2095     ClientData dummy;		/* Not used. */
  2096     Tcl_Interp *interp;		/* Current interpreter. */
  2097     int objc;			/* Number of arguments. */
  2098     Tcl_Obj *CONST objv[];	/* Argument objects. */
  2099 {
  2100     if (objc != 2) {
  2101         Tcl_WrongNumArgs(interp, 2, objv, NULL);
  2102         return TCL_ERROR;
  2103     }
  2104     
  2105 #ifdef TCL_SHLIB_EXT
  2106     Tcl_SetStringObj(Tcl_GetObjResult(interp), TCL_SHLIB_EXT, -1);
  2107 #endif
  2108     return TCL_OK;
  2109 }
  2110 
  2111 /*
  2112  *----------------------------------------------------------------------
  2113  *
  2114  * InfoTclVersionCmd --
  2115  *
  2116  *      Called to implement the "info tclversion" command that returns the
  2117  *      version number for this Tcl library. Handles the following syntax:
  2118  *
  2119  *          info tclversion
  2120  *
  2121  * Results:
  2122  *      Returns TCL_OK if successful and TCL_ERROR if there is an error.
  2123  *
  2124  * Side effects:
  2125  *      Returns a result in the interpreter's result object. If there is
  2126  *	an error, the result is an error message.
  2127  *
  2128  *----------------------------------------------------------------------
  2129  */
  2130 
  2131 static int
  2132 InfoTclVersionCmd(dummy, interp, objc, objv)
  2133     ClientData dummy;		/* Not used. */
  2134     Tcl_Interp *interp;		/* Current interpreter. */
  2135     int objc;			/* Number of arguments. */
  2136     Tcl_Obj *CONST objv[];	/* Argument objects. */
  2137 {
  2138     CONST char *version;
  2139 
  2140     if (objc != 2) {
  2141         Tcl_WrongNumArgs(interp, 2, objv, NULL);
  2142         return TCL_ERROR;
  2143     }
  2144 
  2145     version = Tcl_GetVar(interp, "tcl_version",
  2146         (TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG));
  2147     if (version != NULL) {
  2148         Tcl_SetStringObj(Tcl_GetObjResult(interp), version, -1);
  2149         return TCL_OK;
  2150     }
  2151     return TCL_ERROR;
  2152 }
  2153 
  2154 /*
  2155  *----------------------------------------------------------------------
  2156  *
  2157  * InfoVarsCmd --
  2158  *
  2159  *	Called to implement the "info vars" command that returns the
  2160  *	list of variables in the interpreter that match an optional pattern.
  2161  *	The pattern, if any, consists of an optional sequence of namespace
  2162  *	names separated by "::" qualifiers, which is followed by a
  2163  *	glob-style pattern that restricts which variables are returned.
  2164  *	Handles the following syntax:
  2165  *
  2166  *          info vars ?pattern?
  2167  *
  2168  * Results:
  2169  *      Returns TCL_OK if successful and TCL_ERROR if there is an error.
  2170  *
  2171  * Side effects:
  2172  *      Returns a result in the interpreter's result object. If there is
  2173  *	an error, the result is an error message.
  2174  *
  2175  *----------------------------------------------------------------------
  2176  */
  2177 
  2178 static int
  2179 InfoVarsCmd(dummy, interp, objc, objv)
  2180     ClientData dummy;		/* Not used. */
  2181     Tcl_Interp *interp;		/* Current interpreter. */
  2182     int objc;			/* Number of arguments. */
  2183     Tcl_Obj *CONST objv[];	/* Argument objects. */
  2184 {
  2185     Interp *iPtr = (Interp *) interp;
  2186     char *varName, *pattern;
  2187     CONST char *simplePattern;
  2188     register Tcl_HashEntry *entryPtr;
  2189     Tcl_HashSearch search;
  2190     Var *varPtr;
  2191     Namespace *nsPtr;
  2192     Namespace *globalNsPtr = (Namespace *) Tcl_GetGlobalNamespace(interp);
  2193     Namespace *currNsPtr   = (Namespace *) Tcl_GetCurrentNamespace(interp);
  2194     Tcl_Obj *listPtr, *elemObjPtr;
  2195     int specificNsInPattern = 0;  /* Init. to avoid compiler warning. */
  2196 
  2197     /*
  2198      * Get the pattern and find the "effective namespace" in which to
  2199      * list variables. We only use this effective namespace if there's
  2200      * no active Tcl procedure frame.
  2201      */
  2202 
  2203     if (objc == 2) {
  2204         simplePattern = NULL;
  2205 	nsPtr = currNsPtr;
  2206 	specificNsInPattern = 0;
  2207     } else if (objc == 3) {
  2208 	/*
  2209 	 * From the pattern, get the effective namespace and the simple
  2210 	 * pattern (no namespace qualifiers or ::'s) at the end. If an
  2211 	 * error was found while parsing the pattern, return it. Otherwise,
  2212 	 * if the namespace wasn't found, just leave nsPtr NULL: we will
  2213 	 * return an empty list since no variables there can be found.
  2214 	 */
  2215 
  2216 	Namespace *dummy1NsPtr, *dummy2NsPtr;
  2217 
  2218         pattern = Tcl_GetString(objv[2]);
  2219 	TclGetNamespaceForQualName(interp, pattern, (Namespace *) NULL,
  2220 		/*flags*/ 0, &nsPtr, &dummy1NsPtr, &dummy2NsPtr,
  2221 		&simplePattern);
  2222 
  2223 	if (nsPtr != NULL) {	/* we successfully found the pattern's ns */
  2224 	    specificNsInPattern = (strcmp(simplePattern, pattern) != 0);
  2225 	}
  2226     } else {
  2227         Tcl_WrongNumArgs(interp, 2, objv, "?pattern?");
  2228         return TCL_ERROR;
  2229     }
  2230 
  2231     /*
  2232      * If the namespace specified in the pattern wasn't found, just return.
  2233      */
  2234 
  2235     if (nsPtr == NULL) {
  2236 	return TCL_OK;
  2237     }
  2238     
  2239     listPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
  2240     
  2241     if ((iPtr->varFramePtr == NULL)
  2242 	    || !iPtr->varFramePtr->isProcCallFrame
  2243 	    || specificNsInPattern) {
  2244 	/*
  2245 	 * There is no frame pointer, the frame pointer was pushed only
  2246 	 * to activate a namespace, or we are in a procedure call frame
  2247 	 * but a specific namespace was specified. Create a list containing
  2248 	 * only the variables in the effective namespace's variable table.
  2249 	 */
  2250 	
  2251 	if (simplePattern != NULL && TclMatchIsTrivial(simplePattern)) {
  2252 	    /*
  2253 	     * If we can just do hash lookups, that simplifies things
  2254 	     * a lot.
  2255 	     */
  2256 
  2257 	    entryPtr = Tcl_FindHashEntry(&nsPtr->varTable, simplePattern);
  2258 	    if (entryPtr != NULL) {
  2259 		varPtr = (Var *) Tcl_GetHashValue(entryPtr);
  2260 		if (!TclIsVarUndefined(varPtr)
  2261 			|| (varPtr->flags & VAR_NAMESPACE_VAR)) {
  2262 		    if (specificNsInPattern) {
  2263 			elemObjPtr = Tcl_NewObj();
  2264 			Tcl_GetVariableFullName(interp, (Tcl_Var) varPtr,
  2265 				    elemObjPtr);
  2266 		    } else {
  2267 			elemObjPtr = Tcl_NewStringObj(simplePattern, -1);
  2268 		    }
  2269 		    Tcl_ListObjAppendElement(interp, listPtr, elemObjPtr);
  2270 		}
  2271 	    } else if ((nsPtr != globalNsPtr) && !specificNsInPattern) {
  2272 		entryPtr = Tcl_FindHashEntry(&globalNsPtr->varTable,
  2273 			simplePattern);
  2274 		if (entryPtr != NULL) {
  2275 		    varPtr = (Var *) Tcl_GetHashValue(entryPtr);
  2276 		    if (!TclIsVarUndefined(varPtr)
  2277 			    || (varPtr->flags & VAR_NAMESPACE_VAR)) {
  2278 			Tcl_ListObjAppendElement(interp, listPtr,
  2279 				Tcl_NewStringObj(simplePattern, -1));
  2280 		    }
  2281 		}
  2282 	    }
  2283 	} else {
  2284 	    /*
  2285 	     * Have to scan the tables of variables.
  2286 	     */
  2287 
  2288 	    entryPtr = Tcl_FirstHashEntry(&nsPtr->varTable, &search);
  2289 	    while (entryPtr != NULL) {
  2290 		varPtr = (Var *) Tcl_GetHashValue(entryPtr);
  2291 		if (!TclIsVarUndefined(varPtr)
  2292 			|| (varPtr->flags & VAR_NAMESPACE_VAR)) {
  2293 		    varName = Tcl_GetHashKey(&nsPtr->varTable, entryPtr);
  2294 		    if ((simplePattern == NULL)
  2295 			    || Tcl_StringMatch(varName, simplePattern)) {
  2296 			if (specificNsInPattern) {
  2297 			    elemObjPtr = Tcl_NewObj();
  2298 			    Tcl_GetVariableFullName(interp, (Tcl_Var) varPtr,
  2299 				    elemObjPtr);
  2300 			} else {
  2301 			    elemObjPtr = Tcl_NewStringObj(varName, -1);
  2302 			}
  2303 			Tcl_ListObjAppendElement(interp, listPtr, elemObjPtr);
  2304 		    }
  2305 		}
  2306 		entryPtr = Tcl_NextHashEntry(&search);
  2307 	    }
  2308 
  2309 	    /*
  2310 	     * If the effective namespace isn't the global ::
  2311 	     * namespace, and a specific namespace wasn't requested in
  2312 	     * the pattern (i.e., the pattern only specifies variable
  2313 	     * names), then add in all global :: variables that match
  2314 	     * the simple pattern. Of course, add in only those
  2315 	     * variables that aren't hidden by a variable in the
  2316 	     * effective namespace.
  2317 	     */
  2318 
  2319 	    if ((nsPtr != globalNsPtr) && !specificNsInPattern) {
  2320 		entryPtr = Tcl_FirstHashEntry(&globalNsPtr->varTable, &search);
  2321 		while (entryPtr != NULL) {
  2322 		    varPtr = (Var *) Tcl_GetHashValue(entryPtr);
  2323 		    if (!TclIsVarUndefined(varPtr)
  2324 			    || (varPtr->flags & VAR_NAMESPACE_VAR)) {
  2325 			varName = Tcl_GetHashKey(&globalNsPtr->varTable,
  2326 				entryPtr);
  2327 			if ((simplePattern == NULL)
  2328 				|| Tcl_StringMatch(varName, simplePattern)) {
  2329 			    if (Tcl_FindHashEntry(&nsPtr->varTable,
  2330 				    varName) == NULL) {
  2331 				Tcl_ListObjAppendElement(interp, listPtr,
  2332 					Tcl_NewStringObj(varName, -1));
  2333 			    }
  2334 			}
  2335 		    }
  2336 		    entryPtr = Tcl_NextHashEntry(&search);
  2337 		}
  2338 	    }
  2339 	}
  2340     } else if (((Interp *)interp)->varFramePtr->procPtr != NULL) {
  2341 	AppendLocals(interp, listPtr, simplePattern, 1);
  2342     }
  2343     
  2344     Tcl_SetObjResult(interp, listPtr);
  2345     return TCL_OK;
  2346 }
  2347 
  2348 /*
  2349  *----------------------------------------------------------------------
  2350  *
  2351  * Tcl_JoinObjCmd --
  2352  *
  2353  *	This procedure is invoked to process the "join" Tcl command.
  2354  *	See the user documentation for details on what it does.
  2355  *
  2356  * Results:
  2357  *	A standard Tcl object result.
  2358  *
  2359  * Side effects:
  2360  *	See the user documentation.
  2361  *
  2362  *----------------------------------------------------------------------
  2363  */
  2364 
  2365 	/* ARGSUSED */
  2366 int
  2367 Tcl_JoinObjCmd(dummy, interp, objc, objv)
  2368     ClientData dummy;		/* Not used. */
  2369     Tcl_Interp *interp;		/* Current interpreter. */
  2370     int objc;			/* Number of arguments. */
  2371     Tcl_Obj *CONST objv[];	/* The argument objects. */
  2372 {
  2373     char *joinString, *bytes;
  2374     int joinLength, listLen, length, i, result;
  2375     Tcl_Obj **elemPtrs;
  2376     Tcl_Obj *resObjPtr;
  2377 
  2378     if (objc == 2) {
  2379 	joinString = " ";
  2380 	joinLength = 1;
  2381     } else if (objc == 3) {
  2382 	joinString = Tcl_GetStringFromObj(objv[2], &joinLength);
  2383     } else {
  2384 	Tcl_WrongNumArgs(interp, 1, objv, "list ?joinString?");
  2385 	return TCL_ERROR;
  2386     }
  2387 
  2388     /*
  2389      * Make sure the list argument is a list object and get its length and
  2390      * a pointer to its array of element pointers.
  2391      */
  2392 
  2393     result = Tcl_ListObjGetElements(interp, objv[1], &listLen, &elemPtrs);
  2394     if (result != TCL_OK) {
  2395 	return result;
  2396     }
  2397 
  2398     /*
  2399      * Now concatenate strings to form the "joined" result. We append
  2400      * directly into the interpreter's result object.
  2401      */
  2402 
  2403     resObjPtr = Tcl_GetObjResult(interp);
  2404 
  2405     for (i = 0;  i < listLen;  i++) {
  2406 	bytes = Tcl_GetStringFromObj(elemPtrs[i], &length);
  2407 	if (i > 0) {
  2408 	    Tcl_AppendToObj(resObjPtr, joinString, joinLength);
  2409 	}
  2410 	Tcl_AppendToObj(resObjPtr, bytes, length);
  2411     }
  2412     return TCL_OK;
  2413 }
  2414 
  2415 /*
  2416  *----------------------------------------------------------------------
  2417  *
  2418  * Tcl_LindexObjCmd --
  2419  *
  2420  *	This object-based procedure is invoked to process the "lindex" Tcl
  2421  *	command. See the user documentation for details on what it does.
  2422  *
  2423  * Results:
  2424  *	A standard Tcl object result.
  2425  *
  2426  * Side effects:
  2427  *	See the user documentation.
  2428  *
  2429  *----------------------------------------------------------------------
  2430  */
  2431 
  2432     /* ARGSUSED */
  2433 int
  2434 Tcl_LindexObjCmd(dummy, interp, objc, objv)
  2435     ClientData dummy;		/* Not used. */
  2436     Tcl_Interp *interp;		/* Current interpreter. */
  2437     int objc;			/* Number of arguments. */
  2438     Tcl_Obj *CONST objv[];	/* Argument objects. */
  2439 {
  2440 
  2441     Tcl_Obj *elemPtr;		/* Pointer to the element being extracted */
  2442 
  2443     if (objc < 2) {
  2444 	Tcl_WrongNumArgs(interp, 1, objv, "list ?index...?");
  2445 	return TCL_ERROR;
  2446     }
  2447 
  2448     /*
  2449      * If objc == 3, then objv[ 2 ] may be either a single index or
  2450      * a list of indices: go to TclLindexList to determine which.
  2451      * If objc >= 4, or objc == 2, then objv[ 2 .. objc-2 ] are all
  2452      * single indices and processed as such in TclLindexFlat.
  2453      */
  2454 
  2455     if ( objc == 3 ) {
  2456 
  2457 	elemPtr = TclLindexList( interp, objv[ 1 ], objv[ 2 ] );
  2458 
  2459     } else {
  2460 
  2461 	elemPtr = TclLindexFlat( interp, objv[ 1 ], objc-2, objv+2 );
  2462 
  2463     }
  2464 	
  2465     /*
  2466      * Set the interpreter's object result to the last element extracted
  2467      */
  2468 
  2469     if ( elemPtr == NULL ) {
  2470 	return TCL_ERROR;
  2471     } else {
  2472 	Tcl_SetObjResult(interp, elemPtr);
  2473 	Tcl_DecrRefCount( elemPtr );
  2474 	return TCL_OK;
  2475     }
  2476 }
  2477 
  2478 /*
  2479  *----------------------------------------------------------------------
  2480  *
  2481  * TclLindexList --
  2482  *
  2483  *	This procedure handles the 'lindex' command when objc==3.
  2484  *
  2485  * Results:
  2486  *	Returns a pointer to the object extracted, or NULL if an
  2487  *	error occurred.
  2488  *
  2489  * Side effects:
  2490  *	None.
  2491  *
  2492  * If objv[1] can be parsed as a list, TclLindexList handles extraction
  2493  * of the desired element locally.  Otherwise, it invokes
  2494  * TclLindexFlat to treat objv[1] as a scalar.
  2495  *
  2496  * The reference count of the returned object includes one reference
  2497  * corresponding to the pointer returned.  Thus, the calling code will
  2498  * usually do something like:
  2499  *	Tcl_SetObjResult( interp, result );
  2500  *	Tcl_DecrRefCount( result );
  2501  *
  2502  *----------------------------------------------------------------------
  2503  */
  2504 
  2505 Tcl_Obj *
  2506 TclLindexList( interp, listPtr, argPtr )
  2507     Tcl_Interp* interp;		/* Tcl interpreter */
  2508     Tcl_Obj* listPtr;		/* List being unpacked */
  2509     Tcl_Obj* argPtr;		/* Index or index list */
  2510 {
  2511 
  2512     Tcl_Obj **elemPtrs;		/* Elements of the list being manipulated. */
  2513     int listLen;		/* Length of the list being manipulated. */
  2514     int index;			/* Index into the list */
  2515     int result;			/* Result returned from a Tcl library call */
  2516     int i;			/* Current index number */
  2517     Tcl_Obj** indices;		/* Array of list indices */
  2518     int indexCount;		/* Size of the array of list indices */
  2519     Tcl_Obj* oldListPtr;	/* Temp location to preserve the list
  2520 				 * pointer when replacing it with a sublist */
  2521 
  2522     /*
  2523      * Determine whether argPtr designates a list or a single index.
  2524      * We have to be careful about the order of the checks to avoid
  2525      * repeated shimmering; see TIP#22 and TIP#33 for the details.
  2526      */
  2527 
  2528     if ( argPtr->typePtr != &tclListType 
  2529 	 && TclGetIntForIndex( NULL , argPtr, 0, &index ) == TCL_OK ) {
  2530 
  2531 	/*
  2532 	 * argPtr designates a single index.
  2533 	 */
  2534 
  2535 	return TclLindexFlat( interp, listPtr, 1, &argPtr );
  2536 
  2537     } else if ( Tcl_ListObjGetElements( NULL, argPtr, &indexCount, &indices )
  2538 		!= TCL_OK ) {
  2539 
  2540 	/*
  2541 	 * argPtr designates something that is neither an index nor a
  2542 	 * well-formed list.  Report the error via TclLindexFlat.
  2543 	 */
  2544 	
  2545 	return TclLindexFlat( interp, listPtr, 1, &argPtr );
  2546     }
  2547 
  2548     /*
  2549      * Record the reference to the list that we are maintaining in
  2550      * the activation record.
  2551      */
  2552 
  2553     Tcl_IncrRefCount( listPtr );
  2554 
  2555     /*
  2556      * argPtr designates a list, and the 'else if' above has parsed it
  2557      * into indexCount and indices.
  2558      */
  2559 
  2560     for ( i = 0; i < indexCount; ++i ) {
  2561 
  2562 	/*
  2563 	 * Convert the current listPtr to a list if necessary.
  2564 	 */
  2565 	    
  2566 	result = Tcl_ListObjGetElements( interp, listPtr,
  2567 					 &listLen, &elemPtrs);
  2568 	if (result != TCL_OK) {
  2569 	    Tcl_DecrRefCount( listPtr );
  2570 	    return NULL;
  2571 	}
  2572 	    
  2573 	/*
  2574 	 * Get the index from indices[ i ]
  2575 	 */
  2576 	
  2577 	result = TclGetIntForIndex( interp, indices[ i ],
  2578 				    /*endValue*/ (listLen - 1),
  2579 				    &index );
  2580 	if ( result != TCL_OK ) {
  2581 	    /*
  2582 	     * Index could not be parsed
  2583 	     */
  2584 
  2585 	    Tcl_DecrRefCount( listPtr );
  2586 	    return NULL;
  2587 
  2588 	} else if ( index < 0
  2589 		    || index >= listLen ) {
  2590 	    /*
  2591 	     * Index is out of range
  2592 	     */
  2593 	    Tcl_DecrRefCount( listPtr );
  2594 	    listPtr = Tcl_NewObj();
  2595 	    Tcl_IncrRefCount( listPtr );
  2596 	    return listPtr;
  2597 	}
  2598 	
  2599 	/*
  2600 	 * Make sure listPtr still refers to a list object.
  2601 	 * If it shared a Tcl_Obj structure with the arguments, then
  2602 	 * it might have just been converted to something else.
  2603 	 */
  2604 	
  2605 	if (listPtr->typePtr != &tclListType) {
  2606 	    result = Tcl_ListObjGetElements(interp, listPtr, &listLen,
  2607 					    &elemPtrs);
  2608 	    if (result != TCL_OK) {
  2609 		Tcl_DecrRefCount( listPtr );
  2610 		return NULL;
  2611 	    }
  2612 	}
  2613 	
  2614 	/*
  2615 	 * Extract the pointer to the appropriate element
  2616 	 */
  2617 	
  2618 	oldListPtr = listPtr;
  2619 	listPtr = elemPtrs[ index ];
  2620 	Tcl_IncrRefCount( listPtr );
  2621 	Tcl_DecrRefCount( oldListPtr );
  2622 	
  2623 	/*
  2624 	 * The work we did above may have caused the internal rep
  2625 	 * of *argPtr to change to something else.  Get it back.
  2626 	 */
  2627 	
  2628 	result = Tcl_ListObjGetElements( interp, argPtr,
  2629 					 &indexCount, &indices );
  2630 	if ( result != TCL_OK ) {
  2631 	    /* 
  2632 	     * This can't happen unless some extension corrupted a Tcl_Obj.
  2633 	     */
  2634 	    Tcl_DecrRefCount( listPtr );
  2635 	    return NULL;
  2636 	}
  2637 	
  2638     } /* end for */
  2639 
  2640     /*
  2641      * Return the last object extracted.  Its reference count will include
  2642      * the reference being returned.
  2643      */
  2644 
  2645     return listPtr;
  2646 }
  2647 
  2648 /*
  2649  *----------------------------------------------------------------------
  2650  *
  2651  * TclLindexFlat --
  2652  *
  2653  *	This procedure handles the 'lindex' command, given that the
  2654  *	arguments to the command are known to be a flat list.
  2655  *
  2656  * Results:
  2657  *	Returns a standard Tcl result.
  2658  *
  2659  * Side effects:
  2660  *	None.
  2661  *
  2662  * This procedure is called from either tclExecute.c or
  2663  * Tcl_LindexObjCmd whenever either is presented with
  2664  * objc == 2 or objc >= 4.  It is also called from TclLindexList
  2665  * for the objc==3 case once it is determined that objv[2] cannot
  2666  * be parsed as a list.
  2667  *
  2668  *----------------------------------------------------------------------
  2669  */
  2670 
  2671 Tcl_Obj *
  2672 TclLindexFlat( interp, listPtr, indexCount, indexArray )
  2673     Tcl_Interp* interp;		/* Tcl interpreter */
  2674     Tcl_Obj* listPtr;		/* Tcl object representing the list */
  2675     int indexCount;		/* Count of indices */
  2676     Tcl_Obj* CONST indexArray[];
  2677 				/* Array of pointers to Tcl objects
  2678 				 * representing the indices in the
  2679 				 * list */
  2680 {
  2681 
  2682     int i;			/* Current list index */
  2683     int result;			/* Result of Tcl library calls */
  2684     int listLen;		/* Length of the current list being 
  2685 				 * processed */
  2686     Tcl_Obj** elemPtrs;		/* Array of pointers to the elements
  2687 				 * of the current list */
  2688     int index;			/* Parsed version of the current element
  2689 				 * of indexArray  */
  2690     Tcl_Obj* oldListPtr;	/* Temporary to hold listPtr so that
  2691 				 * its ref count can be decremented. */
  2692 
  2693     /*
  2694      * Record the reference to the 'listPtr' object that we are
  2695      * maintaining in the C activation record.
  2696      */
  2697 
  2698     Tcl_IncrRefCount( listPtr );
  2699 
  2700     for ( i = 0; i < indexCount; ++i ) {
  2701 
  2702 	/*
  2703 	 * Convert the current listPtr to a list if necessary.
  2704 	 */
  2705 	
  2706 	result = Tcl_ListObjGetElements(interp, listPtr,
  2707 					&listLen, &elemPtrs);
  2708 	if (result != TCL_OK) {
  2709 	    Tcl_DecrRefCount( listPtr );
  2710 	    return NULL;
  2711 	}
  2712 	
  2713 	/*
  2714 	 * Get the index from objv[i]
  2715 	 */
  2716 	
  2717 	result = TclGetIntForIndex( interp, indexArray[ i ],
  2718 				    /*endValue*/ (listLen - 1),
  2719 				    &index );
  2720 	if ( result != TCL_OK ) {
  2721 
  2722 	    /* Index could not be parsed */
  2723 
  2724 	    Tcl_DecrRefCount( listPtr );
  2725 	    return NULL;
  2726 
  2727 	} else if ( index < 0
  2728 		    || index >= listLen ) {
  2729 	    
  2730 	    /*
  2731 	     * Index is out of range
  2732 	     */
  2733 		
  2734 	    Tcl_DecrRefCount( listPtr );
  2735 	    listPtr = Tcl_NewObj();
  2736 	    Tcl_IncrRefCount( listPtr );
  2737 	    return listPtr;
  2738 	}
  2739 	    
  2740 	/*
  2741 	 * Make sure listPtr still refers to a list object.
  2742 	 * It might have been converted to something else above
  2743 	 * if objv[1] overlaps with one of the other parameters.
  2744 	 */
  2745 	
  2746 	if (listPtr->typePtr != &tclListType) {
  2747 	    result = Tcl_ListObjGetElements(interp, listPtr, &listLen,
  2748 					    &elemPtrs);
  2749 	    if (result != TCL_OK) {
  2750 		Tcl_DecrRefCount( listPtr );
  2751 		return NULL;
  2752 	    }
  2753 	}
  2754 	
  2755 	/*
  2756 	 * Extract the pointer to the appropriate element
  2757 	 */
  2758 	
  2759 	oldListPtr = listPtr;
  2760 	listPtr = elemPtrs[ index ];
  2761 	Tcl_IncrRefCount( listPtr );
  2762 	Tcl_DecrRefCount( oldListPtr );
  2763 	
  2764     }
  2765 
  2766     return listPtr;
  2767 
  2768 }
  2769 
  2770 /*
  2771  *----------------------------------------------------------------------
  2772  *
  2773  * Tcl_LinsertObjCmd --
  2774  *
  2775  *	This object-based procedure is invoked to process the "linsert" Tcl
  2776  *	command. See the user documentation for details on what it does.
  2777  *
  2778  * Results:
  2779  *	A new Tcl list object formed by inserting zero or more elements 
  2780  *	into a list.
  2781  *
  2782  * Side effects:
  2783  *	See the user documentation.
  2784  *
  2785  *----------------------------------------------------------------------
  2786  */
  2787 
  2788 	/* ARGSUSED */
  2789 int
  2790 Tcl_LinsertObjCmd(dummy, interp, objc, objv)
  2791     ClientData dummy;		/* Not used. */
  2792     Tcl_Interp *interp;		/* Current interpreter. */
  2793     register int objc;		/* Number of arguments. */
  2794     Tcl_Obj *CONST objv[];	/* Argument objects. */
  2795 {
  2796     Tcl_Obj *listPtr;
  2797     int index, isDuplicate, len, result;
  2798 
  2799     if (objc < 4) {
  2800 	Tcl_WrongNumArgs(interp, 1, objv, "list index element ?element ...?");
  2801 	return TCL_ERROR;
  2802     }
  2803 
  2804     result = Tcl_ListObjLength(interp, objv[1], &len);
  2805     if (result != TCL_OK) {
  2806 	return result;
  2807     }
  2808 
  2809     /*
  2810      * Get the index.  "end" is interpreted to be the index after the last
  2811      * element, such that using it will cause any inserted elements to be
  2812      * appended to the list.
  2813      */
  2814 
  2815     result = TclGetIntForIndex(interp, objv[2], /*end*/ len, &index);
  2816     if (result != TCL_OK) {
  2817 	return result;
  2818     }
  2819     if (index > len) {
  2820 	index = len;
  2821     }
  2822 
  2823     /*
  2824      * If the list object is unshared we can modify it directly. Otherwise
  2825      * we create a copy to modify: this is "copy on write".
  2826      */
  2827 
  2828     listPtr = objv[1];
  2829     isDuplicate = 0;
  2830     if (Tcl_IsShared(listPtr)) {
  2831 	listPtr = Tcl_DuplicateObj(listPtr);
  2832 	isDuplicate = 1;
  2833     }
  2834 
  2835     if ((objc == 4) && (index == len)) {
  2836 	/*
  2837 	 * Special case: insert one element at the end of the list.
  2838 	 */
  2839 	result = Tcl_ListObjAppendElement(interp, listPtr, objv[3]);
  2840     } else if (objc > 3) {
  2841 	result = Tcl_ListObjReplace(interp, listPtr, index, 0,
  2842 				    (objc-3), &(objv[3]));
  2843     }
  2844     if (result != TCL_OK) {
  2845 	if (isDuplicate) {
  2846 	    Tcl_DecrRefCount(listPtr); /* free unneeded obj */
  2847 	}
  2848 	return result;
  2849     }
  2850     
  2851     /*
  2852      * Set the interpreter's object result.
  2853      */
  2854 
  2855     Tcl_SetObjResult(interp, listPtr);
  2856     return TCL_OK;
  2857 }
  2858 
  2859 /*
  2860  *----------------------------------------------------------------------
  2861  *
  2862  * Tcl_ListObjCmd --
  2863  *
  2864  *	This procedure is invoked to process the "list" Tcl command.
  2865  *	See the user documentation for details on what it does.
  2866  *
  2867  * Results:
  2868  *	A standard Tcl object result.
  2869  *
  2870  * Side effects:
  2871  *	See the user documentation.
  2872  *
  2873  *----------------------------------------------------------------------
  2874  */
  2875 
  2876 	/* ARGSUSED */
  2877 int
  2878 Tcl_ListObjCmd(dummy, interp, objc, objv)
  2879     ClientData dummy;			/* Not used. */
  2880     Tcl_Interp *interp;			/* Current interpreter. */
  2881     register int objc;			/* Number of arguments. */
  2882     register Tcl_Obj *CONST objv[];	/* The argument objects. */
  2883 {
  2884     /*
  2885      * If there are no list elements, the result is an empty object.
  2886      * Otherwise modify the interpreter's result object to be a list object.
  2887      */
  2888     
  2889     if (objc > 1) {
  2890 	Tcl_SetListObj(Tcl_GetObjResult(interp), (objc-1), &(objv[1]));
  2891     }
  2892     return TCL_OK;
  2893 }
  2894 
  2895 /*
  2896  *----------------------------------------------------------------------
  2897  *
  2898  * Tcl_LlengthObjCmd --
  2899  *
  2900  *	This object-based procedure is invoked to process the "llength" Tcl
  2901  *	command.  See the user documentation for details on what it does.
  2902  *
  2903  * Results:
  2904  *	A standard Tcl object result.
  2905  *
  2906  * Side effects:
  2907  *	See the user documentation.
  2908  *
  2909  *----------------------------------------------------------------------
  2910  */
  2911 
  2912 	/* ARGSUSED */
  2913 int
  2914 Tcl_LlengthObjCmd(dummy, interp, objc, objv)
  2915     ClientData dummy;			/* Not used. */
  2916     Tcl_Interp *interp;			/* Current interpreter. */
  2917     int objc;				/* Number of arguments. */
  2918     register Tcl_Obj *CONST objv[];	/* Argument objects. */
  2919 {
  2920     int listLen, result;
  2921 
  2922     if (objc != 2) {
  2923 	Tcl_WrongNumArgs(interp, 1, objv, "list");
  2924 	return TCL_ERROR;
  2925     }
  2926 
  2927     result = Tcl_ListObjLength(interp, objv[1], &listLen);
  2928     if (result != TCL_OK) {
  2929 	return result;
  2930     }
  2931 
  2932     /*
  2933      * Set the interpreter's object result to an integer object holding the
  2934      * length. 
  2935      */
  2936 
  2937     Tcl_SetIntObj(Tcl_GetObjResult(interp), listLen);
  2938     return TCL_OK;
  2939 }
  2940 
  2941 /*
  2942  *----------------------------------------------------------------------
  2943  *
  2944  * Tcl_LrangeObjCmd --
  2945  *
  2946  *	This procedure is invoked to process the "lrange" Tcl command.
  2947  *	See the user documentation for details on what it does.
  2948  *
  2949  * Results:
  2950  *	A standard Tcl object result.
  2951  *
  2952  * Side effects:
  2953  *	See the user documentation.
  2954  *
  2955  *----------------------------------------------------------------------
  2956  */
  2957 
  2958 	/* ARGSUSED */
  2959 int
  2960 Tcl_LrangeObjCmd(notUsed, interp, objc, objv)
  2961     ClientData notUsed;			/* Not used. */
  2962     Tcl_Interp *interp;			/* Current interpreter. */
  2963     int objc;				/* Number of arguments. */
  2964     register Tcl_Obj *CONST objv[];	/* Argument objects. */
  2965 {
  2966     Tcl_Obj *listPtr;
  2967     Tcl_Obj **elemPtrs;
  2968     int listLen, first, last, numElems, result;
  2969 
  2970     if (objc != 4) {
  2971 	Tcl_WrongNumArgs(interp, 1, objv, "list first last");
  2972 	return TCL_ERROR;
  2973     }
  2974 
  2975     /*
  2976      * Make sure the list argument is a list object and get its length and
  2977      * a pointer to its array of element pointers.
  2978      */
  2979 
  2980     listPtr = objv[1];
  2981     result = Tcl_ListObjGetElements(interp, listPtr, &listLen, &elemPtrs);
  2982     if (result != TCL_OK) {
  2983 	return result;
  2984     }
  2985 
  2986     /*
  2987      * Get the first and last indexes.
  2988      */
  2989 
  2990     result = TclGetIntForIndex(interp, objv[2], /*endValue*/ (listLen - 1),
  2991 	    &first);
  2992     if (result != TCL_OK) {
  2993 	return result;
  2994     }
  2995     if (first < 0) {
  2996 	first = 0;
  2997     }
  2998 
  2999     result = TclGetIntForIndex(interp, objv[3], /*endValue*/ (listLen - 1),
  3000 	    &last);
  3001     if (result != TCL_OK) {
  3002 	return result;
  3003     }
  3004     if (last >= listLen) {
  3005 	last = (listLen - 1);
  3006     }
  3007     
  3008     if (first > last) {
  3009 	return TCL_OK;		/* the result is an empty object */
  3010     }
  3011 
  3012     /*
  3013      * Make sure listPtr still refers to a list object. It might have been
  3014      * converted to an int above if the argument objects were shared.
  3015      */  
  3016 
  3017     if (listPtr->typePtr != &tclListType) {
  3018         result = Tcl_ListObjGetElements(interp, listPtr, &listLen,
  3019                 &elemPtrs);
  3020         if (result != TCL_OK) {
  3021             return result;
  3022         }
  3023     }
  3024 
  3025     /*
  3026      * Extract a range of fields. We modify the interpreter's result object
  3027      * to be a list object containing the specified elements.
  3028      */
  3029 
  3030     numElems = (last - first + 1);
  3031     Tcl_SetListObj(Tcl_GetObjResult(interp), numElems, &(elemPtrs[first]));
  3032     return TCL_OK;
  3033 }
  3034 
  3035 /*
  3036  *----------------------------------------------------------------------
  3037  *
  3038  * Tcl_LreplaceObjCmd --
  3039  *
  3040  *	This object-based procedure is invoked to process the "lreplace" 
  3041  *	Tcl command. See the user documentation for details on what it does.
  3042  *
  3043  * Results:
  3044  *	A new Tcl list object formed by replacing zero or more elements of
  3045  *	a list.
  3046  *
  3047  * Side effects:
  3048  *	See the user documentation.
  3049  *
  3050  *----------------------------------------------------------------------
  3051  */
  3052 
  3053 	/* ARGSUSED */
  3054 int
  3055 Tcl_LreplaceObjCmd(dummy, interp, objc, objv)
  3056     ClientData dummy;		/* Not used. */
  3057     Tcl_Interp *interp;		/* Current interpreter. */
  3058     int objc;			/* Number of arguments. */
  3059     Tcl_Obj *CONST objv[];	/* Argument objects. */
  3060 {
  3061     register Tcl_Obj *listPtr;
  3062     int isDuplicate, first, last, listLen, numToDelete, result;
  3063 
  3064     if (objc < 4) {
  3065 	Tcl_WrongNumArgs(interp, 1, objv,
  3066 		"list first last ?element element ...?");
  3067 	return TCL_ERROR;
  3068     }
  3069 
  3070     result = Tcl_ListObjLength(interp, objv[1], &listLen);
  3071     if (result != TCL_OK) {
  3072 	return result;
  3073     }
  3074 
  3075     /*
  3076      * Get the first and last indexes.  "end" is interpreted to be the index
  3077      * for the last element, such that using it will cause that element to
  3078      * be included for deletion.
  3079      */
  3080 
  3081     result = TclGetIntForIndex(interp, objv[2], /*end*/ (listLen - 1), &first);
  3082     if (result != TCL_OK) {
  3083 	return result;
  3084     }
  3085 
  3086     result = TclGetIntForIndex(interp, objv[3], /*end*/ (listLen - 1), &last);
  3087     if (result != TCL_OK) {
  3088 	return result;
  3089     }
  3090 
  3091     if (first < 0)  {
  3092     	first = 0;
  3093     }
  3094 
  3095     /*
  3096      * Complain if the user asked for a start element that is greater than the
  3097      * list length.  This won't ever trigger for the "end*" case as that will
  3098      * be properly constrained by TclGetIntForIndex because we use listLen-1
  3099      * (to allow for replacing the last elem).
  3100      */
  3101 
  3102     if ((first >= listLen) && (listLen > 0)) {
  3103 	Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
  3104 		"list doesn't contain element ",
  3105 		Tcl_GetString(objv[2]), (int *) NULL);
  3106 	return TCL_ERROR;
  3107     }
  3108     if (last >= listLen) {
  3109     	last = (listLen - 1);
  3110     }
  3111     if (first <= last) {
  3112 	numToDelete = (last - first + 1);
  3113     } else {
  3114 	numToDelete = 0;
  3115     }
  3116 
  3117     /*
  3118      * If the list object is unshared we can modify it directly, otherwise
  3119      * we create a copy to modify: this is "copy on write".
  3120      */
  3121 
  3122     listPtr = objv[1];
  3123     isDuplicate = 0;
  3124     if (Tcl_IsShared(listPtr)) {
  3125 	listPtr = Tcl_DuplicateObj(listPtr);
  3126 	isDuplicate = 1;
  3127     }
  3128     if (objc > 4) {
  3129 	result = Tcl_ListObjReplace(interp, listPtr, first, numToDelete,
  3130 	        (objc-4), &(objv[4]));
  3131     } else {
  3132 	result = Tcl_ListObjReplace(interp, listPtr, first, numToDelete,
  3133 		0, NULL);
  3134     }
  3135     if (result != TCL_OK) {
  3136 	if (isDuplicate) {
  3137 	    Tcl_DecrRefCount(listPtr); /* free unneeded obj */
  3138 	}
  3139 	return result;
  3140     }
  3141 
  3142     /*
  3143      * Set the interpreter's object result. 
  3144      */
  3145 
  3146     Tcl_SetObjResult(interp, listPtr);
  3147     return TCL_OK;
  3148 }
  3149 
  3150 /*
  3151  *----------------------------------------------------------------------
  3152  *
  3153  * Tcl_LsearchObjCmd --
  3154  *
  3155  *	This procedure is invoked to process the "lsearch" Tcl command.
  3156  *	See the user documentation for details on what it does.
  3157  *
  3158  * Results:
  3159  *	A standard Tcl result.
  3160  *
  3161  * Side effects:
  3162  *	See the user documentation.
  3163  *
  3164  *----------------------------------------------------------------------
  3165  */
  3166 
  3167 int
  3168 Tcl_LsearchObjCmd(clientData, interp, objc, objv)
  3169     ClientData clientData;	/* Not used. */
  3170     Tcl_Interp *interp;		/* Current interpreter. */
  3171     int objc;			/* Number of arguments. */
  3172     Tcl_Obj *CONST objv[];	/* Argument values. */
  3173 {
  3174     char *bytes, *patternBytes;
  3175     int i, match, mode, index, result, listc, length, elemLen;
  3176     int dataType, isIncreasing, lower, upper, patInt, objInt;
  3177     int offset, allMatches, inlineReturn, negatedMatch;
  3178     double patDouble, objDouble;
  3179     Tcl_Obj *patObj, **listv, *listPtr, *startPtr;
  3180     Tcl_RegExp regexp = NULL;
  3181     static CONST char *options[] = {
  3182 	"-all",	    "-ascii", "-decreasing", "-dictionary",
  3183 	"-exact",   "-glob",  "-increasing", "-inline",
  3184 	"-integer", "-not",   "-real",	     "-regexp",
  3185 	"-sorted",  "-start", NULL
  3186     };
  3187     enum options {
  3188 	LSEARCH_ALL, LSEARCH_ASCII, LSEARCH_DECREASING, LSEARCH_DICTIONARY,
  3189 	LSEARCH_EXACT, LSEARCH_GLOB, LSEARCH_INCREASING, LSEARCH_INLINE,
  3190 	LSEARCH_INTEGER, LSEARCH_NOT, LSEARCH_REAL, LSEARCH_REGEXP,
  3191 	LSEARCH_SORTED, LSEARCH_START
  3192     };
  3193     enum datatypes {
  3194 	ASCII, DICTIONARY, INTEGER, REAL
  3195     };
  3196     enum modes {
  3197 	EXACT, GLOB, REGEXP, SORTED
  3198     };
  3199 
  3200     mode = GLOB;
  3201     dataType = ASCII;
  3202     isIncreasing = 1;
  3203     allMatches = 0;
  3204     inlineReturn = 0;
  3205     negatedMatch = 0;
  3206     listPtr = NULL;
  3207     startPtr = NULL;
  3208     offset = 0;
  3209 
  3210     if (objc < 3) {
  3211 	Tcl_WrongNumArgs(interp, 1, objv, "?options? list pattern");
  3212 	return TCL_ERROR;
  3213     }
  3214 
  3215     for (i = 1; i < objc-2; i++) {
  3216 	if (Tcl_GetIndexFromObj(interp, objv[i], options, "option", 0, &index)
  3217 		!= TCL_OK) {
  3218 	    if (startPtr) {
  3219 		Tcl_DecrRefCount(startPtr);
  3220 	    }
  3221 	    return TCL_ERROR;
  3222 	}
  3223 	switch ((enum options) index) {
  3224 	case LSEARCH_ALL:		/* -all */
  3225 	    allMatches = 1;
  3226 	    break;
  3227 	case LSEARCH_ASCII:		/* -ascii */
  3228 	    dataType = ASCII;
  3229 	    break;
  3230 	case LSEARCH_DECREASING:	/* -decreasing */
  3231 	    isIncreasing = 0;
  3232 	    break;
  3233 	case LSEARCH_DICTIONARY:	/* -dictionary */
  3234 	    dataType = DICTIONARY;
  3235 	    break;
  3236 	case LSEARCH_EXACT:		/* -increasing */
  3237 	    mode = EXACT;
  3238 	    break;
  3239 	case LSEARCH_GLOB:		/* -glob */
  3240 	    mode = GLOB;
  3241 	    break;
  3242 	case LSEARCH_INCREASING:	/* -increasing */
  3243 	    isIncreasing = 1;
  3244 	    break;
  3245 	case LSEARCH_INLINE:		/* -inline */
  3246 	    inlineReturn = 1;
  3247 	    break;
  3248 	case LSEARCH_INTEGER:		/* -integer */
  3249 	    dataType = INTEGER;
  3250 	    break;
  3251 	case LSEARCH_NOT:		/* -not */
  3252 	    negatedMatch = 1;
  3253 	    break;
  3254 	case LSEARCH_REAL:		/* -real */
  3255 	    dataType = REAL;
  3256 	    break;
  3257 	case LSEARCH_REGEXP:		/* -regexp */
  3258 	    mode = REGEXP;
  3259 	    break;
  3260 	case LSEARCH_SORTED:		/* -sorted */
  3261 	    mode = SORTED;
  3262 	    break;
  3263 	case LSEARCH_START:		/* -start */
  3264 	    /*
  3265 	     * If there was a previous -start option, release its saved
  3266 	     * index because it will either be replaced or there will be
  3267 	     * an error.
  3268 	     */
  3269 	    if (startPtr) {
  3270 		Tcl_DecrRefCount(startPtr);
  3271 	    }
  3272 	    if (i > objc-4) {
  3273 		Tcl_AppendResult(interp, "missing starting index", NULL);
  3274 		return TCL_ERROR;
  3275 	    }
  3276 	    i++;
  3277 	    if (objv[i] == objv[objc - 2]) {
  3278 		/*
  3279 		 * Take copy to prevent shimmering problems.  Note
  3280 		 * that it does not matter if the index obj is also a
  3281 		 * component of the list being searched.  We only need
  3282 		 * to copy where the list and the index are
  3283 		 * one-and-the-same.
  3284 		 */
  3285 		startPtr = Tcl_DuplicateObj(objv[i]);
  3286 	    } else {
  3287 		startPtr = objv[i];
  3288 		Tcl_IncrRefCount(startPtr);
  3289 	    }
  3290 	}
  3291     }
  3292 
  3293     if ((enum modes) mode == REGEXP) {
  3294 	/*
  3295 	 * We can shimmer regexp/list if listv[i] == pattern, so get the
  3296 	 * regexp rep before the list rep. First time round, omit the interp
  3297          * and hope that the compilation will succeed. If it fails, we'll
  3298          * recompile in "expensive" mode with a place to put error messages.
  3299 	 */
  3300 
  3301 	regexp = Tcl_GetRegExpFromObj(NULL, objv[objc - 1],
  3302 		TCL_REG_ADVANCED | TCL_REG_NOSUB);
  3303 	if (regexp == NULL) {
  3304             /*
  3305              * Failed to compile the RE. Try again without the TCL_REG_NOSUB
  3306              * flag in case the RE had sub-expressions in it [Bug 1366683].
  3307              * If this fails, an error message will be left in the
  3308              * interpreter.
  3309              */
  3310 
  3311             regexp = Tcl_GetRegExpFromObj(interp, objv[objc - 1],
  3312 		    TCL_REG_ADVANCED);
  3313 	}
  3314 
  3315 	if (regexp == NULL) {
  3316 	    if (startPtr) {
  3317 		Tcl_DecrRefCount(startPtr);
  3318 	    }
  3319 	    return TCL_ERROR;
  3320 	}
  3321     }
  3322 
  3323     /*
  3324      * Make sure the list argument is a list object and get its length and
  3325      * a pointer to its array of element pointers.
  3326      */
  3327 
  3328     result = Tcl_ListObjGetElements(interp, objv[objc - 2], &listc, &listv);
  3329     if (result != TCL_OK) {
  3330 	if (startPtr) {
  3331 	    Tcl_DecrRefCount(startPtr);
  3332 	}
  3333 	return result;
  3334     }
  3335 
  3336     /*
  3337      * Get the user-specified start offset.
  3338      */
  3339     if (startPtr) {
  3340 	result = TclGetIntForIndex(interp, startPtr, listc-1, &offset);
  3341 	Tcl_DecrRefCount(startPtr);
  3342 	if (result != TCL_OK) {
  3343 	    return result;
  3344 	}
  3345 
  3346 	/*
  3347 	 * If the search started past the end of the list, we just return a
  3348 	 * "did not match anything at all" result straight away. [Bug 1374778]
  3349 	 */
  3350 
  3351 	if (offset > listc-1) {
  3352 	    if (allMatches || inlineReturn) {
  3353 		Tcl_ResetResult(interp);
  3354 	    } else {
  3355 		Tcl_SetObjResult(interp, Tcl_NewIntObj(-1));
  3356 	    }
  3357 	    return TCL_OK;
  3358 	}
  3359 	if (offset < 0) {
  3360 	    offset = 0;
  3361 	}
  3362     }
  3363 
  3364     patObj = objv[objc - 1];
  3365     patternBytes = NULL;
  3366     if ((enum modes) mode == EXACT || (enum modes) mode == SORTED) {
  3367 	switch ((enum datatypes) dataType) {
  3368 	case ASCII:
  3369 	case DICTIONARY:
  3370 	    patternBytes = Tcl_GetStringFromObj(patObj, &length);
  3371 	    break;
  3372 	case INTEGER:
  3373 	    result = Tcl_GetIntFromObj(interp, patObj, &patInt);
  3374 	    if (result != TCL_OK) {
  3375 		return result;
  3376 	    }
  3377 	    break;
  3378 	case REAL:
  3379 	    result = Tcl_GetDoubleFromObj(interp, patObj, &patDouble);
  3380 	    if (result != TCL_OK) {
  3381 		return result;
  3382 	    }
  3383 	    break;
  3384 	}
  3385     } else {
  3386 	patternBytes = Tcl_GetStringFromObj(patObj, &length);
  3387     }
  3388 
  3389     /*
  3390      * Set default index value to -1, indicating failure; if we find the
  3391      * item in the course of our search, index will be set to the correct
  3392      * value.
  3393      */
  3394     index = -1;
  3395     match = 0;
  3396 
  3397     if ((enum modes) mode == SORTED && !allMatches && !negatedMatch) {
  3398 	/*
  3399 	 * If the data is sorted, we can do a more intelligent search.
  3400 	 * Note that there is no point in being smart when -all was
  3401 	 * specified; in that case, we have to look at all items anyway,
  3402 	 * and there is no sense in doing this when the match sense is
  3403 	 * inverted.
  3404 	 */
  3405 	lower = offset - 1;
  3406 	upper = listc;
  3407 	while (lower + 1 != upper) {
  3408 	    i = (lower + upper)/2;
  3409 	    switch ((enum datatypes) dataType) {
  3410 	    case ASCII:
  3411 		bytes = Tcl_GetString(listv[i]);
  3412 		match = strcmp(patternBytes, bytes);
  3413 		break;
  3414 	    case DICTIONARY:
  3415 		bytes = Tcl_GetString(listv[i]);
  3416 		match = DictionaryCompare(patternBytes, bytes);
  3417 		break;
  3418 	    case INTEGER:
  3419 		result = Tcl_GetIntFromObj(interp, listv[i], &objInt);
  3420 		if (result != TCL_OK) {
  3421 		    return result;
  3422 		}
  3423 		if (patInt == objInt) {
  3424 		    match = 0;
  3425 		} else if (patInt < objInt) {
  3426 		    match = -1;
  3427 		} else {
  3428 		    match = 1;
  3429 		}
  3430 		break;
  3431 	    case REAL:
  3432 		result = Tcl_GetDoubleFromObj(interp, listv[i], &objDouble);
  3433 		if (result != TCL_OK) {
  3434 		    return result;
  3435 		}
  3436 		if (patDouble == objDouble) {
  3437 		    match = 0;
  3438 		} else if (patDouble < objDouble) {
  3439 		    match = -1;
  3440 		} else {
  3441 		    match = 1;
  3442 		}
  3443 		break;
  3444 	    }
  3445 	    if (match == 0) {
  3446 		/*
  3447 		 * Normally, binary search is written to stop when it
  3448 		 * finds a match.  If there are duplicates of an element in
  3449 		 * the list, our first match might not be the first occurance.
  3450 		 * Consider:  0 0 0 1 1 1 2 2 2
  3451 		 * To maintain consistancy with standard lsearch semantics,
  3452 		 * we must find the leftmost occurance of the pattern in the
  3453 		 * list.  Thus we don't just stop searching here.  This
  3454 		 * variation means that a search always makes log n
  3455 		 * comparisons (normal binary search might "get lucky" with
  3456 		 * an early comparison).
  3457 		 */
  3458 		index = i;
  3459 		upper = i;
  3460 	    } else if (match > 0) {
  3461 		if (isIncreasing) {
  3462 		    lower = i;
  3463 		} else {
  3464 		    upper = i;
  3465 		}
  3466 	    } else {
  3467 		if (isIncreasing) {
  3468 		    upper = i;
  3469 		} else {
  3470 		    lower = i;
  3471 		}
  3472 	    }
  3473 	}
  3474 
  3475     } else {
  3476 	/*
  3477 	 * We need to do a linear search, because (at least one) of:
  3478 	 *   - our matcher can only tell equal vs. not equal
  3479 	 *   - our matching sense is negated
  3480 	 *   - we're building a list of all matched items
  3481 	 */
  3482 	if (allMatches) {
  3483 	    listPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
  3484 	}
  3485 	for (i = offset; i < listc; i++) {
  3486 	    match = 0;
  3487 	    switch ((enum modes) mode) {
  3488 	    case SORTED:
  3489 	    case EXACT:
  3490 		switch ((enum datatypes) dataType) {
  3491 		case ASCII:
  3492 		    bytes = Tcl_GetStringFromObj(listv[i], &elemLen);
  3493 		    if (length == elemLen) {
  3494 			match = (memcmp(bytes, patternBytes,
  3495 				(size_t) length) == 0);
  3496 		    }
  3497 		    break;
  3498 		case DICTIONARY:
  3499 		    bytes = Tcl_GetString(listv[i]);
  3500 		    match = (DictionaryCompare(bytes, patternBytes) == 0);
  3501 		    break;
  3502 		case INTEGER:
  3503 		    result = Tcl_GetIntFromObj(interp, listv[i], &objInt);
  3504 		    if (result != TCL_OK) {
  3505 			if (listPtr) {
  3506 			    Tcl_DecrRefCount(listPtr);
  3507 			}
  3508 			return result;
  3509 		    }
  3510 		    match = (objInt == patInt);
  3511 		    break;
  3512 		case REAL:
  3513 		    result = Tcl_GetDoubleFromObj(interp, listv[i],
  3514 			    &objDouble);
  3515 		    if (result != TCL_OK) {
  3516 			if (listPtr) {
  3517 			    Tcl_DecrRefCount(listPtr);
  3518 			}
  3519 			return result;
  3520 		    }
  3521 		    match = (objDouble == patDouble);
  3522 		    break;
  3523 		}
  3524 		break;
  3525 	    case GLOB:
  3526 		match = Tcl_StringMatch(Tcl_GetString(listv[i]),
  3527 			patternBytes);
  3528 		break;
  3529 	    case REGEXP:
  3530 		match = Tcl_RegExpExecObj(interp, regexp, listv[i], 0, 0, 0);
  3531 		if (match < 0) {
  3532 		    Tcl_DecrRefCount(patObj);
  3533 		    if (listPtr) {
  3534 			Tcl_DecrRefCount(listPtr);
  3535 		    }
  3536 		    return TCL_ERROR;
  3537 		}
  3538 		break;
  3539 	    }
  3540 	    /*
  3541 	     * Invert match condition for -not
  3542 	     */
  3543 	    if (negatedMatch) {
  3544 		match = !match;
  3545 	    }
  3546 	    if (match != 0) {
  3547 		if (!allMatches) {
  3548 		    index = i;
  3549 		    break;
  3550 		} else if (inlineReturn) {
  3551 		    /*
  3552 		     * Note that these appends are not expected to fail.
  3553 		     */
  3554 		    Tcl_ListObjAppendElement(interp, listPtr, listv[i]);
  3555 		} else {
  3556 		    Tcl_ListObjAppendElement(interp, listPtr,
  3557 			    Tcl_NewIntObj(i));
  3558 		}
  3559 	    }
  3560 	}
  3561     }
  3562 
  3563     /*
  3564      * Return everything or a single value.
  3565      */
  3566     if (allMatches) {
  3567 	Tcl_SetObjResult(interp, listPtr);
  3568     } else if (!inlineReturn) {
  3569 	Tcl_SetIntObj(Tcl_GetObjResult(interp), index);
  3570     } else if (index < 0) {
  3571 	/*
  3572 	 * Is this superfluous?  The result should be a blank object
  3573 	 * by default...
  3574 	 */
  3575 	Tcl_SetObjResult(interp, Tcl_NewObj());
  3576     } else {
  3577 	Tcl_SetObjResult(interp, listv[index]);
  3578     }
  3579     return TCL_OK;
  3580 }
  3581 
  3582 /*
  3583  *----------------------------------------------------------------------
  3584  *
  3585  * Tcl_LsetObjCmd --
  3586  *
  3587  *	This procedure is invoked to process the "lset" Tcl command.
  3588  *	See the user documentation for details on what it does.
  3589  *
  3590  * Results:
  3591  *	A standard Tcl result.
  3592  *
  3593  * Side effects:
  3594  *	See the user documentation.
  3595  *
  3596  *----------------------------------------------------------------------
  3597  */
  3598 
  3599 int
  3600 Tcl_LsetObjCmd( clientData, interp, objc, objv )
  3601     ClientData clientData;	/* Not used. */
  3602     Tcl_Interp *interp;		/* Current interpreter. */
  3603     int objc;			/* Number of arguments. */
  3604     Tcl_Obj *CONST objv[];	/* Argument values. */
  3605 {
  3606 
  3607     Tcl_Obj* listPtr;		/* Pointer to the list being altered. */
  3608     Tcl_Obj* finalValuePtr;	/* Value finally assigned to the variable */
  3609 
  3610     /* Check parameter count */
  3611 
  3612     if ( objc < 3 ) {
  3613 	Tcl_WrongNumArgs( interp, 1, objv, "listVar index ?index...? value" );
  3614 	return TCL_ERROR;
  3615     }
  3616 
  3617     /* Look up the list variable's value */
  3618 
  3619     listPtr = Tcl_ObjGetVar2( interp, objv[ 1 ], (Tcl_Obj*) NULL,
  3620 			      TCL_LEAVE_ERR_MSG );
  3621     if ( listPtr == NULL ) {
  3622 	return TCL_ERROR;
  3623     }
  3624 
  3625     /* 
  3626      * Substitute the value in the value.  Return either the value or
  3627      * else an unshared copy of it.
  3628      */
  3629 
  3630     if ( objc == 4 ) {
  3631 	finalValuePtr = TclLsetList( interp, listPtr,
  3632 				     objv[ 2 ], objv[ 3 ] );
  3633     } else {
  3634 	finalValuePtr = TclLsetFlat( interp, listPtr,
  3635 				     objc-3, objv+2, objv[ objc-1 ] );
  3636     }
  3637 
  3638     /*
  3639      * If substitution has failed, bail out.
  3640      */
  3641 
  3642     if ( finalValuePtr == NULL ) {
  3643 	return TCL_ERROR;
  3644     }
  3645 
  3646     /* Finally, update the variable so that traces fire. */
  3647 
  3648     listPtr = Tcl_ObjSetVar2( interp, objv[1], NULL, finalValuePtr,
  3649 			      TCL_LEAVE_ERR_MSG );
  3650     Tcl_DecrRefCount( finalValuePtr );
  3651     if ( listPtr == NULL ) {
  3652 	return TCL_ERROR;
  3653     }
  3654 
  3655     /* Return the new value of the variable as the interpreter result. */
  3656 
  3657     Tcl_SetObjResult( interp, listPtr );
  3658     return TCL_OK;
  3659 
  3660 }
  3661 
  3662 /*
  3663  *----------------------------------------------------------------------
  3664  *
  3665  * Tcl_LsortObjCmd --
  3666  *
  3667  *	This procedure is invoked to process the "lsort" Tcl command.
  3668  *	See the user documentation for details on what it does.
  3669  *
  3670  * Results:
  3671  *	A standard Tcl result.
  3672  *
  3673  * Side effects:
  3674  *	See the user documentation.
  3675  *
  3676  *----------------------------------------------------------------------
  3677  */
  3678 
  3679 int
  3680 Tcl_LsortObjCmd(clientData, interp, objc, objv)
  3681     ClientData clientData;	/* Not used. */
  3682     Tcl_Interp *interp;		/* Current interpreter. */
  3683     int objc;			/* Number of arguments. */
  3684     Tcl_Obj *CONST objv[];	/* Argument values. */
  3685 {
  3686     int i, index, unique;
  3687     Tcl_Obj *resultPtr;
  3688     int length;
  3689     Tcl_Obj *cmdPtr, **listObjPtrs;
  3690     SortElement *elementArray;
  3691     SortElement *elementPtr;        
  3692     SortInfo sortInfo;                  /* Information about this sort that
  3693                                          * needs to be passed to the 
  3694                                          * comparison function */
  3695     static CONST char *switches[] = {
  3696 	"-ascii", "-command", "-decreasing", "-dictionary", "-increasing",
  3697 	"-index", "-integer", "-real", "-unique", (char *) NULL
  3698     };
  3699 
  3700     resultPtr = Tcl_GetObjResult(interp);
  3701     if (objc < 2) {
  3702 	Tcl_WrongNumArgs(interp, 1, objv, "?options? list");
  3703 	return TCL_ERROR;
  3704     }
  3705 
  3706     /*
  3707      * Parse arguments to set up the mode for the sort.
  3708      */
  3709 
  3710     sortInfo.isIncreasing = 1;
  3711     sortInfo.sortMode = SORTMODE_ASCII;
  3712     sortInfo.index = SORTIDX_NONE;
  3713     sortInfo.interp = interp;
  3714     sortInfo.resultCode = TCL_OK;
  3715     cmdPtr = NULL;
  3716     unique = 0;
  3717     for (i = 1; i < objc-1; i++) {
  3718 	if (Tcl_GetIndexFromObj(interp, objv[i], switches, "option", 0, &index)
  3719 		!= TCL_OK) {
  3720 	    return TCL_ERROR;
  3721 	}
  3722 	switch (index) {
  3723 	    case 0:			/* -ascii */
  3724 		sortInfo.sortMode = SORTMODE_ASCII;
  3725 		break;
  3726 	    case 1:			/* -command */
  3727 		if (i == (objc-2)) {
  3728 		    Tcl_AppendToObj(resultPtr,
  3729 			    "\"-command\" option must be followed by comparison command",
  3730 			    -1);
  3731 		    return TCL_ERROR;
  3732 		}
  3733 		sortInfo.sortMode = SORTMODE_COMMAND;
  3734 		cmdPtr = objv[i+1];
  3735 		i++;
  3736 		break;
  3737 	    case 2:			/* -decreasing */
  3738 		sortInfo.isIncreasing = 0;
  3739 		break;
  3740 	    case 3:			/* -dictionary */
  3741 		sortInfo.sortMode = SORTMODE_DICTIONARY;
  3742 		break;
  3743 	    case 4:			/* -increasing */
  3744 		sortInfo.isIncreasing = 1;
  3745 		break;
  3746 	    case 5:			/* -index */
  3747 		if (i == (objc-2)) {
  3748 		    Tcl_AppendToObj(resultPtr,
  3749 			    "\"-index\" option must be followed by list index",
  3750 			    -1);
  3751 		    return TCL_ERROR;
  3752 		}
  3753 		if (TclGetIntForIndex(interp, objv[i+1], SORTIDX_END,
  3754 			&sortInfo.index) != TCL_OK) {
  3755 		    return TCL_ERROR;
  3756 		}
  3757 		i++;
  3758 		break;
  3759 	    case 6:			/* -integer */
  3760 		sortInfo.sortMode = SORTMODE_INTEGER;
  3761 		break;
  3762 	    case 7:			/* -real */
  3763 		sortInfo.sortMode = SORTMODE_REAL;
  3764 		break;
  3765 	    case 8:			/* -unique */
  3766 		unique = 1;
  3767 		break;
  3768 	}
  3769     }
  3770     if (sortInfo.sortMode == SORTMODE_COMMAND) {
  3771 	/*
  3772 	 * The existing command is a list. We want to flatten it, append
  3773 	 * two dummy arguments on the end, and replace these arguments
  3774 	 * later.
  3775 	 */
  3776 
  3777         Tcl_Obj *newCommandPtr = Tcl_DuplicateObj(cmdPtr);
  3778 	Tcl_Obj *newObjPtr = Tcl_NewObj();
  3779 
  3780 	Tcl_IncrRefCount(newCommandPtr);
  3781 	if (Tcl_ListObjAppendElement(interp, newCommandPtr, newObjPtr)
  3782 		!= TCL_OK) {
  3783 	    Tcl_DecrRefCount(newCommandPtr);
  3784 	    Tcl_IncrRefCount(newObjPtr);
  3785 	    Tcl_DecrRefCount(newObjPtr);
  3786 	    return TCL_ERROR;
  3787 	}
  3788 	Tcl_ListObjAppendElement(interp, newCommandPtr, Tcl_NewObj());
  3789 	sortInfo.compareCmdPtr = newCommandPtr;
  3790     }
  3791 
  3792     sortInfo.resultCode = Tcl_ListObjGetElements(interp, objv[objc-1],
  3793 	    &length, &listObjPtrs);
  3794     if (sortInfo.resultCode != TCL_OK || length <= 0) {
  3795 	goto done;
  3796     }
  3797     elementArray = (SortElement *) ckalloc(length * sizeof(SortElement));
  3798     for (i=0; i < length; i++){
  3799 	elementArray[i].objPtr = listObjPtrs[i];
  3800 	elementArray[i].count = 0;
  3801 	elementArray[i].nextPtr = &elementArray[i+1];
  3802 
  3803 	/*
  3804 	 * When sorting using a command, we are reentrant and therefore might
  3805 	 * have the representation of the list being sorted shimmered out from
  3806 	 * underneath our feet. Increment the reference counts of the elements
  3807 	 * to sort to prevent this. [Bug 1675116]
  3808 	 */
  3809 
  3810 	Tcl_IncrRefCount(elementArray[i].objPtr);
  3811     }
  3812     elementArray[length-1].nextPtr = NULL;
  3813     elementPtr = MergeSort(elementArray, &sortInfo);
  3814     if (sortInfo.resultCode == TCL_OK) {
  3815 	/*
  3816 	 * Note: must clear the interpreter's result object: it could
  3817 	 * have been set by the -command script.
  3818 	 */
  3819 
  3820 	Tcl_ResetResult(interp);
  3821 	resultPtr = Tcl_GetObjResult(interp);
  3822 	if (unique) {
  3823 	    for (; elementPtr != NULL; elementPtr = elementPtr->nextPtr){
  3824 		if (elementPtr->count == 0) {
  3825 		    Tcl_ListObjAppendElement(interp, resultPtr,
  3826 			    elementPtr->objPtr);
  3827 		}
  3828 	    }
  3829 	} else {
  3830 	    for (; elementPtr != NULL; elementPtr = elementPtr->nextPtr){
  3831 		Tcl_ListObjAppendElement(interp, resultPtr,
  3832 			elementPtr->objPtr);
  3833 	    }
  3834 	}
  3835     }
  3836     for (i=0; i<length; i++) {
  3837 	Tcl_DecrRefCount(elementArray[i].objPtr);
  3838     }
  3839     ckfree((char*) elementArray);
  3840 
  3841     done:
  3842     if (sortInfo.sortMode == SORTMODE_COMMAND) {
  3843 	Tcl_DecrRefCount(sortInfo.compareCmdPtr);
  3844 	sortInfo.compareCmdPtr = NULL;
  3845     }
  3846     return sortInfo.resultCode;
  3847 }
  3848 
  3849 /*
  3850  *----------------------------------------------------------------------
  3851  *
  3852  * MergeSort -
  3853  *
  3854  *	This procedure sorts a linked list of SortElement structures
  3855  *	use the merge-sort algorithm.
  3856  *
  3857  * Results:
  3858  *      A pointer to the head of the list after sorting is returned.
  3859  *
  3860  * Side effects:
  3861  *	None, unless a user-defined comparison command does something
  3862  *	weird.
  3863  *
  3864  *----------------------------------------------------------------------
  3865  */
  3866 
  3867 static SortElement *
  3868 MergeSort(headPtr, infoPtr)
  3869     SortElement *headPtr;               /* First element on the list */
  3870     SortInfo *infoPtr;                  /* Information needed by the
  3871                                          * comparison operator */
  3872 {
  3873     /*
  3874      * The subList array below holds pointers to temporary lists built
  3875      * during the merge sort.  Element i of the array holds a list of
  3876      * length 2**i.
  3877      */
  3878 
  3879 #   define NUM_LISTS 30
  3880     SortElement *subList[NUM_LISTS];
  3881     SortElement *elementPtr;
  3882     int i;
  3883 
  3884     for(i = 0; i < NUM_LISTS; i++){
  3885         subList[i] = NULL;
  3886     }
  3887     while (headPtr != NULL) {
  3888 	elementPtr = headPtr;
  3889 	headPtr = headPtr->nextPtr;
  3890 	elementPtr->nextPtr = 0;
  3891 	for (i = 0; (i < NUM_LISTS) && (subList[i] != NULL); i++){
  3892 	    elementPtr = MergeLists(subList[i], elementPtr, infoPtr);
  3893 	    subList[i] = NULL;
  3894 	}
  3895 	if (i >= NUM_LISTS) {
  3896 	    i = NUM_LISTS-1;
  3897 	}
  3898 	subList[i] = elementPtr;
  3899     }
  3900     elementPtr = NULL;
  3901     for (i = 0; i < NUM_LISTS; i++){
  3902         elementPtr = MergeLists(subList[i], elementPtr, infoPtr);
  3903     }
  3904     return elementPtr;
  3905 }
  3906 
  3907 /*
  3908  *----------------------------------------------------------------------
  3909  *
  3910  * MergeLists -
  3911  *
  3912  *	This procedure combines two sorted lists of SortElement structures
  3913  *	into a single sorted list.
  3914  *
  3915  * Results:
  3916  *      The unified list of SortElement structures.
  3917  *
  3918  * Side effects:
  3919  *	None, unless a user-defined comparison command does something
  3920  *	weird.
  3921  *
  3922  *----------------------------------------------------------------------
  3923  */
  3924 
  3925 static SortElement *
  3926 MergeLists(leftPtr, rightPtr, infoPtr)
  3927     SortElement *leftPtr;               /* First list to be merged; may be
  3928 					 * NULL. */
  3929     SortElement *rightPtr;              /* Second list to be merged; may be
  3930 					 * NULL. */
  3931     SortInfo *infoPtr;                  /* Information needed by the
  3932                                          * comparison operator. */
  3933 {
  3934     SortElement *headPtr;
  3935     SortElement *tailPtr;
  3936     int cmp;
  3937 
  3938     if (leftPtr == NULL) {
  3939         return rightPtr;
  3940     }
  3941     if (rightPtr == NULL) {
  3942         return leftPtr;
  3943     }
  3944     cmp = SortCompare(leftPtr->objPtr, rightPtr->objPtr, infoPtr);
  3945     if (cmp > 0) {
  3946 	tailPtr = rightPtr;
  3947 	rightPtr = rightPtr->nextPtr;
  3948     } else {
  3949 	if (cmp == 0) {
  3950 	    leftPtr->count++;
  3951 	}
  3952 	tailPtr = leftPtr;
  3953 	leftPtr = leftPtr->nextPtr;
  3954     }
  3955     headPtr = tailPtr;
  3956     while ((leftPtr != NULL) && (rightPtr != NULL)) {
  3957 	cmp = SortCompare(leftPtr->objPtr, rightPtr->objPtr, infoPtr);
  3958 	if (cmp > 0) {
  3959 	    tailPtr->nextPtr = rightPtr;
  3960 	    tailPtr = rightPtr;
  3961 	    rightPtr = rightPtr->nextPtr;
  3962 	} else {
  3963 	    if (cmp == 0) {
  3964 		leftPtr->count++;
  3965 	    }
  3966 	    tailPtr->nextPtr = leftPtr;
  3967 	    tailPtr = leftPtr;
  3968 	    leftPtr = leftPtr->nextPtr;
  3969 	}
  3970     }
  3971     if (leftPtr != NULL) {
  3972        tailPtr->nextPtr = leftPtr;
  3973     } else {
  3974        tailPtr->nextPtr = rightPtr;
  3975     }
  3976     return headPtr;
  3977 }
  3978 
  3979 /*
  3980  *----------------------------------------------------------------------
  3981  *
  3982  * SortCompare --
  3983  *
  3984  *	This procedure is invoked by MergeLists to determine the proper
  3985  *	ordering between two elements.
  3986  *
  3987  * Results:
  3988  *      A negative results means the the first element comes before the
  3989  *      second, and a positive results means that the second element
  3990  *      should come first.  A result of zero means the two elements
  3991  *      are equal and it doesn't matter which comes first.
  3992  *
  3993  * Side effects:
  3994  *	None, unless a user-defined comparison command does something
  3995  *	weird.
  3996  *
  3997  *----------------------------------------------------------------------
  3998  */
  3999 
  4000 static int
  4001 SortCompare(objPtr1, objPtr2, infoPtr)
  4002     Tcl_Obj *objPtr1, *objPtr2;		/* Values to be compared. */
  4003     SortInfo *infoPtr;                  /* Information passed from the
  4004                                          * top-level "lsort" command */
  4005 {
  4006     int order, listLen, index;
  4007     Tcl_Obj *objPtr;
  4008     char buffer[TCL_INTEGER_SPACE];
  4009 
  4010     order = 0;
  4011     if (infoPtr->resultCode != TCL_OK) {
  4012 	/*
  4013 	 * Once an error has occurred, skip any future comparisons
  4014 	 * so as to preserve the error message in sortInterp->result.
  4015 	 */
  4016 
  4017 	return order;
  4018     }
  4019     if (infoPtr->index != SORTIDX_NONE) {
  4020 	/*
  4021 	 * The "-index" option was specified.  Treat each object as a
  4022 	 * list, extract the requested element from each list, and
  4023 	 * compare the elements, not the lists.  "end"-relative indices
  4024 	 * are signaled here with large negative values.
  4025 	 */
  4026 
  4027 	if (Tcl_ListObjLength(infoPtr->interp, objPtr1, &listLen) != TCL_OK) {
  4028 	    infoPtr->resultCode = TCL_ERROR;
  4029 	    return order;
  4030 	}
  4031 	if (infoPtr->index < SORTIDX_NONE) {
  4032 	    index = listLen + infoPtr->index + 1;
  4033 	} else {
  4034 	    index = infoPtr->index;
  4035 	}
  4036 
  4037 	if (Tcl_ListObjIndex(infoPtr->interp, objPtr1, index, &objPtr)
  4038 		!= TCL_OK) {
  4039 	    infoPtr->resultCode = TCL_ERROR;
  4040 	    return order;
  4041 	}
  4042 	if (objPtr == NULL) {
  4043 	    objPtr = objPtr1;
  4044 	    missingElement:
  4045 	    TclFormatInt(buffer, infoPtr->index);
  4046 	    Tcl_AppendStringsToObj(Tcl_GetObjResult(infoPtr->interp),
  4047 			"element ", buffer, " missing from sublist \"",
  4048 			Tcl_GetString(objPtr), "\"", (char *) NULL);
  4049 	    infoPtr->resultCode = TCL_ERROR;
  4050 	    return order;
  4051 	}
  4052 	objPtr1 = objPtr;
  4053 
  4054 	if (Tcl_ListObjLength(infoPtr->interp, objPtr2, &listLen) != TCL_OK) {
  4055 	    infoPtr->resultCode = TCL_ERROR;
  4056 	    return order;
  4057 	}
  4058 	if (infoPtr->index < SORTIDX_NONE) {
  4059 	    index = listLen + infoPtr->index + 1;
  4060 	} else {
  4061 	    index = infoPtr->index;
  4062 	}
  4063 
  4064 	if (Tcl_ListObjIndex(infoPtr->interp, objPtr2, index, &objPtr)
  4065 		!= TCL_OK) {
  4066 	    infoPtr->resultCode = TCL_ERROR;
  4067 	    return order;
  4068 	}
  4069 	if (objPtr == NULL) {
  4070 	    objPtr = objPtr2;
  4071 	    goto missingElement;
  4072 	}
  4073 	objPtr2 = objPtr;
  4074     }
  4075     if (infoPtr->sortMode == SORTMODE_ASCII) {
  4076 	order = strcmp(Tcl_GetString(objPtr1), Tcl_GetString(objPtr2));
  4077     } else if (infoPtr->sortMode == SORTMODE_DICTIONARY) {
  4078 	order = DictionaryCompare(
  4079 		Tcl_GetString(objPtr1),	Tcl_GetString(objPtr2));
  4080     } else if (infoPtr->sortMode == SORTMODE_INTEGER) {
  4081 	long a, b;
  4082 
  4083 	if ((Tcl_GetLongFromObj(infoPtr->interp, objPtr1, &a) != TCL_OK)
  4084 		|| (Tcl_GetLongFromObj(infoPtr->interp, objPtr2, &b)
  4085 		!= TCL_OK)) {
  4086 	    infoPtr->resultCode = TCL_ERROR;
  4087 	    return order;
  4088 	}
  4089 	if (a > b) {
  4090 	    order = 1;
  4091 	} else if (b > a) {
  4092 	    order = -1;
  4093 	}
  4094     } else if (infoPtr->sortMode == SORTMODE_REAL) {
  4095 	double a, b;
  4096 
  4097 	if ((Tcl_GetDoubleFromObj(infoPtr->interp, objPtr1, &a) != TCL_OK)
  4098 	      || (Tcl_GetDoubleFromObj(infoPtr->interp, objPtr2, &b)
  4099 		      != TCL_OK)) {
  4100 	    infoPtr->resultCode = TCL_ERROR;
  4101 	    return order;
  4102 	}
  4103 	if (a > b) {
  4104 	    order = 1;
  4105 	} else if (b > a) {
  4106 	    order = -1;
  4107 	}
  4108     } else {
  4109 	Tcl_Obj **objv, *paramObjv[2];
  4110 	int objc;
  4111 
  4112 	paramObjv[0] = objPtr1;
  4113 	paramObjv[1] = objPtr2;
  4114 
  4115   	/*
  4116  	 * We made space in the command list for the two things to
  4117 	 * compare. Replace them and evaluate the result.
  4118 	 */
  4119 
  4120 	Tcl_ListObjLength(infoPtr->interp, infoPtr->compareCmdPtr, &objc);
  4121 	Tcl_ListObjReplace(infoPtr->interp, infoPtr->compareCmdPtr, objc - 2,
  4122 		2, 2, paramObjv);
  4123    	Tcl_ListObjGetElements(infoPtr->interp, infoPtr->compareCmdPtr,
  4124 		&objc, &objv);
  4125 
  4126 	infoPtr->resultCode = Tcl_EvalObjv(infoPtr->interp, objc, objv, 0);
  4127   
  4128   	if (infoPtr->resultCode != TCL_OK) {
  4129 	    Tcl_AddErrorInfo(infoPtr->interp,
  4130 		    "\n    (-compare command)");
  4131 	    return order;
  4132 	}
  4133 
  4134 	/*
  4135 	 * Parse the result of the command.
  4136 	 */
  4137 
  4138 	if (Tcl_GetIntFromObj(infoPtr->interp,
  4139 		Tcl_GetObjResult(infoPtr->interp), &order) != TCL_OK) {
  4140 	    Tcl_ResetResult(infoPtr->interp);
  4141 	    Tcl_AppendToObj(Tcl_GetObjResult(infoPtr->interp),
  4142 		    "-compare command returned non-integer result", -1);
  4143 	    infoPtr->resultCode = TCL_ERROR;
  4144 	    return order;
  4145 	}
  4146     }
  4147     if (!infoPtr->isIncreasing) {
  4148 	order = -order;
  4149     }
  4150     return order;
  4151 }
  4152 
  4153 /*
  4154  *----------------------------------------------------------------------
  4155  *
  4156  * DictionaryCompare
  4157  *
  4158  *	This function compares two strings as if they were being used in
  4159  *	an index or card catalog.  The case of alphabetic characters is
  4160  *	ignored, except to break ties.  Thus "B" comes before "b" but
  4161  *	after "a".  Also, integers embedded in the strings compare in
  4162  *	numerical order.  In other words, "x10y" comes after "x9y", not
  4163  *      before it as it would when using strcmp().
  4164  *
  4165  * Results:
  4166  *      A negative result means that the first element comes before the
  4167  *      second, and a positive result means that the second element
  4168  *      should come first.  A result of zero means the two elements
  4169  *      are equal and it doesn't matter which comes first.
  4170  *
  4171  * Side effects:
  4172  *	None.
  4173  *
  4174  *----------------------------------------------------------------------
  4175  */
  4176 
  4177 static int
  4178 DictionaryCompare(left, right)
  4179     char *left, *right;          /* The strings to compare */
  4180 {
  4181     Tcl_UniChar uniLeft, uniRight, uniLeftLower, uniRightLower;
  4182     int diff, zeros;
  4183     int secondaryDiff = 0;
  4184 
  4185     while (1) {
  4186 	if (isdigit(UCHAR(*right)) /* INTL: digit */
  4187 		&& isdigit(UCHAR(*left))) { /* INTL: digit */
  4188 	    /*
  4189 	     * There are decimal numbers embedded in the two
  4190 	     * strings.  Compare them as numbers, rather than
  4191 	     * strings.  If one number has more leading zeros than
  4192 	     * the other, the number with more leading zeros sorts
  4193 	     * later, but only as a secondary choice.
  4194 	     */
  4195 
  4196 	    zeros = 0;
  4197 	    while ((*right == '0') && (isdigit(UCHAR(right[1])))) {
  4198 		right++;
  4199 		zeros--;
  4200 	    }
  4201 	    while ((*left == '0') && (isdigit(UCHAR(left[1])))) {
  4202 		left++;
  4203 		zeros++;
  4204 	    }
  4205 	    if (secondaryDiff == 0) {
  4206 		secondaryDiff = zeros;
  4207 	    }
  4208 
  4209 	    /*
  4210 	     * The code below compares the numbers in the two
  4211 	     * strings without ever converting them to integers.  It
  4212 	     * does this by first comparing the lengths of the
  4213 	     * numbers and then comparing the digit values.
  4214 	     */
  4215 
  4216 	    diff = 0;
  4217 	    while (1) {
  4218 		if (diff == 0) {
  4219 		    diff = UCHAR(*left) - UCHAR(*right);
  4220 		}
  4221 		right++;
  4222 		left++;
  4223 		if (!isdigit(UCHAR(*right))) { /* INTL: digit */
  4224 		    if (isdigit(UCHAR(*left))) { /* INTL: digit */
  4225 			return 1;
  4226 		    } else {
  4227 			/*
  4228 			 * The two numbers have the same length. See
  4229 			 * if their values are different.
  4230 			 */
  4231 
  4232 			if (diff != 0) {
  4233 			    return diff;
  4234 			}
  4235 			break;
  4236 		    }
  4237 		} else if (!isdigit(UCHAR(*left))) { /* INTL: digit */
  4238 		    return -1;
  4239 		}
  4240 	    }
  4241 	    continue;
  4242 	}
  4243 
  4244 	/*
  4245 	 * Convert character to Unicode for comparison purposes.  If either
  4246 	 * string is at the terminating null, do a byte-wise comparison and
  4247 	 * bail out immediately.
  4248 	 */
  4249 
  4250 	if ((*left != '\0') && (*right != '\0')) {
  4251 	    left += Tcl_UtfToUniChar(left, &uniLeft);
  4252 	    right += Tcl_UtfToUniChar(right, &uniRight);
  4253 	    /*
  4254 	     * Convert both chars to lower for the comparison, because
  4255 	     * dictionary sorts are case insensitve.  Covert to lower, not
  4256 	     * upper, so chars between Z and a will sort before A (where most
  4257 	     * other interesting punctuations occur)
  4258 	     */
  4259 	    uniLeftLower = Tcl_UniCharToLower(uniLeft);
  4260 	    uniRightLower = Tcl_UniCharToLower(uniRight);
  4261 	} else {
  4262 	    diff = UCHAR(*left) - UCHAR(*right);
  4263 	    break;
  4264 	}
  4265 
  4266         diff = uniLeftLower - uniRightLower;
  4267         if (diff) {
  4268 	    return diff;
  4269 	} else if (secondaryDiff == 0) {
  4270 	    if (Tcl_UniCharIsUpper(uniLeft) &&
  4271 		    Tcl_UniCharIsLower(uniRight)) {
  4272 		secondaryDiff = -1;
  4273 	    } else if (Tcl_UniCharIsUpper(uniRight)
  4274 		    && Tcl_UniCharIsLower(uniLeft)) {
  4275 		secondaryDiff = 1;
  4276 	    }
  4277         }
  4278     }
  4279     if (diff == 0) {
  4280 	diff = secondaryDiff;
  4281     }
  4282     return diff;
  4283 }
  4284 
  4285 /*
  4286  * Local Variables:
  4287  * mode: c
  4288  * c-basic-offset: 4
  4289  * fill-column: 78
  4290  * End:
  4291  */
  4292