os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/generic/tclBinary.c
author sl@SLION-WIN7.fritz.box
Fri, 15 Jun 2012 03:10:57 +0200
changeset 0 bde4ae8d615e
permissions -rw-r--r--
First public contribution.
sl@0
     1
/* 
sl@0
     2
 * tclBinary.c --
sl@0
     3
 *
sl@0
     4
 *	This file contains the implementation of the "binary" Tcl built-in
sl@0
     5
 *	command and the Tcl binary data object.
sl@0
     6
 *
sl@0
     7
 * Copyright (c) 1997 by Sun Microsystems, Inc.
sl@0
     8
 * Copyright (c) 1998-1999 by Scriptics Corporation.
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: tclBinary.c,v 1.13.2.4 2005/10/23 22:01:29 msofer Exp $
sl@0
    15
 */
sl@0
    16
sl@0
    17
#include "tclInt.h"
sl@0
    18
#include "tclPort.h"
sl@0
    19
#include <math.h>
sl@0
    20
sl@0
    21
/*
sl@0
    22
 * The following constants are used by GetFormatSpec to indicate various
sl@0
    23
 * special conditions in the parsing of a format specifier.
sl@0
    24
 */
sl@0
    25
sl@0
    26
#define BINARY_ALL -1		/* Use all elements in the argument. */
sl@0
    27
#define BINARY_NOCOUNT -2	/* No count was specified in format. */
sl@0
    28
sl@0
    29
/*
sl@0
    30
 * The following defines the maximum number of different (integer)
sl@0
    31
 * numbers placed in the object cache by 'binary scan' before it bails
sl@0
    32
 * out and switches back to Plan A (creating a new object for each
sl@0
    33
 * value.)  Theoretically, it would be possible to keep the cache
sl@0
    34
 * about for the values that are already in it, but that makes the
sl@0
    35
 * code slower in practise when overflow happens, and makes little
sl@0
    36
 * odds the rest of the time (as measured on my machine.)  It is also
sl@0
    37
 * slower (on the sample I tried at least) to grow the cache to hold
sl@0
    38
 * all items we might want to put in it; presumably the extra cost of
sl@0
    39
 * managing the memory for the enlarged table outweighs the benefit
sl@0
    40
 * from allocating fewer objects.  This is probably because as the
sl@0
    41
 * number of objects increases, the likelihood of reuse of any
sl@0
    42
 * particular one drops, and there is very little gain from larger
sl@0
    43
 * maximum cache sizes (the value below is chosen to allow caching to
sl@0
    44
 * work in full with conversion of bytes.) - DKF
sl@0
    45
 */
sl@0
    46
sl@0
    47
#define BINARY_SCAN_MAX_CACHE	260
sl@0
    48
sl@0
    49
/*
sl@0
    50
 * Prototypes for local procedures defined in this file:
sl@0
    51
 */
sl@0
    52
sl@0
    53
static void		DupByteArrayInternalRep _ANSI_ARGS_((Tcl_Obj *srcPtr,
sl@0
    54
			    Tcl_Obj *copyPtr));
sl@0
    55
static int		FormatNumber _ANSI_ARGS_((Tcl_Interp *interp, int type,
sl@0
    56
			    Tcl_Obj *src, unsigned char **cursorPtr));
sl@0
    57
static void		CopyNumber _ANSI_ARGS_((CONST VOID *from, VOID *to,
sl@0
    58
			    unsigned int length));
sl@0
    59
static void		FreeByteArrayInternalRep _ANSI_ARGS_((Tcl_Obj *objPtr));
sl@0
    60
static int		GetFormatSpec _ANSI_ARGS_((char **formatPtr,
sl@0
    61
			    char *cmdPtr, int *countPtr));
sl@0
    62
static Tcl_Obj *	ScanNumber _ANSI_ARGS_((unsigned char *buffer,
sl@0
    63
			    int type, Tcl_HashTable **numberCachePtr));
sl@0
    64
static int		SetByteArrayFromAny _ANSI_ARGS_((Tcl_Interp *interp,
sl@0
    65
			    Tcl_Obj *objPtr));
sl@0
    66
static void		UpdateStringOfByteArray _ANSI_ARGS_((Tcl_Obj *listPtr));
sl@0
    67
static void		DeleteScanNumberCache _ANSI_ARGS_((
sl@0
    68
			    Tcl_HashTable *numberCachePtr));
sl@0
    69
sl@0
    70
/*
sl@0
    71
 * The following object type represents an array of bytes.  An array of
sl@0
    72
 * bytes is not equivalent to an internationalized string.  Conceptually, a
sl@0
    73
 * string is an array of 16-bit quantities organized as a sequence of properly
sl@0
    74
 * formed UTF-8 characters, while a ByteArray is an array of 8-bit quantities.
sl@0
    75
 * Accessor functions are provided to convert a ByteArray to a String or a
sl@0
    76
 * String to a ByteArray.  Two or more consecutive bytes in an array of bytes
sl@0
    77
 * may look like a single UTF-8 character if the array is casually treated as
sl@0
    78
 * a string.  But obtaining the String from a ByteArray is guaranteed to
sl@0
    79
 * produced properly formed UTF-8 sequences so that there is a one-to-one
sl@0
    80
 * map between bytes and characters.
sl@0
    81
 *
sl@0
    82
 * Converting a ByteArray to a String proceeds by casting each byte in the
sl@0
    83
 * array to a 16-bit quantity, treating that number as a Unicode character,
sl@0
    84
 * and storing the UTF-8 version of that Unicode character in the String.
sl@0
    85
 * For ByteArrays consisting entirely of values 1..127, the corresponding
sl@0
    86
 * String representation is the same as the ByteArray representation.
sl@0
    87
 *
sl@0
    88
 * Converting a String to a ByteArray proceeds by getting the Unicode
sl@0
    89
 * representation of each character in the String, casting it to a
sl@0
    90
 * byte by truncating the upper 8 bits, and then storing the byte in the
sl@0
    91
 * ByteArray.  Converting from ByteArray to String and back to ByteArray
sl@0
    92
 * is not lossy, but converting an arbitrary String to a ByteArray may be.
sl@0
    93
 */
sl@0
    94
sl@0
    95
Tcl_ObjType tclByteArrayType = {
sl@0
    96
    "bytearray",
sl@0
    97
    FreeByteArrayInternalRep,
sl@0
    98
    DupByteArrayInternalRep,
sl@0
    99
    UpdateStringOfByteArray,
sl@0
   100
    SetByteArrayFromAny
sl@0
   101
};
sl@0
   102
sl@0
   103
/*
sl@0
   104
 * The following structure is the internal rep for a ByteArray object.
sl@0
   105
 * Keeps track of how much memory has been used and how much has been
sl@0
   106
 * allocated for the byte array to enable growing and shrinking of the
sl@0
   107
 * ByteArray object with fewer mallocs.  
sl@0
   108
 */
sl@0
   109
sl@0
   110
typedef struct ByteArray {
sl@0
   111
    int used;			/* The number of bytes used in the byte
sl@0
   112
				 * array. */
sl@0
   113
    int allocated;		/* The amount of space actually allocated
sl@0
   114
				 * minus 1 byte. */
sl@0
   115
    unsigned char bytes[4];	/* The array of bytes.  The actual size of
sl@0
   116
				 * this field depends on the 'allocated' field
sl@0
   117
				 * above. */
sl@0
   118
} ByteArray;
sl@0
   119
sl@0
   120
#define BYTEARRAY_SIZE(len)	\
sl@0
   121
		((unsigned) (sizeof(ByteArray) - 4 + (len)))
sl@0
   122
#define GET_BYTEARRAY(objPtr) \
sl@0
   123
		((ByteArray *) (objPtr)->internalRep.otherValuePtr)
sl@0
   124
#define SET_BYTEARRAY(objPtr, baPtr) \
sl@0
   125
		(objPtr)->internalRep.otherValuePtr = (VOID *) (baPtr)
sl@0
   126
sl@0
   127

sl@0
   128
/*
sl@0
   129
 *---------------------------------------------------------------------------
sl@0
   130
 *
sl@0
   131
 * Tcl_NewByteArrayObj --
sl@0
   132
 *
sl@0
   133
 *	This procedure is creates a new ByteArray object and initializes
sl@0
   134
 *	it from the given array of bytes.
sl@0
   135
 *
sl@0
   136
 * Results:
sl@0
   137
 *	The newly create object is returned.  This object will have no
sl@0
   138
 *	initial string representation.  The returned object has a ref count
sl@0
   139
 *	of 0.
sl@0
   140
 *
sl@0
   141
 * Side effects:
sl@0
   142
 *	Memory allocated for new object and copy of byte array argument.
sl@0
   143
 *
sl@0
   144
 *---------------------------------------------------------------------------
sl@0
   145
 */
sl@0
   146
sl@0
   147
#ifdef TCL_MEM_DEBUG
sl@0
   148
#undef Tcl_NewByteArrayObj
sl@0
   149
sl@0
   150
sl@0
   151
EXPORT_C Tcl_Obj *
sl@0
   152
Tcl_NewByteArrayObj(bytes, length)
sl@0
   153
    CONST unsigned char *bytes;	/* The array of bytes used to initialize
sl@0
   154
				 * the new object. */
sl@0
   155
    int length;			/* Length of the array of bytes, which must
sl@0
   156
				 * be >= 0. */
sl@0
   157
{
sl@0
   158
    return Tcl_DbNewByteArrayObj(bytes, length, "unknown", 0);
sl@0
   159
}
sl@0
   160
sl@0
   161
#else /* if not TCL_MEM_DEBUG */
sl@0
   162
sl@0
   163
EXPORT_C Tcl_Obj *
sl@0
   164
Tcl_NewByteArrayObj(bytes, length)
sl@0
   165
    CONST unsigned char *bytes;	/* The array of bytes used to initialize
sl@0
   166
				 * the new object. */
sl@0
   167
    int length;			/* Length of the array of bytes, which must
sl@0
   168
				 * be >= 0. */
sl@0
   169
{
sl@0
   170
    Tcl_Obj *objPtr;
sl@0
   171
sl@0
   172
    TclNewObj(objPtr);
sl@0
   173
    Tcl_SetByteArrayObj(objPtr, bytes, length);
sl@0
   174
    return objPtr;
sl@0
   175
}
sl@0
   176
#endif /* TCL_MEM_DEBUG */
sl@0
   177

sl@0
   178
/*
sl@0
   179
 *---------------------------------------------------------------------------
sl@0
   180
 *
sl@0
   181
 * Tcl_DbNewByteArrayObj --
sl@0
   182
 *
sl@0
   183
 *	This procedure is normally called when debugging: i.e., when
sl@0
   184
 *	TCL_MEM_DEBUG is defined. It is the same as the Tcl_NewByteArrayObj
sl@0
   185
 *	above except that it calls Tcl_DbCkalloc directly with the file name
sl@0
   186
 *	and line number from its caller. This simplifies debugging since then
sl@0
   187
 *	the [memory active] command will report the correct file name and line
sl@0
   188
 *	number when reporting objects that haven't been freed.
sl@0
   189
 *
sl@0
   190
 *	When TCL_MEM_DEBUG is not defined, this procedure just returns the
sl@0
   191
 *	result of calling Tcl_NewByteArrayObj.
sl@0
   192
 *
sl@0
   193
 * Results:
sl@0
   194
 *	The newly create object is returned.  This object will have no
sl@0
   195
 *	initial string representation.  The returned object has a ref count
sl@0
   196
 *	of 0.
sl@0
   197
 *
sl@0
   198
 * Side effects:
sl@0
   199
 *	Memory allocated for new object and copy of byte array argument.
sl@0
   200
 *
sl@0
   201
 *---------------------------------------------------------------------------
sl@0
   202
 */
