Subversion Repositories Open64

[/] [sim/] [fsim/] [shell/] [tclsh/] [generic/] [tclBinary.c] - Blame information for rev 2072

Details | Compare with Previous | View Log

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