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