]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add new file ext/misc/zipfile.c, containing a virtual table for read-only
authordan <dan@noemail.net>
Tue, 26 Dec 2017 20:39:58 +0000 (20:39 +0000)
committerdan <dan@noemail.net>
Tue, 26 Dec 2017 20:39:58 +0000 (20:39 +0000)
access to simple zip archives.

FossilOrigin-Name: 8e366b99b13d765d8bf000a7ec5919e582702e51dc07c27a746b6002898a2302

ext/misc/zipfile.c [new file with mode: 0644]
main.mk
manifest
manifest.uuid
src/shell.c.in

diff --git a/ext/misc/zipfile.c b/ext/misc/zipfile.c
new file mode 100644 (file)
index 0000000..bcee873
--- /dev/null
@@ -0,0 +1,696 @@
+/*
+** 2017-12-26
+**
+** The author disclaims copyright to this source code.  In place of
+** a legal notice, here is a blessing:
+**
+**    May you do good and not evil.
+**    May you find forgiveness for yourself and forgive others.
+**    May you share freely, never taking more than you give.
+**
+******************************************************************************
+**
+*/
+#include "sqlite3ext.h"
+SQLITE_EXTENSION_INIT1
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <time.h>
+#include <utime.h>
+#include <errno.h>
+
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+
+#ifndef SQLITE_AMALGAMATION
+typedef sqlite3_int64 i64;
+typedef unsigned char u8;
+typedef unsigned short u16;
+typedef unsigned long u32;
+#define MIN(a,b) ((a)<(b) ? (a) : (b))
+#endif
+
+#define ZIPFILE_SCHEMA "CREATE TABLE y("                           \
+  "name,      /* Name of file in zip archive */"                   \
+  "mode,      /* POSIX mode for file */"                           \
+  "mtime,     /* Last modification time in seconds since epoch */" \
+  "sz,        /* Size of object */"                                \
+  "data,      /* Data stored in zip file (possibly compressed) */" \
+  "method,    /* Compression method (integer) */"                  \
+  "f HIDDEN   /* Name of zip file */"                              \
+");"
+
+#define ZIPFILE_F_COLUMN_IDX 6    /* Index of column "f" in the above */
+
+#define ZIPFILE_BUFFER_SIZE (64*1024)
+
+/*
+** Set the error message contained in context ctx to the results of
+** vprintf(zFmt, ...).
+*/
+static void zipfileCtxErrorMsg(sqlite3_context *ctx, const char *zFmt, ...){
+  char *zMsg = 0;
+  va_list ap;
+  va_start(ap, zFmt);
+  zMsg = sqlite3_vmprintf(zFmt, ap);
+  sqlite3_result_error(ctx, zMsg, -1);
+  sqlite3_free(zMsg);
+  va_end(ap);
+}
+
+
+/*
+*** 4.3.16  End of central directory record:
+***
+***   end of central dir signature    4 bytes  (0x06054b50)
+***   number of this disk             2 bytes
+***   number of the disk with the
+***   start of the central directory  2 bytes
+***   total number of entries in the
+***   central directory on this disk  2 bytes
+***   total number of entries in
+***   the central directory           2 bytes
+***   size of the central directory   4 bytes
+***   offset of start of central
+***   directory with respect to
+***   the starting disk number        4 bytes
+***   .ZIP file comment length        2 bytes
+***   .ZIP file comment       (variable size)
+*/
+typedef struct ZipfileEOCD ZipfileEOCD;
+struct ZipfileEOCD {
+  u16 iDisk;
+  u16 iFirstDisk;
+  u16 nEntry;
+  u16 nEntryTotal;
+  u32 nSize;
+  u32 iOffset;
+};
+
+/*
+*** 4.3.12  Central directory structure:
+***
+*** ...
+***
+***   central file header signature   4 bytes  (0x02014b50)
+***   version made by                 2 bytes
+***   version needed to extract       2 bytes
+***   general purpose bit flag        2 bytes
+***   compression method              2 bytes
+***   last mod file time              2 bytes
+***   last mod file date              2 bytes
+***   crc-32                          4 bytes
+***   compressed size                 4 bytes
+***   uncompressed size               4 bytes
+***   file name length                2 bytes
+***   extra field length              2 bytes
+***   file comment length             2 bytes
+***   disk number start               2 bytes
+***   internal file attributes        2 bytes
+***   external file attributes        4 bytes
+***   relative offset of local header 4 bytes
+*/
+typedef struct ZipfileCDS ZipfileCDS;
+struct ZipfileCDS {
+  u16 iVersionMadeBy;
+  u16 iVersionExtract;
+  u16 flags;
+  u16 iCompression;
+  u16 mTime;
+  u16 mDate;
+  u32 crc32;
+  u32 szCompressed;
+  u32 szUncompressed;
+  u16 nFile;
+  u16 nExtra;
+  u16 nComment;
+  u16 iDiskStart;
+  u16 iInternalAttr;
+  u32 iExternalAttr;
+  u32 iOffset;
+  char *zFile;                    /* Filename (sqlite3_malloc()) */
+};
+
+/*
+*** 4.3.7  Local file header:
+***
+***   local file header signature     4 bytes  (0x04034b50)
+***   version needed to extract       2 bytes
+***   general purpose bit flag        2 bytes
+***   compression method              2 bytes
+***   last mod file time              2 bytes
+***   last mod file date              2 bytes
+***   crc-32                          4 bytes
+***   compressed size                 4 bytes
+***   uncompressed size               4 bytes
+***   file name length                2 bytes
+***   extra field length              2 bytes
+***   
+*/
+typedef struct ZipfileLFH ZipfileLFH;
+struct ZipfileLFH {
+  u16 iVersionExtract;
+  u16 flags;
+  u16 iCompression;
+  u16 mTime;
+  u16 mDate;
+  u32 crc32;
+  u32 szCompressed;
+  u32 szUncompressed;
+  u16 nFile;
+  u16 nExtra;
+};
+
+/* 
+** Cursor type for recursively iterating through a directory structure.
+*/
+typedef struct ZipfileCsr ZipfileCsr;
+
+struct ZipfileCsr {
+  sqlite3_vtab_cursor base;  /* Base class - must be first */
+  i64 iRowid;                /* Rowid for current row */
+  FILE *pFile;               /* Zip file */
+  i64 nByte;                 /* Size of zip file on disk */
+  int bEof;                  /* True when at EOF */
+  i64 iNextOff;              /* Offset of next record in central directory */
+  ZipfileEOCD eocd;          /* Parse of central directory record */
+  ZipfileCDS cds;            /* Central Directory Structure */
+  ZipfileLFH lfh;            /* Local File Header for current entry */
+  i64 iDataOff;              /* Offset in zipfile to data */
+  u32 mTime;                 /* Extended mtime value */
+  int flags;
+  u8 *aBuffer;               /* Buffer used for various tasks */
+};
+
+#define ZIPFILE_MTIME_VALID 0x0001
+
+typedef struct ZipfileTab ZipfileTab;
+struct ZipfileTab {
+  sqlite3_vtab base;         /* Base class - must be first */
+};
+
+/*
+** Construct a new ZipfileTab virtual table object.
+*/
+static int zipfileConnect(
+  sqlite3 *db,
+  void *pAux,
+  int argc, const char *const*argv,
+  sqlite3_vtab **ppVtab,
+  char **pzErr
+){
+  ZipfileTab *pNew = 0;
+  int rc;
+
+  rc = sqlite3_declare_vtab(db, ZIPFILE_SCHEMA);
+  if( rc==SQLITE_OK ){
+    pNew = (ZipfileTab*)sqlite3_malloc( sizeof(*pNew) );
+    if( pNew==0 ) return SQLITE_NOMEM;
+    memset(pNew, 0, sizeof(*pNew));
+  }
+  *ppVtab = (sqlite3_vtab*)pNew;
+  return rc;
+}
+
+/*
+** This method is the destructor for zipfile vtab objects.
+*/
+static int zipfileDisconnect(sqlite3_vtab *pVtab){
+  sqlite3_free(pVtab);
+  return SQLITE_OK;
+}
+
+/*
+** Constructor for a new ZipfileCsr object.
+*/
+static int zipfileOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCsr){
+  ZipfileCsr *pCsr;
+  pCsr = sqlite3_malloc( sizeof(*pCsr) + ZIPFILE_BUFFER_SIZE);
+  if( pCsr==0 ) return SQLITE_NOMEM;
+  memset(pCsr, 0, sizeof(*pCsr));
+  pCsr->aBuffer = (u8*)&pCsr[1];
+  *ppCsr = &pCsr->base;
+  return SQLITE_OK;
+}
+
+/*
+** Reset a cursor back to the state it was in when first returned
+** by zipfileOpen().
+*/
+static void zipfileResetCursor(ZipfileCsr *pCsr){
+  pCsr->iRowid = 0;
+  pCsr->bEof = 0;
+  if( pCsr->pFile ){
+    fclose(pCsr->pFile);
+    pCsr->pFile = 0;
+  }
+}
+
+/*
+** Destructor for an ZipfileCsr.
+*/
+static int zipfileClose(sqlite3_vtab_cursor *cur){
+  ZipfileCsr *pCsr = (ZipfileCsr*)cur;
+  zipfileResetCursor(pCsr);
+  sqlite3_free(pCsr);
+  return SQLITE_OK;
+}
+
+/*
+** Set the error message for the virtual table associated with cursor
+** pCsr to the results of vprintf(zFmt, ...).
+*/
+static void zipfileSetErrmsg(ZipfileCsr *pCsr, const char *zFmt, ...){
+  va_list ap;
+  va_start(ap, zFmt);
+  pCsr->base.pVtab->zErrMsg = sqlite3_vmprintf(zFmt, ap);
+  va_end(ap);
+}
+
+static int zipfileReadData(ZipfileCsr *pCsr, u8 *aRead, int nRead, i64 iOff){
+  size_t n;
+  fseek(pCsr->pFile, iOff, SEEK_SET);
+  n = fread(aRead, 1, nRead, pCsr->pFile);
+  if( n!=nRead ){
+    zipfileSetErrmsg(pCsr, "error in fread()");
+    return SQLITE_ERROR;
+  }
+  return SQLITE_OK;
+}
+
+static u16 zipfileGetU16(const u8 *aBuf){
+  return (aBuf[1] << 8) + aBuf[0];
+}
+static u32 zipfileGetU32(const u8 *aBuf){
+  return ((u32)(aBuf[3]) << 24)
+       + ((u32)(aBuf[2]) << 16)
+       + ((u32)(aBuf[1]) <<  8)
+       + ((u32)(aBuf[0]) <<  0);
+}
+
+#define zipfileRead32(aBuf) ( aBuf+=4, zipfileGetU32(aBuf-4) )
+#define zipfileRead16(aBuf) ( aBuf+=2, zipfileGetU16(aBuf-2) )
+
+static int zipfileReadCDS(ZipfileCsr *pCsr){
+  static const int szFix = 46;    /* Size of fixed-size part of CDS */
+  u8 *aRead = pCsr->aBuffer;
+  int rc;
+
+  rc = zipfileReadData(pCsr, aRead, szFix, pCsr->iNextOff);
+  if( rc==SQLITE_OK ){
+    u32 sig = zipfileRead32(aRead);
+    if( sig!=0x02014b50 ){
+      zipfileSetErrmsg(pCsr,"failed to read CDS at offset %lld",pCsr->iNextOff);
+      rc = SQLITE_ERROR;
+    }else{
+      int nRead;
+      pCsr->cds.iVersionMadeBy = zipfileRead16(aRead);
+      pCsr->cds.iVersionExtract = zipfileRead16(aRead);
+      pCsr->cds.flags = zipfileRead16(aRead);
+      pCsr->cds.iCompression = zipfileRead16(aRead);
+      pCsr->cds.mTime = zipfileRead16(aRead);
+      pCsr->cds.mDate = zipfileRead16(aRead);
+      pCsr->cds.crc32 = zipfileRead32(aRead);
+      pCsr->cds.szCompressed = zipfileRead32(aRead);
+      pCsr->cds.szUncompressed = zipfileRead32(aRead);
+      pCsr->cds.nFile = zipfileRead16(aRead);
+      pCsr->cds.nExtra = zipfileRead16(aRead);
+      pCsr->cds.nComment = zipfileRead16(aRead);
+      pCsr->cds.iDiskStart = zipfileRead16(aRead);
+      pCsr->cds.iInternalAttr = zipfileRead16(aRead);
+      pCsr->cds.iExternalAttr = zipfileRead32(aRead);
+      pCsr->cds.iOffset = zipfileRead32(aRead);
+
+      assert( aRead==&pCsr->aBuffer[szFix] );
+
+      nRead = pCsr->cds.nFile + pCsr->cds.nExtra;
+      aRead = pCsr->aBuffer;
+      rc = zipfileReadData(pCsr, aRead, nRead, pCsr->iNextOff+szFix);
+
+      if( rc==SQLITE_OK ){
+        pCsr->cds.zFile = sqlite3_mprintf("%.*s", (int)pCsr->cds.nFile, aRead);
+        pCsr->iNextOff += szFix;
+        pCsr->iNextOff += pCsr->cds.nFile;
+        pCsr->iNextOff += pCsr->cds.nExtra;
+        pCsr->iNextOff += pCsr->cds.nComment;
+      }
+
+      /* Scan the "extra" fields */
+      if( rc==SQLITE_OK ){
+        u8 *p = &aRead[pCsr->cds.nFile];
+        u8 *pEnd = &p[pCsr->cds.nExtra];
+
+        while( p<pEnd ){
+          u16 id = zipfileRead16(p);
+          u16 nByte = zipfileRead16(p);
+
+          switch( id ){
+            case 0x5455: {        /* Extended timestamp */
+              u8 b = p[0];
+              if( b & 0x01 ){     /* 0x01 -> modtime is present */
+                pCsr->mTime = zipfileGetU32(&p[1]);
+                pCsr->flags |= ZIPFILE_MTIME_VALID;
+              }
+              break;
+            }
+
+            case 0x7875:          /* Info-ZIP Unix (new) */
+              break;
+          }
+
+          p += nByte;
+        }
+      }
+    }
+  }
+
+  return rc;
+}
+
+static int zipfileReadLFH(ZipfileCsr *pCsr){
+  static const int szFix = 30;    /* Size of fixed-size part of LFH */
+  u8 *aRead = pCsr->aBuffer;
+  int rc;
+
+  rc = zipfileReadData(pCsr, aRead, szFix, pCsr->cds.iOffset);
+  if( rc==SQLITE_OK ){
+    u32 sig = zipfileRead32(aRead);
+    if( sig!=0x04034b50 ){
+      zipfileSetErrmsg(pCsr, "failed to read LFH at offset %d", 
+          (int)pCsr->cds.iOffset
+      );
+      rc = SQLITE_ERROR;
+    }else{
+      pCsr->lfh.iVersionExtract = zipfileRead16(aRead);
+      pCsr->lfh.flags = zipfileRead16(aRead);
+      pCsr->lfh.iCompression = zipfileRead16(aRead);
+      pCsr->lfh.mTime = zipfileRead16(aRead);
+      pCsr->lfh.mDate = zipfileRead16(aRead);
+      pCsr->lfh.crc32 = zipfileRead32(aRead);
+      pCsr->lfh.szCompressed = zipfileRead32(aRead);
+      pCsr->lfh.szUncompressed = zipfileRead32(aRead);
+      pCsr->lfh.nFile = zipfileRead16(aRead);
+      pCsr->lfh.nExtra = zipfileRead16(aRead);
+      assert( aRead==&pCsr->aBuffer[szFix] );
+      pCsr->iDataOff = pCsr->cds.iOffset+szFix+pCsr->lfh.nFile+pCsr->lfh.nExtra;
+    }
+  }
+
+  return rc;
+}
+
+
+/*
+** Advance an ZipfileCsr to its next row of output.
+*/
+static int zipfileNext(sqlite3_vtab_cursor *cur){
+  ZipfileCsr *pCsr = (ZipfileCsr*)cur;
+  i64 iEof = pCsr->eocd.iOffset + pCsr->eocd.nSize;
+  int rc = SQLITE_OK;
+
+  if( pCsr->iNextOff>=iEof ){
+    pCsr->bEof = 1;
+  }else{
+    pCsr->iRowid++;
+    pCsr->flags = 0;
+    rc = zipfileReadCDS(pCsr);
+    if( rc==SQLITE_OK ){
+      rc = zipfileReadLFH(pCsr);
+    }
+  }
+  return rc;
+}
+
+/*
+** "Standard" MS-DOS time format:
+**
+**   File modification time:
+**     Bits 00-04: seconds divided by 2
+**     Bits 05-10: minute
+**     Bits 11-15: hour
+**   File modification date:
+**     Bits 00-04: day
+**     Bits 05-08: month (1-12)
+**     Bits 09-15: years from 1980 
+*/
+static time_t zipfileMtime(ZipfileCsr *pCsr){
+  struct tm t;
+  memset(&t, 0, sizeof(t));
+  t.tm_sec = (pCsr->cds.mTime & 0x1F)*2;
+  t.tm_min = (pCsr->cds.mTime >> 5) & 0x2F;
+  t.tm_hour = (pCsr->cds.mTime >> 11) & 0x1F;
+
+  t.tm_mday = (pCsr->cds.mDate & 0x1F);
+  t.tm_mon = ((pCsr->cds.mDate >> 5) & 0x0F) - 1;
+  t.tm_year = 80 + ((pCsr->cds.mDate >> 9) & 0x7F);
+
+  return mktime(&t);
+}
+
+/*
+** Return values of columns for the row at which the series_cursor
+** is currently pointing.
+*/
+static int zipfileColumn(
+  sqlite3_vtab_cursor *cur,   /* The cursor */
+  sqlite3_context *ctx,       /* First argument to sqlite3_result_...() */
+  int i                       /* Which column to return */
+){
+  ZipfileCsr *pCsr = (ZipfileCsr*)cur;
+  int rc = SQLITE_OK;
+  switch( i ){
+    case 0:   /* name */
+      sqlite3_result_text(ctx, pCsr->cds.zFile, -1, SQLITE_TRANSIENT);
+      break;
+    case 1:   /* mode */
+      /* TODO: Whether or not the following is correct surely depends on
+      ** the platform on which the archive was created.  */
+      sqlite3_result_int(ctx, pCsr->cds.iExternalAttr >> 16);
+      break;
+    case 2: { /* mtime */
+      if( pCsr->flags & ZIPFILE_MTIME_VALID ){
+        sqlite3_result_int64(ctx, pCsr->mTime);
+      }else{
+        sqlite3_result_int64(ctx, zipfileMtime(pCsr));
+      }
+      break;
+    }
+    case 3: { /* sz */
+      sqlite3_result_int64(ctx, pCsr->cds.szUncompressed);
+      break;
+    }
+    case 4: { /* data */
+      int sz = pCsr->cds.szCompressed;
+      if( sz>0 ){
+        u8 *aBuf = sqlite3_malloc(sz);
+        if( aBuf==0 ){
+          rc = SQLITE_NOMEM;
+        }else{
+          rc = zipfileReadData(pCsr, aBuf, sz, pCsr->iDataOff);
+        }
+        if( rc==SQLITE_OK ){
+          sqlite3_result_blob(ctx, aBuf, sz, SQLITE_TRANSIENT);
+          sqlite3_free(aBuf);
+        }
+      }
+      break;
+    }
+    case 5:   /* method */
+      sqlite3_result_int(ctx, pCsr->cds.iCompression);
+      break;
+  }
+
+  return SQLITE_OK;
+}
+
+/*
+** Return the rowid for the current row. In this implementation, the
+** first row returned is assigned rowid value 1, and each subsequent
+** row a value 1 more than that of the previous.
+*/
+static int zipfileRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
+  ZipfileCsr *pCsr = (ZipfileCsr*)cur;
+  *pRowid = pCsr->iRowid;
+  return SQLITE_OK;
+}
+
+/*
+** Return TRUE if the cursor has been moved off of the last
+** row of output.
+*/
+static int zipfileEof(sqlite3_vtab_cursor *cur){
+  ZipfileCsr *pCsr = (ZipfileCsr*)cur;
+  return pCsr->bEof;
+}
+
+/*
+** The zip file has been successfully opened (so pCsr->pFile is valid). 
+** This function attempts to locate and read the End of central
+** directory record from the file.
+**
+*/
+static int zipfileReadEOCD(ZipfileCsr *pCsr, ZipfileEOCD *pEOCD){
+  u8 *aRead = pCsr->aBuffer;
+  int nRead = (int)(MIN(pCsr->nByte, ZIPFILE_BUFFER_SIZE));
+  i64 iOff = pCsr->nByte - nRead;
+
+  int rc = zipfileReadData(pCsr, aRead, nRead, iOff);
+  if( rc==SQLITE_OK ){
+    int i;
+
+    /* Scan backwards looking for the signature bytes */
+    for(i=nRead-20; i>=0; i--){
+      if( aRead[i]==0x50 && aRead[i+1]==0x4b 
+       && aRead[i+2]==0x05 && aRead[i+3]==0x06 
+      ){
+        break;
+      }
+    }
+    if( i<0 ){
+      zipfileSetErrmsg(pCsr, "cannot find end of central directory record");
+      return SQLITE_ERROR;
+    }
+
+    aRead += i+4;
+    pEOCD->iDisk = zipfileRead16(aRead);
+    pEOCD->iFirstDisk = zipfileRead16(aRead);
+    pEOCD->nEntry = zipfileRead16(aRead);
+    pEOCD->nEntryTotal = zipfileRead16(aRead);
+    pEOCD->nSize = zipfileRead32(aRead);
+    pEOCD->iOffset = zipfileRead32(aRead);
+
+#if 0
+    printf("iDisk=%d  iFirstDisk=%d  nEntry=%d  "
+           "nEntryTotal=%d  nSize=%d  iOffset=%d", 
+           (int)pEOCD->iDisk, (int)pEOCD->iFirstDisk, (int)pEOCD->nEntry,
+           (int)pEOCD->nEntryTotal, (int)pEOCD->nSize, (int)pEOCD->iOffset
+    );
+#endif
+  }
+
+  return SQLITE_OK;
+}
+
+/*
+** xFilter callback.
+*/
+static int zipfileFilter(
+  sqlite3_vtab_cursor *cur, 
+  int idxNum, const char *idxStr,
+  int argc, sqlite3_value **argv
+){
+  ZipfileCsr *pCsr = (ZipfileCsr*)cur;
+  const char *zFile;              /* Zip file to scan */
+  int rc = SQLITE_OK;             /* Return Code */
+
+  zipfileResetCursor(pCsr);
+
+  assert( idxNum==argc && (idxNum==0 || idxNum==1) );
+  if( idxNum==0 ){
+    /* Error. User did not supply a file name. */
+    zipfileSetErrmsg(pCsr, "table function zipfile() requires an argument");
+    return SQLITE_ERROR;
+  }
+
+  zFile = sqlite3_value_text(argv[0]);
+  pCsr->pFile = fopen(zFile, "rb");
+  if( pCsr->pFile==0 ){
+    zipfileSetErrmsg(pCsr, "cannot open file: %s", zFile);
+    rc = SQLITE_ERROR;
+  }else{
+    fseek(pCsr->pFile, 0, SEEK_END);
+    pCsr->nByte = (i64)ftell(pCsr->pFile);
+    rc = zipfileReadEOCD(pCsr, &pCsr->eocd);
+    if( rc==SQLITE_OK ){
+      pCsr->iNextOff = pCsr->eocd.iOffset;
+      rc = zipfileNext(cur);
+    }
+  }
+
+  return rc;
+}
+
+/*
+** xBestIndex callback.
+*/
+static int zipfileBestIndex(
+  sqlite3_vtab *tab,
+  sqlite3_index_info *pIdxInfo
+){
+  int i;
+
+  for(i=0; i<pIdxInfo->nConstraint; i++){
+    const struct sqlite3_index_constraint *pCons = &pIdxInfo->aConstraint[i];
+    if( pCons->usable==0 ) continue;
+    if( pCons->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
+    if( pCons->iColumn!=ZIPFILE_F_COLUMN_IDX ) continue;
+    break;
+  }
+
+  if( i<pIdxInfo->nConstraint ){
+    pIdxInfo->aConstraintUsage[i].argvIndex = 1;
+    pIdxInfo->aConstraintUsage[i].omit = 1;
+    pIdxInfo->estimatedCost = 1000.0;
+    pIdxInfo->idxNum = 1;
+  }else{
+    pIdxInfo->estimatedCost = (double)(((sqlite3_int64)1) << 50);
+    pIdxInfo->idxNum = 0;
+  }
+
+  return SQLITE_OK;
+}
+
+/*
+** Register the "zipfile" virtual table.
+*/
+static int zipfileRegister(sqlite3 *db){
+  static sqlite3_module zipfileModule = {
+    0,                         /* iVersion */
+    0,                         /* xCreate */
+    zipfileConnect,            /* xConnect */
+    zipfileBestIndex,          /* xBestIndex */
+    zipfileDisconnect,         /* xDisconnect */
+    0,                         /* xDestroy */
+    zipfileOpen,               /* xOpen - open a cursor */
+    zipfileClose,              /* xClose - close a cursor */
+    zipfileFilter,             /* xFilter - configure scan constraints */
+    zipfileNext,               /* xNext - advance a cursor */
+    zipfileEof,                /* xEof - check for end of scan */
+    zipfileColumn,             /* xColumn - read data */
+    zipfileRowid,              /* xRowid - read data */
+    0,                         /* xUpdate */
+    0,                         /* xBegin */
+    0,                         /* xSync */
+    0,                         /* xCommit */
+    0,                         /* xRollback */
+    0,                         /* xFindMethod */
+    0,                         /* xRename */
+  };
+
+  int rc = sqlite3_create_module(db, "zipfile"  , &zipfileModule, 0);
+  return rc;
+}
+#else         /* SQLITE_OMIT_VIRTUALTABLE */
+# define zipfileRegister(x) SQLITE_OK
+#endif
+
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+int sqlite3_zipfile_init(
+  sqlite3 *db, 
+  char **pzErrMsg, 
+  const sqlite3_api_routines *pApi
+){
+  int rc = SQLITE_OK;
+  SQLITE_EXTENSION_INIT2(pApi);
+  (void)pzErrMsg;  /* Unused parameter */
+  return zipfileRegister(db);
+}
+
diff --git a/main.mk b/main.mk
index ea0ff8bc2e9938423b1ab42e14fe3d23d51539bd..ff379da861310fd93f48dbf497bbcf93928cacb0 100644 (file)
--- a/main.mk
+++ b/main.mk
@@ -695,7 +695,8 @@ SHELL_SRC = \
        $(TOP)/ext/misc/completion.c \
        $(TOP)/ext/misc/sqlar.c \
        $(TOP)/ext/expert/sqlite3expert.c \
-       $(TOP)/ext/expert/sqlite3expert.h
+       $(TOP)/ext/expert/sqlite3expert.h \
+       $(TOP)/ext/misc/zipfile.c
 
 shell.c:       $(SHELL_SRC) $(TOP)/tool/mkshellc.tcl
        tclsh $(TOP)/tool/mkshellc.tcl >shell.c
index 0293660ba45d6b38d08149e12ea0647006ecf030..a788cbbd267b703a910f6e704928b0dff2428ee0 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Merge\senhancements\sfrom\strunk.
-D 2017-12-23T18:34:49.545
+C Add\snew\sfile\sext/misc/zipfile.c,\scontaining\sa\svirtual\stable\sfor\sread-only\naccess\sto\ssimple\szip\sarchives.
+D 2017-12-26T20:39:58.777
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F Makefile.in ceb40bfcb30ebba8e1202b34c56ff7e13e112f9809e2381d99be32c2726058f5
@@ -302,6 +302,7 @@ F ext/misc/vfsstat.c bf10ef0bc51e1ad6756629e1edb142f7a8db1178
 F ext/misc/vtablog.c 31d0d8f4406795679dcd3a67917c213d3a2a5fb3ea5de35f6e773491ed7e13c9
 F ext/misc/vtshim.c 1976e6dd68dd0d64508c91a6dfab8e75f8aaf6cd
 F ext/misc/wholenumber.c 784b12543d60702ebdd47da936e278aa03076212
+F ext/misc/zipfile.c 9736694a5eb029397e769f06517250be8b8e3836f4869246bfb60942a4047227
 F ext/rbu/rbu.c ea7d1b7eb44c123a2a619332e19fe5313500705c4a58aaa1887905c0d83ffc2e
 F ext/rbu/rbu1.test 43836fac8c7179a358eaf38a8a1ef3d6e6285842
 F ext/rbu/rbu10.test 1846519a438697f45e9dcb246908af81b551c29e1078d0304fae83f1fed7e9ee
@@ -404,7 +405,7 @@ F ext/userauth/userauth.c 3410be31283abba70255d71fd24734e017a4497f
 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
 F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
-F main.mk eef9a9918485b5df70d7a69ed3d0e1dd182bf714740122a144905a59d42da5c6
+F main.mk fc13303745f7a06e2eac69406ee0e7ba481f6df37a528ceb6ebacc03e310a020
 F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
 F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
 F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
@@ -482,7 +483,7 @@ F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
 F src/resolve.c bbee7e31d369a18a2f4836644769882e9c5d40ef4a3af911db06410b65cb3730
 F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac
 F src/select.c 8b22abe193e4d8243befa2038e4ae2405802fed1c446e5e502d11f652e09ba74
-F src/shell.c.in e739db2809b9ad38ac389a89bead6986542a679ca33b153aef5850856998b525
+F src/shell.c.in 1c927f9407fa4e58ed114577971525209ea12a293d25fe689d1973d9fef17f74
 F src/sqlite.h.in 2126192945019d4cdce335cb236b440a05ec75c93e4cd94c9c6d6e7fcc654cc4
 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
 F src/sqlite3ext.h c02d628cca67f3889c689d82d25c3eb45e2c155db08e4c6089b5840d64687d34
@@ -1691,7 +1692,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 7652b3c2374084047b6c1da3e525e0cac34fe220597f81e793bc4fd9f33358da 07c773148d8db185fa54991df09298b64f4fef28879e6c9395759265e8183977
-R 0cdaf396f246262d8fd00807c02fe094
-U drh
-Z d44ebee6c3c37877974b2b88daec7bfe
+P 150f07fec1e6d1fc0601820d717d8712fc513fe0d4bed67c8679eb51bca30d53
+R ab04bfd243997fb0b46867b0347c7799
+U dan
+Z 06eecfc1ea586319973b447240342e1d
index 3fe37939e4a20d91365a3169c96398f1f39a90c7..85c9e9cdf518fd9e390a99b6917430d5e091d10f 100644 (file)
@@ -1 +1 @@
-150f07fec1e6d1fc0601820d717d8712fc513fe0d4bed67c8679eb51bca30d53
\ No newline at end of file
+8e366b99b13d765d8bf000a7ec5919e582702e51dc07c27a746b6002898a2302
\ No newline at end of file
index 444df588995bcd0550426e0818ca11dab0ffbc91..b3ef375c0b62d5f4f532409de9c8f2f907b51bba 100644 (file)
@@ -797,6 +797,7 @@ INCLUDE ../ext/misc/shathree.c
 INCLUDE ../ext/misc/fileio.c
 INCLUDE ../ext/misc/completion.c
 #ifdef SQLITE_HAVE_ZLIB
+INCLUDE ../ext/misc/zipfile.c
 INCLUDE ../ext/misc/sqlar.c
 #endif
 INCLUDE ../ext/expert/sqlite3expert.h
@@ -3002,6 +3003,7 @@ static void open_db(ShellState *p, int keepAlive){
     sqlite3_shathree_init(p->db, 0, 0);
     sqlite3_completion_init(p->db, 0, 0);
 #ifdef SQLITE_HAVE_ZLIB
+    sqlite3_zipfile_init(p->db, 0, 0);
     sqlite3_sqlar_init(p->db, 0, 0);
 #endif
     sqlite3_create_function(p->db, "shell_add_schema", 2, SQLITE_UTF8, 0,