blob: 9e1e8de80d021b0f3d6deff62019e4195b2117d6 [file] [log] [blame]
drh15926592007-04-06 15:02:13 +00001/*
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**
drh498b8762008-05-12 16:17:42 +000020** $Id: test_hexio.c,v 1.7 2008/05/12 16:17:42 drh Exp $
drh15926592007-04-06 15:02:13 +000021*/
drh53c14022007-05-10 17:23:11 +000022#include "sqliteInt.h"
drh15926592007-04-06 15:02:13 +000023#include "tcl.h"
24#include <stdlib.h>
25#include <string.h>
26#include <assert.h>
27
drh15926592007-04-06 15:02:13 +000028
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*/
drh9c7a60d2007-10-19 17:47:24 +000034void sqlite3TestBinToHex(unsigned char *zBuf, int N){
drh15926592007-04-06 15:02:13 +000035 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*/
drh9c7a60d2007-10-19 17:47:24 +000054int sqlite3TestHexToBin(const unsigned char *zIn, int N, unsigned char *aOut){
drh15926592007-04-06 15:02:13 +000055 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*/
99static 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]);
drh17435752007-08-16 04:30:38 +0000118 zBuf = sqlite3_malloc( amt*2+1 );
drh15926592007-04-06 15:02:13 +0000119 if( zBuf==0 ){
120 return TCL_ERROR;
121 }
drh498b8762008-05-12 16:17:42 +0000122 in = fopen(zFile, "rb");
123 if( in==0 ){
124 in = fopen(zFile, "r");
125 }
drh15926592007-04-06 15:02:13 +0000126 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 }
drh9c7a60d2007-10-19 17:47:24 +0000136 sqlite3TestBinToHex(zBuf, got);
drh15926592007-04-06 15:02:13 +0000137 Tcl_AppendResult(interp, zBuf, 0);
drh17435752007-08-16 04:30:38 +0000138 sqlite3_free(zBuf);
drh15926592007-04-06 15:02:13 +0000139 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*/
149static 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);
drh17435752007-08-16 04:30:38 +0000169 aOut = sqlite3_malloc( nIn/2 );
drh15926592007-04-06 15:02:13 +0000170 if( aOut==0 ){
171 return TCL_ERROR;
172 }
drh9c7a60d2007-10-19 17:47:24 +0000173 nOut = sqlite3TestHexToBin(zIn, nIn, aOut);
drh498b8762008-05-12 16:17:42 +0000174 out = fopen(zFile, "r+b");
175 if( out==0 ){
176 out = fopen(zFile, "r+");
177 }
drh15926592007-04-06 15:02:13 +0000178 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);
drh17435752007-08-16 04:30:38 +0000184 sqlite3_free(aOut);
drh15926592007-04-06 15:02:13 +0000185 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*/
197static 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);
drh17435752007-08-16 04:30:38 +0000214 aOut = sqlite3_malloc( nIn/2 );
drh15926592007-04-06 15:02:13 +0000215 if( aOut==0 ){
216 return TCL_ERROR;
217 }
drh9c7a60d2007-10-19 17:47:24 +0000218 nOut = sqlite3TestHexToBin(zIn, nIn, aOut);
drh15926592007-04-06 15:02:13 +0000219 if( nOut>=4 ){
220 memcpy(aNum, aOut, 4);
221 }else{
222 memset(aNum, 0, sizeof(aNum));
drh431e8532007-04-09 20:30:11 +0000223 memcpy(&aNum[4-nOut], aOut, nOut);
drh15926592007-04-06 15:02:13 +0000224 }
drh17435752007-08-16 04:30:38 +0000225 sqlite3_free(aOut);
drh15926592007-04-06 15:02:13 +0000226 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*/
237static 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;
drh9c7a60d2007-10-19 17:47:24 +0000253 sqlite3TestBinToHex(aNum, 2);
drh15926592007-04-06 15:02:13 +0000254 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*/
264static 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;
drh9c7a60d2007-10-19 17:47:24 +0000282 sqlite3TestBinToHex(aNum, 4);
drh15926592007-04-06 15:02:13 +0000283 Tcl_SetObjResult(interp, Tcl_NewStringObj((char*)aNum, 8));
284 return TCL_OK;
285}
286
drh53c14022007-05-10 17:23:11 +0000287/*
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*/
294static int utf8_to_utf8(
295 void * clientData,
296 Tcl_Interp *interp,
297 int objc,
298 Tcl_Obj *CONST objv[]
299){
danielk197728c66302007-09-01 11:04:26 +0000300#ifdef SQLITE_DEBUG
drh53c14022007-05-10 17:23:11 +0000301 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 );
drh9c7a60d2007-10-19 17:47:24 +0000311 n = sqlite3TestHexToBin(zOrig, n, z);
drh53c14022007-05-10 17:23:11 +0000312 z[n] = 0;
313 nOut = sqlite3Utf8To8(z);
drh9c7a60d2007-10-19 17:47:24 +0000314 sqlite3TestBinToHex(z,nOut);
drh53c14022007-05-10 17:23:11 +0000315 Tcl_AppendResult(interp, (char*)z, 0);
316 sqlite3_free(z);
danielk197728c66302007-09-01 11:04:26 +0000317#endif
drh53c14022007-05-10 17:23:11 +0000318 return TCL_OK;
319}
drh15926592007-04-06 15:02:13 +0000320
321
322/*
323** Register commands with the TCL interpreter.
324*/
325int 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 },
drh53c14022007-05-10 17:23:11 +0000335 { "utf8_to_utf8", utf8_to_utf8 },
drh15926592007-04-06 15:02:13 +0000336 };
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}