sl@0
   203
sl@0
   204
#ifdef TCL_MEM_DEBUG
sl@0
   205
sl@0
   206
EXPORT_C Tcl_Obj *
sl@0
   207
Tcl_DbNewByteArrayObj(bytes, length, file, line)
sl@0
   208
    CONST unsigned char *bytes;	/* The array of bytes used to initialize
sl@0
   209
				 * the new object. */
sl@0
   210
    int length;			/* Length of the array of bytes, which must
sl@0
   211
				 * be >= 0. */
sl@0
   212
    CONST char *file;		/* The name of the source file calling this
sl@0
   213
				 * procedure; used for debugging. */
sl@0
   214
    int line;			/* Line number in the source file; used
sl@0
   215
				 * for debugging. */
sl@0
   216
{
sl@0
   217
    Tcl_Obj *objPtr;
sl@0
   218
sl@0
   219
    TclDbNewObj(objPtr, file, line);
sl@0
   220
    Tcl_SetByteArrayObj(objPtr, bytes, length);
sl@0
   221
    return objPtr;
sl@0
   222
}
sl@0
   223
sl@0
   224
#else /* if not TCL_MEM_DEBUG */
sl@0
   225
sl@0
   226
EXPORT_C Tcl_Obj *
sl@0
   227
Tcl_DbNewByteArrayObj(bytes, length, file, line)
sl@0
   228
    CONST unsigned char *bytes;	/* The array of bytes used to initialize
sl@0
   229
				 * the new object. */
sl@0
   230
    int length;			/* Length of the array of bytes, which must
sl@0
   231
				 * be >= 0. */
sl@0
   232
    CONST char *file;		/* The name of the source file calling this
sl@0
   233
				 * procedure; used for debugging. */
sl@0
   234
    int line;			/* Line number in the source file; used
sl@0
   235
				 * for debugging. */
sl@0
   236
{
sl@0
   237
    return Tcl_NewByteArrayObj(bytes, length);
sl@0
   238
}
sl@0
   239
#endif /* TCL_MEM_DEBUG */
sl@0
   240
sl@0
   241
/*
sl@0
   242
 *---------------------------------------------------------------------------
sl@0
   243
 *
sl@0
   244
 * Tcl_SetByteArrayObj --
sl@0
   245
 *
sl@0
   246
 *	Modify an object to be a ByteArray object and to have the specified
sl@0
   247
 *	array of bytes as its value.
sl@0
   248
 *
sl@0
   249
 * Results:
sl@0
   250
 *	None.
sl@0
   251
 *
sl@0
   252
 * Side effects:
sl@0
   253
 *	The object's old string rep and internal rep is freed.
sl@0
   254
 *	Memory allocated for copy of byte array argument.
sl@0
   255
 *
sl@0
   256
 *----------------------------------------------------------------------
sl@0
   257
 */
sl@0
   258
sl@0
   259
EXPORT_C void
sl@0
   260
Tcl_SetByteArrayObj(objPtr, bytes, length)
sl@0
   261
    Tcl_Obj *objPtr;		/* Object to initialize as a ByteArray. */
sl@0
   262
    CONST unsigned char *bytes;	/* The array of bytes to use as the new
sl@0
   263
				 * value. */
sl@0
   264
    int length;			/* Length of the array of bytes, which must
sl@0
   265
				 * be >= 0. */
sl@0
   266
{
sl@0
   267
    Tcl_ObjType *typePtr;
sl@0
   268
    ByteArray *byteArrayPtr;
sl@0
   269
sl@0
   270
    if (Tcl_IsShared(objPtr)) {
sl@0
   271
	panic("Tcl_SetByteArrayObj called with shared object");
sl@0
   272
    }
sl@0
   273
    typePtr = objPtr->typePtr;
sl@0
   274
    if ((typePtr != NULL) && (typePtr->freeIntRepProc != NULL)) {
sl@0
   275
	(*typePtr->freeIntRepProc)(objPtr);
sl@0
   276
    }
sl@0
   277
    Tcl_InvalidateStringRep(objPtr);
sl@0
   278
sl@0
   279
    byteArrayPtr = (ByteArray *) ckalloc(BYTEARRAY_SIZE(length));
sl@0
   280
    byteArrayPtr->used = length;
sl@0
   281
    byteArrayPtr->allocated = length;
sl@0
   282
    memcpy((VOID *) byteArrayPtr->bytes, (VOID *) bytes, (size_t) length);
sl@0
   283
sl@0
   284
    objPtr->typePtr = &tclByteArrayType;
sl@0
   285
    SET_BYTEARRAY(objPtr, byteArrayPtr);
sl@0
   286
}
sl@0
   287

sl@0
   288
/*
sl@0
   289
 *----------------------------------------------------------------------
sl@0
   290
 *
sl@0
   291
 * Tcl_GetByteArrayFromObj --
sl@0
   292
 *
sl@0
   293
 *	Attempt to get the array of bytes from the Tcl object.  If the
sl@0
   294
 *	object is not already a ByteArray object, an attempt will be
sl@0
   295
 *	made to convert it to one.
sl@0
   296
 *
sl@0
   297
 * Results:
sl@0
   298
 *	Pointer to array of bytes representing the ByteArray object.
sl@0
   299
 *
sl@0
   300
 * Side effects:
sl@0
   301
 *	Frees old internal rep.  Allocates memory for new internal rep.
sl@0
   302
 *
sl@0
   303
 *----------------------------------------------------------------------
sl@0
   304
 */
sl@0
   305
sl@0
   306
EXPORT_C unsigned char *
sl@0
   307
Tcl_GetByteArrayFromObj(objPtr, lengthPtr)
sl@0
   308
    Tcl_Obj *objPtr;		/* The ByteArray object. */
sl@0
   309
    int *lengthPtr;		/* If non-NULL, filled with length of the
sl@0
   310
				 * array of bytes in the ByteArray object. */
sl@0
   311
{
sl@0
   312
    ByteArray *baPtr;
sl@0
   313
    
sl@0
   314
    SetByteArrayFromAny(NULL, objPtr);
sl@0
   315
    baPtr = GET_BYTEARRAY(objPtr);
sl@0
   316
sl@0
   317
    if (lengthPtr != NULL) {
sl@0
   318
	*lengthPtr = baPtr->used;
sl@0
   319
    }
sl@0
   320
    return (unsigned char *) baPtr->bytes;
sl@0
   321
}
sl@0
   322

sl@0
   323
/*
sl@0
   324
 *----------------------------------------------------------------------
sl@0
   325
 *
sl@0
   326
 * Tcl_SetByteArrayLength --
sl@0
   327
 *
sl@0
   328
 *	This procedure changes the length of the byte array for this
sl@0
   329
 *	object.  Once the caller has set the length of the array, it
sl@0
   330
 *	is acceptable to directly modify the bytes in the array up until
sl@0
   331
 *	Tcl_GetStringFromObj() has been called on this object.
sl@0
   332
 *
sl@0
   333
 * Results:
sl@0
   334
 *	The new byte array of the specified length.
sl@0
   335
 *
sl@0
   336
 * Side effects:
sl@0
   337
 *	Allocates enough memory for an array of bytes of the requested
sl@0
   338
 *	size.  When growing the array, the old array is copied to the
sl@0
   339
 *	new array; new bytes are undefined.  When shrinking, the
sl@0
   340
 *	old array is truncated to the specified length.
sl@0
   341
 *
sl@0
   342
 *---------------------------------------------------------------------------
sl@0
   343
 */
sl@0
   344
sl@0
   345
EXPORT_C unsigned char *
sl@0
   346
Tcl_SetByteArrayLength(objPtr, length)
sl@0
   347
    Tcl_Obj *objPtr;		/* The ByteArray object. */
sl@0
   348
    int length;			/* New length for internal byte array. */
sl@0
   349
{
sl@0
   350
    ByteArray *byteArrayPtr, *newByteArrayPtr;
sl@0
   351
    
sl@0
   352
    if (Tcl_IsShared(objPtr)) {
sl@0
   353
	panic("Tcl_SetObjLength called with shared object");
sl@0
   354
    }
sl@0
   355
    if (objPtr->typePtr != &tclByteArrayType) {
sl@0
   356
	SetByteArrayFromAny(NULL, objPtr);
sl@0
   357
    }
sl@0
   358
sl@0
   359
    byteArrayPtr = GET_BYTEARRAY(objPtr);
sl@0
   360
    if (length > byteArrayPtr->allocated) {
sl@0
   361
	newByteArrayPtr = (ByteArray *) ckalloc(BYTEARRAY_SIZE(length));
sl@0
   362
	newByteArrayPtr->used = length;
sl@0
   363
	newByteArrayPtr->allocated = length;
sl@0
   364
	memcpy((VOID *) newByteArrayPtr->bytes,
sl@0
   365
		(VOID *) byteArrayPtr->bytes, (size_t) byteArrayPtr->used);
sl@0
   366
	ckfree((char *) byteArrayPtr);
sl@0
   367
	byteArrayPtr = newByteArrayPtr;
sl@0
   368
	SET_BYTEARRAY(objPtr, byteArrayPtr);
sl@0
   369
    }
sl@0
   370
    Tcl_InvalidateStringRep(objPtr);
sl@0
   371
    byteArrayPtr->used = length;
sl@0
   372
    return byteArrayPtr->bytes;
sl@0
   373
}
sl@0
   374

sl@0
   375
/*
sl@0
   376
 *---------------------------------------------------------------------------
sl@0
   377
 *
sl@0
   378
 * SetByteArrayFromAny --
sl@0
   379
 *
sl@0
   380
 *	Generate the ByteArray internal rep from the string rep.
sl@0
   381
 *
sl@0
   382
 * Results:
sl@0
   383
 *	The return value is always TCL_OK.
sl@0
   384
 *
sl@0
   385
 * Side effects:
sl@0
   386
 *	A ByteArray object is stored as the internal rep of objPtr.
sl@0
   387
 *
sl@0
   388
 *---------------------------------------------------------------------------
sl@0
   389
 */
sl@0
   390
sl@0
   391
static int
sl@0
   392
SetByteArrayFromAny(interp, objPtr)
sl@0
   393
    Tcl_Interp *interp;		/* Not used. */
sl@0
   394
    Tcl_Obj *objPtr;		/* The object to convert to type ByteArray. */
