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

通讯编程

开发平台:

Visual C++

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