tclIndexObj.cGo to the documentation of this file.00001 /* 00002 * tclIndexObj.c -- 00003 * 00004 * This file implements objects of type "index". This object type is used 00005 * to lookup a keyword in a table of valid values and cache the index of 00006 * the matching entry. 00007 * 00008 * Copyright (c) 1997 Sun Microsystems, Inc. 00009 * 00010 * See the file "license.terms" for information on usage and redistribution of 00011 * this file, and for a DISCLAIMER OF ALL WARRANTIES. 00012 * 00013 * RCS: @(#) $Id: tclIndexObj.c,v 1.38 2007/12/13 15:23:18 dgp Exp $ 00014 */ 00015 00016 #include "tclInt.h" 00017 00018 /* 00019 * Prototypes for functions defined later in this file: 00020 */ 00021 00022 static int SetIndexFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr); 00023 static void UpdateStringOfIndex(Tcl_Obj *objPtr); 00024 static void DupIndex(Tcl_Obj *srcPtr, Tcl_Obj *dupPtr); 00025 static void FreeIndex(Tcl_Obj *objPtr); 00026 00027 /* 00028 * The structure below defines the index Tcl object type by means of functions 00029 * that can be invoked by generic object code. 00030 */ 00031 00032 static Tcl_ObjType indexType = { 00033 "index", /* name */ 00034 FreeIndex, /* freeIntRepProc */ 00035 DupIndex, /* dupIntRepProc */ 00036 UpdateStringOfIndex, /* updateStringProc */ 00037 SetIndexFromAny /* setFromAnyProc */ 00038 }; 00039 00040 /* 00041 * The definition of the internal representation of the "index" object; The 00042 * internalRep.otherValuePtr field of an object of "index" type will be a 00043 * pointer to one of these structures. 00044 * 00045 * Keep this structure declaration in sync with tclTestObj.c 00046 */ 00047 00048 typedef struct { 00049 void *tablePtr; /* Pointer to the table of strings */ 00050 int offset; /* Offset between table entries */ 00051 int index; /* Selected index into table. */ 00052 } IndexRep; 00053 00054 /* 00055 * The following macros greatly simplify moving through a table... 00056 */ 00057 00058 #define STRING_AT(table, offset, index) \ 00059 (*((const char *const *)(((char *)(table)) + ((offset) * (index))))) 00060 #define NEXT_ENTRY(table, offset) \ 00061 (&(STRING_AT(table, offset, 1))) 00062 #define EXPAND_OF(indexRep) \ 00063 STRING_AT((indexRep)->tablePtr, (indexRep)->offset, (indexRep)->index) 00064 00065 /* 00066 *---------------------------------------------------------------------- 00067 * 00068 * Tcl_GetIndexFromObj -- 00069 * 00070 * This function looks up an object's value in a table of strings and 00071 * returns the index of the matching string, if any. 00072 * 00073 * Results: 00074 * If the value of objPtr is identical to or a unique abbreviation for 00075 * one of the entries in objPtr, then the return value is TCL_OK and the 00076 * index of the matching entry is stored at *indexPtr. If there isn't a 00077 * proper match, then TCL_ERROR is returned and an error message is left 00078 * in interp's result (unless interp is NULL). The msg argument is used 00079 * in the error message; for example, if msg has the value "option" then 00080 * the error message will say something flag 'bad option "foo": must be 00081 * ...' 00082 * 00083 * Side effects: 00084 * The result of the lookup is cached as the internal rep of objPtr, so 00085 * that repeated lookups can be done quickly. 00086 * 00087 *---------------------------------------------------------------------- 00088 */ 00089 00090 int 00091 Tcl_GetIndexFromObj( 00092 Tcl_Interp *interp, /* Used for error reporting if not NULL. */ 00093 Tcl_Obj *objPtr, /* Object containing the string to lookup. */ 00094 const char **tablePtr, /* Array of strings to compare against the 00095 * value of objPtr; last entry must be NULL 00096 * and there must not be duplicate entries. */ 00097 const char *msg, /* Identifying word to use in error 00098 * messages. */ 00099 int flags, /* 0 or TCL_EXACT */ 00100 int *indexPtr) /* Place to store resulting integer index. */ 00101 { 00102 00103 /* 00104 * See if there is a valid cached result from a previous lookup (doing the 00105 * check here saves the overhead of calling Tcl_GetIndexFromObjStruct in 00106 * the common case where the result is cached). 00107 */ 00108 00109 if (objPtr->typePtr == &indexType) { 00110 IndexRep *indexRep = objPtr->internalRep.otherValuePtr; 00111 00112 /* 00113 * Here's hoping we don't get hit by unfortunate packing constraints 00114 * on odd platforms like a Cray PVP... 00115 */ 00116 00117 if (indexRep->tablePtr == (void *) tablePtr 00118 && indexRep->offset == sizeof(char *)) { 00119 *indexPtr = indexRep->index; 00120 return TCL_OK; 00121 } 00122 } 00123 return Tcl_GetIndexFromObjStruct(interp, objPtr, tablePtr, sizeof(char *), 00124 msg, flags, indexPtr); 00125 } 00126 00127 /* 00128 *---------------------------------------------------------------------- 00129 * 00130 * Tcl_GetIndexFromObjStruct -- 00131 * 00132 * This function looks up an object's value given a starting string and 00133 * an offset for the amount of space between strings. This is useful when 00134 * the strings are embedded in some other kind of array. 00135 * 00136 * Results: 00137 * If the value of objPtr is identical to or a unique abbreviation for 00138 * one of the entries in objPtr, then the return value is TCL_OK and the 00139 * index of the matching entry is stored at *indexPtr. If there isn't a 00140 * proper match, then TCL_ERROR is returned and an error message is left 00141 * in interp's result (unless interp is NULL). The msg argument is used 00142 * in the error message; for example, if msg has the value "option" then 00143 * the error message will say something flag 'bad option "foo": must be 00144 * ...' 00145 * 00146 * Side effects: 00147 * The result of the lookup is cached as the internal rep of objPtr, so 00148 * that repeated lookups can be done quickly. 00149 * 00150 *---------------------------------------------------------------------- 00151 */ 00152 00153 int 00154 Tcl_GetIndexFromObjStruct( 00155 Tcl_Interp *interp, /* Used for error reporting if not NULL. */ 00156 Tcl_Obj *objPtr, /* Object containing the string to lookup. */ 00157 const void *tablePtr, /* The first string in the table. The second 00158 * string will be at this address plus the 00159 * offset, the third plus the offset again, 00160 * etc. The last entry must be NULL and there 00161 * must not be duplicate entries. */ 00162 int offset, /* The number of bytes between entries */ 00163 const char *msg, /* Identifying word to use in error 00164 * messages. */ 00165 int flags, /* 0 or TCL_EXACT */ 00166 int *indexPtr) /* Place to store resulting integer index. */ 00167 { 00168 int index, idx, numAbbrev; 00169 char *key, *p1; 00170 const char *p2; 00171 const char *const *entryPtr; 00172 Tcl_Obj *resultPtr; 00173 IndexRep *indexRep; 00174 00175 /* 00176 * See if there is a valid cached result from a previous lookup. 00177 */ 00178 00179 if (objPtr->typePtr == &indexType) { 00180 indexRep = objPtr->internalRep.otherValuePtr; 00181 if (indexRep->tablePtr==tablePtr && indexRep->offset==offset) { 00182 *indexPtr = indexRep->index; 00183 return TCL_OK; 00184 } 00185 } 00186 00187 /* 00188 * Lookup the value of the object in the table. Accept unique 00189 * abbreviations unless TCL_EXACT is set in flags. 00190 */ 00191 00192 key = TclGetString(objPtr); 00193 index = -1; 00194 numAbbrev = 0; 00195 00196 /* 00197 * Scan the table looking for one of: 00198 * - An exact match (always preferred) 00199 * - A single abbreviation (allowed depending on flags) 00200 * - Several abbreviations (never allowed, but overridden by exact match) 00201 */ 00202 00203 for (entryPtr = tablePtr, idx = 0; *entryPtr != NULL; 00204 entryPtr = NEXT_ENTRY(entryPtr, offset), idx++) { 00205 for (p1 = key, p2 = *entryPtr; *p1 == *p2; p1++, p2++) { 00206 if (*p1 == '\0') { 00207 index = idx; 00208 goto done; 00209 } 00210 } 00211 if (*p1 == '\0') { 00212 /* 00213 * The value is an abbreviation for this entry. Continue checking 00214 * other entries to make sure it's unique. If we get more than one 00215 * unique abbreviation, keep searching to see if there is an exact 00216 * match, but remember the number of unique abbreviations and 00217 * don't allow either. 00218 */ 00219 00220 numAbbrev++; 00221 index = idx; 00222 } 00223 } 00224 00225 /* 00226 * Check if we were instructed to disallow abbreviations. 00227 */ 00228 00229 if ((flags & TCL_EXACT) || (key[0] == '\0') || (numAbbrev != 1)) { 00230 goto error; 00231 } 00232 00233 done: 00234 /* 00235 * Cache the found representation. Note that we want to avoid allocating a 00236 * new internal-rep if at all possible since that is potentially a slow 00237 * operation. 00238 */ 00239 00240 if (objPtr->typePtr == &indexType) { 00241 indexRep = objPtr->internalRep.otherValuePtr; 00242 } else { 00243 TclFreeIntRep(objPtr); 00244 indexRep = (IndexRep *) ckalloc(sizeof(IndexRep)); 00245 objPtr->internalRep.otherValuePtr = indexRep; 00246 objPtr->typePtr = &indexType; 00247 } 00248 indexRep->tablePtr = (void *) tablePtr; 00249 indexRep->offset = offset; 00250 indexRep->index = index; 00251 00252 *indexPtr = index; 00253 return TCL_OK; 00254 00255 error: 00256 if (interp != NULL) { 00257 /* 00258 * Produce a fancy error message. 00259 */ 00260 00261 int count; 00262 00263 TclNewObj(resultPtr); 00264 Tcl_SetObjResult(interp, resultPtr); 00265 Tcl_AppendStringsToObj(resultPtr, (numAbbrev > 1) && 00266 !(flags & TCL_EXACT) ? "ambiguous " : "bad ", msg, " \"", key, 00267 "\": must be ", STRING_AT(tablePtr, offset, 0), NULL); 00268 for (entryPtr = NEXT_ENTRY(tablePtr, offset), count = 0; 00269 *entryPtr != NULL; 00270 entryPtr = NEXT_ENTRY(entryPtr, offset), count++) { 00271 if (*NEXT_ENTRY(entryPtr, offset) == NULL) { 00272 Tcl_AppendStringsToObj(resultPtr, ((count > 0) ? "," : ""), 00273 " or ", *entryPtr, NULL); 00274 } else { 00275 Tcl_AppendStringsToObj(resultPtr, ", ", *entryPtr, NULL); 00276 } 00277 } 00278 Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "INDEX", msg, key, NULL); 00279 } 00280 return TCL_ERROR; 00281 } 00282 00283 /* 00284 *---------------------------------------------------------------------- 00285 * 00286 * SetIndexFromAny -- 00287 * 00288 * This function is called to convert a Tcl object to index internal 00289 * form. However, this doesn't make sense (need to have a table of 00290 * keywords in order to do the conversion) so the function always 00291 * generates an error. 00292 * 00293 * Results: 00294 * The return value is always TCL_ERROR, and an error message is left in 00295 * interp's result if interp isn't NULL. 00296 * 00297 * Side effects: 00298 * None. 00299 * 00300 *---------------------------------------------------------------------- 00301 */ 00302 00303 static int 00304 SetIndexFromAny( 00305 Tcl_Interp *interp, /* Used for error reporting if not NULL. */ 00306 register Tcl_Obj *objPtr) /* The object to convert. */ 00307 { 00308 Tcl_SetObjResult(interp, Tcl_NewStringObj( 00309 "can't convert value to index except via Tcl_GetIndexFromObj API", 00310 -1)); 00311 return TCL_ERROR; 00312 } 00313 00314 /* 00315 *---------------------------------------------------------------------- 00316 * 00317 * UpdateStringOfIndex -- 00318 * 00319 * This function is called to convert a Tcl object from index internal 00320 * form to its string form. No abbreviation is ever generated. 00321 * 00322 * Results: 00323 * None. 00324 * 00325 * Side effects: 00326 * The string representation of the object is updated. 00327 * 00328 *---------------------------------------------------------------------- 00329 */ 00330 00331 static void 00332 UpdateStringOfIndex( 00333 Tcl_Obj *objPtr) 00334 { 00335 IndexRep *indexRep = objPtr->internalRep.otherValuePtr; 00336 register char *buf; 00337 register unsigned len; 00338 register const char *indexStr = EXPAND_OF(indexRep); 00339 00340 len = strlen(indexStr); 00341 buf = (char *) ckalloc(len + 1); 00342 memcpy(buf, indexStr, len+1); 00343 objPtr->bytes = buf; 00344 objPtr->length = len; 00345 } 00346 00347 /* 00348 *---------------------------------------------------------------------- 00349 * 00350 * DupIndex -- 00351 * 00352 * This function is called to copy the internal rep of an index Tcl 00353 * object from to another object. 00354 * 00355 * Results: 00356 * None. 00357 * 00358 * Side effects: 00359 * The internal representation of the target object is updated and the 00360 * type is set. 00361 * 00362 *---------------------------------------------------------------------- 00363 */ 00364 00365 static void 00366 DupIndex( 00367 Tcl_Obj *srcPtr, 00368 Tcl_Obj *dupPtr) 00369 { 00370 IndexRep *srcIndexRep = srcPtr->internalRep.otherValuePtr; 00371 IndexRep *dupIndexRep = (IndexRep *) ckalloc(sizeof(IndexRep)); 00372 00373 memcpy(dupIndexRep, srcIndexRep, sizeof(IndexRep)); 00374 dupPtr->internalRep.otherValuePtr = dupIndexRep; 00375 dupPtr->typePtr = &indexType; 00376 } 00377 00378 /* 00379 *---------------------------------------------------------------------- 00380 * 00381 * FreeIndex -- 00382 * 00383 * This function is called to delete the internal rep of an index Tcl 00384 * object. 00385 * 00386 * Results: 00387 * None. 00388 * 00389 * Side effects: 00390 * The internal representation of the target object is deleted. 00391 * 00392 *---------------------------------------------------------------------- 00393 */ 00394 00395 static void 00396 FreeIndex( 00397 Tcl_Obj *objPtr) 00398 { 00399 ckfree((char *) objPtr->internalRep.otherValuePtr); 00400 } 00401 00402 /* 00403 *---------------------------------------------------------------------- 00404 * 00405 * Tcl_WrongNumArgs -- 00406 * 00407 * This function generates a "wrong # args" error message in an 00408 * interpreter. It is used as a utility function by many command 00409 * functions, including the function that implements procedures. 00410 * 00411 * Results: 00412 * None. 00413 * 00414 * Side effects: 00415 * An error message is generated in interp's result object to indicate 00416 * that a command was invoked with the wrong number of arguments. The 00417 * message has the form 00418 * wrong # args: should be "foo bar additional stuff" 00419 * where "foo" and "bar" are the initial objects in objv (objc determines 00420 * how many of these are printed) and "additional stuff" is the contents 00421 * of the message argument. 00422 * 00423 * The message printed is modified somewhat if the command is wrapped 00424 * inside an ensemble. In that case, the error message generated is 00425 * rewritten in such a way that it appears to be generated from the 00426 * user-visible command and not how that command is actually implemented, 00427 * giving a better overall user experience. 00428 * 00429 * Internally, the Tcl core may set the flag INTERP_ALTERNATE_WRONG_ARGS 00430 * in the interpreter to generate complex multi-part messages by calling 00431 * this function repeatedly. This allows the code that knows how to 00432 * handle ensemble-related error messages to be kept here while still 00433 * generating suitable error messages for commands like [read] and 00434 * [socket]. Ideally, this would be done through an extra flags argument, 00435 * but that wouldn't be source-compatible with the existing API and it's 00436 * a fairly rare requirement anyway. 00437 * 00438 *---------------------------------------------------------------------- 00439 */ 00440 00441 void 00442 Tcl_WrongNumArgs( 00443 Tcl_Interp *interp, /* Current interpreter. */ 00444 int objc, /* Number of arguments to print from objv. */ 00445 Tcl_Obj *const objv[], /* Initial argument objects, which should be 00446 * included in the error message. */ 00447 const char *message) /* Error message to print after the leading 00448 * objects in objv. The message may be 00449 * NULL. */ 00450 { 00451 Tcl_Obj *objPtr; 00452 int i, len, elemLen, flags; 00453 Interp *iPtr = (Interp *) interp; 00454 const char *elementStr; 00455 00456 /* 00457 * [incr Tcl] does something fairly horrific when generating error 00458 * messages for its ensembles; it passes the whole set of ensemble 00459 * arguments as a list in the first argument. This means that this code 00460 * causes a problem in iTcl if it attempts to correctly quote all 00461 * arguments, which would be the correct thing to do. We work around this 00462 * nasty behaviour for now, and hope that we can remove it all in the 00463 * future... 00464 */ 00465 00466 #ifndef AVOID_HACKS_FOR_ITCL 00467 int isFirst = 1; /* Special flag used to inhibit the treating 00468 * of the first word as a list element so the 00469 * hacky way Itcl generates error messages for 00470 * its ensembles will still work. [Bug 00471 * 1066837] */ 00472 # define MAY_QUOTE_WORD (!isFirst) 00473 # define AFTER_FIRST_WORD (isFirst = 0) 00474 #else /* !AVOID_HACKS_FOR_ITCL */ 00475 # define MAY_QUOTE_WORD 1 00476 # define AFTER_FIRST_WORD (void) 0 00477 #endif /* AVOID_HACKS_FOR_ITCL */ 00478 00479 TclNewObj(objPtr); 00480 if (iPtr->flags & INTERP_ALTERNATE_WRONG_ARGS) { 00481 Tcl_AppendObjToObj(objPtr, Tcl_GetObjResult(interp)); 00482 Tcl_AppendToObj(objPtr, " or \"", -1); 00483 } else { 00484 Tcl_AppendToObj(objPtr, "wrong # args: should be \"", -1); 00485 } 00486 00487 /* 00488 * Check to see if we are processing an ensemble implementation, and if so 00489 * rewrite the results in terms of how the ensemble was invoked. 00490 */ 00491 00492 if (iPtr->ensembleRewrite.sourceObjs != NULL) { 00493 int toSkip = iPtr->ensembleRewrite.numInsertedObjs; 00494 int toPrint = iPtr->ensembleRewrite.numRemovedObjs; 00495 Tcl_Obj *const *origObjv = iPtr->ensembleRewrite.sourceObjs; 00496 00497 /* 00498 * We only know how to do rewriting if all the replaced objects are 00499 * actually arguments (in objv) to this function. Otherwise it just 00500 * gets too complicated and we'd be better off just giving a slightly 00501 * confusing error message... 00502 */ 00503 00504 if (objc < toSkip) { 00505 goto addNormalArgumentsToMessage; 00506 } 00507 00508 /* 00509 * Strip out the actual arguments that the ensemble inserted. 00510 */ 00511 00512 objv += toSkip; 00513 objc -= toSkip; 00514 00515 /* 00516 * We assume no object is of index type. 00517 */ 00518 00519 for (i=0 ; i<toPrint ; i++) { 00520 /* 00521 * Add the element, quoting it if necessary. 00522 */ 00523 00524 if (origObjv[i]->typePtr == &indexType) { 00525 register IndexRep *indexRep = 00526 origObjv[i]->internalRep.otherValuePtr; 00527 00528 elementStr = EXPAND_OF(indexRep); 00529 elemLen = strlen(elementStr); 00530 } else if (origObjv[i]->typePtr == &tclEnsembleCmdType) { 00531 register EnsembleCmdRep *ecrPtr = 00532 origObjv[i]->internalRep.otherValuePtr; 00533 00534 elementStr = ecrPtr->fullSubcmdName; 00535 elemLen = strlen(elementStr); 00536 } else { 00537 elementStr = TclGetStringFromObj(origObjv[i], &elemLen); 00538 } 00539 len = Tcl_ScanCountedElement(elementStr, elemLen, &flags); 00540 00541 if (MAY_QUOTE_WORD && len != elemLen) { 00542 char *quotedElementStr = TclStackAlloc(interp, (unsigned)len); 00543 00544 len = Tcl_ConvertCountedElement(elementStr, elemLen, 00545 quotedElementStr, flags); 00546 Tcl_AppendToObj(objPtr, quotedElementStr, len); 00547 TclStackFree(interp, quotedElementStr); 00548 } else { 00549 Tcl_AppendToObj(objPtr, elementStr, elemLen); 00550 } 00551 00552 AFTER_FIRST_WORD; 00553 00554 /* 00555 * Add a space if the word is not the last one (which has a 00556 * moderately complex condition here). 00557 */ 00558 00559 if (i<toPrint-1 || objc!=0 || message!=NULL) { 00560 Tcl_AppendStringsToObj(objPtr, " ", NULL); 00561 } 00562 } 00563 } 00564 00565 /* 00566 * Now add the arguments (other than those rewritten) that the caller took 00567 * from its calling context. 00568 */ 00569 00570 addNormalArgumentsToMessage: 00571 for (i = 0; i < objc; i++) { 00572 /* 00573 * If the object is an index type use the index table which allows for 00574 * the correct error message even if the subcommand was abbreviated. 00575 * Otherwise, just use the string rep. 00576 */ 00577 00578 if (objv[i]->typePtr == &indexType) { 00579 register IndexRep *indexRep = objv[i]->internalRep.otherValuePtr; 00580 00581 Tcl_AppendStringsToObj(objPtr, EXPAND_OF(indexRep), NULL); 00582 } else if (objv[i]->typePtr == &tclEnsembleCmdType) { 00583 register EnsembleCmdRep *ecrPtr = 00584 objv[i]->internalRep.otherValuePtr; 00585 00586 Tcl_AppendStringsToObj(objPtr, ecrPtr->fullSubcmdName, NULL); 00587 } else { 00588 /* 00589 * Quote the argument if it contains spaces (Bug 942757). 00590 */ 00591 00592 elementStr = TclGetStringFromObj(objv[i], &elemLen); 00593 len = Tcl_ScanCountedElement(elementStr, elemLen, &flags); 00594 00595 if (MAY_QUOTE_WORD && len != elemLen) { 00596 char *quotedElementStr = TclStackAlloc(interp,(unsigned) len); 00597 00598 len = Tcl_ConvertCountedElement(elementStr, elemLen, 00599 quotedElementStr, flags); 00600 Tcl_AppendToObj(objPtr, quotedElementStr, len); 00601 TclStackFree(interp, quotedElementStr); 00602 } else { 00603 Tcl_AppendToObj(objPtr, elementStr, elemLen); 00604 } 00605 } 00606 00607 AFTER_FIRST_WORD; 00608 00609 /* 00610 * Append a space character (" ") if there is more text to follow 00611 * (either another element from objv, or the message string). 00612 */ 00613 00614 if (i<objc-1 || message!=NULL) { 00615 Tcl_AppendStringsToObj(objPtr, " ", NULL); 00616 } 00617 } 00618 00619 /* 00620 * Add any trailing message bits and set the resulting string as the 00621 * interpreter result. Caller is responsible for reporting this as an 00622 * actual error. 00623 */ 00624 00625 if (message != NULL) { 00626 Tcl_AppendStringsToObj(objPtr, message, NULL); 00627 } 00628 Tcl_AppendStringsToObj(objPtr, "\"", NULL); 00629 Tcl_SetObjResult(interp, objPtr); 00630 #undef MAY_QUOTE_WORD 00631 #undef AFTER_FIRST_WORD 00632 } 00633 00634 /* 00635 * Local Variables: 00636 * mode: c 00637 * c-basic-offset: 4 00638 * fill-column: 78 00639 * End: 00640 */
Generated on Wed Mar 12 12:18:16 2008 by 1.5.1 |