sl@0
   395
{
sl@0
   396
    Tcl_ObjType *typePtr;
sl@0
   397
    int length;
sl@0
   398
    char *src, *srcEnd;
sl@0
   399
    unsigned char *dst;
sl@0
   400
    ByteArray *byteArrayPtr;
sl@0
   401
    Tcl_UniChar ch;
sl@0
   402
    
sl@0
   403
    typePtr = objPtr->typePtr;
sl@0
   404
    if (typePtr != &tclByteArrayType) {
sl@0
   405
	src = Tcl_GetStringFromObj(objPtr, &length);
sl@0
   406
	srcEnd = src + length;
sl@0
   407
sl@0
   408
	byteArrayPtr = (ByteArray *) ckalloc(BYTEARRAY_SIZE(length));
sl@0
   409
	for (dst = byteArrayPtr->bytes; src < srcEnd; ) {
sl@0
   410
	    src += Tcl_UtfToUniChar(src, &ch);
sl@0
   411
	    *dst++ = (unsigned char) ch;
sl@0
   412
	}
sl@0
   413
sl@0
   414
	byteArrayPtr->used = dst - byteArrayPtr->bytes;
sl@0
   415
	byteArrayPtr->allocated = length;
sl@0
   416
sl@0
   417
	if ((typePtr != NULL) && (typePtr->freeIntRepProc) != NULL) {
sl@0
   418
	    (*typePtr->freeIntRepProc)(objPtr);
sl@0
   419
	}
sl@0
   420
	objPtr->typePtr = &tclByteArrayType;
sl@0
   421
	SET_BYTEARRAY(objPtr, byteArrayPtr);
sl@0
   422
    }
sl@0
   423
    return TCL_OK;
sl@0
   424
}
sl@0
   425

sl@0
   426
/*
sl@0
   427
 *----------------------------------------------------------------------
sl@0
   428
 *
sl@0
   429
 * FreeByteArrayInternalRep --
sl@0
   430
 *
sl@0
   431
 *	Deallocate the storage associated with a ByteArray data object's
sl@0
   432
 *	internal representation.
sl@0
   433
 *
sl@0
   434
 * Results:
sl@0
   435
 *	None.
sl@0
   436
 *
sl@0
   437
 * Side effects:
sl@0
   438
 *	Frees memory. 
sl@0
   439
 *
sl@0
   440
 *----------------------------------------------------------------------
sl@0
   441
 */
sl@0
   442
sl@0
   443
static void
sl@0
   444
FreeByteArrayInternalRep(objPtr)
sl@0
   445
    Tcl_Obj *objPtr;		/* Object with internal rep to free. */
sl@0
   446
{
sl@0
   447
    ckfree((char *) GET_BYTEARRAY(objPtr));
sl@0
   448
}
sl@0
   449

sl@0
   450
/*
sl@0
   451
 *---------------------------------------------------------------------------
sl@0
   452
 *
sl@0
   453
 * DupByteArrayInternalRep --
sl@0
   454
 *
sl@0
   455
 *	Initialize the internal representation of a ByteArray Tcl_Obj
sl@0
   456
 *	to a copy of the internal representation of an existing ByteArray
sl@0
   457
 *	object. 
sl@0
   458
 *
sl@0
   459
 * Results:
sl@0
   460
 *	None.
sl@0
   461
 *
sl@0
   462
 * Side effects:
sl@0
   463
 *	Allocates memory.
sl@0
   464
 *
sl@0
   465
 *---------------------------------------------------------------------------
sl@0
   466
 */
sl@0
   467
sl@0
   468
static void
sl@0
   469
DupByteArrayInternalRep(srcPtr, copyPtr)
sl@0
   470
    Tcl_Obj *srcPtr;		/* Object with internal rep to copy. */
sl@0
   471
    Tcl_Obj *copyPtr;		/* Object with internal rep to set. */
sl@0
   472
{
sl@0
   473
    int length;
sl@0
   474
    ByteArray *srcArrayPtr, *copyArrayPtr;    
sl@0
   475
sl@0
   476
    srcArrayPtr = GET_BYTEARRAY(srcPtr);
sl@0
   477
    length = srcArrayPtr->used;
sl@0
   478
sl@0
   479
    copyArrayPtr = (ByteArray *) ckalloc(BYTEARRAY_SIZE(length));
sl@0
   480
    copyArrayPtr->used = length;
sl@0
   481
    copyArrayPtr->allocated = length;
sl@0
   482
    memcpy((VOID *) copyArrayPtr->bytes, (VOID *) srcArrayPtr->bytes,
sl@0
   483
	    (size_t) length);
sl@0
   484
    SET_BYTEARRAY(copyPtr, copyArrayPtr);
sl@0
   485
sl@0
   486
    copyPtr->typePtr = &tclByteArrayType;
sl@0
   487
}
sl@0
   488

sl@0
   489
/*
sl@0
   490
 *---------------------------------------------------------------------------
sl@0
   491
 *
sl@0
   492
 * UpdateStringOfByteArray --
sl@0
   493
 *
sl@0
   494
 *	Update the string representation for a ByteArray data object.
sl@0
   495
 *	Note: This procedure does not invalidate an existing old string rep
sl@0
   496
 *	so storage will be lost if this has not already been done. 
sl@0
   497
 *
sl@0
   498
 * Results:
sl@0
   499
 *	None.
sl@0
   500
 *
sl@0
   501
 * Side effects:
sl@0
   502
 *	The object's string is set to a valid string that results from
sl@0
   503
 *	the ByteArray-to-string conversion.
sl@0
   504
 *
sl@0
   505
 *	The object becomes a string object -- the internal rep is
sl@0
   506
 *	discarded and the typePtr becomes NULL.
sl@0
   507
 *
sl@0
   508
 *---------------------------------------------------------------------------
sl@0
   509
 */
sl@0
   510
sl@0
   511
static void
sl@0
   512
UpdateStringOfByteArray(objPtr)
sl@0
   513
    Tcl_Obj *objPtr;		/* ByteArray object whose string rep to
sl@0
   514
				 * update. */
sl@0
   515
{
sl@0
   516
    int i, length, size;
sl@0
   517
    unsigned char *src;
sl@0
   518
    char *dst;
sl@0
   519
    ByteArray *byteArrayPtr;
sl@0
   520
sl@0
   521
    byteArrayPtr = GET_BYTEARRAY(objPtr);
sl@0
   522
    src = byteArrayPtr->bytes;
sl@0
   523
    length = byteArrayPtr->used;
sl@0
   524
sl@0
   525
    /*
sl@0
   526
     * How much space will string rep need?
sl@0
   527
     */
sl@0
   528
     
sl@0
   529
    size = length;
sl@0
   530
    for (i = 0; i < length; i++) {
sl@0
   531
	if ((src[i] == 0) || (src[i] > 127)) {
sl@0
   532
	    size++;
sl@0
   533
	}
sl@0
   534
    }
sl@0
   535
sl@0
   536
    dst = (char *) ckalloc((unsigned) (size + 1));
sl@0
   537
    objPtr->bytes = dst;
sl@0
   538
    objPtr->length = size;
sl@0
   539
sl@0
   540
    if (size == length) {
sl@0
   541
	memcpy((VOID *) dst, (VOID *) src, (size_t) size);
sl@0
   542
	dst[size] = '\0';
sl@0
   543
    } else {
sl@0
   544
	for (i = 0; i < length; i++) {
sl@0
   545
	    dst += Tcl_UniCharToUtf(src[i], dst);
sl@0
   546
	}
sl@0
   547
	*dst = '\0';
sl@0
   548
    }
sl@0
   549
}
sl@0
   550

sl@0
   551
/*
sl@0
   552
 *----------------------------------------------------------------------
sl@0
   553
 *
sl@0
   554
 * Tcl_BinaryObjCmd --
sl@0
   555
 *
sl@0
   556
 *	This procedure implements the "binary" Tcl command.
sl@0
   557
 *
sl@0
   558
 * Results:
sl@0
   559
 *	A standard Tcl result.
sl@0
   560
 *
sl@0
   561
 * Side effects:
sl@0
   562
 *	See the user documentation.
sl@0
   563
 *
sl@0
   564
 *----------------------------------------------------------------------
sl@0
   565
 */
sl@0
   566
sl@0
   567
int
sl@0
   568
Tcl_BinaryObjCmd(dummy, interp, objc, objv)
sl@0
   569
    ClientData dummy;		/* Not used. */
sl@0
   570
    Tcl_Interp *interp;		/* Current interpreter. */
sl@0
   571
    int objc;			/* Number of arguments. */
sl@0
   572
    Tcl_Obj *CONST objv[];	/* Argument objects. */
