tclCmdIL.c

Go to the documentation of this file.
00001 /*
00002  * tclCmdIL.c --
00003  *
00004  *      This file contains the top-level command routines for most of the Tcl
00005  *      built-in commands whose names begin with the letters I through L. It
00006  *      contains only commands in the generic core (i.e. those that don't
00007  *      depend much upon UNIX facilities).
00008  *
00009  * Copyright (c) 1987-1993 The Regents of the University of California.
00010  * Copyright (c) 1993-1997 Lucent Technologies.
00011  * Copyright (c) 1994-1997 Sun Microsystems, Inc.
00012  * Copyright (c) 1998-1999 by Scriptics Corporation.
00013  * Copyright (c) 2001 by Kevin B. Kenny. All rights reserved.
00014  * Copyright (c) 2005 Donal K. Fellows.
00015  *
00016  * See the file "license.terms" for information on usage and redistribution of
00017  * this file, and for a DISCLAIMER OF ALL WARRANTIES.
00018  *
00019  * RCS: @(#) $Id: tclCmdIL.c,v 1.136 2008/01/22 11:38:33 msofer Exp $
00020  */
00021 
00022 #include "tclInt.h"
00023 #include "tclRegexp.h"
00024 
00025 /*
00026  * During execution of the "lsort" command, structures of the following type
00027  * are used to arrange the objects being sorted into a collection of linked
00028  * lists.
00029  */
00030 
00031 typedef struct SortElement {
00032     union {
00033         char *strValuePtr;
00034         long   intValue;
00035         double doubleValue;
00036         Tcl_Obj *objValuePtr;
00037     } index;
00038     Tcl_Obj *objPtr;            /* Object being sorted, or its index. */
00039     struct SortElement *nextPtr;/* Next element in the list, or NULL for end
00040                                  * of list. */
00041 } SortElement;
00042 
00043 /*
00044  * These function pointer types are used with the "lsearch" and "lsort"
00045  * commands to facilitate the "-nocase" option.
00046  */
00047 
00048 typedef int (*SortStrCmpFn_t) (const char *, const char *);
00049 typedef int (*SortMemCmpFn_t) (const void *, const void *, size_t);
00050 
00051 /*
00052  * The "lsort" command needs to pass certain information down to the function
00053  * that compares two list elements, and the comparison function needs to pass
00054  * success or failure information back up to the top-level "lsort" command.
00055  * The following structure is used to pass this information.
00056  */
00057 
00058 typedef struct SortInfo {
00059     int isIncreasing;           /* Nonzero means sort in increasing order. */
00060     int sortMode;               /* The sort mode. One of SORTMODE_* values
00061                                  * defined below. */
00062     Tcl_Obj *compareCmdPtr;     /* The Tcl comparison command when sortMode is
00063                                  * SORTMODE_COMMAND. Pre-initialized to hold
00064                                  * base of command. */
00065     int *indexv;                /* If the -index option was specified, this
00066                                  * holds the indexes contained in the list
00067                                  * supplied as an argument to that option.
00068                                  * NULL if no indexes supplied, and points to
00069                                  * singleIndex field when only one
00070                                  * supplied. */
00071     int indexc;                 /* Number of indexes in indexv array. */
00072     int singleIndex;            /* Static space for common index case. */
00073     int unique;
00074     int numElements;
00075     Tcl_Interp *interp;         /* The interpreter in which the sort is being
00076                                  * done. */
00077     int resultCode;             /* Completion code for the lsort command. If
00078                                  * an error occurs during the sort this is
00079                                  * changed from TCL_OK to TCL_ERROR. */
00080 } SortInfo;
00081 
00082 /*
00083  * The "sortMode" field of the SortInfo structure can take on any of the
00084  * following values.
00085  */
00086 
00087 #define SORTMODE_ASCII          0
00088 #define SORTMODE_INTEGER        1
00089 #define SORTMODE_REAL           2
00090 #define SORTMODE_COMMAND        3
00091 #define SORTMODE_DICTIONARY     4
00092 #define SORTMODE_ASCII_NC       8
00093 
00094 /*
00095  * Magic values for the index field of the SortInfo structure. Note that the
00096  * index "end-1" will be translated to SORTIDX_END-1, etc.
00097  */
00098 
00099 #define SORTIDX_NONE    -1      /* Not indexed; use whole value. */
00100 #define SORTIDX_END     -2      /* Indexed from end. */
00101 
00102 /*
00103  * Forward declarations for procedures defined in this file:
00104  */
00105 
00106 static int              DictionaryCompare(char *left, char *right);
00107 static int              InfoArgsCmd(ClientData dummy, Tcl_Interp *interp,
00108                             int objc, Tcl_Obj *CONST objv[]);
00109 static int              InfoBodyCmd(ClientData dummy, Tcl_Interp *interp,
00110                             int objc, Tcl_Obj *CONST objv[]);
00111 static int              InfoCmdCountCmd(ClientData dummy, Tcl_Interp *interp,
00112                             int objc, Tcl_Obj *CONST objv[]);
00113 static int              InfoCommandsCmd(ClientData dummy, Tcl_Interp *interp,
00114                             int objc, Tcl_Obj *CONST objv[]);
00115 static int              InfoCompleteCmd(ClientData dummy, Tcl_Interp *interp,
00116                             int objc, Tcl_Obj *CONST objv[]);
00117 static int              InfoDefaultCmd(ClientData dummy, Tcl_Interp *interp,
00118                             int objc, Tcl_Obj *CONST objv[]);
00119 /* TIP #280 - New 'info' subcommand 'frame' */
00120 static int              InfoFrameCmd(ClientData dummy, Tcl_Interp *interp,
00121                             int objc, Tcl_Obj *CONST objv[]);
00122 static int              InfoFunctionsCmd(ClientData dummy, Tcl_Interp *interp,
00123                             int objc, Tcl_Obj *CONST objv[]);
00124 static int              InfoHostnameCmd(ClientData dummy, Tcl_Interp *interp,
00125                             int objc, Tcl_Obj *CONST objv[]);
00126 static int              InfoLevelCmd(ClientData dummy, Tcl_Interp *interp,
00127                             int objc, Tcl_Obj *CONST objv[]);
00128 static int              InfoLibraryCmd(ClientData dummy, Tcl_Interp *interp,
00129                             int objc, Tcl_Obj *CONST objv[]);
00130 static int              InfoLoadedCmd(ClientData dummy, Tcl_Interp *interp,
00131                             int objc, Tcl_Obj *CONST objv[]);
00132 static int              InfoNameOfExecutableCmd(ClientData dummy,
00133                             Tcl_Interp *interp, int objc,
00134                             Tcl_Obj *CONST objv[]);
00135 static int              InfoPatchLevelCmd(ClientData dummy, Tcl_Interp *interp,
00136                             int objc, Tcl_Obj *CONST objv[]);
00137 static int              InfoProcsCmd(ClientData dummy, Tcl_Interp *interp,
00138                             int objc, Tcl_Obj *CONST objv[]);
00139 static int              InfoScriptCmd(ClientData dummy, Tcl_Interp *interp,
00140                             int objc, Tcl_Obj *CONST objv[]);
00141 static int              InfoSharedlibCmd(ClientData dummy, Tcl_Interp *interp,
00142                             int objc, Tcl_Obj *CONST objv[]);
00143 static int              InfoTclVersionCmd(ClientData dummy, Tcl_Interp *interp,
00144                             int objc, Tcl_Obj *CONST objv[]);
00145 static SortElement *    MergeLists(SortElement *leftPtr, SortElement *rightPtr,
00146                             SortInfo *infoPtr);
00147 static int              SortCompare(SortElement *firstPtr, SortElement *second,
00148                             SortInfo *infoPtr);
00149 static Tcl_Obj *        SelectObjFromSublist(Tcl_Obj *firstPtr,
00150                             SortInfo *infoPtr);
00151 
00152 /*
00153  * Array of values describing how to implement each standard subcommand of the
00154  * "info" command.
00155  */
00156 
00157 static const EnsembleImplMap defaultInfoMap[] = {
00158     {"args",               InfoArgsCmd,             NULL},
00159     {"body",               InfoBodyCmd,             NULL},
00160     {"cmdcount",           InfoCmdCountCmd,         NULL},
00161     {"commands",           InfoCommandsCmd,         NULL},
00162     {"complete",           InfoCompleteCmd,         NULL},
00163     {"default",            InfoDefaultCmd,          NULL},
00164     {"exists",             TclInfoExistsCmd,        TclCompileInfoExistsCmd},
00165     {"frame",              InfoFrameCmd,            NULL},
00166     {"functions",          InfoFunctionsCmd,        NULL},
00167     {"globals",            TclInfoGlobalsCmd,       NULL},
00168     {"hostname",           InfoHostnameCmd,         NULL},
00169     {"level",              InfoLevelCmd,            NULL},
00170     {"library",            InfoLibraryCmd,          NULL},
00171     {"loaded",             InfoLoadedCmd,           NULL},
00172     {"locals",             TclInfoLocalsCmd,        NULL},
00173     {"nameofexecutable",   InfoNameOfExecutableCmd, NULL},
00174     {"patchlevel",         InfoPatchLevelCmd,       NULL},
00175     {"procs",              InfoProcsCmd,            NULL},
00176     {"script",             InfoScriptCmd,           NULL},
00177     {"sharedlibextension", InfoSharedlibCmd,        NULL},
00178     {"tclversion",         InfoTclVersionCmd,       NULL},
00179     {"vars",               TclInfoVarsCmd,          NULL},
00180     {NULL, NULL, NULL}
00181 };
00182 
00183 /*
00184  *----------------------------------------------------------------------
00185  *
00186  * Tcl_IfObjCmd --
00187  *
00188  *      This procedure is invoked to process the "if" Tcl command. See the
00189  *      user documentation for details on what it does.
00190  *
00191  *      With the bytecode compiler, this procedure is only called when a
00192  *      command name is computed at runtime, and is "if" or the name to which
00193  *      "if" was renamed: e.g., "set z if; $z 1 {puts foo}"
00194  *
00195  * Results:
00196  *      A standard Tcl result.
00197  *
00198  * Side effects:
00199  *      See the user documentation.
00200  *
00201  *----------------------------------------------------------------------
00202  */
00203 
00204 int
00205 Tcl_IfObjCmd(
00206     ClientData dummy,           /* Not used. */
00207     Tcl_Interp *interp,         /* Current interpreter. */
00208     int objc,                   /* Number of arguments. */
00209     Tcl_Obj *CONST objv[])      /* Argument objects. */
00210 {
00211     int thenScriptIndex = 0;    /* "then" script to be evaled after syntax
00212                                  * check. */
00213     Interp *iPtr = (Interp *) interp;
00214     int i, result, value;
00215     char *clause;
00216 
00217     i = 1;
00218     while (1) {
00219         /*
00220          * At this point in the loop, objv and objc refer to an expression to
00221          * test, either for the main expression or an expression following an
00222          * "elseif". The arguments after the expression must be "then"
00223          * (optional) and a script to execute if the expression is true.
00224          */
00225 
00226         if (i >= objc) {
00227             clause = TclGetString(objv[i-1]);
00228             Tcl_AppendResult(interp, "wrong # args: ",
00229                     "no expression after \"", clause, "\" argument", NULL);
00230             return TCL_ERROR;
00231         }
00232         if (!thenScriptIndex) {
00233             result = Tcl_ExprBooleanObj(interp, objv[i], &value);
00234             if (result != TCL_OK) {
00235                 return result;
00236             }
00237         }
00238         i++;
00239         if (i >= objc) {
00240         missingScript:
00241             clause = TclGetString(objv[i-1]);
00242             Tcl_AppendResult(interp, "wrong # args: ",
00243                     "no script following \"", clause, "\" argument", NULL);
00244             return TCL_ERROR;
00245         }
00246         clause = TclGetString(objv[i]);
00247         if ((i < objc) && (strcmp(clause, "then") == 0)) {
00248             i++;
00249         }
00250         if (i >= objc) {
00251             goto missingScript;
00252         }
00253         if (value) {
00254             thenScriptIndex = i;
00255             value = 0;
00256         }
00257 
00258         /*
00259          * The expression evaluated to false. Skip the command, then see if
00260          * there is an "else" or "elseif" clause.
00261          */
00262 
00263         i++;
00264         if (i >= objc) {
00265             if (thenScriptIndex) {
00266                 /*
00267                  * TIP #280. Make invoking context available to branch.
00268                  */
00269 
00270                 return TclEvalObjEx(interp, objv[thenScriptIndex], 0,
00271                         iPtr->cmdFramePtr, thenScriptIndex);
00272             }
00273             return TCL_OK;
00274         }
00275         clause = TclGetString(objv[i]);
00276         if ((clause[0] == 'e') && (strcmp(clause, "elseif") == 0)) {
00277             i++;
00278             continue;
00279         }
00280         break;
00281     }
00282 
00283     /*
00284      * Couldn't find a "then" or "elseif" clause to execute. Check now for an
00285      * "else" clause. We know that there's at least one more argument when we
00286      * get here.
00287      */
00288 
00289     if (strcmp(clause, "else") == 0) {
00290         i++;
00291         if (i >= objc) {
00292             Tcl_AppendResult(interp, "wrong # args: ",
00293                     "no script following \"else\" argument", NULL);
00294             return TCL_ERROR;
00295         }
00296     }
00297     if (i < objc - 1) {
00298         Tcl_AppendResult(interp, "wrong # args: ",
00299                 "extra words after \"else\" clause in \"if\" command", NULL);
00300         return TCL_ERROR;
00301     }
00302     if (thenScriptIndex) {
00303         /*
00304          * TIP #280. Make invoking context available to branch/else.
00305          */
00306 
00307         return TclEvalObjEx(interp, objv[thenScriptIndex], 0,
00308                 iPtr->cmdFramePtr, thenScriptIndex);
00309     }
00310     return TclEvalObjEx(interp, objv[i], 0, iPtr->cmdFramePtr, i);
00311 }
00312 
00313 /*
00314  *----------------------------------------------------------------------
00315  *
00316  * Tcl_IncrObjCmd --
00317  *
00318  *      This procedure is invoked to process the "incr" Tcl command. See the
00319  *      user documentation for details on what it does.
00320  *
00321  *      With the bytecode compiler, this procedure is only called when a
00322  *      command name is computed at runtime, and is "incr" or the name to
00323  *      which "incr" was renamed: e.g., "set z incr; $z i -1"
00324  *
00325  * Results:
00326  *      A standard Tcl result.
00327  *
00328  * Side effects:
00329  *      See the user documentation.
00330  *
00331  *----------------------------------------------------------------------
00332  */
00333 
00334 int
00335 Tcl_IncrObjCmd(
00336     ClientData dummy,           /* Not used. */
00337     Tcl_Interp *interp,         /* Current interpreter. */
00338     int objc,                   /* Number of arguments. */
00339     Tcl_Obj *CONST objv[])      /* Argument objects. */
00340 {
00341     Tcl_Obj *newValuePtr, *incrPtr;
00342 
00343     if ((objc != 2) && (objc != 3)) {
00344         Tcl_WrongNumArgs(interp, 1, objv, "varName ?increment?");
00345         return TCL_ERROR;
00346     }
00347 
00348     if (objc == 3) {
00349         incrPtr = objv[2];
00350     } else {
00351         incrPtr = Tcl_NewIntObj(1);
00352     }
00353     Tcl_IncrRefCount(incrPtr);
00354     newValuePtr = TclIncrObjVar2(interp, objv[1], NULL,
00355             incrPtr, TCL_LEAVE_ERR_MSG);
00356     Tcl_DecrRefCount(incrPtr);
00357 
00358     if (newValuePtr == NULL) {
00359         return TCL_ERROR;
00360     }
00361 
00362     /*
00363      * Set the interpreter's object result to refer to the variable's new
00364      * value object.
00365      */
00366 
00367     Tcl_SetObjResult(interp, newValuePtr);
00368     return TCL_OK;
00369 }
00370 
00371 /*
00372  *----------------------------------------------------------------------
00373  *
00374  * TclInitInfoCmd --
00375  *
00376  *      This function is called to create the "info" Tcl command. See the user
00377  *      documentation for details on what it does.
00378  *
00379  * Results:
00380  *      FIXME
00381  *
00382  * Side effects:
00383  *      none
00384  *
00385  *----------------------------------------------------------------------
00386  */
00387 
00388 Tcl_Command
00389 TclInitInfoCmd(
00390     Tcl_Interp *interp)         /* Current interpreter. */
00391 {
00392     return TclMakeEnsemble(interp, "info", defaultInfoMap);
00393 }
00394 
00395 /*
00396  *----------------------------------------------------------------------
00397  *
00398  * InfoArgsCmd --
00399  *
00400  *      Called to implement the "info args" command that returns the argument
00401  *      list for a procedure. Handles the following syntax:
00402  *
00403  *          info args procName
00404  *
00405  * Results:
00406  *      Returns TCL_OK if successful and TCL_ERROR if there is an error.
00407  *
00408  * Side effects:
00409  *      Returns a result in the interpreter's result object. If there is an
00410  *      error, the result is an error message.
00411  *
00412  *----------------------------------------------------------------------
00413  */
00414 
00415 static int
00416 InfoArgsCmd(
00417     ClientData dummy,           /* Not used. */
00418     Tcl_Interp *interp,         /* Current interpreter. */
00419     int objc,                   /* Number of arguments. */
00420     Tcl_Obj *CONST objv[])      /* Argument objects. */
00421 {
00422     register Interp *iPtr = (Interp *) interp;
00423     char *name;
00424     Proc *procPtr;
00425     CompiledLocal *localPtr;
00426     Tcl_Obj *listObjPtr;
00427 
00428     if (objc != 2) {
00429         Tcl_WrongNumArgs(interp, 1, objv, "procname");
00430         return TCL_ERROR;
00431     }
00432 
00433     name = TclGetString(objv[1]);
00434     procPtr = TclFindProc(iPtr, name);
00435     if (procPtr == NULL) {
00436         Tcl_AppendResult(interp, "\"", name, "\" isn't a procedure", NULL);
00437         return TCL_ERROR;
00438     }
00439 
00440     /*
00441      * Build a return list containing the arguments.
00442      */
00443 
00444     listObjPtr = Tcl_NewListObj(0, NULL);
00445     for (localPtr = procPtr->firstLocalPtr;  localPtr != NULL;
00446             localPtr = localPtr->nextPtr) {
00447         if (TclIsVarArgument(localPtr)) {
00448             Tcl_ListObjAppendElement(interp, listObjPtr,
00449                     Tcl_NewStringObj(localPtr->name, -1));
00450         }
00451     }
00452     Tcl_SetObjResult(interp, listObjPtr);
00453     return TCL_OK;
00454 }
00455 
00456 /*
00457  *----------------------------------------------------------------------
00458  *
00459  * InfoBodyCmd --
00460  *
00461  *      Called to implement the "info body" command that returns the body for
00462  *      a procedure. Handles the following syntax:
00463  *
00464  *          info body procName
00465  *
00466  * Results:
00467  *      Returns TCL_OK if successful and TCL_ERROR if there is an error.
00468  *
00469  * Side effects:
00470  *      Returns a result in the interpreter's result object. If there is an
00471  *      error, the result is an error message.
00472  *
00473  *----------------------------------------------------------------------
00474  */
00475 
00476 static int
00477 InfoBodyCmd(
00478     ClientData dummy,           /* Not used. */
00479     Tcl_Interp *interp,         /* Current interpreter. */
00480     int objc,                   /* Number of arguments. */
00481     Tcl_Obj *CONST objv[])      /* Argument objects. */
00482 {
00483     register Interp *iPtr = (Interp *) interp;
00484     char *name;
00485     Proc *procPtr;
00486     Tcl_Obj *bodyPtr, *resultPtr;
00487 
00488     if (objc != 2) {
00489         Tcl_WrongNumArgs(interp, 1, objv, "procname");
00490         return TCL_ERROR;
00491     }
00492 
00493     name = TclGetString(objv[1]);
00494     procPtr = TclFindProc(iPtr, name);
00495     if (procPtr == NULL) {
00496         Tcl_AppendResult(interp, "\"", name, "\" isn't a procedure", NULL);
00497         return TCL_ERROR;
00498     }
00499 
00500     /*
00501      * Here we used to return procPtr->bodyPtr, except when the body was
00502      * bytecompiled - in that case, the return was a copy of the body's string
00503      * rep. In order to better isolate the implementation details of the
00504      * compiler/engine subsystem, we now always return a copy of the string
00505      * rep. It is important to return a copy so that later manipulations of
00506      * the object do not invalidate the internal rep.
00507      */
00508 
00509     bodyPtr = procPtr->bodyPtr;
00510     if (bodyPtr->bytes == NULL) {
00511         /*
00512          * The string rep might not be valid if the procedure has never been
00513          * run before. [Bug #545644]
00514          */
00515 
00516         (void) TclGetString(bodyPtr);
00517     }
00518     resultPtr = Tcl_NewStringObj(bodyPtr->bytes, bodyPtr->length);
00519 
00520     Tcl_SetObjResult(interp, resultPtr);
00521     return TCL_OK;
00522 }
00523 
00524 /*
00525  *----------------------------------------------------------------------
00526  *
00527  * InfoCmdCountCmd --
00528  *
00529  *      Called to implement the "info cmdcount" command that returns the
00530  *      number of commands that have been executed. Handles the following
00531  *      syntax:
00532  *
00533  *          info cmdcount
00534  *
00535  * Results:
00536  *      Returns TCL_OK if successful and TCL_ERROR if there is an error.
00537  *
00538  * Side effects:
00539  *      Returns a result in the interpreter's result object. If there is an
00540  *      error, the result is an error message.
00541  *
00542  *----------------------------------------------------------------------
00543  */
00544 
00545 static int
00546 InfoCmdCountCmd(
00547     ClientData dummy,           /* Not used. */
00548     Tcl_Interp *interp,         /* Current interpreter. */
00549     int objc,                   /* Number of arguments. */
00550     Tcl_Obj *CONST objv[])      /* Argument objects. */
00551 {
00552     Interp *iPtr = (Interp *) interp;
00553 
00554     if (objc != 1) {
00555         Tcl_WrongNumArgs(interp, 1, objv, NULL);
00556         return TCL_ERROR;
00557     }
00558 
00559     Tcl_SetObjResult(interp, Tcl_NewIntObj(iPtr->cmdCount));
00560     return TCL_OK;
00561 }
00562 
00563 /*
00564  *----------------------------------------------------------------------
00565  *
00566  * InfoCommandsCmd --
00567  *
00568  *      Called to implement the "info commands" command that returns the list
00569  *      of commands in the interpreter that match an optional pattern. The
00570  *      pattern, if any, consists of an optional sequence of namespace names
00571  *      separated by "::" qualifiers, which is followed by a glob-style
00572  *      pattern that restricts which commands are returned. Handles the
00573  *      following syntax:
00574  *
00575  *          info commands ?pattern?
00576  *
00577  * Results:
00578  *      Returns TCL_OK if successful and TCL_ERROR if there is an error.
00579  *
00580  * Side effects:
00581  *      Returns a result in the interpreter's result object. If there is an
00582  *      error, the result is an error message.
00583  *
00584  *----------------------------------------------------------------------
00585  */
00586 
00587 static int
00588 InfoCommandsCmd(
00589     ClientData dummy,           /* Not used. */
00590     Tcl_Interp *interp,         /* Current interpreter. */
00591     int objc,                   /* Number of arguments. */
00592     Tcl_Obj *CONST objv[])      /* Argument objects. */
00593 {
00594     char *cmdName, *pattern;
00595     CONST char *simplePattern;
00596     register Tcl_HashEntry *entryPtr;
00597     Tcl_HashSearch search;
00598     Namespace *nsPtr;
00599     Namespace *globalNsPtr = (Namespace *) Tcl_GetGlobalNamespace(interp);
00600     Namespace *currNsPtr = (Namespace *) Tcl_GetCurrentNamespace(interp);
00601     Tcl_Obj *listPtr, *elemObjPtr;
00602     int specificNsInPattern = 0;/* Init. to avoid compiler warning. */
00603     Tcl_Command cmd;
00604     int i;
00605 
00606     /*
00607      * Get the pattern and find the "effective namespace" in which to list
00608      * commands.
00609      */
00610 
00611     if (objc == 1) {
00612         simplePattern = NULL;
00613         nsPtr = currNsPtr;
00614         specificNsInPattern = 0;
00615     } else if (objc == 2) {
00616         /*
00617          * From the pattern, get the effective namespace and the simple
00618          * pattern (no namespace qualifiers or ::'s) at the end. If an error
00619          * was found while parsing the pattern, return it. Otherwise, if the
00620          * namespace wasn't found, just leave nsPtr NULL: we will return an
00621          * empty list since no commands there can be found.
00622          */
00623 
00624         Namespace *dummy1NsPtr, *dummy2NsPtr;
00625 
00626         pattern = TclGetString(objv[1]);
00627         TclGetNamespaceForQualName(interp, pattern, (Namespace *) NULL, 0,
00628                 &nsPtr, &dummy1NsPtr, &dummy2NsPtr, &simplePattern);
00629 
00630         if (nsPtr != NULL) {    /* We successfully found the pattern's ns. */
00631             specificNsInPattern = (strcmp(simplePattern, pattern) != 0);
00632         }
00633     } else {
00634         Tcl_WrongNumArgs(interp, 1, objv, "?pattern?");
00635         return TCL_ERROR;
00636     }
00637 
00638     /*
00639      * Exit as quickly as possible if we couldn't find the namespace.
00640      */
00641 
00642     if (nsPtr == NULL) {
00643         return TCL_OK;
00644     }
00645 
00646     /*
00647      * Scan through the effective namespace's command table and create a list
00648      * with all commands that match the pattern. If a specific namespace was
00649      * requested in the pattern, qualify the command names with the namespace
00650      * name.
00651      */
00652 
00653     listPtr = Tcl_NewListObj(0, NULL);
00654 
00655     if (simplePattern != NULL && TclMatchIsTrivial(simplePattern)) {
00656         /*
00657          * Special case for when the pattern doesn't include any of glob's
00658          * special characters. This lets us avoid scans of any hash tables.
00659          */
00660 
00661         entryPtr = Tcl_FindHashEntry(&nsPtr->cmdTable, simplePattern);
00662         if (entryPtr != NULL) {
00663             if (specificNsInPattern) {
00664                 cmd = (Tcl_Command) Tcl_GetHashValue(entryPtr);
00665                 elemObjPtr = Tcl_NewObj();
00666                 Tcl_GetCommandFullName(interp, cmd, elemObjPtr);
00667             } else {
00668                 cmdName = Tcl_GetHashKey(&nsPtr->cmdTable, entryPtr);
00669                 elemObjPtr = Tcl_NewStringObj(cmdName, -1);
00670             }
00671             Tcl_ListObjAppendElement(interp, listPtr, elemObjPtr);
00672             Tcl_SetObjResult(interp, listPtr);
00673             return TCL_OK;
00674         }
00675         if ((nsPtr != globalNsPtr) && !specificNsInPattern) {
00676             Tcl_HashTable *tablePtr = NULL;     /* Quell warning. */
00677 
00678             for (i=0 ; i<nsPtr->commandPathLength ; i++) {
00679                 Namespace *pathNsPtr = nsPtr->commandPathArray[i].nsPtr;
00680 
00681                 if (pathNsPtr == NULL) {
00682                     continue;
00683                 }
00684                 tablePtr = &pathNsPtr->cmdTable;
00685                 entryPtr = Tcl_FindHashEntry(tablePtr, simplePattern);
00686                 if (entryPtr != NULL) {
00687                     break;
00688                 }
00689             }
00690             if (entryPtr == NULL) {
00691                 tablePtr = &globalNsPtr->cmdTable;
00692                 entryPtr = Tcl_FindHashEntry(tablePtr, simplePattern);
00693             }
00694             if (entryPtr != NULL) {
00695                 cmdName = Tcl_GetHashKey(tablePtr, entryPtr);
00696                 Tcl_ListObjAppendElement(interp, listPtr,
00697                         Tcl_NewStringObj(cmdName, -1));
00698                 Tcl_SetObjResult(interp, listPtr);
00699                 return TCL_OK;
00700             }
00701         }
00702     } else if (nsPtr->commandPathLength == 0 || specificNsInPattern) {
00703         /*
00704          * The pattern is non-trivial, but either there is no explicit path or
00705          * there is an explicit namespace in the pattern. In both cases, the
00706          * old matching scheme is perfect.
00707          */
00708 
00709         entryPtr = Tcl_FirstHashEntry(&nsPtr->cmdTable, &search);
00710         while (entryPtr != NULL) {
00711             cmdName = Tcl_GetHashKey(&nsPtr->cmdTable, entryPtr);
00712             if ((simplePattern == NULL)
00713                     || Tcl_StringMatch(cmdName, simplePattern)) {
00714                 if (specificNsInPattern) {
00715                     cmd = (Tcl_Command) Tcl_GetHashValue(entryPtr);
00716                     elemObjPtr = Tcl_NewObj();
00717                     Tcl_GetCommandFullName(interp, cmd, elemObjPtr);
00718                 } else {
00719                     elemObjPtr = Tcl_NewStringObj(cmdName, -1);
00720                 }
00721                 Tcl_ListObjAppendElement(interp, listPtr, elemObjPtr);
00722             }
00723             entryPtr = Tcl_NextHashEntry(&search);
00724         }
00725 
00726         /*
00727          * If the effective namespace isn't the global :: namespace, and a
00728          * specific namespace wasn't requested in the pattern, then add in all
00729          * global :: commands that match the simple pattern. Of course, we add
00730          * in only those commands that aren't hidden by a command in the
00731          * effective namespace.
00732          */
00733 
00734         if ((nsPtr != globalNsPtr) && !specificNsInPattern) {
00735             entryPtr = Tcl_FirstHashEntry(&globalNsPtr->cmdTable, &search);
00736             while (entryPtr != NULL) {
00737                 cmdName = Tcl_GetHashKey(&globalNsPtr->cmdTable, entryPtr);
00738                 if ((simplePattern == NULL)
00739                         || Tcl_StringMatch(cmdName, simplePattern)) {
00740                     if (Tcl_FindHashEntry(&nsPtr->cmdTable,cmdName) == NULL) {
00741                         Tcl_ListObjAppendElement(interp, listPtr,
00742                                 Tcl_NewStringObj(cmdName, -1));
00743                     }
00744                 }
00745                 entryPtr = Tcl_NextHashEntry(&search);
00746             }
00747         }
00748     } else {
00749         /*
00750          * The pattern is non-trivial (can match more than one command name),
00751          * there is an explicit path, and there is no explicit namespace in
00752          * the pattern. This means that we have to traverse the path to
00753          * discover all the commands defined.
00754          */
00755 
00756         Tcl_HashTable addedCommandsTable;
00757         int isNew;
00758         int foundGlobal = (nsPtr == globalNsPtr);
00759 
00760         /*
00761          * We keep a hash of the objects already added to the result list.
00762          */
00763 
00764         Tcl_InitObjHashTable(&addedCommandsTable);
00765 
00766         entryPtr = Tcl_FirstHashEntry(&nsPtr->cmdTable, &search);
00767         while (entryPtr != NULL) {
00768             cmdName = Tcl_GetHashKey(&nsPtr->cmdTable, entryPtr);
00769             if ((simplePattern == NULL)
00770                     || Tcl_StringMatch(cmdName, simplePattern)) {
00771                 elemObjPtr = Tcl_NewStringObj(cmdName, -1);
00772                 Tcl_ListObjAppendElement(interp, listPtr, elemObjPtr);
00773                 (void) Tcl_CreateHashEntry(&addedCommandsTable,
00774                         (char *)elemObjPtr, &isNew);
00775             }
00776             entryPtr = Tcl_NextHashEntry(&search);
00777         }
00778 
00779         /*
00780          * Search the path next.
00781          */
00782 
00783         for (i=0 ; i<nsPtr->commandPathLength ; i++) {
00784             Namespace *pathNsPtr = nsPtr->commandPathArray[i].nsPtr;
00785 
00786             if (pathNsPtr == NULL) {
00787                 continue;
00788             }
00789             if (pathNsPtr == globalNsPtr) {
00790                 foundGlobal = 1;
00791             }
00792             entryPtr = Tcl_FirstHashEntry(&pathNsPtr->cmdTable, &search);
00793             while (entryPtr != NULL) {
00794                 cmdName = Tcl_GetHashKey(&pathNsPtr->cmdTable, entryPtr);
00795                 if ((simplePattern == NULL)
00796                         || Tcl_StringMatch(cmdName, simplePattern)) {
00797                     elemObjPtr = Tcl_NewStringObj(cmdName, -1);
00798                     (void) Tcl_CreateHashEntry(&addedCommandsTable,
00799                             (char *) elemObjPtr, &isNew);
00800                     if (isNew) {
00801                         Tcl_ListObjAppendElement(interp, listPtr, elemObjPtr);
00802                     } else {
00803                         TclDecrRefCount(elemObjPtr);
00804                     }
00805                 }
00806                 entryPtr = Tcl_NextHashEntry(&search);
00807             }
00808         }
00809 
00810         /*
00811          * If the effective namespace isn't the global :: namespace, and a
00812          * specific namespace wasn't requested in the pattern, then add in all
00813          * global :: commands that match the simple pattern. Of course, we add
00814          * in only those commands that aren't hidden by a command in the
00815          * effective namespace.
00816          */
00817 
00818         if (!foundGlobal) {
00819             entryPtr = Tcl_FirstHashEntry(&globalNsPtr->cmdTable, &search);
00820             while (entryPtr != NULL) {
00821                 cmdName = Tcl_GetHashKey(&globalNsPtr->cmdTable, entryPtr);
00822                 if ((simplePattern == NULL)
00823                         || Tcl_StringMatch(cmdName, simplePattern)) {
00824                     elemObjPtr = Tcl_NewStringObj(cmdName, -1);
00825                     if (Tcl_FindHashEntry(&addedCommandsTable,
00826                             (char *) elemObjPtr) == NULL) {
00827                         Tcl_ListObjAppendElement(interp, listPtr, elemObjPtr);
00828                     } else {
00829                         TclDecrRefCount(elemObjPtr);
00830                     }
00831                 }
00832                 entryPtr = Tcl_NextHashEntry(&search);
00833             }
00834         }
00835 
00836         Tcl_DeleteHashTable(&addedCommandsTable);
00837     }
00838 
00839     Tcl_SetObjResult(interp, listPtr);
00840     return TCL_OK;
00841 }
00842 
00843 /*
00844  *----------------------------------------------------------------------
00845  *
00846  * InfoCompleteCmd --
00847  *
00848  *      Called to implement the "info complete" command that determines
00849  *      whether a string is a complete Tcl command. Handles the following
00850  *      syntax:
00851  *
00852  *          info complete command
00853  *
00854  * Results:
00855  *      Returns TCL_OK if successful and TCL_ERROR if there is an error.
00856  *
00857  * Side effects:
00858  *      Returns a result in the interpreter's result object. If there is an
00859  *      error, the result is an error message.
00860  *
00861  *----------------------------------------------------------------------
00862  */
00863 
00864 static int
00865 InfoCompleteCmd(
00866     ClientData dummy,           /* Not used. */
00867     Tcl_Interp *interp,         /* Current interpreter. */
00868     int objc,                   /* Number of arguments. */
00869     Tcl_Obj *CONST objv[])      /* Argument objects. */
00870 {
00871     if (objc != 2) {
00872         Tcl_WrongNumArgs(interp, 1, objv, "command");
00873         return TCL_ERROR;
00874     }
00875 
00876     Tcl_SetObjResult(interp, Tcl_NewBooleanObj(
00877             TclObjCommandComplete(objv[1])));
00878     return TCL_OK;
00879 }
00880 
00881 /*
00882  *----------------------------------------------------------------------
00883  *
00884  * InfoDefaultCmd --
00885  *
00886  *      Called to implement the "info default" command that returns the
00887  *      default value for a procedure argument. Handles the following syntax:
00888  *
00889  *          info default procName arg varName
00890  *
00891  * Results:
00892  *      Returns TCL_OK if successful and TCL_ERROR if there is an error.
00893  *
00894  * Side effects:
00895  *      Returns a result in the interpreter's result object. If there is an
00896  *      error, the result is an error message.
00897  *
00898  *----------------------------------------------------------------------
00899  */
00900 
00901 static int
00902 InfoDefaultCmd(
00903     ClientData dummy,           /* Not used. */
00904     Tcl_Interp *interp,         /* Current interpreter. */
00905     int objc,                   /* Number of arguments. */
00906     Tcl_Obj *CONST objv[])      /* Argument objects. */
00907 {
00908     Interp *iPtr = (Interp *) interp;
00909     char *procName, *argName, *varName;
00910     Proc *procPtr;
00911     CompiledLocal *localPtr;
00912     Tcl_Obj *valueObjPtr;
00913 
00914     if (objc != 4) {
00915         Tcl_WrongNumArgs(interp, 1, objv, "procname arg varname");
00916         return TCL_ERROR;
00917     }
00918 
00919     procName = TclGetString(objv[1]);
00920     argName = TclGetString(objv[2]);
00921 
00922     procPtr = TclFindProc(iPtr, procName);
00923     if (procPtr == NULL) {
00924         Tcl_AppendResult(interp, "\"", procName, "\" isn't a procedure",NULL);
00925         return TCL_ERROR;
00926     }
00927 
00928     for (localPtr = procPtr->firstLocalPtr;  localPtr != NULL;
00929             localPtr = localPtr->nextPtr) {
00930         if (TclIsVarArgument(localPtr)
00931                 && (strcmp(argName, localPtr->name) == 0)) {
00932             if (localPtr->defValuePtr != NULL) {
00933                 valueObjPtr = Tcl_ObjSetVar2(interp, objv[3], NULL,
00934                         localPtr->defValuePtr, 0);
00935                 if (valueObjPtr == NULL) {
00936                     goto defStoreError;
00937                 }
00938                 Tcl_SetObjResult(interp, Tcl_NewIntObj(1));
00939             } else {
00940                 Tcl_Obj *nullObjPtr = Tcl_NewObj();
00941                 valueObjPtr = Tcl_ObjSetVar2(interp, objv[3], NULL,
00942                         nullObjPtr, 0);
00943                 if (valueObjPtr == NULL) {
00944                     goto defStoreError;
00945                 }
00946                 Tcl_SetObjResult(interp, Tcl_NewIntObj(0));
00947             }
00948             return TCL_OK;
00949         }
00950     }
00951 
00952     Tcl_AppendResult(interp, "procedure \"", procName,
00953             "\" doesn't have an argument \"", argName, "\"", NULL);
00954     return TCL_ERROR;
00955 
00956   defStoreError:
00957     varName = TclGetString(objv[3]);
00958     Tcl_AppendResult(interp, "couldn't store default value in variable \"",
00959             varName, "\"", NULL);
00960     return TCL_ERROR;
00961 }
00962 
00963 /*
00964  *----------------------------------------------------------------------
00965  *
00966  * TclInfoExistsCmd --
00967  *
00968  *      Called to implement the "info exists" command that determines whether
00969  *      a variable exists. Handles the following syntax:
00970  *
00971  *          info exists varName
00972  *
00973  * Results:
00974  *      Returns TCL_OK if successful and TCL_ERROR if there is an error.
00975  *
00976  * Side effects:
00977  *      Returns a result in the interpreter's result object. If there is an
00978  *      error, the result is an error message.
00979  *
00980  *----------------------------------------------------------------------
00981  */
00982 
00983 int
00984 TclInfoExistsCmd(
00985     ClientData dummy,           /* Not used. */
00986     Tcl_Interp *interp,         /* Current interpreter. */
00987     int objc,                   /* Number of arguments. */
00988     Tcl_Obj *CONST objv[])      /* Argument objects. */
00989 {
00990     char *varName;
00991     Var *varPtr;
00992 
00993     if (objc != 2) {
00994         Tcl_WrongNumArgs(interp, 1, objv, "varName");
00995         return TCL_ERROR;
00996     }
00997 
00998     varName = TclGetString(objv[1]);
00999     varPtr = TclVarTraceExists(interp, varName);
01000 
01001     Tcl_SetObjResult(interp,
01002             Tcl_NewBooleanObj(varPtr && varPtr->value.objPtr));
01003     return TCL_OK;
01004 }
01005 
01006 /*
01007  *----------------------------------------------------------------------
01008  *
01009  * InfoFrameCmd --
01010  *      TIP #280
01011  *
01012  *      Called to implement the "info frame" command that returns the location
01013  *      of either the currently executing command, or its caller. Handles the
01014  *      following syntax:
01015  *
01016  *              info frame ?number?
01017  *
01018  * Results:
01019  *      Returns TCL_OK if successful and TCL_ERROR if there is an error.
01020  *
01021  * Side effects:
01022  *      Returns a result in the interpreter's result object. If there is an
01023  *      error, the result is an error message.
01024  *
01025  *----------------------------------------------------------------------
01026  */
01027 
01028 static int
01029 InfoFrameCmd(
01030     ClientData dummy,           /* Not used. */
01031     Tcl_Interp *interp,         /* Current interpreter. */
01032     int objc,                   /* Number of arguments. */
01033     Tcl_Obj *CONST objv[])      /* Argument objects. */
01034 {
01035     Interp *iPtr = (Interp *) interp;
01036     int level;
01037     CmdFrame *framePtr;
01038 
01039     if (objc == 1) {
01040         /*
01041          * Just "info frame".
01042          */
01043 
01044         int levels =
01045                 (iPtr->cmdFramePtr == NULL ? 0 : iPtr->cmdFramePtr->level);
01046 
01047         Tcl_SetIntObj(Tcl_GetObjResult(interp), levels);
01048         return TCL_OK;
01049     } else if (objc != 2) {
01050         Tcl_WrongNumArgs(interp, 1, objv, "?number?");
01051         return TCL_ERROR;
01052     }
01053 
01054     /*
01055      * We've got "info frame level" and must parse the level first.
01056      */
01057 
01058     if (TclGetIntFromObj(interp, objv[1], &level) != TCL_OK) {
01059         return TCL_ERROR;
01060     }
01061     if (level <= 0) {
01062         /*
01063          * Negative levels are adressing relative to the current frame's
01064          * depth.
01065          */
01066 
01067         if (iPtr->cmdFramePtr == NULL) {
01068         levelError:
01069             Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "bad level \"",
01070                     TclGetString(objv[1]), "\"", NULL);
01071             return TCL_ERROR;
01072         }
01073 
01074         /*
01075          * Convert to absolute.
01076          */
01077 
01078         level += iPtr->cmdFramePtr->level;
01079     }
01080 
01081     for (framePtr = iPtr->cmdFramePtr; framePtr != NULL;
01082             framePtr = framePtr->nextPtr) {
01083         if (framePtr->level == level) {
01084             break;
01085         }
01086     }
01087     if (framePtr == NULL) {
01088         goto levelError;
01089     }
01090 
01091     Tcl_SetObjResult(interp, TclInfoFrame(interp, framePtr));
01092     return TCL_OK;
01093 }
01094 
01095 /*
01096  *----------------------------------------------------------------------
01097  *
01098  * TclInfoFrame --
01099  *
01100  *      Core of InfoFrameCmd, returns TIP280 dict for a given frame.
01101  *
01102  * Results:
01103  *      Returns TIP280 dict.
01104  *
01105  * Side effects:
01106  *      None.
01107  *
01108  *----------------------------------------------------------------------
01109  */
01110 
01111 Tcl_Obj *
01112 TclInfoFrame(
01113     Tcl_Interp *interp,         /* Current interpreter. */
01114     CmdFrame *framePtr)         /* Frame to get info for. */
01115 {
01116     Interp *iPtr = (Interp *) interp;
01117     Tcl_Obj *lv[20];            /* Keep uptodate when more keys are added to
01118                                  * the dict. */
01119     int lc = 0;
01120     /*
01121      * This array is indexed by the TCL_LOCATION_... values, except
01122      * for _LAST.
01123      */
01124     static CONST char *typeString[TCL_LOCATION_LAST] = {
01125         "eval", "eval", "eval", "precompiled", "source", "proc"
01126     };
01127     Tcl_Obj *tmpObj;
01128 
01129    /*
01130      * Pull the information and construct the dictionary to return, as list.
01131      * Regarding use of the CmdFrame fields see tclInt.h, and its definition.
01132      */
01133 
01134 #define ADD_PAIR(name, value) \
01135         TclNewLiteralStringObj(tmpObj, name); \
01136         lv[lc++] = tmpObj; \
01137         lv[lc++] = (value)
01138 
01139     switch (framePtr->type) {
01140     case TCL_LOCATION_EVAL:
01141         /*
01142          * Evaluation, dynamic script. Type, line, cmd, the latter through
01143          * str.
01144          */
01145 
01146         ADD_PAIR("type", Tcl_NewStringObj(typeString[framePtr->type], -1));
01147         ADD_PAIR("line", Tcl_NewIntObj(framePtr->line[0]));
01148         ADD_PAIR("cmd", Tcl_NewStringObj(framePtr->cmd.str.cmd,
01149                 framePtr->cmd.str.len));
01150         break;
01151 
01152     case TCL_LOCATION_EVAL_LIST:
01153         /*
01154          * List optimized evaluation. Type, line, cmd, the latter through
01155          * listPtr, possibly a frame.
01156          */
01157 
01158         ADD_PAIR("type", Tcl_NewStringObj(typeString[framePtr->type], -1));
01159         ADD_PAIR("line", Tcl_NewIntObj(framePtr->line[0]));
01160 
01161         /*
01162          * We put a duplicate of the command list obj into the result to
01163          * ensure that the 'pure List'-property of the command itself is not
01164          * destroyed. Otherwise the query here would disable the list
01165          * optimization path in Tcl_EvalObjEx.
01166          */
01167 
01168         ADD_PAIR("cmd", Tcl_DuplicateObj(framePtr->cmd.listPtr));
01169         break;
01170 
01171     case TCL_LOCATION_PREBC:
01172         /*
01173          * Precompiled. Result contains the type as signal, nothing else.
01174          */
01175 
01176         ADD_PAIR("type", Tcl_NewStringObj(typeString[framePtr->type], -1));
01177         break;
01178 
01179     case TCL_LOCATION_BC: {
01180         /*
01181          * Execution of bytecode. Talk to the BC engine to fill out the frame.
01182          */
01183 
01184         Proc *procPtr =
01185                 framePtr->framePtr ? framePtr->framePtr->procPtr : NULL;
01186         CmdFrame *fPtr;
01187 
01188         fPtr = (CmdFrame *) TclStackAlloc(interp, sizeof(CmdFrame));
01189         *fPtr = *framePtr;
01190 
01191         /*
01192          * Note:
01193          * Type BC => f.data.eval.path    is not used.
01194          *            f.data.tebc.codePtr is used instead.
01195          */
01196 
01197         TclGetSrcInfoForPc(fPtr);
01198 
01199         /*
01200          * Now filled: cmd.str.(cmd,len), line
01201          * Possibly modified: type, path!
01202          */
01203 
01204         ADD_PAIR("type", Tcl_NewStringObj(typeString[fPtr->type], -1));
01205         ADD_PAIR("line", Tcl_NewIntObj(fPtr->line[0]));
01206 
01207         if (fPtr->type == TCL_LOCATION_SOURCE) {
01208             ADD_PAIR("file", fPtr->data.eval.path);
01209 
01210             /*
01211              * Death of reference by TclGetSrcInfoForPc.
01212              */
01213 
01214             Tcl_DecrRefCount(fPtr->data.eval.path);
01215         }
01216 
01217         ADD_PAIR("cmd",
01218                 Tcl_NewStringObj(fPtr->cmd.str.cmd, fPtr->cmd.str.len));
01219 
01220         if (procPtr != NULL) {
01221             Tcl_HashEntry *namePtr = procPtr->cmdPtr->hPtr;
01222 
01223             if (namePtr) {
01224                 /*
01225                  * This is a regular command.
01226                  */
01227 
01228                 char *procName = Tcl_GetHashKey(namePtr->tablePtr, namePtr);
01229                 char *nsName = procPtr->cmdPtr->nsPtr->fullName;
01230 
01231                 ADD_PAIR("proc", Tcl_NewStringObj(nsName, -1));
01232 
01233                 if (strcmp(nsName, "::") != 0) {
01234                     Tcl_AppendToObj(lv[lc-1], "::", -1);
01235                 }
01236                 Tcl_AppendToObj(lv[lc-1], procName, -1);
01237             } else if (procPtr->cmdPtr->clientData) {
01238                 ExtraFrameInfo *efiPtr = procPtr->cmdPtr->clientData;
01239                 int i;
01240 
01241                 /*
01242                  * This is a non-standard command. Luckily, it's told us how
01243                  * to render extra information about its frame.
01244                  */
01245 
01246                 for (i=0 ; i<efiPtr->length ; i++) {
01247                     lv[lc++] = Tcl_NewStringObj(efiPtr->fields[i].name, -1);
01248                     if (efiPtr->fields[i].proc) {
01249                         lv[lc++] = efiPtr->fields[i].proc(
01250                                 efiPtr->fields[i].clientData);
01251                     } else {
01252                         lv[lc++] = efiPtr->fields[i].clientData;
01253                     }
01254                 }
01255             }
01256         }
01257         TclStackFree(interp, fPtr);
01258         break;
01259     }
01260 
01261     case TCL_LOCATION_SOURCE:
01262         /*
01263          * Evaluation of a script file.
01264          */
01265 
01266         ADD_PAIR("type", Tcl_NewStringObj(typeString[framePtr->type], -1));
01267         ADD_PAIR("line", Tcl_NewIntObj(framePtr->line[0]));
01268         ADD_PAIR("file", framePtr->data.eval.path);
01269 
01270         /*
01271          * Refcount framePtr->data.eval.path goes up when lv is converted into
01272          * the result list object.
01273          */
01274 
01275         ADD_PAIR("cmd", Tcl_NewStringObj(framePtr->cmd.str.cmd,
01276                 framePtr->cmd.str.len));
01277         break;
01278 
01279     case TCL_LOCATION_PROC:
01280         Tcl_Panic("TCL_LOCATION_PROC found in standard frame");
01281         break;
01282     }
01283 
01284     /*
01285      * 'level'. Common to all frame types. Conditional on having an associated
01286      * _visible_ CallFrame.
01287      */
01288 
01289     if ((framePtr->framePtr != NULL) && (iPtr->varFramePtr != NULL)) {
01290         CallFrame *current = framePtr->framePtr;
01291         CallFrame *top = iPtr->varFramePtr;
01292         CallFrame *idx;
01293 
01294         for (idx=top ; idx!=NULL ; idx=idx->callerVarPtr) {
01295             if (idx == current) {
01296                 int c = framePtr->framePtr->level;
01297                 int t = iPtr->varFramePtr->level;
01298 
01299                 ADD_PAIR("level", Tcl_NewIntObj(t - c));
01300                 break;
01301             }
01302         }
01303     }
01304 
01305     return Tcl_NewListObj(lc, lv);
01306 }
01307 
01308 /*
01309  *----------------------------------------------------------------------
01310  *
01311  * InfoFunctionsCmd --
01312  *
01313  *      Called to implement the "info functions" command that returns the list
01314  *      of math functions matching an optional pattern. Handles the following
01315  *      syntax:
01316  *
01317  *          info functions ?pattern?
01318  *
01319  * Results:
01320  *      Returns TCL_OK if successful and TCL_ERROR if there is an error.
01321  *
01322  * Side effects:
01323  *      Returns a result in the interpreter's result object. If there is an
01324  *      error, the result is an error message.
01325  *
01326  *----------------------------------------------------------------------
01327  */
01328 
01329 static int
01330 InfoFunctionsCmd(
01331     ClientData dummy,           /* Not used. */
01332     Tcl_Interp *interp,         /* Current interpreter. */
01333     int objc,                   /* Number of arguments. */
01334     Tcl_Obj *CONST objv[])      /* Argument objects. */
01335 {
01336     char *pattern;
01337 
01338     if (objc == 1) {
01339         pattern = NULL;
01340     } else if (objc == 2) {
01341         pattern = TclGetString(objv[1]);
01342     } else {
01343         Tcl_WrongNumArgs(interp, 1, objv, "?pattern?");
01344         return TCL_ERROR;
01345     }
01346 
01347     Tcl_SetObjResult(interp, Tcl_ListMathFuncs(interp, pattern));
01348     return TCL_OK;
01349 }
01350 
01351 /*
01352  *----------------------------------------------------------------------
01353  *
01354  * InfoHostnameCmd --
01355  *
01356  *      Called to implement the "info hostname" command that returns the host
01357  *      name. Handles the following syntax:
01358  *
01359  *          info hostname
01360  *
01361  * Results:
01362  *      Returns TCL_OK if successful and TCL_ERROR if there is an error.
01363  *
01364  * Side effects:
01365  *      Returns a result in the interpreter's result object. If there is an
01366  *      error, the result is an error message.
01367  *
01368  *----------------------------------------------------------------------
01369  */
01370 
01371 static int
01372 InfoHostnameCmd(
01373     ClientData dummy,           /* Not used. */
01374     Tcl_Interp *interp,         /* Current interpreter. */
01375     int objc,                   /* Number of arguments. */
01376     Tcl_Obj *CONST objv[])      /* Argument objects. */
01377 {
01378     CONST char *name;
01379 
01380     if (objc != 1) {
01381         Tcl_WrongNumArgs(interp, 1, objv, NULL);
01382         return TCL_ERROR;
01383     }
01384 
01385     name = Tcl_GetHostName();
01386     if (name) {
01387         Tcl_SetObjResult(interp, Tcl_NewStringObj(name, -1));
01388         return TCL_OK;
01389     }
01390     Tcl_SetResult(interp, "unable to determine name of host", TCL_STATIC);
01391     return TCL_ERROR;
01392 }
01393 
01394 /*
01395  *----------------------------------------------------------------------
01396  *
01397  * InfoLevelCmd --
01398  *
01399  *      Called to implement the "info level" command that returns information
01400  *      about the call stack. Handles the following syntax:
01401  *
01402  *          info level ?number?
01403  *
01404  * Results:
01405  *      Returns TCL_OK if successful and TCL_ERROR if there is an error.
01406  *
01407  * Side effects:
01408  *      Returns a result in the interpreter's result object. If there is an
01409  *      error, the result is an error message.
01410  *
01411  *----------------------------------------------------------------------
01412  */
01413 
01414 static int
01415 InfoLevelCmd(
01416     ClientData dummy,           /* Not used. */
01417     Tcl_Interp *interp,         /* Current interpreter. */
01418     int objc,                   /* Number of arguments. */
01419     Tcl_Obj *CONST objv[])      /* Argument objects. */
01420 {
01421     Interp *iPtr = (Interp *) interp;
01422 
01423     if (objc == 1) {            /* Just "info level" */
01424         Tcl_SetObjResult(interp, Tcl_NewIntObj(iPtr->varFramePtr->level));
01425         return TCL_OK;
01426     }
01427 
01428     if (objc == 2) {
01429         int level;
01430         CallFrame *framePtr, *rootFramePtr = iPtr->rootFramePtr;
01431 
01432         if (TclGetIntFromObj(interp, objv[1], &level) != TCL_OK) {
01433             return TCL_ERROR;
01434         }
01435         if (level <= 0) {
01436             if (iPtr->varFramePtr == rootFramePtr) {
01437                 goto levelError;
01438             }
01439             level += iPtr->varFramePtr->level;
01440         }
01441         for (framePtr=iPtr->varFramePtr ; framePtr!=rootFramePtr;
01442                 framePtr=framePtr->callerVarPtr) {
01443             if (framePtr->level == level) {
01444                 break;
01445             }
01446         }
01447         if (framePtr == rootFramePtr) {
01448             goto levelError;
01449         }
01450 
01451         Tcl_SetObjResult(interp,
01452                 Tcl_NewListObj(framePtr->objc, framePtr->objv));
01453         return TCL_OK;
01454     }
01455 
01456     Tcl_WrongNumArgs(interp, 1, objv, "?number?");
01457     return TCL_ERROR;
01458 
01459   levelError:
01460     Tcl_AppendResult(interp, "bad level \"", TclGetString(objv[1]), "\"",
01461             NULL);
01462     return TCL_ERROR;
01463 }
01464 
01465 /*
01466  *----------------------------------------------------------------------
01467  *
01468  * InfoLibraryCmd --
01469  *
01470  *      Called to implement the "info library" command that returns the
01471  *      library directory for the Tcl installation. Handles the following
01472  *      syntax:
01473  *
01474  *          info library
01475  *
01476  * Results:
01477  *      Returns TCL_OK if successful and TCL_ERROR if there is an error.
01478  *
01479  * Side effects:
01480  *      Returns a result in the interpreter's result object. If there is an
01481  *      error, the result is an error message.
01482  *
01483  *----------------------------------------------------------------------
01484  */
01485 
01486 static int
01487 InfoLibraryCmd(
01488     ClientData dummy,           /* Not used. */
01489     Tcl_Interp *interp,         /* Current interpreter. */
01490     int objc,                   /* Number of arguments. */
01491     Tcl_Obj *CONST objv[])      /* Argument objects. */
01492 {
01493     CONST char *libDirName;
01494 
01495     if (objc != 1) {
01496         Tcl_WrongNumArgs(interp, 1, objv, NULL);
01497         return TCL_ERROR;
01498     }
01499 
01500     libDirName = Tcl_GetVar(interp, "tcl_library", TCL_GLOBAL_ONLY);
01501     if (libDirName != NULL) {
01502         Tcl_SetObjResult(interp, Tcl_NewStringObj(libDirName, -1));
01503         return TCL_OK;
01504     }
01505     Tcl_SetResult(interp, "no library has been specified for Tcl",TCL_STATIC);
01506     return TCL_ERROR;
01507 }
01508 
01509 /*
01510  *----------------------------------------------------------------------
01511  *
01512  * InfoLoadedCmd --
01513  *
01514  *      Called to implement the "info loaded" command that returns the
01515  *      packages that have been loaded into an interpreter. Handles the
01516  *      following syntax:
01517  *
01518  *          info loaded ?interp?
01519  *
01520  * Results:
01521  *      Returns TCL_OK if successful and TCL_ERROR if there is an error.
01522  *
01523  * Side effects:
01524  *      Returns a result in the interpreter's result object. If there is an
01525  *      error, the result is an error message.
01526  *
01527  *----------------------------------------------------------------------
01528  */
01529 
01530 static int
01531 InfoLoadedCmd(
01532     ClientData dummy,           /* Not used. */
01533     Tcl_Interp *interp,         /* Current interpreter. */
01534     int objc,                   /* Number of arguments. */
01535     Tcl_Obj *CONST objv[])      /* Argument objects. */
01536 {
01537     char *interpName;
01538     int result;
01539 
01540     if ((objc != 1) && (objc != 2)) {
01541         Tcl_WrongNumArgs(interp, 1, objv, "?interp?");
01542         return TCL_ERROR;
01543     }
01544 
01545     if (objc == 1) {            /* Get loaded pkgs in all interpreters. */
01546         interpName = NULL;
01547     } else {                    /* Get pkgs just in specified interp. */
01548         interpName = TclGetString(objv[1]);
01549     }
01550     result = TclGetLoadedPackages(interp, interpName);
01551     return result;
01552 }
01553 
01554 /*
01555  *----------------------------------------------------------------------
01556  *
01557  * InfoNameOfExecutableCmd --
01558  *
01559  *      Called to implement the "info nameofexecutable" command that returns
01560  *      the name of the binary file running this application. Handles the
01561  *      following syntax:
01562  *
01563  *          info nameofexecutable
01564  *
01565  * Results:
01566  *      Returns TCL_OK if successful and TCL_ERROR if there is an error.
01567  *
01568  * Side effects:
01569  *      Returns a result in the interpreter's result object. If there is an
01570  *      error, the result is an error message.
01571  *
01572  *----------------------------------------------------------------------
01573  */
01574 
01575 static int
01576 InfoNameOfExecutableCmd(
01577     ClientData dummy,           /* Not used. */
01578     Tcl_Interp *interp,         /* Current interpreter. */
01579     int objc,                   /* Number of arguments. */
01580     Tcl_Obj *CONST objv[])      /* Argument objects. */
01581 {
01582     if (objc != 1) {
01583         Tcl_WrongNumArgs(interp, 1, objv, NULL);
01584         return TCL_ERROR;
01585     }
01586     Tcl_SetObjResult(interp, TclGetObjNameOfExecutable());
01587     return TCL_OK;
01588 }
01589 
01590 /*
01591  *----------------------------------------------------------------------
01592  *
01593  * InfoPatchLevelCmd --
01594  *
01595  *      Called to implement the "info patchlevel" command that returns the
01596  *      default value for an argument to a procedure. Handles the following
01597  *      syntax:
01598  *
01599  *          info patchlevel
01600  *
01601  * Results:
01602  *      Returns TCL_OK if successful and TCL_ERROR if there is an error.
01603  *
01604  * Side effects:
01605  *      Returns a result in the interpreter's result object. If there is an
01606  *      error, the result is an error message.
01607  *
01608  *----------------------------------------------------------------------
01609  */
01610 
01611 static int
01612 InfoPatchLevelCmd(
01613     ClientData dummy,           /* Not used. */
01614     Tcl_Interp *interp,         /* Current interpreter. */
01615     int objc,                   /* Number of arguments. */
01616     Tcl_Obj *CONST objv[])      /* Argument objects. */
01617 {
01618     CONST char *patchlevel;
01619 
01620     if (objc != 1) {
01621         Tcl_WrongNumArgs(interp, 1, objv, NULL);
01622         return TCL_ERROR;
01623     }
01624 
01625     patchlevel = Tcl_GetVar(interp, "tcl_patchLevel",
01626             (TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG));
01627     if (patchlevel != NULL) {
01628         Tcl_SetObjResult(interp, Tcl_NewStringObj(patchlevel, -1));
01629         return TCL_OK;
01630     }
01631     return TCL_ERROR;
01632 }
01633 
01634 /*
01635  *----------------------------------------------------------------------
01636  *
01637  * InfoProcsCmd --
01638  *
01639  *      Called to implement the "info procs" command that returns the list of
01640  *      procedures in the interpreter that match an optional pattern. The
01641  *      pattern, if any, consists of an optional sequence of namespace names
01642  *      separated by "::" qualifiers, which is followed by a glob-style
01643  *      pattern that restricts which commands are returned. Handles the
01644  *      following syntax:
01645  *
01646  *          info procs ?pattern?
01647  *
01648  * Results:
01649  *      Returns TCL_OK if successful and TCL_ERROR if there is an error.
01650  *
01651  * Side effects:
01652  *      Returns a result in the interpreter's result object. If there is an
01653  *      error, the result is an error message.
01654  *
01655  *----------------------------------------------------------------------
01656  */
01657 
01658 static int
01659 InfoProcsCmd(
01660     ClientData dummy,           /* Not used. */
01661     Tcl_Interp *interp,         /* Current interpreter. */
01662     int objc,                   /* Number of arguments. */
01663     Tcl_Obj *CONST objv[])      /* Argument objects. */
01664 {
01665     char *cmdName, *pattern;
01666     CONST char *simplePattern;
01667     Namespace *nsPtr;
01668 #ifdef INFO_PROCS_SEARCH_GLOBAL_NS
01669     Namespace *globalNsPtr = (Namespace *) Tcl_GetGlobalNamespace(interp);
01670 #endif
01671     Namespace *currNsPtr = (Namespace *) Tcl_GetCurrentNamespace(interp);
01672     Tcl_Obj *listPtr, *elemObjPtr;
01673     int specificNsInPattern = 0;/* Init. to avoid compiler warning. */
01674     register Tcl_HashEntry *entryPtr;
01675     Tcl_HashSearch search;
01676     Command *cmdPtr, *realCmdPtr;
01677 
01678     /*
01679      * Get the pattern and find the "effective namespace" in which to list
01680      * procs.
01681      */
01682 
01683     if (objc == 1) {
01684         simplePattern = NULL;
01685         nsPtr = currNsPtr;
01686         specificNsInPattern = 0;
01687     } else if (objc == 2) {
01688         /*
01689          * From the pattern, get the effective namespace and the simple
01690          * pattern (no namespace qualifiers or ::'s) at the end. If an error
01691          * was found while parsing the pattern, return it. Otherwise, if the
01692          * namespace wasn't found, just leave nsPtr NULL: we will return an
01693          * empty list since no commands there can be found.
01694          */
01695 
01696         Namespace *dummy1NsPtr, *dummy2NsPtr;
01697 
01698         pattern = TclGetString(objv[1]);
01699         TclGetNamespaceForQualName(interp, pattern, (Namespace *) NULL,
01700                 /*flags*/ 0, &nsPtr, &dummy1NsPtr, &dummy2NsPtr,
01701                 &simplePattern);
01702 
01703         if (nsPtr != NULL) {    /* We successfully found the pattern's ns. */
01704             specificNsInPattern = (strcmp(simplePattern, pattern) != 0);
01705         }
01706     } else {
01707         Tcl_WrongNumArgs(interp, 1, objv, "?pattern?");
01708         return TCL_ERROR;
01709     }
01710 
01711     if (nsPtr == NULL) {
01712         return TCL_OK;
01713     }
01714 
01715     /*
01716      * Scan through the effective namespace's command table and create a list
01717      * with all procs that match the pattern. If a specific namespace was
01718      * requested in the pattern, qualify the command names with the namespace
01719      * name.
01720      */
01721 
01722     listPtr = Tcl_NewListObj(0, NULL);
01723 #ifndef INFO_PROCS_SEARCH_GLOBAL_NS
01724     if (simplePattern != NULL && TclMatchIsTrivial(simplePattern)) {
01725         entryPtr = Tcl_FindHashEntry(&nsPtr->cmdTable, simplePattern);
01726         if (entryPtr != NULL) {
01727             cmdPtr = (Command *) Tcl_GetHashValue(entryPtr);
01728 
01729             if (!TclIsProc(cmdPtr)) {
01730                 realCmdPtr = (Command *)
01731                         TclGetOriginalCommand((Tcl_Command) cmdPtr);
01732                 if (realCmdPtr != NULL && TclIsProc(realCmdPtr)) {
01733                     goto simpleProcOK;
01734                 }
01735             } else {
01736             simpleProcOK:
01737                 if (specificNsInPattern) {
01738                     elemObjPtr = Tcl_NewObj();
01739                     Tcl_GetCommandFullName(interp, (Tcl_Command) cmdPtr,
01740                             elemObjPtr);
01741                 } else {
01742                     elemObjPtr = Tcl_NewStringObj(simplePattern, -1);
01743                 }
01744                 Tcl_ListObjAppendElement(interp, listPtr, elemObjPtr);
01745             }
01746         }
01747     } else
01748 #endif /* !INFO_PROCS_SEARCH_GLOBAL_NS */
01749     {
01750         entryPtr = Tcl_FirstHashEntry(&nsPtr->cmdTable, &search);
01751         while (entryPtr != NULL) {
01752             cmdName = Tcl_GetHashKey(&nsPtr->cmdTable, entryPtr);
01753             if ((simplePattern == NULL)
01754                     || Tcl_StringMatch(cmdName, simplePattern)) {
01755                 cmdPtr = (Command *) Tcl_GetHashValue(entryPtr);
01756 
01757                 if (!TclIsProc(cmdPtr)) {
01758                     realCmdPtr = (Command *)
01759                             TclGetOriginalCommand((Tcl_Command) cmdPtr);
01760                     if (realCmdPtr != NULL && TclIsProc(realCmdPtr)) {
01761                         goto procOK;
01762                     }
01763                 } else {
01764                 procOK:
01765                     if (specificNsInPattern) {
01766                         elemObjPtr = Tcl_NewObj();
01767                         Tcl_GetCommandFullName(interp, (Tcl_Command) cmdPtr,
01768                                 elemObjPtr);
01769                     } else {
01770                         elemObjPtr = Tcl_NewStringObj(cmdName, -1);
01771                     }
01772                     Tcl_ListObjAppendElement(interp, listPtr, elemObjPtr);
01773                 }
01774             }
01775             entryPtr = Tcl_NextHashEntry(&search);
01776         }
01777 
01778         /*
01779          * If the effective namespace isn't the global :: namespace, and a
01780          * specific namespace wasn't requested in the pattern, then add in all
01781          * global :: procs that match the simple pattern. Of course, we add in
01782          * only those procs that aren't hidden by a proc in the effective
01783          * namespace.
01784          */
01785 
01786 #ifdef INFO_PROCS_SEARCH_GLOBAL_NS
01787         /*
01788          * If "info procs" worked like "info commands", returning the commands
01789          * also seen in the global namespace, then you would include this
01790          * code. As this could break backwards compatibilty with 8.0-8.2, we
01791          * decided not to "fix" it in 8.3, leaving the behavior slightly
01792          * different.
01793          */
01794 
01795         if ((nsPtr != globalNsPtr) && !specificNsInPattern) {
01796             entryPtr = Tcl_FirstHashEntry(&globalNsPtr->cmdTable, &search);
01797             while (entryPtr != NULL) {
01798                 cmdName = Tcl_GetHashKey(&globalNsPtr->cmdTable, entryPtr);
01799                 if ((simplePattern == NULL)
01800                         || Tcl_StringMatch(cmdName, simplePattern)) {
01801                     if (Tcl_FindHashEntry(&nsPtr->cmdTable,cmdName) == NULL) {
01802                         cmdPtr = (Command *) Tcl_GetHashValue(entryPtr);
01803                         realCmdPtr = (Command *) TclGetOriginalCommand(
01804                                 (Tcl_Command) cmdPtr);
01805 
01806                         if (TclIsProc(cmdPtr) || ((realCmdPtr != NULL)
01807                                 && TclIsProc(realCmdPtr))) {
01808                             Tcl_ListObjAppendElement(interp, listPtr,
01809                                     Tcl_NewStringObj(cmdName, -1));
01810                         }
01811                     }
01812                 }
01813                 entryPtr = Tcl_NextHashEntry(&search);
01814             }
01815         }
01816 #endif
01817     }
01818 
01819     Tcl_SetObjResult(interp, listPtr);
01820     return TCL_OK;
01821 }
01822 
01823 /*
01824  *----------------------------------------------------------------------
01825  *
01826  * InfoScriptCmd --
01827  *
01828  *      Called to implement the "info script" command that returns the script
01829  *      file that is currently being evaluated. Handles the following syntax:
01830  *
01831  *          info script ?newName?
01832  *
01833  *      If newName is specified, it will set that as the internal name.
01834  *
01835  * Results:
01836  *      Returns TCL_OK if successful and TCL_ERROR if there is an error.
01837  *
01838  * Side effects:
01839  *      Returns a result in the interpreter's result object. If there is an
01840  *      error, the result is an error message. It may change the internal
01841  *      script filename.
01842  *
01843  *----------------------------------------------------------------------
01844  */
01845 
01846 static int
01847 InfoScriptCmd(
01848     ClientData dummy,           /* Not used. */
01849     Tcl_Interp *interp,         /* Current interpreter. */
01850     int objc,                   /* Number of arguments. */
01851     Tcl_Obj *CONST objv[])      /* Argument objects. */
01852 {
01853     Interp *iPtr = (Interp *) interp;
01854     if ((objc != 1) && (objc != 2)) {
01855         Tcl_WrongNumArgs(interp, 1, objv, "?filename?");
01856         return TCL_ERROR;
01857     }
01858 
01859     if (objc == 2) {
01860         if (iPtr->scriptFile != NULL) {
01861             Tcl_DecrRefCount(iPtr->scriptFile);
01862         }
01863         iPtr->scriptFile = objv[1];
01864         Tcl_IncrRefCount(iPtr->scriptFile);
01865     }
01866     if (iPtr->scriptFile != NULL) {
01867         Tcl_SetObjResult(interp, iPtr->scriptFile);
01868     }
01869     return TCL_OK;
01870 }
01871 
01872 /*
01873  *----------------------------------------------------------------------
01874  *
01875  * InfoSharedlibCmd --
01876  *
01877  *      Called to implement the "info sharedlibextension" command that returns
01878  *      the file extension used for shared libraries. Handles the following
01879  *      syntax:
01880  *
01881  *          info sharedlibextension
01882  *
01883  * Results:
01884  *      Returns TCL_OK if successful and TCL_ERROR if there is an error.
01885  *
01886  * Side effects:
01887  *      Returns a result in the interpreter's result object. If there is an
01888  *      error, the result is an error message.
01889  *
01890  *----------------------------------------------------------------------
01891  */
01892 
01893 static int
01894 InfoSharedlibCmd(
01895     ClientData dummy,           /* Not used. */
01896     Tcl_Interp *interp,         /* Current interpreter. */
01897     int objc,                   /* Number of arguments. */
01898     Tcl_Obj *CONST objv[])      /* Argument objects. */
01899 {
01900     if (objc != 1) {
01901         Tcl_WrongNumArgs(interp, 1, objv, NULL);
01902         return TCL_ERROR;
01903     }
01904 
01905 #ifdef TCL_SHLIB_EXT
01906     Tcl_SetObjResult(interp, Tcl_NewStringObj(TCL_SHLIB_EXT, -1));
01907 #endif
01908     return TCL_OK;
01909 }
01910 
01911 /*
01912  *----------------------------------------------------------------------
01913  *
01914  * InfoTclVersionCmd --
01915  *
01916  *      Called to implement the "info tclversion" command that returns the
01917  *      version number for this Tcl library. Handles the following syntax:
01918  *
01919  *          info tclversion
01920  *
01921  * Results:
01922  *      Returns TCL_OK if successful and TCL_ERROR if there is an error.
01923  *
01924  * Side effects:
01925  *      Returns a result in the interpreter's result object. If there is an
01926  *      error, the result is an error message.
01927  *
01928  *----------------------------------------------------------------------
01929  */
01930 
01931 static int
01932 InfoTclVersionCmd(
01933     ClientData dummy,           /* Not used. */
01934     Tcl_Interp *interp,         /* Current interpreter. */
01935     int objc,                   /* Number of arguments. */
01936     Tcl_Obj *CONST objv[])      /* Argument objects. */
01937 {
01938     Tcl_Obj *version;
01939 
01940     if (objc != 1) {
01941         Tcl_WrongNumArgs(interp, 1, objv, NULL);
01942         return TCL_ERROR;
01943     }
01944 
01945     version = Tcl_GetVar2Ex(interp, "tcl_version", NULL,
01946             (TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG));
01947     if (version != NULL) {
01948         Tcl_SetObjResult(interp, version);
01949         return TCL_OK;
01950     }
01951     return TCL_ERROR;
01952 }
01953 
01954 /*
01955  *----------------------------------------------------------------------
01956  *
01957  * Tcl_JoinObjCmd --
01958  *
01959  *      This procedure is invoked to process the "join" Tcl command. See the
01960  *      user documentation for details on what it does.
01961  *
01962  * Results:
01963  *      A standard Tcl object result.
01964  *
01965  * Side effects:
01966  *      See the user documentation.
01967  *
01968  *----------------------------------------------------------------------
01969  */
01970 
01971 int
01972 Tcl_JoinObjCmd(
01973     ClientData dummy,           /* Not used. */
01974     Tcl_Interp *interp,         /* Current interpreter. */
01975     int objc,                   /* Number of arguments. */
01976     Tcl_Obj *CONST objv[])      /* The argument objects. */
01977 {
01978     int listLen, i;
01979     Tcl_Obj *resObjPtr, *joinObjPtr, **elemPtrs;
01980 
01981     if ((objc < 2) || (objc > 3)) {
01982         Tcl_WrongNumArgs(interp, 1, objv, "list ?joinString?");
01983         return TCL_ERROR;
01984     }
01985 
01986     /*
01987      * Make sure the list argument is a list object and get its length and a
01988      * pointer to its array of element pointers.
01989      */
01990 
01991     if (TclListObjGetElements(interp, objv[1], &listLen,
01992             &elemPtrs) != TCL_OK) {
01993         return TCL_ERROR;
01994     }
01995 
01996     joinObjPtr = (objc == 2) ? Tcl_NewStringObj(" ", 1) : objv[2];
01997     Tcl_IncrRefCount(joinObjPtr);
01998 
01999     resObjPtr = Tcl_NewObj();
02000     for (i = 0;  i < listLen;  i++) {
02001         if (i > 0) {
02002             Tcl_AppendObjToObj(resObjPtr, joinObjPtr);
02003         }
02004         Tcl_AppendObjToObj(resObjPtr, elemPtrs[i]);
02005     }
02006     Tcl_DecrRefCount(joinObjPtr);
02007     Tcl_SetObjResult(interp, resObjPtr);
02008     return TCL_OK;
02009 }
02010 
02011 /*
02012  *----------------------------------------------------------------------
02013  *
02014  * Tcl_LassignObjCmd --
02015  *
02016  *      This object-based procedure is invoked to process the "lassign" Tcl
02017  *      command. See the user documentation for details on what it does.
02018  *
02019  * Results:
02020  *      A standard Tcl object result.
02021  *
02022  * Side effects:
02023  *      See the user documentation.
02024  *
02025  *----------------------------------------------------------------------
02026  */
02027 
02028 int
02029 Tcl_LassignObjCmd(
02030     ClientData dummy,           /* Not used. */
02031     Tcl_Interp *interp,         /* Current interpreter. */
02032     int objc,                   /* Number of arguments. */
02033     Tcl_Obj *CONST objv[])      /* Argument objects. */
02034 {
02035     Tcl_Obj *listCopyPtr;
02036     Tcl_Obj **listObjv;         /* The contents of the list. */
02037     int listObjc;               /* The length of the list. */
02038     int code = TCL_OK;
02039 
02040     if (objc < 3) {
02041         Tcl_WrongNumArgs(interp, 1, objv, "list varName ?varName ...?");
02042         return TCL_ERROR;
02043     }
02044 
02045     listCopyPtr = TclListObjCopy(interp, objv[1]);
02046     if (listCopyPtr == NULL) {
02047         return TCL_ERROR;
02048     }
02049 
02050     TclListObjGetElements(NULL, listCopyPtr, &listObjc, &listObjv);
02051 
02052     objc -= 2;
02053     objv += 2;
02054     while (code == TCL_OK && objc > 0 && listObjc > 0) {
02055         if (NULL == Tcl_ObjSetVar2(interp, *objv++, NULL,
02056                 *listObjv++, TCL_LEAVE_ERR_MSG)) {
02057             code = TCL_ERROR;
02058         }
02059         objc--; listObjc--;
02060     }
02061 
02062     if (code == TCL_OK && objc > 0) {
02063         Tcl_Obj *emptyObj;
02064         TclNewObj(emptyObj);
02065         Tcl_IncrRefCount(emptyObj);
02066         while (code == TCL_OK && objc-- > 0) {
02067             if (NULL == Tcl_ObjSetVar2(interp, *objv++, NULL,
02068                     emptyObj, TCL_LEAVE_ERR_MSG)) {
02069                 code = TCL_ERROR;
02070             }
02071         }
02072         Tcl_DecrRefCount(emptyObj);
02073     }
02074 
02075     if (code == TCL_OK && listObjc > 0) {
02076         Tcl_SetObjResult(interp, Tcl_NewListObj(listObjc, listObjv));
02077     }
02078 
02079     Tcl_DecrRefCount(listCopyPtr);
02080     return code;
02081 }
02082 
02083 /*
02084  *----------------------------------------------------------------------
02085  *
02086  * Tcl_LindexObjCmd --
02087  *
02088  *      This object-based procedure is invoked to process the "lindex" Tcl
02089  *      command. See the user documentation for details on what it does.
02090  *
02091  * Results:
02092  *      A standard Tcl object result.
02093  *
02094  * Side effects:
02095  *      See the user documentation.
02096  *
02097  *----------------------------------------------------------------------
02098  */
02099 
02100 int
02101 Tcl_LindexObjCmd(
02102     ClientData dummy,           /* Not used. */
02103     Tcl_Interp *interp,         /* Current interpreter. */
02104     int objc,                   /* Number of arguments. */
02105     Tcl_Obj *CONST objv[])      /* Argument objects. */
02106 {
02107 
02108     Tcl_Obj *elemPtr;           /* Pointer to the element being extracted. */
02109 
02110     if (objc < 2) {
02111         Tcl_WrongNumArgs(interp, 1, objv, "list ?index...?");
02112         return TCL_ERROR;
02113     }
02114 
02115     /*
02116      * If objc==3, then objv[2] may be either a single index or a list of
02117      * indices: go to TclLindexList to determine which. If objc>=4, or
02118      * objc==2, then objv[2 .. objc-2] are all single indices and processed as
02119      * such in TclLindexFlat.
02120      */
02121 
02122     if (objc == 3) {
02123         elemPtr = TclLindexList(interp, objv[1], objv[2]);
02124     } else {
02125         elemPtr = TclLindexFlat(interp, objv[1], objc-2, objv+2);
02126     }
02127 
02128     /*
02129      * Set the interpreter's object result to the last element extracted.
02130      */
02131 
02132     if (elemPtr == NULL) {
02133         return TCL_ERROR;
02134     } else {
02135         Tcl_SetObjResult(interp, elemPtr);
02136         Tcl_DecrRefCount(elemPtr);
02137         return TCL_OK;
02138     }
02139 }
02140 
02141 /*
02142  *----------------------------------------------------------------------
02143  *
02144  * Tcl_LinsertObjCmd --
02145  *
02146  *      This object-based procedure is invoked to process the "linsert" Tcl
02147  *      command. See the user documentation for details on what it does.
02148  *
02149  * Results:
02150  *      A new Tcl list object formed by inserting zero or more elements into a
02151  *      list.
02152  *
02153  * Side effects:
02154  *      See the user documentation.
02155  *
02156  *----------------------------------------------------------------------
02157  */
02158 
02159 int
02160 Tcl_LinsertObjCmd(
02161     ClientData dummy,           /* Not used. */
02162     Tcl_Interp *interp,         /* Current interpreter. */
02163     register int objc,          /* Number of arguments. */
02164     Tcl_Obj *CONST objv[])      /* Argument objects. */
02165 {
02166     Tcl_Obj *listPtr;
02167     int index, len, result;
02168 
02169     if (objc < 4) {
02170         Tcl_WrongNumArgs(interp, 1, objv, "list index element ?element ...?");
02171         return TCL_ERROR;
02172     }
02173 
02174     result = TclListObjLength(interp, objv[1], &len);
02175     if (result != TCL_OK) {
02176         return result;
02177     }
02178 
02179     /*
02180      * Get the index. "end" is interpreted to be the index after the last
02181      * element, such that using it will cause any inserted elements to be
02182      * appended to the list.
02183      */
02184 
02185     result = TclGetIntForIndexM(interp, objv[2], /*end*/ len, &index);
02186     if (result != TCL_OK) {
02187         return result;
02188     }
02189     if (index > len) {
02190         index = len;
02191     }
02192 
02193     /*
02194      * If the list object is unshared we can modify it directly. Otherwise we
02195      * create a copy to modify: this is "copy on write".
02196      */
02197 
02198     listPtr = objv[1];
02199     if (Tcl_IsShared(listPtr)) {
02200         listPtr = TclListObjCopy(NULL, listPtr);
02201     }
02202 
02203     if ((objc == 4) && (index == len)) {
02204         /*
02205          * Special case: insert one element at the end of the list.
02206          */
02207 
02208         Tcl_ListObjAppendElement(NULL, listPtr, objv[3]);
02209     } else {
02210         Tcl_ListObjReplace(NULL, listPtr, index, 0, (objc-3), &(objv[3]));
02211     }
02212 
02213     /*
02214      * Set the interpreter's object result.
02215      */
02216 
02217     Tcl_SetObjResult(interp, listPtr);
02218     return TCL_OK;
02219 }
02220 
02221 /*
02222  *----------------------------------------------------------------------
02223  *
02224  * Tcl_ListObjCmd --
02225  *
02226  *      This procedure is invoked to process the "list" Tcl command. See the
02227  *      user documentation for details on what it does.
02228  *
02229  * Results:
02230  *      A standard Tcl object result.
02231  *
02232  * Side effects:
02233  *      See the user documentation.
02234  *
02235  *----------------------------------------------------------------------
02236  */
02237 
02238 int
02239 Tcl_ListObjCmd(
02240     ClientData dummy,           /* Not used. */
02241     Tcl_Interp *interp,         /* Current interpreter. */
02242     register int objc,          /* Number of arguments. */
02243     register Tcl_Obj *CONST objv[])
02244                                 /* The argument objects. */
02245 {
02246     /*
02247      * If there are no list elements, the result is an empty object.
02248      * Otherwise set the interpreter's result object to be a list object.
02249      */
02250 
02251     if (objc > 1) {
02252         Tcl_SetObjResult(interp, Tcl_NewListObj((objc-1), &(objv[1])));
02253     }
02254     return TCL_OK;
02255 }
02256 
02257 /*
02258  *----------------------------------------------------------------------
02259  *
02260  * Tcl_LlengthObjCmd --
02261  *
02262  *      This object-based procedure is invoked to process the "llength" Tcl
02263  *      command. See the user documentation for details on what it does.
02264  *
02265  * Results:
02266  *      A standard Tcl object result.
02267  *
02268  * Side effects:
02269  *      See the user documentation.
02270  *
02271  *----------------------------------------------------------------------
02272  */
02273 
02274 int
02275 Tcl_LlengthObjCmd(
02276     ClientData dummy,           /* Not used. */
02277     Tcl_Interp *interp,         /* Current interpreter. */
02278     int objc,                   /* Number of arguments. */
02279     register Tcl_Obj *CONST objv[])
02280                                 /* Argument objects. */
02281 {
02282     int listLen, result;
02283 
02284     if (objc != 2) {
02285         Tcl_WrongNumArgs(interp, 1, objv, "list");
02286         return TCL_ERROR;
02287     }
02288 
02289     result = TclListObjLength(interp, objv[1], &listLen);
02290     if (result != TCL_OK) {
02291         return result;
02292     }
02293 
02294     /*
02295      * Set the interpreter's object result to an integer object holding the
02296      * length.
02297      */
02298 
02299     Tcl_SetObjResult(interp, Tcl_NewIntObj(listLen));
02300     return TCL_OK;
02301 }
02302 
02303 /*
02304  *----------------------------------------------------------------------
02305  *
02306  * Tcl_LrangeObjCmd --
02307  *
02308  *      This procedure is invoked to process the "lrange" Tcl command. See the
02309  *      user documentation for details on what it does.
02310  *
02311  * Results:
02312  *      A standard Tcl object result.
02313  *
02314  * Side effects:
02315  *      See the user documentation.
02316  *
02317  *----------------------------------------------------------------------
02318  */
02319 
02320 int
02321 Tcl_LrangeObjCmd(
02322     ClientData notUsed,         /* Not used. */
02323     Tcl_Interp *interp,         /* Current interpreter. */
02324     int objc,                   /* Number of arguments. */
02325     register Tcl_Obj *CONST objv[])
02326                                 /* Argument objects. */
02327 {
02328     Tcl_Obj *listPtr, **elemPtrs;
02329     int listLen, first, result;
02330 
02331     if (objc != 4) {
02332         Tcl_WrongNumArgs(interp, 1, objv, "list first last");
02333         return TCL_ERROR;
02334     }
02335 
02336     /*
02337      * Make sure the list argument is a list object and get its length and a
02338      * pointer to its array of element pointers.
02339      */
02340 
02341     listPtr = TclListObjCopy(interp, objv[1]);
02342     if (listPtr == NULL) {
02343         return TCL_ERROR;
02344     }
02345     TclListObjGetElements(NULL, listPtr, &listLen, &elemPtrs);
02346 
02347     result = TclGetIntForIndexM(interp, objv[2], /*endValue*/ listLen - 1,
02348             &first);
02349     if (result == TCL_OK) {
02350         int last;
02351 
02352         if (first < 0) {
02353             first = 0;
02354         }
02355 
02356         result = TclGetIntForIndexM(interp, objv[3], /*endValue*/ listLen - 1,
02357                 &last);
02358         if (result == TCL_OK) {
02359             if (last >= listLen) {
02360                 last = (listLen - 1);
02361             }
02362 
02363             if (first <= last) {
02364                 int numElems = (last - first + 1);
02365 
02366                 Tcl_SetObjResult(interp,
02367                         Tcl_NewListObj(numElems, &(elemPtrs[first])));
02368             }
02369         }
02370     }
02371 
02372     Tcl_DecrRefCount(listPtr);
02373     return result;
02374 }
02375 
02376 /*
02377  *----------------------------------------------------------------------
02378  *
02379  * Tcl_LrepeatObjCmd --
02380  *
02381  *      This procedure is invoked to process the "lrepeat" Tcl command. See
02382  *      the user documentation for details on what it does.
02383  *
02384  * Results:
02385  *      A standard Tcl object result.
02386  *
02387  * Side effects:
02388  *      See the user documentation.
02389  *
02390  *----------------------------------------------------------------------
02391  */
02392 
02393 int
02394 Tcl_LrepeatObjCmd(
02395     ClientData dummy,           /* Not used. */
02396     Tcl_Interp *interp,         /* Current interpreter. */
02397     register int objc,          /* Number of arguments. */
02398     register Tcl_Obj *CONST objv[])
02399                                 /* The argument objects. */
02400 {
02401     int elementCount, i, result;
02402     Tcl_Obj *listPtr, **dataArray;
02403     List *listRepPtr;
02404 
02405     /*
02406      * Check arguments for legality:
02407      *          lrepeat posInt value ?value ...?
02408      */
02409 
02410     if (objc < 3) {
02411         Tcl_WrongNumArgs(interp, 1, objv, "positiveCount value ?value ...?");
02412         return TCL_ERROR;
02413     }
02414     result = TclGetIntFromObj(interp, objv[1], &elementCount);
02415     if (result == TCL_ERROR) {
02416         return TCL_ERROR;
02417     }
02418     if (elementCount < 1) {
02419         Tcl_AppendResult(interp, "must have a count of at least 1", NULL);
02420         return TCL_ERROR;
02421     }
02422 
02423     /*
02424      * Skip forward to the interesting arguments now we've finished parsing.
02425      */
02426 
02427     objc -= 2;
02428     objv += 2;
02429 
02430     /*
02431      * Get an empty list object that is allocated large enough to hold each
02432      * init value elementCount times.
02433      */
02434 
02435     listPtr = Tcl_NewListObj(elementCount*objc, NULL);
02436     listRepPtr = (List *) listPtr->internalRep.twoPtrValue.ptr1;
02437     listRepPtr->elemCount = elementCount*objc;
02438     dataArray = &listRepPtr->elements;
02439 
02440     /*
02441      * Set the elements. Note that we handle the common degenerate case of a
02442      * single value being repeated separately to permit the compiler as much
02443      * room as possible to optimize a loop that might be run a very large
02444      * number of times.
02445      */
02446 
02447     if (objc == 1) {
02448         register Tcl_Obj *tmpPtr = objv[0];
02449 
02450         tmpPtr->refCount += elementCount;
02451         for (i=0 ; i<elementCount ; i++) {
02452             dataArray[i] = tmpPtr;
02453         }
02454     } else {
02455         int j, k = 0;
02456 
02457         for (i=0 ; i<elementCount ; i++) {
02458             for (j=0 ; j<objc ; j++) {
02459                 Tcl_IncrRefCount(objv[j]);
02460                 dataArray[k++] = objv[j];
02461             }
02462         }
02463     }
02464 
02465     Tcl_SetObjResult(interp, listPtr);
02466     return TCL_OK;
02467 }
02468 
02469 /*
02470  *----------------------------------------------------------------------
02471  *
02472  * Tcl_LreplaceObjCmd --
02473  *
02474  *      This object-based procedure is invoked to process the "lreplace" Tcl
02475  *      command. See the user documentation for details on what it does.
02476  *
02477  * Results:
02478  *      A new Tcl list object formed by replacing zero or more elements of a
02479  *      list.
02480  *
02481  * Side effects:
02482  *      See the user documentation.
02483  *
02484  *----------------------------------------------------------------------
02485  */
02486 
02487 int
02488 Tcl_LreplaceObjCmd(
02489     ClientData dummy,           /* Not used. */
02490     Tcl_Interp *interp,         /* Current interpreter. */
02491     int objc,                   /* Number of arguments. */
02492     Tcl_Obj *CONST objv[])      /* Argument objects. */
02493 {
02494     register Tcl_Obj *listPtr;
02495     int first, last, listLen, numToDelete, result;
02496 
02497     if (objc < 4) {
02498         Tcl_WrongNumArgs(interp, 1, objv,
02499                 "list first last ?element element ...?");
02500         return TCL_ERROR;
02501     }
02502 
02503     result = TclListObjLength(interp, objv[1], &listLen);
02504     if (result != TCL_OK) {
02505         return result;
02506     }
02507 
02508     /*
02509      * Get the first and last indexes. "end" is interpreted to be the index
02510      * for the last element, such that using it will cause that element to be
02511      * included for deletion.
02512      */
02513 
02514     result = TclGetIntForIndexM(interp, objv[2], /*end*/ listLen-1, &first);
02515     if (result != TCL_OK) {
02516         return result;
02517     }
02518 
02519     result = TclGetIntForIndexM(interp, objv[3], /*end*/ listLen-1, &last);
02520     if (result != TCL_OK) {
02521         return result;
02522     }
02523 
02524     if (first < 0) {
02525         first = 0;
02526     }
02527 
02528     /*
02529      * Complain if the user asked for a start element that is greater than the
02530      * list length. This won't ever trigger for the "end-*" case as that will
02531      * be properly constrained by TclGetIntForIndex because we use listLen-1
02532      * (to allow for replacing the last elem).
02533      */
02534 
02535     if ((first >= listLen) && (listLen > 0)) {
02536         Tcl_AppendResult(interp, "list doesn't contain element ",
02537                 TclGetString(objv[2]), NULL);
02538         return TCL_ERROR;
02539     }
02540     if (last >= listLen) {
02541         last = (listLen - 1);
02542     }
02543     if (first <= last) {
02544         numToDelete = (last - first + 1);
02545     } else {
02546         numToDelete = 0;
02547     }
02548 
02549     /*
02550      * If the list object is unshared we can modify it directly, otherwise we
02551      * create a copy to modify: this is "copy on write".
02552      */
02553 
02554     listPtr = objv[1];
02555     if (Tcl_IsShared(listPtr)) {
02556         listPtr = TclListObjCopy(NULL, listPtr);
02557     }
02558 
02559     /*
02560      * Note that we call Tcl_ListObjReplace even when numToDelete == 0 and
02561      * objc == 4. In this case, the list value of listPtr is not changed (no
02562      * elements are removed or added), but by making the call we are assured
02563      * we end up with a list in canonical form. Resist any temptation to
02564      * optimize this case away.
02565      */
02566 
02567     Tcl_ListObjReplace(NULL, listPtr, first, numToDelete, objc-4, &(objv[4]));
02568 
02569     /*
02570      * Set the interpreter's object result.
02571      */
02572 
02573     Tcl_SetObjResult(interp, listPtr);
02574     return TCL_OK;
02575 }
02576 
02577 /*
02578  *----------------------------------------------------------------------
02579  *
02580  * Tcl_LreverseObjCmd --
02581  *
02582  *      This procedure is invoked to process the "lreverse" Tcl command. See
02583  *      the user documentation for details on what it does.
02584  *
02585  * Results:
02586  *      A standard Tcl result.
02587  *
02588  * Side effects:
02589  *      See the user documentation.
02590  *
02591  *----------------------------------------------------------------------
02592  */
02593 
02594 int
02595 Tcl_LreverseObjCmd(
02596     ClientData clientData,      /* Not used. */
02597     Tcl_Interp *interp,         /* Current interpreter. */
02598     int objc,                   /* Number of arguments. */
02599     Tcl_Obj *CONST objv[])      /* Argument values. */
02600 {
02601     Tcl_Obj **elemv;
02602     int elemc, i, j;
02603 
02604     if (objc != 2) {
02605         Tcl_WrongNumArgs(interp, 1, objv, "list");
02606         return TCL_ERROR;
02607     }
02608     if (TclListObjGetElements(interp, objv[1], &elemc, &elemv) != TCL_OK) {
02609         return TCL_ERROR;
02610     }
02611 
02612     /*
02613      * If the list is empty, just return it [Bug 1876793]
02614      */
02615 
02616     if (!elemc) {
02617         Tcl_SetObjResult(interp, objv[1]);
02618         return TCL_OK;
02619     }
02620 
02621     if (Tcl_IsShared(objv[1])) {
02622         Tcl_Obj *resultObj, **dataArray;
02623         List *listPtr;
02624 
02625     makeNewReversedList:
02626         resultObj = Tcl_NewListObj(elemc, NULL);
02627         listPtr = (List *) resultObj->internalRep.twoPtrValue.ptr1;
02628         listPtr->elemCount = elemc;
02629         dataArray = &listPtr->elements;
02630 
02631         for (i=0,j=elemc-1 ; i<elemc ; i++,j--) {
02632             dataArray[j] = elemv[i];
02633             Tcl_IncrRefCount(elemv[i]);
02634         }
02635 
02636         Tcl_SetObjResult(interp, resultObj);
02637     } else {
02638         /*
02639          * It is theoretically possible for a list object to have a shared
02640          * internal representation, but be an unshared object. Check for this
02641          * and use the "shared" code if we have that problem. [Bug 1675044]
02642          */
02643 
02644         if (((List *) objv[1]->internalRep.twoPtrValue.ptr1)->refCount > 1) {
02645             goto makeNewReversedList;
02646         }
02647 
02648         /*
02649          * Not shared, so swap "in place". This relies on Tcl_LOGE above
02650          * returning a pointer to the live array of Tcl_Obj values.
02651          */
02652 
02653         for (i=0,j=elemc-1 ; i<j ; i++,j--) {
02654             Tcl_Obj *tmp = elemv[i];
02655 
02656             elemv[i] = elemv[j];
02657             elemv[j] = tmp;
02658         }
02659         TclInvalidateStringRep(objv[1]);
02660         Tcl_SetObjResult(interp, objv[1]);
02661     }
02662     return TCL_OK;
02663 }
02664 
02665 /*
02666  *----------------------------------------------------------------------
02667  *
02668  * Tcl_LsearchObjCmd --
02669  *
02670  *      This procedure is invoked to process the "lsearch" Tcl command. See
02671  *      the user documentation for details on what it does.
02672  *
02673  * Results:
02674  *      A standard Tcl result.
02675  *
02676  * Side effects:
02677  *      See the user documentation.
02678  *
02679  *----------------------------------------------------------------------
02680  */
02681 
02682 int
02683 Tcl_LsearchObjCmd(
02684     ClientData clientData,      /* Not used. */
02685     Tcl_Interp *interp,         /* Current interpreter. */
02686     int objc,                   /* Number of arguments. */
02687     Tcl_Obj *CONST objv[])      /* Argument values. */
02688 {
02689     char *bytes, *patternBytes;
02690     int i, match, mode, index, result, listc, length, elemLen;
02691     int dataType, isIncreasing, lower, upper, patInt, objInt, offset;
02692     int allMatches, inlineReturn, negatedMatch, returnSubindices, noCase;
02693     double patDouble, objDouble;
02694     SortInfo sortInfo;
02695     Tcl_Obj *patObj, **listv, *listPtr, *startPtr, *itemPtr;
02696     SortStrCmpFn_t strCmpFn = strcmp;
02697     Tcl_RegExp regexp = NULL;
02698     static CONST char *options[] = {
02699         "-all",     "-ascii",   "-decreasing", "-dictionary",
02700         "-exact",   "-glob",    "-increasing", "-index",
02701         "-inline",  "-integer", "-nocase",     "-not",
02702         "-real",    "-regexp",  "-sorted",     "-start",
02703         "-subindices", NULL
02704     };
02705     enum options {
02706         LSEARCH_ALL, LSEARCH_ASCII, LSEARCH_DECREASING, LSEARCH_DICTIONARY,
02707         LSEARCH_EXACT, LSEARCH_GLOB, LSEARCH_INCREASING, LSEARCH_INDEX,
02708         LSEARCH_INLINE, LSEARCH_INTEGER, LSEARCH_NOCASE, LSEARCH_NOT,
02709         LSEARCH_REAL, LSEARCH_REGEXP, LSEARCH_SORTED, LSEARCH_START,
02710         LSEARCH_SUBINDICES
02711     };
02712     enum datatypes {
02713         ASCII, DICTIONARY, INTEGER, REAL
02714     };
02715     enum modes {
02716         EXACT, GLOB, REGEXP, SORTED
02717     };
02718 
02719     mode = GLOB;
02720     dataType = ASCII;
02721     isIncreasing = 1;
02722     allMatches = 0;
02723     inlineReturn = 0;
02724     returnSubindices = 0;
02725     negatedMatch = 0;
02726     listPtr = NULL;
02727     startPtr = NULL;
02728     offset = 0;
02729     noCase = 0;
02730     sortInfo.compareCmdPtr = NULL;
02731     sortInfo.isIncreasing = 1;
02732     sortInfo.sortMode = 0;
02733     sortInfo.interp = interp;
02734     sortInfo.resultCode = TCL_OK;
02735     sortInfo.indexv = NULL;
02736     sortInfo.indexc = 0;
02737 
02738     if (objc < 3) {
02739         Tcl_WrongNumArgs(interp, 1, objv, "?options? list pattern");
02740         return TCL_ERROR;
02741     }
02742 
02743     for (i = 1; i < objc-2; i++) {
02744         if (Tcl_GetIndexFromObj(interp, objv[i], options, "option", 0, &index)
02745                 != TCL_OK) {
02746             if (startPtr != NULL) {
02747                 Tcl_DecrRefCount(startPtr);
02748             }
02749             if (sortInfo.indexc > 1) {
02750                 ckfree((char *) sortInfo.indexv);
02751             }
02752             return TCL_ERROR;
02753         }
02754         switch ((enum options) index) {
02755         case LSEARCH_ALL:               /* -all */
02756             allMatches = 1;
02757             break;
02758         case LSEARCH_ASCII:             /* -ascii */
02759             dataType = ASCII;
02760             break;
02761         case LSEARCH_DECREASING:        /* -decreasing */
02762             isIncreasing = 0;
02763             sortInfo.isIncreasing = 0;
02764             break;
02765         case LSEARCH_DICTIONARY:        /* -dictionary */
02766             dataType = DICTIONARY;
02767             break;
02768         case LSEARCH_EXACT:             /* -increasing */
02769             mode = EXACT;
02770             break;
02771         case LSEARCH_GLOB:              /* -glob */
02772             mode = GLOB;
02773             break;
02774         case LSEARCH_INCREASING:        /* -increasing */
02775             isIncreasing = 1;
02776             sortInfo.isIncreasing = 1;
02777             break;
02778         case LSEARCH_INLINE:            /* -inline */
02779             inlineReturn = 1;
02780             break;
02781         case LSEARCH_INTEGER:           /* -integer */
02782             dataType = INTEGER;
02783             break;
02784         case LSEARCH_NOCASE:            /* -nocase */
02785             strCmpFn = strcasecmp;
02786             noCase = 1;
02787             break;
02788         case LSEARCH_NOT:               /* -not */
02789             negatedMatch = 1;
02790             break;
02791         case LSEARCH_REAL:              /* -real */
02792             dataType = REAL;
02793             break;
02794         case LSEARCH_REGEXP:            /* -regexp */
02795             mode = REGEXP;
02796             break;
02797         case LSEARCH_SORTED:            /* -sorted */
02798             mode = SORTED;
02799             break;
02800         case LSEARCH_SUBINDICES:        /* -subindices */
02801             returnSubindices = 1;
02802             break;
02803         case LSEARCH_START:             /* -start */
02804             /*
02805              * If there was a previous -start option, release its saved index
02806              * because it will either be replaced or there will be an error.
02807              */
02808 
02809             if (startPtr != NULL) {
02810                 Tcl_DecrRefCount(startPtr);
02811             }
02812             if (i > objc-4) {
02813                 if (sortInfo.indexc > 1) {
02814                     ckfree((char *) sortInfo.indexv);
02815                 }
02816                 Tcl_AppendResult(interp, "missing starting index", NULL);
02817                 return TCL_ERROR;
02818             }
02819             i++;
02820             if (objv[i] == objv[objc - 2]) {
02821                 /*
02822                  * Take copy to prevent shimmering problems. Note that it does
02823                  * not matter if the index obj is also a component of the list
02824                  * being searched. We only need to copy where the list and the
02825                  * index are one-and-the-same.
02826                  */
02827 
02828                 startPtr = Tcl_DuplicateObj(objv[i]);
02829             } else {
02830                 startPtr = objv[i];
02831                 Tcl_IncrRefCount(startPtr);
02832             }
02833             break;
02834         case LSEARCH_INDEX: {           /* -index */
02835             Tcl_Obj **indices;
02836             int j;
02837 
02838             if (sortInfo.indexc > 1) {
02839                 ckfree((char *) sortInfo.indexv);
02840             }
02841             if (i > objc-4) {
02842                 if (startPtr != NULL) {
02843                     Tcl_DecrRefCount(startPtr);
02844                 }
02845                 Tcl_AppendResult(interp,
02846                         "\"-index\" option must be followed by list index",
02847                         NULL);
02848                 return TCL_ERROR;
02849             }
02850 
02851             /*
02852              * Store the extracted indices for processing by sublist
02853              * extraction. Note that we don't do this using objects because
02854              * that has shimmering problems.
02855              */
02856 
02857             i++;
02858             if (TclListObjGetElements(interp, objv[i],
02859                     &sortInfo.indexc, &indices) != TCL_OK) {
02860                 if (startPtr != NULL) {
02861                     Tcl_DecrRefCount(startPtr);
02862                 }
02863                 return TCL_ERROR;
02864             }
02865             switch (sortInfo.indexc) {
02866             case 0:
02867                 sortInfo.indexv = NULL;
02868                 break;
02869             case 1:
02870                 sortInfo.indexv = &sortInfo.singleIndex;
02871                 break;
02872             default:
02873                 sortInfo.indexv = (int *)
02874                         ckalloc(sizeof(int) * sortInfo.indexc);
02875             }
02876 
02877             /*
02878              * Fill the array by parsing each index. We don't know whether
02879              * their scale is sensible yet, but we at least perform the
02880              * syntactic check here.
02881              */
02882 
02883             for (j=0 ; j<sortInfo.indexc ; j++) {
02884                 if (TclGetIntForIndexM(interp, indices[j], SORTIDX_END,
02885                         &sortInfo.indexv[j]) != TCL_OK) {
02886                     if (sortInfo.indexc > 1) {
02887                         ckfree((char *) sortInfo.indexv);
02888                     }
02889                     Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf(
02890                             "\n    (-index option item number %d)", j));
02891                     return TCL_ERROR;
02892                 }
02893             }
02894             break;
02895         }
02896         }
02897     }
02898 
02899     /*
02900      * Subindices only make sense if asked for with -index option set.
02901      */
02902 
02903     if (returnSubindices && sortInfo.indexc==0) {
02904         if (startPtr != NULL) {
02905             Tcl_DecrRefCount(startPtr);
02906         }
02907         Tcl_AppendResult(interp,
02908                 "-subindices cannot be used without -index option", NULL);
02909         return TCL_ERROR;
02910     }
02911 
02912     if ((enum modes) mode == REGEXP) {
02913         /*
02914          * We can shimmer regexp/list if listv[i] == pattern, so get the
02915          * regexp rep before the list rep. First time round, omit the interp
02916          * and hope that the compilation will succeed. If it fails, we'll
02917          * recompile in "expensive" mode with a place to put error messages.
02918          */
02919 
02920         regexp = Tcl_GetRegExpFromObj(NULL, objv[objc - 1],
02921                 TCL_REG_ADVANCED | TCL_REG_NOSUB |
02922                 (noCase ? TCL_REG_NOCASE : 0));
02923         if (regexp == NULL) {
02924             /*
02925              * Failed to compile the RE. Try again without the TCL_REG_NOSUB
02926              * flag in case the RE had sub-expressions in it [Bug 1366683]. If
02927              * this fails, an error message will be left in the interpreter.
02928              */
02929 
02930             regexp = Tcl_GetRegExpFromObj(interp, objv[objc - 1],
02931                     TCL_REG_ADVANCED | (noCase ? TCL_REG_NOCASE : 0));
02932         }
02933 
02934         if (regexp == NULL) {
02935             if (startPtr != NULL) {
02936                 Tcl_DecrRefCount(startPtr);
02937             }
02938             if (sortInfo.indexc > 1) {
02939                 ckfree((char *) sortInfo.indexv);
02940             }
02941             return TCL_ERROR;
02942         }
02943     }
02944 
02945     /*
02946      * Make sure the list argument is a list object and get its length and a
02947      * pointer to its array of element pointers.
02948      */
02949 
02950     result = TclListObjGetElements(interp, objv[objc - 2], &listc, &listv);
02951     if (result != TCL_OK) {
02952         if (startPtr != NULL) {
02953             Tcl_DecrRefCount(startPtr);
02954         }
02955         if (sortInfo.indexc > 1) {
02956             ckfree((char *) sortInfo.indexv);
02957         }
02958         return result;
02959     }
02960 
02961     /*
02962      * Get the user-specified start offset.
02963      */
02964 
02965     if (startPtr) {
02966         result = TclGetIntForIndexM(interp, startPtr, listc-1, &offset);
02967         Tcl_DecrRefCount(startPtr);
02968         if (result != TCL_OK) {
02969             if (sortInfo.indexc > 1) {
02970                 ckfree((char *) sortInfo.indexv);
02971             }
02972             return result;
02973         }
02974         if (offset < 0) {
02975             offset = 0;
02976         }
02977 
02978         /*
02979          * If the search started past the end of the list, we just return a
02980          * "did not match anything at all" result straight away. [Bug 1374778]
02981          */
02982 
02983         if (offset > listc-1) {
02984             if (sortInfo.indexc > 1) {
02985                 ckfree((char *) sortInfo.indexv);
02986             }
02987             if (allMatches || inlineReturn) {
02988                 Tcl_ResetResult(interp);
02989             } else {
02990                 Tcl_SetObjResult(interp, Tcl_NewIntObj(-1));
02991             }
02992             return TCL_OK;
02993         }
02994     }
02995 
02996     patObj = objv[objc - 1];
02997     patternBytes = NULL;
02998     if ((enum modes) mode == EXACT || (enum modes) mode == SORTED) {
02999         switch ((enum datatypes) dataType) {
03000         case ASCII:
03001         case DICTIONARY:
03002             patternBytes = TclGetStringFromObj(patObj, &length);
03003             break;
03004         case INTEGER:
03005             result = TclGetIntFromObj(interp, patObj, &patInt);
03006             if (result != TCL_OK) {
03007                 if (sortInfo.indexc > 1) {
03008                     ckfree((char *) sortInfo.indexv);
03009                 }
03010                 return result;
03011             }
03012 
03013             /*
03014              * List representation might have been shimmered; restore it. [Bug
03015              * 1844789]
03016              */
03017 
03018             TclListObjGetElements(NULL, objv[objc - 2], &listc, &listv);
03019             break;
03020         case REAL:
03021             result = Tcl_GetDoubleFromObj(interp, patObj, &patDouble);
03022             if (result != TCL_OK) {
03023                 if (sortInfo.indexc > 1) {
03024                     ckfree((char *) sortInfo.indexv);
03025                 }
03026                 return result;
03027             }
03028 
03029             /*
03030              * List representation might have been shimmered; restore it. [Bug
03031              * 1844789]
03032              */
03033 
03034             TclListObjGetElements(NULL, objv[objc - 2], &listc, &listv);
03035             break;
03036         }
03037     } else {
03038         patternBytes = TclGetStringFromObj(patObj, &length);
03039     }
03040 
03041     /*
03042      * Set default index value to -1, indicating failure; if we find the item
03043      * in the course of our search, index will be set to the correct value.
03044      */
03045 
03046     index = -1;
03047     match = 0;
03048 
03049     if ((enum modes) mode == SORTED && !allMatches && !negatedMatch) {
03050         /*
03051          * If the data is sorted, we can do a more intelligent search. Note
03052          * that there is no point in being smart when -all was specified; in
03053          * that case, we have to look at all items anyway, and there is no
03054          * sense in doing this when the match sense is inverted.
03055          */
03056 
03057         lower = offset - 1;
03058         upper = listc;
03059         while (lower + 1 != upper && sortInfo.resultCode == TCL_OK) {
03060             i = (lower + upper)/2;
03061             if (sortInfo.indexc != 0) {
03062                 itemPtr = SelectObjFromSublist(listv[i], &sortInfo);
03063                 if (sortInfo.resultCode != TCL_OK) {
03064                     if (sortInfo.indexc > 1) {
03065                         ckfree((char *) sortInfo.indexv);
03066                     }
03067                     return sortInfo.resultCode;
03068                 }
03069             } else {
03070                 itemPtr = listv[i];
03071             }
03072             switch ((enum datatypes) dataType) {
03073             case ASCII:
03074                 bytes = TclGetString(itemPtr);
03075                 match = strCmpFn(patternBytes, bytes);
03076                 break;
03077             case DICTIONARY:
03078                 bytes = TclGetString(itemPtr);
03079                 match = DictionaryCompare(patternBytes, bytes);
03080                 break;
03081             case INTEGER:
03082                 result = TclGetIntFromObj(interp, itemPtr, &objInt);
03083                 if (result != TCL_OK) {
03084                     if (sortInfo.indexc > 1) {
03085                         ckfree((char *) sortInfo.indexv);
03086                     }
03087                     return result;
03088                 }
03089                 if (patInt == objInt) {
03090                     match = 0;
03091                 } else if (patInt < objInt) {
03092                     match = -1;
03093                 } else {
03094                     match = 1;
03095                 }
03096                 break;
03097             case REAL:
03098                 result = Tcl_GetDoubleFromObj(interp, itemPtr, &objDouble);
03099                 if (result != TCL_OK) {
03100                     if (sortInfo.indexc > 1) {
03101                         ckfree((char *) sortInfo.indexv);
03102                     }
03103                     return result;
03104                 }
03105                 if (patDouble == objDouble) {
03106                     match = 0;
03107                 } else if (patDouble < objDouble) {
03108                     match = -1;
03109                 } else {
03110                     match = 1;
03111                 }
03112                 break;
03113             }
03114             if (match == 0) {
03115                 /*
03116                  * Normally, binary search is written to stop when it finds a
03117                  * match. If there are duplicates of an element in the list,
03118                  * our first match might not be the first occurance.
03119                  * Consider: 0 0 0 1 1 1 2 2 2
03120                  *
03121                  * To maintain consistancy with standard lsearch semantics, we
03122                  * must find the leftmost occurance of the pattern in the
03123                  * list. Thus we don't just stop searching here. This
03124                  * variation means that a search always makes log n
03125                  * comparisons (normal binary search might "get lucky" with an
03126                  * early comparison).
03127                  */
03128 
03129                 index = i;
03130                 upper = i;
03131             } else if (match > 0) {
03132                 if (isIncreasing) {
03133                     lower = i;
03134                 } else {
03135                     upper = i;
03136                 }
03137             } else {
03138                 if (isIncreasing) {
03139                     upper = i;
03140                 } else {
03141                     lower = i;
03142                 }
03143             }
03144         }
03145 
03146     } else {
03147         /*
03148          * We need to do a linear search, because (at least one) of:
03149          *   - our matcher can only tell equal vs. not equal
03150          *   - our matching sense is negated
03151          *   - we're building a list of all matched items
03152          */
03153 
03154         if (allMatches) {
03155             listPtr = Tcl_NewListObj(0, NULL);
03156         }
03157         for (i = offset; i < listc; i++) {
03158             match = 0;
03159             if (sortInfo.indexc != 0) {     
03160                 itemPtr = SelectObjFromSublist(listv[i], &sortInfo);
03161                 if (sortInfo.resultCode != TCL_OK) {
03162                     if (listPtr != NULL) {
03163                         Tcl_DecrRefCount(listPtr);
03164                     }
03165                     if (sortInfo.indexc > 1) {
03166                         ckfree((char *) sortInfo.indexv);
03167                     }
03168                     return sortInfo.resultCode;
03169                 }
03170             } else {
03171                 itemPtr = listv[i];
03172             }
03173                 
03174             switch ((enum modes) mode) {
03175             case SORTED:
03176             case EXACT:
03177                 switch ((enum datatypes) dataType) {
03178                 case ASCII:
03179                     bytes = TclGetStringFromObj(itemPtr, &elemLen);
03180                     if (length == elemLen) {
03181                         /*
03182                          * This split allows for more optimal compilation of
03183                          * memcmp/strcasecmp.
03184                          */
03185 
03186                         if (noCase) {
03187                             match = (strcasecmp(bytes, patternBytes) == 0);
03188                         } else {
03189                             match = (memcmp(bytes, patternBytes,
03190                                     (size_t) length) == 0);
03191                         }
03192                     }
03193                     break;
03194 
03195                 case DICTIONARY:
03196                     bytes = TclGetString(itemPtr);
03197                     match = (DictionaryCompare(bytes, patternBytes) == 0);
03198                     break;
03199 
03200                 case INTEGER:
03201                     result = TclGetIntFromObj(interp, itemPtr, &objInt);
03202                     if (result != TCL_OK) {
03203                         if (listPtr != NULL) {
03204                             Tcl_DecrRefCount(listPtr);
03205                         }
03206                         if (sortInfo.indexc > 1) {
03207                             ckfree((char *) sortInfo.indexv);
03208                         }
03209                         return result;
03210                     }
03211                     match = (objInt == patInt);
03212                     break;
03213 
03214                 case REAL:
03215                     result = Tcl_GetDoubleFromObj(interp,itemPtr, &objDouble);
03216                     if (result != TCL_OK) {
03217                         if (listPtr) {
03218                             Tcl_DecrRefCount(listPtr);
03219                         }
03220                         if (sortInfo.indexc > 1) {
03221                             ckfree((char *) sortInfo.indexv);
03222                         }
03223                         return result;
03224                     }
03225                     match = (objDouble == patDouble);
03226                     break;
03227                 }
03228                 break;
03229 
03230             case GLOB:
03231                 match = Tcl_StringCaseMatch(TclGetString(itemPtr),
03232                         patternBytes, noCase);
03233                 break;
03234 
03235             case REGEXP:
03236                 match = Tcl_RegExpExecObj(interp, regexp, itemPtr, 0, 0, 0);
03237                 if (match < 0) {
03238                     Tcl_DecrRefCount(patObj);
03239                     if (listPtr != NULL) {
03240                         Tcl_DecrRefCount(listPtr);
03241                     }
03242                     if (sortInfo.indexc > 1) {
03243                         ckfree((char *) sortInfo.indexv);
03244                     }
03245                     return TCL_ERROR;
03246                 }
03247                 break;
03248             }
03249 
03250             /*
03251              * Invert match condition for -not.
03252              */
03253 
03254             if (negatedMatch) {
03255                 match = !match;
03256             }
03257             if (!match) {
03258                 continue;
03259             }
03260             if (!allMatches) {
03261                 index = i;
03262                 break;
03263             } else if (inlineReturn) {
03264                 /*
03265                  * Note that these appends are not expected to fail.
03266                  */
03267 
03268                 if (returnSubindices && (sortInfo.indexc != 0)) {
03269                     itemPtr = SelectObjFromSublist(listv[i], &sortInfo);
03270                 } else {
03271                     itemPtr = listv[i];
03272                 }
03273                 Tcl_ListObjAppendElement(interp, listPtr, itemPtr);
03274             } else if (returnSubindices) {
03275                 int j;
03276 
03277                 itemPtr = Tcl_NewIntObj(i);
03278                 for (j=0 ; j<sortInfo.indexc ; j++) {
03279                     Tcl_ListObjAppendElement(interp, itemPtr,
03280                             Tcl_NewIntObj(sortInfo.indexv[j]));
03281                 }
03282                 Tcl_ListObjAppendElement(interp, listPtr, itemPtr);
03283             } else {
03284                 Tcl_ListObjAppendElement(interp, listPtr, Tcl_NewIntObj(i));
03285             }
03286         }
03287     }
03288 
03289     /*
03290      * Return everything or a single value.
03291      */
03292 
03293     if (allMatches) {
03294         Tcl_SetObjResult(interp, listPtr);
03295     } else if (!inlineReturn) {
03296         if (returnSubindices) {
03297             int j;
03298 
03299             itemPtr = Tcl_NewIntObj(index);
03300             for (j=0 ; j<sortInfo.indexc ; j++) {
03301                 Tcl_ListObjAppendElement(interp, itemPtr,
03302                         Tcl_NewIntObj(sortInfo.indexv[j]));
03303             }
03304             Tcl_SetObjResult(interp, itemPtr);
03305         } else {
03306             Tcl_SetObjResult(interp, Tcl_NewIntObj(index));
03307         }
03308     } else if (index < 0) {
03309         /*
03310          * Is this superfluous? The result should be a blank object by
03311          * default...
03312          */
03313 
03314         Tcl_SetObjResult(interp, Tcl_NewObj());
03315     } else {
03316         Tcl_SetObjResult(interp, listv[index]);
03317     }
03318 
03319     /*
03320      * Cleanup the index list array.
03321      */
03322 
03323     if (sortInfo.indexc > 1) {
03324         ckfree((char *) sortInfo.indexv);
03325     }
03326     return TCL_OK;
03327 }
03328 
03329 /*
03330  *----------------------------------------------------------------------
03331  *
03332  * Tcl_LsetObjCmd --
03333  *
03334  *      This procedure is invoked to process the "lset" Tcl command. See the
03335  *      user documentation for details on what it does.
03336  *
03337  * Results:
03338  *      A standard Tcl result.
03339  *
03340  * Side effects:
03341  *      See the user documentation.
03342  *
03343  *----------------------------------------------------------------------
03344  */
03345 
03346 int
03347 Tcl_LsetObjCmd(
03348     ClientData clientData,      /* Not used. */
03349     Tcl_Interp *interp,         /* Current interpreter. */
03350     int objc,                   /* Number of arguments. */
03351     Tcl_Obj *CONST objv[])      /* Argument values. */
03352 {
03353     Tcl_Obj *listPtr;           /* Pointer to the list being altered. */
03354     Tcl_Obj *finalValuePtr;     /* Value finally assigned to the variable. */
03355 
03356     /*
03357      * Check parameter count.
03358      */
03359 
03360     if (objc < 3) {
03361         Tcl_WrongNumArgs(interp, 1, objv, "listVar index ?index...? value");
03362         return TCL_ERROR;
03363     }
03364 
03365     /*
03366      * Look up the list variable's value.
03367      */
03368 
03369     listPtr = Tcl_ObjGetVar2(interp, objv[1], (Tcl_Obj *) NULL,
03370             TCL_LEAVE_ERR_MSG);
03371     if (listPtr == NULL) {
03372         return TCL_ERROR;
03373     }
03374 
03375     /*
03376      * Substitute the value in the value. Return either the value or else an
03377      * unshared copy of it.
03378      */
03379 
03380     if (objc == 4) {
03381         finalValuePtr = TclLsetList(interp, listPtr, objv[2], objv[3]);
03382     } else {
03383         finalValuePtr = TclLsetFlat(interp, listPtr, objc-3, objv+2,
03384                 objv[objc-1]);
03385     }
03386 
03387     /*
03388      * If substitution has failed, bail out.
03389      */
03390 
03391     if (finalValuePtr == NULL) {
03392         return TCL_ERROR;
03393     }
03394 
03395     /*
03396      * Finally, update the variable so that traces fire.
03397      */
03398 
03399     listPtr = Tcl_ObjSetVar2(interp, objv[1], NULL, finalValuePtr,
03400             TCL_LEAVE_ERR_MSG);
03401     Tcl_DecrRefCount(finalValuePtr);
03402     if (listPtr == NULL) {
03403         return TCL_ERROR;
03404     }
03405 
03406     /*
03407      * Return the new value of the variable as the interpreter result.
03408      */
03409 
03410     Tcl_SetObjResult(interp, listPtr);
03411     return TCL_OK;
03412 }
03413 
03414 /*
03415  *----------------------------------------------------------------------
03416  *
03417  * Tcl_LsortObjCmd --
03418  *
03419  *      This procedure is invoked to process the "lsort" Tcl command. See the
03420  *      user documentation for details on what it does.
03421  *
03422  * Results:
03423  *      A standard Tcl result.
03424  *
03425  * Side effects:
03426  *      See the user documentation.
03427  *
03428  *----------------------------------------------------------------------
03429  */
03430 
03431 int
03432 Tcl_LsortObjCmd(
03433     ClientData clientData,      /* Not used. */
03434     Tcl_Interp *interp,         /* Current interpreter. */
03435     int objc,                   /* Number of arguments. */
03436     Tcl_Obj *CONST objv[])      /* Argument values. */
03437 {
03438     int i, j, index, unique, indices, length, nocase = 0, sortMode, indexc;
03439     Tcl_Obj *resultPtr, *cmdPtr, **listObjPtrs, *listObj, *indexPtr;
03440     SortElement *elementArray, *elementPtr;
03441     SortInfo sortInfo;          /* Information about this sort that needs to
03442                                  * be passed to the comparison function. */
03443     static CONST char *switches[] = {
03444         "-ascii", "-command", "-decreasing", "-dictionary", "-increasing",
03445         "-index", "-indices", "-integer", "-nocase", "-real", "-unique", NULL
03446     };
03447     enum Lsort_Switches {
03448         LSORT_ASCII, LSORT_COMMAND, LSORT_DECREASING, LSORT_DICTIONARY,
03449         LSORT_INCREASING, LSORT_INDEX, LSORT_INDICES, LSORT_INTEGER,
03450         LSORT_NOCASE, LSORT_REAL, LSORT_UNIQUE
03451     };
03452 
03453     /*
03454      * The subList array below holds pointers to temporary lists built during
03455      * the merge sort. Element i of the array holds a list of length 2**i.
03456      */
03457 #   define NUM_LISTS 30
03458     SortElement *subList[NUM_LISTS+1];
03459 
03460     if (objc < 2) {
03461         Tcl_WrongNumArgs(interp, 1, objv, "?options? list");
03462         return TCL_ERROR;
03463     }
03464 
03465     /*
03466      * Parse arguments to set up the mode for the sort.
03467      */
03468 
03469     sortInfo.isIncreasing = 1;
03470     sortInfo.sortMode = SORTMODE_ASCII;
03471     sortInfo.indexv = NULL;
03472     sortInfo.indexc = 0;
03473     sortInfo.unique = 0;
03474     sortInfo.interp = interp;
03475     sortInfo.resultCode = TCL_OK;    
03476     cmdPtr = NULL;
03477     unique = 0;
03478     indices = 0;
03479     for (i = 1; i < objc-1; i++) {
03480         if (Tcl_GetIndexFromObj(interp, objv[i], switches, "option", 0,
03481                 &index) != TCL_OK) {
03482             return TCL_ERROR;
03483         }
03484         switch ((enum Lsort_Switches) index) {
03485         case LSORT_ASCII:
03486             sortInfo.sortMode = SORTMODE_ASCII;
03487             break;
03488         case LSORT_COMMAND:
03489             if (i == (objc-2)) {
03490                 if (sortInfo.indexc > 1) {
03491                     ckfree((char *) sortInfo.indexv);
03492                 }
03493                 Tcl_AppendResult(interp,
03494                         "\"-command\" option must be followed "
03495                         "by comparison command", NULL);
03496                 return TCL_ERROR;
03497             }
03498             sortInfo.sortMode = SORTMODE_COMMAND;
03499             cmdPtr = objv[i+1];
03500             i++;
03501             break;
03502         case LSORT_DECREASING:
03503             sortInfo.isIncreasing = 0;
03504             break;
03505         case LSORT_DICTIONARY:
03506             sortInfo.sortMode = SORTMODE_DICTIONARY;
03507             break;
03508         case LSORT_INCREASING:
03509             sortInfo.isIncreasing = 1;
03510             break;
03511         case LSORT_INDEX: {
03512             Tcl_Obj **indices;
03513 
03514             if (sortInfo.indexc > 1) {
03515                 ckfree((char *) sortInfo.indexv);
03516             }
03517             if (i == (objc-2)) {
03518                 Tcl_AppendResult(interp, "\"-index\" option must be "
03519                         "followed by list index", NULL);
03520                 return TCL_ERROR;
03521             }
03522 
03523             /*
03524              * Take copy to prevent shimmering problems.
03525              */
03526 
03527             if (TclListObjGetElements(interp, objv[i+1], &sortInfo.indexc,
03528                     &indices) != TCL_OK) {
03529                 return TCL_ERROR;
03530             }
03531             switch (sortInfo.indexc) {
03532             case 0:
03533                 sortInfo.indexv = NULL;
03534                 break;
03535             case 1:
03536                 sortInfo.indexv = &sortInfo.singleIndex;
03537                 break;
03538             default:
03539                 sortInfo.indexv = (int *)
03540                         ckalloc(sizeof(int) * sortInfo.indexc);
03541             }
03542 
03543             /*
03544              * Fill the array by parsing each index. We don't know whether
03545              * their scale is sensible yet, but we at least perform the
03546              * syntactic check here.
03547              */
03548 
03549             for (j=0 ; j<sortInfo.indexc ; j++) {
03550                 if (TclGetIntForIndexM(interp, indices[j], SORTIDX_END,
03551                         &sortInfo.indexv[j]) != TCL_OK) {
03552                     if (sortInfo.indexc > 1) {
03553                         ckfree((char *) sortInfo.indexv);
03554                     }
03555                     Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf(
03556                             "\n    (-index option item number %d)", j));
03557                     return TCL_ERROR;
03558                 }
03559             }
03560             i++;
03561             break;
03562         }
03563         case LSORT_INTEGER:
03564             sortInfo.sortMode = SORTMODE_INTEGER;
03565             break;
03566         case LSORT_NOCASE:
03567             nocase = 1;
03568             break;
03569         case LSORT_REAL:
03570             sortInfo.sortMode = SORTMODE_REAL;
03571             break;
03572         case LSORT_UNIQUE:
03573             unique = 1;
03574             sortInfo.unique = 1;
03575             break;
03576         case LSORT_INDICES:
03577             indices = 1;
03578             break;
03579         }
03580     }
03581     if (nocase && (sortInfo.sortMode == SORTMODE_ASCII)) {
03582         sortInfo.sortMode = SORTMODE_ASCII_NC;
03583     }
03584 
03585     listObj = objv[objc-1];
03586 
03587     if (sortInfo.sortMode == SORTMODE_COMMAND) {
03588         Tcl_Obj *newCommandPtr, *newObjPtr;
03589 
03590         /*
03591          * When sorting using a command, we are reentrant and therefore might
03592          * have the representation of the list being sorted shimmered out from
03593          * underneath our feet. Take a copy (cheap) to prevent this. [Bug
03594          * 1675116]
03595          */
03596 
03597         listObj = TclListObjCopy(interp, listObj);
03598         if (listObj == NULL) {
03599             if (sortInfo.indexc > 1) {
03600                 ckfree((char *) sortInfo.indexv);
03601             }
03602             return TCL_ERROR;
03603         }
03604 
03605         /*
03606          * The existing command is a list. We want to flatten it, append two
03607          * dummy arguments on the end, and replace these arguments later.
03608          */
03609 
03610         newCommandPtr = Tcl_DuplicateObj(cmdPtr);
03611         TclNewObj(newObjPtr);
03612         Tcl_IncrRefCount(newCommandPtr);
03613         if (Tcl_ListObjAppendElement(interp, newCommandPtr, newObjPtr)
03614                 != TCL_OK) {
03615             TclDecrRefCount(newCommandPtr);
03616             TclDecrRefCount(listObj);
03617             Tcl_IncrRefCount(newObjPtr);
03618             TclDecrRefCount(newObjPtr);
03619             if (sortInfo.indexc > 1) {
03620                 ckfree((char *) sortInfo.indexv);
03621             }
03622             return TCL_ERROR;
03623         }
03624         Tcl_ListObjAppendElement(interp, newCommandPtr, Tcl_NewObj());
03625         sortInfo.compareCmdPtr = newCommandPtr;
03626     }
03627 
03628     sortInfo.resultCode = TclListObjGetElements(interp, listObj,
03629             &length, &listObjPtrs);
03630     if (sortInfo.resultCode != TCL_OK || length <= 0) {
03631         goto done;
03632     }
03633     sortInfo.numElements = length;
03634     
03635     indexc = sortInfo.indexc;
03636     sortMode = sortInfo.sortMode;
03637     if ((sortMode == SORTMODE_ASCII_NC)
03638             || (sortMode == SORTMODE_DICTIONARY)) {
03639         /*
03640          * For this function's purpose all string-based modes are equivalent
03641          */
03642         
03643         sortMode = SORTMODE_ASCII;
03644     }
03645 
03646     /*
03647      * Initialize the sublists. After the following loop, subList[i] will
03648      * contain a sorted sublist of length 2**i. Use one extra subList at the
03649      * end, always at NULL, to indicate the end of the lists.
03650      */
03651     
03652     for (j=0 ; j<=NUM_LISTS ; j++) {
03653         subList[j] = NULL;
03654     }
03655 
03656     /*
03657      * The following loop creates a SortElement for each list element and
03658      * begins sorting it into the sublists as it appears.
03659      */
03660 
03661     elementArray = (SortElement *)
03662             TclStackAlloc(interp, length * sizeof(SortElement));
03663 
03664     for (i=0; i < length; i++){
03665         if (indexc) {
03666             /*
03667              * If this is an indexed sort, retrieve the corresponding element
03668              */
03669             indexPtr = SelectObjFromSublist(listObjPtrs[i], &sortInfo);
03670             if (sortInfo.resultCode != TCL_OK) {
03671                 goto done1;
03672             }
03673         } else {
03674             indexPtr = listObjPtrs[i];
03675         }
03676 
03677         /*
03678          * Determine the "value" of this object for sorting purposes
03679          */
03680         
03681         if (sortMode == SORTMODE_ASCII) {
03682             elementArray[i].index.strValuePtr = TclGetString(indexPtr);
03683         } else if (sortMode == SORTMODE_INTEGER) {
03684             long a;
03685             if (TclGetLongFromObj(sortInfo.interp, indexPtr, &a) != TCL_OK) {
03686                 sortInfo.resultCode = TCL_ERROR;
03687                 goto done1;
03688             }
03689             elementArray[i].index.intValue = a;
03690         } else if (sortInfo.sortMode == SORTMODE_REAL) {
03691             double a;
03692             if (Tcl_GetDoubleFromObj(sortInfo.interp, indexPtr, &a) != TCL_OK) {
03693                 sortInfo.resultCode = TCL_ERROR;
03694                 goto done1;
03695             }
03696             elementArray[i].index.doubleValue = a;
03697         } else {
03698             elementArray[i].index.objValuePtr = indexPtr;
03699         }
03700 
03701         /*
03702          * Determine the representation of this element in the result: either
03703          * the objPtr itself, or its index in the original list.
03704          */
03705         
03706         elementArray[i].objPtr = (indices ? INT2PTR(i) : listObjPtrs[i]);
03707 
03708         /*
03709          * Merge this element in the pre-existing sublists (and merge together
03710          * sublists when we have two of the same size).
03711          */
03712         
03713         elementArray[i].nextPtr = NULL;
03714         elementPtr = &elementArray[i];
03715         for (j=0 ; subList[j] ; j++) {
03716             elementPtr = MergeLists(subList[j], elementPtr, &sortInfo);
03717             subList[j] = NULL;
03718         }
03719         if (j >= NUM_LISTS) {
03720             j = NUM_LISTS-1;
03721         }
03722         subList[j] = elementPtr;
03723     }
03724 
03725     /*
03726      * Merge all sublists
03727      */
03728     
03729     elementPtr = subList[0];
03730     for (j=1 ; j<NUM_LISTS ; j++) {
03731         elementPtr = MergeLists(subList[j], elementPtr, &sortInfo);
03732     }
03733 
03734 
03735     /*
03736      * Now store the sorted elements in the result list.
03737      */
03738     
03739     if (sortInfo.resultCode == TCL_OK) {
03740         List *listRepPtr;
03741         Tcl_Obj **newArray, *objPtr;
03742         int i;
03743         
03744         resultPtr = Tcl_NewListObj(sortInfo.numElements, NULL);
03745         listRepPtr = (List *) resultPtr->internalRep.twoPtrValue.ptr1;
03746         newArray = &listRepPtr->elements;
03747         if (indices) {
03748             for (i = 0; elementPtr != NULL ; elementPtr = elementPtr->nextPtr){
03749                 objPtr = Tcl_NewIntObj(PTR2INT(elementPtr->objPtr));
03750                 newArray[i++] = objPtr;
03751                 Tcl_IncrRefCount(objPtr);
03752             }
03753         } else {
03754             for (i = 0; elementPtr != NULL ; elementPtr = elementPtr->nextPtr){
03755                 objPtr = elementPtr->objPtr;
03756                 newArray[i++] = objPtr;
03757                 Tcl_IncrRefCount(objPtr);
03758             }
03759         }
03760         listRepPtr->elemCount = i;
03761         Tcl_SetObjResult(interp, resultPtr);
03762     }
03763 
03764   done1:
03765     TclStackFree(interp, elementArray);
03766 
03767   done:
03768     if (sortInfo.sortMode == SORTMODE_COMMAND) {
03769         TclDecrRefCount(sortInfo.compareCmdPtr);
03770         TclDecrRefCount(listObj);
03771         sortInfo.compareCmdPtr = NULL;
03772     }
03773     if (sortInfo.indexc > 1) {
03774         ckfree((char *) sortInfo.indexv);
03775     }
03776     return sortInfo.resultCode;
03777 }
03778 
03779 /*
03780  *----------------------------------------------------------------------
03781  *
03782  * MergeLists -
03783  *
03784  *      This procedure combines two sorted lists of SortElement structures
03785  *      into a single sorted list.
03786  *
03787  * Results:
03788  *      The unified list of SortElement structures.
03789  *
03790  * Side effects:
03791  *      If infoPtr->unique is set then infoPtr->numElements may be updated.
03792  *      Possibly others, if a user-defined comparison command does something
03793  *      weird. 
03794  *
03795  * Note:
03796  *      If infoPtr->unique is set, the merge assumes that there are no
03797  *      "repeated" elements in each of the left and right lists. In that case,
03798  *      if any element of the left list is equivalent to one in the right list
03799  *      it is omitted from the merged list.
03800  *      This simplified mechanism works because of the special way
03801  *      our MergeSort creates the sublists to be merged and will fail to
03802  *      eliminate all repeats in the general case where they are already
03803  *      present in either the left or right list. A general code would need to
03804  *      skip adjacent initial repeats in the left and right lists before
03805  *      comparing their initial elements, at each step. 
03806  *----------------------------------------------------------------------
03807  */
03808 
03809 static SortElement *
03810 MergeLists(
03811     SortElement *leftPtr,       /* First list to be merged; may be NULL. */
03812     SortElement *rightPtr,      /* Second list to be merged; may be NULL. */
03813     SortInfo *infoPtr)          /* Information needed by the comparison
03814                                  * operator. */
03815 {
03816     SortElement *headPtr, *tailPtr;
03817     int cmp;
03818 
03819     if (leftPtr == NULL) {
03820         return rightPtr;
03821     }
03822     if (rightPtr == NULL) {
03823         return leftPtr;
03824     }
03825     cmp = SortCompare(leftPtr, rightPtr, infoPtr);
03826     if (cmp > 0 || (cmp == 0 && infoPtr->unique)) {
03827         if (cmp == 0) {
03828             infoPtr->numElements--;
03829             leftPtr = leftPtr->nextPtr;
03830         }
03831         tailPtr = rightPtr;
03832         rightPtr = rightPtr->nextPtr;
03833     } else {
03834         tailPtr = leftPtr;
03835         leftPtr = leftPtr->nextPtr;
03836     }
03837     headPtr = tailPtr;
03838     if (!infoPtr->unique) {
03839         while ((leftPtr != NULL) && (rightPtr != NULL)) {
03840             cmp = SortCompare(leftPtr, rightPtr, infoPtr);
03841             if (cmp > 0) {
03842                 tailPtr->nextPtr = rightPtr;
03843                 tailPtr = rightPtr;
03844                 rightPtr = rightPtr->nextPtr;
03845             } else {
03846                 tailPtr->nextPtr = leftPtr;
03847                 tailPtr = leftPtr;
03848                 leftPtr = leftPtr->nextPtr;
03849             }
03850         }
03851     } else {
03852         while ((leftPtr != NULL) && (rightPtr != NULL)) {
03853             cmp = SortCompare(leftPtr, rightPtr, infoPtr);
03854             if (cmp >= 0) {
03855                 if (cmp == 0) {
03856                     infoPtr->numElements--;
03857                     leftPtr = leftPtr->nextPtr;
03858                 }
03859                 tailPtr->nextPtr = rightPtr;
03860                 tailPtr = rightPtr;
03861                 rightPtr = rightPtr->nextPtr;
03862             } else {
03863                 tailPtr->nextPtr = leftPtr;
03864                 tailPtr = leftPtr;
03865                 leftPtr = leftPtr->nextPtr;
03866             }
03867         }
03868     }
03869     if (leftPtr != NULL) {
03870         tailPtr->nextPtr = leftPtr;
03871     } else {
03872         tailPtr->nextPtr = rightPtr;
03873     }
03874     return headPtr;
03875 }
03876 
03877 /*
03878  *----------------------------------------------------------------------
03879  *
03880  * SortCompare --
03881  *
03882  *      This procedure is invoked by MergeLists to determine the proper
03883  *      ordering between two elements.
03884  *
03885  * Results:
03886  *      A negative results means the the first element comes before the
03887  *      second, and a positive results means that the second element should
03888  *      come first. A result of zero means the two elements are equal and it
03889  *      doesn't matter which comes first.
03890  *
03891  * Side effects:
03892  *      None, unless a user-defined comparison command does something weird.
03893  *
03894  *----------------------------------------------------------------------
03895  */
03896 
03897 static int
03898 SortCompare(
03899     SortElement *elemPtr1, SortElement *elemPtr2,
03900                                 /* Values to be compared. */
03901     SortInfo *infoPtr)          /* Information passed from the top-level
03902                                  * "lsort" command. */
03903 {
03904     int order = 0;
03905 
03906     if (infoPtr->sortMode == SORTMODE_ASCII) {
03907         order = strcmp(elemPtr1->index.strValuePtr,
03908                 elemPtr2->index.strValuePtr);
03909     } else if (infoPtr->sortMode == SORTMODE_ASCII_NC) {
03910         order = strcasecmp(elemPtr1->index.strValuePtr,
03911                 elemPtr2->index.strValuePtr);
03912     } else if (infoPtr->sortMode == SORTMODE_DICTIONARY) {
03913         order = DictionaryCompare(elemPtr1->index.strValuePtr,
03914                 elemPtr2->index.strValuePtr);
03915     } else if (infoPtr->sortMode == SORTMODE_INTEGER) {
03916         long a, b;
03917 
03918         a = elemPtr1->index.intValue;
03919         b = elemPtr2->index.intValue;
03920         order = ((a >= b) - (a <= b));
03921     } else if (infoPtr->sortMode == SORTMODE_REAL) {
03922         double a, b;
03923 
03924         a = elemPtr1->index.doubleValue;
03925         b = elemPtr2->index.doubleValue;
03926         order = ((a >= b) - (a <= b));
03927     } else {
03928         Tcl_Obj **objv, *paramObjv[2];
03929         int objc;
03930         Tcl_Obj *objPtr1, *objPtr2;
03931 
03932         if (infoPtr->resultCode != TCL_OK) {
03933             /*
03934              * Once an error has occurred, skip any future comparisons so as
03935              * to preserve the error message in sortInterp->result.
03936              */
03937             
03938             return 0;
03939         }
03940 
03941 
03942         objPtr1 = elemPtr1->index.objValuePtr;
03943         objPtr2 = elemPtr2->index.objValuePtr;
03944         
03945         paramObjv[0] = objPtr1;
03946         paramObjv[1] = objPtr2;
03947 
03948         /*
03949          * We made space in the command list for the two things to compare.
03950          * Replace them and evaluate the result.
03951          */
03952 
03953         TclListObjLength(infoPtr->interp, infoPtr->compareCmdPtr, &objc);
03954         Tcl_ListObjReplace(infoPtr->interp, infoPtr->compareCmdPtr, objc - 2,
03955                 2, 2, paramObjv);
03956         TclListObjGetElements(infoPtr->interp, infoPtr->compareCmdPtr,
03957                 &objc, &objv);
03958 
03959         infoPtr->resultCode = Tcl_EvalObjv(infoPtr->interp, objc, objv, 0);
03960 
03961         if (infoPtr->resultCode != TCL_OK) {
03962             Tcl_AddErrorInfo(infoPtr->interp,
03963                     "\n    (-compare command)");
03964             return 0;
03965         }
03966 
03967         /*
03968          * Parse the result of the command.
03969          */
03970 
03971         if (TclGetIntFromObj(infoPtr->interp,
03972                 Tcl_GetObjResult(infoPtr->interp), &order) != TCL_OK) {
03973             Tcl_ResetResult(infoPtr->interp);
03974             Tcl_AppendResult(infoPtr->interp,
03975                     "-compare command returned non-integer result", NULL);
03976             infoPtr->resultCode = TCL_ERROR;
03977             return 0;
03978         }
03979     }
03980     if (!infoPtr->isIncreasing) {
03981         order = -order;
03982     }
03983     return order;
03984 }
03985 
03986 /*
03987  *----------------------------------------------------------------------
03988  *
03989  * DictionaryCompare
03990  *
03991  *      This function compares two strings as if they were being used in an
03992  *      index or card catalog. The case of alphabetic characters is ignored,
03993  *      except to break ties. Thus "B" comes before "b" but after "a". Also,
03994  *      integers embedded in the strings compare in numerical order. In other
03995  *      words, "x10y" comes after "x9y", not * before it as it would when
03996  *      using strcmp().
03997  *
03998  * Results:
03999  *      A negative result means that the first element comes before the
04000  *      second, and a positive result means that the second element should
04001  *      come first. A result of zero means the two elements are equal and it
04002  *      doesn't matter which comes first.
04003  *
04004  * Side effects:
04005  *      None.
04006  *
04007  *----------------------------------------------------------------------
04008  */
04009 
04010 static int
04011 DictionaryCompare(
04012     char *left, char *right)    /* The strings to compare. */
04013 {
04014     Tcl_UniChar uniLeft, uniRight, uniLeftLower, uniRightLower;
04015     int diff, zeros;
04016     int secondaryDiff = 0;
04017 
04018     while (1) {
04019         if (isdigit(UCHAR(*right))              /* INTL: digit */
04020                 && isdigit(UCHAR(*left))) {     /* INTL: digit */
04021             /*
04022              * There are decimal numbers embedded in the two strings. Compare
04023              * them as numbers, rather than strings. If one number has more
04024              * leading zeros than the other, the number with more leading
04025              * zeros sorts later, but only as a secondary choice.
04026              */
04027 
04028             zeros = 0;
04029             while ((*right == '0') && (isdigit(UCHAR(right[1])))) {
04030                 right++;
04031                 zeros--;
04032             }
04033             while ((*left == '0') && (isdigit(UCHAR(left[1])))) {
04034                 left++;
04035                 zeros++;
04036             }
04037             if (secondaryDiff == 0) {
04038                 secondaryDiff = zeros;
04039             }
04040 
04041             /*
04042              * The code below compares the numbers in the two strings without
04043              * ever converting them to integers. It does this by first
04044              * comparing the lengths of the numbers and then comparing the
04045              * digit values.
04046              */
04047 
04048             diff = 0;
04049             while (1) {
04050                 if (diff == 0) {
04051                     diff = UCHAR(*left) - UCHAR(*right);
04052                 }
04053                 right++;
04054                 left++;
04055                 if (!isdigit(UCHAR(*right))) {          /* INTL: digit */
04056                     if (isdigit(UCHAR(*left))) {        /* INTL: digit */
04057                         return 1;
04058                     } else {
04059                         /*
04060                          * The two numbers have the same length. See if their
04061                          * values are different.
04062                          */
04063 
04064                         if (diff != 0) {
04065                             return diff;
04066                         }
04067                         break;
04068                     }
04069                 } else if (!isdigit(UCHAR(*left))) {    /* INTL: digit */
04070                     return -1;
04071                 }
04072             }
04073             continue;
04074         }
04075 
04076         /*
04077          * Convert character to Unicode for comparison purposes. If either
04078          * string is at the terminating null, do a byte-wise comparison and
04079          * bail out immediately.
04080          */
04081 
04082         if ((*left != '\0') && (*right != '\0')) {
04083             left += Tcl_UtfToUniChar(left, &uniLeft);
04084             right += Tcl_UtfToUniChar(right, &uniRight);
04085 
04086             /*
04087              * Convert both chars to lower for the comparison, because
04088              * dictionary sorts are case insensitve. Covert to lower, not
04089              * upper, so chars between Z and a will sort before A (where most
04090              * other interesting punctuations occur).
04091              */
04092 
04093             uniLeftLower = Tcl_UniCharToLower(uniLeft);
04094             uniRightLower = Tcl_UniCharToLower(uniRight);
04095         } else {
04096             diff = UCHAR(*left) - UCHAR(*right);
04097             break;
04098         }
04099 
04100         diff = uniLeftLower - uniRightLower;
04101         if (diff) {
04102             return diff;
04103         }
04104         if (secondaryDiff == 0) {
04105             if (Tcl_UniCharIsUpper(uniLeft) && Tcl_UniCharIsLower(uniRight)) {
04106                 secondaryDiff = -1;
04107             } else if (Tcl_UniCharIsUpper(uniRight)
04108                     && Tcl_UniCharIsLower(uniLeft)) {
04109                 secondaryDiff = 1;
04110             }
04111         }
04112     }
04113     if (diff == 0) {
04114         diff = secondaryDiff;
04115     }
04116     return diff;
04117 }
04118 
04119 /*
04120  *----------------------------------------------------------------------
04121  *
04122  * SelectObjFromSublist --
04123  *
04124  *      This procedure is invoked from lsearch and SortCompare. It is used for
04125  *      implementing the -index option, for the lsort and lsearch commands.
04126  *
04127  * Results:
04128  *      Returns NULL if a failure occurs, and sets the result in the infoPtr.
04129  *      Otherwise returns the Tcl_Obj* to the item.
04130  *
04131  * Side effects:
04132  *      None.
04133  *
04134  * Note:
04135  *      No reference counting is done, as the result is only used internally
04136  *      and never passed directly to user code.
04137  *
04138  *----------------------------------------------------------------------
04139  */
04140 
04141 static Tcl_Obj *
04142 SelectObjFromSublist(
04143     Tcl_Obj *objPtr,            /* Obj to select sublist from. */
04144     SortInfo *infoPtr)          /* Information passed from the top-level
04145                                  * "lsearch" or "lsort" command. */
04146 {
04147     int i;
04148 
04149     /*
04150      * Quick check for case when no "-index" option is there.
04151      */
04152 
04153     if (infoPtr->indexc == 0) {
04154         return objPtr;
04155     }
04156 
04157     /*
04158      * Iterate over the indices, traversing through the nested sublists as we
04159      * go.
04160      */
04161 
04162     for (i=0 ; i<infoPtr->indexc ; i++) {
04163         int listLen, index;
04164         Tcl_Obj *currentObj;
04165 
04166         if (TclListObjLength(infoPtr->interp, objPtr, &listLen) != TCL_OK) {
04167             infoPtr->resultCode = TCL_ERROR;
04168             return NULL;
04169         }
04170         index = infoPtr->indexv[i];
04171 
04172         /*
04173          * Adjust for end-based indexing.
04174          */
04175 
04176         if (index < SORTIDX_NONE) {
04177             index += listLen + 1;
04178         }
04179 
04180         if (Tcl_ListObjIndex(infoPtr->interp, objPtr, index,
04181                 &currentObj) != TCL_OK) {
04182             infoPtr->resultCode = TCL_ERROR;
04183             return NULL;
04184         }
04185         if (currentObj == NULL) {
04186             char buffer[TCL_INTEGER_SPACE];
04187 
04188             TclFormatInt(buffer, index);
04189             Tcl_AppendResult(infoPtr->interp, "element ", buffer,
04190                     " missing from sublist \"", TclGetString(objPtr), "\"",
04191                     NULL);
04192             infoPtr->resultCode = TCL_ERROR;
04193             return NULL;
04194         }
04195         objPtr = currentObj;
04196     }
04197     return objPtr;
04198 }
04199 
04200 /*
04201  * Local Variables:
04202  * mode: c
04203  * c-basic-offset: 4
04204  * fill-column: 78
04205  * End:
04206  */



Generated on Wed Mar 12 12:18:12 2008 by  doxygen 1.5.1