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