sl@0
   573
{
sl@0
   574
    int arg;			/* Index of next argument to consume. */
sl@0
   575
    int value = 0;		/* Current integer value to be packed.
sl@0
   576
				 * Initialized to avoid compiler warning. */
sl@0
   577
    char cmd;			/* Current format character. */
sl@0
   578
    int count;			/* Count associated with current format
sl@0
   579
				 * character. */
sl@0
   580
    char *format;		/* Pointer to current position in format
sl@0
   581
				 * string. */
sl@0
   582
    Tcl_Obj *resultPtr;		/* Object holding result buffer. */
sl@0
   583
    unsigned char *buffer;	/* Start of result buffer. */
sl@0
   584
    unsigned char *cursor;	/* Current position within result buffer. */
sl@0
   585
    unsigned char *maxPos;	/* Greatest position within result buffer that
sl@0
   586
				 * cursor has visited.*/
sl@0
   587
    char *errorString, *errorValue, *str;
sl@0
   588
    int offset, size, length, index;
sl@0
   589
    static CONST char *options[] = { 
sl@0
   590
	"format",	"scan",		NULL 
sl@0
   591
    };
sl@0
   592
    enum options { 
sl@0
   593
	BINARY_FORMAT,	BINARY_SCAN
sl@0
   594
    };
sl@0
   595
sl@0
   596
    if (objc < 2) {
sl@0
   597
    	Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?");
sl@0
   598
	return TCL_ERROR;
sl@0
   599
    }
sl@0
   600
sl@0
   601
    if (Tcl_GetIndexFromObj(interp, objv[1], options, "option", 0,
sl@0
   602
	    &index) != TCL_OK) {
sl@0
   603
    	return TCL_ERROR;
sl@0
   604
    }
sl@0
   605
sl@0
   606
    switch ((enum options) index) {
sl@0
   607
	case BINARY_FORMAT: {
sl@0
   608
	    if (objc < 3) {
sl@0
   609
		Tcl_WrongNumArgs(interp, 2, objv, "formatString ?arg arg ...?");
sl@0
   610
		return TCL_ERROR;
sl@0
   611
	    }
sl@0
   612
sl@0
   613
	    /*
sl@0
   614
	     * To avoid copying the data, we format the string in two passes.
sl@0
   615
	     * The first pass computes the size of the output buffer.  The
sl@0
   616
	     * second pass places the formatted data into the buffer.
sl@0
   617
	     */
sl@0
   618
sl@0
   619
	    format = Tcl_GetString(objv[2]);
sl@0
   620
	    arg = 3;
sl@0
   621
	    offset = 0;
sl@0
   622
	    length = 0;
sl@0
   623
	    while (*format != '\0') {
sl@0
   624
		str = format;
sl@0
   625
		if (!GetFormatSpec(&format, &cmd, &count)) {
sl@0
   626
		    break;
sl@0
   627
		}
sl@0
   628
		switch (cmd) {
sl@0
   629
		    case 'a':
sl@0
   630
		    case 'A':
sl@0
   631
		    case 'b':
sl@0
   632
		    case 'B':
sl@0
   633
		    case 'h':
sl@0
   634
		    case 'H': {
sl@0
   635
			/*
sl@0
   636
			 * For string-type specifiers, the count corresponds
sl@0
   637
			 * to the number of bytes in a single argument.
sl@0
   638
			 */
sl@0
   639
sl@0
   640
			if (arg >= objc) {
sl@0
   641
			    goto badIndex;
sl@0
   642
			}
sl@0
   643
			if (count == BINARY_ALL) {
sl@0
   644
			    Tcl_GetByteArrayFromObj(objv[arg], &count);
sl@0
   645
			} else if (count == BINARY_NOCOUNT) {
sl@0
   646
			    count = 1;
sl@0
   647
			}
sl@0
   648
			arg++;
sl@0
   649
			if (cmd == 'a' || cmd == 'A') {
sl@0
   650
			    offset += count;
sl@0
   651
			} else if (cmd == 'b' || cmd == 'B') {
sl@0
   652
			    offset += (count + 7) / 8;
sl@0
   653
			} else {
sl@0
   654
			    offset += (count + 1) / 2;
sl@0
   655
			}
sl@0
   656
			break;
sl@0
   657
		    }
sl@0
   658
		    case 'c': {
sl@0
   659
			size = 1;
sl@0
   660
			goto doNumbers;
sl@0
   661
		    }
sl@0
   662
		    case 's':
sl@0
   663
		    case 'S': {
sl@0
   664
			size = 2;
sl@0
   665
			goto doNumbers;
sl@0
   666
		    }
sl@0
   667
		    case 'i':
sl@0
   668
		    case 'I': {
sl@0
   669
			size = 4;
sl@0
   670
			goto doNumbers;
sl@0
   671
		    }
sl@0
   672
		    case 'w':
sl@0
   673
		    case 'W': {
sl@0
   674
			size = 8;
sl@0
   675
			goto doNumbers;
sl@0
   676
		    }
sl@0
   677
		    case 'f': {
sl@0
   678
			size = sizeof(float);
sl@0
   679
			goto doNumbers;
sl@0
   680
		    }
sl@0
   681
		    case 'd': {
sl@0
   682
			size = sizeof(double);
sl@0
   683
			
sl@0
   684
			doNumbers:
sl@0
   685
			if (arg >= objc) {
sl@0
   686
			    goto badIndex;
sl@0
   687
			}
sl@0
   688
sl@0
   689
			/*
sl@0
   690
			 * For number-type specifiers, the count corresponds
sl@0
   691
			 * to the number of elements in the list stored in
sl@0
   692
			 * a single argument.  If no count is specified, then
sl@0
   693
			 * the argument is taken as a single non-list value.
sl@0
   694
			 */
sl@0
   695
sl@0
   696
			if (count == BINARY_NOCOUNT) {
sl@0
   697
			    arg++;
sl@0
   698
			    count = 1;
sl@0
   699
			} else {
sl@0
   700
			    int listc;
sl@0
   701
			    Tcl_Obj **listv;
sl@0
   702
			    if (Tcl_ListObjGetElements(interp, objv[arg++],
sl@0
   703
				    &listc, &listv) != TCL_OK) {
sl@0
   704
				return TCL_ERROR;
sl@0
   705
			    }
sl@0
   706
			    if (count == BINARY_ALL) {
sl@0
   707
				count = listc;
sl@0
   708
			    } else if (count > listc) {
sl@0
   709
			        Tcl_AppendResult(interp, 
sl@0
   710
					"number of elements in list does not match count",
sl@0
   711
					(char *) NULL);
sl@0
   712
				return TCL_ERROR;
sl@0
   713
			    }
sl@0
   714
			}
sl@0
   715
			offset += count*size;
sl@0
   716
			break;
sl@0
   717
		    }
sl@0
   718
		    case 'x': {
sl@0
   719
			if (count == BINARY_ALL) {
sl@0
   720
			    Tcl_AppendResult(interp, 
sl@0
   721
				    "cannot use \"*\" in format string with \"x\"",
sl@0
   722
				    (char *) NULL);
sl@0
   723
			    return TCL_ERROR;
sl@0
   724
			} else if (count == BINARY_NOCOUNT) {
sl@0
   725
			    count = 1;
sl@0
   726
			}
sl@0
   727
			offset += count;
sl@0
   728
			break;
sl@0
   729
		    }
sl@0
   730
		    case 'X': {
sl@0
   731
			if (count == BINARY_NOCOUNT) {
sl@0
   732
			    count = 1;
sl@0
   733
			}
sl@0
   734
			if ((count > offset) || (count == BINARY_ALL)) {
sl@0
   735
			    count = offset;
sl@0
   736
			}
sl@0
   737
			if (offset > length) {
sl@0
   738
			    length = offset;
sl@0
   739
			}
sl@0
   740
			offset -= count;
sl@0
   741
			break;
sl@0
   742
		    }
sl@0
   743
		    case '@': {
sl@0
   744
			if (offset > length) {
sl@0
   745
			    length = offset;
sl@0
   746
			}
sl@0
   747
			if (count == BINARY_ALL) {
sl@0
   748
			    offset = length;
sl@0
   749
			} else if (count == BINARY_NOCOUNT) {
sl@0
   750
			    goto badCount;
sl@0
   751
			} else {
sl@0
   752
			    offset = count;
sl@0
   753
			}
sl@0
   754
			break;
sl@0
   755
		    }
sl@0
   756
		    default: {
sl@0
   757
			errorString = str;
sl@0
   758
			goto badField;
sl@0
   759
		    }
sl@0
   760
		}
sl@0
   761
	    }
sl@0
   762
	    if (offset > length) {
sl@0
   763
		length = offset;
sl@0
   764
	    }
sl@0
   765
	    if (length == 0) {
sl@0
   766
		return TCL_OK;
sl@0
   767
	    }
sl@0
   768
sl@0
   769
	    /*
sl@0
   770
	     * Prepare the result object by preallocating the caclulated
sl@0
   771
	     * number of bytes and filling with nulls.
sl@0
   772
	     */
sl@0
   773
sl@0
   774
	    resultPtr = Tcl_GetObjResult(interp);
sl@0
   775
	    buffer = Tcl_SetByteArrayLength(resultPtr, length);
sl@0
   776
	    memset((VOID *) buffer, 0, (size_t) length);
sl@0
   777
sl@0
   778
	    /*
sl@0
   779
	     * Pack the data into the result object.  Note that we can skip
sl@0
   780
	     * the error checking during this pass, since we have already
sl@0
   781
	     * parsed the string once.
sl@0
   782
	     */
sl@0
   783
sl@0
   784
	    arg = 3;
sl@0
   785
	    format = Tcl_GetString(objv[2]);
sl@0
   786
	    cursor = buffer;
sl@0
   787
	    maxPos = cursor;
sl@0
   788
	    while (*format != 0) {
sl@0
   789
		if (!GetFormatSpec(&format, &cmd, &count)) {
sl@0
   790
		    break;
sl@0
   791
		}
sl@0
   792
		if ((count == 0) && (cmd != '@')) {
sl@0
   793
		    arg++;
sl@0
   794
		    continue;
sl@0
   795
		}
sl@0
   796
		switch (cmd) {
sl@0
   797
		    case 'a':
sl@0
   798
		    case 'A': {
sl@0
   799
			char pad = (char) (cmd == 'a' ? '\0' : ' ');
sl@0
   800
			unsigned char *bytes;
sl@0
   801
sl@0
   802
			bytes = Tcl_GetByteArrayFromObj(objv[arg++], &length);
sl@0
   803
sl@0
   804
			if (count == BINARY_ALL) {
sl@0
   805
			    count = length;
sl@0
   806
			} else if (count == BINARY_NOCOUNT) {
sl@0
   807
			    count = 1;
sl@0
   808
			}
sl@0
   809
			if (length >= count) {
sl@0
   810
			    memcpy((VOID *) cursor, (VOID *) bytes,
sl@0
   811
				    (size_t) count);
sl@0
   812
			} else {
sl@0
   813
			    memcpy((VOID *) cursor, (VOID *) bytes,
sl@0
   814
				    (size_t) length);
sl@0
   815
			    memset((VOID *) (cursor + length), pad,
sl@0
   816
			            (size_t) (count - length));
sl@0
   817
			}
sl@0
   818
			cursor += count;
sl@0
   819
			break;
sl@0
   820
		    }
sl@0
   821
		    case 'b':
sl@0
   822
		    case 'B': {
sl@0
   823
			unsigned char *last;
sl@0
   824
			
sl@0
   825
			str = Tcl_GetStringFromObj(objv[arg++], &length);
sl@0
   826
			if (count == BINARY_ALL) {
sl@0
   827
			    count = length;
sl@0
   828
			} else if (count == BINARY_NOCOUNT) {
sl@0
   829
			    count = 1;
sl@0
   830
			}
sl@0
   831
			last = cursor + ((count + 7) / 8);
sl@0
   832
			if (count > length) {
sl@0
   833
			    count = length;
sl@0
   834
			}
sl@0
   835
			value = 0;
sl@0
   836
			errorString = "binary";
sl@0
   837
			if (cmd == 'B') {
sl@0
   838
			    for (offset = 0; offset < count; offset++) {
sl@0
   839
				value <<= 1;
sl@0
   840
				if (str[offset] == '1') {
sl@0
   841
				    value |= 1;
sl@0
   842
				} else if (str[offset] != '0') {
sl@0
   843
				    errorValue = str;
sl@0
   844
				    goto badValue;
sl@0
   845
				}
sl@0
   846
				if (((offset + 1) % 8) == 0) {
sl@0
   847
				    *cursor++ = (unsigned char) value;
sl@0
   848
				    value = 0;
sl@0
   849
				}
sl@0
   850
			    }
sl@0
   851
			} else {
sl@0
   852
			    for (offset = 0; offset < count; offset++) {
sl@0
   853
				value >>= 1;
sl@0
   854
				if (str[offset] == '1') {
sl@0
   855
				    value |= 128;
sl@0
   856
				} else if (str[offset] != '0') {
sl@0
   857
				    errorValue = str;
sl@0
   858
				    goto badValue;
sl@0
   859
				}
sl@0
   860
				if (!((offset + 1) % 8)) {
sl@0
   861
				    *cursor++ = (unsigned char) value;
sl@0
   862
				    value = 0;
sl@0
   863
				}
sl@0
   864
			    }
sl@0
   865
			}
sl@0
   866
			if ((offset % 8) != 0) {
sl@0
   867
			    if (cmd == 'B') {
sl@0
   868
				value <<= 8 - (offset % 8);
sl@0
   869
			    } else {
sl@0
   870
				value >>= 8 - (offset % 8);
sl@0
   871
			    }
sl@0
   872
			    *cursor++ = (unsigned char) value;
sl@0
   873
			}
sl@0
   874
			while (cursor < last) {
sl@0
   875
			    *cursor++ = '\0';
sl@0
   876
			}
sl@0
   877
			break;
sl@0
   878
		    }
sl@0
   879
		    case 'h':
sl@0
   880
		    case 'H': {
sl@0
   881
			unsigned char *last;
sl@0
   882
			int c;
sl@0
   883
			
sl@0
   884
			str = Tcl_GetStringFromObj(objv[arg++], &length);
sl@0
   885
			if (count == BINARY_ALL) {
sl@0
   886
			    count = length;
sl@0
   887
			} else if (count == BINARY_NOCOUNT) {
sl@0
   888
			    count = 1;
sl@0
   889
			}
sl@0
   890
			last = cursor + ((count + 1) / 2);
sl@0
   891
			if (count > length) {
sl@0
   892
			    count = length;
sl@0
   893
			}
sl@0
   894
			value = 0;
sl@0
   895
			errorString = "hexadecimal";
sl@0
   896
			if (cmd == 'H') {
sl@0
   897
			    for (offset = 0; offset < count; offset++) {
sl@0
   898
				value <<= 4;
sl@0
   899
				if (!isxdigit(UCHAR(str[offset]))) { /* INTL: digit */
sl@0
   900
				    errorValue = str;
sl@0
   901
				    goto badValue;
sl@0
   902
				}
sl@0
   903
				c = str[offset] - '0';
sl@0
   904
				if (c > 9) {
sl@0
   905
				    c += ('0' - 'A') + 10;
sl@0
   906
				}
sl@0
   907
				if (c > 16) {
sl@0
   908
				    c += ('A' - 'a');
sl@0
   909
				}
sl@0
   910
				value |= (c & 0xf);
sl@0
   911
				if (offset % 2) {
sl@0
   912
				    *cursor++ = (char) value;
sl@0
   913
				    value = 0;
sl@0
   914
				}
sl@0
   915
			    }
sl@0
   916
			} else {
sl@0
   917
			    for (offset = 0; offset < count; offset++) {
sl@0
   918
				value >>= 4;
sl@0
   919
sl@0
   920
				if (!isxdigit(UCHAR(str[offset]))) { /* INTL: digit */
sl@0
   921
				    errorValue = str;
sl@0
   922
				    goto badValue;
sl@0
   923
				}
sl@0
   924
				c = str[offset] - '0';
sl@0
   925
				if (c > 9) {
sl@0
   926
				    c += ('0' - 'A') + 10;
sl@0
   927
				}
sl@0
   928
				if (c > 16) {
sl@0
   929
				    c += ('A' - 'a');
sl@0
   930
				}
sl@0
   931
				value |= ((c << 4) & 0xf0);
sl@0
   932
				if (offset % 2) {
sl@0
   933
				    *cursor++ = (unsigned char)(value & 0xff);
sl@0
   934
				    value = 0;
sl@0
   935
				}
sl@0
   936
			    }
sl@0
   937
			}
sl@0
   938
			if (offset % 2) {
sl@0
   939
			    if (cmd == 'H') {
sl@0
   940
				value <<= 4;
sl@0
   941
			    } else {
sl@0
   942
				value >>= 4;
sl@0
   943
			    }
sl@0
   944
			    *cursor++ = (unsigned char) value;
sl@0
   945
			}
sl@0
   946
sl@0
   947
			while (cursor < last) {
sl@0
   948
			    *cursor++ = '\0';
sl@0
   949
			}
sl@0
   950
			break;
sl@0
   951
		    }
sl@0
   952
		    case 'c':
sl@0
   953
		    case 's':
sl@0
   954
		    case 'S':
sl@0
   955
		    case 'i':
sl@0
   956
		    case 'I':
sl@0
   957
		    case 'w':
sl@0
   958
		    case 'W':
sl@0
   959
		    case 'd':
sl@0
   960
		    case 'f': {
sl@0
   961
			int listc, i;
sl@0
   962
			Tcl_Obj **listv;
sl@0
   963
sl@0
   964
			if (count == BINARY_NOCOUNT) {
sl@0
   965
			    /*
sl@0
   966
			     * Note that we are casting away the const-ness of
sl@0
   967
			     * objv, but this is safe since we aren't going to
sl@0
   968
			     * modify the array.
sl@0
   969
			     */
sl@0
   970
sl@0
   971
			    listv = (Tcl_Obj**)(objv + arg);
sl@0
   972
			    listc = 1;
sl@0
   973
			    count = 1;
sl@0
   974
			} else {
sl@0
   975
			    Tcl_ListObjGetElements(interp, objv[arg],
sl@0
   976
				    &listc, &listv);
sl@0
   977
			    if (count == BINARY_ALL) {
sl@0
   978
				count = listc;
sl@0
   979
			    }
sl@0
   980
			}
sl@0
   981
			arg++;
sl@0
   982
			for (i = 0; i < count; i++) {
sl@0
   983
			    if (FormatNumber(interp, cmd, listv[i], &cursor)
sl@0
   984
				    != TCL_OK) {
sl@0
   985
				return TCL_ERROR;
sl@0
   986
			    }
sl@0
   987
			}
sl@0
   988
			break;
sl@0
   989
		    }
sl@0
   990
		    case 'x': {
sl@0
   991
			if (count == BINARY_NOCOUNT) {
sl@0
   992
			    count = 1;
sl@0
   993
			}
sl@0
   994
			memset(cursor, 0, (size_t) count);
sl@0
   995
			cursor += count;
sl@0
   996
			break;
sl@0
   997
		    }
sl@0
   998
		    case 'X': {
sl@0
   999
			if (cursor > maxPos) {
sl@0
  1000
			    maxPos = cursor;
sl@0
  1001
			}
sl@0
  1002
			if (count == BINARY_NOCOUNT) {
sl@0
  1003
			    count = 1;
sl@0
  1004
			}
sl@0
  1005
			if ((count == BINARY_ALL)
sl@0
  1006
				|| (count > (cursor - buffer))) {
sl@0
  1007
			    cursor = buffer;
sl@0
  1008
			} else {
sl@0
  1009
			    cursor -= count;
sl@0
  1010
			}
sl@0
  1011
			break;
sl@0
  1012
		    }
sl@0
  1013
		    case '@': {
sl@0
  1014
			if (cursor > maxPos) {
sl@0
  1015
			    maxPos = cursor;
sl@0
  1016
			}
sl@0
  1017
			if (count == BINARY_ALL) {
sl@0
  1018
			    cursor = maxPos;
sl@0
  1019
			} else {
sl@0
  1020
			    cursor = buffer + count;
sl@0
  1021
			}
sl@0
  1022
			break;
sl@0
  1023
		    }
sl@0
  1024
		}
sl@0
  1025
	    }
sl@0
  1026
	    break;
sl@0
  1027
	}
sl@0
  1028
	case BINARY_SCAN: {
sl@0
  1029
	    int i;
sl@0
  1030
	    Tcl_Obj *valuePtr, *elementPtr;
sl@0
  1031
	    Tcl_HashTable numberCacheHash;
sl@0
  1032
	    Tcl_HashTable *numberCachePtr;
sl@0
  1033
sl@0
  1034
	    if (objc < 4) {
sl@0
  1035
		Tcl_WrongNumArgs(interp, 2, objv,
sl@0
  1036
			"value formatString ?varName varName ...?");
sl@0
  1037
		return TCL_ERROR;
sl@0
  1038
	    }
sl@0
  1039
	    numberCachePtr = &numberCacheHash;
sl@0
  1040
	    Tcl_InitHashTable(numberCachePtr, TCL_ONE_WORD_KEYS);
sl@0
  1041
	    buffer = Tcl_GetByteArrayFromObj(objv[2], &length);
sl@0
  1042
	    format = Tcl_GetString(objv[3]);
sl@0
  1043
	    cursor = buffer;
sl@0
  1044
	    arg = 4;
sl@0
  1045
	    offset = 0;
sl@0
  1046
	    while (*format != '\0') {
sl@0
  1047
		str = format;
sl@0
  1048
		if (!GetFormatSpec(&format, &cmd, &count)) {
sl@0
  1049
		    goto done;
sl@0
  1050
		}
sl@0
  1051
		switch (cmd) {
sl@0
  1052
		    case 'a':
sl@0
  1053
		    case 'A': {
sl@0
  1054
			unsigned char *src;
sl@0
  1055
sl@0
  1056
			if (arg >= objc) {
sl@0
  1057
			    DeleteScanNumberCache(numberCachePtr);
sl@0
  1058
			    goto badIndex;
sl@0
  1059
			}
sl@0
  1060
			if (count == BINARY_ALL) {
sl@0
  1061
			    count = length - offset;
sl@0
  1062
			} else {
sl@0
  1063
			    if (count == BINARY_NOCOUNT) {
sl@0
  1064
				count = 1;
sl@0
  1065
			    }
sl@0
  1066
			    if (count > (length - offset)) {
sl@0
  1067
				goto done;
sl@0
  1068
			    }
sl@0
  1069
			}
sl@0
  1070
sl@0
  1071
			src = buffer + offset;
sl@0
  1072
			size = count;
sl@0
  1073
sl@0
  1074
			/*
sl@0
  1075
			 * Trim trailing nulls and spaces, if necessary.
sl@0
  1076
			 */
sl@0
  1077
sl@0
  1078
			if (cmd == 'A') {
sl@0
  1079
			    while (size > 0) {
sl@0
  1080
				if (src[size-1] != '\0' && src[size-1] != ' ') {
sl@0
  1081
				    break;
sl@0
  1082
				}
sl@0
  1083
				size--;
sl@0
  1084
			    }
sl@0
  1085
			}
sl@0
  1086
			valuePtr = Tcl_NewByteArrayObj(src, size);
sl@0
  1087
			Tcl_IncrRefCount(valuePtr);
sl@0
  1088
			resultPtr = Tcl_ObjSetVar2(interp, objv[arg],
sl@0
  1089
				NULL, valuePtr, TCL_LEAVE_ERR_MSG);
sl@0
  1090
			Tcl_DecrRefCount(valuePtr);
sl@0
  1091
			arg++;
sl@0
  1092
			if (resultPtr == NULL) {
sl@0
  1093
			    DeleteScanNumberCache(numberCachePtr);
sl@0
  1094
			    return TCL_ERROR;
sl@0
  1095
			}
sl@0
  1096
			offset += count;
sl@0
  1097
			break;
sl@0
  1098
		    }
sl@0
  1099
		    case 'b':
sl@0
  1100
		    case 'B': {
sl@0
  1101
			unsigned char *src;
sl@0
  1102
			char *dest;
sl@0
  1103
sl@0
  1104
			if (arg >= objc) {
sl@0
  1105
			    DeleteScanNumberCache(numberCachePtr);
sl@0
  1106
			    goto badIndex;
sl@0
  1107
			}
sl@0
  1108
			if (count == BINARY_ALL) {
sl@0
  1109
			    count = (length - offset) * 8;
sl@0
  1110
			} else {
sl@0
  1111
			    if (count == BINARY_NOCOUNT) {
sl@0
  1112
				count = 1;
sl@0
  1113
			    }
sl@0
  1114
			    if (count > (length - offset) * 8) {
sl@0
  1115
				goto done;
sl@0
  1116
			    }
sl@0
  1117
			}
sl@0
  1118
			src = buffer + offset;
sl@0
  1119
			valuePtr = Tcl_NewObj();
sl@0
  1120
			Tcl_SetObjLength(valuePtr, count);
sl@0
  1121
			dest = Tcl_GetString(valuePtr);
sl@0
  1122
sl@0
  1123
			if (cmd == 'b') {
sl@0
  1124
			    for (i = 0; i < count; i++) {
sl@0
  1125
				if (i % 8) {
sl@0
  1126
				    value >>= 1;
sl@0
  1127
				} else {
sl@0
  1128
				    value = *src++;
sl@0
  1129
				}
sl@0
  1130
				*dest++ = (char) ((value & 1) ? '1' : '0');
sl@0
  1131
			    }
sl@0
  1132
			} else {
sl@0
  1133
			    for (i = 0; i < count; i++) {
sl@0
  1134
				if (i % 8) {
sl@0
  1135
				    value <<= 1;
sl@0
  1136
				} else {
sl@0
  1137
				    value = *src++;
sl@0
  1138
				}
sl@0
  1139
				*dest++ = (char) ((value & 0x80) ? '1' : '0');
sl@0
  1140
			    }
sl@0
  1141
			}
sl@0
  1142
sl@0
  1143
			Tcl_IncrRefCount(valuePtr);			
sl@0
  1144
			resultPtr = Tcl_ObjSetVar2(interp, objv[arg],
sl@0
  1145
				NULL, valuePtr, TCL_LEAVE_ERR_MSG);
sl@0
  1146
			Tcl_DecrRefCount(valuePtr);
sl@0
  1147
			arg++;
sl@0
  1148
			if (resultPtr == NULL) {
sl@0
  1149
			    DeleteScanNumberCache(numberCachePtr);
sl@0
  1150
			    return TCL_ERROR;
sl@0
  1151
			}
sl@0
  1152
			offset += (count + 7 ) / 8;
sl@0
  1153
			break;
sl@0
  1154
		    }
sl@0
  1155
		    case 'h':
sl@0
  1156
		    case 'H': {
sl@0
  1157
			char *dest;
sl@0
  1158
			unsigned char *src;
sl@0
  1159
			int i;
sl@0
  1160
			static char hexdigit[] = "0123456789abcdef";
sl@0
  1161
sl@0
  1162
			if (arg >= objc) {
sl@0
  1163
			    DeleteScanNumberCache(numberCachePtr);
sl@0
  1164
			    goto badIndex;
sl@0
  1165
			}
sl@0
  1166
			if (count == BINARY_ALL) {
sl@0
  1167
			    count = (length - offset)*2;
sl@0
  1168
			} else {
sl@0
  1169
			    if (count == BINARY_NOCOUNT) {
sl@0
  1170
				count = 1;
sl@0
  1171
			    }
sl@0
  1172
			    if (count > (length - offset)*2) {
sl@0
  1173
				goto done;
sl@0
  1174
			    }
sl@0
  1175
			}
sl@0
  1176
			src = buffer + offset;
sl@0
  1177
			valuePtr = Tcl_NewObj();
sl@0
  1178
			Tcl_SetObjLength(valuePtr, count);
sl@0
  1179
			dest = Tcl_GetString(valuePtr);
sl@0
  1180
sl@0
  1181
			if (cmd == 'h') {
sl@0
  1182
			    for (i = 0; i < count; i++) {
sl@0
  1183
				if (i % 2) {
sl@0
  1184
				    value >>= 4;
sl@0
  1185
				} else {
sl@0
  1186
				    value = *src++;
sl@0
  1187
				}
sl@0
  1188
				*dest++ = hexdigit[value & 0xf];
sl@0
  1189
			    }
sl@0
  1190
			} else {
sl@0
  1191
			    for (i = 0; i < count; i++) {
sl@0
  1192
				if (i % 2) {
sl@0
  1193
				    value <<= 4;
sl@0
  1194
				} else {
sl@0
  1195
				    value = *src++;
sl@0
  1196
				}
sl@0
  1197
				*dest++ = hexdigit[(value >> 4) & 0xf];
sl@0
  1198
			    }
sl@0
  1199
			}
sl@0
  1200
			
sl@0
  1201
			Tcl_IncrRefCount(valuePtr);
sl@0
  1202
			resultPtr = Tcl_ObjSetVar2(interp, objv[arg],
sl@0
  1203
				NULL, valuePtr, TCL_LEAVE_ERR_MSG);
sl@0
  1204
			Tcl_DecrRefCount(valuePtr);
sl@0
  1205
			arg++;
sl@0
  1206
			if (resultPtr == NULL) {
sl@0
  1207
			    DeleteScanNumberCache(numberCachePtr);
sl@0
  1208
			    return TCL_ERROR;
sl@0
  1209
			}
sl@0
  1210
			offset += (count + 1) / 2;
sl@0
  1211
			break;
sl@0
  1212
		    }
sl@0
  1213
		    case 'c': {
sl@0
  1214
			size = 1;
sl@0
  1215
			goto scanNumber;
sl@0
  1216
		    }
sl@0
  1217
		    case 's':
sl@0
  1218
		    case 'S': {
sl@0
  1219
			size = 2;
sl@0
  1220
			goto scanNumber;
sl@0
  1221
		    }
sl@0
  1222
		    case 'i':
sl@0
  1223
		    case 'I': {
sl@0
  1224
			size = 4;
sl@0
  1225
			goto scanNumber;
sl@0
  1226
		    }
sl@0
  1227
		    case 'w':
sl@0
  1228
		    case 'W': {
sl@0
  1229
			size = 8;
sl@0
  1230
			goto scanNumber;
sl@0
  1231
		    }
sl@0
  1232
		    case 'f': {
sl@0
  1233
			size = sizeof(float);
sl@0
  1234
			goto scanNumber;
sl@0
  1235
		    }
sl@0
  1236
		    case 'd': {
sl@0
  1237
			unsigned char *src;
sl@0
  1238
sl@0
  1239
			size = sizeof(double);
sl@0
  1240
			/* fall through */
sl@0
  1241
			
sl@0
  1242
			scanNumber:
sl@0
  1243
			if (arg >= objc) {
sl@0
  1244
			    DeleteScanNumberCache(numberCachePtr);
sl@0
  1245
			    goto badIndex;
sl@0
  1246
			}
sl@0
  1247
			if (count == BINARY_NOCOUNT) {
sl@0
  1248
			    if ((length - offset) < size) {
sl@0
  1249
				goto done;
sl@0
  1250
			    }
sl@0
  1251
			    valuePtr = ScanNumber(buffer+offset, cmd,
sl@0
  1252
				    &numberCachePtr);
sl@0
  1253
			    offset += size;
sl@0
  1254
			} else {
sl@0
  1255
			    if (count == BINARY_ALL) {
sl@0
  1256
				count = (length - offset) / size;
sl@0
  1257
			    }
sl@0
  1258
			    if ((length - offset) < (count * size)) {
sl@0
  1259
				goto done;
sl@0
  1260
			    }
sl@0
  1261
			    valuePtr = Tcl_NewObj();
sl@0
  1262
			    src = buffer+offset;
sl@0
  1263
			    for (i = 0; i < count; i++) {
sl@0
  1264
				elementPtr = ScanNumber(src, cmd,
sl@0
  1265
					&numberCachePtr);
sl@0
  1266
				src += size;
sl@0
  1267
				Tcl_ListObjAppendElement(NULL, valuePtr,
sl@0
  1268
					elementPtr);
sl@0
  1269
			    }
sl@0
  1270
			    offset += count*size;
sl@0
  1271
			}
sl@0
  1272
sl@0
  1273
			Tcl_IncrRefCount(valuePtr); 
sl@0
  1274
			resultPtr = Tcl_ObjSetVar2(interp, objv[arg],
sl@0
  1275
				NULL, valuePtr, TCL_LEAVE_ERR_MSG);
sl@0
  1276
			Tcl_DecrRefCount(valuePtr);
sl@0
  1277
			arg++;
sl@0
  1278
			if (resultPtr == NULL) {
sl@0
  1279
			    DeleteScanNumberCache(numberCachePtr);
sl@0
  1280
			    return TCL_ERROR;
sl@0
  1281
			}
sl@0
  1282
			break;
sl@0
  1283
		    }
sl@0
  1284
		    case 'x': {
sl@0
  1285
			if (count == BINARY_NOCOUNT) {
sl@0
  1286
			    count = 1;
sl@0
  1287
			}
sl@0
  1288
			if ((count == BINARY_ALL)
sl@0
  1289
				|| (count > (length - offset))) {
sl@0
  1290
			    offset = length;
sl@0
  1291
			} else {
sl@0
  1292
			    offset += count;
sl@0
  1293
			}
sl@0
  1294
			break;
sl@0
  1295
		    }
sl@0
  1296
		    case 'X': {
sl@0
  1297
			if (count == BINARY_NOCOUNT) {
sl@0
  1298
			    count = 1;
sl@0
  1299
			}
sl@0
  1300
			if ((count == BINARY_ALL) || (count > offset)) {
sl@0
  1301
			    offset = 0;
sl@0
  1302
			} else {
sl@0
  1303
			    offset -= count;
sl@0
  1304
			}
sl@0
  1305
			break;
sl@0
  1306
		    }
sl@0
  1307
		    case '@': {
sl@0
  1308
			if (count == BINARY_NOCOUNT) {
sl@0
  1309
			    DeleteScanNumberCache(numberCachePtr);
sl@0
  1310
			    goto badCount;
sl@0
  1311
			}
sl@0
  1312
			if ((count == BINARY_ALL) || (count > length)) {
sl@0
  1313
			    offset = length;
sl@0
  1314
			} else {
sl@0
  1315
			    offset = count;
sl@0
  1316
			}
sl@0
  1317
			break;
sl@0
  1318
		    }
sl@0
  1319
		    default: {
sl@0
  1320
			DeleteScanNumberCache(numberCachePtr);
sl@0
  1321
			errorString = str;
sl@0
  1322
			goto badField;
sl@0
  1323
		    }
sl@0
  1324
		}
sl@0
  1325
	    }
sl@0
  1326
sl@0
  1327
	    /*
sl@0
  1328
	     * Set the result to the last position of the cursor.
sl@0
  1329
	     */
sl@0
  1330
sl@0
  1331
	    done:
sl@0
  1332
	    Tcl_ResetResult(interp);
sl@0
  1333
	    Tcl_SetLongObj(Tcl_GetObjResult(interp), arg - 4);
sl@0
  1334
	    DeleteScanNumberCache(numberCachePtr);
sl@0
  1335
	    break;
sl@0
  1336
	}
sl@0
  1337
    }
sl@0
  1338
    return TCL_OK;
sl@0
  1339
sl@0
  1340
    badValue:
sl@0
  1341
    Tcl_ResetResult(interp);
sl@0
  1342
    Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "expected ", errorString,
sl@0
  1343
	    " string but got \"", errorValue, "\" instead", NULL);
sl@0
  1344
    return TCL_ERROR;
sl@0
  1345
sl@0
  1346
    badCount:
sl@0
  1347
    errorString = "missing count for \"@\" field specifier";
sl@0
  1348
    goto error;
sl@0
  1349
sl@0
  1350
    badIndex:
sl@0
  1351
    errorString = "not enough arguments for all format specifiers";
sl@0
  1352
    goto error;
sl@0
  1353
sl@0
  1354
    badField:
sl@0
  1355
    {
sl@0
  1356
	Tcl_UniChar ch;
sl@0
  1357
	char buf[TCL_UTF_MAX + 1];
sl@0
  1358
sl@0
  1359
	Tcl_UtfToUniChar(errorString, &ch);
sl@0
  1360
	buf[Tcl_UniCharToUtf(ch, buf)] = '\0';
sl@0
  1361
	Tcl_AppendResult(interp, "bad field specifier \"", buf, "\"", NULL);
sl@0
  1362
	return TCL_ERROR;
sl@0
  1363
    }
sl@0
  1364
sl@0
  1365
    error:
sl@0
  1366
    Tcl_AppendResult(interp, errorString, NULL);
sl@0
  1367
    return TCL_ERROR;
sl@0
  1368
}
sl@0
  1369

sl@0
  1370
