summaryrefslogtreecommitdiffstats
path: root/generic/tclBinary.c
diff options
context:
space:
mode:
Diffstat (limited to 'generic/tclBinary.c')
-rw-r--r--generic/tclBinary.c196
1 files changed, 119 insertions, 77 deletions
diff --git a/generic/tclBinary.c b/generic/tclBinary.c
index de2d319..5c33308 100644
--- a/generic/tclBinary.c
+++ b/generic/tclBinary.c
@@ -9,8 +9,6 @@
*
* See the file "license.terms" for information on usage and redistribution of
* this file, and for a DISCLAIMER OF ALL WARRANTIES.
- *
- * RCS: @(#) $Id: tclBinary.c,v 1.66 2010/09/15 22:12:00 dkf Exp $
*/
#include "tclInt.h"
@@ -174,13 +172,13 @@ typedef struct ByteArray {
* array. */
int allocated; /* The amount of space actually allocated
* minus 1 byte. */
- unsigned char bytes[4]; /* The array of bytes. The actual size of this
+ unsigned char bytes[1]; /* The array of bytes. The actual size of this
* field depends on the 'allocated' field
* above. */
} ByteArray;
#define BYTEARRAY_SIZE(len) \
- ((unsigned) (sizeof(ByteArray) - 4 + (len)))
+ ((unsigned) (TclOffset(ByteArray, bytes) + (len)))
#define GET_BYTEARRAY(objPtr) \
((ByteArray *) (objPtr)->internalRep.otherValuePtr)
#define SET_BYTEARRAY(objPtr, baPtr) \
@@ -305,15 +303,16 @@ Tcl_SetByteArrayObj(
TclFreeIntRep(objPtr);
Tcl_InvalidateStringRep(objPtr);
- length = (length < 0) ? 0 : length;
- byteArrayPtr = (ByteArray *) ckalloc(BYTEARRAY_SIZE(length));
- memset(byteArrayPtr, 0, BYTEARRAY_SIZE(length));
+ if (length < 0) {
+ length = 0;
+ }
+ byteArrayPtr = ckalloc(BYTEARRAY_SIZE(length));
byteArrayPtr->used = length;
byteArrayPtr->allocated = length;
- if (bytes && length) {
+
+ if ((bytes != NULL) && (length > 0)) {
memcpy(byteArrayPtr->bytes, bytes, (size_t) length);
}
-
objPtr->typePtr = &tclByteArrayType;
SET_BYTEARRAY(objPtr, byteArrayPtr);
}
@@ -393,8 +392,7 @@ Tcl_SetByteArrayLength(
byteArrayPtr = GET_BYTEARRAY(objPtr);
if (length > byteArrayPtr->allocated) {
- byteArrayPtr = (ByteArray *)
- ckrealloc((char *) byteArrayPtr, BYTEARRAY_SIZE(length));
+ byteArrayPtr = ckrealloc(byteArrayPtr, BYTEARRAY_SIZE(length));
byteArrayPtr->allocated = length;
SET_BYTEARRAY(objPtr, byteArrayPtr);
}
@@ -434,7 +432,7 @@ SetByteArrayFromAny(
src = TclGetStringFromObj(objPtr, &length);
srcEnd = src + length;
- byteArrayPtr = (ByteArray *) ckalloc(BYTEARRAY_SIZE(length));
+ byteArrayPtr = ckalloc(BYTEARRAY_SIZE(length));
for (dst = byteArrayPtr->bytes; src < srcEnd; ) {
src += Tcl_UtfToUniChar(src, &ch);
*dst++ = UCHAR(ch);
@@ -471,7 +469,7 @@ static void
FreeByteArrayInternalRep(
Tcl_Obj *objPtr) /* Object with internal rep to free. */
{
- ckfree((char *) GET_BYTEARRAY(objPtr));
+ ckfree(GET_BYTEARRAY(objPtr));
objPtr->typePtr = NULL;
}
@@ -503,7 +501,7 @@ DupByteArrayInternalRep(
srcArrayPtr = GET_BYTEARRAY(srcPtr);
length = srcArrayPtr->used;
- copyArrayPtr = (ByteArray *) ckalloc(BYTEARRAY_SIZE(length));
+ copyArrayPtr = ckalloc(BYTEARRAY_SIZE(length));
copyArrayPtr->used = length;
copyArrayPtr->allocated = length;
memcpy(copyArrayPtr->bytes, srcArrayPtr->bytes, (size_t) length);
@@ -562,7 +560,7 @@ UpdateStringOfByteArray(
Tcl_Panic("max size for a Tcl value (%d bytes) exceeded", INT_MAX);
}
- dst = (char *) ckalloc((unsigned) (size + 1));
+ dst = ckalloc(size + 1);
objPtr->bytes = dst;
objPtr->length = size;
@@ -643,9 +641,8 @@ TclAppendBytesToByteArray(
}
if (BYTEARRAY_SIZE(attempt) > BYTEARRAY_SIZE(used)) {
- tmpByteArrayPtr = (ByteArray *)
- attemptckrealloc((char *) byteArrayPtr,
- BYTEARRAY_SIZE(attempt));
+ tmpByteArrayPtr = attemptckrealloc(byteArrayPtr,
+ BYTEARRAY_SIZE(attempt));
}
if (tmpByteArrayPtr == NULL) {
@@ -653,7 +650,7 @@ TclAppendBytesToByteArray(
if (BYTEARRAY_SIZE(attempt) < BYTEARRAY_SIZE(used)) {
Tcl_Panic("attempt to allocate a bigger buffer than we can handle");
}
- tmpByteArrayPtr = (ByteArray *) ckrealloc((char *) byteArrayPtr,
+ tmpByteArrayPtr = ckrealloc(byteArrayPtr,
BYTEARRAY_SIZE(attempt));
}
@@ -691,29 +688,30 @@ TclAppendBytesToByteArray(
*----------------------------------------------------------------------
*/
+static const EnsembleImplMap binaryMap[] = {
+{ "format", BinaryFormatCmd, NULL, NULL, NULL, 0 },
+{ "scan", BinaryScanCmd, NULL, NULL, NULL, 0 },
+{ "encode", NULL, NULL, NULL, NULL, 0 },
+{ "decode", NULL, NULL, NULL, NULL, 0 },
+{ NULL, NULL, NULL, NULL, NULL, 0 }
+};
+static const EnsembleImplMap encodeMap[] = {
+{ "hex", BinaryEncodeHex, NULL, NULL, (ClientData)HexDigits, 0 },
+{ "uuencode", BinaryEncode64, NULL, NULL, (ClientData)UueDigits, 0 },
+{ "base64", BinaryEncode64, NULL, NULL, (ClientData)B64Digits, 0 },
+{ NULL, NULL, NULL, NULL, NULL, 0 }
+};
+static const EnsembleImplMap decodeMap[] = {
+{ "hex", BinaryDecodeHex, NULL, NULL, NULL, 0 },
+{ "uuencode", BinaryDecodeUu, NULL, NULL, NULL, 0 },
+{ "base64", BinaryDecode64, NULL, NULL, NULL, 0 },
+{ NULL, NULL, NULL, NULL, NULL, 0 }
+};
+
Tcl_Command
TclInitBinaryCmd(
Tcl_Interp *interp)
{
- const EnsembleImplMap binaryMap[] = {
- { "format", BinaryFormatCmd, NULL, NULL ,NULL },
- { "scan", BinaryScanCmd, NULL,NULL ,NULL },
- { "encode", NULL, NULL, NULL, NULL },
- { "decode", NULL, NULL, NULL, NULL },
- { NULL, NULL, NULL, NULL, NULL }
- };
- const EnsembleImplMap encodeMap[] = {
- { "hex", BinaryEncodeHex, NULL, NULL, (ClientData)HexDigits },
- { "uuencode", BinaryEncode64, NULL, NULL, (ClientData)UueDigits },
- { "base64", BinaryEncode64, NULL, NULL, (ClientData)B64Digits },
- { NULL, NULL, NULL, NULL, NULL }
- };
- const EnsembleImplMap decodeMap[] = {
- { "hex", BinaryDecodeHex, NULL, NULL, NULL },
- { "uuencode", BinaryDecodeUu, NULL, NULL, NULL },
- { "base64", BinaryDecode64, NULL, NULL, NULL },
- { NULL, NULL, NULL, NULL, NULL }
- };
Tcl_Command binaryEnsemble;
binaryEnsemble = TclMakeEnsemble(interp, "binary", binaryMap);
@@ -873,9 +871,9 @@ BinaryFormatCmd(
if (count == BINARY_ALL) {
count = listc;
} else if (count > listc) {
- Tcl_AppendResult(interp,
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(
"number of elements in list does not match count",
- NULL);
+ -1));
return TCL_ERROR;
}
}
@@ -884,9 +882,8 @@ BinaryFormatCmd(
case 'x':
if (count == BINARY_ALL) {
- Tcl_AppendResult(interp,
- "cannot use \"*\" in format string with \"x\"",
- NULL);
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(
+ "cannot use \"*\" in format string with \"x\"", -1));
return TCL_ERROR;
} else if (count == BINARY_NOCOUNT) {
count = 1;
@@ -1198,8 +1195,9 @@ BinaryFormatCmd(
badValue:
Tcl_ResetResult(interp);
- Tcl_AppendResult(interp, "expected ", errorString,
- " string but got \"", errorValue, "\" instead", NULL);
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "expected %s string but got \"%s\" instead",
+ errorString, errorValue));
return TCL_ERROR;
badCount:
@@ -1217,12 +1215,13 @@ BinaryFormatCmd(
Tcl_UtfToUniChar(errorString, &ch);
buf[Tcl_UniCharToUtf(ch, buf)] = '\0';
- Tcl_AppendResult(interp, "bad field specifier \"", buf, "\"", NULL);
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "bad field specifier \"%s\"", buf));
return TCL_ERROR;
}
error:
- Tcl_AppendResult(interp, errorString, NULL);
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(errorString, -1));
return TCL_ERROR;
}
@@ -1586,12 +1585,13 @@ BinaryScanCmd(
Tcl_UtfToUniChar(errorString, &ch);
buf[Tcl_UniCharToUtf(ch, buf)] = '\0';
- Tcl_AppendResult(interp, "bad field specifier \"", buf, "\"", NULL);
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "bad field specifier \"%s\"", buf));
return TCL_ERROR;
}
error:
- Tcl_AppendResult(interp, errorString, NULL);
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(errorString, -1));
return TCL_ERROR;
}
@@ -2454,7 +2454,7 @@ BinaryDecodeHex(
} \
} \
if (cursor > limit) { \
- Tcl_Panic("limit hit\n"); \
+ Tcl_Panic("limit hit"); \
} \
} while (0)
@@ -2658,12 +2658,12 @@ BinaryDecode64(
Tcl_Obj *const objv[])
{
Tcl_Obj *resultObj = NULL;
- unsigned char *data, *datastart, *dataend, c;
+ unsigned char *data, *datastart, *dataend, c = '\0';
unsigned char *begin = NULL;
unsigned char *cursor = NULL;
int strict = 0;
int i, index, size, cut = 0, count = 0;
- enum {OPT_STRICT };
+ enum { OPT_STRICT };
static const char *const optStrings[] = { "-strict", NULL };
if (objc < 2 || objc > 3) {
@@ -2691,43 +2691,85 @@ BinaryDecode64(
while (data < dataend) {
unsigned long value = 0;
- for (i=0 ; i<4 ; i++) {
+ /*
+ * Decode the current block. Each base64 block consists of four input
+ * characters A-Z, a-z, 0-9, +, or /. Each character supplies six bits
+ * of output data, so each block's output is 24 bits (three bytes) in
+ * length. The final block can be shorter by one or two bytes, denoted
+ * by the input ending with one or two ='s, respectively.
+ */
+
+ for (i = 0; i < 4; i++) {
+ /*
+ * Get the next input character. At end of input, pad with at most
+ * two ='s. If more than two ='s would be needed, instead discard
+ * the block read thus far.
+ */
+
if (data < dataend) {
c = *data++;
+ } else if (i > 1) {
+ c = '=';
+ } else {
+ cut += 3;
+ break;
+ }
- if (c >= 'A' && c <= 'Z') {
- value = (value << 6) | ((c - 'A') & 0x3f);
- } else if (c >= 'a' && c <= 'z') {
- value = (value << 6) | ((c - 'a' + 26) & 0x3f);
- } else if (c >= '0' && c <= '9') {
- value = (value << 6) | ((c - '0' + 52) & 0x3f);
- } else if (c == '+') {
- value = (value << 6) | 0x3e;
- } else if (c == '/') {
- value = (value << 6) | 0x3f;
- } else if (c == '=') {
- value <<= 6;
- if (cut < 2) {
- cut++;
- }
+ /*
+ * Load the character into the block value. Handle ='s specially
+ * because they're only valid as the last character or two of the
+ * final block of input. Unless strict mode is enabled, skip any
+ * input whitespace characters.
+ */
+
+ if (cut) {
+ if (c == '=' && i > 1) {
+ value <<= 6;
+ cut++;
+ } else if (!strict && isspace(c)) {
+ i--;
} else {
- if (strict || !isspace(c)) {
- goto bad64;
- }
- i--;
- continue;
+ goto bad64;
}
- } else {
+ } else if (c >= 'A' && c <= 'Z') {
+ value = (value << 6) | ((c - 'A') & 0x3f);
+ } else if (c >= 'a' && c <= 'z') {
+ value = (value << 6) | ((c - 'a' + 26) & 0x3f);
+ } else if (c >= '0' && c <= '9') {
+ value = (value << 6) | ((c - '0' + 52) & 0x3f);
+ } else if (c == '+') {
+ value = (value << 6) | 0x3e;
+ } else if (c == '/') {
+ value = (value << 6) | 0x3f;
+ } else if (c == '=') {
value <<= 6;
cut++;
+ } else if (strict || !isspace(c)) {
+ goto bad64;
+ } else {
+ i--;
}
}
*cursor++ = UCHAR((value >> 16) & 0xff);
*cursor++ = UCHAR((value >> 8) & 0xff);
*cursor++ = UCHAR(value & 0xff);
- }
- if (cut > size) {
- cut = size;
+
+ /*
+ * Since = is only valid within the final block, if it was encountered
+ * but there are still more input characters, confirm that strict mode
+ * is off and all subsequent characters are whitespace.
+ */
+
+ if (cut && data < dataend) {
+ if (strict) {
+ goto bad64;
+ }
+ for (; data < dataend; data++) {
+ if (!isspace(*data)) {
+ goto bad64;
+ }
+ }
+ }
}
Tcl_SetByteArrayLength(resultObj, cursor - begin - cut);
Tcl_SetObjResult(interp, resultObj);