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.7 2008/05/12 16:17:42 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, "rb"); 123 if( in==0 ){ 124 in = fopen(zFile, "r"); 125 } 126 if( in==0 ){ 127 Tcl_AppendResult(interp, "cannot open input file ", zFile, 0); 128 return TCL_ERROR; 129 } 130 fseek(in, offset, SEEK_SET); 131 got = fread(zBuf, 1, amt, in); 132 fclose(in); 133 if( got<0 ){ 134 got = 0; 135 } 136 sqlite3TestBinToHex(zBuf, got); 137 Tcl_AppendResult(interp, zBuf, 0); 138 sqlite3_free(zBuf); 139 return TCL_OK; 140 } 141 142 143 /* 144 ** Usage: hexio_write FILENAME OFFSET DATA 145 ** 146 ** Write DATA into file FILENAME beginning at OFFSET from the 147 ** beginning of the file. DATA is expressed in hexadecimal. 148 */ 149 static int hexio_write( 150 void * clientData, 151 Tcl_Interp *interp, 152 int objc, 153 Tcl_Obj *CONST objv[] 154 ){ 155 int offset; 156 int nIn, nOut, written; 157 const char *zFile; 158 const unsigned char *zIn; 159 unsigned char *aOut; 160 FILE *out; 161 162 if( objc!=4 ){ 163 Tcl_WrongNumArgs(interp, 1, objv, "FILENAME OFFSET HEXDATA"); 164 return TCL_ERROR; 165 } 166 if( Tcl_GetIntFromObj(interp, objv[2], &offset) ) return TCL_ERROR; 167 zFile = Tcl_GetString(objv[1]); 168 zIn = (const unsigned char *)Tcl_GetStringFromObj(objv[3], &nIn); 169 aOut = sqlite3_malloc( nIn/2 ); 170 if( aOut==0 ){ 171 return TCL_ERROR; 172 } 173 nOut = sqlite3TestHexToBin(zIn, nIn, aOut); 174 out = fopen(zFile, "r+b"); 175 if( out==0 ){ 176 out = fopen(zFile, "r+"); 177 } 178 if( out==0 ){ 179 Tcl_AppendResult(interp, "cannot open output file ", zFile, 0); 180 return TCL_ERROR; 181 } 182 fseek(out, offset, SEEK_SET); 183 written = fwrite(aOut, 1, nOut, out); 184 sqlite3_free(aOut); 185 fclose(out); 186 Tcl_SetObjResult(interp, Tcl_NewIntObj(written)); 187 return TCL_OK; 188 } 189 190 /* 191 ** USAGE: hexio_get_int HEXDATA 192 ** 193 ** Interpret the HEXDATA argument as a big-endian integer. Return 194 ** the value of that integer. HEXDATA can contain between 2 and 8 195 ** hexadecimal digits. 196 */ 197 static int hexio_get_int( 198 void * clientData, 199 Tcl_Interp *interp, 200 int objc, 201 Tcl_Obj *CONST objv[] 202 ){ 203 int val; 204 int nIn, nOut; 205 const unsigned char *zIn; 206 unsigned char *aOut; 207 unsigned char aNum[4]; 208 209 if( objc!=2 ){ 210 Tcl_WrongNumArgs(interp, 1, objv, "HEXDATA"); 211 return TCL_ERROR; 212 } 213 zIn = (const unsigned char *)Tcl_GetStringFromObj(objv[1], &nIn); 214 aOut = sqlite3_malloc( nIn/2 ); 215 if( aOut==0 ){ 216 return TCL_ERROR; 217 } 218 nOut = sqlite3TestHexToBin(zIn, nIn, aOut); 219 if( nOut>=4 ){ 220 memcpy(aNum, aOut, 4); 221 }else{ 222 memset(aNum, 0, sizeof(aNum)); 223 memcpy(&aNum[4-nOut], aOut, nOut); 224 } 225 sqlite3_free(aOut); 226 val = (aNum[0]<<24) | (aNum[1]<<16) | (aNum[2]<<8) | aNum[3]; 227 Tcl_SetObjResult(interp, Tcl_NewIntObj(val)); 228 return TCL_OK; 229 } 230 231 232 /* 233 ** USAGE: hexio_render_int16 INTEGER 234 ** 235 ** Render INTEGER has a 16-bit big-endian integer in hexadecimal. 236 */ 237 static int hexio_render_int16( 238 void * clientData, 239 Tcl_Interp *interp, 240 int objc, 241 Tcl_Obj *CONST objv[] 242 ){ 243 int val; 244 unsigned char aNum[10]; 245 246 if( objc!=2 ){ 247 Tcl_WrongNumArgs(interp, 1, objv, "INTEGER"); 248 return TCL_ERROR; 249 } 250 if( Tcl_GetIntFromObj(interp, objv[1], &val) ) return TCL_ERROR; 251 aNum[0] = val>>8; 252 aNum[1] = val; 253 sqlite3TestBinToHex(aNum, 2); 254 Tcl_SetObjResult(interp, Tcl_NewStringObj((char*)aNum, 4)); 255 return TCL_OK; 256 } 257 258 259 /* 260 ** USAGE: hexio_render_int32 INTEGER 261 ** 262 ** Render INTEGER has a 32-bit big-endian integer in hexadecimal. 263 */ 264 static int hexio_render_int32( 265 void * clientData, 266 Tcl_Interp *interp, 267 int objc, 268 Tcl_Obj *CONST objv[] 269 ){ 270 int val; 271 unsigned char aNum[10]; 272 273 if( objc!=2 ){ 274 Tcl_WrongNumArgs(interp, 1, objv, "INTEGER"); 275 return TCL_ERROR; 276 } 277 if( Tcl_GetIntFromObj(interp, objv[1], &val) ) return TCL_ERROR; 278 aNum[0] = val>>24; 279 aNum[1] = val>>16; 280 aNum[2] = val>>8; 281 aNum[3] = val; 282 sqlite3TestBinToHex(aNum, 4); 283 Tcl_SetObjResult(interp, Tcl_NewStringObj((char*)aNum, 8)); 284 return TCL_OK; 285 } 286 287 /* 288 ** USAGE: utf8_to_utf8 HEX 289 ** 290 ** The argument is a UTF8 string represented in hexadecimal. 291 ** The UTF8 might not be well-formed. Run this string through 292 ** sqlite3Utf8to8() convert it back to hex and return the result. 293 */ 294 static int utf8_to_utf8( 295 void * clientData, 296 Tcl_Interp *interp, 297 int objc, 298 Tcl_Obj *CONST objv[] 299 ){ 300 #ifdef SQLITE_DEBUG 301 int n; 302 int nOut; 303 const unsigned char *zOrig; 304 unsigned char *z; 305 if( objc!=2 ){ 306 Tcl_WrongNumArgs(interp, 1, objv, "HEX"); 307 return TCL_ERROR; 308 } 309 zOrig = (unsigned char *)Tcl_GetStringFromObj(objv[1], &n); 310 z = sqlite3_malloc( n+3 ); 311 n = sqlite3TestHexToBin(zOrig, n, z); 312 z[n] = 0; 313 nOut = sqlite3Utf8To8(z); 314 sqlite3TestBinToHex(z,nOut); 315 Tcl_AppendResult(interp, (char*)z, 0); 316 sqlite3_free(z); 317 #endif 318 return TCL_OK; 319 } 320 321 322 /* 323 ** Register commands with the TCL interpreter. 324 */ 325 int Sqlitetest_hexio_Init(Tcl_Interp *interp){ 326 static struct { 327 char *zName; 328 Tcl_ObjCmdProc *xProc; 329 } aObjCmd[] = { 330 { "hexio_read", hexio_read }, 331 { "hexio_write", hexio_write }, 332 { "hexio_get_int", hexio_get_int }, 333 { "hexio_render_int16", hexio_render_int16 }, 334 { "hexio_render_int32", hexio_render_int32 }, 335 { "utf8_to_utf8", utf8_to_utf8 }, 336 }; 337 int i; 338 for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){ 339 Tcl_CreateObjCommand(interp, aObjCmd[i].zName, aObjCmd[i].xProc, 0, 0); 340 } 341 return TCL_OK; 342 } 343