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