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*/
32void 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*/
52int 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*/
97static 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*/
147static 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*/
195static 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*/
235static 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*/
262static 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*/
292static 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
324static 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*/
343static 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*/
370int 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