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.6 2007/10/19 17:47:25 drh Exp $ 21 */ 22 #include "sqliteInt.h" 23 #include "tcl.h" 24 #include <stdlib.h> 25 #include <string.h> 26 #include <assert.h> 27 28 29 /* 30 ** Convert binary to hex. The input zBuf[] contains N bytes of 31 ** binary data. zBuf[] is 2*n+1 bytes long. Overwrite zBuf[] 32 ** with a hexadecimal representation of its original binary input. 33 */ 34 void sqlite3TestBinToHex(unsigned char *zBuf, int N){ 35 const unsigned char zHex[] = "0123456789ABCDEF"; 36 int i, j; 37 unsigned char c; 38 i = N*2; 39 zBuf[i--] = 0; 40 for(j=N-1; j>=0; j--){ 41 c = zBuf[j]; 42 zBuf[i--] = zHex[c&0xf]; 43 zBuf[i--] = zHex[c>>4]; 44 } 45 assert( i==-1 ); 46 } 47 48 /* 49 ** Convert hex to binary. The input zIn[] contains N bytes of 50 ** hexadecimal. Convert this into binary and write aOut[] with 51 ** the binary data. Spaces in the original input are ignored. 52 ** Return the number of bytes of binary rendered. 53 */ 54 int sqlite3TestHexToBin(const unsigned char *zIn, int N, unsigned char *aOut){ 55 const unsigned char aMap[] = { 56 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 57 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 59 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 0, 0, 0, 0, 0, 0, 60 0,11,12,13,14,15,16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 61 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62 0,11,12,13,14,15,16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 69 0, 0, 0, 0, 0, 0, 0, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 72 }; 73 int i, j; 74 int hi=1; 75 unsigned char c; 76 77 for(i=j=0; i<N; i++){ 78 c = aMap[zIn[i]]; 79 if( c==0 ) continue; 80 if( hi ){ 81 aOut[j] = (c-1)<<4; 82 hi = 0; 83 }else{ 84 aOut[j++] |= c-1; 85 hi = 1; 86 } 87 } 88 return j; 89 } 90 91 92 /* 93 ** Usage: hexio_read FILENAME OFFSET AMT 94 ** 95 ** Read AMT bytes from file FILENAME beginning at OFFSET from the 96 ** beginning of the file. Convert that information to hexadecimal 97 ** and return the resulting HEX string. 98 */ 99 static int hexio_read( 100 void * clientData, 101 Tcl_Interp *interp, 102 int objc, 103 Tcl_Obj *CONST objv[] 104 ){ 105 int offset; 106 int amt, got; 107 const char *zFile; 108 unsigned char *zBuf; 109 FILE *in; 110 111 if( objc!=4 ){ 112 Tcl_WrongNumArgs(interp, 1, objv, "FILENAME OFFSET AMT"); 113 return TCL_ERROR; 114 } 115 if( Tcl_GetIntFromObj(interp, objv[2], &offset) ) return TCL_ERROR; 116 if( Tcl_GetIntFromObj(interp, objv[3], &amt) ) return TCL_ERROR; 117 zFile = Tcl_GetString(objv[1]); 118 zBuf = sqlite3_malloc( amt*2+1 ); 119 if( zBuf==0 ){ 120 return TCL_ERROR; 121 } 122 in = fopen(zFile, "r"); 123 if( in==0 ){ 124 Tcl_AppendResult(interp, "cannot open input file ", zFile, 0); 125 return TCL_ERROR; 126 } 127 fseek(in, offset, SEEK_SET); 128 got = fread(zBuf, 1, amt, in); 129 fclose(in); 130 if( got<0 ){ 131 got = 0; 132 } 133 sqlite3TestBinToHex(zBuf, got); 134 Tcl_AppendResult(interp, zBuf, 0); 135 sqlite3_free(zBuf); 136 return TCL_OK; 137 } 138 139 140 /* 141 ** Usage: hexio_write FILENAME OFFSET DATA 142 ** 143 ** Write DATA into file FILENAME beginning at OFFSET from the 144 ** beginning of the file. DATA is expressed in hexadecimal. 145 */ 146 static int hexio_write( 147 void * clientData, 148 Tcl_Interp *interp, 149 int objc, 150 Tcl_Obj *CONST objv[] 151 ){ 152 int offset; 153 int nIn, nOut, written; 154 const char *zFile; 155 const unsigned char *zIn; 156 unsigned char *aOut; 157 FILE *out; 158 159 if( objc!=4 ){ 160 Tcl_WrongNumArgs(interp, 1, objv, "FILENAME OFFSET HEXDATA"); 161 return TCL_ERROR; 162 } 163 if( Tcl_GetIntFromObj(interp, objv[2], &offset) ) return TCL_ERROR; 164 zFile = Tcl_GetString(objv[1]); 165 zIn = (const unsigned char *)Tcl_GetStringFromObj(objv[3], &nIn); 166 aOut = sqlite3_malloc( nIn/2 ); 167 if( aOut==0 ){ 168 return TCL_ERROR; 169 } 170 nOut = sqlite3TestHexToBin(zIn, nIn, aOut); 171 out = fopen(zFile, "r+"); 172 if( out==0 ){ 173 Tcl_AppendResult(interp, "cannot open output file ", zFile, 0); 174 return TCL_ERROR; 175 } 176 fseek(out, offset, SEEK_SET); 177 written = fwrite(aOut, 1, nOut, out); 178 sqlite3_free(aOut); 179 fclose(out); 180 Tcl_SetObjResult(interp, Tcl_NewIntObj(written)); 181 return TCL_OK; 182 } 183 184 /* 185 ** USAGE: hexio_get_int HEXDATA 186 ** 187 ** Interpret the HEXDATA argument as a big-endian integer. Return 188 ** the value of that integer. HEXDATA can contain between 2 and 8 189 ** hexadecimal digits. 190 */ 191 static int hexio_get_int( 192 void * clientData, 193 Tcl_Interp *interp, 194 int objc, 195 Tcl_Obj *CONST objv[] 196 ){ 197 int val; 198 int nIn, nOut; 199 const unsigned char *zIn; 200 unsigned char *aOut; 201 unsigned char aNum[4]; 202 203 if( objc!=2 ){ 204 Tcl_WrongNumArgs(interp, 1, objv, "HEXDATA"); 205 return TCL_ERROR; 206 } 207 zIn = (const unsigned char *)Tcl_GetStringFromObj(objv[1], &nIn); 208 aOut = sqlite3_malloc( nIn/2 ); 209 if( aOut==0 ){ 210 return TCL_ERROR; 211 } 212 nOut = sqlite3TestHexToBin(zIn, nIn, aOut); 213 if( nOut>=4 ){ 214 memcpy(aNum, aOut, 4); 215 }else{ 216 memset(aNum, 0, sizeof(aNum)); 217 memcpy(&aNum[4-nOut], aOut, nOut); 218 } 219 sqlite3_free(aOut); 220 val = (aNum[0]<<24) | (aNum[1]<<16) | (aNum[2]<<8) | aNum[3]; 221 Tcl_SetObjResult(interp, Tcl_NewIntObj(val)); 222 return TCL_OK; 223 } 224 225 226 /* 227 ** USAGE: hexio_render_int16 INTEGER 228 ** 229 ** Render INTEGER has a 16-bit big-endian integer in hexadecimal. 230 */ 231 static int hexio_render_int16( 232 void * clientData, 233 Tcl_Interp *interp, 234 int objc, 235 Tcl_Obj *CONST objv[] 236 ){ 237 int val; 238 unsigned char aNum[10]; 239 240 if( objc!=2 ){ 241 Tcl_WrongNumArgs(interp, 1, objv, "INTEGER"); 242 return TCL_ERROR; 243 } 244 if( Tcl_GetIntFromObj(interp, objv[1], &val) ) return TCL_ERROR; 245 aNum[0] = val>>8; 246 aNum[1] = val; 247 sqlite3TestBinToHex(aNum, 2); 248 Tcl_SetObjResult(interp, Tcl_NewStringObj((char*)aNum, 4)); 249 return TCL_OK; 250 } 251 252 253 /* 254 ** USAGE: hexio_render_int32 INTEGER 255 ** 256 ** Render INTEGER has a 32-bit big-endian integer in hexadecimal. 257 */ 258 static int hexio_render_int32( 259 void * clientData, 260 Tcl_Interp *interp, 261 int objc, 262 Tcl_Obj *CONST objv[] 263 ){ 264 int val; 265 unsigned char aNum[10]; 266 267 if( objc!=2 ){ 268 Tcl_WrongNumArgs(interp, 1, objv, "INTEGER"); 269 return TCL_ERROR; 270 } 271 if( Tcl_GetIntFromObj(interp, objv[1], &val) ) return TCL_ERROR; 272 aNum[0] = val>>24; 273 aNum[1] = val>>16; 274 aNum[2] = val>>8; 275 aNum[3] = val; 276 sqlite3TestBinToHex(aNum, 4); 277 Tcl_SetObjResult(interp, Tcl_NewStringObj((char*)aNum, 8)); 278 return TCL_OK; 279 } 280 281 /* 282 ** USAGE: utf8_to_utf8 HEX 283 ** 284 ** The argument is a UTF8 string represented in hexadecimal. 285 ** The UTF8 might not be well-formed. Run this string through 286 ** sqlite3Utf8to8() convert it back to hex and return the result. 287 */ 288 static int utf8_to_utf8( 289 void * clientData, 290 Tcl_Interp *interp, 291 int objc, 292 Tcl_Obj *CONST objv[] 293 ){ 294 #ifdef SQLITE_DEBUG 295 int n; 296 int nOut; 297 const unsigned char *zOrig; 298 unsigned char *z; 299 if( objc!=2 ){ 300 Tcl_WrongNumArgs(interp, 1, objv, "HEX"); 301 return TCL_ERROR; 302 } 303 zOrig = (unsigned char *)Tcl_GetStringFromObj(objv[1], &n); 304 z = sqlite3_malloc( n+3 ); 305 n = sqlite3TestHexToBin(zOrig, n, z); 306 z[n] = 0; 307 nOut = sqlite3Utf8To8(z); 308 sqlite3TestBinToHex(z,nOut); 309 Tcl_AppendResult(interp, (char*)z, 0); 310 sqlite3_free(z); 311 #endif 312 return TCL_OK; 313 } 314 315 316 /* 317 ** Register commands with the TCL interpreter. 318 */ 319 int Sqlitetest_hexio_Init(Tcl_Interp *interp){ 320 static struct { 321 char *zName; 322 Tcl_ObjCmdProc *xProc; 323 } aObjCmd[] = { 324 { "hexio_read", hexio_read }, 325 { "hexio_write", hexio_write }, 326 { "hexio_get_int", hexio_get_int }, 327 { "hexio_render_int16", hexio_render_int16 }, 328 { "hexio_render_int32", hexio_render_int32 }, 329 { "utf8_to_utf8", utf8_to_utf8 }, 330 }; 331 int i; 332 for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){ 333 Tcl_CreateObjCommand(interp, aObjCmd[i].zName, aObjCmd[i].xProc, 0, 0); 334 } 335 return TCL_OK; 336 } 337