tclCmdIL.cGo 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 ¤tObj) != 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 1.5.1 |