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