• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 */
sqlite3TestBinToHex(unsigned char * zBuf,int N)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 */
sqlite3TestHexToBin(const unsigned char * zIn,int N,unsigned char * aOut)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 */
hexio_read(void * clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])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 */
hexio_write(void * clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])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 */
hexio_get_int(void * clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])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 */
hexio_render_int16(void * clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])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 */
hexio_render_int32(void * clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])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 */
utf8_to_utf8(void * clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])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 
getFts3Varint(const char * p,sqlite_int64 * v)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 */
read_fts3varint(void * clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])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 */
Sqlitetest_hexio_Init(Tcl_Interp * interp)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