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