Annotation of embedaddon/sqlite3/tool/showdb.c, revision 1.1

1.1     ! misho       1: /*
        !             2: ** A utility for printing all or part of an SQLite database 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: #include <unistd.h>
        !            10: #include <stdlib.h>
        !            11: #include <string.h>
        !            12: 
        !            13: 
        !            14: static int pagesize = 1024;     /* Size of a database page */
        !            15: static int db = -1;             /* File descriptor for reading the DB */
        !            16: static int mxPage = 0;          /* Last page number */
        !            17: static int perLine = 16;        /* HEX elements to print per line */
        !            18: 
        !            19: typedef long long int i64;      /* Datatype for 64-bit integers */
        !            20: 
        !            21: 
        !            22: /*
        !            23: ** Convert the var-int format into i64.  Return the number of bytes
        !            24: ** in the var-int.  Write the var-int value into *pVal.
        !            25: */
        !            26: static int decodeVarint(const unsigned char *z, i64 *pVal){
        !            27:   i64 v = 0;
        !            28:   int i;
        !            29:   for(i=0; i<8; i++){
        !            30:     v = (v<<7) + (z[i]&0x7f);
        !            31:     if( (z[i]&0x80)==0 ){ *pVal = v; return i+1; }
        !            32:   }
        !            33:   v = (v<<8) + (z[i]&0xff);
        !            34:   *pVal = v;
        !            35:   return 9;
        !            36: }
        !            37: 
        !            38: /*
        !            39: ** Extract a big-endian 32-bit integer
        !            40: */
        !            41: static unsigned int decodeInt32(const unsigned char *z){
        !            42:   return (z[0]<<24) + (z[1]<<16) + (z[2]<<8) + z[3];
        !            43: }
        !            44: 
        !            45: /* Report an out-of-memory error and die.
        !            46: */
        !            47: static void out_of_memory(void){
        !            48:   fprintf(stderr,"Out of memory...\n");
        !            49:   exit(1);
        !            50: }
        !            51: 
        !            52: /*
        !            53: ** Read content from the file.
        !            54: **
        !            55: ** Space to hold the content is obtained from malloc() and needs to be
        !            56: ** freed by the caller.
        !            57: */
        !            58: static unsigned char *getContent(int ofst, int nByte){
        !            59:   unsigned char *aData;
        !            60:   aData = malloc(nByte+32);
        !            61:   if( aData==0 ) out_of_memory();
        !            62:   memset(aData, 0, nByte+32);
        !            63:   lseek(db, ofst, SEEK_SET);
        !            64:   read(db, aData, nByte);
        !            65:   return aData;
        !            66: }
        !            67: 
        !            68: /*
        !            69: ** Print a range of bytes as hex and as ascii.
        !            70: */
        !            71: static unsigned char *print_byte_range(
        !            72:   int ofst,          /* First byte in the range of bytes to print */
        !            73:   int nByte,         /* Number of bytes to print */
        !            74:   int printOfst      /* Add this amount to the index on the left column */
        !            75: ){
        !            76:   unsigned char *aData;
        !            77:   int i, j;
        !            78:   const char *zOfstFmt;
        !            79: 
        !            80:   if( ((printOfst+nByte)&~0xfff)==0 ){
        !            81:     zOfstFmt = " %03x: ";
        !            82:   }else if( ((printOfst+nByte)&~0xffff)==0 ){
        !            83:     zOfstFmt = " %04x: ";
        !            84:   }else if( ((printOfst+nByte)&~0xfffff)==0 ){
        !            85:     zOfstFmt = " %05x: ";
        !            86:   }else if( ((printOfst+nByte)&~0xffffff)==0 ){
        !            87:     zOfstFmt = " %06x: ";
        !            88:   }else{
        !            89:     zOfstFmt = " %08x: ";
        !            90:   }
        !            91: 
        !            92:   aData = getContent(ofst, nByte);
        !            93:   for(i=0; i<nByte; i += perLine){
        !            94:     fprintf(stdout, zOfstFmt, i+printOfst);
        !            95:     for(j=0; j<perLine; j++){
        !            96:       if( i+j>nByte ){
        !            97:         fprintf(stdout, "   ");
        !            98:       }else{
        !            99:         fprintf(stdout,"%02x ", aData[i+j]);
        !           100:       }
        !           101:     }
        !           102:     for(j=0; j<perLine; j++){
        !           103:       if( i+j>nByte ){
        !           104:         fprintf(stdout, " ");
        !           105:       }else{
        !           106:         fprintf(stdout,"%c", isprint(aData[i+j]) ? aData[i+j] : '.');
        !           107:       }
        !           108:     }
        !           109:     fprintf(stdout,"\n");
        !           110:   }
        !           111:   return aData;
        !           112: }
        !           113: 
        !           114: /*
        !           115: ** Print an entire page of content as hex
        !           116: */
        !           117: static print_page(int iPg){
        !           118:   int iStart;
        !           119:   unsigned char *aData;
        !           120:   iStart = (iPg-1)*pagesize;
        !           121:   fprintf(stdout, "Page %d:   (offsets 0x%x..0x%x)\n",
        !           122:           iPg, iStart, iStart+pagesize-1);
        !           123:   aData = print_byte_range(iStart, pagesize, 0);
        !           124:   free(aData);
        !           125: }
        !           126: 
        !           127: /* Print a line of decode output showing a 4-byte integer.
        !           128: */
        !           129: static print_decode_line(
        !           130:   unsigned char *aData,      /* Content being decoded */
        !           131:   int ofst, int nByte,       /* Start and size of decode */
        !           132:   const char *zMsg           /* Message to append */
        !           133: ){
        !           134:   int i, j;
        !           135:   int val = aData[ofst];
        !           136:   char zBuf[100];
        !           137:   sprintf(zBuf, " %03x: %02x", ofst, aData[ofst]);
        !           138:   i = strlen(zBuf);
        !           139:   for(j=1; j<4; j++){
        !           140:     if( j>=nByte ){
        !           141:       sprintf(&zBuf[i], "   ");
        !           142:     }else{
        !           143:       sprintf(&zBuf[i], " %02x", aData[ofst+j]);
        !           144:       val = val*256 + aData[ofst+j];
        !           145:     }
        !           146:     i += strlen(&zBuf[i]);
        !           147:   }
        !           148:   sprintf(&zBuf[i], "   %9d", val);
        !           149:   printf("%s  %s\n", zBuf, zMsg);
        !           150: }
        !           151: 
        !           152: /*
        !           153: ** Decode the database header.
        !           154: */
        !           155: static void print_db_header(void){
        !           156:   unsigned char *aData;
        !           157:   aData = print_byte_range(0, 100, 0);
        !           158:   printf("Decoded:\n");
        !           159:   print_decode_line(aData, 16, 2, "Database page size");
        !           160:   print_decode_line(aData, 18, 1, "File format write version");
        !           161:   print_decode_line(aData, 19, 1, "File format read version");
        !           162:   print_decode_line(aData, 20, 1, "Reserved space at end of page");
        !           163:   print_decode_line(aData, 24, 4, "File change counter");
        !           164:   print_decode_line(aData, 28, 4, "Size of database in pages");
        !           165:   print_decode_line(aData, 32, 4, "Page number of first freelist page");
        !           166:   print_decode_line(aData, 36, 4, "Number of freelist pages");
        !           167:   print_decode_line(aData, 40, 4, "Schema cookie");
        !           168:   print_decode_line(aData, 44, 4, "Schema format version");
        !           169:   print_decode_line(aData, 48, 4, "Default page cache size");
        !           170:   print_decode_line(aData, 52, 4, "Largest auto-vac root page");
        !           171:   print_decode_line(aData, 56, 4, "Text encoding");
        !           172:   print_decode_line(aData, 60, 4, "User version");
        !           173:   print_decode_line(aData, 64, 4, "Incremental-vacuum mode");
        !           174:   print_decode_line(aData, 68, 4, "meta[7]");
        !           175:   print_decode_line(aData, 72, 4, "meta[8]");
        !           176:   print_decode_line(aData, 76, 4, "meta[9]");
        !           177:   print_decode_line(aData, 80, 4, "meta[10]");
        !           178:   print_decode_line(aData, 84, 4, "meta[11]");
        !           179:   print_decode_line(aData, 88, 4, "meta[12]");
        !           180:   print_decode_line(aData, 92, 4, "Change counter for version number");
        !           181:   print_decode_line(aData, 96, 4, "SQLite version number");
        !           182: }
        !           183: 
        !           184: /*
        !           185: ** Describe cell content.
        !           186: */
        !           187: static int describeContent(
        !           188:   unsigned char *a,       /* Cell content */
        !           189:   int nLocal,             /* Bytes in a[] */
        !           190:   char *zDesc             /* Write description here */
        !           191: ){
        !           192:   int nDesc = 0;
        !           193:   int n, i, j;
        !           194:   i64 x, v;
        !           195:   const unsigned char *pData;
        !           196:   const unsigned char *pLimit;
        !           197:   char sep = ' ';
        !           198: 
        !           199:   pLimit = &a[nLocal];
        !           200:   n = decodeVarint(a, &x);
        !           201:   pData = &a[x];
        !           202:   a += n;
        !           203:   i = x - n;
        !           204:   while( i>0 && pData<=pLimit ){
        !           205:     n = decodeVarint(a, &x);
        !           206:     a += n;
        !           207:     i -= n;
        !           208:     nLocal -= n;
        !           209:     zDesc[0] = sep;
        !           210:     sep = ',';
        !           211:     nDesc++;
        !           212:     zDesc++;
        !           213:     if( x==0 ){
        !           214:       sprintf(zDesc, "*");     /* NULL is a "*" */
        !           215:     }else if( x>=1 && x<=6 ){
        !           216:       v = (signed char)pData[0];
        !           217:       pData++;
        !           218:       switch( x ){
        !           219:         case 6:  v = (v<<16) + (pData[0]<<8) + pData[1];  pData += 2;
        !           220:         case 5:  v = (v<<16) + (pData[0]<<8) + pData[1];  pData += 2;
        !           221:         case 4:  v = (v<<8) + pData[0];  pData++;
        !           222:         case 3:  v = (v<<8) + pData[0];  pData++;
        !           223:         case 2:  v = (v<<8) + pData[0];  pData++;
        !           224:       }
        !           225:       sprintf(zDesc, "%lld", v);
        !           226:     }else if( x==7 ){
        !           227:       sprintf(zDesc, "real");
        !           228:       pData += 8;
        !           229:     }else if( x==8 ){
        !           230:       sprintf(zDesc, "0");
        !           231:     }else if( x==9 ){
        !           232:       sprintf(zDesc, "1");
        !           233:     }else if( x>=12 ){
        !           234:       int size = (x-12)/2;
        !           235:       if( (x&1)==0 ){
        !           236:         sprintf(zDesc, "blob(%d)", size);
        !           237:       }else{
        !           238:         sprintf(zDesc, "txt(%d)", size);
        !           239:       }
        !           240:       pData += size;
        !           241:     }
        !           242:     j = strlen(zDesc);
        !           243:     zDesc += j;
        !           244:     nDesc += j;
        !           245:   }
        !           246:   return nDesc;
        !           247: }
        !           248: 
        !           249: /*
        !           250: ** Compute the local payload size given the total payload size and
        !           251: ** the page size.
        !           252: */
        !           253: static int localPayload(i64 nPayload, char cType){
        !           254:   int maxLocal;
        !           255:   int minLocal;
        !           256:   int surplus;
        !           257:   int nLocal;
        !           258:   if( cType==13 ){
        !           259:     /* Table leaf */
        !           260:     maxLocal = pagesize-35;
        !           261:     minLocal = (pagesize-12)*32/255-23;
        !           262:   }else{
        !           263:     maxLocal = (pagesize-12)*64/255-23;
        !           264:     minLocal = (pagesize-12)*32/255-23;
        !           265:   }
        !           266:   if( nPayload>maxLocal ){
        !           267:     surplus = minLocal + (nPayload-minLocal)%(pagesize-4);
        !           268:     if( surplus<=maxLocal ){
        !           269:       nLocal = surplus;
        !           270:     }else{
        !           271:       nLocal = minLocal;
        !           272:     }
        !           273:   }else{
        !           274:     nLocal = nPayload;
        !           275:   }
        !           276:   return nLocal;
        !           277: }
        !           278:   
        !           279: 
        !           280: /*
        !           281: ** Create a description for a single cell.
        !           282: **
        !           283: ** The return value is the local cell size.
        !           284: */
        !           285: static int describeCell(
        !           286:   unsigned char cType,    /* Page type */
        !           287:   unsigned char *a,       /* Cell content */
        !           288:   int showCellContent,    /* Show cell content if true */
        !           289:   char **pzDesc           /* Store description here */
        !           290: ){
        !           291:   int i;
        !           292:   int nDesc = 0;
        !           293:   int n = 0;
        !           294:   int leftChild;
        !           295:   i64 nPayload;
        !           296:   i64 rowid;
        !           297:   int nLocal;
        !           298:   static char zDesc[1000];
        !           299:   i = 0;
        !           300:   if( cType<=5 ){
        !           301:     leftChild = ((a[0]*256 + a[1])*256 + a[2])*256 + a[3];
        !           302:     a += 4;
        !           303:     n += 4;
        !           304:     sprintf(zDesc, "lx: %d ", leftChild);
        !           305:     nDesc = strlen(zDesc);
        !           306:   }
        !           307:   if( cType!=5 ){
        !           308:     i = decodeVarint(a, &nPayload);
        !           309:     a += i;
        !           310:     n += i;
        !           311:     sprintf(&zDesc[nDesc], "n: %lld ", nPayload);
        !           312:     nDesc += strlen(&zDesc[nDesc]);
        !           313:     nLocal = localPayload(nPayload, cType);
        !           314:   }else{
        !           315:     nPayload = nLocal = 0;
        !           316:   }
        !           317:   if( cType==5 || cType==13 ){
        !           318:     i = decodeVarint(a, &rowid);
        !           319:     a += i;
        !           320:     n += i;
        !           321:     sprintf(&zDesc[nDesc], "r: %lld ", rowid);
        !           322:     nDesc += strlen(&zDesc[nDesc]);
        !           323:   }
        !           324:   if( nLocal<nPayload ){
        !           325:     int ovfl;
        !           326:     unsigned char *b = &a[nLocal];
        !           327:     ovfl = ((b[0]*256 + b[1])*256 + b[2])*256 + b[3];
        !           328:     sprintf(&zDesc[nDesc], "ov: %d ", ovfl);
        !           329:     nDesc += strlen(&zDesc[nDesc]);
        !           330:     n += 4;
        !           331:   }
        !           332:   if( showCellContent && cType!=5 ){
        !           333:     nDesc += describeContent(a, nLocal, &zDesc[nDesc-1]);
        !           334:   }
        !           335:   *pzDesc = zDesc;
        !           336:   return nLocal+n;
        !           337: }
        !           338: 
        !           339: /*
        !           340: ** Decode a btree page
        !           341: */
        !           342: static void decode_btree_page(
        !           343:   unsigned char *a,   /* Page content */
        !           344:   int pgno,           /* Page number */
        !           345:   int hdrSize,        /* Size of the page header.  0 or 100 */
        !           346:   char *zArgs         /* Flags to control formatting */
        !           347: ){
        !           348:   const char *zType = "unknown";
        !           349:   int nCell;
        !           350:   int i, j;
        !           351:   int iCellPtr;
        !           352:   int showCellContent = 0;
        !           353:   int showMap = 0;
        !           354:   char *zMap = 0;
        !           355:   switch( a[0] ){
        !           356:     case 2:  zType = "index interior node";  break;
        !           357:     case 5:  zType = "table interior node";  break;
        !           358:     case 10: zType = "index leaf";           break;
        !           359:     case 13: zType = "table leaf";           break;
        !           360:   }
        !           361:   while( zArgs[0] ){
        !           362:     switch( zArgs[0] ){
        !           363:       case 'c': showCellContent = 1;  break;
        !           364:       case 'm': showMap = 1;          break;
        !           365:     }
        !           366:     zArgs++;
        !           367:   }
        !           368:   printf("Decode of btree page %d:\n", pgno);
        !           369:   print_decode_line(a, 0, 1, zType);
        !           370:   print_decode_line(a, 1, 2, "Offset to first freeblock");
        !           371:   print_decode_line(a, 3, 2, "Number of cells on this page");
        !           372:   nCell = a[3]*256 + a[4];
        !           373:   print_decode_line(a, 5, 2, "Offset to cell content area");
        !           374:   print_decode_line(a, 7, 1, "Fragmented byte count");
        !           375:   if( a[0]==2 || a[0]==5 ){
        !           376:     print_decode_line(a, 8, 4, "Right child");
        !           377:     iCellPtr = 12;
        !           378:   }else{
        !           379:     iCellPtr = 8;
        !           380:   }
        !           381:   if( nCell>0 ){
        !           382:     printf(" key: lx=left-child n=payload-size r=rowid\n");
        !           383:   }
        !           384:   if( showMap ){
        !           385:     zMap = malloc(pagesize);
        !           386:     memset(zMap, '.', pagesize);
        !           387:     memset(zMap, '1', hdrSize);
        !           388:     memset(&zMap[hdrSize], 'H', iCellPtr);
        !           389:     memset(&zMap[hdrSize+iCellPtr], 'P', 2*nCell);
        !           390:   }
        !           391:   for(i=0; i<nCell; i++){
        !           392:     int cofst = iCellPtr + i*2;
        !           393:     char *zDesc;
        !           394:     int n;
        !           395: 
        !           396:     cofst = a[cofst]*256 + a[cofst+1];
        !           397:     n = describeCell(a[0], &a[cofst-hdrSize], showCellContent, &zDesc);
        !           398:     if( showMap ){
        !           399:       char zBuf[30];
        !           400:       memset(&zMap[cofst], '*', n);
        !           401:       zMap[cofst] = '[';
        !           402:       zMap[cofst+n-1] = ']';
        !           403:       sprintf(zBuf, "%d", i);
        !           404:       j = strlen(zBuf);
        !           405:       if( j<=n-2 ) memcpy(&zMap[cofst+1], zBuf, j);
        !           406:     }
        !           407:     printf(" %03x: cell[%d] %s\n", cofst, i, zDesc);
        !           408:   }
        !           409:   if( showMap ){
        !           410:     for(i=0; i<pagesize; i+=64){
        !           411:       printf(" %03x: %.64s\n", i, &zMap[i]);
        !           412:     }
        !           413:     free(zMap);
        !           414:   }  
        !           415: }
        !           416: 
        !           417: /*
        !           418: ** Decode a freelist trunk page.
        !           419: */
        !           420: static void decode_trunk_page(
        !           421:   int pgno,             /* The page number */
        !           422:   int pagesize,         /* Size of each page */
        !           423:   int detail,           /* Show leaf pages if true */
        !           424:   int recursive         /* Follow the trunk change if true */
        !           425: ){
        !           426:   int n, i, k;
        !           427:   unsigned char *a;
        !           428:   while( pgno>0 ){
        !           429:     a = getContent((pgno-1)*pagesize, pagesize);
        !           430:     printf("Decode of freelist trunk page %d:\n", pgno);
        !           431:     print_decode_line(a, 0, 4, "Next freelist trunk page");
        !           432:     print_decode_line(a, 4, 4, "Number of entries on this page");
        !           433:     if( detail ){
        !           434:       n = (int)decodeInt32(&a[4]);
        !           435:       for(i=0; i<n; i++){
        !           436:         unsigned int x = decodeInt32(&a[8+4*i]);
        !           437:         char zIdx[10];
        !           438:         sprintf(zIdx, "[%d]", i);
        !           439:         printf("  %5s %7u", zIdx, x);
        !           440:         if( i%5==4 ) printf("\n");
        !           441:       }
        !           442:       if( i%5!=0 ) printf("\n");
        !           443:     }
        !           444:     if( !recursive ){
        !           445:       pgno = 0;
        !           446:     }else{
        !           447:       pgno = (int)decodeInt32(&a[0]);
        !           448:     }
        !           449:     free(a);
        !           450:   }
        !           451: }
        !           452: 
        !           453: /*
        !           454: ** Print a usage comment
        !           455: */
        !           456: static void usage(const char *argv0){
        !           457:   fprintf(stderr, "Usage %s FILENAME ?args...?\n\n", argv0);
        !           458:   fprintf(stderr,
        !           459:     "args:\n"
        !           460:     "    dbheader        Show database header\n"
        !           461:     "    NNN..MMM        Show hex of pages NNN through MMM\n"
        !           462:     "    NNN..end        Show hex of pages NNN through end of file\n"
        !           463:     "    NNNb            Decode btree page NNN\n"
        !           464:     "    NNNbc           Decode btree page NNN and show content\n"
        !           465:     "    NNNbm           Decode btree page NNN and show a layout map\n"
        !           466:     "    NNNt            Decode freelist trunk page NNN\n"
        !           467:     "    NNNtd           Show leaf freelist pages on the decode\n"
        !           468:     "    NNNtr           Recurisvely decode freelist starting at NNN\n"
        !           469:   );
        !           470: }
        !           471: 
        !           472: int main(int argc, char **argv){
        !           473:   struct stat sbuf;
        !           474:   unsigned char zPgSz[2];
        !           475:   if( argc<2 ){
        !           476:     usage(argv[0]);
        !           477:     exit(1);
        !           478:   }
        !           479:   db = open(argv[1], O_RDONLY);
        !           480:   if( db<0 ){
        !           481:     fprintf(stderr,"%s: can't open %s\n", argv[0], argv[1]);
        !           482:     exit(1);
        !           483:   }
        !           484:   zPgSz[0] = 0;
        !           485:   zPgSz[1] = 0;
        !           486:   lseek(db, 16, SEEK_SET);
        !           487:   read(db, zPgSz, 2);
        !           488:   pagesize = zPgSz[0]*256 + zPgSz[1]*65536;
        !           489:   if( pagesize==0 ) pagesize = 1024;
        !           490:   printf("Pagesize: %d\n", pagesize);
        !           491:   fstat(db, &sbuf);
        !           492:   mxPage = sbuf.st_size/pagesize;
        !           493:   printf("Available pages: 1..%d\n", mxPage);
        !           494:   if( argc==2 ){
        !           495:     int i;
        !           496:     for(i=1; i<=mxPage; i++) print_page(i);
        !           497:   }else{
        !           498:     int i;
        !           499:     for(i=2; i<argc; i++){
        !           500:       int iStart, iEnd;
        !           501:       char *zLeft;
        !           502:       if( strcmp(argv[i], "dbheader")==0 ){
        !           503:         print_db_header();
        !           504:         continue;
        !           505:       }
        !           506:       if( !isdigit(argv[i][0]) ){
        !           507:         fprintf(stderr, "%s: unknown option: [%s]\n", argv[0], argv[i]);
        !           508:         continue;
        !           509:       }
        !           510:       iStart = strtol(argv[i], &zLeft, 0);
        !           511:       if( zLeft && strcmp(zLeft,"..end")==0 ){
        !           512:         iEnd = mxPage;
        !           513:       }else if( zLeft && zLeft[0]=='.' && zLeft[1]=='.' ){
        !           514:         iEnd = strtol(&zLeft[2], 0, 0);
        !           515:       }else if( zLeft && zLeft[0]=='b' ){
        !           516:         int ofst, nByte, hdrSize;
        !           517:         unsigned char *a;
        !           518:         if( iStart==1 ){
        !           519:           ofst = hdrSize = 100;
        !           520:           nByte = pagesize-100;
        !           521:         }else{
        !           522:           hdrSize = 0;
        !           523:           ofst = (iStart-1)*pagesize;
        !           524:           nByte = pagesize;
        !           525:         }
        !           526:         a = getContent(ofst, nByte);
        !           527:         decode_btree_page(a, iStart, hdrSize, &zLeft[1]);
        !           528:         free(a);
        !           529:         continue;
        !           530:       }else if( zLeft && zLeft[0]=='t' ){
        !           531:         unsigned char *a;
        !           532:         int detail = 0;
        !           533:         int recursive = 0;
        !           534:         int i;
        !           535:         for(i=1; zLeft[i]; i++){
        !           536:           if( zLeft[i]=='r' ) recursive = 1;
        !           537:           if( zLeft[i]=='d' ) detail = 1;
        !           538:         }
        !           539:         decode_trunk_page(iStart, pagesize, detail, recursive);
        !           540:         continue;
        !           541:       }else{
        !           542:         iEnd = iStart;
        !           543:       }
        !           544:       if( iStart<1 || iEnd<iStart || iEnd>mxPage ){
        !           545:         fprintf(stderr,
        !           546:           "Page argument should be LOWER?..UPPER?.  Range 1 to %d\n",
        !           547:           mxPage);
        !           548:         exit(1);
        !           549:       }
        !           550:       while( iStart<=iEnd ){
        !           551:         print_page(iStart);
        !           552:         iStart++;
        !           553:       }
        !           554:     }
        !           555:   }
        !           556:   close(db);
        !           557: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>