Merge trunk changes into this branch.
[sqlite.git] / tool / dbtotxt.c
blobfbd6e3d5195171a510ce4cb187b6f924b7334235
1 /*
2 ** Copyright 2008 D. Richard Hipp and Hipp, Wyrick & Company, Inc.
3 ** All Rights Reserved
4 **
5 ******************************************************************************
6 **
7 ** This file implements a stand-alone utility program that converts
8 ** a binary file (usually an SQLite database) into a text format that
9 ** is compact and friendly to human-readers.
11 ** Usage:
13 ** dbtotxt [OPTIONS] FILENAME
15 ** where OPTIONS are zero or more of:
17 ** --for-cli prepending '.open --hexdb' to the output
19 ** --script The input file is expected to start with a
20 ** zero-terminated SQL string. Output the
21 ** ".open --hexdb" header, then the database
22 ** then the SQL.
24 ** --pagesize N set the database page size for later reading
26 ** The translation of the database appears on standard output. If the
27 ** --pagesize command-line option is omitted, then the page size is taken
28 ** from the database header.
30 ** Compactness is achieved by suppressing lines of all zero bytes. This
31 ** works well at compressing test databases that are mostly empty. But
32 ** the output will probably be lengthy for a real database containing lots
33 ** of real content. For maximum compactness, it is suggested that test
34 ** databases be constructed with "zeroblob()" rather than "randomblob()"
35 ** used for filler content and with "PRAGMA secure_delete=ON" selected to
36 ** zero-out deleted content.
38 #include <stdio.h>
39 #include <string.h>
40 #include <stdlib.h>
41 #include <ctype.h>
43 /* Return true if the line is all zeros */
44 static int allZero(unsigned char *aLine){
45 int i;
46 for(i=0; i<16 && aLine[i]==0; i++){}
47 return i==16;
50 int main(int argc, char **argv){
51 int pgsz = 0; /* page size */
52 int forCli = 0; /* whether to prepend with .open */
53 int bSQL = 0; /* Expect and SQL prefix */
54 long szFile; /* Size of the input file in bytes */
55 FILE *in; /* Input file */
56 int nSQL; /* Number of bytes of script */
57 int i, j; /* Loop counters */
58 int nErr = 0; /* Number of errors */
59 const char *zInputFile = 0; /* Name of the input file */
60 const char *zBaseName = 0; /* Base name of the file */
61 int lastPage = 0; /* Last page number shown */
62 int iPage; /* Current page number */
63 unsigned char *aData = 0; /* All data */
64 unsigned char *aLine; /* A single line of the file */
65 unsigned char *aHdr; /* File header */
66 unsigned char bShow[256]; /* Characters ok to display */
67 memset(bShow, '.', sizeof(bShow));
68 for(i=' '; i<='~'; i++){
69 if( i!='{' && i!='}' && i!='"' && i!='\\' ) bShow[i] = (unsigned char)i;
71 for(i=1; i<argc; i++){
72 if( argv[i][0]=='-' ){
73 const char *z = argv[i];
74 z++;
75 if( z[0]=='-' ) z++;
76 if( strcmp(z,"pagesize")==0 ){
77 i++;
78 pgsz = atoi(argv[i]);
79 if( pgsz<512 || pgsz>65536 || (pgsz&(pgsz-1))!=0 ){
80 fprintf(stderr, "Page size must be a power of two between"
81 " 512 and 65536.\n");
82 nErr++;
84 continue;
85 }else if( strcmp(z,"for-cli")==0 ){
86 forCli = 1;
87 continue;
88 }else if( strcmp(z,"script")==0 ){
89 forCli = 1;
90 bSQL = 1;
91 continue;
93 fprintf(stderr, "Unknown option: %s\n", argv[i]);
94 nErr++;
95 }else if( zInputFile ){
96 fprintf(stderr, "Already using a different input file: [%s]\n", argv[i]);
97 nErr++;
98 }else{
99 zInputFile = argv[i];
102 if( zInputFile==0 ){
103 fprintf(stderr, "No input file specified.\n");
104 nErr++;
106 if( nErr ){
107 fprintf(stderr,
108 "Usage: %s [--pagesize N] [--script] [--for-cli] FILENAME\n", argv[0]);
109 exit(1);
111 in = fopen(zInputFile, "rb");
112 if( in==0 ){
113 fprintf(stderr, "Cannot open input file [%s]\n", zInputFile);
114 exit(1);
116 fseek(in, 0, SEEK_END);
117 szFile = ftell(in);
118 rewind(in);
119 if( szFile<100 ){
120 fprintf(stderr, "File too short. Minimum size is 100 bytes.\n");
121 exit(1);
123 aData = malloc( szFile+16 );
124 if( aData==0 ){
125 fprintf(stderr, "Failed to allocate %ld bytes\n", szFile);
126 exit(1);
128 if( fread(aData, szFile, 1, in)!=1 ){
129 fprintf(stderr, "Cannot read file info memory\n");
130 exit(1);
132 memset(aData+szFile, 0, 16);
133 fclose(in);
134 if( bSQL ){
135 for(i=0; i<szFile && aData[i]!=0; i++){}
136 if( i==szFile ){
137 fprintf(stderr, "No zero terminator on SQL script\n");
138 exit(1);
140 nSQL = i+1;
141 if( szFile - nSQL<100 ){
142 fprintf(stderr, "Less than 100 bytes in the database\n");
143 exit(1);
145 }else{
146 nSQL = 0;
148 aHdr = aData + nSQL;
149 if( pgsz==0 ){
150 pgsz = (aHdr[16]<<8) | aHdr[17];
151 if( pgsz==1 ) pgsz = 65536;
152 if( pgsz<512 || (pgsz&(pgsz-1))!=0 ){
153 fprintf(stderr, "Invalid page size in header: %d\n", pgsz);
154 exit(1);
157 zBaseName = zInputFile;
158 for(i=0; zInputFile[i]; i++){
159 if( zInputFile[i]=='/' && zInputFile[i+1]!=0 ) zBaseName = zInputFile+i+1;
161 if( forCli ){
162 printf(".open --hexdb\n");
164 printf("| size %d pagesize %d filename %s\n",(int)szFile,pgsz,zBaseName);
165 for(i=nSQL; i<szFile; i+=16){
166 aLine = aData+i;
167 if( allZero(aLine) ) continue;
168 iPage = i/pgsz + 1;
169 if( lastPage!=iPage ){
170 printf("| page %d offset %d\n", iPage, (iPage-1)*pgsz);
171 lastPage = iPage;
173 printf("| %5d:", i-(iPage-1)*pgsz);
174 for(j=0; j<16; j++) printf(" %02x", aLine[j]);
175 printf(" ");
176 for(j=0; j<16; j++){
177 unsigned char c = (unsigned char)aLine[j];
178 fputc( bShow[c], stdout);
180 fputc('\n', stdout);
182 printf("| end %s\n", zBaseName);
183 if( nSQL>0 ){
184 printf("%s\n", aData);
186 free( aData );
187 return 0;