/ Check-in [8e366b99]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Add new file ext/misc/zipfile.c, containing a virtual table for read-only access to simple zip archives.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | sqlar-shell-support
Files: files | file ages | folders
SHA3-256: 8e366b99b13d765d8bf000a7ec5919e582702e51dc07c27a746b6002898a2302
User & Date: dan 2017-12-26 20:39:58
Context
2017-12-27
18:54
Have the shell tool ".ar --list" and ".ar --extract" commands support zip files. Currently the "-zip" switch is required. check-in: a532a0f6 user: dan tags: sqlar-shell-support
2017-12-26
20:39
Add new file ext/misc/zipfile.c, containing a virtual table for read-only access to simple zip archives. check-in: 8e366b99 user: dan tags: sqlar-shell-support
2017-12-23
18:34
Merge enhancements from trunk. check-in: 150f07fe user: drh tags: sqlar-shell-support
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Added ext/misc/zipfile.c.

            1  +/*
            2  +** 2017-12-26
            3  +**
            4  +** The author disclaims copyright to this source code.  In place of
            5  +** a legal notice, here is a blessing:
            6  +**
            7  +**    May you do good and not evil.
            8  +**    May you find forgiveness for yourself and forgive others.
            9  +**    May you share freely, never taking more than you give.
           10  +**
           11  +******************************************************************************
           12  +**
           13  +*/
           14  +#include "sqlite3ext.h"
           15  +SQLITE_EXTENSION_INIT1
           16  +#include <stdio.h>
           17  +#include <string.h>
           18  +#include <assert.h>
           19  +
           20  +#include <sys/types.h>
           21  +#include <sys/stat.h>
           22  +#include <fcntl.h>
           23  +#include <unistd.h>
           24  +#include <dirent.h>
           25  +#include <time.h>
           26  +#include <utime.h>
           27  +#include <errno.h>
           28  +
           29  +#ifndef SQLITE_OMIT_VIRTUALTABLE
           30  +
           31  +#ifndef SQLITE_AMALGAMATION
           32  +typedef sqlite3_int64 i64;
           33  +typedef unsigned char u8;
           34  +typedef unsigned short u16;
           35  +typedef unsigned long u32;
           36  +#define MIN(a,b) ((a)<(b) ? (a) : (b))
           37  +#endif
           38  +
           39  +#define ZIPFILE_SCHEMA "CREATE TABLE y("                           \
           40  +  "name,      /* Name of file in zip archive */"                   \
           41  +  "mode,      /* POSIX mode for file */"                           \
           42  +  "mtime,     /* Last modification time in seconds since epoch */" \
           43  +  "sz,        /* Size of object */"                                \
           44  +  "data,      /* Data stored in zip file (possibly compressed) */" \
           45  +  "method,    /* Compression method (integer) */"                  \
           46  +  "f HIDDEN   /* Name of zip file */"                              \
           47  +");"
           48  +
           49  +#define ZIPFILE_F_COLUMN_IDX 6    /* Index of column "f" in the above */
           50  +
           51  +#define ZIPFILE_BUFFER_SIZE (64*1024)
           52  +
           53  +/*
           54  +** Set the error message contained in context ctx to the results of
           55  +** vprintf(zFmt, ...).
           56  +*/
           57  +static void zipfileCtxErrorMsg(sqlite3_context *ctx, const char *zFmt, ...){
           58  +  char *zMsg = 0;
           59  +  va_list ap;
           60  +  va_start(ap, zFmt);
           61  +  zMsg = sqlite3_vmprintf(zFmt, ap);
           62  +  sqlite3_result_error(ctx, zMsg, -1);
           63  +  sqlite3_free(zMsg);
           64  +  va_end(ap);
           65  +}
           66  +
           67  +
           68  +/*
           69  +*** 4.3.16  End of central directory record:
           70  +***
           71  +***   end of central dir signature    4 bytes  (0x06054b50)
           72  +***   number of this disk             2 bytes
           73  +***   number of the disk with the
           74  +***   start of the central directory  2 bytes
           75  +***   total number of entries in the
           76  +***   central directory on this disk  2 bytes
           77  +***   total number of entries in
           78  +***   the central directory           2 bytes
           79  +***   size of the central directory   4 bytes
           80  +***   offset of start of central
           81  +***   directory with respect to
           82  +***   the starting disk number        4 bytes
           83  +***   .ZIP file comment length        2 bytes
           84  +***   .ZIP file comment       (variable size)
           85  +*/
           86  +typedef struct ZipfileEOCD ZipfileEOCD;
           87  +struct ZipfileEOCD {
           88  +  u16 iDisk;
           89  +  u16 iFirstDisk;
           90  +  u16 nEntry;
           91  +  u16 nEntryTotal;
           92  +  u32 nSize;
           93  +  u32 iOffset;
           94  +};
           95  +
           96  +/*
           97  +*** 4.3.12  Central directory structure:
           98  +***
           99  +*** ...
          100  +***
          101  +***   central file header signature   4 bytes  (0x02014b50)
          102  +***   version made by                 2 bytes
          103  +***   version needed to extract       2 bytes
          104  +***   general purpose bit flag        2 bytes
          105  +***   compression method              2 bytes
          106  +***   last mod file time              2 bytes
          107  +***   last mod file date              2 bytes
          108  +***   crc-32                          4 bytes
          109  +***   compressed size                 4 bytes
          110  +***   uncompressed size               4 bytes
          111  +***   file name length                2 bytes
          112  +***   extra field length              2 bytes
          113  +***   file comment length             2 bytes
          114  +***   disk number start               2 bytes
          115  +***   internal file attributes        2 bytes
          116  +***   external file attributes        4 bytes
          117  +***   relative offset of local header 4 bytes
          118  +*/
          119  +typedef struct ZipfileCDS ZipfileCDS;
          120  +struct ZipfileCDS {
          121  +  u16 iVersionMadeBy;
          122  +  u16 iVersionExtract;
          123  +  u16 flags;
          124  +  u16 iCompression;
          125  +  u16 mTime;
          126  +  u16 mDate;
          127  +  u32 crc32;
          128  +  u32 szCompressed;
          129  +  u32 szUncompressed;
          130  +  u16 nFile;
          131  +  u16 nExtra;
          132  +  u16 nComment;
          133  +  u16 iDiskStart;
          134  +  u16 iInternalAttr;
          135  +  u32 iExternalAttr;
          136  +  u32 iOffset;
          137  +  char *zFile;                    /* Filename (sqlite3_malloc()) */
          138  +};
          139  +
          140  +/*
          141  +*** 4.3.7  Local file header:
          142  +***
          143  +***   local file header signature     4 bytes  (0x04034b50)
          144  +***   version needed to extract       2 bytes
          145  +***   general purpose bit flag        2 bytes
          146  +***   compression method              2 bytes
          147  +***   last mod file time              2 bytes
          148  +***   last mod file date              2 bytes
          149  +***   crc-32                          4 bytes
          150  +***   compressed size                 4 bytes
          151  +***   uncompressed size               4 bytes
          152  +***   file name length                2 bytes
          153  +***   extra field length              2 bytes
          154  +***   
          155  +*/
          156  +typedef struct ZipfileLFH ZipfileLFH;
          157  +struct ZipfileLFH {
          158  +  u16 iVersionExtract;
          159  +  u16 flags;
          160  +  u16 iCompression;
          161  +  u16 mTime;
          162  +  u16 mDate;
          163  +  u32 crc32;
          164  +  u32 szCompressed;
          165  +  u32 szUncompressed;
          166  +  u16 nFile;
          167  +  u16 nExtra;
          168  +};
          169  +
          170  +/* 
          171  +** Cursor type for recursively iterating through a directory structure.
          172  +*/
          173  +typedef struct ZipfileCsr ZipfileCsr;
          174  +
          175  +struct ZipfileCsr {
          176  +  sqlite3_vtab_cursor base;  /* Base class - must be first */
          177  +  i64 iRowid;                /* Rowid for current row */
          178  +  FILE *pFile;               /* Zip file */
          179  +  i64 nByte;                 /* Size of zip file on disk */
          180  +  int bEof;                  /* True when at EOF */
          181  +  i64 iNextOff;              /* Offset of next record in central directory */
          182  +  ZipfileEOCD eocd;          /* Parse of central directory record */
          183  +  ZipfileCDS cds;            /* Central Directory Structure */
          184  +  ZipfileLFH lfh;            /* Local File Header for current entry */
          185  +  i64 iDataOff;              /* Offset in zipfile to data */
          186  +  u32 mTime;                 /* Extended mtime value */
          187  +  int flags;
          188  +  u8 *aBuffer;               /* Buffer used for various tasks */
          189  +};
          190  +
          191  +#define ZIPFILE_MTIME_VALID 0x0001
          192  +
          193  +typedef struct ZipfileTab ZipfileTab;
          194  +struct ZipfileTab {
          195  +  sqlite3_vtab base;         /* Base class - must be first */
          196  +};
          197  +
          198  +/*
          199  +** Construct a new ZipfileTab virtual table object.
          200  +*/
          201  +static int zipfileConnect(
          202  +  sqlite3 *db,
          203  +  void *pAux,
          204  +  int argc, const char *const*argv,
          205  +  sqlite3_vtab **ppVtab,
          206  +  char **pzErr
          207  +){
          208  +  ZipfileTab *pNew = 0;
          209  +  int rc;
          210  +
          211  +  rc = sqlite3_declare_vtab(db, ZIPFILE_SCHEMA);
          212  +  if( rc==SQLITE_OK ){
          213  +    pNew = (ZipfileTab*)sqlite3_malloc( sizeof(*pNew) );
          214  +    if( pNew==0 ) return SQLITE_NOMEM;
          215  +    memset(pNew, 0, sizeof(*pNew));
          216  +  }
          217  +  *ppVtab = (sqlite3_vtab*)pNew;
          218  +  return rc;
          219  +}
          220  +
          221  +/*
          222  +** This method is the destructor for zipfile vtab objects.
          223  +*/
          224  +static int zipfileDisconnect(sqlite3_vtab *pVtab){
          225  +  sqlite3_free(pVtab);
          226  +  return SQLITE_OK;
          227  +}
          228  +
          229  +/*
          230  +** Constructor for a new ZipfileCsr object.
          231  +*/
          232  +static int zipfileOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCsr){
          233  +  ZipfileCsr *pCsr;
          234  +  pCsr = sqlite3_malloc( sizeof(*pCsr) + ZIPFILE_BUFFER_SIZE);
          235  +  if( pCsr==0 ) return SQLITE_NOMEM;
          236  +  memset(pCsr, 0, sizeof(*pCsr));
          237  +  pCsr->aBuffer = (u8*)&pCsr[1];
          238  +  *ppCsr = &pCsr->base;
          239  +  return SQLITE_OK;
          240  +}
          241  +
          242  +/*
          243  +** Reset a cursor back to the state it was in when first returned
          244  +** by zipfileOpen().
          245  +*/
          246  +static void zipfileResetCursor(ZipfileCsr *pCsr){
          247  +  pCsr->iRowid = 0;
          248  +  pCsr->bEof = 0;
          249  +  if( pCsr->pFile ){
          250  +    fclose(pCsr->pFile);
          251  +    pCsr->pFile = 0;
          252  +  }
          253  +}
          254  +
          255  +/*
          256  +** Destructor for an ZipfileCsr.
          257  +*/
          258  +static int zipfileClose(sqlite3_vtab_cursor *cur){
          259  +  ZipfileCsr *pCsr = (ZipfileCsr*)cur;
          260  +  zipfileResetCursor(pCsr);
          261  +  sqlite3_free(pCsr);
          262  +  return SQLITE_OK;
          263  +}
          264  +
          265  +/*
          266  +** Set the error message for the virtual table associated with cursor
          267  +** pCsr to the results of vprintf(zFmt, ...).
          268  +*/
          269  +static void zipfileSetErrmsg(ZipfileCsr *pCsr, const char *zFmt, ...){
          270  +  va_list ap;
          271  +  va_start(ap, zFmt);
          272  +  pCsr->base.pVtab->zErrMsg = sqlite3_vmprintf(zFmt, ap);
          273  +  va_end(ap);
          274  +}
          275  +
          276  +static int zipfileReadData(ZipfileCsr *pCsr, u8 *aRead, int nRead, i64 iOff){
          277  +  size_t n;
          278  +  fseek(pCsr->pFile, iOff, SEEK_SET);
          279  +  n = fread(aRead, 1, nRead, pCsr->pFile);
          280  +  if( n!=nRead ){
          281  +    zipfileSetErrmsg(pCsr, "error in fread()");
          282  +    return SQLITE_ERROR;
          283  +  }
          284  +  return SQLITE_OK;
          285  +}
          286  +
          287  +static u16 zipfileGetU16(const u8 *aBuf){
          288  +  return (aBuf[1] << 8) + aBuf[0];
          289  +}
          290  +static u32 zipfileGetU32(const u8 *aBuf){
          291  +  return ((u32)(aBuf[3]) << 24)
          292  +       + ((u32)(aBuf[2]) << 16)
          293  +       + ((u32)(aBuf[1]) <<  8)
          294  +       + ((u32)(aBuf[0]) <<  0);
          295  +}
          296  +
          297  +#define zipfileRead32(aBuf) ( aBuf+=4, zipfileGetU32(aBuf-4) )
          298  +#define zipfileRead16(aBuf) ( aBuf+=2, zipfileGetU16(aBuf-2) )
          299  +
          300  +static int zipfileReadCDS(ZipfileCsr *pCsr){
          301  +  static const int szFix = 46;    /* Size of fixed-size part of CDS */
          302  +  u8 *aRead = pCsr->aBuffer;
          303  +  int rc;
          304  +
          305  +  rc = zipfileReadData(pCsr, aRead, szFix, pCsr->iNextOff);
          306  +  if( rc==SQLITE_OK ){
          307  +    u32 sig = zipfileRead32(aRead);
          308  +    if( sig!=0x02014b50 ){
          309  +      zipfileSetErrmsg(pCsr,"failed to read CDS at offset %lld",pCsr->iNextOff);
          310  +      rc = SQLITE_ERROR;
          311  +    }else{
          312  +      int nRead;
          313  +      pCsr->cds.iVersionMadeBy = zipfileRead16(aRead);
          314  +      pCsr->cds.iVersionExtract = zipfileRead16(aRead);
          315  +      pCsr->cds.flags = zipfileRead16(aRead);
          316  +      pCsr->cds.iCompression = zipfileRead16(aRead);
          317  +      pCsr->cds.mTime = zipfileRead16(aRead);
          318  +      pCsr->cds.mDate = zipfileRead16(aRead);
          319  +      pCsr->cds.crc32 = zipfileRead32(aRead);
          320  +      pCsr->cds.szCompressed = zipfileRead32(aRead);
          321  +      pCsr->cds.szUncompressed = zipfileRead32(aRead);
          322  +      pCsr->cds.nFile = zipfileRead16(aRead);
          323  +      pCsr->cds.nExtra = zipfileRead16(aRead);
          324  +      pCsr->cds.nComment = zipfileRead16(aRead);
          325  +      pCsr->cds.iDiskStart = zipfileRead16(aRead);
          326  +      pCsr->cds.iInternalAttr = zipfileRead16(aRead);
          327  +      pCsr->cds.iExternalAttr = zipfileRead32(aRead);
          328  +      pCsr->cds.iOffset = zipfileRead32(aRead);
          329  +
          330  +      assert( aRead==&pCsr->aBuffer[szFix] );
          331  +
          332  +      nRead = pCsr->cds.nFile + pCsr->cds.nExtra;
          333  +      aRead = pCsr->aBuffer;
          334  +      rc = zipfileReadData(pCsr, aRead, nRead, pCsr->iNextOff+szFix);
          335  +
          336  +      if( rc==SQLITE_OK ){
          337  +        pCsr->cds.zFile = sqlite3_mprintf("%.*s", (int)pCsr->cds.nFile, aRead);
          338  +        pCsr->iNextOff += szFix;
          339  +        pCsr->iNextOff += pCsr->cds.nFile;
          340  +        pCsr->iNextOff += pCsr->cds.nExtra;
          341  +        pCsr->iNextOff += pCsr->cds.nComment;
          342  +      }
          343  +
          344  +      /* Scan the "extra" fields */
          345  +      if( rc==SQLITE_OK ){
          346  +        u8 *p = &aRead[pCsr->cds.nFile];
          347  +        u8 *pEnd = &p[pCsr->cds.nExtra];
          348  +
          349  +        while( p<pEnd ){
          350  +          u16 id = zipfileRead16(p);
          351  +          u16 nByte = zipfileRead16(p);
          352  +
          353  +          switch( id ){
          354  +            case 0x5455: {        /* Extended timestamp */
          355  +              u8 b = p[0];
          356  +              if( b & 0x01 ){     /* 0x01 -> modtime is present */
          357  +                pCsr->mTime = zipfileGetU32(&p[1]);
          358  +                pCsr->flags |= ZIPFILE_MTIME_VALID;
          359  +              }
          360  +              break;
          361  +            }
          362  +
          363  +            case 0x7875:          /* Info-ZIP Unix (new) */
          364  +              break;
          365  +          }
          366  +
          367  +          p += nByte;
          368  +        }
          369  +      }
          370  +    }
          371  +  }
          372  +
          373  +  return rc;
          374  +}
          375  +
          376  +static int zipfileReadLFH(ZipfileCsr *pCsr){
          377  +  static const int szFix = 30;    /* Size of fixed-size part of LFH */
          378  +  u8 *aRead = pCsr->aBuffer;
          379  +  int rc;
          380  +
          381  +  rc = zipfileReadData(pCsr, aRead, szFix, pCsr->cds.iOffset);
          382  +  if( rc==SQLITE_OK ){
          383  +    u32 sig = zipfileRead32(aRead);
          384  +    if( sig!=0x04034b50 ){
          385  +      zipfileSetErrmsg(pCsr, "failed to read LFH at offset %d", 
          386  +          (int)pCsr->cds.iOffset
          387  +      );
          388  +      rc = SQLITE_ERROR;
          389  +    }else{
          390  +      pCsr->lfh.iVersionExtract = zipfileRead16(aRead);
          391  +      pCsr->lfh.flags = zipfileRead16(aRead);
          392  +      pCsr->lfh.iCompression = zipfileRead16(aRead);
          393  +      pCsr->lfh.mTime = zipfileRead16(aRead);
          394  +      pCsr->lfh.mDate = zipfileRead16(aRead);
          395  +      pCsr->lfh.crc32 = zipfileRead32(aRead);
          396  +      pCsr->lfh.szCompressed = zipfileRead32(aRead);
          397  +      pCsr->lfh.szUncompressed = zipfileRead32(aRead);
          398  +      pCsr->lfh.nFile = zipfileRead16(aRead);
          399  +      pCsr->lfh.nExtra = zipfileRead16(aRead);
          400  +      assert( aRead==&pCsr->aBuffer[szFix] );
          401  +      pCsr->iDataOff = pCsr->cds.iOffset+szFix+pCsr->lfh.nFile+pCsr->lfh.nExtra;
          402  +    }
          403  +  }
          404  +
          405  +  return rc;
          406  +}
          407  +
          408  +
          409  +/*
          410  +** Advance an ZipfileCsr to its next row of output.
          411  +*/
          412  +static int zipfileNext(sqlite3_vtab_cursor *cur){
          413  +  ZipfileCsr *pCsr = (ZipfileCsr*)cur;
          414  +  i64 iEof = pCsr->eocd.iOffset + pCsr->eocd.nSize;
          415  +  int rc = SQLITE_OK;
          416  +
          417  +  if( pCsr->iNextOff>=iEof ){
          418  +    pCsr->bEof = 1;
          419  +  }else{
          420  +    pCsr->iRowid++;
          421  +    pCsr->flags = 0;
          422  +    rc = zipfileReadCDS(pCsr);
          423  +    if( rc==SQLITE_OK ){
          424  +      rc = zipfileReadLFH(pCsr);
          425  +    }
          426  +  }
          427  +  return rc;
          428  +}
          429  +
          430  +/*
          431  +** "Standard" MS-DOS time format:
          432  +**
          433  +**   File modification time:
          434  +**     Bits 00-04: seconds divided by 2
          435  +**     Bits 05-10: minute
          436  +**     Bits 11-15: hour
          437  +**   File modification date:
          438  +**     Bits 00-04: day
          439  +**     Bits 05-08: month (1-12)
          440  +**     Bits 09-15: years from 1980 
          441  +*/
          442  +static time_t zipfileMtime(ZipfileCsr *pCsr){
          443  +  struct tm t;
          444  +  memset(&t, 0, sizeof(t));
          445  +  t.tm_sec = (pCsr->cds.mTime & 0x1F)*2;
          446  +  t.tm_min = (pCsr->cds.mTime >> 5) & 0x2F;
          447  +  t.tm_hour = (pCsr->cds.mTime >> 11) & 0x1F;
          448  +
          449  +  t.tm_mday = (pCsr->cds.mDate & 0x1F);
          450  +  t.tm_mon = ((pCsr->cds.mDate >> 5) & 0x0F) - 1;
          451  +  t.tm_year = 80 + ((pCsr->cds.mDate >> 9) & 0x7F);
          452  +
          453  +  return mktime(&t);
          454  +}
          455  +
          456  +/*
          457  +** Return values of columns for the row at which the series_cursor
          458  +** is currently pointing.
          459  +*/
          460  +static int zipfileColumn(
          461  +  sqlite3_vtab_cursor *cur,   /* The cursor */
          462  +  sqlite3_context *ctx,       /* First argument to sqlite3_result_...() */
          463  +  int i                       /* Which column to return */
          464  +){
          465  +  ZipfileCsr *pCsr = (ZipfileCsr*)cur;
          466  +  int rc = SQLITE_OK;
          467  +  switch( i ){
          468  +    case 0:   /* name */
          469  +      sqlite3_result_text(ctx, pCsr->cds.zFile, -1, SQLITE_TRANSIENT);
          470  +      break;
          471  +    case 1:   /* mode */
          472  +      /* TODO: Whether or not the following is correct surely depends on
          473  +      ** the platform on which the archive was created.  */
          474  +      sqlite3_result_int(ctx, pCsr->cds.iExternalAttr >> 16);
          475  +      break;
          476  +    case 2: { /* mtime */
          477  +      if( pCsr->flags & ZIPFILE_MTIME_VALID ){
          478  +        sqlite3_result_int64(ctx, pCsr->mTime);
          479  +      }else{
          480  +        sqlite3_result_int64(ctx, zipfileMtime(pCsr));
          481  +      }
          482  +      break;
          483  +    }
          484  +    case 3: { /* sz */
          485  +      sqlite3_result_int64(ctx, pCsr->cds.szUncompressed);
          486  +      break;
          487  +    }
          488  +    case 4: { /* data */
          489  +      int sz = pCsr->cds.szCompressed;
          490  +      if( sz>0 ){
          491  +        u8 *aBuf = sqlite3_malloc(sz);
          492  +        if( aBuf==0 ){
          493  +          rc = SQLITE_NOMEM;
          494  +        }else{
          495  +          rc = zipfileReadData(pCsr, aBuf, sz, pCsr->iDataOff);
          496  +        }
          497  +        if( rc==SQLITE_OK ){
          498  +          sqlite3_result_blob(ctx, aBuf, sz, SQLITE_TRANSIENT);
          499  +          sqlite3_free(aBuf);
          500  +        }
          501  +      }
          502  +      break;
          503  +    }
          504  +    case 5:   /* method */
          505  +      sqlite3_result_int(ctx, pCsr->cds.iCompression);
          506  +      break;
          507  +  }
          508  +
          509  +  return SQLITE_OK;
          510  +}
          511  +
          512  +/*
          513  +** Return the rowid for the current row. In this implementation, the
          514  +** first row returned is assigned rowid value 1, and each subsequent
          515  +** row a value 1 more than that of the previous.
          516  +*/
          517  +static int zipfileRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
          518  +  ZipfileCsr *pCsr = (ZipfileCsr*)cur;
          519  +  *pRowid = pCsr->iRowid;
          520  +  return SQLITE_OK;
          521  +}
          522  +
          523  +/*
          524  +** Return TRUE if the cursor has been moved off of the last
          525  +** row of output.
          526  +*/
          527  +static int zipfileEof(sqlite3_vtab_cursor *cur){
          528  +  ZipfileCsr *pCsr = (ZipfileCsr*)cur;
          529  +  return pCsr->bEof;
          530  +}
          531  +
          532  +/*
          533  +** The zip file has been successfully opened (so pCsr->pFile is valid). 
          534  +** This function attempts to locate and read the End of central
          535  +** directory record from the file.
          536  +**
          537  +*/
          538  +static int zipfileReadEOCD(ZipfileCsr *pCsr, ZipfileEOCD *pEOCD){
          539  +  u8 *aRead = pCsr->aBuffer;
          540  +  int nRead = (int)(MIN(pCsr->nByte, ZIPFILE_BUFFER_SIZE));
          541  +  i64 iOff = pCsr->nByte - nRead;
          542  +
          543  +  int rc = zipfileReadData(pCsr, aRead, nRead, iOff);
          544  +  if( rc==SQLITE_OK ){
          545  +    int i;
          546  +
          547  +    /* Scan backwards looking for the signature bytes */
          548  +    for(i=nRead-20; i>=0; i--){
          549  +      if( aRead[i]==0x50 && aRead[i+1]==0x4b 
          550  +       && aRead[i+2]==0x05 && aRead[i+3]==0x06 
          551  +      ){
          552  +        break;
          553  +      }
          554  +    }
          555  +    if( i<0 ){
          556  +      zipfileSetErrmsg(pCsr, "cannot find end of central directory record");
          557  +      return SQLITE_ERROR;
          558  +    }
          559  +
          560  +    aRead += i+4;
          561  +    pEOCD->iDisk = zipfileRead16(aRead);
          562  +    pEOCD->iFirstDisk = zipfileRead16(aRead);
          563  +    pEOCD->nEntry = zipfileRead16(aRead);
          564  +    pEOCD->nEntryTotal = zipfileRead16(aRead);
          565  +    pEOCD->nSize = zipfileRead32(aRead);
          566  +    pEOCD->iOffset = zipfileRead32(aRead);
          567  +
          568  +#if 0
          569  +    printf("iDisk=%d  iFirstDisk=%d  nEntry=%d  "
          570  +           "nEntryTotal=%d  nSize=%d  iOffset=%d", 
          571  +           (int)pEOCD->iDisk, (int)pEOCD->iFirstDisk, (int)pEOCD->nEntry,
          572  +           (int)pEOCD->nEntryTotal, (int)pEOCD->nSize, (int)pEOCD->iOffset
          573  +    );
          574  +#endif
          575  +  }
          576  +
          577  +  return SQLITE_OK;
          578  +}
          579  +
          580  +/*
          581  +** xFilter callback.
          582  +*/
          583  +static int zipfileFilter(
          584  +  sqlite3_vtab_cursor *cur, 
          585  +  int idxNum, const char *idxStr,
          586  +  int argc, sqlite3_value **argv
          587  +){
          588  +  ZipfileCsr *pCsr = (ZipfileCsr*)cur;
          589  +  const char *zFile;              /* Zip file to scan */
          590  +  int rc = SQLITE_OK;             /* Return Code */
          591  +
          592  +  zipfileResetCursor(pCsr);
          593  +
          594  +  assert( idxNum==argc && (idxNum==0 || idxNum==1) );
          595  +  if( idxNum==0 ){
          596  +    /* Error. User did not supply a file name. */
          597  +    zipfileSetErrmsg(pCsr, "table function zipfile() requires an argument");
          598  +    return SQLITE_ERROR;
          599  +  }
          600  +
          601  +  zFile = sqlite3_value_text(argv[0]);
          602  +  pCsr->pFile = fopen(zFile, "rb");
          603  +  if( pCsr->pFile==0 ){
          604  +    zipfileSetErrmsg(pCsr, "cannot open file: %s", zFile);
          605  +    rc = SQLITE_ERROR;
          606  +  }else{
          607  +    fseek(pCsr->pFile, 0, SEEK_END);
          608  +    pCsr->nByte = (i64)ftell(pCsr->pFile);
          609  +    rc = zipfileReadEOCD(pCsr, &pCsr->eocd);
          610  +    if( rc==SQLITE_OK ){
          611  +      pCsr->iNextOff = pCsr->eocd.iOffset;
          612  +      rc = zipfileNext(cur);
          613  +    }
          614  +  }
          615  +
          616  +  return rc;
          617  +}
          618  +
          619  +/*
          620  +** xBestIndex callback.
          621  +*/
          622  +static int zipfileBestIndex(
          623  +  sqlite3_vtab *tab,
          624  +  sqlite3_index_info *pIdxInfo
          625  +){
          626  +  int i;
          627  +
          628  +  for(i=0; i<pIdxInfo->nConstraint; i++){
          629  +    const struct sqlite3_index_constraint *pCons = &pIdxInfo->aConstraint[i];
          630  +    if( pCons->usable==0 ) continue;
          631  +    if( pCons->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
          632  +    if( pCons->iColumn!=ZIPFILE_F_COLUMN_IDX ) continue;
          633  +    break;
          634  +  }
          635  +
          636  +  if( i<pIdxInfo->nConstraint ){
          637  +    pIdxInfo->aConstraintUsage[i].argvIndex = 1;
          638  +    pIdxInfo->aConstraintUsage[i].omit = 1;
          639  +    pIdxInfo->estimatedCost = 1000.0;
          640  +    pIdxInfo->idxNum = 1;
          641  +  }else{
          642  +    pIdxInfo->estimatedCost = (double)(((sqlite3_int64)1) << 50);
          643  +    pIdxInfo->idxNum = 0;
          644  +  }
          645  +
          646  +  return SQLITE_OK;
          647  +}
          648  +
          649  +/*
          650  +** Register the "zipfile" virtual table.
          651  +*/
          652  +static int zipfileRegister(sqlite3 *db){
          653  +  static sqlite3_module zipfileModule = {
          654  +    0,                         /* iVersion */
          655  +    0,                         /* xCreate */
          656  +    zipfileConnect,            /* xConnect */
          657  +    zipfileBestIndex,          /* xBestIndex */
          658  +    zipfileDisconnect,         /* xDisconnect */
          659  +    0,                         /* xDestroy */
          660  +    zipfileOpen,               /* xOpen - open a cursor */
          661  +    zipfileClose,              /* xClose - close a cursor */
          662  +    zipfileFilter,             /* xFilter - configure scan constraints */
          663  +    zipfileNext,               /* xNext - advance a cursor */
          664  +    zipfileEof,                /* xEof - check for end of scan */
          665  +    zipfileColumn,             /* xColumn - read data */
          666  +    zipfileRowid,              /* xRowid - read data */
          667  +    0,                         /* xUpdate */
          668  +    0,                         /* xBegin */
          669  +    0,                         /* xSync */
          670  +    0,                         /* xCommit */
          671  +    0,                         /* xRollback */
          672  +    0,                         /* xFindMethod */
          673  +    0,                         /* xRename */
          674  +  };
          675  +
          676  +  int rc = sqlite3_create_module(db, "zipfile"  , &zipfileModule, 0);
          677  +  return rc;
          678  +}
          679  +#else         /* SQLITE_OMIT_VIRTUALTABLE */
          680  +# define zipfileRegister(x) SQLITE_OK
          681  +#endif
          682  +
          683  +#ifdef _WIN32
          684  +__declspec(dllexport)
          685  +#endif
          686  +int sqlite3_zipfile_init(
          687  +  sqlite3 *db, 
          688  +  char **pzErrMsg, 
          689  +  const sqlite3_api_routines *pApi
          690  +){
          691  +  int rc = SQLITE_OK;
          692  +  SQLITE_EXTENSION_INIT2(pApi);
          693  +  (void)pzErrMsg;  /* Unused parameter */
          694  +  return zipfileRegister(db);
          695  +}
          696  +

Changes to main.mk.

   691    691   SHELL_SRC = \
   692    692   	$(TOP)/src/shell.c.in \
   693    693   	$(TOP)/ext/misc/shathree.c \
   694    694   	$(TOP)/ext/misc/fileio.c \
   695    695   	$(TOP)/ext/misc/completion.c \
   696    696   	$(TOP)/ext/misc/sqlar.c \
   697    697   	$(TOP)/ext/expert/sqlite3expert.c \
   698         -	$(TOP)/ext/expert/sqlite3expert.h
          698  +	$(TOP)/ext/expert/sqlite3expert.h \
          699  +	$(TOP)/ext/misc/zipfile.c
   699    700   
   700    701   shell.c:	$(SHELL_SRC) $(TOP)/tool/mkshellc.tcl
   701    702   	tclsh $(TOP)/tool/mkshellc.tcl >shell.c
   702    703   
   703    704   
   704    705   
   705    706   # Rules to build the extension objects.

Changes to src/shell.c.in.

   793    793   #define SQLITE_EXTENSION_INIT1
   794    794   #define SQLITE_EXTENSION_INIT2(X) (void)(X)
   795    795   
   796    796   INCLUDE ../ext/misc/shathree.c
   797    797   INCLUDE ../ext/misc/fileio.c
   798    798   INCLUDE ../ext/misc/completion.c
   799    799   #ifdef SQLITE_HAVE_ZLIB
          800  +INCLUDE ../ext/misc/zipfile.c
   800    801   INCLUDE ../ext/misc/sqlar.c
   801    802   #endif
   802    803   INCLUDE ../ext/expert/sqlite3expert.h
   803    804   INCLUDE ../ext/expert/sqlite3expert.c
   804    805   
   805    806   #if defined(SQLITE_ENABLE_SESSION)
   806    807   /*
................................................................................
  2998   2999   #ifndef SQLITE_OMIT_LOAD_EXTENSION
  2999   3000       sqlite3_enable_load_extension(p->db, 1);
  3000   3001   #endif
  3001   3002       sqlite3_fileio_init(p->db, 0, 0);
  3002   3003       sqlite3_shathree_init(p->db, 0, 0);
  3003   3004       sqlite3_completion_init(p->db, 0, 0);
  3004   3005   #ifdef SQLITE_HAVE_ZLIB
         3006  +    sqlite3_zipfile_init(p->db, 0, 0);
  3005   3007       sqlite3_sqlar_init(p->db, 0, 0);
  3006   3008   #endif
  3007   3009       sqlite3_create_function(p->db, "shell_add_schema", 2, SQLITE_UTF8, 0,
  3008   3010                               shellAddSchemaName, 0, 0);
  3009   3011     }
  3010   3012   }
  3011   3013