/*
sl@0
  1371
 *----------------------------------------------------------------------
sl@0
  1372
 *
sl@0
  1373
 * GetFormatSpec --
sl@0
  1374
 *
sl@0
  1375
 *	This function parses the format strings used in the binary
sl@0
  1376
 *	format and scan commands.
sl@0
  1377
 *
sl@0
  1378
 * Results:
sl@0
  1379
 *	Moves the formatPtr to the start of the next command. Returns
sl@0
  1380
 *	the current command character and count in cmdPtr and countPtr.
sl@0
  1381
 *	The count is set to BINARY_ALL if the count character was '*'
sl@0
  1382
 *	or BINARY_NOCOUNT if no count was specified.  Returns 1 on
sl@0
  1383
 *	success, or 0 if the string did not have a format specifier.
sl@0
  1384
 *
sl@0
  1385
 * Side effects:
sl@0
  1386
 *	None.
sl@0
  1387
 *
sl@0
  1388
 *----------------------------------------------------------------------
sl@0
  1389
 */
sl@0
  1390
sl@0
  1391
static int
sl@0
  1392
GetFormatSpec(formatPtr, cmdPtr, countPtr)
sl@0
  1393
    char **formatPtr;		/* Pointer to format string. */
sl@0
  1394
    char *cmdPtr;		/* Pointer to location of command char. */
