|
1 /* |
|
2 ** 2007 April 6 |
|
3 ** |
|
4 ** Portions Copyright (c) 2008 Nokia Corporation and/or its subsidiaries. All rights reserved. |
|
5 ** |
|
6 ** The author disclaims copyright to this source code. In place of |
|
7 ** a legal notice, here is a blessing: |
|
8 ** |
|
9 ** May you do good and not evil. |
|
10 ** May you find forgiveness for yourself and forgive others. |
|
11 ** May you share freely, never taking more than you give. |
|
12 ** |
|
13 ************************************************************************* |
|
14 ** Code for testing all sorts of SQLite interfaces. This code |
|
15 ** implements TCL commands for reading and writing the binary |
|
16 ** database files and displaying the content of those files as |
|
17 ** hexadecimal. We could, in theory, use the built-in "binary" |
|
18 ** command of TCL to do a lot of this, but there are some issues |
|
19 ** with historical versions of the "binary" command. So it seems |
|
20 ** easier and safer to build our own mechanism. |
|
21 ** |
|
22 ** $Id: test_hexio.c,v 1.7 2008/05/12 16:17:42 drh Exp $ |
|
23 */ |
|
24 #include "sqliteInt.h" |
|
25 #include "tcl.h" |
|
26 #include <stdlib.h> |
|
27 #include <string.h> |
|
28 #include <assert.h> |
|
29 #include <unistd.h> |
|
30 #include <sys/param.h> |
|
31 |
|
32 extern char* FullFilePath(char* aPath, const char* aFileName); |
|
33 |
|
34 /* Symbian OS - often TCL scripts attempt to open a file just using the file name without path. |
|
35 This is not working on the hardware. The function bellow will contstruct the full file name. */ |
|
36 extern char* GetFullFilePath(char* aPath, const char* aFileName) |
|
37 { |
|
38 return FullFilePath(aPath, aFileName); |
|
39 } |
|
40 |
|
41 /* |
|
42 ** Convert binary to hex. The input zBuf[] contains N bytes of |
|
43 ** binary data. zBuf[] is 2*n+1 bytes long. Overwrite zBuf[] |
|
44 ** with a hexadecimal representation of its original binary input. |
|
45 */ |
|
46 void sqlite3TestBinToHex(unsigned char *zBuf, int N){ |
|
47 const unsigned char zHex[] = "0123456789ABCDEF"; |
|
48 int i, j; |
|
49 unsigned char c; |
|
50 i = N*2; |
|
51 zBuf[i--] = 0; |
|
52 for(j=N-1; j>=0; j--){ |
|
53 c = zBuf[j]; |
|
54 zBuf[i--] = zHex[c&0xf]; |
|
55 zBuf[i--] = zHex[c>>4]; |
|
56 } |
|
57 assert( i==-1 ); |
|
58 } |
|
59 |
|
60 /* |
|
61 ** Convert hex to binary. The input zIn[] contains N bytes of |
|
62 ** hexadecimal. Convert this into binary and write aOut[] with |
|
63 ** the binary data. Spaces in the original input are ignored. |
|
64 ** Return the number of bytes of binary rendered. |
|
65 */ |
|
66 int sqlite3TestHexToBin(const unsigned char *zIn, int N, unsigned char *aOut){ |
|
67 const unsigned char aMap[] = { |
|
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 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 0, 0, 0, 0, 0, 0, |
|
72 0,11,12,13,14,15,16, 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,11,12,13,14,15,16, 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 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
|
82 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
|
83 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
|
84 }; |
|
85 int i, j; |
|
86 int hi=1; |
|
87 unsigned char c; |
|
88 |
|
89 for(i=j=0; i<N; i++){ |
|
90 c = aMap[zIn[i]]; |
|
91 if( c==0 ) continue; |
|
92 if( hi ){ |
|
93 aOut[j] = (c-1)<<4; |
|
94 hi = 0; |
|
95 }else{ |
|
96 aOut[j++] |= c-1; |
|
97 hi = 1; |
|
98 } |
|
99 } |
|
100 return j; |
|
101 } |
|
102 |
|
103 |
|
104 /* |
|
105 ** Usage: hexio_read FILENAME OFFSET AMT |
|
106 ** |
|
107 ** Read AMT bytes from file FILENAME beginning at OFFSET from the |
|
108 ** beginning of the file. Convert that information to hexadecimal |
|
109 ** and return the resulting HEX string. |
|
110 */ |
|
111 static int hexio_read( |
|
112 void * clientData, |
|
113 Tcl_Interp *interp, |
|
114 int objc, |
|
115 Tcl_Obj *CONST objv[] |
|
116 ){ |
|
117 int offset; |
|
118 int amt, got; |
|
119 const char *zFile; |
|
120 unsigned char *zBuf; |
|
121 FILE *in; |
|
122 char fnamebuf[MAXPATHLEN + 1]; |
|
123 |
|
124 if( objc!=4 ){ |
|
125 Tcl_WrongNumArgs(interp, 1, objv, "FILENAME OFFSET AMT"); |
|
126 return TCL_ERROR; |
|
127 } |
|
128 if( Tcl_GetIntFromObj(interp, objv[2], &offset) ) return TCL_ERROR; |
|
129 if( Tcl_GetIntFromObj(interp, objv[3], &amt) ) return TCL_ERROR; |
|
130 zFile = Tcl_GetString(objv[1]); |
|
131 zBuf = sqlite3_malloc( amt*2+1 ); |
|
132 if( zBuf==0 ){ |
|
133 return TCL_ERROR; |
|
134 } |
|
135 if(GetFullFilePath(fnamebuf, zFile) == 0) |
|
136 return TCL_ERROR; |
|
137 in = fopen(fnamebuf, "rb"); |
|
138 if( in==0 ){ |
|
139 in = fopen(fnamebuf, "r"); |
|
140 } |
|
141 if( in==0 ){ |
|
142 Tcl_AppendResult(interp, "cannot open input file ", fnamebuf, 0); |
|
143 return TCL_ERROR; |
|
144 } |
|
145 fseek(in, offset, SEEK_SET); |
|
146 got = fread(zBuf, 1, amt, in); |
|
147 fclose(in); |
|
148 if( got<0 ){ |
|
149 got = 0; |
|
150 } |
|
151 sqlite3TestBinToHex(zBuf, got); |
|
152 Tcl_AppendResult(interp, zBuf, 0); |
|
153 sqlite3_free(zBuf); |
|
154 return TCL_OK; |
|
155 } |
|
156 |
|
157 |
|
158 /* |
|
159 ** Usage: hexio_write FILENAME OFFSET DATA |
|
160 ** |
|
161 ** Write DATA into file FILENAME beginning at OFFSET from the |
|
162 ** beginning of the file. DATA is expressed in hexadecimal. |
|
163 */ |
|
164 static int hexio_write( |
|
165 void * clientData, |
|
166 Tcl_Interp *interp, |
|
167 int objc, |
|
168 Tcl_Obj *CONST objv[] |
|
169 ){ |
|
170 int offset; |
|
171 int nIn, nOut, written; |
|
172 const char *zFile; |
|
173 const unsigned char *zIn; |
|
174 unsigned char *aOut; |
|
175 FILE *out; |
|
176 char fnamebuf[MAXPATHLEN + 1]; |
|
177 |
|
178 if( objc!=4 ){ |
|
179 Tcl_WrongNumArgs(interp, 1, objv, "FILENAME OFFSET HEXDATA"); |
|
180 return TCL_ERROR; |
|
181 } |
|
182 if( Tcl_GetIntFromObj(interp, objv[2], &offset) ) return TCL_ERROR; |
|
183 zFile = Tcl_GetString(objv[1]); |
|
184 zIn = (const unsigned char *)Tcl_GetStringFromObj(objv[3], &nIn); |
|
185 aOut = sqlite3_malloc( nIn/2 ); |
|
186 if( aOut==0 ){ |
|
187 return TCL_ERROR; |
|
188 } |
|
189 nOut = sqlite3TestHexToBin(zIn, nIn, aOut); |
|
190 if(GetFullFilePath(fnamebuf, zFile) == 0) |
|
191 return TCL_ERROR; |
|
192 out = fopen(fnamebuf, "r+b"); |
|
193 if( out==0 ){ |
|
194 out = fopen(fnamebuf, "r+"); |
|
195 } |
|
196 if( out==0 ){ |
|
197 Tcl_AppendResult(interp, "cannot open output file ", fnamebuf, 0); |
|
198 return TCL_ERROR; |
|
199 } |
|
200 fseek(out, offset, SEEK_SET); |
|
201 written = fwrite(aOut, 1, nOut, out); |
|
202 sqlite3_free(aOut); |
|
203 fclose(out); |
|
204 Tcl_SetObjResult(interp, Tcl_NewIntObj(written)); |
|
205 return TCL_OK; |
|
206 } |
|
207 |
|
208 /* |
|
209 ** USAGE: hexio_get_int HEXDATA |
|
210 ** |
|
211 ** Interpret the HEXDATA argument as a big-endian integer. Return |
|
212 ** the value of that integer. HEXDATA can contain between 2 and 8 |
|
213 ** hexadecimal digits. |
|
214 */ |
|
215 static int hexio_get_int( |
|
216 void * clientData, |
|
217 Tcl_Interp *interp, |
|
218 int objc, |
|
219 Tcl_Obj *CONST objv[] |
|
220 ){ |
|
221 int val; |
|
222 int nIn, nOut; |
|
223 const unsigned char *zIn; |
|
224 unsigned char *aOut; |
|
225 unsigned char aNum[4]; |
|
226 |
|
227 if( objc!=2 ){ |
|
228 Tcl_WrongNumArgs(interp, 1, objv, "HEXDATA"); |
|
229 return TCL_ERROR; |
|
230 } |
|
231 zIn = (const unsigned char *)Tcl_GetStringFromObj(objv[1], &nIn); |
|
232 aOut = sqlite3_malloc( nIn/2 ); |
|
233 if( aOut==0 ){ |
|
234 return TCL_ERROR; |
|
235 } |
|
236 nOut = sqlite3TestHexToBin(zIn, nIn, aOut); |
|
237 if( nOut>=4 ){ |
|
238 memcpy(aNum, aOut, 4); |
|
239 }else{ |
|
240 memset(aNum, 0, sizeof(aNum)); |
|
241 memcpy(&aNum[4-nOut], aOut, nOut); |
|
242 } |
|
243 sqlite3_free(aOut); |
|
244 val = (aNum[0]<<24) | (aNum[1]<<16) | (aNum[2]<<8) | aNum[3]; |
|
245 Tcl_SetObjResult(interp, Tcl_NewIntObj(val)); |
|
246 return TCL_OK; |
|
247 } |
|
248 |
|
249 |
|
250 /* |
|
251 ** USAGE: hexio_render_int16 INTEGER |
|
252 ** |
|
253 ** Render INTEGER has a 16-bit big-endian integer in hexadecimal. |
|
254 */ |
|
255 static int hexio_render_int16( |
|
256 void * clientData, |
|
257 Tcl_Interp *interp, |
|
258 int objc, |
|
259 Tcl_Obj *CONST objv[] |
|
260 ){ |
|
261 int val; |
|
262 unsigned char aNum[10]; |
|
263 |
|
264 if( objc!=2 ){ |
|
265 Tcl_WrongNumArgs(interp, 1, objv, "INTEGER"); |
|
266 return TCL_ERROR; |
|
267 } |
|
268 if( Tcl_GetIntFromObj(interp, objv[1], &val) ) return TCL_ERROR; |
|
269 aNum[0] = val>>8; |
|
270 aNum[1] = val; |
|
271 sqlite3TestBinToHex(aNum, 2); |
|
272 Tcl_SetObjResult(interp, Tcl_NewStringObj((char*)aNum, 4)); |
|
273 return TCL_OK; |
|
274 } |
|
275 |
|
276 |
|
277 /* |
|
278 ** USAGE: hexio_render_int32 INTEGER |
|
279 ** |
|
280 ** Render INTEGER has a 32-bit big-endian integer in hexadecimal. |
|
281 */ |
|
282 static int hexio_render_int32( |
|
283 void * clientData, |
|
284 Tcl_Interp *interp, |
|
285 int objc, |
|
286 Tcl_Obj *CONST objv[] |
|
287 ){ |
|
288 int val; |
|
289 unsigned char aNum[10]; |
|
290 |
|
291 if( objc!=2 ){ |
|
292 Tcl_WrongNumArgs(interp, 1, objv, "INTEGER"); |
|
293 return TCL_ERROR; |
|
294 } |
|
295 if( Tcl_GetIntFromObj(interp, objv[1], &val) ) return TCL_ERROR; |
|
296 aNum[0] = val>>24; |
|
297 aNum[1] = val>>16; |
|
298 aNum[2] = val>>8; |
|
299 aNum[3] = val; |
|
300 sqlite3TestBinToHex(aNum, 4); |
|
301 Tcl_SetObjResult(interp, Tcl_NewStringObj((char*)aNum, 8)); |
|
302 return TCL_OK; |
|
303 } |
|
304 |
|
305 /* |
|
306 ** USAGE: utf8_to_utf8 HEX |
|
307 ** |
|
308 ** The argument is a UTF8 string represented in hexadecimal. |
|
309 ** The UTF8 might not be well-formed. Run this string through |
|
310 ** sqlite3Utf8to8() convert it back to hex and return the result. |
|
311 */ |
|
312 static int utf8_to_utf8( |
|
313 void * clientData, |
|
314 Tcl_Interp *interp, |
|
315 int objc, |
|
316 Tcl_Obj *CONST objv[] |
|
317 ){ |
|
318 #ifdef SQLITE_DEBUG |
|
319 int n; |
|
320 int nOut; |
|
321 const unsigned char *zOrig; |
|
322 unsigned char *z; |
|
323 if( objc!=2 ){ |
|
324 Tcl_WrongNumArgs(interp, 1, objv, "HEX"); |
|
325 return TCL_ERROR; |
|
326 } |
|
327 zOrig = (unsigned char *)Tcl_GetStringFromObj(objv[1], &n); |
|
328 z = sqlite3_malloc( n+3 ); |
|
329 n = sqlite3TestHexToBin(zOrig, n, z); |
|
330 z[n] = 0; |
|
331 nOut = sqlite3Utf8To8(z); |
|
332 sqlite3TestBinToHex(z,nOut); |
|
333 Tcl_AppendResult(interp, (char*)z, 0); |
|
334 sqlite3_free(z); |
|
335 #endif |
|
336 return TCL_OK; |
|
337 } |
|
338 |
|
339 |
|
340 /* |
|
341 ** Register commands with the TCL interpreter. |
|
342 */ |
|
343 int Sqlitetest_hexio_Init(Tcl_Interp *interp){ |
|
344 static struct { |
|
345 char *zName; |
|
346 Tcl_ObjCmdProc *xProc; |
|
347 } aObjCmd[] = { |
|
348 { "hexio_read", hexio_read }, |
|
349 { "hexio_write", hexio_write }, |
|
350 { "hexio_get_int", hexio_get_int }, |
|
351 { "hexio_render_int16", hexio_render_int16 }, |
|
352 { "hexio_render_int32", hexio_render_int32 }, |
|
353 { "utf8_to_utf8", utf8_to_utf8 }, |
|
354 }; |
|
355 int i; |
|
356 for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){ |
|
357 Tcl_CreateObjCommand(interp, aObjCmd[i].zName, aObjCmd[i].xProc, 0, 0); |
|
358 } |
|
359 return TCL_OK; |
|
360 } |