1 /* 2 ** 2007 April 6 3 ** 4 ** The author disclaims copyright to this source code. In place of 5 ** a legal notice, here is a blessing: 6 ** 7 ** May you do good and not evil. 8 ** May you find forgiveness for yourself and forgive others. 9 ** May you share freely, never taking more than you give. 10 ** 11 ************************************************************************* 12 ** Code for testing all sorts of SQLite interfaces. This code 13 ** implements TCL commands for reading and writing the binary 14 ** database files and displaying the content of those files as 15 ** hexadecimal. We could, in theory, use the built-in "binary" 16 ** command of TCL to do a lot of this, but there are some issues 17 ** with historical versions of the "binary" command. So it seems 18 ** easier and safer to build our own mechanism. 19 ** 20 ** $Id: test_hexio.c,v 1.2 2007/04/09 20:30:11 drh Exp $ 21 */ 22 #include "tcl.h" 23 #include <stdlib.h> 24 #include <string.h> 25 #include <assert.h> 26 27 /* 28 hexio_read filename offset amt 29 hexio_write filename offset hexdata 30 hexio_get_int hexdata 31 hexio_get_varint hexdata 32 hexio_render_int8 integer 33 hexio_render_int16 integer 34 hexio_render_int38 integer 35 hexio_render_varint integer 36 */ 37 38 /* 39 ** Convert binary to hex. The input zBuf[] contains N bytes of 40 ** binary data. zBuf[] is 2*n+1 bytes long. Overwrite zBuf[] 41 ** with a hexadecimal representation of its original binary input. 42 */ 43 static void binToHex(unsigned char *zBuf, int N){ 44 const unsigned char zHex[] = "0123456789ABCDEF"; 45 int i, j; 46 unsigned char c; 47 i = N*2; 48 zBuf[i--] = 0; 49 for(j=N-1; j>=0; j--){ 50 c = zBuf[j]; 51 zBuf[i--] = zHex[c&0xf]; 52 zBuf[i--] = zHex[c>>4]; 53 } 54 assert( i==-1 ); 55 } 56 57 /* 58 ** Convert hex to binary. The input zIn[] contains N bytes of 59 ** hexadecimal. Convert this into binary and write aOut[] with 60 ** the binary data. Spaces in the original input are ignored. 61 ** Return the number of bytes of binary rendered. 62 */ 63 static int hexToBin(const unsigned char *zIn, int N, unsigned char *aOut){ 64 const unsigned char aMap[] = { 65 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 66 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 0, 0, 0, 0, 0, 0, 69 0,11,12,13,14,15,16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71 0,11,12,13,14,15,16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 72 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 74 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 75 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 76 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 77 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 78 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 79 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 81 }; 82 int i, j; 83 int hi=1; 84 unsigned char c; 85 86 for(i=j=0; i<N; i++){ 87 c = aMap[zIn[i]]; 88 if( c==0 ) continue; 89 if( hi ){ 90 aOut[j] = (c-1)<<4; 91 hi = 0; 92 }else{ 93 aOut[j++] |= c-1; 94 hi = 1; 95 } 96 } 97 return j; 98 } 99 100 101 /* 102 ** Usage: hexio_read FILENAME OFFSET AMT 103 ** 104 ** Read AMT bytes from file FILENAME beginning at OFFSET from the 105 ** beginning of the file. Convert that information to hexadecimal 106 ** and return the resulting HEX string. 107 */ 108 static int hexio_read( 109 void * clientData, 110 Tcl_Interp *interp, 111 int objc, 112 Tcl_Obj *CONST objv[] 113 ){ 114 int offset; 115 int amt, got; 116 const char *zFile; 117 unsigned char *zBuf; 118 FILE *in; 119 120 if( objc!=4 ){ 121 Tcl_WrongNumArgs(interp, 1, objv, "FILENAME OFFSET AMT"); 122 return TCL_ERROR; 123 } 124 if( Tcl_GetIntFromObj(interp, objv[2], &offset) ) return TCL_ERROR; 125 if( Tcl_GetIntFromObj(interp, objv[3], &amt) ) return TCL_ERROR; 126 zFile = Tcl_GetString(objv[1]); 127 zBuf = malloc( amt*2+1 ); 128 if( zBuf==0 ){ 129 return TCL_ERROR; 130 } 131 in = fopen(zFile, "r"); 132 if( in==0 ){ 133 Tcl_AppendResult(interp, "cannot open input file ", zFile, 0); 134 return TCL_ERROR; 135 } 136 fseek(in, offset, SEEK_SET); 137 got = fread(zBuf, 1, amt, in); 138 fclose(in); 139 if( got<0 ){ 140 got = 0; 141 } 142 binToHex(zBuf, got); 143 Tcl_AppendResult(interp, zBuf, 0); 144 free(zBuf); 145 return TCL_OK; 146 } 147 148 149 /* 150 ** Usage: hexio_write FILENAME OFFSET DATA 151 ** 152 ** Write DATA into file FILENAME beginning at OFFSET from the 153 ** beginning of the file. DATA is expressed in hexadecimal. 154 */ 155 static int hexio_write( 156 void * clientData, 157 Tcl_Interp *interp, 158 int objc, 159 Tcl_Obj *CONST objv[] 160 ){ 161 int offset; 162 int nIn, nOut, written; 163 const char *zFile; 164 const unsigned char *zIn; 165 unsigned char *aOut; 166 FILE *out; 167 168 if( objc!=4 ){ 169 Tcl_WrongNumArgs(interp, 1, objv, "FILENAME OFFSET HEXDATA"); 170 return TCL_ERROR; 171 } 172 if( Tcl_GetIntFromObj(interp, objv[2], &offset) ) return TCL_ERROR; 173 zFile = Tcl_GetString(objv[1]); 174 zIn = (const unsigned char *)Tcl_GetStringFromObj(objv[3], &nIn); 175 aOut = malloc( nIn/2 ); 176 if( aOut==0 ){ 177 return TCL_ERROR; 178 } 179 nOut = hexToBin(zIn, nIn, aOut); 180 out = fopen(zFile, "r+"); 181 if( out==0 ){ 182 Tcl_AppendResult(interp, "cannot open output file ", zFile, 0); 183 return TCL_ERROR; 184 } 185 fseek(out, offset, SEEK_SET); 186 written = fwrite(aOut, 1, nOut, out); 187 free(aOut); 188 fclose(out); 189 Tcl_SetObjResult(interp, Tcl_NewIntObj(written)); 190 return TCL_OK; 191 } 192 193 /* 194 ** USAGE: hexio_get_int HEXDATA 195 ** 196 ** Interpret the HEXDATA argument as a big-endian integer. Return 197 ** the value of that integer. HEXDATA can contain between 2 and 8 198 ** hexadecimal digits. 199 */ 200 static int hexio_get_int( 201 void * clientData, 202 Tcl_Interp *interp, 203 int objc, 204 Tcl_Obj *CONST objv[] 205 ){ 206 int val; 207 int nIn, nOut; 208 const unsigned char *zIn; 209 unsigned char *aOut; 210 unsigned char aNum[4]; 211 212 if( objc!=2 ){ 213 Tcl_WrongNumArgs(interp, 1, objv, "HEXDATA"); 214 return TCL_ERROR; 215 } 216 zIn = (const unsigned char *)Tcl_GetStringFromObj(objv[1], &nIn); 217 aOut = malloc( nIn/2 ); 218 if( aOut==0 ){ 219 return TCL_ERROR; 220 } 221 nOut = hexToBin(zIn, nIn, aOut); 222 if( nOut>=4 ){ 223 memcpy(aNum, aOut, 4); 224 }else{ 225 memset(aNum, 0, sizeof(aNum)); 226 memcpy(&aNum[4-nOut], aOut, nOut); 227 } 228 free(aOut); 229 val = (aNum[0]<<24) | (aNum[1]<<16) | (aNum[2]<<8) | aNum[3]; 230 Tcl_SetObjResult(interp, Tcl_NewIntObj(val)); 231 return TCL_OK; 232 } 233 234 235 /* 236 ** USAGE: hexio_render_int16 INTEGER 237 ** 238 ** Render INTEGER has a 16-bit big-endian integer in hexadecimal. 239 */ 240 static int hexio_render_int16( 241 void * clientData, 242 Tcl_Interp *interp, 243 int objc, 244 Tcl_Obj *CONST objv[] 245 ){ 246 int val; 247 unsigned char aNum[10]; 248 249 if( objc!=2 ){ 250 Tcl_WrongNumArgs(interp, 1, objv, "INTEGER"); 251 return TCL_ERROR; 252 } 253 if( Tcl_GetIntFromObj(interp, objv[1], &val) ) return TCL_ERROR; 254 aNum[0] = val>>8; 255 aNum[1] = val; 256 binToHex(aNum, 2); 257 Tcl_SetObjResult(interp, Tcl_NewStringObj((char*)aNum, 4)); 258 return TCL_OK; 259 } 260 261 262 /* 263 ** USAGE: hexio_render_int32 INTEGER 264 ** 265 ** Render INTEGER has a 32-bit big-endian integer in hexadecimal. 266 */ 267 static int hexio_render_int32( 268 void * clientData, 269 Tcl_Interp *interp, 270 int objc, 271 Tcl_Obj *CONST objv[] 272 ){ 273 int val; 274 unsigned char aNum[10]; 275 276 if( objc!=2 ){ 277 Tcl_WrongNumArgs(interp, 1, objv, "INTEGER"); 278 return TCL_ERROR; 279 } 280 if( Tcl_GetIntFromObj(interp, objv[1], &val) ) return TCL_ERROR; 281 aNum[0] = val>>24; 282 aNum[1] = val>>16; 283 aNum[2] = val>>8; 284 aNum[3] = val; 285 binToHex(aNum, 4); 286 Tcl_SetObjResult(interp, Tcl_NewStringObj((char*)aNum, 8)); 287 return TCL_OK; 288 } 289 290 291 292 /* 293 ** Register commands with the TCL interpreter. 294 */ 295 int Sqlitetest_hexio_Init(Tcl_Interp *interp){ 296 static struct { 297 char *zName; 298 Tcl_ObjCmdProc *xProc; 299 } aObjCmd[] = { 300 { "hexio_read", hexio_read }, 301 { "hexio_write", hexio_write }, 302 { "hexio_get_int", hexio_get_int }, 303 { "hexio_render_int16", hexio_render_int16 }, 304 { "hexio_render_int32", hexio_render_int32 }, 305 }; 306 int i; 307 for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){ 308 Tcl_CreateObjCommand(interp, aObjCmd[i].zName, aObjCmd[i].xProc, 0, 0); 309 } 310 return TCL_OK; 311 } 312