xref: /sqlite-3.40.0/tool/dbtotxt.c (revision 5187c955)
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.
10 **
11 ** Usage:
12 **
13 **         dbtotxt [OPTIONS] FILENAME
14 **
15 ** where OPTIONS are zero or more of:
16 **
17 **    --for-cli          prepending '.open --hexdb' to the output
18 **
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.
23 **
24 **    --pagesize N       set the database page size for later reading
25 **
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.
29 **
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.
37 */
38 #include <stdio.h>
39 #include <string.h>
40 #include <stdlib.h>
41 #include <ctype.h>
42 
43 /* Return true if the line is all zeros */
allZero(unsigned char * aLine)44 static int allZero(unsigned char *aLine){
45   int i;
46   for(i=0; i<16 && aLine[i]==0; i++){}
47   return i==16;
48 }
49 
main(int argc,char ** argv)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;
70   }
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++;
83         }
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;
92       }
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];
100     }
101   }
102   if( zInputFile==0 ){
103     fprintf(stderr, "No input file specified.\n");
104     nErr++;
105   }
106   if( nErr ){
107     fprintf(stderr,
108        "Usage: %s [--pagesize N] [--script] [--for-cli] FILENAME\n", argv[0]);
109     exit(1);
110   }
111   in = fopen(zInputFile, "rb");
112   if( in==0 ){
113     fprintf(stderr, "Cannot open input file [%s]\n", zInputFile);
114     exit(1);
115   }
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);
122   }
123   aData = malloc( szFile+16 );
124   if( aData==0 ){
125     fprintf(stderr, "Failed to allocate %ld bytes\n", szFile);
126     exit(1);
127   }
128   if( fread(aData, szFile, 1, in)!=1 ){
129     fprintf(stderr, "Cannot read file info memory\n");
130     exit(1);
131   }
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);
139     }
140     nSQL = i+1;
141     if( szFile - nSQL<100 ){
142       fprintf(stderr, "Less than 100 bytes in the database\n");
143       exit(1);
144     }
145   }else{
146     nSQL = 0;
147   }
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);
155     }
156   }
157   zBaseName = zInputFile;
158   for(i=0; zInputFile[i]; i++){
159     if( zInputFile[i]=='/' && zInputFile[i+1]!=0 ) zBaseName = zInputFile+i+1;
160   }
161   if( forCli ){
162     printf(".open --hexdb\n");
163   }
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;
172     }
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);
179     }
180     fputc('\n', stdout);
181   }
182   printf("| end %s\n", zBaseName);
183   if( nSQL>0 ){
184     printf("%s\n", aData);
185   }
186   free( aData );
187   return 0;
188 }
189