showwal.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640
  1. /*
  2. ** A utility for printing content from a write-ahead log file.
  3. */
  4. #include <stdio.h>
  5. #include <ctype.h>
  6. #include <sys/types.h>
  7. #include <sys/stat.h>
  8. #include <fcntl.h>
  9. #define ISDIGIT(X) isdigit((unsigned char)(X))
  10. #define ISPRINT(X) isprint((unsigned char)(X))
  11. #if !defined(_MSC_VER)
  12. #include <unistd.h>
  13. #include <sys/types.h>
  14. #else
  15. #include <io.h>
  16. #endif
  17. #include <stdlib.h>
  18. #include <string.h>
  19. static int pagesize = 1024; /* Size of a database page */
  20. static int fd = -1; /* File descriptor for reading the WAL file */
  21. static int mxFrame = 0; /* Last frame */
  22. static int perLine = 16; /* HEX elements to print per line */
  23. typedef long long int i64; /* Datatype for 64-bit integers */
  24. /* Information for computing the checksum */
  25. typedef struct Cksum Cksum;
  26. struct Cksum {
  27. int bSwap; /* True to do byte swapping on 32-bit words */
  28. unsigned s0, s1; /* Current checksum value */
  29. };
  30. /*
  31. ** extract a 32-bit big-endian integer
  32. */
  33. static unsigned int getInt32(const unsigned char *a){
  34. unsigned int x = (a[0]<<24) + (a[1]<<16) + (a[2]<<8) + a[3];
  35. return x;
  36. }
  37. /*
  38. ** Swap bytes on a 32-bit unsigned integer
  39. */
  40. static unsigned int swab32(unsigned int x){
  41. return (((x)&0x000000FF)<<24) + (((x)&0x0000FF00)<<8)
  42. + (((x)&0x00FF0000)>>8) + (((x)&0xFF000000)>>24);
  43. }
  44. /* Extend the checksum. Reinitialize the checksum if bInit is true.
  45. */
  46. static void extendCksum(
  47. Cksum *pCksum,
  48. unsigned char *aData,
  49. unsigned int nByte,
  50. int bInit
  51. ){
  52. unsigned int *a32;
  53. if( bInit ){
  54. int a = 0;
  55. *((char*)&a) = 1;
  56. if( a==1 ){
  57. /* Host is little-endian */
  58. pCksum->bSwap = getInt32(aData)!=0x377f0682;
  59. }else{
  60. /* Host is big-endian */
  61. pCksum->bSwap = getInt32(aData)!=0x377f0683;
  62. }
  63. pCksum->s0 = 0;
  64. pCksum->s1 = 0;
  65. }
  66. a32 = (unsigned int*)aData;
  67. while( nByte>0 ){
  68. unsigned int x0 = a32[0];
  69. unsigned int x1 = a32[1];
  70. if( pCksum->bSwap ){
  71. x0 = swab32(x0);
  72. x1 = swab32(x1);
  73. }
  74. pCksum->s0 += x0 + pCksum->s1;
  75. pCksum->s1 += x1 + pCksum->s0;
  76. nByte -= 8;
  77. a32 += 2;
  78. }
  79. }
  80. /*
  81. ** Convert the var-int format into i64. Return the number of bytes
  82. ** in the var-int. Write the var-int value into *pVal.
  83. */
  84. static int decodeVarint(const unsigned char *z, i64 *pVal){
  85. i64 v = 0;
  86. int i;
  87. for(i=0; i<8; i++){
  88. v = (v<<7) + (z[i]&0x7f);
  89. if( (z[i]&0x80)==0 ){ *pVal = v; return i+1; }
  90. }
  91. v = (v<<8) + (z[i]&0xff);
  92. *pVal = v;
  93. return 9;
  94. }
  95. /* Report an out-of-memory error and die.
  96. */
  97. static void out_of_memory(void){
  98. fprintf(stderr,"Out of memory...\n");
  99. exit(1);
  100. }
  101. /*
  102. ** Read content from the file.
  103. **
  104. ** Space to hold the content is obtained from malloc() and needs to be
  105. ** freed by the caller.
  106. */
  107. static unsigned char *getContent(i64 ofst, int nByte){
  108. unsigned char *aData;
  109. aData = malloc(nByte);
  110. if( aData==0 ) out_of_memory();
  111. lseek(fd, ofst, SEEK_SET);
  112. read(fd, aData, nByte);
  113. return aData;
  114. }
  115. /*
  116. ** Print a range of bytes as hex and as ascii.
  117. */
  118. static void print_byte_range(
  119. int ofst, /* First byte in the range of bytes to print */
  120. int nByte, /* Number of bytes to print */
  121. unsigned char *aData, /* Content to print */
  122. int printOfst /* Add this amount to the index on the left column */
  123. ){
  124. int i, j;
  125. const char *zOfstFmt;
  126. if( ((printOfst+nByte)&~0xfff)==0 ){
  127. zOfstFmt = " %03x: ";
  128. }else if( ((printOfst+nByte)&~0xffff)==0 ){
  129. zOfstFmt = " %04x: ";
  130. }else if( ((printOfst+nByte)&~0xfffff)==0 ){
  131. zOfstFmt = " %05x: ";
  132. }else if( ((printOfst+nByte)&~0xffffff)==0 ){
  133. zOfstFmt = " %06x: ";
  134. }else{
  135. zOfstFmt = " %08x: ";
  136. }
  137. for(i=0; i<nByte; i += perLine){
  138. fprintf(stdout, zOfstFmt, i+printOfst);
  139. for(j=0; j<perLine; j++){
  140. if( i+j>nByte ){
  141. fprintf(stdout, " ");
  142. }else{
  143. fprintf(stdout,"%02x ", aData[i+j]);
  144. }
  145. }
  146. for(j=0; j<perLine; j++){
  147. if( i+j>nByte ){
  148. fprintf(stdout, " ");
  149. }else{
  150. fprintf(stdout,"%c", ISPRINT(aData[i+j]) ? aData[i+j] : '.');
  151. }
  152. }
  153. fprintf(stdout,"\n");
  154. }
  155. }
  156. /* Print a line of decode output showing a 4-byte integer.
  157. */
  158. static void print_decode_line(
  159. unsigned char *aData, /* Content being decoded */
  160. int ofst, int nByte, /* Start and size of decode */
  161. int asHex, /* If true, output value as hex */
  162. const char *zMsg /* Message to append */
  163. ){
  164. int i, j;
  165. int val = aData[ofst];
  166. char zBuf[100];
  167. sprintf(zBuf, " %03x: %02x", ofst, aData[ofst]);
  168. i = (int)strlen(zBuf);
  169. for(j=1; j<4; j++){
  170. if( j>=nByte ){
  171. sprintf(&zBuf[i], " ");
  172. }else{
  173. sprintf(&zBuf[i], " %02x", aData[ofst+j]);
  174. val = val*256 + aData[ofst+j];
  175. }
  176. i += (int)strlen(&zBuf[i]);
  177. }
  178. if( asHex ){
  179. sprintf(&zBuf[i], " 0x%08x", val);
  180. }else{
  181. sprintf(&zBuf[i], " %9d", val);
  182. }
  183. printf("%s %s\n", zBuf, zMsg);
  184. }
  185. /*
  186. ** Print an entire page of content as hex
  187. */
  188. static void print_frame(int iFrame){
  189. i64 iStart;
  190. unsigned char *aData;
  191. iStart = 32 + (i64)(iFrame-1)*(pagesize+24);
  192. fprintf(stdout, "Frame %d: (offsets 0x%llx..0x%llx)\n",
  193. iFrame, iStart, iStart+pagesize+24);
  194. aData = getContent(iStart, pagesize+24);
  195. print_decode_line(aData, 0, 4, 0, "Page number");
  196. print_decode_line(aData, 4, 4, 0, "DB size, or 0 for non-commit");
  197. print_decode_line(aData, 8, 4, 1, "Salt-1");
  198. print_decode_line(aData,12, 4, 1, "Salt-2");
  199. print_decode_line(aData,16, 4, 1, "Checksum-1");
  200. print_decode_line(aData,20, 4, 1, "Checksum-2");
  201. print_byte_range(iStart+24, pagesize, aData+24, 0);
  202. free(aData);
  203. }
  204. /*
  205. ** Summarize a single frame on a single line.
  206. */
  207. static void print_oneline_frame(int iFrame, Cksum *pCksum){
  208. i64 iStart;
  209. unsigned char *aData;
  210. unsigned int s0, s1;
  211. iStart = 32 + (i64)(iFrame-1)*(pagesize+24);
  212. aData = getContent(iStart, 24);
  213. extendCksum(pCksum, aData, 8, 0);
  214. extendCksum(pCksum, getContent(iStart+24, pagesize), pagesize, 0);
  215. s0 = getInt32(aData+16);
  216. s1 = getInt32(aData+20);
  217. fprintf(stdout, "Frame %4d: %6d %6d 0x%08x,%08x 0x%08x,%08x",
  218. iFrame,
  219. getInt32(aData),
  220. getInt32(aData+4),
  221. getInt32(aData+8),
  222. getInt32(aData+12),
  223. s0,
  224. s1
  225. );
  226. if( s0==pCksum->s0 && s1==pCksum->s1 ){
  227. fprintf(stdout, "\n");
  228. }else{
  229. fprintf(stdout, " should be 0x%08x,%08x\n",
  230. pCksum->s0, pCksum->s1);
  231. }
  232. /* Reset the checksum so that a single frame checksum failure will not
  233. ** cause all subsequent frames to also show a failure. */
  234. pCksum->s0 = s0;
  235. pCksum->s1 = s1;
  236. free(aData);
  237. }
  238. /*
  239. ** Decode the WAL header.
  240. */
  241. static void print_wal_header(Cksum *pCksum){
  242. unsigned char *aData;
  243. aData = getContent(0, 32);
  244. if( pCksum ){
  245. extendCksum(pCksum, aData, 24, 1);
  246. printf("Checksum byte order: %s\n", pCksum->bSwap ? "swapped" : "native");
  247. }
  248. printf("WAL Header:\n");
  249. print_decode_line(aData, 0, 4,1,"Magic. 0x377f0682 (le) or 0x377f0683 (be)");
  250. print_decode_line(aData, 4, 4, 0, "File format");
  251. print_decode_line(aData, 8, 4, 0, "Database page size");
  252. print_decode_line(aData, 12,4, 0, "Checkpoint sequence number");
  253. print_decode_line(aData, 16,4, 1, "Salt-1");
  254. print_decode_line(aData, 20,4, 1, "Salt-2");
  255. print_decode_line(aData, 24,4, 1, "Checksum-1");
  256. print_decode_line(aData, 28,4, 1, "Checksum-2");
  257. if( pCksum ){
  258. if( pCksum->s0!=getInt32(aData+24) ){
  259. printf("**** cksum-1 mismatch: 0x%08x\n", pCksum->s0);
  260. }
  261. if( pCksum->s1!=getInt32(aData+28) ){
  262. printf("**** cksum-2 mismatch: 0x%08x\n", pCksum->s1);
  263. }
  264. }
  265. free(aData);
  266. }
  267. /*
  268. ** Describe cell content.
  269. */
  270. static i64 describeContent(
  271. unsigned char *a, /* Cell content */
  272. i64 nLocal, /* Bytes in a[] */
  273. char *zDesc /* Write description here */
  274. ){
  275. int nDesc = 0;
  276. int n, j;
  277. i64 i, x, v;
  278. const unsigned char *pData;
  279. const unsigned char *pLimit;
  280. char sep = ' ';
  281. pLimit = &a[nLocal];
  282. n = decodeVarint(a, &x);
  283. pData = &a[x];
  284. a += n;
  285. i = x - n;
  286. while( i>0 && pData<=pLimit ){
  287. n = decodeVarint(a, &x);
  288. a += n;
  289. i -= n;
  290. nLocal -= n;
  291. zDesc[0] = sep;
  292. sep = ',';
  293. nDesc++;
  294. zDesc++;
  295. if( x==0 ){
  296. sprintf(zDesc, "*"); /* NULL is a "*" */
  297. }else if( x>=1 && x<=6 ){
  298. v = (signed char)pData[0];
  299. pData++;
  300. switch( x ){
  301. case 6: v = (v<<16) + (pData[0]<<8) + pData[1]; pData += 2;
  302. case 5: v = (v<<16) + (pData[0]<<8) + pData[1]; pData += 2;
  303. case 4: v = (v<<8) + pData[0]; pData++;
  304. case 3: v = (v<<8) + pData[0]; pData++;
  305. case 2: v = (v<<8) + pData[0]; pData++;
  306. }
  307. sprintf(zDesc, "%lld", v);
  308. }else if( x==7 ){
  309. sprintf(zDesc, "real");
  310. pData += 8;
  311. }else if( x==8 ){
  312. sprintf(zDesc, "0");
  313. }else if( x==9 ){
  314. sprintf(zDesc, "1");
  315. }else if( x>=12 ){
  316. i64 size = (x-12)/2;
  317. if( (x&1)==0 ){
  318. sprintf(zDesc, "blob(%lld)", size);
  319. }else{
  320. sprintf(zDesc, "txt(%lld)", size);
  321. }
  322. pData += size;
  323. }
  324. j = (int)strlen(zDesc);
  325. zDesc += j;
  326. nDesc += j;
  327. }
  328. return nDesc;
  329. }
  330. /*
  331. ** Compute the local payload size given the total payload size and
  332. ** the page size.
  333. */
  334. static i64 localPayload(i64 nPayload, char cType){
  335. i64 maxLocal;
  336. i64 minLocal;
  337. i64 surplus;
  338. i64 nLocal;
  339. if( cType==13 ){
  340. /* Table leaf */
  341. maxLocal = pagesize-35;
  342. minLocal = (pagesize-12)*32/255-23;
  343. }else{
  344. maxLocal = (pagesize-12)*64/255-23;
  345. minLocal = (pagesize-12)*32/255-23;
  346. }
  347. if( nPayload>maxLocal ){
  348. surplus = minLocal + (nPayload-minLocal)%(pagesize-4);
  349. if( surplus<=maxLocal ){
  350. nLocal = surplus;
  351. }else{
  352. nLocal = minLocal;
  353. }
  354. }else{
  355. nLocal = nPayload;
  356. }
  357. return nLocal;
  358. }
  359. /*
  360. ** Create a description for a single cell.
  361. **
  362. ** The return value is the local cell size.
  363. */
  364. static i64 describeCell(
  365. unsigned char cType, /* Page type */
  366. unsigned char *a, /* Cell content */
  367. int showCellContent, /* Show cell content if true */
  368. char **pzDesc /* Store description here */
  369. ){
  370. int i;
  371. i64 nDesc = 0;
  372. int n = 0;
  373. int leftChild;
  374. i64 nPayload;
  375. i64 rowid;
  376. i64 nLocal;
  377. static char zDesc[1000];
  378. i = 0;
  379. if( cType<=5 ){
  380. leftChild = ((a[0]*256 + a[1])*256 + a[2])*256 + a[3];
  381. a += 4;
  382. n += 4;
  383. sprintf(zDesc, "lx: %d ", leftChild);
  384. nDesc = strlen(zDesc);
  385. }
  386. if( cType!=5 ){
  387. i = decodeVarint(a, &nPayload);
  388. a += i;
  389. n += i;
  390. sprintf(&zDesc[nDesc], "n: %lld ", nPayload);
  391. nDesc += strlen(&zDesc[nDesc]);
  392. nLocal = localPayload(nPayload, cType);
  393. }else{
  394. nPayload = nLocal = 0;
  395. }
  396. if( cType==5 || cType==13 ){
  397. i = decodeVarint(a, &rowid);
  398. a += i;
  399. n += i;
  400. sprintf(&zDesc[nDesc], "r: %lld ", rowid);
  401. nDesc += strlen(&zDesc[nDesc]);
  402. }
  403. if( nLocal<nPayload ){
  404. int ovfl;
  405. unsigned char *b = &a[nLocal];
  406. ovfl = ((b[0]*256 + b[1])*256 + b[2])*256 + b[3];
  407. sprintf(&zDesc[nDesc], "ov: %d ", ovfl);
  408. nDesc += strlen(&zDesc[nDesc]);
  409. n += 4;
  410. }
  411. if( showCellContent && cType!=5 ){
  412. nDesc += describeContent(a, nLocal, &zDesc[nDesc-1]);
  413. }
  414. *pzDesc = zDesc;
  415. return nLocal+n;
  416. }
  417. /*
  418. ** Decode a btree page
  419. */
  420. static void decode_btree_page(
  421. unsigned char *a, /* Content of the btree page to be decoded */
  422. int pgno, /* Page number */
  423. int hdrSize, /* Size of the page1-header in bytes */
  424. const char *zArgs /* Flags to control formatting */
  425. ){
  426. const char *zType = "unknown";
  427. int nCell;
  428. int i, j;
  429. int iCellPtr;
  430. int showCellContent = 0;
  431. int showMap = 0;
  432. char *zMap = 0;
  433. switch( a[0] ){
  434. case 2: zType = "index interior node"; break;
  435. case 5: zType = "table interior node"; break;
  436. case 10: zType = "index leaf"; break;
  437. case 13: zType = "table leaf"; break;
  438. }
  439. while( zArgs[0] ){
  440. switch( zArgs[0] ){
  441. case 'c': showCellContent = 1; break;
  442. case 'm': showMap = 1; break;
  443. }
  444. zArgs++;
  445. }
  446. printf("Decode of btree page %d:\n", pgno);
  447. print_decode_line(a, 0, 1, 0, zType);
  448. print_decode_line(a, 1, 2, 0, "Offset to first freeblock");
  449. print_decode_line(a, 3, 2, 0, "Number of cells on this page");
  450. nCell = a[3]*256 + a[4];
  451. print_decode_line(a, 5, 2, 0, "Offset to cell content area");
  452. print_decode_line(a, 7, 1, 0, "Fragmented byte count");
  453. if( a[0]==2 || a[0]==5 ){
  454. print_decode_line(a, 8, 4, 0, "Right child");
  455. iCellPtr = 12;
  456. }else{
  457. iCellPtr = 8;
  458. }
  459. if( nCell>0 ){
  460. printf(" key: lx=left-child n=payload-size r=rowid\n");
  461. }
  462. if( showMap ){
  463. zMap = malloc(pagesize);
  464. memset(zMap, '.', pagesize);
  465. memset(zMap, '1', hdrSize);
  466. memset(&zMap[hdrSize], 'H', iCellPtr);
  467. memset(&zMap[hdrSize+iCellPtr], 'P', 2*nCell);
  468. }
  469. for(i=0; i<nCell; i++){
  470. int cofst = iCellPtr + i*2;
  471. char *zDesc;
  472. i64 n;
  473. cofst = a[cofst]*256 + a[cofst+1];
  474. n = describeCell(a[0], &a[cofst-hdrSize], showCellContent, &zDesc);
  475. if( showMap ){
  476. char zBuf[30];
  477. memset(&zMap[cofst], '*', (size_t)n);
  478. zMap[cofst] = '[';
  479. zMap[cofst+n-1] = ']';
  480. sprintf(zBuf, "%d", i);
  481. j = (int)strlen(zBuf);
  482. if( j<=n-2 ) memcpy(&zMap[cofst+1], zBuf, j);
  483. }
  484. printf(" %03x: cell[%d] %s\n", cofst, i, zDesc);
  485. }
  486. if( showMap ){
  487. for(i=0; i<pagesize; i+=64){
  488. printf(" %03x: %.64s\n", i, &zMap[i]);
  489. }
  490. free(zMap);
  491. }
  492. }
  493. /*
  494. ** Check the range validity for a page number. Print an error and
  495. ** exit if the page is out of range.
  496. */
  497. static void checkPageValidity(int iPage, int mxPage){
  498. if( iPage<1 || iPage>mxPage ){
  499. fprintf(stderr, "Invalid page number %d: valid range is 1..%d\n",
  500. iPage, mxPage);
  501. exit(1);
  502. }
  503. }
  504. int main(int argc, char **argv){
  505. struct stat sbuf;
  506. unsigned char zPgSz[4];
  507. if( argc<2 ){
  508. fprintf(stderr,"Usage: %s FILENAME ?PAGE? ...\n", argv[0]);
  509. exit(1);
  510. }
  511. fd = open(argv[1], O_RDONLY);
  512. if( fd<0 ){
  513. fprintf(stderr,"%s: can't open %s\n", argv[0], argv[1]);
  514. exit(1);
  515. }
  516. zPgSz[0] = 0;
  517. zPgSz[1] = 0;
  518. fstat(fd, &sbuf);
  519. if( sbuf.st_size<32 ){
  520. printf("%s: file too small to be a WAL - only %d bytes\n",
  521. argv[1], (int)sbuf.st_size);
  522. return 0;
  523. }
  524. if( lseek(fd, 8, SEEK_SET)!=8 ){
  525. printf("\"%s\" seems to not be a valid WAL file\n", argv[1]);
  526. return 1;
  527. }
  528. if( read(fd, zPgSz, 4)!=4 ){
  529. printf("\"%s\": cannot read the page size\n", argv[1]);
  530. return 1;
  531. }
  532. pagesize = zPgSz[1]*65536 + zPgSz[2]*256 + zPgSz[3];
  533. if( pagesize==0 ) pagesize = 1024;
  534. printf("Pagesize: %d\n", pagesize);
  535. if( (pagesize & (pagesize-1))!=0 || pagesize<512 || pagesize>65536 ){
  536. printf("\"%s\": invalid page size.\n", argv[1]);
  537. return 1;
  538. }
  539. mxFrame = (sbuf.st_size - 32)/(pagesize + 24);
  540. printf("Available pages: 1..%d\n", mxFrame);
  541. if( argc==2 ){
  542. int i;
  543. Cksum x;
  544. print_wal_header(&x);
  545. for(i=1; i<=mxFrame; i++){
  546. print_oneline_frame(i, &x);
  547. }
  548. }else{
  549. int i;
  550. for(i=2; i<argc; i++){
  551. int iStart, iEnd;
  552. char *zLeft;
  553. if( strcmp(argv[i], "header")==0 ){
  554. print_wal_header(0);
  555. continue;
  556. }
  557. if( !ISDIGIT(argv[i][0]) ){
  558. fprintf(stderr, "%s: unknown option: [%s]\n", argv[0], argv[i]);
  559. continue;
  560. }
  561. iStart = strtol(argv[i], &zLeft, 0);
  562. checkPageValidity(iStart, mxFrame);
  563. if( zLeft && strcmp(zLeft,"..end")==0 ){
  564. iEnd = mxFrame;
  565. }else if( zLeft && zLeft[0]=='.' && zLeft[1]=='.' ){
  566. iEnd = strtol(&zLeft[2], 0, 0);
  567. checkPageValidity(iEnd, mxFrame);
  568. }else if( zLeft && zLeft[0]=='b' ){
  569. i64 ofst;
  570. int nByte, hdrSize;
  571. unsigned char *a;
  572. if( iStart==1 ){
  573. hdrSize = 100;
  574. ofst = hdrSize = 100;
  575. nByte = pagesize-100;
  576. }else{
  577. hdrSize = 0;
  578. ofst = (i64)(iStart-1)*pagesize;
  579. nByte = pagesize;
  580. }
  581. ofst = 32 + hdrSize + (i64)(iStart-1)*(pagesize+24) + 24;
  582. a = getContent(ofst, nByte);
  583. decode_btree_page(a, iStart, hdrSize, zLeft+1);
  584. free(a);
  585. continue;
  586. #if !defined(_MSC_VER)
  587. }else if( zLeft && strcmp(zLeft,"truncate")==0 ){
  588. /* Frame number followed by "truncate" truncates the WAL file
  589. ** after that frame */
  590. off_t newSize = 32 + iStart*(pagesize+24);
  591. truncate(argv[1], newSize);
  592. continue;
  593. #endif
  594. }else{
  595. iEnd = iStart;
  596. }
  597. if( iStart<1 || iEnd<iStart || iEnd>mxFrame ){
  598. fprintf(stderr,
  599. "Page argument should be LOWER?..UPPER?. Range 1 to %d\n",
  600. mxFrame);
  601. exit(1);
  602. }
  603. while( iStart<=iEnd ){
  604. print_frame(iStart);
  605. iStart++;
  606. }
  607. }
  608. }
  609. close(fd);
  610. return 0;
  611. }