summaryrefslogtreecommitdiff
path: root/chromium/third_party/sqlite/src/tool/dbtotxt.c
blob: fbd6e3d5195171a510ce4cb187b6f924b7334235 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
/*
** Copyright 2008 D. Richard Hipp and Hipp, Wyrick & Company, Inc.
** All Rights Reserved
**
******************************************************************************
**
** This file implements a stand-alone utility program that converts
** a binary file (usually an SQLite database) into a text format that
** is compact and friendly to human-readers.
**
** Usage:
**
**         dbtotxt [OPTIONS] FILENAME
**
** where OPTIONS are zero or more of:
**
**    --for-cli          prepending '.open --hexdb' to the output
**
**    --script           The input file is expected to start with a
**                       zero-terminated SQL string.  Output the
**                       ".open --hexdb" header, then the database
**                       then the SQL.
**
**    --pagesize N       set the database page size for later reading
**
** The translation of the database appears on standard output.  If the
** --pagesize command-line option is omitted, then the page size is taken
** from the database header.
**
** Compactness is achieved by suppressing lines of all zero bytes.  This
** works well at compressing test databases that are mostly empty.  But
** the output will probably be lengthy for a real database containing lots
** of real content.  For maximum compactness, it is suggested that test
** databases be constructed with "zeroblob()" rather than "randomblob()"
** used for filler content and with "PRAGMA secure_delete=ON" selected to
** zero-out deleted content.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
 
/* Return true if the line is all zeros */
static int allZero(unsigned char *aLine){
  int i;
  for(i=0; i<16 && aLine[i]==0; i++){}
  return i==16;
}

int main(int argc, char **argv){
  int pgsz = 0;               /* page size */
  int forCli = 0;             /* whether to prepend with .open */
  int bSQL = 0;               /* Expect and SQL prefix */
  long szFile;                /* Size of the input file in bytes */
  FILE *in;                   /* Input file */
  int nSQL;                   /* Number of bytes of script */
  int i, j;                   /* Loop counters */
  int nErr = 0;               /* Number of errors */
  const char *zInputFile = 0; /* Name of the input file */
  const char *zBaseName = 0;  /* Base name of the file */
  int lastPage = 0;           /* Last page number shown */
  int iPage;                  /* Current page number */
  unsigned char *aData = 0;   /* All data */
  unsigned char *aLine;       /* A single line of the file */
  unsigned char *aHdr;        /* File header */
  unsigned char bShow[256];   /* Characters ok to display */
  memset(bShow, '.', sizeof(bShow));
  for(i=' '; i<='~'; i++){
    if( i!='{' && i!='}' && i!='"' && i!='\\' ) bShow[i] = (unsigned char)i;
  }
  for(i=1; i<argc; i++){
    if( argv[i][0]=='-' ){
      const char *z = argv[i];
      z++;
      if( z[0]=='-' ) z++;
      if( strcmp(z,"pagesize")==0 ){
        i++;
        pgsz = atoi(argv[i]);
        if( pgsz<512 || pgsz>65536 || (pgsz&(pgsz-1))!=0 ){
          fprintf(stderr, "Page size must be a power of two between"
                          " 512 and 65536.\n");
          nErr++;
        }
        continue;
      }else if( strcmp(z,"for-cli")==0 ){
        forCli = 1;
        continue;
      }else if( strcmp(z,"script")==0 ){
        forCli = 1;
        bSQL = 1;
        continue;
      }
      fprintf(stderr, "Unknown option: %s\n", argv[i]);
      nErr++;
    }else if( zInputFile ){
      fprintf(stderr, "Already using a different input file: [%s]\n", argv[i]);
      nErr++;
    }else{
      zInputFile = argv[i];
    }
  }
  if( zInputFile==0 ){
    fprintf(stderr, "No input file specified.\n");
    nErr++;
  }
  if( nErr ){
    fprintf(stderr, 
       "Usage: %s [--pagesize N] [--script] [--for-cli] FILENAME\n", argv[0]);
    exit(1);
  }
  in = fopen(zInputFile, "rb");
  if( in==0 ){
    fprintf(stderr, "Cannot open input file [%s]\n", zInputFile);
    exit(1);
  }
  fseek(in, 0, SEEK_END);
  szFile = ftell(in);
  rewind(in);
  if( szFile<100 ){
    fprintf(stderr, "File too short. Minimum size is 100 bytes.\n");
    exit(1);
  }
  aData = malloc( szFile+16 );
  if( aData==0 ){
    fprintf(stderr, "Failed to allocate %ld bytes\n", szFile);
    exit(1);
  }
  if( fread(aData, szFile, 1, in)!=1 ){
    fprintf(stderr, "Cannot read file info memory\n");
    exit(1);
  }
  memset(aData+szFile, 0, 16);
  fclose(in);
  if( bSQL ){
    for(i=0; i<szFile && aData[i]!=0; i++){}
    if( i==szFile ){
      fprintf(stderr, "No zero terminator on SQL script\n");
      exit(1);
    }
    nSQL = i+1;
    if( szFile - nSQL<100 ){
      fprintf(stderr, "Less than 100 bytes in the database\n");
      exit(1);
    }
  }else{
    nSQL = 0;
  }
  aHdr = aData + nSQL;
  if( pgsz==0 ){
    pgsz = (aHdr[16]<<8) | aHdr[17];
    if( pgsz==1 ) pgsz = 65536;
    if( pgsz<512 || (pgsz&(pgsz-1))!=0 ){
      fprintf(stderr, "Invalid page size in header: %d\n", pgsz);
      exit(1);
    }
  }
  zBaseName = zInputFile;
  for(i=0; zInputFile[i]; i++){
    if( zInputFile[i]=='/' && zInputFile[i+1]!=0 ) zBaseName = zInputFile+i+1;
  }
  if( forCli ){
    printf(".open --hexdb\n");
  }
  printf("| size %d pagesize %d filename %s\n",(int)szFile,pgsz,zBaseName);
  for(i=nSQL; i<szFile; i+=16){
    aLine = aData+i;
    if( allZero(aLine) ) continue;
    iPage = i/pgsz + 1;
    if( lastPage!=iPage ){
      printf("| page %d offset %d\n", iPage, (iPage-1)*pgsz);
      lastPage = iPage;
    }
    printf("|  %5d:", i-(iPage-1)*pgsz);
    for(j=0; j<16; j++) printf(" %02x", aLine[j]);
    printf("   ");
    for(j=0; j<16; j++){
      unsigned char c = (unsigned char)aLine[j];
      fputc( bShow[c], stdout);
    }
    fputc('\n', stdout);
  }
  printf("| end %s\n", zBaseName);
  if( nSQL>0 ){
    printf("%s\n", aData);
  }
  free( aData );
  return 0;
}