xref: /sqlite-3.40.0/src/test_hexio.c (revision d230f648)
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.2 2007/04/09 20:30:11 drh Exp $
21 */
22 #include "tcl.h"
23 #include <stdlib.h>
24 #include <string.h>
25 #include <assert.h>
26 
27 /*
28 hexio_read  filename offset amt
29 hexio_write filename offset hexdata
30 hexio_get_int  hexdata
31 hexio_get_varint  hexdata
32 hexio_render_int8  integer
33 hexio_render_int16  integer
34 hexio_render_int38  integer
35 hexio_render_varint  integer
36 */
37 
38 /*
39 ** Convert binary to hex.  The input zBuf[] contains N bytes of
40 ** binary data.  zBuf[] is 2*n+1 bytes long.  Overwrite zBuf[]
41 ** with a hexadecimal representation of its original binary input.
42 */
43 static void binToHex(unsigned char *zBuf, int N){
44   const unsigned char zHex[] = "0123456789ABCDEF";
45   int i, j;
46   unsigned char c;
47   i = N*2;
48   zBuf[i--] = 0;
49   for(j=N-1; j>=0; j--){
50     c = zBuf[j];
51     zBuf[i--] = zHex[c&0xf];
52     zBuf[i--] = zHex[c>>4];
53   }
54   assert( i==-1 );
55 }
56 
57 /*
58 ** Convert hex to binary.  The input zIn[] contains N bytes of
59 ** hexadecimal.  Convert this into binary and write aOut[] with
60 ** the binary data.  Spaces in the original input are ignored.
61 ** Return the number of bytes of binary rendered.
62 */
63 static int hexToBin(const unsigned char *zIn, int N, unsigned char *aOut){
64   const unsigned char aMap[] = {
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      1, 2, 3, 4, 5, 6, 7, 8,  9,10, 0, 0, 0, 0, 0, 0,
69      0,11,12,13,14,15,16, 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,11,12,13,14,15,16, 0,  0, 0, 0, 0, 0, 0, 0, 0,
72      0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
73      0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
74      0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
75      0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
76      0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
77      0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
78      0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
79      0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
80      0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
81   };
82   int i, j;
83   int hi=1;
84   unsigned char c;
85 
86   for(i=j=0; i<N; i++){
87     c = aMap[zIn[i]];
88     if( c==0 ) continue;
89     if( hi ){
90       aOut[j] = (c-1)<<4;
91       hi = 0;
92     }else{
93       aOut[j++] |= c-1;
94       hi = 1;
95     }
96   }
97   return j;
98 }
99 
100 
101 /*
102 ** Usage:   hexio_read  FILENAME  OFFSET  AMT
103 **
104 ** Read AMT bytes from file FILENAME beginning at OFFSET from the
105 ** beginning of the file.  Convert that information to hexadecimal
106 ** and return the resulting HEX string.
107 */
108 static int hexio_read(
109   void * clientData,
110   Tcl_Interp *interp,
111   int objc,
112   Tcl_Obj *CONST objv[]
113 ){
114   int offset;
115   int amt, got;
116   const char *zFile;
117   unsigned char *zBuf;
118   FILE *in;
119 
120   if( objc!=4 ){
121     Tcl_WrongNumArgs(interp, 1, objv, "FILENAME OFFSET AMT");
122     return TCL_ERROR;
123   }
124   if( Tcl_GetIntFromObj(interp, objv[2], &offset) ) return TCL_ERROR;
125   if( Tcl_GetIntFromObj(interp, objv[3], &amt) ) return TCL_ERROR;
126   zFile = Tcl_GetString(objv[1]);
127   zBuf = malloc( amt*2+1 );
128   if( zBuf==0 ){
129     return TCL_ERROR;
130   }
131   in = fopen(zFile, "r");
132   if( in==0 ){
133     Tcl_AppendResult(interp, "cannot open input file ", zFile, 0);
134     return TCL_ERROR;
135   }
136   fseek(in, offset, SEEK_SET);
137   got = fread(zBuf, 1, amt, in);
138   fclose(in);
139   if( got<0 ){
140     got = 0;
141   }
142   binToHex(zBuf, got);
143   Tcl_AppendResult(interp, zBuf, 0);
144   free(zBuf);
145   return TCL_OK;
146 }
147 
148 
149 /*
150 ** Usage:   hexio_write  FILENAME  OFFSET  DATA
151 **
152 ** Write DATA into file FILENAME beginning at OFFSET from the
153 ** beginning of the file.  DATA is expressed in hexadecimal.
154 */
155 static int hexio_write(
156   void * clientData,
157   Tcl_Interp *interp,
158   int objc,
159   Tcl_Obj *CONST objv[]
160 ){
161   int offset;
162   int nIn, nOut, written;
163   const char *zFile;
164   const unsigned char *zIn;
165   unsigned char *aOut;
166   FILE *out;
167 
168   if( objc!=4 ){
169     Tcl_WrongNumArgs(interp, 1, objv, "FILENAME OFFSET HEXDATA");
170     return TCL_ERROR;
171   }
172   if( Tcl_GetIntFromObj(interp, objv[2], &offset) ) return TCL_ERROR;
173   zFile = Tcl_GetString(objv[1]);
174   zIn = (const unsigned char *)Tcl_GetStringFromObj(objv[3], &nIn);
175   aOut = malloc( nIn/2 );
176   if( aOut==0 ){
177     return TCL_ERROR;
178   }
179   nOut = hexToBin(zIn, nIn, aOut);
180   out = fopen(zFile, "r+");
181   if( out==0 ){
182     Tcl_AppendResult(interp, "cannot open output file ", zFile, 0);
183     return TCL_ERROR;
184   }
185   fseek(out, offset, SEEK_SET);
186   written = fwrite(aOut, 1, nOut, out);
187   free(aOut);
188   fclose(out);
189   Tcl_SetObjResult(interp, Tcl_NewIntObj(written));
190   return TCL_OK;
191 }
192 
193 /*
194 ** USAGE:   hexio_get_int   HEXDATA
195 **
196 ** Interpret the HEXDATA argument as a big-endian integer.  Return
197 ** the value of that integer.  HEXDATA can contain between 2 and 8
198 ** hexadecimal digits.
199 */
200 static int hexio_get_int(
201   void * clientData,
202   Tcl_Interp *interp,
203   int objc,
204   Tcl_Obj *CONST objv[]
205 ){
206   int val;
207   int nIn, nOut;
208   const unsigned char *zIn;
209   unsigned char *aOut;
210   unsigned char aNum[4];
211 
212   if( objc!=2 ){
213     Tcl_WrongNumArgs(interp, 1, objv, "HEXDATA");
214     return TCL_ERROR;
215   }
216   zIn = (const unsigned char *)Tcl_GetStringFromObj(objv[1], &nIn);
217   aOut = malloc( nIn/2 );
218   if( aOut==0 ){
219     return TCL_ERROR;
220   }
221   nOut = hexToBin(zIn, nIn, aOut);
222   if( nOut>=4 ){
223     memcpy(aNum, aOut, 4);
224   }else{
225     memset(aNum, 0, sizeof(aNum));
226     memcpy(&aNum[4-nOut], aOut, nOut);
227   }
228   free(aOut);
229   val = (aNum[0]<<24) | (aNum[1]<<16) | (aNum[2]<<8) | aNum[3];
230   Tcl_SetObjResult(interp, Tcl_NewIntObj(val));
231   return TCL_OK;
232 }
233 
234 
235 /*
236 ** USAGE:   hexio_render_int16   INTEGER
237 **
238 ** Render INTEGER has a 16-bit big-endian integer in hexadecimal.
239 */
240 static int hexio_render_int16(
241   void * clientData,
242   Tcl_Interp *interp,
243   int objc,
244   Tcl_Obj *CONST objv[]
245 ){
246   int val;
247   unsigned char aNum[10];
248 
249   if( objc!=2 ){
250     Tcl_WrongNumArgs(interp, 1, objv, "INTEGER");
251     return TCL_ERROR;
252   }
253   if( Tcl_GetIntFromObj(interp, objv[1], &val) ) return TCL_ERROR;
254   aNum[0] = val>>8;
255   aNum[1] = val;
256   binToHex(aNum, 2);
257   Tcl_SetObjResult(interp, Tcl_NewStringObj((char*)aNum, 4));
258   return TCL_OK;
259 }
260 
261 
262 /*
263 ** USAGE:   hexio_render_int32   INTEGER
264 **
265 ** Render INTEGER has a 32-bit big-endian integer in hexadecimal.
266 */
267 static int hexio_render_int32(
268   void * clientData,
269   Tcl_Interp *interp,
270   int objc,
271   Tcl_Obj *CONST objv[]
272 ){
273   int val;
274   unsigned char aNum[10];
275 
276   if( objc!=2 ){
277     Tcl_WrongNumArgs(interp, 1, objv, "INTEGER");
278     return TCL_ERROR;
279   }
280   if( Tcl_GetIntFromObj(interp, objv[1], &val) ) return TCL_ERROR;
281   aNum[0] = val>>24;
282   aNum[1] = val>>16;
283   aNum[2] = val>>8;
284   aNum[3] = val;
285   binToHex(aNum, 4);
286   Tcl_SetObjResult(interp, Tcl_NewStringObj((char*)aNum, 8));
287   return TCL_OK;
288 }
289 
290 
291 
292 /*
293 ** Register commands with the TCL interpreter.
294 */
295 int Sqlitetest_hexio_Init(Tcl_Interp *interp){
296   static struct {
297      char *zName;
298      Tcl_ObjCmdProc *xProc;
299   } aObjCmd[] = {
300      { "hexio_read",                   hexio_read            },
301      { "hexio_write",                  hexio_write           },
302      { "hexio_get_int",                hexio_get_int         },
303      { "hexio_render_int16",           hexio_render_int16    },
304      { "hexio_render_int32",           hexio_render_int32    },
305   };
306   int i;
307   for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
308     Tcl_CreateObjCommand(interp, aObjCmd[i].zName, aObjCmd[i].xProc, 0, 0);
309   }
310   return TCL_OK;
311 }
312