sl@0
  1395
    int *countPtr;		/* Pointer to repeat count value. */
sl@0
  1396
{
sl@0
  1397
    /*
sl@0
  1398
     * Skip any leading blanks.
sl@0
  1399
     */
sl@0
  1400
sl@0
  1401
    while (**formatPtr == ' ') {
sl@0
  1402
	(*formatPtr)++;
sl@0
  1403
    }
sl@0
  1404
sl@0
  1405
    /*
sl@0
  1406
     * The string was empty, except for whitespace, so fail.
sl@0
  1407
     */
sl@0
  1408
sl@0
  1409
    if (!(**formatPtr)) {
sl@0
  1410
	return 0;
sl@0
  1411
    }
sl@0
  1412
sl@0
  1413
    /*
sl@0
  1414
     * Extract the command character and any trailing digits or '*'.
sl@0
  1415
     */
sl@0
  1416
sl@0
  1417
    *cmdPtr = **formatPtr;
sl@0
  1418
    (*formatPtr)++;
sl@0
  1419
    if (**formatPtr == '*') {
sl@0
  1420
	(*formatPtr)++;
sl@0
  1421
	(*countPtr) = BINARY_ALL;
sl@0
  1422
    } else if (isdigit(UCHAR(**formatPtr))) { /* INTL: digit */
sl@0
  1423
	(*countPtr) = strtoul(*formatPtr, formatPtr, 10);
sl@0
  1424
    } else {
sl@0
  1425
	(*countPtr) = BINARY_NOCOUNT;
sl@0
  1426
    }
sl@0
  1427
    return 1;
sl@0
  1428
}
sl@0
  1429

sl@0
  1430
/*
sl@0
  1431
 *----------------------------------------------------------------------
sl@0
  1432
 *
sl@0
  1433
 * FormatNumber --
sl@0
  1434
 *
sl@0
  1435
 *	This routine is called by Tcl_BinaryObjCmd to format a number
sl@0
  1436
 *	into a location pointed at by cursor.
sl@0
  1437
 *
sl@0
  1438
 * Results:
sl@0
  1439
 *	 A standard Tcl result.
sl@0
  1440
 *
sl@0
  1441
 * Side effects:
sl@0
  1442
 *	Moves the cursor to the next location to be written into.
sl@0
  1443
 *
sl@0
  1444
 *----------------------------------------------------------------------
sl@0
  1445
 */
sl@0
  1446
sl@0
  1447
static int
sl@0
  1448
FormatNumber(interp, type, src, cursorPtr)
sl@0
  1449
    Tcl_Interp *interp;		/* Current interpreter, used to report
sl@0
  1450
				 * errors. */
sl@0
  1451
    int type;			/* Type of number to format. */
sl@0
  1452
    Tcl_Obj *src;		/* Number to format. */
sl@0
  1453
    unsigned char **cursorPtr;	/* Pointer to index into destination buffer. */
sl@0
  1454
{
sl@0
  1455
    long value;
sl@0
  1456
    double dvalue;
sl@0
  1457
    Tcl_WideInt wvalue;
sl@0
  1458
sl@0
  1459
    switch (type) {
sl@0
  1460
    case 'd':
sl@0
  1461
    case 'f':
sl@0
  1462
	/*
sl@0
  1463
	 * For floating point types, we need to copy the data using
sl@0
  1464
	 * memcpy to avoid alignment issues.
sl@0
  1465
	 */
sl@0
  1466
sl@0
  1467
	if (Tcl_GetDoubleFromObj(interp, src, &dvalue) != TCL_OK) {
sl@0
  1468
	    return TCL_ERROR;
sl@0
  1469
	}
sl@0
  1470
	if (type == 'd') {
sl@0
  1471
	    /*
sl@0
  1472
	     * Can't just memcpy() here. [Bug 1116542]
sl@0
  1473
	     */
sl@0
  1474
sl@0
  1475
	    CopyNumber(&dvalue, *cursorPtr, sizeof(double));
sl@0
  1476
	    *cursorPtr += sizeof(double);
sl@0
  1477
	} else {
sl@0
  1478
	    float fvalue;
sl@0
  1479
sl@0
  1480
	    /*
sl@0
  1481
	     * Because some compilers will generate floating point exceptions
sl@0
  1482
	     * on an overflow cast (e.g. Borland), we restrict the values
sl@0
  1483
	     * to the valid range for float.
sl@0
  1484
	     */
sl@0
  1485
sl@0
  1486
	    if (fabs(dvalue) > (double)FLT_MAX) {
sl@0
  1487
		fvalue = (dvalue >= 0.0) ? FLT_MAX : -FLT_MAX;
sl@0
  1488
	    } else {
sl@0
  1489
		fvalue = (float) dvalue;
sl@0
  1490
	    }
sl@0
  1491
	    memcpy((VOID *) *cursorPtr, (VOID *) &fvalue, sizeof(float));
sl@0
  1492
	    *cursorPtr += sizeof(float);
sl@0
  1493
	}
sl@0
  1494
	return TCL_OK;
sl@0
  1495
sl@0
  1496
	/*
sl@0
  1497
	 * Next cases separate from other integer cases because we
sl@0
  1498
	 * need a different API to get a wide.
sl@0
  1499
	 */
sl@0
  1500
    case 'w':
sl@0
  1501
    case 'W':
sl@0
  1502
	if (Tcl_GetWideIntFromObj(interp, src, &wvalue) != TCL_OK) {
sl@0
  1503
	    return TCL_ERROR;
sl@0
  1504
	}
sl@0
  1505
	if (type == 'w') {
sl@0
  1506
	    *(*cursorPtr)++ = (unsigned char) wvalue;
sl@0
  1507
	    *(*cursorPtr)++ = (unsigned char) (wvalue >> 8);
sl@0
  1508
	    *(*cursorPtr)++ = (unsigned char) (wvalue >> 16);
sl@0
  1509
	    *(*cursorPtr)++ = (unsigned char) (wvalue >> 24);
sl@0
  1510
	    *(*cursorPtr)++ = (unsigned char) (wvalue >> 32);
sl@0
  1511
	    *(*cursorPtr)++ = (unsigned char) (wvalue >> 40);
sl@0
  1512
	    *(*cursorPtr)++ = (unsigned char) (wvalue >> 48);
sl@0
  1513
	    *(*cursorPtr)++ = (unsigned char) (wvalue >> 56);
sl@0
  1514
	} else {
sl@0
  1515
	    *(*cursorPtr)++ = (unsigned char) (wvalue >> 56);
sl@0
  1516
	    *(*cursorPtr)++ = (unsigned char) (wvalue >> 48);
sl@0
  1517
	    *(*cursorPtr)++ = (unsigned char) (wvalue >> 40);
sl@0
  1518
	    *(*cursorPtr)++ = (unsigned char) (wvalue >> 32);
sl@0
  1519
	    *(*cursorPtr)++ = (unsigned char) (wvalue >> 24);
sl@0
  1520
	    *(*cursorPtr)++ = (unsigned char) (wvalue >> 16);
sl@0
  1521
	    *(*cursorPtr)++ = (unsigned char) (wvalue >> 8);
sl@0
  1522
	    *(*cursorPtr)++ = (unsigned char) wvalue;
sl@0
  1523
	}
sl@0
  1524
	return TCL_OK;
sl@0
  1525
    default:
sl@0
  1526
	if (Tcl_GetLongFromObj(interp, src, &value) != TCL_OK) {
sl@0
  1527
	    return TCL_ERROR;
sl@0
  1528
	}
sl@0
  1529
	if (type == 'c') {
sl@0
  1530
	    *(*cursorPtr)++ = (unsigned char) value;
sl@0
  1531
	} else if (type == 's') {
sl@0
  1532
	    *(*cursorPtr)++ = (unsigned char) value;
sl@0
  1533
	    *(*cursorPtr)++ = (unsigned char) (value >> 8);
sl@0
  1534
	} else if (type == 'S') {
sl@0
  1535
	    *(*cursorPtr)++ = (unsigned char) (value >> 8);
sl@0
  1536
	    *(*cursorPtr)++ = (unsigned char) value;
sl@0
  1537
	} else if (type == 'i') {
sl@0
  1538
	    *(*cursorPtr)++ = (unsigned char) value;
sl@0
  1539
	    *(*cursorPtr)++ = (unsigned char) (value >> 8);
sl@0
  1540
	    *(*cursorPtr)++ = (unsigned char) (value >> 16);
sl@0
  1541
	    *(*cursorPtr)++ = (unsigned char) (value >> 24);
sl@0
  1542
	} else if (type == 'I') {
sl@0
  1543
	    *(*cursorPtr)++ = (unsigned char) (value >> 24);
sl@0
  1544
	    *(*cursorPtr)++ = (unsigned char) (value >> 16);
sl@0
  1545
	    *(*cursorPtr)++ = (unsigned char) (value >> 8);
sl@0
  1546
	    *(*cursorPtr)++ = (unsigned char) value;
sl@0
  1547
	}
sl@0
  1548
	return TCL_OK;
sl@0
  1549
    }
sl@0
  1550
}
sl@0
  1551

sl@0
  1552
/* Ugly workaround for old and broken compiler! */
sl@0
  1553
static void
sl@0
  1554
CopyNumber(from, to, length)
sl@0
  1555
    CONST VOID *from;
sl@0
  1556
    VOID *to;
sl@0
  1557
    unsigned int length;
sl@0
  1558
{
sl@0
  1559
    memcpy(to, from, length);
sl@0
  1560
}
sl@0
  1561

sl@0
  1562
