tclIndexObj.c

Go 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  doxygen 1.5.1