OLD | NEW |
| (Empty) |
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 */ | |
32 void 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 */ | |
52 int 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 */ | |
97 static 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 = (int)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 */ | |
147 static 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 = (int)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 */ | |
195 static 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 */ | |
235 static 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 */ | |
262 static 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 */ | |
292 static 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 | |
324 static 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 */ | |
343 static 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 */ | |
370 int 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 } | |
OLD | NEW |