/*
sl@0
  1563
 *----------------------------------------------------------------------
sl@0
  1564
 *
sl@0
  1565
 * ScanNumber --
sl@0
  1566
 *
sl@0
  1567
 *	This routine is called by Tcl_BinaryObjCmd to scan a number
sl@0
  1568
 *	out of a buffer.
sl@0
  1569
 *
sl@0
  1570
 * Results:
sl@0
  1571
 *	Returns a newly created object containing the scanned number.
sl@0
  1572
 *	This object has a ref count of zero.
sl@0
  1573
 *
sl@0
  1574
 * Side effects:
sl@0
  1575
 *	Might reuse an object in the number cache, place a new object
sl@0
  1576
 *	in the cache, or delete the cache and set the reference to
sl@0
  1577
 *	it (itself passed in by reference) to NULL.
sl@0
  1578
 *
sl@0
  1579
 *----------------------------------------------------------------------
sl@0
  1580
 */
sl@0
  1581
sl@0
  1582
static Tcl_Obj *
sl@0
  1583
ScanNumber(buffer, type, numberCachePtrPtr)
sl@0
  1584
    unsigned char *buffer;	/* Buffer to scan number from. */
sl@0
  1585
    int type;			/* Format character from "binary scan" */
sl@0
  1586
    Tcl_HashTable **numberCachePtrPtr;
sl@0
  1587
				/* Place to look for cache of scanned
sl@0
  1588
				 * value objects, or NULL if too many
sl@0
  1589
				 * different numbers have been scanned. */
sl@0
  1590
{
sl@0
  1591
    long value;
sl@0
  1592
    Tcl_WideUInt uwvalue;
sl@0
  1593
sl@0
  1594
    /*
sl@0
  1595
     * We cannot rely on the compiler to properly sign extend integer values
sl@0
  1596
     * when we cast from smaller values to larger values because we don't know
sl@0
  1597
     * the exact size of the integer types.  So, we have to handle sign
sl@0
  1598
     * extension explicitly by checking the high bit and padding with 1's as
sl@0
  1599
     * needed.
sl@0
  1600
     */
sl@0
  1601
sl@0
  1602
    switch (type) {
sl@0
  1603
	case 'c':
sl@0
  1604
	    /*
sl@0
  1605
	     * Characters need special handling.  We want to produce a
sl@0
  1606
	     * signed result, but on some platforms (such as AIX) chars
sl@0
  1607
	     * are unsigned.  To deal with this, check for a value that
sl@0
  1608
	     * should be negative but isn't.
sl@0
  1609
	     */
sl@0
  1610
sl@0
  1611
	    value = buffer[0];
sl@0
  1612
	    if (value & 0x80) {
sl@0
  1613
		value |= -0x100;
sl@0
  1614
	    }
sl@0
  1615
	    goto returnNumericObject;
sl@0
  1616
sl@0
  1617
	case 's':
sl@0
  1618
	    value = (long) (buffer[0] + (buffer[1] << 8));
sl@0
  1619
	    goto shortValue;
sl@0
  1620
	case 'S':
sl@0
  1621
	    value = (long) (buffer[1] + (buffer[0] << 8));
sl@0
  1622
	    shortValue:
sl@0
  1623
	    if (value & 0x8000) {
sl@0
  1624
		value |= -0x10000;
sl@0
  1625
	    }
sl@0
  1626
	    goto returnNumericObject;
sl@0
  1627
sl@0
  1628
	case 'i':
sl@0
  1629
	    value = (long) (buffer[0] 
sl@0
  1630
		    + (buffer[1] << 8)
sl@0
  1631
		    + (buffer[2] << 16)
sl@0
  1632
		    + (buffer[3] << 24));
sl@0
  1633
	    goto intValue;
sl@0
  1634
	case 'I':
sl@0
  1635
	    value = (long) (buffer[3]
sl@0
  1636
		    + (buffer[2] << 8)
sl@0
  1637
		    + (buffer[1] << 16)
sl@0
  1638
		    + (buffer[0] << 24));
sl@0
  1639
	    intValue:
sl@0
  1640
	    /*
sl@0
  1641
	     * Check to see if the value was sign extended properly on
sl@0
  1642
	     * systems where an int is more than 32-bits.
sl@0
  1643
	     */
sl@0
  1644
sl@0
  1645
	    if ((value & (((unsigned int)1)<<31)) && (value > 0)) {
sl@0
  1646
		value -= (((unsigned int)1)<<31);
sl@0
  1647
		value -= (((unsigned int)1)<<31);
sl@0
  1648
	    }
sl@0
  1649
	    returnNumericObject:
sl@0
  1650
	    if (*numberCachePtrPtr == NULL) {
sl@0
  1651
		return Tcl_NewLongObj(value);
sl@0
  1652
	    } else {
sl@0
  1653
		register Tcl_HashTable *tablePtr = *numberCachePtrPtr;
sl@0
  1654
		register Tcl_HashEntry *hPtr;
sl@0
  1655
		int isNew;
sl@0
  1656
sl@0
  1657
		hPtr = Tcl_CreateHashEntry(tablePtr, (char *)value, &isNew);
sl@0
  1658
		if (!isNew) {
sl@0
  1659
		    return (Tcl_Obj *) Tcl_GetHashValue(hPtr);
sl@0
  1660
		}
sl@0
  1661
		if (tablePtr->numEntries > BINARY_SCAN_MAX_CACHE) {
sl@0
  1662
		    /*
sl@0
  1663
		     * We've overflowed the cache!  Someone's parsing
sl@0
  1664
		     * a LOT of varied binary data in a single call!
sl@0
  1665
		     * Bail out by switching back to the old behaviour
sl@0
  1666
		     * for the rest of the scan.
sl@0
  1667
		     *
sl@0
  1668
		     * Note that anyone just using the 'c' conversion
sl@0
  1669
		     * (for bytes) cannot trigger this.
sl@0
  1670
		     */
sl@0
  1671
		    DeleteScanNumberCache(tablePtr);
sl@0
  1672
		    *numberCachePtrPtr = NULL;
sl@0
  1673
		    return Tcl_NewLongObj(value);
sl@0
  1674
		} else {
sl@0
  1675
		    register Tcl_Obj *objPtr = Tcl_NewLongObj(value);
sl@0
  1676
sl@0
  1677
		    Tcl_IncrRefCount(objPtr);
sl@0
  1678
		    Tcl_SetHashValue(hPtr, (ClientData) objPtr);
sl@0
  1679
		    return objPtr;
sl@0
  1680
		}
sl@0
  1681
	    }
sl@0
  1682
sl@0
  1683
	    /*
sl@0
  1684
	     * Do not cache wide values; they are already too large to
sl@0
  1685
	     * use as keys.
sl@0
  1686
	     */
sl@0
  1687
	case 'w':
sl@0
  1688
	    uwvalue =  ((Tcl_WideUInt) buffer[0])
sl@0
  1689
		    | (((Tcl_WideUInt) buffer[1]) << 8)
sl@0
  1690
		    | (((Tcl_WideUInt) buffer[2]) << 16)
sl@0
  1691
		    | (((Tcl_WideUInt) buffer[3]) << 24)
sl@0
  1692
		    | (((Tcl_WideUInt) buffer[4]) << 32)
sl@0
  1693
		    | (((Tcl_WideUInt) buffer[5]) << 40)
sl@0
  1694
		    | (((Tcl_WideUInt) buffer[6]) << 48)
sl@0
  1695
		    | (((Tcl_WideUInt) buffer[7]) << 56);
sl@0
  1696
	    return Tcl_NewWideIntObj((Tcl_WideInt) uwvalue);
sl@0
  1697
	case 'W':
sl@0
  1698
	    uwvalue =  ((Tcl_WideUInt) buffer[7])
sl@0
  1699
		    | (((Tcl_WideUInt) buffer[6]) << 8)
sl@0
  1700
		    | (((Tcl_WideUInt) buffer[5]) << 16)
sl@0
  1701
		    | (((Tcl_WideUInt) buffer[4]) << 24)
sl@0
  1702
		    | (((Tcl_WideUInt) buffer[3]) << 32)
sl@0
  1703
		    | (((Tcl_WideUInt) buffer[2]) << 40)
sl@0
  1704
		    | (((Tcl_WideUInt) buffer[1]) << 48)
sl@0
  1705
		    | (((Tcl_WideUInt) buffer[0]) << 56);
sl@0
  1706
	    return Tcl_NewWideIntObj((Tcl_WideInt) uwvalue);
sl@0
  1707
sl@0
  1708
	    /*
sl@0
  1709
	     * Do not cache double values; they are already too large
sl@0
  1710
	     * to use as keys and the values stored are utterly
sl@0
  1711
	     * incompatible too.
sl@0
  1712
	     */
sl@0
  1713
	case 'f': {
sl@0
  1714
	    float fvalue;
sl@0
  1715
	    memcpy((VOID *) &fvalue, (VOID *) buffer, sizeof(float));
sl@0
  1716
	    return Tcl_NewDoubleObj(fvalue);
sl@0
  1717
	}
sl@0
  1718
	case 'd': {
sl@0
  1719
	    double dvalue;
sl@0
  1720
	    memcpy((VOID *) &dvalue, (VOID *) buffer, sizeof(double));
sl@0
  1721
	    return Tcl_NewDoubleObj(dvalue);
sl@0
  1722
	}
sl@0
  1723
    }
sl@0
  1724
    return NULL;
sl@0
  1725
}
sl@0
  1726

sl@0
  1727
/*
sl@0
  1728
 *----------------------------------------------------------------------
sl@0
  1729
 *
sl@0
  1730
 * DeleteScanNumberCache --
sl@0
  1731
 * 
sl@0
  1732
 *	Deletes the hash table acting as a scan number cache.
sl@0
  1733
 *
sl@0
  1734
 * Results:
sl@0
  1735
 *	None
sl@0
  1736
 *
sl@0
  1737
 * Side effects:
sl@0
  1738
 *	Decrements the reference counts of the objects in the cache.
sl@0
  1739
 *
sl@0
  1740
 *----------------------------------------------------------------------
sl@0
  1741
 */
sl@0
  1742
sl@0
  1743
static void
sl@0
  1744
DeleteScanNumberCache(numberCachePtr)
sl@0
  1745
    Tcl_HashTable *numberCachePtr;	/* Pointer to the hash table, or
sl@0
  1746
					 * NULL (when the cache has already
sl@0
  1747
					 * been deleted due to overflow.) */
sl@0
  1748
{
sl@0
  1749
    Tcl_HashEntry *hEntry;
sl@0
  1750
    Tcl_HashSearch search;
sl@0
  1751
sl@0
  1752
    if (numberCachePtr == NULL) {
sl@0
  1753
	return;
sl@0
  1754
    }
sl@0
  1755
sl@0
  1756
    hEntry = Tcl_FirstHashEntry(numberCachePtr, &search);
sl@0
  1757
    while (hEntry != NULL) {
sl@0
  1758
	register Tcl_Obj *value = (Tcl_Obj *) Tcl_GetHashValue(hEntry);
sl@0
  1759
sl@0
  1760
	if (value != NULL) {
sl@0
  1761
	    Tcl_DecrRefCount(value);
sl@0
  1762
	}
sl@0
  1763
	hEntry = Tcl_NextHashEntry(&search);
sl@0
  1764
    }
sl@0
  1765
    Tcl_DeleteHashTable(numberCachePtr);
sl@0
  1766
}