tclIndexObj.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:14k
源码类别:

通讯编程

开发平台:

Visual C++

  1. /* 
  2.  * tclIndexObj.c --
  3.  *
  4.  * This file implements objects of type "index".  This object type
  5.  * is used to lookup a keyword in a table of valid values and cache
  6.  * the index of the matching entry.
  7.  *
  8.  * Copyright (c) 1997 Sun Microsystems, Inc.
  9.  *
  10.  * See the file "license.terms" for information on usage and redistribution
  11.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  12.  *
  13.  * RCS: @(#) $Id: tclIndexObj.c,v 1.16.2.5 2006/04/06 18:57:24 dgp Exp $
  14.  */
  15. #include "tclInt.h"
  16. #include "tclPort.h"
  17. /*
  18.  * Prototypes for procedures defined later in this file:
  19.  */
  20. static int SetIndexFromAny _ANSI_ARGS_((Tcl_Interp *interp,
  21.     Tcl_Obj *objPtr));
  22. static void UpdateStringOfIndex _ANSI_ARGS_((Tcl_Obj *objPtr));
  23. static void DupIndex _ANSI_ARGS_((Tcl_Obj *srcPtr,
  24.     Tcl_Obj *dupPtr));
  25. static void FreeIndex _ANSI_ARGS_((Tcl_Obj *objPtr));
  26. /*
  27.  * The structure below defines the index Tcl object type by means of
  28.  * procedures that can be invoked by generic object code.
  29.  */
  30. Tcl_ObjType tclIndexType = {
  31.     "index", /* name */
  32.     FreeIndex, /* freeIntRepProc */
  33.     DupIndex, /* dupIntRepProc */
  34.     UpdateStringOfIndex, /* updateStringProc */
  35.     SetIndexFromAny /* setFromAnyProc */
  36. };
  37. /*
  38.  * The definition of the internal representation of the "index"
  39.  * object; The internalRep.otherValuePtr field of an object of "index"
  40.  * type will be a pointer to one of these structures.
  41.  *
  42.  * Keep this structure declaration in sync with tclTestObj.c
  43.  */
  44. typedef struct {
  45.     VOID *tablePtr; /* Pointer to the table of strings */
  46.     int offset; /* Offset between table entries */
  47.     int index; /* Selected index into table. */
  48. } IndexRep;
  49. /*
  50.  * The following macros greatly simplify moving through a table...
  51.  */
  52. #define STRING_AT(table, offset, index) 
  53. (*((CONST char * CONST *)(((char *)(table)) + ((offset) * (index)))))
  54. #define NEXT_ENTRY(table, offset) 
  55. (&(STRING_AT(table, offset, 1)))
  56. #define EXPAND_OF(indexRep) 
  57. STRING_AT((indexRep)->tablePtr, (indexRep)->offset, (indexRep)->index)
  58. /*
  59.  *----------------------------------------------------------------------
  60.  *
  61.  * Tcl_GetIndexFromObj --
  62.  *
  63.  * This procedure looks up an object's value in a table of strings
  64.  * and returns the index of the matching string, if any.
  65.  *
  66.  * Results:
  67.  *
  68.  * If the value of objPtr is identical to or a unique abbreviation
  69.  * for one of the entries in objPtr, then the return value is
  70.  * TCL_OK and the index of the matching entry is stored at
  71.  * *indexPtr.  If there isn't a proper match, then TCL_ERROR is
  72.  * returned and an error message is left in interp's result (unless
  73.  * interp is NULL).  The msg argument is used in the error
  74.  * message; for example, if msg has the value "option" then the
  75.  * error message will say something flag 'bad option "foo": must be
  76.  * ...'
  77.  *
  78.  * Side effects:
  79.  * The result of the lookup is cached as the internal rep of
  80.  * objPtr, so that repeated lookups can be done quickly.
  81.  *
  82.  *----------------------------------------------------------------------
  83.  */
  84. int
  85. Tcl_GetIndexFromObj(interp, objPtr, tablePtr, msg, flags, indexPtr)
  86.     Tcl_Interp *interp;  /* Used for error reporting if not NULL. */
  87.     Tcl_Obj *objPtr; /* Object containing the string to lookup. */
  88.     CONST char **tablePtr; /* Array of strings to compare against the
  89.  * value of objPtr; last entry must be NULL
  90.  * and there must not be duplicate entries. */
  91.     CONST char *msg; /* Identifying word to use in error messages. */
  92.     int flags; /* 0 or TCL_EXACT */
  93.     int *indexPtr; /* Place to store resulting integer index. */
  94. {
  95.     /*
  96.      * See if there is a valid cached result from a previous lookup
  97.      * (doing the check here saves the overhead of calling
  98.      * Tcl_GetIndexFromObjStruct in the common case where the result
  99.      * is cached).
  100.      */
  101.     if (objPtr->typePtr == &tclIndexType) {
  102. IndexRep *indexRep = (IndexRep *) objPtr->internalRep.otherValuePtr;
  103. /*
  104.  * Here's hoping we don't get hit by unfortunate packing
  105.  * constraints on odd platforms like a Cray PVP...
  106.  */
  107. if (indexRep->tablePtr == (VOID *)tablePtr &&
  108. indexRep->offset == sizeof(char *)) {
  109.     *indexPtr = indexRep->index;
  110.     return TCL_OK;
  111. }
  112.     }
  113.     return Tcl_GetIndexFromObjStruct(interp, objPtr, tablePtr, sizeof(char *),
  114.     msg, flags, indexPtr);
  115. }
  116. /*
  117.  *----------------------------------------------------------------------
  118.  *
  119.  * Tcl_GetIndexFromObjStruct --
  120.  *
  121.  * This procedure looks up an object's value given a starting
  122.  * string and an offset for the amount of space between strings.
  123.  * This is useful when the strings are embedded in some other
  124.  * kind of array.
  125.  *
  126.  * Results:
  127.  *
  128.  * If the value of objPtr is identical to or a unique abbreviation
  129.  * for one of the entries in objPtr, then the return value is
  130.  * TCL_OK and the index of the matching entry is stored at
  131.  * *indexPtr.  If there isn't a proper match, then TCL_ERROR is
  132.  * returned and an error message is left in interp's result (unless
  133.  * interp is NULL).  The msg argument is used in the error
  134.  * message; for example, if msg has the value "option" then the
  135.  * error message will say something flag 'bad option "foo": must be
  136.  * ...'
  137.  *
  138.  * Side effects:
  139.  * The result of the lookup is cached as the internal rep of
  140.  * objPtr, so that repeated lookups can be done quickly.
  141.  *
  142.  *----------------------------------------------------------------------
  143.  */
  144. int
  145. Tcl_GetIndexFromObjStruct(interp, objPtr, tablePtr, offset, msg, flags, 
  146. indexPtr)
  147.     Tcl_Interp *interp;  /* Used for error reporting if not NULL. */
  148.     Tcl_Obj *objPtr; /* Object containing the string to lookup. */
  149.     CONST VOID *tablePtr; /* The first string in the table. The second
  150.  * string will be at this address plus the
  151.  * offset, the third plus the offset again,
  152.  * etc. The last entry must be NULL
  153.  * and there must not be duplicate entries. */
  154.     int offset; /* The number of bytes between entries */
  155.     CONST char *msg; /* Identifying word to use in error messages. */
  156.     int flags; /* 0 or TCL_EXACT */
  157.     int *indexPtr; /* Place to store resulting integer index. */
  158. {
  159.     int index, i, numAbbrev;
  160.     char *key, *p1;
  161.     CONST char *p2;
  162.     CONST char * CONST *entryPtr;
  163.     Tcl_Obj *resultPtr;
  164.     IndexRep *indexRep;
  165.     /*
  166.      * See if there is a valid cached result from a previous lookup.
  167.      */
  168.     if (objPtr->typePtr == &tclIndexType) {
  169. indexRep = (IndexRep *) objPtr->internalRep.otherValuePtr;
  170. if (indexRep->tablePtr==tablePtr && indexRep->offset==offset) {
  171.     *indexPtr = indexRep->index;
  172.     return TCL_OK;
  173. }
  174.     }
  175.     /*
  176.      * Lookup the value of the object in the table.  Accept unique
  177.      * abbreviations unless TCL_EXACT is set in flags.
  178.      */
  179.     key = TclGetString(objPtr);
  180.     index = -1;
  181.     numAbbrev = 0;
  182.     /*
  183.      * Scan the table looking for one of:
  184.      *  - An exact match (always preferred)
  185.      *  - A single abbreviation (allowed depending on flags)
  186.      *  - Several abbreviations (never allowed, but overridden by exact match)
  187.      */
  188.     for (entryPtr = tablePtr, i = 0; *entryPtr != NULL; 
  189.     entryPtr = NEXT_ENTRY(entryPtr, offset), i++) {
  190. for (p1 = key, p2 = *entryPtr; *p1 == *p2; p1++, p2++) {
  191.     if (*p1 == '') {
  192. index = i;
  193. goto done;
  194.     }
  195. }
  196. if (*p1 == '') {
  197.     /*
  198.      * The value is an abbreviation for this entry.  Continue
  199.      * checking other entries to make sure it's unique.  If we
  200.      * get more than one unique abbreviation, keep searching to
  201.      * see if there is an exact match, but remember the number
  202.      * of unique abbreviations and don't allow either.
  203.      */
  204.     numAbbrev++;
  205.     index = i;
  206. }
  207.     }
  208.     /*
  209.      * Check if we were instructed to disallow abbreviations. 
  210.      */
  211.     if ((flags & TCL_EXACT) || (key[0] == '') || (numAbbrev != 1)) {
  212. goto error;
  213.     }
  214.     done:
  215.     /*
  216.      * Cache the found representation.  Note that we want to avoid
  217.      * allocating a new internal-rep if at all possible since that is
  218.      * potentially a slow operation.
  219.      */
  220.     if (objPtr->typePtr == &tclIndexType) {
  221.   indexRep = (IndexRep *) objPtr->internalRep.otherValuePtr;
  222.     } else {
  223.   if ((objPtr->typePtr != NULL)
  224. && (objPtr->typePtr->freeIntRepProc != NULL)) {
  225.       objPtr->typePtr->freeIntRepProc(objPtr);
  226.   }
  227.   indexRep = (IndexRep *) ckalloc(sizeof(IndexRep));
  228.   objPtr->internalRep.otherValuePtr = (VOID *) indexRep;
  229.   objPtr->typePtr = &tclIndexType;
  230.     }
  231.     indexRep->tablePtr = (VOID*) tablePtr;
  232.     indexRep->offset = offset;
  233.     indexRep->index = index;
  234.     *indexPtr = index;
  235.     return TCL_OK;
  236.     error:
  237.     if (interp != NULL) {
  238. /*
  239.  * Produce a fancy error message.
  240.  */
  241. int count;
  242. TclNewObj(resultPtr);
  243. Tcl_SetObjResult(interp, resultPtr);
  244. Tcl_AppendStringsToObj(resultPtr, (numAbbrev > 1) &&
  245. !(flags & TCL_EXACT) ? "ambiguous " : "bad ", msg, " "",
  246. key, "": must be ", STRING_AT(tablePtr,offset,0), (char*)NULL);
  247. for (entryPtr = NEXT_ENTRY(tablePtr, offset), count = 0;
  248. *entryPtr != NULL;
  249. entryPtr = NEXT_ENTRY(entryPtr, offset), count++) {
  250.     if (*NEXT_ENTRY(entryPtr, offset) == NULL) {
  251. Tcl_AppendStringsToObj(resultPtr,
  252. (count > 0) ? ", or " : " or ", *entryPtr,
  253. (char *) NULL);
  254.     } else {
  255. Tcl_AppendStringsToObj(resultPtr, ", ", *entryPtr,
  256. (char *) NULL);
  257.     }
  258. }
  259.     }
  260.     return TCL_ERROR;
  261. }
  262. /*
  263.  *----------------------------------------------------------------------
  264.  *
  265.  * SetIndexFromAny --
  266.  *
  267.  * This procedure is called to convert a Tcl object to index
  268.  * internal form. However, this doesn't make sense (need to have a
  269.  * table of keywords in order to do the conversion) so the
  270.  * procedure always generates an error.
  271.  *
  272.  * Results:
  273.  * The return value is always TCL_ERROR, and an error message is
  274.  * left in interp's result if interp isn't NULL. 
  275.  *
  276.  * Side effects:
  277.  * None.
  278.  *
  279.  *----------------------------------------------------------------------
  280.  */
  281. static int
  282. SetIndexFromAny(interp, objPtr)
  283.     Tcl_Interp *interp; /* Used for error reporting if not NULL. */
  284.     register Tcl_Obj *objPtr; /* The object to convert. */
  285. {
  286.     Tcl_AppendToObj(Tcl_GetObjResult(interp),
  287.     "can't convert value to index except via Tcl_GetIndexFromObj API",
  288.     -1);
  289.     return TCL_ERROR;
  290. }
  291. /*
  292.  *----------------------------------------------------------------------
  293.  *
  294.  * UpdateStringOfIndex --
  295.  *
  296.  * This procedure is called to convert a Tcl object from index
  297.  * internal form to its string form.  No abbreviation is ever
  298.  * generated.
  299.  *
  300.  * Results:
  301.  * None.
  302.  *
  303.  * Side effects:
  304.  * The string representation of the object is updated.
  305.  *
  306.  *----------------------------------------------------------------------
  307.  */
  308. static void
  309. UpdateStringOfIndex(objPtr)
  310.     Tcl_Obj *objPtr;
  311. {
  312.     IndexRep *indexRep = (IndexRep *) objPtr->internalRep.otherValuePtr;
  313.     register char *buf;
  314.     register unsigned len;
  315.     register CONST char *indexStr = EXPAND_OF(indexRep);
  316.     len = strlen(indexStr);
  317.     buf = (char *) ckalloc(len + 1);
  318.     memcpy(buf, indexStr, len+1);
  319.     objPtr->bytes = buf;
  320.     objPtr->length = len;
  321. }
  322. /*
  323.  *----------------------------------------------------------------------
  324.  *
  325.  * DupIndex --
  326.  *
  327.  * This procedure is called to copy the internal rep of an index
  328.  * Tcl object from to another object.
  329.  *
  330.  * Results:
  331.  * None.
  332.  *
  333.  * Side effects:
  334.  * The internal representation of the target object is updated
  335.  * and the type is set.
  336.  *
  337.  *----------------------------------------------------------------------
  338.  */
  339. static void
  340. DupIndex(srcPtr, dupPtr)
  341.     Tcl_Obj *srcPtr, *dupPtr;
  342. {
  343.     IndexRep *srcIndexRep = (IndexRep *) srcPtr->internalRep.otherValuePtr;
  344.     IndexRep *dupIndexRep = (IndexRep *) ckalloc(sizeof(IndexRep));
  345.     memcpy(dupIndexRep, srcIndexRep, sizeof(IndexRep));
  346.     dupPtr->internalRep.otherValuePtr = (VOID *) dupIndexRep;
  347.     dupPtr->typePtr = &tclIndexType;
  348. }
  349. /*
  350.  *----------------------------------------------------------------------
  351.  *
  352.  * FreeIndex --
  353.  *
  354.  * This procedure is called to delete the internal rep of an index
  355.  * Tcl object.
  356.  *
  357.  * Results:
  358.  * None.
  359.  *
  360.  * Side effects:
  361.  * The internal representation of the target object is deleted.
  362.  *
  363.  *----------------------------------------------------------------------
  364.  */
  365. static void
  366. FreeIndex(objPtr)
  367.     Tcl_Obj *objPtr;
  368. {
  369.     ckfree((char *) objPtr->internalRep.otherValuePtr);
  370. }
  371. /*
  372.  *----------------------------------------------------------------------
  373.  *
  374.  * Tcl_WrongNumArgs --
  375.  *
  376.  * This procedure generates a "wrong # args" error message in an
  377.  * interpreter.  It is used as a utility function by many command
  378.  * procedures.
  379.  *
  380.  * Results:
  381.  * None.
  382.  *
  383.  * Side effects:
  384.  * An error message is generated in interp's result object to
  385.  * indicate that a command was invoked with the wrong number of
  386.  * arguments.  The message has the form
  387.  * wrong # args: should be "foo bar additional stuff"
  388.  * where "foo" and "bar" are the initial objects in objv (objc
  389.  * determines how many of these are printed) and "additional stuff"
  390.  * is the contents of the message argument.
  391.  *
  392.  *----------------------------------------------------------------------
  393.  */
  394. void
  395. Tcl_WrongNumArgs(interp, objc, objv, message)
  396.     Tcl_Interp *interp; /* Current interpreter. */
  397.     int objc; /* Number of arguments to print
  398.  * from objv. */
  399.     Tcl_Obj *CONST objv[]; /* Initial argument objects, which
  400.  * should be included in the error
  401.  * message. */
  402.     CONST char *message; /* Error message to print after the
  403.  * leading objects in objv. The
  404.  * message may be NULL. */
  405. {
  406.     Tcl_Obj *objPtr;
  407.     int i;
  408.     register IndexRep *indexRep;
  409.     TclNewObj(objPtr);
  410.     Tcl_SetObjResult(interp, objPtr);
  411.     Tcl_AppendToObj(objPtr, "wrong # args: should be "", -1);
  412.     for (i = 0; i < objc; i++) {
  413. /*
  414.  * If the object is an index type use the index table which allows
  415.  * for the correct error message even if the subcommand was
  416.  * abbreviated.  Otherwise, just use the string rep.
  417.  */
  418. if (objv[i]->typePtr == &tclIndexType) {
  419.     indexRep = (IndexRep *) objv[i]->internalRep.otherValuePtr;
  420.     Tcl_AppendStringsToObj(objPtr, EXPAND_OF(indexRep), (char *) NULL);
  421. } else {
  422.     Tcl_AppendStringsToObj(objPtr, Tcl_GetString(objv[i]),
  423.     (char *) NULL);
  424. }
  425. /*
  426.  * Append a space character (" ") if there is more text to follow
  427.  * (either another element from objv, or the message string).
  428.  */
  429. if ((i < (objc - 1)) || message) {
  430.     Tcl_AppendStringsToObj(objPtr, " ", (char *) NULL);
  431. }
  432.     }
  433.     if (message) {
  434. Tcl_AppendStringsToObj(objPtr, message, (char *) NULL);
  435.     }
  436.     Tcl_AppendStringsToObj(objPtr, """, (char *) NULL);
  437. }