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