xref: /sqlite-3.40.0/src/test_hexio.c (revision cd7274ce)
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