xref: /sqlite-3.40.0/tool/dbtotxt.c (revision 3961b263)
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 **    --pagesize N  => setting the database page size for later readin
17 **    --for-cli     => prepending '.open --hexdb' to the output
18 **
19 ** The translation of the database appears on standard output.  If the
20 ** --pagesize command-line option is omitted, then the page size is taken
21 ** from the database header.
22 **
23 ** Compactness is achieved by suppressing lines of all zero bytes.  This
24 ** works well at compressing test databases that are mostly empty.  But
25 ** the output will probably be lengthy for a real database containing lots
26 ** of real content.  For maximum compactness, it is suggested that test
27 ** databases be constructed with "zeroblob()" rather than "randomblob()"
28 ** used for filler content and with "PRAGMA secure_delete=ON" selected to
29 ** zero-out deleted content.
30 */
31 #include <stdio.h>
32 #include <string.h>
33 #include <stdlib.h>
34 #include <ctype.h>
35 
36 /* Return true if the line is all zeros */
37 static int allZero(unsigned char *aLine){
38   int i;
39   for(i=0; i<16 && aLine[i]==0; i++){}
40   return i==16;
41 }
42 
43 int main(int argc, char **argv){
44   int pgsz = 0;               /* page size */
45   int forCli = 0;             /* whether to prepend with .open */
46   long szFile;                /* Size of the input file in bytes */
47   FILE *in;                   /* Input file */
48   int i, j;                   /* Loop counters */
49   int nErr = 0;               /* Number of errors */
50   const char *zInputFile = 0; /* Name of the input file */
51   const char *zBaseName = 0;  /* Base name of the file */
52   int lastPage = 0;           /* Last page number shown */
53   int iPage;                  /* Current page number */
54   unsigned char aLine[16];    /* A single line of the file */
55   unsigned char aHdr[100];    /* File header */
56   unsigned char bShow[256];      /* Characters ok to display */
57   memset(bShow, '.', sizeof(bShow));
58   for(i=' '; i<='~'; i++){
59     if( i!='{' && i!='}' && i!='"' && i!='\\' ) bShow[i] = (unsigned char)i;
60   }
61   for(i=1; i<argc; i++){
62     if( argv[i][0]=='-' ){
63       const char *z = argv[i];
64       z++;
65       if( z[0]=='-' ) z++;
66       if( strcmp(z,"pagesize")==0 ){
67         i++;
68         pgsz = atoi(argv[i]);
69         if( pgsz<512 || pgsz>65536 || (pgsz&(pgsz-1))!=0 ){
70           fprintf(stderr, "Page size must be a power of two between"
71                           " 512 and 65536.\n");
72           nErr++;
73         }
74         continue;
75       }else if( strcmp(z,"for-cli")==0 ){
76         forCli = 1;
77         continue;
78       }
79       fprintf(stderr, "Unknown option: %s\n", argv[i]);
80       nErr++;
81     }else if( zInputFile ){
82       fprintf(stderr, "Already using a different input file: [%s]\n", argv[i]);
83       nErr++;
84     }else{
85       zInputFile = argv[i];
86     }
87   }
88   if( zInputFile==0 ){
89     fprintf(stderr, "No input file specified.\n");
90     nErr++;
91   }
92   if( nErr ){
93     fprintf(stderr, "Usage: %s [--pagesize N] FILENAME\n", argv[0]);
94     exit(1);
95   }
96   in = fopen(zInputFile, "rb");
97   if( in==0 ){
98     fprintf(stderr, "Cannot open input file [%s]\n", zInputFile);
99     exit(1);
100   }
101   fseek(in, 0, SEEK_END);
102   szFile = ftell(in);
103   rewind(in);
104   if( szFile<100 ){
105     fprintf(stderr, "File too short. Minimum size is 100 bytes.\n");
106     exit(1);
107   }
108   if( fread(aHdr, 100, 1, in)!=1 ){
109     fprintf(stderr, "Cannot read file header\n");
110     exit(1);
111   }
112   rewind(in);
113   if( pgsz==0 ){
114     pgsz = (aHdr[16]<<8) | aHdr[17];
115     if( pgsz==1 ) pgsz = 65536;
116     if( pgsz<512 || (pgsz&(pgsz-1))!=0 ){
117       fprintf(stderr, "Invalid page size in header: %d\n", pgsz);
118       exit(1);
119     }
120   }
121   zBaseName = zInputFile;
122   for(i=0; zInputFile[i]; i++){
123     if( zInputFile[i]=='/' && zInputFile[i+1]!=0 ) zBaseName = zInputFile+i+1;
124   }
125   if( forCli ){
126     printf(".open --hexdb\n");
127   }
128   printf("| size %d pagesize %d filename %s\n",(int)szFile,pgsz,zBaseName);
129   for(i=0; i<szFile; i+=16){
130     int got = (int)fread(aLine, 1, 16, in);
131     if( got!=16 ){
132       static int once = 1;
133       if( once ){
134         fprintf(stderr, "Could not read input file starting at byte %d\n",
135                          i+got);
136       }
137       memset(aLine+got, 0, 16-got);
138     }
139     if( allZero(aLine) ) continue;
140     iPage = i/pgsz + 1;
141     if( lastPage!=iPage ){
142       printf("| page %d offset %d\n", iPage, (iPage-1)*pgsz);
143       lastPage = iPage;
144     }
145     printf("|  %5d:", i-(iPage-1)*pgsz);
146     for(j=0; j<16; j++) printf(" %02x", aLine[j]);
147     printf("   ");
148     for(j=0; j<16; j++){
149       unsigned char c = (unsigned char)aLine[j];
150       fputc( bShow[c], stdout);
151     }
152     fputc('\n', stdout);
153   }
154   fclose(in);
155   printf("| end %s\n", zBaseName);
156   return 0;
157 }
158