dbtotxt.c 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  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. /* Return true if the line is all zeros */
  43. static int allZero(unsigned char *aLine){
  44. int i;
  45. for(i=0; i<16 && aLine[i]==0; i++){}
  46. return i==16;
  47. }
  48. int main(int argc, char **argv){
  49. int pgsz = 0; /* page size */
  50. int forCli = 0; /* whether to prepend with .open */
  51. int bSQL = 0; /* Expect and SQL prefix */
  52. long szFile; /* Size of the input file in bytes */
  53. FILE *in; /* Input file */
  54. int nSQL; /* Number of bytes of script */
  55. int i, j; /* Loop counters */
  56. int nErr = 0; /* Number of errors */
  57. const char *zInputFile = 0; /* Name of the input file */
  58. const char *zBaseName = 0; /* Base name of the file */
  59. int lastPage = 0; /* Last page number shown */
  60. int iPage; /* Current page number */
  61. unsigned char *aData = 0; /* All data */
  62. unsigned char *aLine; /* A single line of the file */
  63. unsigned char *aHdr; /* File header */
  64. unsigned char bShow[256]; /* Characters ok to display */
  65. memset(bShow, '.', sizeof(bShow));
  66. for(i=' '; i<='~'; i++){
  67. if( i!='{' && i!='}' && i!='"' && i!='\\' ) bShow[i] = (unsigned char)i;
  68. }
  69. for(i=1; i<argc; i++){
  70. if( argv[i][0]=='-' ){
  71. const char *z = argv[i];
  72. z++;
  73. if( z[0]=='-' ) z++;
  74. if( strcmp(z,"pagesize")==0 ){
  75. i++;
  76. pgsz = atoi(argv[i]);
  77. if( pgsz<512 || pgsz>65536 || (pgsz&(pgsz-1))!=0 ){
  78. fprintf(stderr, "Page size must be a power of two between"
  79. " 512 and 65536.\n");
  80. nErr++;
  81. }
  82. continue;
  83. }else if( strcmp(z,"for-cli")==0 ){
  84. forCli = 1;
  85. continue;
  86. }else if( strcmp(z,"script")==0 ){
  87. forCli = 1;
  88. bSQL = 1;
  89. continue;
  90. }
  91. fprintf(stderr, "Unknown option: %s\n", argv[i]);
  92. nErr++;
  93. }else if( zInputFile ){
  94. fprintf(stderr, "Already using a different input file: [%s]\n", argv[i]);
  95. nErr++;
  96. }else{
  97. zInputFile = argv[i];
  98. }
  99. }
  100. if( zInputFile==0 ){
  101. fprintf(stderr, "No input file specified.\n");
  102. nErr++;
  103. }
  104. if( nErr ){
  105. fprintf(stderr,
  106. "Usage: %s [--pagesize N] [--script] [--for-cli] FILENAME\n", argv[0]);
  107. exit(1);
  108. }
  109. in = fopen(zInputFile, "rb");
  110. if( in==0 ){
  111. fprintf(stderr, "Cannot open input file [%s]\n", zInputFile);
  112. exit(1);
  113. }
  114. fseek(in, 0, SEEK_END);
  115. szFile = ftell(in);
  116. rewind(in);
  117. if( szFile<100 ){
  118. fprintf(stderr, "File too short. Minimum size is 100 bytes.\n");
  119. exit(1);
  120. }
  121. aData = malloc( szFile+16 );
  122. if( aData==0 ){
  123. fprintf(stderr, "Failed to allocate %ld bytes\n", szFile);
  124. exit(1);
  125. }
  126. if( fread(aData, szFile, 1, in)!=1 ){
  127. fprintf(stderr, "Cannot read file info memory\n");
  128. exit(1);
  129. }
  130. memset(aData+szFile, 0, 16);
  131. fclose(in);
  132. if( bSQL ){
  133. for(i=0; i<szFile && aData[i]!=0; i++){}
  134. if( i==szFile ){
  135. fprintf(stderr, "No zero terminator on SQL script\n");
  136. exit(1);
  137. }
  138. nSQL = i+1;
  139. if( szFile - nSQL<100 ){
  140. fprintf(stderr, "Less than 100 bytes in the database\n");
  141. exit(1);
  142. }
  143. }else{
  144. nSQL = 0;
  145. }
  146. aHdr = aData + nSQL;
  147. if( pgsz==0 ){
  148. pgsz = (aHdr[16]<<8) | aHdr[17];
  149. if( pgsz==1 ) pgsz = 65536;
  150. if( pgsz<512 || (pgsz&(pgsz-1))!=0 ){
  151. fprintf(stderr, "Invalid page size in header: %d\n", pgsz);
  152. exit(1);
  153. }
  154. }
  155. zBaseName = zInputFile;
  156. for(i=0; zInputFile[i]; i++){
  157. if( zInputFile[i]=='/' && zInputFile[i+1]!=0 ) zBaseName = zInputFile+i+1;
  158. }
  159. if( forCli ){
  160. printf(".open --hexdb\n");
  161. }
  162. printf("| size %d pagesize %d filename %s\n",(int)szFile,pgsz,zBaseName);
  163. for(i=nSQL; i<szFile; i+=16){
  164. aLine = aData+i;
  165. if( allZero(aLine) ) continue;
  166. iPage = i/pgsz + 1;
  167. if( lastPage!=iPage ){
  168. printf("| page %d offset %d\n", iPage, (iPage-1)*pgsz);
  169. lastPage = iPage;
  170. }
  171. printf("| %5d:", i-(iPage-1)*pgsz);
  172. for(j=0; j<16; j++) printf(" %02x", aLine[j]);
  173. printf(" ");
  174. for(j=0; j<16; j++){
  175. unsigned char c = (unsigned char)aLine[j];
  176. fputc( bShow[c], stdout);
  177. }
  178. fputc('\n', stdout);
  179. }
  180. printf("| end %s\n", zBaseName);
  181. if( nSQL>0 ){
  182. printf("%s\n", aData);
  183. }
  184. free( aData );
  185. return 0;
  186. }