📄 tclbinary.c
字号:
/* * tclBinary.c -- * * This file contains the implementation of the "binary" Tcl built-in * command . * * Copyright (c) 1997 by Sun Microsystems, Inc. * * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * SCCS: @(#) tclBinary.c 1.26 97/11/05 13:02:05 */#include <math.h>#include "tclInt.h"#include "tclPort.h"/* * The following constants are used by GetFormatSpec to indicate various * special conditions in the parsing of a format specifier. */#define BINARY_ALL -1 /* Use all elements in the argument. */#define BINARY_NOCOUNT -2 /* No count was specified in format. *//* * Prototypes for local procedures defined in this file: */static int GetFormatSpec _ANSI_ARGS_((char **formatPtr, char *cmdPtr, int *countPtr));static int FormatNumber _ANSI_ARGS_((Tcl_Interp *interp, int type, Tcl_Obj *src, char **cursorPtr));static Tcl_Obj * ScanNumber _ANSI_ARGS_((char *buffer, int type));/* *---------------------------------------------------------------------- * * Tcl_BinaryObjCmd -- * * This procedure implements the "binary" Tcl command. * * Results: * A standard Tcl result. * * Side effects: * See the user documentation. * *---------------------------------------------------------------------- */intTcl_BinaryObjCmd(dummy, interp, objc, objv) ClientData dummy; /* Not used. */ Tcl_Interp *interp; /* Current interpreter. */ int objc; /* Number of arguments. */ Tcl_Obj *CONST objv[]; /* Argument objects. */{ int arg; /* Index of next argument to consume. */ int value = 0; /* Current integer value to be packed. * Initialized to avoid compiler warning. */ char cmd; /* Current format character. */ int count; /* Count associated with current format * character. */ char *format; /* Pointer to current position in format * string. */ char *cursor; /* Current position within result buffer. */ char *maxPos; /* Greatest position within result buffer that * cursor has visited.*/ char *buffer; /* Start of data buffer. */ char *errorString, *errorValue, *str; int offset, size, length; Tcl_Obj *resultPtr; static char *subCmds[] = { "format", "scan", (char *) NULL }; enum { BinaryFormat, BinaryScan } index; if (objc < 2) { Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?"); return TCL_ERROR; } if (Tcl_GetIndexFromObj(interp, objv[1], subCmds, "option", 0, (int *) &index) != TCL_OK) { return TCL_ERROR; } switch (index) { case BinaryFormat: if (objc < 3) { Tcl_WrongNumArgs(interp, 2, objv, "formatString ?arg arg ...?"); return TCL_ERROR; } /* * To avoid copying the data, we format the string in two passes. * The first pass computes the size of the output buffer. The * second pass places the formatted data into the buffer. */ format = Tcl_GetStringFromObj(objv[2], NULL); arg = 3; offset = length = 0; while (*format != 0) { if (!GetFormatSpec(&format, &cmd, &count)) { break; } switch (cmd) { case 'a': case 'A': case 'b': case 'B': case 'h': case 'H': /* * For string-type specifiers, the count corresponds * to the number of characters in a single argument. */ if (arg >= objc) { goto badIndex; } if (count == BINARY_ALL) { (void)Tcl_GetStringFromObj(objv[arg], &count); } else if (count == BINARY_NOCOUNT) { count = 1; } arg++; if (cmd == 'a' || cmd == 'A') { offset += count; } else if (cmd == 'b' || cmd == 'B') { offset += (count + 7) / 8; } else { offset += (count + 1) / 2; } break; case 'c': size = 1; goto doNumbers; case 's': case 'S': size = 2; goto doNumbers; case 'i': case 'I': size = 4; goto doNumbers; case 'f': size = sizeof(float); goto doNumbers; case 'd': size = sizeof(double); doNumbers: if (arg >= objc) { goto badIndex; } /* * For number-type specifiers, the count corresponds * to the number of elements in the list stored in * a single argument. If no count is specified, then * the argument is taken as a single non-list value. */ if (count == BINARY_NOCOUNT) { arg++; count = 1; } else { int listc; Tcl_Obj **listv; if (Tcl_ListObjGetElements(interp, objv[arg++], &listc, &listv) != TCL_OK) { return TCL_ERROR; } if (count == BINARY_ALL) { count = listc; } else if (count > listc) { errorString = "number of elements in list does not match count"; goto error; } } offset += count*size; break; case 'x': if (count == BINARY_ALL) { errorString = "cannot use \"*\" in format string with \"x\""; goto error; } else if (count == BINARY_NOCOUNT) { count = 1; } offset += count; break; case 'X': if (count == BINARY_NOCOUNT) { count = 1; } if ((count > offset) || (count == BINARY_ALL)) { count = offset; } if (offset > length) { length = offset; } offset -= count; break; case '@': if (offset > length) { length = offset; } if (count == BINARY_ALL) { offset = length; } else if (count == BINARY_NOCOUNT) { goto badCount; } else { offset = count; } break; default: { char buf[2]; Tcl_ResetResult(interp); buf[0] = cmd; buf[1] = '\0'; Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "bad field specifier \"", buf, "\"", NULL); return TCL_ERROR; } } } if (offset > length) { length = offset; } if (length == 0) { return TCL_OK; } /* * Prepare the result object by preallocating the caclulated * number of bytes and filling with nulls. */ resultPtr = Tcl_GetObjResult(interp); Tcl_SetObjLength(resultPtr, length); buffer = Tcl_GetStringFromObj(resultPtr, NULL); memset(buffer, 0, (size_t) length); /* * Pack the data into the result object. Note that we can skip * the error checking during this pass, since we have already * parsed the string once. */ arg = 3; format = Tcl_GetStringFromObj(objv[2], NULL); cursor = buffer; maxPos = cursor; while (*format != 0) { if (!GetFormatSpec(&format, &cmd, &count)) { break; } if ((count == 0) && (cmd != '@')) { arg++; continue; } switch (cmd) { case 'a': case 'A': { char pad = (char) (cmd == 'a' ? '\0' : ' '); str = Tcl_GetStringFromObj(objv[arg++], &length); if (count == BINARY_ALL) { count = length; } else if (count == BINARY_NOCOUNT) { count = 1; } if (length >= count) { memcpy((VOID *) cursor, (VOID *) str, (size_t) count); } else { memcpy((VOID *) cursor, (VOID *) str, (size_t) length); memset(cursor+length, pad, (size_t) (count - length)); } cursor += count; break; } case 'b': case 'B': { char *last; str = Tcl_GetStringFromObj(objv[arg++], &length); if (count == BINARY_ALL) { count = length; } else if (count == BINARY_NOCOUNT) { count = 1; } last = cursor + ((count + 7) / 8); if (count > length) { count = length; } value = 0; errorString = "binary"; if (cmd == 'B') { for (offset = 0; offset < count; offset++) { value <<= 1; if (str[offset] == '1') { value |= 1; } else if (str[offset] != '0') { errorValue = str; goto badValue; } if (((offset + 1) % 8) == 0) { *cursor++ = (char)(value & 0xff); value = 0; } } } else { for (offset = 0; offset < count; offset++) { value >>= 1; if (str[offset] == '1') { value |= 128; } else if (str[offset] != '0') { errorValue = str; goto badValue; } if (!((offset + 1) % 8)) { *cursor++ = (char)(value & 0xff); value = 0; } } } if ((offset % 8) != 0) { if (cmd == 'B') { value <<= 8 - (offset % 8); } else { value >>= 8 - (offset % 8); } *cursor++ = (char)(value & 0xff); } while (cursor < last) { *cursor++ = '\0'; } break; } case 'h': case 'H': { char *last; int c; str = Tcl_GetStringFromObj(objv[arg++], &length); if (count == BINARY_ALL) { count = length; } else if (count == BINARY_NOCOUNT) { count = 1; } last = cursor + ((count + 1) / 2); if (count > length) { count = length; } value = 0; errorString = "hexadecimal"; if (cmd == 'H') { for (offset = 0; offset < count; offset++) { value <<= 4; c = tolower(((unsigned char *) str)[offset]); if ((c >= 'a') && (c <= 'f')) { value |= ((c - 'a' + 10) & 0xf); } else if ((c >= '0') && (c <= '9')) { value |= (c - '0') & 0xf; } else { errorValue = str; goto badValue; } if (offset % 2) { *cursor++ = (char) value; value = 0; } } } else { for (offset = 0; offset < count; offset++) { value >>= 4; c = tolower(((unsigned char *) str)[offset]); if ((c >= 'a') && (c <= 'f')) { value |= ((c - 'a' + 10) << 4) & 0xf0; } else if ((c >= '0') && (c <= '9')) { value |= ((c - '0') << 4) & 0xf0; } else { errorValue = str; goto badValue; } if (offset % 2) { *cursor++ = (char)(value & 0xff); value = 0; } } } if (offset % 2) { if (cmd == 'H') { value <<= 4; } else { value >>= 4; } *cursor++ = (char) value; } while (cursor < last) { *cursor++ = '\0'; } break; } case 'c': case 's': case 'S': case 'i': case 'I': case 'd': case 'f': { int listc, i; Tcl_Obj **listv; if (count == BINARY_NOCOUNT) { /* * Note that we are casting away the const-ness of * objv, but this is safe since we aren't going to * modify the array. */ listv = (Tcl_Obj**)(objv + arg); listc = 1; count = 1; } else { Tcl_ListObjGetElements(interp, objv[arg], &listc, &listv); if (count == BINARY_ALL) { count = listc; } } arg++; for (i = 0; i < count; i++) { if (FormatNumber(interp, cmd, listv[i], &cursor) != TCL_OK) { return TCL_ERROR; } } break; } case 'x': if (count == BINARY_NOCOUNT) { count = 1; } memset(cursor, 0, (size_t) count); cursor += count; break; case 'X': if (cursor > maxPos) { maxPos = cursor; } if (count == BINARY_NOCOUNT) { count = 1; } if ((count == BINARY_ALL) || (count > (cursor - buffer))) { cursor = buffer; } else { cursor -= count; } break; case '@': if (cursor > maxPos) { maxPos = cursor; } if (count == BINARY_ALL) { cursor = maxPos; } else { cursor = buffer + count; } break; } } break; case BinaryScan: { int i; Tcl_Obj *valuePtr, *elementPtr; if (objc < 4) { Tcl_WrongNumArgs(interp, 2, objv, "value formatString ?varName varName ...?"); return TCL_ERROR; } buffer = Tcl_GetStringFromObj(objv[2], &length); format = Tcl_GetStringFromObj(objv[3], NULL); cursor = buffer; arg = 4; offset = 0; while (*format != 0) { if (!GetFormatSpec(&format, &cmd, &count)) { goto done; } switch (cmd) { case 'a': case 'A': if (arg >= objc) { goto badIndex;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -