diff options
Diffstat (limited to 'generic/tclBinary.c')
| -rw-r--r-- | generic/tclBinary.c | 2864 | 
1 files changed, 1996 insertions, 868 deletions
| diff --git a/generic/tclBinary.c b/generic/tclBinary.c index c20f4df..a3e5071 100644 --- a/generic/tclBinary.c +++ b/generic/tclBinary.c @@ -9,11 +9,10 @@   *   * See the file "license.terms" for information on usage and redistribution of   * this file, and for a DISCLAIMER OF ALL WARRANTIES. - * - * RCS: @(#) $Id: tclBinary.c,v 1.27 2005/11/04 22:38:38 msofer Exp $   */  #include "tclInt.h" +#include "tommath.h"  #include <math.h> @@ -26,6 +25,13 @@  #define BINARY_NOCOUNT -2	/* No count was specified in format. */  /* + * The following flags may be ORed together and returned by GetFormatSpec + */ + +#define BINARY_SIGNED 0		/* Field to be read as signed data */ +#define BINARY_UNSIGNED 1	/* Field to be read as unsigned data */ + +/*   * The following defines the maximum number of different (integer) numbers   * placed in the object cache by 'binary scan' before it bails out and   * switches back to Plan A (creating a new object for each value.) @@ -53,48 +59,204 @@ static void		DupByteArrayInternalRep(Tcl_Obj *srcPtr,  static int		FormatNumber(Tcl_Interp *interp, int type,  			    Tcl_Obj *src, unsigned char **cursorPtr);  static void		FreeByteArrayInternalRep(Tcl_Obj *objPtr); -static int		GetFormatSpec(char **formatPtr, char *cmdPtr, -			    int *countPtr); +static int		GetFormatSpec(const char **formatPtr, char *cmdPtr, +			    int *countPtr, int *flagsPtr);  static Tcl_Obj *	ScanNumber(unsigned char *buffer, int type, -			    Tcl_HashTable **numberCachePtr); +			    int flags, Tcl_HashTable **numberCachePtr);  static int		SetByteArrayFromAny(Tcl_Interp *interp,  			    Tcl_Obj *objPtr);  static void		UpdateStringOfByteArray(Tcl_Obj *listPtr);  static void		DeleteScanNumberCache(Tcl_HashTable *numberCachePtr);  static int		NeedReversing(int format); -static void		CopyNumber(CONST void *from, void *to, -			    unsigned int length, int type); +static void		CopyNumber(const void *from, void *to, +			    unsigned length, int type); +/* Binary ensemble commands */ +static int		BinaryFormatCmd(ClientData clientData, +			    Tcl_Interp *interp, +			    int objc, Tcl_Obj *const objv[]); +static int		BinaryScanCmd(ClientData clientData, +			    Tcl_Interp *interp, +			    int objc, Tcl_Obj *const objv[]); +/* Binary encoding sub-ensemble commands */ +static int		BinaryEncodeHex(ClientData clientData, +			    Tcl_Interp *interp, +			    int objc, Tcl_Obj *const objv[]); +static int		BinaryDecodeHex(ClientData clientData, +			    Tcl_Interp *interp, +			    int objc, Tcl_Obj *const objv[]); +static int		BinaryEncode64(ClientData clientData, +			    Tcl_Interp *interp, +			    int objc, Tcl_Obj *const objv[]); +static int		BinaryDecode64(ClientData clientData, +			    Tcl_Interp *interp, +			    int objc, Tcl_Obj *const objv[]); +static int		BinaryEncodeUu(ClientData clientData, +			    Tcl_Interp *interp, int objc, +			    Tcl_Obj *const objv[]); +static int		BinaryDecodeUu(ClientData clientData, +			    Tcl_Interp *interp, +			    int objc, Tcl_Obj *const objv[]); + +/* + * The following tables are used by the binary encoders + */ + +static const char HexDigits[16] = { +    '0', '1', '2', '3', '4', '5', '6', '7', +    '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' +}; + +static const char UueDigits[65] = { +    '`', '!', '"', '#', '$', '%', '&', '\'', +    '(', ')', '*', '+', ',', '-', '.', '/', +    '0', '1', '2', '3', '4', '5', '6', '7', +    '8', '9', ':', ';', '<', '=', '>', '?', +    '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', +    'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', +    'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', +    'X', 'Y', 'Z', '[', '\\',']', '^', '_', +    '`' +}; + +static const char B64Digits[65] = { +    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', +    'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', +    'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', +    'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', +    'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', +    'o', 'p', 'q', 'r', 's', 't', 'u', 'v', +    'w', 'x', 'y', 'z', '0', '1', '2', '3', +    '4', '5', '6', '7', '8', '9', '+', '/', +    '=' +};  /* - * The following object type represents an array of bytes. An array of bytes - * is not equivalent to an internationalized string. Conceptually, a string is - * an array of 16-bit quantities organized as a sequence of properly formed - * UTF-8 characters, while a ByteArray is an array of 8-bit quantities. - * Accessor functions are provided to convert a ByteArray to a String or a - * String to a ByteArray. Two or more consecutive bytes in an array of bytes - * may look like a single UTF-8 character if the array is casually treated as - * a string. But obtaining the String from a ByteArray is guaranteed to - * produced properly formed UTF-8 sequences so that there is a one-to-one map - * between bytes and characters. - * - * Converting a ByteArray to a String proceeds by casting each byte in the - * array to a 16-bit quantity, treating that number as a Unicode character, - * and storing the UTF-8 version of that Unicode character in the String. For - * ByteArrays consisting entirely of values 1..127, the corresponding String - * representation is the same as the ByteArray representation. - * - * Converting a String to a ByteArray proceeds by getting the Unicode - * representation of each character in the String, casting it to a byte by - * truncating the upper 8 bits, and then storing the byte in the ByteArray. - * Converting from ByteArray to String and back to ByteArray is not lossy, but - * converting an arbitrary String to a ByteArray may be. + * How to construct the ensembles.   */ -Tcl_ObjType tclByteArrayType = { +static const EnsembleImplMap binaryMap[] = { +    { "format", BinaryFormatCmd, TclCompileBasicMin1ArgCmd, NULL, NULL, 0 }, +    { "scan",   BinaryScanCmd, TclCompileBasicMin2ArgCmd, NULL, NULL, 0 }, +    { "encode", NULL, NULL, NULL, NULL, 0 }, +    { "decode", NULL, NULL, NULL, NULL, 0 }, +    { NULL, NULL, NULL, NULL, NULL, 0 } +}; +static const EnsembleImplMap encodeMap[] = { +    { "hex",      BinaryEncodeHex, TclCompileBasic1ArgCmd, NULL, NULL, 0 }, +    { "uuencode", BinaryEncodeUu,  NULL, NULL, NULL, 0 }, +    { "base64",   BinaryEncode64,  NULL, NULL, NULL, 0 }, +    { NULL, NULL, NULL, NULL, NULL, 0 } +}; +static const EnsembleImplMap decodeMap[] = { +    { "hex",      BinaryDecodeHex, TclCompileBasic1Or2ArgCmd, NULL, NULL, 0 }, +    { "uuencode", BinaryDecodeUu,  TclCompileBasic1Or2ArgCmd, NULL, NULL, 0 }, +    { "base64",   BinaryDecode64,  TclCompileBasic1Or2ArgCmd, NULL, NULL, 0 }, +    { NULL, NULL, NULL, NULL, NULL, 0 } +}; + +/* + * The following object types represent an array of bytes. The intent is + * to allow arbitrary binary data to pass through Tcl as a Tcl value + * without loss or damage. Such values are useful for things like + * encoded strings or Tk images to name just two. + * + * It's strange to have two Tcl_ObjTypes in place for this task when + * one would do, so a bit of detail and history how we got to this point + * and where we might go from here. + * + * A bytearray is an ordered sequence of bytes. Each byte is an integer + * value in the range [0-255].  To be a Tcl value type, we need a way to + * encode each value in the value set as a Tcl string.  The simplest + * encoding is to represent each byte value as the same codepoint value. + * A bytearray of N bytes is encoded into a Tcl string of N characters + * where the codepoint of each character is the value of corresponding byte. + * This approach creates a one-to-one map between all bytearray values + * and a subset of Tcl string values. + * + * When converting a Tcl string value to the bytearray internal rep, the + * question arises what to do with strings outside that subset?  That is, + * those Tcl strings containing at least one codepoint greater than 255? + * The obviously correct answer is to raise an error!  That string value + * does not represent any valid bytearray value. Full Stop.  The + * setFromAnyProc signature has a completion code return value for just + * this reason, to reject invalid inputs. + * + * Unfortunately this was not the path taken by the authors of the + * original tclByteArrayType.  They chose to accept all Tcl string values + * as acceptable string encodings of the bytearray values that result + * from masking away the high bits of any codepoint value at all. This + * meant that every bytearray value had multiple accepted string + * representations. + * + * The implications of this choice are truly ugly.  When a Tcl value has + * a string representation, we are required to accept that as the true + * value.  Bytearray values that possess a string representation cannot + * be processed as bytearrays because we cannot know which true value + * that bytearray represents.  The consequence is that we drag around + * an internal rep that we cannot make any use of.  This painful price + * is extracted at any point after a string rep happens to be generated + * for the value.  This happens even when the troublesome codepoints + * outside the byte range never show up.  This happens rather routinely + * in normal Tcl operations unless we burden the script writer with the + * cognitive burden of avoiding it.  The price is also paid by callers + * of the C interface.  The routine + * + *	unsigned char *Tcl_GetByteArrayFromObj(objPtr, lenPtr) + * + * has a guarantee to always return a non-NULL value, but that value + * points to a byte sequence that cannot be used by the caller to + * process the Tcl value absent some sideband testing that objPtr + * is "pure".  Tcl offers no public interface to perform this test, + * so callers either break encapsulation or are unavoidably buggy.  Tcl + * has defined a public interface that cannot be used correctly. The + * Tcl source code itself suffers the same problem, and has been buggy, + * but progressively less so as more and more portions of the code have + * been retrofitted with the required "purity testing".  The set of values + * able to pass the purity test can be increased via the introduction of + * a "canonical" flag marker, but the only way the broken interface itself + * can be discarded is to start over and define the Tcl_ObjType properly. + * Bytearrays should simply be usable as bytearrays without a kabuki + * dance of testing. + * + * The Tcl_ObjType "properByteArrayType" is (nearly) a correct + * implementation of bytearrays.  Any Tcl value with the type + * properByteArrayType can have its bytearray value fetched and + * used with confidence that acting on that value is equivalent to + * acting on the true Tcl string value.  This still implies a side + * testing burden -- past mistakes will not let us avoid that + * immediately, but it is at least a conventional test of type, and + * can be implemented entirely by examining the objPtr fields, with + * no need to query the intrep, as a canonical flag would require. + * + * Until Tcl_GetByteArrayFromObj() and Tcl_SetByteArrayLength() can + * be revised to admit the possibility of returning NULL when the true + * value is not a valid bytearray, we need a mechanism to retain + * compatibility with the deployed callers of the broken interface. + * That's what the retained "tclByteArrayType" provides.  In those + * unusual circumstances where we convert an invalid bytearray value + * to a bytearray type, it is to this legacy type.  Essentially any + * time this legacy type gets used, it's a signal of a bug being ignored. + * A TIP should be drafted to remove this connection to the broken past + * so that Tcl 9 will no longer have any trace of it.  Prescribing a + * migration path will be the key element of that work.  The internal + * changes now in place are the limit of what can be done short of + * interface repair.  They provide a great expansion of the histories + * over which bytearray values can be useful in the meanwhile. + */ + +static const Tcl_ObjType properByteArrayType = {      "bytearray",      FreeByteArrayInternalRep,      DupByteArrayInternalRep,      UpdateStringOfByteArray, +    NULL +}; + +const Tcl_ObjType tclByteArrayType = { +    "bytearray", +    FreeByteArrayInternalRep, +    DupByteArrayInternalRep, +    NULL,      SetByteArrayFromAny  }; @@ -110,18 +272,24 @@ typedef struct ByteArray {  				 * array. */      int allocated;		/* The amount of space actually allocated  				 * minus 1 byte. */ -    unsigned char bytes[4];	/* The array of bytes. The actual size of this +    unsigned char bytes[1];	/* The array of bytes. The actual size of this  				 * field depends on the 'allocated' field  				 * above. */  } ByteArray; -#define BYTEARRAY_SIZE(len)	\ -		((unsigned) (sizeof(ByteArray) - 4 + (len))) +#define BYTEARRAY_SIZE(len) \ +		((unsigned) (TclOffset(ByteArray, bytes) + (len)))  #define GET_BYTEARRAY(objPtr) \ -		((ByteArray *) (objPtr)->internalRep.otherValuePtr) +		((ByteArray *) (objPtr)->internalRep.twoPtrValue.ptr1)  #define SET_BYTEARRAY(objPtr, baPtr) \ -		(objPtr)->internalRep.otherValuePtr = (VOID *) (baPtr) +		(objPtr)->internalRep.twoPtrValue.ptr1 = (void *) (baPtr) +int +TclIsPureByteArray( +    Tcl_Obj * objPtr) +{ +    return (objPtr->typePtr == &properByteArrayType); +}  /*   *---------------------------------------------------------------------- @@ -141,35 +309,25 @@ typedef struct ByteArray {   *----------------------------------------------------------------------   */ -#ifdef TCL_MEM_DEBUG  #undef Tcl_NewByteArrayObj  Tcl_Obj *  Tcl_NewByteArrayObj( -    CONST unsigned char *bytes,	/* The array of bytes used to initialize the +    const unsigned char *bytes,	/* The array of bytes used to initialize the  				 * new object. */      int length)			/* Length of the array of bytes, which must be  				 * >= 0. */  { +#ifdef TCL_MEM_DEBUG      return Tcl_DbNewByteArrayObj(bytes, length, "unknown", 0); -} -  #else /* if not TCL_MEM_DEBUG */ - -Tcl_Obj * -Tcl_NewByteArrayObj( -    CONST unsigned char *bytes,	/* The array of bytes used to initialize the -				 * new object. */ -    int length)			/* Length of the array of bytes, which must be -				 * >= 0. */ -{      Tcl_Obj *objPtr;      TclNewObj(objPtr);      Tcl_SetByteArrayObj(objPtr, bytes, length);      return objPtr; -}  #endif /* TCL_MEM_DEBUG */ +}  /*   *---------------------------------------------------------------------- @@ -196,43 +354,28 @@ Tcl_NewByteArrayObj(   *----------------------------------------------------------------------   */ -#ifdef TCL_MEM_DEBUG -  Tcl_Obj *  Tcl_DbNewByteArrayObj( -    CONST unsigned char *bytes,	/* The array of bytes used to initialize the +    const unsigned char *bytes,	/* The array of bytes used to initialize the  				 * new object. */      int length,			/* Length of the array of bytes, which must be  				 * >= 0. */ -    CONST char *file,		/* The name of the source file calling this +    const char *file,		/* The name of the source file calling this  				 * procedure; used for debugging. */      int line)			/* Line number in the source file; used for  				 * debugging. */  { +#ifdef TCL_MEM_DEBUG      Tcl_Obj *objPtr;      TclDbNewObj(objPtr, file, line);      Tcl_SetByteArrayObj(objPtr, bytes, length);      return objPtr; -} -  #else /* if not TCL_MEM_DEBUG */ - -Tcl_Obj * -Tcl_DbNewByteArrayObj( -    CONST unsigned char *bytes,	/* The array of bytes used to initialize the -				 * new object. */ -    int length,			/* Length of the array of bytes, which must be -				 * >= 0. */ -    CONST char *file,		/* The name of the source file calling this -				 * procedure; used for debugging. */ -    int line)			/* Line number in the source file; used for -				 * debugging. */ -{      return Tcl_NewByteArrayObj(bytes, length); -}  #endif /* TCL_MEM_DEBUG */ - +} +  /*   *---------------------------------------------------------------------------   * @@ -254,25 +397,30 @@ Tcl_DbNewByteArrayObj(  void  Tcl_SetByteArrayObj(      Tcl_Obj *objPtr,		/* Object to initialize as a ByteArray. */ -    CONST unsigned char *bytes,	/* The array of bytes to use as the new -				 * value. */ -    int length)			/* Length of the array of bytes, which must be -				 * >= 0. */ +    const unsigned char *bytes,	/* The array of bytes to use as the new +				   value. May be NULL even if length > 0. */ +    int length)			/* Length of the array of bytes, which must +				   be >= 0. */  {      ByteArray *byteArrayPtr;      if (Tcl_IsShared(objPtr)) { -	Tcl_Panic("Tcl_SetByteArrayObj called with shared object"); +	Tcl_Panic("%s called with shared object", "Tcl_SetByteArrayObj");      }      TclFreeIntRep(objPtr); -    Tcl_InvalidateStringRep(objPtr); +    TclInvalidateStringRep(objPtr); -    byteArrayPtr = (ByteArray *) ckalloc(BYTEARRAY_SIZE(length)); +    if (length < 0) { +	length = 0; +    } +    byteArrayPtr = ckalloc(BYTEARRAY_SIZE(length));      byteArrayPtr->used = length;      byteArrayPtr->allocated = length; -    memcpy((VOID *) byteArrayPtr->bytes, (VOID *) bytes, (size_t) length); -    objPtr->typePtr = &tclByteArrayType; +    if ((bytes != NULL) && (length > 0)) { +	memcpy(byteArrayPtr->bytes, bytes, (size_t) length); +    } +    objPtr->typePtr = &properByteArrayType;      SET_BYTEARRAY(objPtr, byteArrayPtr);  } @@ -302,7 +450,10 @@ Tcl_GetByteArrayFromObj(  {      ByteArray *baPtr; -    SetByteArrayFromAny(NULL, objPtr); +    if ((objPtr->typePtr != &properByteArrayType) +	    && (objPtr->typePtr != &tclByteArrayType)) { +	SetByteArrayFromAny(NULL, objPtr); +    }      baPtr = GET_BYTEARRAY(objPtr);      if (lengthPtr != NULL) { @@ -338,27 +489,23 @@ Tcl_SetByteArrayLength(      Tcl_Obj *objPtr,		/* The ByteArray object. */      int length)			/* New length for internal byte array. */  { -    ByteArray *byteArrayPtr, *newByteArrayPtr; +    ByteArray *byteArrayPtr;      if (Tcl_IsShared(objPtr)) { -	Tcl_Panic("Tcl_SetObjLength called with shared object"); +	Tcl_Panic("%s called with shared object", "Tcl_SetByteArrayLength");      } -    if (objPtr->typePtr != &tclByteArrayType) { +    if ((objPtr->typePtr != &properByteArrayType) +	    && (objPtr->typePtr != &tclByteArrayType)) {  	SetByteArrayFromAny(NULL, objPtr);      }      byteArrayPtr = GET_BYTEARRAY(objPtr);      if (length > byteArrayPtr->allocated) { -	newByteArrayPtr = (ByteArray *) ckalloc(BYTEARRAY_SIZE(length)); -	newByteArrayPtr->used = length; -	newByteArrayPtr->allocated = length; -	memcpy((VOID *) newByteArrayPtr->bytes, -		(VOID *) byteArrayPtr->bytes, (size_t) byteArrayPtr->used); -	ckfree((char *) byteArrayPtr); -	byteArrayPtr = newByteArrayPtr; +	byteArrayPtr = ckrealloc(byteArrayPtr, BYTEARRAY_SIZE(length)); +	byteArrayPtr->allocated = length;  	SET_BYTEARRAY(objPtr, byteArrayPtr);      } -    Tcl_InvalidateStringRep(objPtr); +    TclInvalidateStringRep(objPtr);      byteArrayPtr->used = length;      return byteArrayPtr->bytes;  } @@ -384,29 +531,37 @@ SetByteArrayFromAny(      Tcl_Interp *interp,		/* Not used. */      Tcl_Obj *objPtr)		/* The object to convert to type ByteArray. */  { -    int length; -    char *src, *srcEnd; +    size_t length; +    int improper = 0; +    const char *src, *srcEnd;      unsigned char *dst;      ByteArray *byteArrayPtr;      Tcl_UniChar ch; -    if (objPtr->typePtr != &tclByteArrayType) { -	src = Tcl_GetStringFromObj(objPtr, &length); -	srcEnd = src + length; - -	byteArrayPtr = (ByteArray *) ckalloc(BYTEARRAY_SIZE(length)); -	for (dst = byteArrayPtr->bytes; src < srcEnd; ) { -	    src += Tcl_UtfToUniChar(src, &ch); -	    *dst++ = (unsigned char) ch; -	} +    if (objPtr->typePtr == &properByteArrayType) { +	return TCL_OK; +    } +    if (objPtr->typePtr == &tclByteArrayType) { +	return TCL_OK; +    } -	byteArrayPtr->used = dst - byteArrayPtr->bytes; -	byteArrayPtr->allocated = length; +    src = TclGetString(objPtr); +    length = objPtr->length; +    srcEnd = src + length; -	TclFreeIntRep(objPtr); -	objPtr->typePtr = &tclByteArrayType; -	SET_BYTEARRAY(objPtr, byteArrayPtr); +    byteArrayPtr = ckalloc(BYTEARRAY_SIZE(length)); +    for (dst = byteArrayPtr->bytes; src < srcEnd; ) { +	src += Tcl_UtfToUniChar(src, &ch); +	improper = improper || (ch > 255); +	*dst++ = UCHAR(ch);      } + +    byteArrayPtr->used = dst - byteArrayPtr->bytes; +    byteArrayPtr->allocated = length; + +    TclFreeIntRep(objPtr); +    objPtr->typePtr = improper ? &tclByteArrayType : &properByteArrayType; +    SET_BYTEARRAY(objPtr, byteArrayPtr);      return TCL_OK;  } @@ -431,7 +586,8 @@ static void  FreeByteArrayInternalRep(      Tcl_Obj *objPtr)		/* Object with internal rep to free. */  { -    ckfree((char *) GET_BYTEARRAY(objPtr)); +    ckfree(GET_BYTEARRAY(objPtr)); +    objPtr->typePtr = NULL;  }  /* @@ -462,14 +618,13 @@ DupByteArrayInternalRep(      srcArrayPtr = GET_BYTEARRAY(srcPtr);      length = srcArrayPtr->used; -    copyArrayPtr = (ByteArray *) ckalloc(BYTEARRAY_SIZE(length)); +    copyArrayPtr = ckalloc(BYTEARRAY_SIZE(length));      copyArrayPtr->used = length;      copyArrayPtr->allocated = length; -    memcpy((VOID *) copyArrayPtr->bytes, (VOID *) srcArrayPtr->bytes, -	    (size_t) length); +    memcpy(copyArrayPtr->bytes, srcArrayPtr->bytes, (size_t) length);      SET_BYTEARRAY(copyPtr, copyArrayPtr); -    copyPtr->typePtr = &tclByteArrayType; +    copyPtr->typePtr = srcPtr->typePtr;  }  /* @@ -513,18 +668,21 @@ UpdateStringOfByteArray(       */      size = length; -    for (i = 0; i < length; i++) { +    for (i = 0; i < length && size >= 0; i++) {  	if ((src[i] == 0) || (src[i] > 127)) {  	    size++;  	}      } +    if (size < 0) { +	Tcl_Panic("max size for a Tcl value (%d bytes) exceeded", INT_MAX); +    } -    dst = (char *) ckalloc((unsigned) (size + 1)); +    dst = ckalloc(size + 1);      objPtr->bytes = dst;      objPtr->length = size;      if (size == length) { -	memcpy((VOID *) dst, (VOID *) src, (size_t) size); +	memcpy(dst, src, (size_t) size);  	dst[size] = '\0';      } else {  	for (i = 0; i < length; i++) { @@ -537,9 +695,127 @@ UpdateStringOfByteArray(  /*   *----------------------------------------------------------------------   * - * Tcl_BinaryObjCmd -- + * TclAppendBytesToByteArray -- + * + *	This function appends an array of bytes to a byte array object. Note + *	that the object *must* be unshared, and the array of bytes *must not* + *	refer to the object being appended to. + * + * Results: + *	None. + * + * Side effects: + *	Allocates enough memory for an array of bytes of the requested total + *	size, or possibly larger. [Bug 2992970] + * + *---------------------------------------------------------------------- + */ + +void +TclAppendBytesToByteArray( +    Tcl_Obj *objPtr, +    const unsigned char *bytes, +    int len) +{ +    ByteArray *byteArrayPtr; +    int needed; + +    if (Tcl_IsShared(objPtr)) { +	Tcl_Panic("%s called with shared object","TclAppendBytesToByteArray"); +    } +    if (len < 0) { +	Tcl_Panic("%s must be called with definite number of bytes to append", +		"TclAppendBytesToByteArray"); +    } +    if (len == 0) { +	/* Append zero bytes is a no-op. */ +	return; +    } +    if ((objPtr->typePtr != &properByteArrayType) +	    && (objPtr->typePtr != &tclByteArrayType)) { +	SetByteArrayFromAny(NULL, objPtr); +    } +    byteArrayPtr = GET_BYTEARRAY(objPtr); + +    if (len > INT_MAX - byteArrayPtr->used) { +	Tcl_Panic("max size for a Tcl value (%d bytes) exceeded", INT_MAX); +    } + +    needed = byteArrayPtr->used + len; +    /* +     * If we need to, resize the allocated space in the byte array. +     */ + +    if (needed > byteArrayPtr->allocated) { +	ByteArray *ptr = NULL; +	int attempt; + +	if (needed <= INT_MAX/2) { +	    /* Try to allocate double the total space that is needed. */ +	    attempt = 2 * needed; +	    ptr = attemptckrealloc(byteArrayPtr, BYTEARRAY_SIZE(attempt)); +	} +	if (ptr == NULL) { +	    /* Try to allocate double the increment that is needed (plus). */ +	    unsigned int limit = INT_MAX - needed; +	    unsigned int extra = len + TCL_MIN_GROWTH; +	    int growth = (int) ((extra > limit) ? limit : extra); + +	    attempt = needed + growth; +	    ptr = attemptckrealloc(byteArrayPtr, BYTEARRAY_SIZE(attempt)); +	} +	if (ptr == NULL) { +	    /* Last chance: Try to allocate exactly what is needed. */ +	    attempt = needed; +	    ptr = ckrealloc(byteArrayPtr, BYTEARRAY_SIZE(attempt)); +	} +	byteArrayPtr = ptr; +	byteArrayPtr->allocated = attempt; +	SET_BYTEARRAY(objPtr, byteArrayPtr); +    } + +    if (bytes) { +	memcpy(byteArrayPtr->bytes + byteArrayPtr->used, bytes, len); +    } +    byteArrayPtr->used += len; +    TclInvalidateStringRep(objPtr); +} + +/* + *---------------------------------------------------------------------- + * + * TclInitBinaryCmd --   * - *	This procedure implements the "binary" Tcl command. + *	This function is called to create the "binary" Tcl command. See the + *	user documentation for details on what it does. + * + * Results: + *	A command token for the new command. + * + * Side effects: + *	Creates a new binary command as a mapped ensemble. + * + *---------------------------------------------------------------------- + */ + +Tcl_Command +TclInitBinaryCmd( +    Tcl_Interp *interp) +{ +    Tcl_Command binaryEnsemble; + +    binaryEnsemble = TclMakeEnsemble(interp, "binary", binaryMap); +    TclMakeEnsemble(interp, "binary encode", encodeMap); +    TclMakeEnsemble(interp, "binary decode", decodeMap); +    return binaryEnsemble; +} + +/* + *---------------------------------------------------------------------- + * + * BinaryFormatCmd -- + * + *	This procedure implements the "binary format" Tcl command.   *   * Results:   *	A standard Tcl result. @@ -550,12 +826,12 @@ UpdateStringOfByteArray(   *----------------------------------------------------------------------   */ -int -Tcl_BinaryObjCmd( +static int +BinaryFormatCmd(      ClientData dummy,		/* Not used. */      Tcl_Interp *interp,		/* Current interpreter. */      int objc,			/* Number of arguments. */ -    Tcl_Obj *CONST objv[])	/* Argument objects. */ +    Tcl_Obj *const objv[])	/* Argument objects. */  {      int arg;			/* Index of next argument to consume. */      int value = 0;		/* Current integer value to be packed. @@ -563,788 +839,849 @@ Tcl_BinaryObjCmd(      char cmd;			/* Current format character. */      int count;			/* Count associated with current format  				 * character. */ -    char *format;		/* Pointer to current position in format +    int flags;			/* Format field flags */ +    const char *format;	/* Pointer to current position in format  				 * string. */      Tcl_Obj *resultPtr = NULL;	/* Object holding result buffer. */      unsigned char *buffer;	/* Start of result buffer. */      unsigned char *cursor;	/* Current position within result buffer. */      unsigned char *maxPos;	/* Greatest position within result buffer that  				 * cursor has visited.*/ -    char *errorString, *errorValue, *str; -    int offset, size, length, index; -    static CONST char *options[] = { -	"format",	"scan",		NULL -    }; -    enum options { -	BINARY_FORMAT,	BINARY_SCAN -    }; +    const char *errorString; +    const char *errorValue, *str; +    int offset, size, length;      if (objc < 2) { -    	Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?"); +	Tcl_WrongNumArgs(interp, 1, objv, "formatString ?arg ...?");  	return TCL_ERROR;      } -    if (Tcl_GetIndexFromObj(interp, objv[1], options, "option", 0, -	    &index) != TCL_OK) { -    	return TCL_ERROR; -    } +    /* +     * To avoid copying the data, we format the string in two passes. The +     * first pass computes the size of the output buffer. The second pass +     * places the formatted data into the buffer. +     */ -    switch ((enum options) index) { -    case BINARY_FORMAT: -	if (objc < 3) { -	    Tcl_WrongNumArgs(interp, 2, objv, "formatString ?arg arg ...?"); -	    return TCL_ERROR; +    format = TclGetString(objv[1]); +    arg = 2; +    offset = 0; +    length = 0; +    while (*format != '\0') { +	str = format; +	flags = 0; +	if (!GetFormatSpec(&format, &cmd, &count, &flags)) { +	    break;  	} - -	/* -	 * To avoid copying the data, we format the string in two passes. The -	 * first pass computes the size of the output buffer. The second pass -	 * places the formatted data into the buffer. -	 */ - -	format = Tcl_GetString(objv[2]); -	arg = 3; -	offset = 0; -	length = 0; -	while (*format != '\0') { -	    str = format; -	    if (!GetFormatSpec(&format, &cmd, &count)) { -		break; +	switch (cmd) { +	case 'a': +	case 'A': +	case 'b': +	case 'B': +	case 'h': +	case 'H': +	    /* +	     * For string-type specifiers, the count corresponds to the number +	     * of bytes in a single argument. +	     */ + +	    if (arg >= objc) { +		goto badIndex; +	    } +	    if (count == BINARY_ALL) { +		Tcl_GetByteArrayFromObj(objv[arg], &count); +	    } else if (count == BINARY_NOCOUNT) { +		count = 1; +	    } +	    arg++; +	    if (cmd == 'a' || cmd == 'A') { +		offset += count; +	    } else if (cmd == 'b' || cmd == 'B') { +		offset += (count + 7) / 8; +	    } else { +		offset += (count + 1) / 2; +	    } +	    break; +	case 'c': +	    size = 1; +	    goto doNumbers; +	case 't': +	case 's': +	case 'S': +	    size = 2; +	    goto doNumbers; +	case 'n': +	case 'i': +	case 'I': +	    size = 4; +	    goto doNumbers; +	case 'm': +	case 'w': +	case 'W': +	    size = 8; +	    goto doNumbers; +	case 'r': +	case 'R': +	case 'f': +	    size = sizeof(float); +	    goto doNumbers; +	case 'q': +	case 'Q': +	case 'd': +	    size = sizeof(double); + +	doNumbers: +	    if (arg >= objc) { +		goto badIndex;  	    } -	    switch (cmd) { -	    case 'a': -	    case 'A': -	    case 'b': -	    case 'B': -	    case 'h': -	    case 'H': -		/* -		 * For string-type specifiers, the count corresponds to the -		 * number of bytes in a single argument. -		 */ -		if (arg >= objc) { -		    goto badIndex; -		} -		if (count == BINARY_ALL) { -		    Tcl_GetByteArrayFromObj(objv[arg], &count); -		} else if (count == BINARY_NOCOUNT) { -		    count = 1; -		} +	    /* +	     * For number-type specifiers, the count corresponds to the number +	     * of elements in the list stored in a single argument. If no +	     * count is specified, then the argument is taken as a single +	     * non-list value. +	     */ + +	    if (count == BINARY_NOCOUNT) {  		arg++; -		if (cmd == 'a' || cmd == 'A') { -		    offset += count; -		} else if (cmd == 'b' || cmd == 'B') { -		    offset += (count + 7) / 8; -		} else { -		    offset += (count + 1) / 2; -		} -		break; -	    case 'c': -		size = 1; -		goto doNumbers; -	    case 't': -	    case 's': -	    case 'S': -		size = 2; -		goto doNumbers; -	    case 'n': -	    case 'i': -	    case 'I': -		size = 4; -		goto doNumbers; -	    case 'm': -	    case 'w': -	    case 'W': -		size = 8; -		goto doNumbers; -	    case 'r': -	    case 'R': -	    case 'f': -		size = sizeof(float); -		goto doNumbers; -	    case 'q': -	    case 'Q': -	    case 'd': -		size = sizeof(double); - -	    doNumbers: -		if (arg >= objc) { -		    goto badIndex; -		} +		count = 1; +	    } else { +		int listc; +		Tcl_Obj **listv;  		/* -		 * For number-type specifiers, the count corresponds to the -		 * number of elements in the list stored in a single argument. -		 * If no count is specified, then the argument is taken as a -		 * single non-list value. +		 * The macro evals its args more than once: avoid arg++  		 */ -		if (count == BINARY_NOCOUNT) { -		    arg++; -		    count = 1; -		} else { -		    int listc; -		    Tcl_Obj **listv; - -		    if (Tcl_ListObjGetElements(interp, objv[arg++], &listc, -			    &listv) != TCL_OK) { -			return TCL_ERROR; -		    } -		    if (count == BINARY_ALL) { -			count = listc; -		    } else if (count > listc) { -			Tcl_AppendResult(interp, -				"number of elements in list does not match count", -				(char *) NULL); -			return TCL_ERROR; -		    } +		if (TclListObjGetElements(interp, objv[arg], &listc, +			&listv) != TCL_OK) { +		    return TCL_ERROR;  		} -		offset += count*size; -		break; +		arg++; -	    case 'x':  		if (count == BINARY_ALL) { -		    Tcl_AppendResult(interp, -			    "cannot use \"*\" in format string with \"x\"", -			    (char *) NULL); +		    count = listc; +		} else if (count > listc) { +		    Tcl_SetObjResult(interp, Tcl_NewStringObj( +			    "number of elements in list does not match count", +			    -1));  		    return TCL_ERROR; -		} else if (count == BINARY_NOCOUNT) { -		    count = 1; -		} -		offset += count; -		break; -	    case 'X': -		if (count == BINARY_NOCOUNT) { -		    count = 1;  		} -		if ((count > offset) || (count == BINARY_ALL)) { -		    count = offset; -		} -		if (offset > length) { -		    length = offset; -		} -		offset -= count; -		break; -	    case '@': -		if (offset > length) { -		    length = offset; -		} -		if (count == BINARY_ALL) { -		    offset = length; -		} else if (count == BINARY_NOCOUNT) { -		    goto badCount; -		} else { -		    offset = count; -		} -		break; -	    default: -		errorString = str; -		goto badField;  	    } +	    offset += count*size; +	    break; + +	case 'x': +	    if (count == BINARY_ALL) { +		Tcl_SetObjResult(interp, Tcl_NewStringObj( +			"cannot use \"*\" in format string with \"x\"", -1)); +		return TCL_ERROR; +	    } else if (count == BINARY_NOCOUNT) { +		count = 1; +	    } +	    offset += count; +	    break; +	case 'X': +	    if (count == BINARY_NOCOUNT) { +		count = 1; +	    } +	    if ((count > offset) || (count == BINARY_ALL)) { +		count = offset; +	    } +	    if (offset > length) { +		length = offset; +	    } +	    offset -= count; +	    break; +	case '@': +	    if (offset > length) { +		length = offset; +	    } +	    if (count == BINARY_ALL) { +		offset = length; +	    } else if (count == BINARY_NOCOUNT) { +		goto badCount; +	    } else { +		offset = count; +	    } +	    break; +	default: +	    errorString = str; +	    goto badField;  	} -	if (offset > length) { -	    length = offset; -	} -	if (length == 0) { -	    return TCL_OK; -	} +    } +    if (offset > length) { +	length = offset; +    } +    if (length == 0) { +	return TCL_OK; +    } -	/* -	 * Prepare the result object by preallocating the caclulated number of -	 * bytes and filling with nulls. -	 */ +    /* +     * Prepare the result object by preallocating the caclulated number of +     * bytes and filling with nulls. +     */ -	resultPtr = Tcl_NewObj(); -	buffer = Tcl_SetByteArrayLength(resultPtr, length); -	memset((VOID *) buffer, 0, (size_t) length); +    resultPtr = Tcl_NewObj(); +    buffer = Tcl_SetByteArrayLength(resultPtr, length); +    memset(buffer, 0, (size_t) length); -	/* -	 * Pack the data into the result object. Note that we can skip the -	 * error checking during this pass, since we have already parsed the -	 * string once. -	 */ +    /* +     * Pack the data into the result object. Note that we can skip the +     * error checking during this pass, since we have already parsed the +     * string once. +     */ -	arg = 3; -	format = Tcl_GetString(objv[2]); -	cursor = buffer; -	maxPos = cursor; -	while (*format != 0) { -	    if (!GetFormatSpec(&format, &cmd, &count)) { -		break; -	    } -	    if ((count == 0) && (cmd != '@')) { +    arg = 2; +    format = TclGetString(objv[1]); +    cursor = buffer; +    maxPos = cursor; +    while (*format != 0) { +	flags = 0; +	if (!GetFormatSpec(&format, &cmd, &count, &flags)) { +	    break; +	} +	if ((count == 0) && (cmd != '@')) { +	    if (cmd != 'x') {  		arg++; -		continue;  	    } -	    switch (cmd) { -	    case 'a': -	    case 'A': { -		char pad = (char) (cmd == 'a' ? '\0' : ' '); -		unsigned char *bytes; - -		bytes = Tcl_GetByteArrayFromObj(objv[arg++], &length); - -		if (count == BINARY_ALL) { -		    count = length; -		} else if (count == BINARY_NOCOUNT) { -		    count = 1; -		} -		if (length >= count) { -		    memcpy((VOID *) cursor, (VOID *) bytes, (size_t) count); -		} else { -		    memcpy((VOID *) cursor, (VOID *) bytes, (size_t) length); -		    memset((VOID *) (cursor + length), pad, -			    (size_t) (count - length)); -		} -		cursor += count; -		break; +	    continue; +	} +	switch (cmd) { +	case 'a': +	case 'A': { +	    char pad = (char) (cmd == 'a' ? '\0' : ' '); +	    unsigned char *bytes; + +	    bytes = Tcl_GetByteArrayFromObj(objv[arg++], &length); + +	    if (count == BINARY_ALL) { +		count = length; +	    } else if (count == BINARY_NOCOUNT) { +		count = 1;  	    } -	    case 'b': -	    case 'B': { -		unsigned char *last; - -		str = Tcl_GetStringFromObj(objv[arg++], &length); -		if (count == BINARY_ALL) { -		    count = length; -		} else if (count == BINARY_NOCOUNT) { -		    count = 1; -		} -		last = cursor + ((count + 7) / 8); -		if (count > length) { -		    count = length; -		} -		value = 0; -		errorString = "binary"; -		if (cmd == 'B') { -		    for (offset = 0; offset < count; offset++) { -			value <<= 1; -			if (str[offset] == '1') { -			    value |= 1; -			} else if (str[offset] != '0') { -			    errorValue = str; -			    goto badValue; -			} -			if (((offset + 1) % 8) == 0) { -			    *cursor++ = (unsigned char) value; -			    value = 0; -			} +	    if (length >= count) { +		memcpy(cursor, bytes, (size_t) count); +	    } else { +		memcpy(cursor, bytes, (size_t) length); +		memset(cursor + length, pad, (size_t) (count - length)); +	    } +	    cursor += count; +	    break; +	} +	case 'b': +	case 'B': { +	    unsigned char *last; + +	    str = TclGetStringFromObj(objv[arg], &length); +	    arg++; +	    if (count == BINARY_ALL) { +		count = length; +	    } else if (count == BINARY_NOCOUNT) { +		count = 1; +	    } +	    last = cursor + ((count + 7) / 8); +	    if (count > length) { +		count = length; +	    } +	    value = 0; +	    errorString = "binary"; +	    if (cmd == 'B') { +		for (offset = 0; offset < count; offset++) { +		    value <<= 1; +		    if (str[offset] == '1') { +			value |= 1; +		    } else if (str[offset] != '0') { +			errorValue = str; +			Tcl_DecrRefCount(resultPtr); +			goto badValue;  		    } -		} else { -		    for (offset = 0; offset < count; offset++) { -			value >>= 1; -			if (str[offset] == '1') { -			    value |= 128; -			} else if (str[offset] != '0') { -			    errorValue = str; -			    goto badValue; -			} -			if (!((offset + 1) % 8)) { -			    *cursor++ = (unsigned char) value; -			    value = 0; -			} +		    if (((offset + 1) % 8) == 0) { +			*cursor++ = UCHAR(value); +			value = 0;  		    }  		} -		if ((offset % 8) != 0) { -		    if (cmd == 'B') { -			value <<= 8 - (offset % 8); -		    } else { -			value >>= 8 - (offset % 8); +	    } else { +		for (offset = 0; offset < count; offset++) { +		    value >>= 1; +		    if (str[offset] == '1') { +			value |= 128; +		    } else if (str[offset] != '0') { +			errorValue = str; +			Tcl_DecrRefCount(resultPtr); +			goto badValue; +		    } +		    if (!((offset + 1) % 8)) { +			*cursor++ = UCHAR(value); +			value = 0;  		    } -		    *cursor++ = (unsigned char) value; -		} -		while (cursor < last) { -		    *cursor++ = '\0';  		} -		break;  	    } -	    case 'h': -	    case 'H': { -		unsigned char *last; -		int c; - -		str = Tcl_GetStringFromObj(objv[arg++], &length); -		if (count == BINARY_ALL) { -		    count = length; -		} else if (count == BINARY_NOCOUNT) { -		    count = 1; -		} -		last = cursor + ((count + 1) / 2); -		if (count > length) { -		    count = length; +	    if ((offset % 8) != 0) { +		if (cmd == 'B') { +		    value <<= 8 - (offset % 8); +		} else { +		    value >>= 8 - (offset % 8);  		} -		value = 0; -		errorString = "hexadecimal"; -		if (cmd == 'H') { -		    for (offset = 0; offset < count; offset++) { -			value <<= 4; -			if (!isxdigit(UCHAR(str[offset]))) { /* INTL: digit */ -			    errorValue = str; -			    goto badValue; -			} -			c = str[offset] - '0'; -			if (c > 9) { -			    c += ('0' - 'A') + 10; -			} -			if (c > 16) { -			    c += ('A' - 'a'); -			} -			value |= (c & 0xf); -			if (offset % 2) { -			    *cursor++ = (char) value; -			    value = 0; -			} +		*cursor++ = UCHAR(value); +	    } +	    while (cursor < last) { +		*cursor++ = '\0'; +	    } +	    break; +	} +	case 'h': +	case 'H': { +	    unsigned char *last; +	    int c; + +	    str = TclGetStringFromObj(objv[arg], &length); +	    arg++; +	    if (count == BINARY_ALL) { +		count = length; +	    } else if (count == BINARY_NOCOUNT) { +		count = 1; +	    } +	    last = cursor + ((count + 1) / 2); +	    if (count > length) { +		count = length; +	    } +	    value = 0; +	    errorString = "hexadecimal"; +	    if (cmd == 'H') { +		for (offset = 0; offset < count; offset++) { +		    value <<= 4; +		    if (!isxdigit(UCHAR(str[offset]))) {     /* INTL: digit */ +			errorValue = str; +			Tcl_DecrRefCount(resultPtr); +			goto badValue;  		    } -		} else { -		    for (offset = 0; offset < count; offset++) { -			value >>= 4; - -			if (!isxdigit(UCHAR(str[offset]))) { /* INTL: digit */ -			    errorValue = str; -			    goto badValue; -			} -			c = str[offset] - '0'; -			if (c > 9) { -			    c += ('0' - 'A') + 10; -			} -			if (c > 16) { -			    c += ('A' - 'a'); -			} -			value |= ((c << 4) & 0xf0); -			if (offset % 2) { -			    *cursor++ = (unsigned char)(value & 0xff); -			    value = 0; -			} +		    c = str[offset] - '0'; +		    if (c > 9) { +			c += ('0' - 'A') + 10;  		    } -		} -		if (offset % 2) { -		    if (cmd == 'H') { -			value <<= 4; -		    } else { -			value >>= 4; +		    if (c > 16) { +			c += ('A' - 'a'); +		    } +		    value |= (c & 0xf); +		    if (offset % 2) { +			*cursor++ = (char) value; +			value = 0;  		    } -		    *cursor++ = (unsigned char) value; -		} - -		while (cursor < last) { -		    *cursor++ = '\0';  		} -		break; -	    } -	    case 'c': -	    case 't': -	    case 's': -	    case 'S': -	    case 'n': -	    case 'i': -	    case 'I': -	    case 'm': -	    case 'w': -	    case 'W': -	    case 'r': -	    case 'R': -	    case 'd': -	    case 'q': -	    case 'Q': -	    case 'f': { -		int listc, i; -		Tcl_Obj **listv; +	    } else { +		for (offset = 0; offset < count; offset++) { +		    value >>= 4; -		if (count == BINARY_NOCOUNT) { -		    /* -		     * Note that we are casting away the const-ness of objv, -		     * but this is safe since we aren't going to modify the -		     * array. -		     */ - -		    listv = (Tcl_Obj**)(objv + arg); -		    listc = 1; -		    count = 1; -		} else { -		    Tcl_ListObjGetElements(interp, objv[arg], &listc, &listv); -		    if (count == BINARY_ALL) { -			count = listc; +		    if (!isxdigit(UCHAR(str[offset]))) {     /* INTL: digit */ +			errorValue = str; +			Tcl_DecrRefCount(resultPtr); +			goto badValue;  		    } -		} -		arg++; -		for (i = 0; i < count; i++) { -		    if (FormatNumber(interp, cmd, listv[i], &cursor)!=TCL_OK) { -			return TCL_ERROR; +		    c = str[offset] - '0'; +		    if (c > 9) { +			c += ('0' - 'A') + 10; +		    } +		    if (c > 16) { +			c += ('A' - 'a'); +		    } +		    value |= ((c << 4) & 0xf0); +		    if (offset % 2) { +			*cursor++ = UCHAR(value & 0xff); +			value = 0;  		    }  		} -		break;  	    } -	    case 'x': -		if (count == BINARY_NOCOUNT) { -		    count = 1; -		} -		memset(cursor, 0, (size_t) count); -		cursor += count; -		break; -	    case 'X': -		if (cursor > maxPos) { -		    maxPos = cursor; -		} -		if (count == BINARY_NOCOUNT) { -		    count = 1; -		} -		if ((count == BINARY_ALL) || (count > (cursor - buffer))) { -		    cursor = buffer; +	    if (offset % 2) { +		if (cmd == 'H') { +		    value <<= 4;  		} else { -		    cursor -= count; -		} -		break; -	    case '@': -		if (cursor > maxPos) { -		    maxPos = cursor; +		    value >>= 4;  		} +		*cursor++ = UCHAR(value); +	    } + +	    while (cursor < last) { +		*cursor++ = '\0'; +	    } +	    break; +	} +	case 'c': +	case 't': +	case 's': +	case 'S': +	case 'n': +	case 'i': +	case 'I': +	case 'm': +	case 'w': +	case 'W': +	case 'r': +	case 'R': +	case 'd': +	case 'q': +	case 'Q': +	case 'f': { +	    int listc, i; +	    Tcl_Obj **listv; + +	    if (count == BINARY_NOCOUNT) { +		/* +		 * Note that we are casting away the const-ness of objv, but +		 * this is safe since we aren't going to modify the array. +		 */ + +		listv = (Tcl_Obj **) (objv + arg); +		listc = 1; +		count = 1; +	    } else { +		TclListObjGetElements(interp, objv[arg], &listc, &listv);  		if (count == BINARY_ALL) { -		    cursor = maxPos; -		} else { -		    cursor = buffer + count; +		    count = listc; +		} +	    } +	    arg++; +	    for (i = 0; i < count; i++) { +		if (FormatNumber(interp, cmd, listv[i], &cursor)!=TCL_OK) { +		    Tcl_DecrRefCount(resultPtr); +		    return TCL_ERROR;  		} -		break;  	    } +	    break;  	} -	Tcl_SetObjResult(interp, resultPtr); -	break; -    case BINARY_SCAN: { -	int i; -	Tcl_Obj *valuePtr, *elementPtr; -	Tcl_HashTable numberCacheHash; -	Tcl_HashTable *numberCachePtr; - -	if (objc < 4) { -	    Tcl_WrongNumArgs(interp, 2, objv, -		    "value formatString ?varName varName ...?"); -	    return TCL_ERROR; +	case 'x': +	    if (count == BINARY_NOCOUNT) { +		count = 1; +	    } +	    memset(cursor, 0, (size_t) count); +	    cursor += count; +	    break; +	case 'X': +	    if (cursor > maxPos) { +		maxPos = cursor; +	    } +	    if (count == BINARY_NOCOUNT) { +		count = 1; +	    } +	    if ((count == BINARY_ALL) || (count > (cursor - buffer))) { +		cursor = buffer; +	    } else { +		cursor -= count; +	    } +	    break; +	case '@': +	    if (cursor > maxPos) { +		maxPos = cursor; +	    } +	    if (count == BINARY_ALL) { +		cursor = maxPos; +	    } else { +		cursor = buffer + count; +	    } +	    break; +	} +    } +    Tcl_SetObjResult(interp, resultPtr); +    return TCL_OK; + + badValue: +    Tcl_ResetResult(interp); +    Tcl_SetObjResult(interp, Tcl_ObjPrintf( +	    "expected %s string but got \"%s\" instead", +	    errorString, errorValue)); +    return TCL_ERROR; + + badCount: +    errorString = "missing count for \"@\" field specifier"; +    goto error; + + badIndex: +    errorString = "not enough arguments for all format specifiers"; +    goto error; + + badField: +    { +	Tcl_UniChar ch; +	char buf[TCL_UTF_MAX + 1]; + +	Tcl_UtfToUniChar(errorString, &ch); +	buf[Tcl_UniCharToUtf(ch, buf)] = '\0'; +	Tcl_SetObjResult(interp, Tcl_ObjPrintf( +		"bad field specifier \"%s\"", buf)); +	return TCL_ERROR; +    } + + error: +    Tcl_SetObjResult(interp, Tcl_NewStringObj(errorString, -1)); +    return TCL_ERROR; +} + +/* + *---------------------------------------------------------------------- + * + * BinaryScanCmd -- + * + *	This procedure implements the "binary scan" Tcl command. + * + * Results: + *	A standard Tcl result. + * + * Side effects: + *	See the user documentation. + * + *---------------------------------------------------------------------- + */ + +int +BinaryScanCmd( +    ClientData dummy,		/* Not used. */ +    Tcl_Interp *interp,		/* Current interpreter. */ +    int objc,			/* Number of arguments. */ +    Tcl_Obj *const objv[])	/* Argument objects. */ +{ +    int arg;			/* Index of next argument to consume. */ +    int value = 0;		/* Current integer value to be packed. +				 * Initialized to avoid compiler warning. */ +    char cmd;			/* Current format character. */ +    int count;			/* Count associated with current format +				 * character. */ +    int flags;			/* Format field flags */ +    const char *format;	/* Pointer to current position in format +				 * string. */ +    Tcl_Obj *resultPtr = NULL;	/* Object holding result buffer. */ +    unsigned char *buffer;	/* Start of result buffer. */ +    const char *errorString; +    const char *str; +    int offset, size, length; + +    int i; +    Tcl_Obj *valuePtr, *elementPtr; +    Tcl_HashTable numberCacheHash; +    Tcl_HashTable *numberCachePtr; + +    if (objc < 3) { +	Tcl_WrongNumArgs(interp, 1, objv, +		"value formatString ?varName ...?"); +	return TCL_ERROR; +    } +    numberCachePtr = &numberCacheHash; +    Tcl_InitHashTable(numberCachePtr, TCL_ONE_WORD_KEYS); +    buffer = Tcl_GetByteArrayFromObj(objv[1], &length); +    format = TclGetString(objv[2]); +    arg = 3; +    offset = 0; +    while (*format != '\0') { +	str = format; +	flags = 0; +	if (!GetFormatSpec(&format, &cmd, &count, &flags)) { +	    goto done;  	} -	numberCachePtr = &numberCacheHash; -	Tcl_InitHashTable(numberCachePtr, TCL_ONE_WORD_KEYS); -	buffer = Tcl_GetByteArrayFromObj(objv[2], &length); -	format = Tcl_GetString(objv[3]); -	cursor = buffer; -	arg = 4; -	offset = 0; -	while (*format != '\0') { -	    str = format; -	    if (!GetFormatSpec(&format, &cmd, &count)) { -		goto done; -	    } -	    switch (cmd) { -	    case 'a': -	    case 'A': { -		unsigned char *src; - -		if (arg >= objc) { -		    DeleteScanNumberCache(numberCachePtr); -		    goto badIndex; +	switch (cmd) { +	case 'a': +	case 'A': { +	    unsigned char *src; + +	    if (arg >= objc) { +		DeleteScanNumberCache(numberCachePtr); +		goto badIndex; +	    } +	    if (count == BINARY_ALL) { +		count = length - offset; +	    } else { +		if (count == BINARY_NOCOUNT) { +		    count = 1;  		} -		if (count == BINARY_ALL) { -		    count = length - offset; -		} else { -		    if (count == BINARY_NOCOUNT) { -			count = 1; -		    } -		    if (count > (length - offset)) { -			goto done; -		    } +		if (count > (length - offset)) { +		    goto done;  		} +	    } -		src = buffer + offset; -		size = count; +	    src = buffer + offset; +	    size = count; -		/* -		 * Trim trailing nulls and spaces, if necessary. -		 */ +	    /* +	     * Trim trailing nulls and spaces, if necessary. +	     */ -		if (cmd == 'A') { -		    while (size > 0) { -			if (src[size-1] != '\0' && src[size-1] != ' ') { -			    break; -			} -			size--; +	    if (cmd == 'A') { +		while (size > 0) { +		    if (src[size-1] != '\0' && src[size-1] != ' ') { +			break;  		    } +		    size--;  		} +	    } -		/* -		 * Have to do this #ifdef-fery because (as part of defining -		 * Tcl_NewByteArrayObj) we removed the #def that hides this -		 * stuff normally. If this code ever gets copied to another -		 * file, it should be changed back to the simpler version. -		 */ +	    /* +	     * Have to do this #ifdef-fery because (as part of defining +	     * Tcl_NewByteArrayObj) we removed the #def that hides this stuff +	     * normally. If this code ever gets copied to another file, it +	     * should be changed back to the simpler version. +	     */  #ifdef TCL_MEM_DEBUG -		valuePtr = Tcl_DbNewByteArrayObj(src, size, __FILE__,__LINE__); +	    valuePtr = Tcl_DbNewByteArrayObj(src, size, __FILE__, __LINE__);  #else -		valuePtr = Tcl_NewByteArrayObj(src, size); +	    valuePtr = Tcl_NewByteArrayObj(src, size);  #endif /* TCL_MEM_DEBUG */ -		resultPtr = Tcl_ObjSetVar2(interp, objv[arg], NULL, valuePtr, -			TCL_LEAVE_ERR_MSG); -		arg++; -		if (resultPtr == NULL) { -		    DeleteScanNumberCache(numberCachePtr); -		    return TCL_ERROR; -		} -		offset += count; -		break; +	    resultPtr = Tcl_ObjSetVar2(interp, objv[arg], NULL, valuePtr, +		    TCL_LEAVE_ERR_MSG); +	    arg++; +	    if (resultPtr == NULL) { +		DeleteScanNumberCache(numberCachePtr); +		return TCL_ERROR;  	    } -	    case 'b': -	    case 'B': { -		unsigned char *src; -		char *dest; +	    offset += count; +	    break; +	} +	case 'b': +	case 'B': { +	    unsigned char *src; +	    char *dest; -		if (arg >= objc) { -		    DeleteScanNumberCache(numberCachePtr); -		    goto badIndex; -		} -		if (count == BINARY_ALL) { -		    count = (length - offset) * 8; -		} else { -		    if (count == BINARY_NOCOUNT) { -			count = 1; -		    } -		    if (count > (length - offset) * 8) { -			goto done; -		    } -		} -		src = buffer + offset; -		valuePtr = Tcl_NewObj(); -		Tcl_SetObjLength(valuePtr, count); -		dest = Tcl_GetString(valuePtr); - -		if (cmd == 'b') { -		    for (i = 0; i < count; i++) { -			if (i % 8) { -			    value >>= 1; -			} else { -			    value = *src++; -			} -			*dest++ = (char) ((value & 1) ? '1' : '0'); -		    } -		} else { -		    for (i = 0; i < count; i++) { -			if (i % 8) { -			    value <<= 1; -			} else { -			    value = *src++; -			} -			*dest++ = (char) ((value & 0x80) ? '1' : '0'); -		    } +	    if (arg >= objc) { +		DeleteScanNumberCache(numberCachePtr); +		goto badIndex; +	    } +	    if (count == BINARY_ALL) { +		count = (length - offset) * 8; +	    } else { +		if (count == BINARY_NOCOUNT) { +		    count = 1;  		} - -		resultPtr = Tcl_ObjSetVar2(interp, objv[arg], NULL, valuePtr, -			TCL_LEAVE_ERR_MSG); -		arg++; -		if (resultPtr == NULL) { -		    DeleteScanNumberCache(numberCachePtr); -		    return TCL_ERROR; +		if (count > (length - offset) * 8) { +		    goto done;  		} -		offset += (count + 7 ) / 8; -		break;  	    } -	    case 'h': -	    case 'H': { -		char *dest; -		unsigned char *src; -		int i; -		static CONST char hexdigit[] = "0123456789abcdef"; +	    src = buffer + offset; +	    valuePtr = Tcl_NewObj(); +	    Tcl_SetObjLength(valuePtr, count); +	    dest = TclGetString(valuePtr); -		if (arg >= objc) { -		    DeleteScanNumberCache(numberCachePtr); -		    goto badIndex; -		} -		if (count == BINARY_ALL) { -		    count = (length - offset)*2; -		} else { -		    if (count == BINARY_NOCOUNT) { -			count = 1; -		    } -		    if (count > (length - offset)*2) { -			goto done; +	    if (cmd == 'b') { +		for (i = 0; i < count; i++) { +		    if (i % 8) { +			value >>= 1; +		    } else { +			value = *src++;  		    } +		    *dest++ = (char) ((value & 1) ? '1' : '0');  		} -		src = buffer + offset; -		valuePtr = Tcl_NewObj(); -		Tcl_SetObjLength(valuePtr, count); -		dest = Tcl_GetString(valuePtr); - -		if (cmd == 'h') { -		    for (i = 0; i < count; i++) { -			if (i % 2) { -			    value >>= 4; -			} else { -			    value = *src++; -			} -			*dest++ = hexdigit[value & 0xf]; -		    } -		} else { -		    for (i = 0; i < count; i++) { -			if (i % 2) { -			    value <<= 4; -			} else { -			    value = *src++; -			} -			*dest++ = hexdigit[(value >> 4) & 0xf]; +	    } else { +		for (i = 0; i < count; i++) { +		    if (i % 8) { +			value <<= 1; +		    } else { +			value = *src++;  		    } +		    *dest++ = (char) ((value & 0x80) ? '1' : '0');  		} +	    } -		resultPtr = Tcl_ObjSetVar2(interp, objv[arg], NULL, valuePtr, -			TCL_LEAVE_ERR_MSG); -		arg++; -		if (resultPtr == NULL) { -		    DeleteScanNumberCache(numberCachePtr); -		    return TCL_ERROR; -		} -		offset += (count + 1) / 2; -		break; +	    resultPtr = Tcl_ObjSetVar2(interp, objv[arg], NULL, valuePtr, +		    TCL_LEAVE_ERR_MSG); +	    arg++; +	    if (resultPtr == NULL) { +		DeleteScanNumberCache(numberCachePtr); +		return TCL_ERROR;  	    } -	    case 'c': -		size = 1; -		goto scanNumber; -	    case 't': -	    case 's': -	    case 'S': -		size = 2; -		goto scanNumber; -	    case 'n': -	    case 'i': -	    case 'I': -		size = 4; -		goto scanNumber; -	    case 'm': -	    case 'w': -	    case 'W': -		size = 8; -		goto scanNumber; -	    case 'r': -	    case 'R': -	    case 'f': -		size = sizeof(float); -		goto scanNumber; -	    case 'q': -	    case 'Q': -	    case 'd': { -		unsigned char *src; - -		size = sizeof(double); -		/* fall through */ - -	    scanNumber: -		if (arg >= objc) { -		    DeleteScanNumberCache(numberCachePtr); -		    goto badIndex; -		} -		if (count == BINARY_NOCOUNT) { -		    if ((length - offset) < size) { -			goto done; -		    } -		    valuePtr = ScanNumber(buffer+offset, cmd, &numberCachePtr); -		    offset += size; -		} else { -		    if (count == BINARY_ALL) { -			count = (length - offset) / size; -		    } -		    if ((length - offset) < (count * size)) { -			goto done; -		    } -		    valuePtr = Tcl_NewObj(); -		    src = buffer+offset; -		    for (i = 0; i < count; i++) { -			elementPtr = ScanNumber(src, cmd, &numberCachePtr); -			src += size; -			Tcl_ListObjAppendElement(NULL, valuePtr, elementPtr); -		    } -		    offset += count*size; -		} +	    offset += (count + 7) / 8; +	    break; +	} +	case 'h': +	case 'H': { +	    char *dest; +	    unsigned char *src; +	    static const char hexdigit[] = "0123456789abcdef"; -		resultPtr = Tcl_ObjSetVar2(interp, objv[arg], NULL, valuePtr, -			TCL_LEAVE_ERR_MSG); -		arg++; -		if (resultPtr == NULL) { -		    DeleteScanNumberCache(numberCachePtr); -		    return TCL_ERROR; -		} -		break; +	    if (arg >= objc) { +		DeleteScanNumberCache(numberCachePtr); +		goto badIndex;  	    } -	    case 'x': +	    if (count == BINARY_ALL) { +		count = (length - offset)*2; +	    } else {  		if (count == BINARY_NOCOUNT) {  		    count = 1;  		} -		if ((count == BINARY_ALL) || (count > (length - offset))) { -		    offset = length; -		} else { -		    offset += count; +		if (count > (length - offset)*2) { +		    goto done;  		} -		break; -	    case 'X': -		if (count == BINARY_NOCOUNT) { -		    count = 1; +	    } +	    src = buffer + offset; +	    valuePtr = Tcl_NewObj(); +	    Tcl_SetObjLength(valuePtr, count); +	    dest = TclGetString(valuePtr); + +	    if (cmd == 'h') { +		for (i = 0; i < count; i++) { +		    if (i % 2) { +			value >>= 4; +		    } else { +			value = *src++; +		    } +		    *dest++ = hexdigit[value & 0xf];  		} -		if ((count == BINARY_ALL) || (count > offset)) { -		    offset = 0; -		} else { -		    offset -= count; +	    } else { +		for (i = 0; i < count; i++) { +		    if (i % 2) { +			value <<= 4; +		    } else { +			value = *src++; +		    } +		    *dest++ = hexdigit[(value >> 4) & 0xf];  		} -		break; -	    case '@': -		if (count == BINARY_NOCOUNT) { -		    DeleteScanNumberCache(numberCachePtr); -		    goto badCount; +	    } + +	    resultPtr = Tcl_ObjSetVar2(interp, objv[arg], NULL, valuePtr, +		    TCL_LEAVE_ERR_MSG); +	    arg++; +	    if (resultPtr == NULL) { +		DeleteScanNumberCache(numberCachePtr); +		return TCL_ERROR; +	    } +	    offset += (count + 1) / 2; +	    break; +	} +	case 'c': +	    size = 1; +	    goto scanNumber; +	case 't': +	case 's': +	case 'S': +	    size = 2; +	    goto scanNumber; +	case 'n': +	case 'i': +	case 'I': +	    size = 4; +	    goto scanNumber; +	case 'm': +	case 'w': +	case 'W': +	    size = 8; +	    goto scanNumber; +	case 'r': +	case 'R': +	case 'f': +	    size = sizeof(float); +	    goto scanNumber; +	case 'q': +	case 'Q': +	case 'd': { +	    unsigned char *src; + +	    size = sizeof(double); +	    /* fall through */ + +	scanNumber: +	    if (arg >= objc) { +		DeleteScanNumberCache(numberCachePtr); +		goto badIndex; +	    } +	    if (count == BINARY_NOCOUNT) { +		if ((length - offset) < size) { +		    goto done;  		} -		if ((count == BINARY_ALL) || (count > length)) { -		    offset = length; -		} else { -		    offset = count; +		valuePtr = ScanNumber(buffer+offset, cmd, flags, +			&numberCachePtr); +		offset += size; +	    } else { +		if (count == BINARY_ALL) { +		    count = (length - offset) / size;  		} -		break; -	    default: +		if ((length - offset) < (count * size)) { +		    goto done; +		} +		valuePtr = Tcl_NewObj(); +		src = buffer + offset; +		for (i = 0; i < count; i++) { +		    elementPtr = ScanNumber(src, cmd, flags, &numberCachePtr); +		    src += size; +		    Tcl_ListObjAppendElement(NULL, valuePtr, elementPtr); +		} +		offset += count * size; +	    } + +	    resultPtr = Tcl_ObjSetVar2(interp, objv[arg], NULL, valuePtr, +		    TCL_LEAVE_ERR_MSG); +	    arg++; +	    if (resultPtr == NULL) {  		DeleteScanNumberCache(numberCachePtr); -		errorString = str; -		goto badField; +		return TCL_ERROR;  	    } +	    break;  	} +	case 'x': +	    if (count == BINARY_NOCOUNT) { +		count = 1; +	    } +	    if ((count == BINARY_ALL) || (count > (length - offset))) { +		offset = length; +	    } else { +		offset += count; +	    } +	    break; +	case 'X': +	    if (count == BINARY_NOCOUNT) { +		count = 1; +	    } +	    if ((count == BINARY_ALL) || (count > offset)) { +		offset = 0; +	    } else { +		offset -= count; +	    } +	    break; +	case '@': +	    if (count == BINARY_NOCOUNT) { +		DeleteScanNumberCache(numberCachePtr); +		goto badCount; +	    } +	    if ((count == BINARY_ALL) || (count > length)) { +		offset = length; +	    } else { +		offset = count; +	    } +	    break; +	default: +	    DeleteScanNumberCache(numberCachePtr); +	    errorString = str; +	    goto badField; +	} +    } -	/* -	 * Set the result to the last position of the cursor. -	 */ +    /* +     * Set the result to the last position of the cursor. +     */ -    done: -	Tcl_SetObjResult(interp, Tcl_NewLongObj(arg - 4)); -	DeleteScanNumberCache(numberCachePtr); -	break; -    } -    } -    return TCL_OK; + done: +    Tcl_SetObjResult(interp, Tcl_NewLongObj(arg - 3)); +    DeleteScanNumberCache(numberCachePtr); -  badValue: -    Tcl_ResetResult(interp); -    Tcl_AppendResult(interp, "expected ", errorString, -	    " string but got \"", errorValue, "\" instead", NULL); -    return TCL_ERROR; +    return TCL_OK; -  badCount: + badCount:      errorString = "missing count for \"@\" field specifier";      goto error; -  badIndex: + badIndex:      errorString = "not enough arguments for all format specifiers";      goto error; -  badField: + badField:      {  	Tcl_UniChar ch;  	char buf[TCL_UTF_MAX + 1];  	Tcl_UtfToUniChar(errorString, &ch);  	buf[Tcl_UniCharToUtf(ch, buf)] = '\0'; -	Tcl_AppendResult(interp, "bad field specifier \"", buf, "\"", NULL); +	Tcl_SetObjResult(interp, Tcl_ObjPrintf( +		"bad field specifier \"%s\"", buf));  	return TCL_ERROR;      } -  error: -    Tcl_AppendResult(interp, errorString, NULL); + error: +    Tcl_SetObjResult(interp, Tcl_NewStringObj(errorString, -1));      return TCL_ERROR;  } @@ -1371,9 +1708,10 @@ Tcl_BinaryObjCmd(  static int  GetFormatSpec( -    char **formatPtr,		/* Pointer to format string. */ +    const char **formatPtr,	/* Pointer to format string. */      char *cmdPtr,		/* Pointer to location of command char. */ -    int *countPtr)		/* Pointer to repeat count value. */ +    int *countPtr,		/* Pointer to repeat count value. */ +    int *flagsPtr)		/* Pointer to field flags */  {      /*       * Skip any leading blanks. @@ -1397,13 +1735,17 @@ GetFormatSpec(      *cmdPtr = **formatPtr;      (*formatPtr)++; +    if (**formatPtr == 'u') { +	(*formatPtr)++; +	*flagsPtr |= BINARY_UNSIGNED; +    }      if (**formatPtr == '*') {  	(*formatPtr)++; -	(*countPtr) = BINARY_ALL; +	*countPtr = BINARY_ALL;      } else if (isdigit(UCHAR(**formatPtr))) { /* INTL: digit */ -	(*countPtr) = strtoul(*formatPtr, formatPtr, 10); +	*countPtr = strtoul(*formatPtr, (char **) formatPtr, 10);      } else { -	(*countPtr) = BINARY_NOCOUNT; +	*countPtr = BINARY_NOCOUNT;      }      return 1;  } @@ -1413,7 +1755,8 @@ GetFormatSpec(   *   * NeedReversing --   * - *	This routine determines, if bytes of a number need to be reversed. + *	This routine determines, if bytes of a number need to be re-ordered, + *	and returns a numeric code indicating the re-ordering to be done.   *	This depends on the endiannes of the machine and the desired format.   *	It is in effect a table (whose contents depend on the endianness of   *	the system) describing whether a value needs reversing or not. Anyone @@ -1423,7 +1766,10 @@ GetFormatSpec(   *	Windows) don't need to do anything.   *   * Results: - *	1 if reversion is required, 0 if not. + *	0	No re-ordering needed. + *	1	Reverse the bytes:	01234567 <-> 76543210 (little to big) + *	2	Apply this re-ordering: 01234567 <-> 45670123 (Nokia to little) + *	3	Apply this re-ordering: 01234567 <-> 32107654 (Nokia to big)   *   * Side effects:   *	None @@ -1448,12 +1794,11 @@ NeedReversing(      case 'n':      case 't':      case 'm': -	/* f+d: reverse if we're little-endian */ +	/* f: reverse if we're little-endian */      case 'Q':      case 'R':  #else /* !WORDS_BIGENDIAN */  	/* small endian floats: reverse if we're big-endian */ -    case 'q':      case 'r':  #endif /* WORDS_BIGENDIAN */  	return 0; @@ -1467,8 +1812,7 @@ NeedReversing(      case 'n':      case 't':      case 'm': -	/* f+d: reverse if we're little-endian */ -    case 'Q': +	/* f: reverse if we're little-endian */      case 'R':  #endif /* WORDS_BIGENDIAN */  	/* small endian ints: always reverse */ @@ -1476,9 +1820,28 @@ NeedReversing(      case 's':      case 'w':  	return 1; + +#ifndef WORDS_BIGENDIAN +    /* +     * The Q and q formats need special handling to account for the unusual +     * byte ordering of 8-byte floats on Nokia 770 systems, which claim to be +     * little-endian, but also reverse word order. +     */ + +    case 'Q': +	if (TclNokia770Doubles()) { +	    return 3; +	} +	return 1; +    case 'q': +	if (TclNokia770Doubles()) { +	    return 2; +	} +	return 0; +#endif      } -    Tcl_Panic("unexpected fall-through"); +    Tcl_Panic("unexpected fallthrough");      return 0;  } @@ -1503,14 +1866,18 @@ NeedReversing(  static void  CopyNumber( -    CONST void *from,		/* source */ +    const void *from,		/* source */      void *to,			/* destination */ -    unsigned int length,	/* Number of bytes to copy */ +    unsigned length,		/* Number of bytes to copy */      int type)			/* What type of thing are we copying? */  { -    if (NeedReversing(type)) { -	CONST unsigned char *fromPtr = (CONST unsigned char *) from; -	unsigned char *toPtr = (unsigned char *) to; +    switch (NeedReversing(type)) { +    case 0: +	memcpy(to, from, length); +	break; +    case 1: { +	const unsigned char *fromPtr = from; +	unsigned char *toPtr = to;  	switch (length) {  	case 4: @@ -1530,8 +1897,36 @@ CopyNumber(  	    toPtr[7] = fromPtr[0];  	    break;  	} -    } else { -	memcpy(to, from, length); +	break; +    } +    case 2: { +	const unsigned char *fromPtr = from; +	unsigned char *toPtr = to; + +	toPtr[0] = fromPtr[4]; +	toPtr[1] = fromPtr[5]; +	toPtr[2] = fromPtr[6]; +	toPtr[3] = fromPtr[7]; +	toPtr[4] = fromPtr[0]; +	toPtr[5] = fromPtr[1]; +	toPtr[6] = fromPtr[2]; +	toPtr[7] = fromPtr[3]; +	break; +    } +    case 3: { +	const unsigned char *fromPtr = from; +	unsigned char *toPtr = to; + +	toPtr[0] = fromPtr[3]; +	toPtr[1] = fromPtr[2]; +	toPtr[2] = fromPtr[1]; +	toPtr[3] = fromPtr[0]; +	toPtr[4] = fromPtr[7]; +	toPtr[5] = fromPtr[6]; +	toPtr[6] = fromPtr[5]; +	toPtr[7] = fromPtr[4]; +	break; +    }      }  } @@ -1576,7 +1971,7 @@ FormatNumber(  	 */  	if (Tcl_GetDoubleFromObj(interp, src, &dvalue) != TCL_OK) { -	    if ( src->typePtr != &tclDoubleType ) { +	    if (src->typePtr != &tclDoubleType) {  		return TCL_ERROR;  	    }  	    dvalue = src->internalRep.doubleValue; @@ -1595,7 +1990,7 @@ FormatNumber(  	 */  	if (Tcl_GetDoubleFromObj(interp, src, &dvalue) != TCL_OK) { -	    if ( src->typePtr != &tclDoubleType ) { +	    if (src->typePtr != &tclDoubleType) {  		return TCL_ERROR;  	    }  	    dvalue = src->internalRep.doubleValue; @@ -1626,23 +2021,23 @@ FormatNumber(  	    return TCL_ERROR;  	}  	if (NeedReversing(type)) { -	    *(*cursorPtr)++ = (unsigned char) wvalue; -	    *(*cursorPtr)++ = (unsigned char) (wvalue >> 8); -	    *(*cursorPtr)++ = (unsigned char) (wvalue >> 16); -	    *(*cursorPtr)++ = (unsigned char) (wvalue >> 24); -	    *(*cursorPtr)++ = (unsigned char) (wvalue >> 32); -	    *(*cursorPtr)++ = (unsigned char) (wvalue >> 40); -	    *(*cursorPtr)++ = (unsigned char) (wvalue >> 48); -	    *(*cursorPtr)++ = (unsigned char) (wvalue >> 56); +	    *(*cursorPtr)++ = UCHAR(wvalue); +	    *(*cursorPtr)++ = UCHAR(wvalue >> 8); +	    *(*cursorPtr)++ = UCHAR(wvalue >> 16); +	    *(*cursorPtr)++ = UCHAR(wvalue >> 24); +	    *(*cursorPtr)++ = UCHAR(wvalue >> 32); +	    *(*cursorPtr)++ = UCHAR(wvalue >> 40); +	    *(*cursorPtr)++ = UCHAR(wvalue >> 48); +	    *(*cursorPtr)++ = UCHAR(wvalue >> 56);  	} else { -	    *(*cursorPtr)++ = (unsigned char) (wvalue >> 56); -	    *(*cursorPtr)++ = (unsigned char) (wvalue >> 48); -	    *(*cursorPtr)++ = (unsigned char) (wvalue >> 40); -	    *(*cursorPtr)++ = (unsigned char) (wvalue >> 32); -	    *(*cursorPtr)++ = (unsigned char) (wvalue >> 24); -	    *(*cursorPtr)++ = (unsigned char) (wvalue >> 16); -	    *(*cursorPtr)++ = (unsigned char) (wvalue >> 8); -	    *(*cursorPtr)++ = (unsigned char) wvalue; +	    *(*cursorPtr)++ = UCHAR(wvalue >> 56); +	    *(*cursorPtr)++ = UCHAR(wvalue >> 48); +	    *(*cursorPtr)++ = UCHAR(wvalue >> 40); +	    *(*cursorPtr)++ = UCHAR(wvalue >> 32); +	    *(*cursorPtr)++ = UCHAR(wvalue >> 24); +	    *(*cursorPtr)++ = UCHAR(wvalue >> 16); +	    *(*cursorPtr)++ = UCHAR(wvalue >> 8); +	    *(*cursorPtr)++ = UCHAR(wvalue);  	}  	return TCL_OK; @@ -1652,19 +2047,19 @@ FormatNumber(      case 'i':      case 'I':      case 'n': -	if (Tcl_GetLongFromObj(interp, src, &value) != TCL_OK) { +	if (TclGetLongFromObj(interp, src, &value) != TCL_OK) {  	    return TCL_ERROR;  	}  	if (NeedReversing(type)) { -	    *(*cursorPtr)++ = (unsigned char) value; -	    *(*cursorPtr)++ = (unsigned char) (value >> 8); -	    *(*cursorPtr)++ = (unsigned char) (value >> 16); -	    *(*cursorPtr)++ = (unsigned char) (value >> 24); +	    *(*cursorPtr)++ = UCHAR(value); +	    *(*cursorPtr)++ = UCHAR(value >> 8); +	    *(*cursorPtr)++ = UCHAR(value >> 16); +	    *(*cursorPtr)++ = UCHAR(value >> 24);  	} else { -	    *(*cursorPtr)++ = (unsigned char) (value >> 24); -	    *(*cursorPtr)++ = (unsigned char) (value >> 16); -	    *(*cursorPtr)++ = (unsigned char) (value >> 8); -	    *(*cursorPtr)++ = (unsigned char) value; +	    *(*cursorPtr)++ = UCHAR(value >> 24); +	    *(*cursorPtr)++ = UCHAR(value >> 16); +	    *(*cursorPtr)++ = UCHAR(value >> 8); +	    *(*cursorPtr)++ = UCHAR(value);  	}  	return TCL_OK; @@ -1674,15 +2069,15 @@ FormatNumber(      case 's':      case 'S':      case 't': -	if (Tcl_GetLongFromObj(interp, src, &value) != TCL_OK) { +	if (TclGetLongFromObj(interp, src, &value) != TCL_OK) {  	    return TCL_ERROR;  	}  	if (NeedReversing(type)) { -	    *(*cursorPtr)++ = (unsigned char) value; -	    *(*cursorPtr)++ = (unsigned char) (value >> 8); +	    *(*cursorPtr)++ = UCHAR(value); +	    *(*cursorPtr)++ = UCHAR(value >> 8);  	} else { -	    *(*cursorPtr)++ = (unsigned char) (value >> 8); -	    *(*cursorPtr)++ = (unsigned char) value; +	    *(*cursorPtr)++ = UCHAR(value >> 8); +	    *(*cursorPtr)++ = UCHAR(value);  	}  	return TCL_OK; @@ -1690,10 +2085,10 @@ FormatNumber(  	 * 8-bit integer values.  	 */      case 'c': -	if (Tcl_GetLongFromObj(interp, src, &value) != TCL_OK) { +	if (TclGetLongFromObj(interp, src, &value) != TCL_OK) {  	    return TCL_ERROR;  	} -	*(*cursorPtr)++ = (unsigned char) value; +	*(*cursorPtr)++ = UCHAR(value);  	return TCL_OK;      default: @@ -1726,6 +2121,7 @@ static Tcl_Obj *  ScanNumber(      unsigned char *buffer,	/* Buffer to scan number from. */      int type,			/* Format character from "binary scan" */ +    int flags,			/* Format field flags */      Tcl_HashTable **numberCachePtrPtr)  				/* Place to look for cache of scanned  				 * value objects, or NULL if too many @@ -1741,7 +2137,7 @@ ScanNumber(       * when we cast from smaller values to larger values because we don't know       * the exact size of the integer types. So, we have to handle sign       * extension explicitly by checking the high bit and padding with 1's as -     * needed. +     * needed. This practice is disabled if the BINARY_UNSIGNED flag is set.       */      switch (type) { @@ -1754,8 +2150,10 @@ ScanNumber(  	 */  	value = buffer[0]; -	if (value & 0x80) { -	    value |= -0x100; +	if (!(flags & BINARY_UNSIGNED)) { +	    if (value & 0x80) { +		value |= -0x100; +	    }  	}  	goto returnNumericObject; @@ -1772,8 +2170,10 @@ ScanNumber(  	} else {  	    value = (long) (buffer[1] + (buffer[0] << 8));  	} -	if (value & 0x8000) { -	    value |= -0x10000; +	if (!(flags & BINARY_UNSIGNED)) { +	    if (value & 0x8000) { +		value |= -0x10000; +	    }  	}  	goto returnNumericObject; @@ -1788,26 +2188,30 @@ ScanNumber(  	    value = (long) (buffer[0]  		    + (buffer[1] << 8)  		    + (buffer[2] << 16) -		    + (buffer[3] << 24)); +		    + (((long)buffer[3]) << 24));  	} else {  	    value = (long) (buffer[3]  		    + (buffer[2] << 8)  		    + (buffer[1] << 16) -		    + (buffer[0] << 24)); +		    + (((long) buffer[0]) << 24));  	}  	/*  	 * Check to see if the value was sign extended properly on systems  	 * where an int is more than 32-bits. +	 * We avoid caching unsigned integers as we cannot distinguish between +	 * 32bit signed and unsigned in the hash (short and char are ok).  	 */ -	if ((value & (((unsigned int)1)<<31)) && (value > 0)) { -	    value -= (((unsigned int)1)<<31); -	    value -= (((unsigned int)1)<<31); +	if (flags & BINARY_UNSIGNED) { +	    return Tcl_NewWideIntObj((Tcl_WideInt)(unsigned long)value); +	} +	if ((value & (((unsigned) 1)<<31)) && (value > 0)) { +	    value -= (((unsigned) 1)<<31); +	    value -= (((unsigned) 1)<<31);  	}      returnNumericObject: -  	if (*numberCachePtrPtr == NULL) {  	    return Tcl_NewLongObj(value);  	} else { @@ -1815,30 +2219,30 @@ ScanNumber(  	    register Tcl_HashEntry *hPtr;  	    int isNew; -	    hPtr = Tcl_CreateHashEntry(tablePtr, (char *)value, &isNew); +	    hPtr = Tcl_CreateHashEntry(tablePtr, INT2PTR(value), &isNew);  	    if (!isNew) { -		return (Tcl_Obj *) Tcl_GetHashValue(hPtr); +		return Tcl_GetHashValue(hPtr);  	    } -	    if (tablePtr->numEntries > BINARY_SCAN_MAX_CACHE) { -		/* -		 * We've overflowed the cache! Someone's parsing a LOT of -		 * varied binary data in a single call! Bail out by switching -		 * back to the old behaviour for the rest of the scan. -		 * -		 * Note that anyone just using the 'c' conversion (for bytes) -		 * cannot trigger this. -		 */ - -		DeleteScanNumberCache(tablePtr); -		*numberCachePtrPtr = NULL; -		return Tcl_NewLongObj(value); -	    } else { +	    if (tablePtr->numEntries <= BINARY_SCAN_MAX_CACHE) {  		register Tcl_Obj *objPtr = Tcl_NewLongObj(value);  		Tcl_IncrRefCount(objPtr); -		Tcl_SetHashValue(hPtr, (ClientData) objPtr); +		Tcl_SetHashValue(hPtr, objPtr);  		return objPtr;  	    } + +	    /* +	     * We've overflowed the cache! Someone's parsing a LOT of varied +	     * binary data in a single call! Bail out by switching back to the +	     * old behaviour for the rest of the scan. +	     * +	     * Note that anyone just using the 'c' conversion (for bytes) +	     * cannot trigger this. +	     */ + +	    DeleteScanNumberCache(tablePtr); +	    *numberCachePtrPtr = NULL; +	    return Tcl_NewLongObj(value);  	}  	/* @@ -1868,6 +2272,14 @@ ScanNumber(  		    | (((Tcl_WideUInt) buffer[1]) << 48)  		    | (((Tcl_WideUInt) buffer[0]) << 56);  	} +	if (flags & BINARY_UNSIGNED) { +	    Tcl_Obj *bigObj = NULL; +	    mp_int big; + +	    TclBNInitBignumFromWideUInt(&big, uwvalue); +	    bigObj = Tcl_NewBignumObj(&big); +	    return bigObj; +	}  	return Tcl_NewWideIntObj((Tcl_WideInt) uwvalue);  	/* @@ -1931,7 +2343,7 @@ DeleteScanNumberCache(      hEntry = Tcl_FirstHashEntry(numberCachePtr, &search);      while (hEntry != NULL) { -	register Tcl_Obj *value = (Tcl_Obj *) Tcl_GetHashValue(hEntry); +	register Tcl_Obj *value = Tcl_GetHashValue(hEntry);  	if (value != NULL) {  	    Tcl_DecrRefCount(value); @@ -1942,9 +2354,725 @@ DeleteScanNumberCache(  }  /* + * ---------------------------------------------------------------------- + * + * NOTES -- + * + *	Some measurements show that it is faster to use a table to to perform + *	uuencode and base64 value encoding than to calculate the output (at + *	least on intel P4 arch). + * + *	Conversely using a lookup table for the decoding is slower than just + *	calculating the values. We therefore use the fastest of each method. + * + *	Presumably this has to do with the size of the tables. The base64 + *	decode table is 255 bytes while the encode table is only 65 bytes. The + *	choice likely depends on CPU memory cache sizes. + */ + +/* + *---------------------------------------------------------------------- + * + * BinaryEncodeHex -- + * + *	Implement the [binary encode hex] binary encoding. clientData must be + *	a table to convert values to hexadecimal digits. + * + * Results: + *	Interp result set to an encoded byte array object + * + * Side effects: + *	None + * + *---------------------------------------------------------------------- + */ + +static int +BinaryEncodeHex( +    ClientData clientData, +    Tcl_Interp *interp, +    int objc, +    Tcl_Obj *const objv[]) +{ +    Tcl_Obj *resultObj = NULL; +    unsigned char *data = NULL; +    unsigned char *cursor = NULL; +    int offset = 0, count = 0; + +    if (objc != 2) { +	Tcl_WrongNumArgs(interp, 1, objv, "data"); +	return TCL_ERROR; +    } + +    TclNewObj(resultObj); +    data = Tcl_GetByteArrayFromObj(objv[1], &count); +    cursor = Tcl_SetByteArrayLength(resultObj, count * 2); +    for (offset = 0; offset < count; ++offset) { +	*cursor++ = HexDigits[((data[offset] >> 4) & 0x0f)]; +	*cursor++ = HexDigits[(data[offset] & 0x0f)]; +    } +    Tcl_SetObjResult(interp, resultObj); +    return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * BinaryDecodeHex -- + * + *	Implement the [binary decode hex] binary encoding. + * + * Results: + *	Interp result set to an decoded byte array object + * + * Side effects: + *	None + * + *---------------------------------------------------------------------- + */ + +static int +BinaryDecodeHex( +    ClientData clientData, +    Tcl_Interp *interp, +    int objc, +    Tcl_Obj *const objv[]) +{ +    Tcl_Obj *resultObj = NULL; +    unsigned char *data, *datastart, *dataend; +    unsigned char *begin, *cursor, c; +    int i, index, value, size, count = 0, cut = 0, strict = 0; +    enum {OPT_STRICT }; +    static const char *const optStrings[] = { "-strict", NULL }; + +    if (objc < 2 || objc > 3) { +	Tcl_WrongNumArgs(interp, 1, objv, "?options? data"); +	return TCL_ERROR; +    } +    for (i = 1; i < objc-1; ++i) { +	if (Tcl_GetIndexFromObj(interp, objv[i], optStrings, "option", +		TCL_EXACT, &index) != TCL_OK) { +	    return TCL_ERROR; +	} +	switch (index) { +	case OPT_STRICT: +	    strict = 1; +	    break; +	} +    } + +    TclNewObj(resultObj); +    datastart = data = (unsigned char *) +	    TclGetStringFromObj(objv[objc-1], &count); +    dataend = data + count; +    size = (count + 1) / 2; +    begin = cursor = Tcl_SetByteArrayLength(resultObj, size); +    while (data < dataend) { +	value = 0; +	for (i=0 ; i<2 ; i++) { +	    if (data >= dataend) { +		value <<= 4; +		break; +	    } + +	    c = *data++; +	    if (!isxdigit((int) c)) { +		if (strict || !isspace(c)) { +		    goto badChar; +		} +		i--; +		continue; +	    } + +	    value <<= 4; +	    c -= '0'; +	    if (c > 9) { +		c += ('0' - 'A') + 10; +	    } +	    if (c > 16) { +		c += ('A' - 'a'); +	    } +	    value |= (c & 0xf); +	} +	if (i < 2) { +	    cut++; +	} +	*cursor++ = UCHAR(value); +	value = 0; +    } +    if (cut > size) { +	cut = size; +    } +    Tcl_SetByteArrayLength(resultObj, cursor - begin - cut); +    Tcl_SetObjResult(interp, resultObj); +    return TCL_OK; + +  badChar: +    TclDecrRefCount(resultObj); +    Tcl_SetObjResult(interp, Tcl_ObjPrintf( +	    "invalid hexadecimal digit \"%c\" at position %d", +	    c, (int) (data - datastart - 1))); +    return TCL_ERROR; +} + +/* + *---------------------------------------------------------------------- + * + * BinaryEncode64 -- + * + *	This implements a generic 6 bit binary encoding. Input is broken into + *	6 bit chunks and a lookup table passed in via clientData is used to + *	turn these values into output characters. This is used to implement + *	base64 binary encodings. + * + * Results: + *	Interp result set to an encoded byte array object + * + * Side effects: + *	None + * + *---------------------------------------------------------------------- + */ + +#define OUTPUT(c) \ +    do {						\ +	*cursor++ = (c);				\ +	outindex++;					\ +	if (maxlen > 0 && cursor != limit) {		\ +	    if (outindex == maxlen) {			\ +		memcpy(cursor, wrapchar, wrapcharlen);	\ +		cursor += wrapcharlen;			\ +		outindex = 0;				\ +	    }						\ +	}						\ +	if (cursor > limit) {				\ +	    Tcl_Panic("limit hit");			\ +	}						\ +    } while (0) + +static int +BinaryEncode64( +    ClientData clientData, +    Tcl_Interp *interp, +    int objc, +    Tcl_Obj *const objv[]) +{ +    Tcl_Obj *resultObj; +    unsigned char *data, *cursor, *limit; +    int maxlen = 0; +    const char *wrapchar = "\n"; +    int wrapcharlen = 1; +    int offset, i, index, size, outindex = 0, count = 0; +    enum {OPT_MAXLEN, OPT_WRAPCHAR }; +    static const char *const optStrings[] = { "-maxlen", "-wrapchar", NULL }; + +    if (objc < 2 || objc%2 != 0) { +	Tcl_WrongNumArgs(interp, 1, objv, +		"?-maxlen len? ?-wrapchar char? data"); +	return TCL_ERROR; +    } +    for (i = 1; i < objc-1; i += 2) { +	if (Tcl_GetIndexFromObj(interp, objv[i], optStrings, "option", +		TCL_EXACT, &index) != TCL_OK) { +	    return TCL_ERROR; +	} +	switch (index) { +	case OPT_MAXLEN: +	    if (Tcl_GetIntFromObj(interp, objv[i+1], &maxlen) != TCL_OK) { +		return TCL_ERROR; +	    } +	    if (maxlen < 0) { +		Tcl_SetObjResult(interp, Tcl_NewStringObj( +			"line length out of range", -1)); +		Tcl_SetErrorCode(interp, "TCL", "BINARY", "ENCODE", +			"LINE_LENGTH", NULL); +		return TCL_ERROR; +	    } +	    break; +	case OPT_WRAPCHAR: +	    wrapchar = TclGetStringFromObj(objv[i+1], &wrapcharlen); +	    if (wrapcharlen == 0) { +		maxlen = 0; +	    } +	    break; +	} +    } + +    resultObj = Tcl_NewObj(); +    data = Tcl_GetByteArrayFromObj(objv[objc-1], &count); +    if (count > 0) { +	size = (((count * 4) / 3) + 3) & ~3; /* ensure 4 byte chunks */ +	if (maxlen > 0 && size > maxlen) { +	    int adjusted = size + (wrapcharlen * (size / maxlen)); + +	    if (size % maxlen == 0) { +		adjusted -= wrapcharlen; +	    } +	    size = adjusted; +	} +	cursor = Tcl_SetByteArrayLength(resultObj, size); +	limit = cursor + size; +	for (offset = 0; offset < count; offset+=3) { +	    unsigned char d[3] = {0, 0, 0}; + +	    for (i = 0; i < 3 && offset+i < count; ++i) { +		d[i] = data[offset + i]; +	    } +	    OUTPUT(B64Digits[d[0] >> 2]); +	    OUTPUT(B64Digits[((d[0] & 0x03) << 4) | (d[1] >> 4)]); +	    if (offset+1 < count) { +		OUTPUT(B64Digits[((d[1] & 0x0f) << 2) | (d[2] >> 6)]); +	    } else { +		OUTPUT(B64Digits[64]); +	    } +	    if (offset+2 < count) { +		OUTPUT(B64Digits[d[2] & 0x3f]); +	    } else { +		OUTPUT(B64Digits[64]); +	    } +	} +    } +    Tcl_SetObjResult(interp, resultObj); +    return TCL_OK; +} +#undef OUTPUT + +/* + *---------------------------------------------------------------------- + * + * BinaryEncodeUu -- + * + *	This implements the uuencode binary encoding. Input is broken into 6 + *	bit chunks and a lookup table is used to turn these values into output + *	characters. This differs from the generic code above in that line + *	lengths are also encoded. + * + * Results: + *	Interp result set to an encoded byte array object + * + * Side effects: + *	None + * + *---------------------------------------------------------------------- + */ + +static int +BinaryEncodeUu( +    ClientData clientData, +    Tcl_Interp *interp, +    int objc, +    Tcl_Obj *const objv[]) +{ +    Tcl_Obj *resultObj; +    unsigned char *data, *start, *cursor; +    int offset, count, rawLength, n, i, j, bits, index; +    int lineLength = 61; +    const unsigned char SingleNewline[] = { (unsigned char) '\n' }; +    const unsigned char *wrapchar = SingleNewline; +    int wrapcharlen = sizeof(SingleNewline); +    enum { OPT_MAXLEN, OPT_WRAPCHAR }; +    static const char *const optStrings[] = { "-maxlen", "-wrapchar", NULL }; + +    if (objc < 2 || objc%2 != 0) { +	Tcl_WrongNumArgs(interp, 1, objv, +		"?-maxlen len? ?-wrapchar char? data"); +	return TCL_ERROR; +    } +    for (i = 1; i < objc-1; i += 2) { +	if (Tcl_GetIndexFromObj(interp, objv[i], optStrings, "option", +		TCL_EXACT, &index) != TCL_OK) { +	    return TCL_ERROR; +	} +	switch (index) { +	case OPT_MAXLEN: +	    if (Tcl_GetIntFromObj(interp, objv[i+1], &lineLength) != TCL_OK) { +		return TCL_ERROR; +	    } +	    if (lineLength < 3 || lineLength > 85) { +		Tcl_SetObjResult(interp, Tcl_NewStringObj( +			"line length out of range", -1)); +		Tcl_SetErrorCode(interp, "TCL", "BINARY", "ENCODE", +			"LINE_LENGTH", NULL); +		return TCL_ERROR; +	    } +	    break; +	case OPT_WRAPCHAR: +	    wrapchar = Tcl_GetByteArrayFromObj(objv[i+1], &wrapcharlen); +	    break; +	} +    } + +    /* +     * Allocate the buffer. This is a little bit too long, but is "good +     * enough". +     */ + +    resultObj = Tcl_NewObj(); +    offset = 0; +    data = Tcl_GetByteArrayFromObj(objv[objc-1], &count); +    rawLength = (lineLength - 1) * 3 / 4; +    start = cursor = Tcl_SetByteArrayLength(resultObj, +	    (lineLength + wrapcharlen) * +	    ((count + (rawLength - 1)) / rawLength)); +    n = bits = 0; + +    /* +     * Encode the data. Each output line first has the length of raw data +     * encoded by the output line described in it by one encoded byte, then +     * the encoded data follows (encoding each 6 bits as one character). +     * Encoded lines are always terminated by a newline. +     */ + +    while (offset < count) { +	int lineLen = count - offset; + +	if (lineLen > rawLength) { +	    lineLen = rawLength; +	} +	*cursor++ = UueDigits[lineLen]; +	for (i=0 ; i<lineLen ; i++) { +	    n <<= 8; +	    n |= data[offset++]; +	    for (bits += 8; bits > 6 ; bits -= 6) { +		*cursor++ = UueDigits[(n >> (bits-6)) & 0x3f]; +	    } +	} +	if (bits > 0) { +	    n <<= 8; +	    *cursor++ = UueDigits[(n >> (bits + 2)) & 0x3f]; +	    bits = 0; +	} +	for (j=0 ; j<wrapcharlen ; ++j) { +	    *cursor++ = wrapchar[j]; +	} +    } + +    /* +     * Fix the length of the output bytearray. +     */ + +    Tcl_SetByteArrayLength(resultObj, cursor-start); +    Tcl_SetObjResult(interp, resultObj); +    return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * BinaryDecodeUu -- + * + *	Decode a uuencoded string. + * + * Results: + *	Interp result set to an byte array object + * + * Side effects: + *	None + * + *---------------------------------------------------------------------- + */ + +static int +BinaryDecodeUu( +    ClientData clientData, +    Tcl_Interp *interp, +    int objc, +    Tcl_Obj *const objv[]) +{ +    Tcl_Obj *resultObj = NULL; +    unsigned char *data, *datastart, *dataend; +    unsigned char *begin, *cursor; +    int i, index, size, count = 0, strict = 0, lineLen; +    unsigned char c; +    enum {OPT_STRICT }; +    static const char *const optStrings[] = { "-strict", NULL }; + +    if (objc < 2 || objc > 3) { +	Tcl_WrongNumArgs(interp, 1, objv, "?options? data"); +	return TCL_ERROR; +    } +    for (i = 1; i < objc-1; ++i) { +	if (Tcl_GetIndexFromObj(interp, objv[i], optStrings, "option", +		TCL_EXACT, &index) != TCL_OK) { +	    return TCL_ERROR; +	} +	switch (index) { +	case OPT_STRICT: +	    strict = 1; +	    break; +	} +    } + +    TclNewObj(resultObj); +    datastart = data = (unsigned char *) +	    TclGetStringFromObj(objv[objc-1], &count); +    dataend = data + count; +    size = ((count + 3) & ~3) * 3 / 4; +    begin = cursor = Tcl_SetByteArrayLength(resultObj, size); +    lineLen = -1; + +    /* +     * The decoding loop. First, we get the length of line (strictly, the +     * number of data bytes we expect to generate from the line) we're +     * processing this time round if it is not already known (i.e., when the +     * lineLen variable is set to the magic value, -1). +     */ + +    while (data < dataend) { +	char d[4] = {0, 0, 0, 0}; + +	if (lineLen < 0) { +	    c = *data++; +	    if (c < 32 || c > 96) { +		if (strict || !isspace(c)) { +		    goto badUu; +		} +		i--; +		continue; +	    } +	    lineLen = (c - 32) & 0x3f; +	} + +	/* +	 * Now we read a four-character grouping. +	 */ + +	for (i=0 ; i<4 ; i++) { +	    if (data < dataend) { +		d[i] = c = *data++; +		if (c < 32 || c > 96) { +		    if (strict) { +			if (!isspace(c)) { +			    goto badUu; +			} else if (c == '\n') { +			    goto shortUu; +			} +		    } +		    i--; +		    continue; +		} +	    } +	} + +	/* +	 * Translate that grouping into (up to) three binary bytes output. +	 */ + +	if (lineLen > 0) { +	    *cursor++ = (((d[0] - 0x20) & 0x3f) << 2) +		    | (((d[1] - 0x20) & 0x3f) >> 4); +	    if (--lineLen > 0) { +		*cursor++ = (((d[1] - 0x20) & 0x3f) << 4) +			| (((d[2] - 0x20) & 0x3f) >> 2); +		if (--lineLen > 0) { +		    *cursor++ = (((d[2] - 0x20) & 0x3f) << 6) +			    | (((d[3] - 0x20) & 0x3f)); +		    lineLen--; +		} +	    } +	} + +	/* +	 * If we've reached the end of the line, skip until we process a +	 * newline. +	 */ + +	if (lineLen == 0 && data < dataend) { +	    lineLen = -1; +	    do { +		c = *data++; +		if (c == '\n') { +		    break; +		} else if (c >= 32 && c <= 96) { +		    data--; +		    break; +		} else if (strict || !isspace(c)) { +		    goto badUu; +		} +	    } while (data < dataend); +	} +    } + +    /* +     * Sanity check, clean up and finish. +     */ + +    if (lineLen > 0 && strict) { +	goto shortUu; +    } +    Tcl_SetByteArrayLength(resultObj, cursor - begin); +    Tcl_SetObjResult(interp, resultObj); +    return TCL_OK; + +  shortUu: +    Tcl_SetObjResult(interp, Tcl_ObjPrintf("short uuencode data")); +    Tcl_SetErrorCode(interp, "TCL", "BINARY", "DECODE", "SHORT", NULL); +    TclDecrRefCount(resultObj); +    return TCL_ERROR; + +  badUu: +    Tcl_SetObjResult(interp, Tcl_ObjPrintf( +	    "invalid uuencode character \"%c\" at position %d", +	    c, (int) (data - datastart - 1))); +    Tcl_SetErrorCode(interp, "TCL", "BINARY", "DECODE", "INVALID", NULL); +    TclDecrRefCount(resultObj); +    return TCL_ERROR; +} + +/* + *---------------------------------------------------------------------- + * + * BinaryDecode64 -- + * + *	Decode a base64 encoded string. + * + * Results: + *	Interp result set to an byte array object + * + * Side effects: + *	None + * + *---------------------------------------------------------------------- + */ + +static int +BinaryDecode64( +    ClientData clientData, +    Tcl_Interp *interp, +    int objc, +    Tcl_Obj *const objv[]) +{ +    Tcl_Obj *resultObj = NULL; +    unsigned char *data, *datastart, *dataend, c = '\0'; +    unsigned char *begin = NULL; +    unsigned char *cursor = NULL; +    int strict = 0; +    int i, index, size, cut = 0, count = 0; +    enum { OPT_STRICT }; +    static const char *const optStrings[] = { "-strict", NULL }; + +    if (objc < 2 || objc > 3) { +	Tcl_WrongNumArgs(interp, 1, objv, "?options? data"); +	return TCL_ERROR; +    } +    for (i = 1; i < objc-1; ++i) { +	if (Tcl_GetIndexFromObj(interp, objv[i], optStrings, "option", +		TCL_EXACT, &index) != TCL_OK) { +	    return TCL_ERROR; +	} +	switch (index) { +	case OPT_STRICT: +	    strict = 1; +	    break; +	} +    } + +    TclNewObj(resultObj); +    datastart = data = (unsigned char *) +	    TclGetStringFromObj(objv[objc-1], &count); +    dataend = data + count; +    size = ((count + 3) & ~3) * 3 / 4; +    begin = cursor = Tcl_SetByteArrayLength(resultObj, size); +    while (data < dataend) { +	unsigned long value = 0; + +	/* +	 * Decode the current block. Each base64 block consists of four input +	 * characters A-Z, a-z, 0-9, +, or /. Each character supplies six bits +	 * of output data, so each block's output is 24 bits (three bytes) in +	 * length. The final block can be shorter by one or two bytes, denoted +	 * by the input ending with one or two ='s, respectively. +	 */ + +	for (i = 0; i < 4; i++) { +	    /* +	     * Get the next input character. At end of input, pad with at most +	     * two ='s. If more than two ='s would be needed, instead discard +	     * the block read thus far. +	     */ + +	    if (data < dataend) { +		c = *data++; +	    } else if (i > 1) { +		c = '='; +	    } else { +		cut += 3; +		break; +	    } + +	    /* +	     * Load the character into the block value. Handle ='s specially +	     * because they're only valid as the last character or two of the +	     * final block of input. Unless strict mode is enabled, skip any +	     * input whitespace characters. +	     */ + +	    if (cut) { +		if (c == '=' && i > 1) { +		     value <<= 6; +		     cut++; +		} else if (!strict && isspace(c)) { +		     i--; +		} else { +		    goto bad64; +		} +	    } else if (c >= 'A' && c <= 'Z') { +		value = (value << 6) | ((c - 'A') & 0x3f); +	    } else if (c >= 'a' && c <= 'z') { +		value = (value << 6) | ((c - 'a' + 26) & 0x3f); +	    } else if (c >= '0' && c <= '9') { +		value = (value << 6) | ((c - '0' + 52) & 0x3f); +	    } else if (c == '+') { +		value = (value << 6) | 0x3e; +	    } else if (c == '/') { +		value = (value << 6) | 0x3f; +	    } else if (c == '=') { +		value <<= 6; +		cut++; +	    } else if (strict || !isspace(c)) { +		goto bad64; +	    } else { +		i--; +	    } +	} +	*cursor++ = UCHAR((value >> 16) & 0xff); +	*cursor++ = UCHAR((value >> 8) & 0xff); +	*cursor++ = UCHAR(value & 0xff); + +	/* +	 * Since = is only valid within the final block, if it was encountered +	 * but there are still more input characters, confirm that strict mode +	 * is off and all subsequent characters are whitespace. +	 */ + +	if (cut && data < dataend) { +	    if (strict) { +		goto bad64; +	    } +	    for (; data < dataend; data++) { +		if (!isspace(*data)) { +		    goto bad64; +		} +	    } +	} +    } +    Tcl_SetByteArrayLength(resultObj, cursor - begin - cut); +    Tcl_SetObjResult(interp, resultObj); +    return TCL_OK; + +  bad64: +    Tcl_SetObjResult(interp, Tcl_ObjPrintf( +	    "invalid base64 character \"%c\" at position %d", +	    (char) c, (int) (data - datastart - 1))); +    TclDecrRefCount(resultObj); +    return TCL_ERROR; +} + +/*   * Local Variables:   * mode: c   * c-basic-offset: 4   * fill-column: 78   * End:   */ + | 
