/ Check-in [2dec2dec]
Login

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

Overview
Comment:Update ext/misc/zipfile.c to support creating and adding entries to existing zip archives.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | sqlar-shell-support
Files: files | file ages | folders
SHA3-256:2dec2dec592c0726ebe87b841b9c8d493dea7074a99f278eb1bf0b744d658a9d
User & Date: dan 2017-12-29 20:19:03
Context
2017-12-30
14:26
Rearrange things a bit so that writing to a zipfile does not invert the order of objects it contains. check-in: f69e8194 user: dan tags: sqlar-shell-support
2017-12-29
20:19
Update ext/misc/zipfile.c to support creating and adding entries to existing zip archives. check-in: 2dec2dec user: dan tags: sqlar-shell-support
2017-12-27
21:13
Improve the shell tool ".ar --list --verbose" command. check-in: b64681a6 user: dan tags: sqlar-shell-support
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to ext/misc/zipfile.c.

    21     21   #include <sys/stat.h>
    22     22   #include <fcntl.h>
    23     23   #include <unistd.h>
    24     24   #include <dirent.h>
    25     25   #include <time.h>
    26     26   #include <utime.h>
    27     27   #include <errno.h>
           28  +
           29  +#include <zlib.h>
    28     30   
    29     31   #ifndef SQLITE_OMIT_VIRTUALTABLE
    30     32   
    31     33   #ifndef SQLITE_AMALGAMATION
    32     34   typedef sqlite3_int64 i64;
    33     35   typedef unsigned char u8;
    34     36   typedef unsigned short u16;
................................................................................
    43     45     "sz,        /* Size of object */"                                \
    44     46     "data,      /* Data stored in zip file (possibly compressed) */" \
    45     47     "method,    /* Compression method (integer) */"                  \
    46     48     "f HIDDEN   /* Name of zip file */"                              \
    47     49   ");"
    48     50   
    49     51   #define ZIPFILE_F_COLUMN_IDX 6    /* Index of column "f" in the above */
    50         -
    51     52   #define ZIPFILE_BUFFER_SIZE (64*1024)
    52     53   
    53     54   
    54         -#define ZIPFILE_EXTRA_TIMESTAMP 0x5455
           55  +/*
           56  +** Magic numbers used to read and write zip files.
           57  +**
           58  +** ZIPFILE_NEWENTRY_MADEBY:
           59  +**   Use this value for the "version-made-by" field in new zip file
           60  +**   entries. The upper byte indicates "unix", and the lower byte 
           61  +**   indicates that the zip file matches pkzip specification 3.0. 
           62  +**   This is what info-zip seems to do.
           63  +**
           64  +** ZIPFILE_NEWENTRY_REQUIRED:
           65  +**   Value for "version-required-to-extract" field of new entries.
           66  +**   Version 2.0 is required to support folders and deflate compression.
           67  +**
           68  +** ZIPFILE_NEWENTRY_FLAGS:
           69  +**   Value for "general-purpose-bit-flags" field of new entries. Bit
           70  +**   11 means "utf-8 filename and comment".
           71  +**
           72  +** ZIPFILE_SIGNATURE_CDS:
           73  +**   First 4 bytes of a valid CDS record.
           74  +**
           75  +** ZIPFILE_SIGNATURE_LFH:
           76  +**   First 4 bytes of a valid LFH record.
           77  +*/
           78  +#define ZIPFILE_EXTRA_TIMESTAMP   0x5455
           79  +#define ZIPFILE_NEWENTRY_MADEBY   ((3<<8) + 30)
           80  +#define ZIPFILE_NEWENTRY_REQUIRED 20
           81  +#define ZIPFILE_NEWENTRY_FLAGS    0x800
           82  +#define ZIPFILE_SIGNATURE_CDS     0x02014b50
           83  +#define ZIPFILE_SIGNATURE_LFH     0x04034b50
           84  +#define ZIPFILE_SIGNATURE_EOCD    0x06054b50
           85  +#define ZIPFILE_LFH_FIXED_SZ      30
    55     86   
    56     87   /*
    57     88   ** Set the error message contained in context ctx to the results of
    58     89   ** vprintf(zFmt, ...).
    59     90   */
    60     91   static void zipfileCtxErrorMsg(sqlite3_context *ctx, const char *zFmt, ...){
    61     92     char *zMsg = 0;
................................................................................
   184    215     i64 iNextOff;              /* Offset of next record in central directory */
   185    216     ZipfileEOCD eocd;          /* Parse of central directory record */
   186    217     ZipfileCDS cds;            /* Central Directory Structure */
   187    218     ZipfileLFH lfh;            /* Local File Header for current entry */
   188    219     i64 iDataOff;              /* Offset in zipfile to data */
   189    220     u32 mTime;                 /* Extended mtime value */
   190    221     int flags;
   191         -  u8 *aBuffer;               /* Buffer used for various tasks */
   192    222   };
   193    223   
          224  +/*
          225  +** Values for ZipfileCsr.flags.
          226  +*/
   194    227   #define ZIPFILE_MTIME_VALID 0x0001
          228  +
          229  +typedef struct ZipfileEntry ZipfileEntry;
          230  +struct ZipfileEntry {
          231  +  char *zPath;               /* Path of zipfile entry */
          232  +  u8 *aCdsEntry;             /* Buffer containing entire CDS entry */
          233  +  int nCdsEntry;             /* Size of buffer aCdsEntry[] in bytes */
          234  +  ZipfileEntry *pNext;
          235  +};
   195    236   
   196    237   typedef struct ZipfileTab ZipfileTab;
   197    238   struct ZipfileTab {
   198    239     sqlite3_vtab base;         /* Base class - must be first */
          240  +  char *zFile;               /* Zip file this table accesses (may be NULL) */
          241  +  u8 *aBuffer;               /* Temporary buffer used for various tasks */
          242  +
          243  +  /* The following are used by write transactions only */
          244  +  ZipfileEntry *pEntry;      /* Linked list of all files (if pWriteFd!=0) */
          245  +  FILE *pWriteFd;            /* File handle open on zip archive */
          246  +  i64 szCurrent;             /* Current size of zip archive */
          247  +  i64 szOrig;                /* Size of archive at start of transaction */
   199    248   };
          249  +
          250  +static void zipfileDequote(char *zIn){
          251  +  char q = zIn[0];
          252  +  if( q=='"' || q=='\'' || q=='`' || q=='[' ){
          253  +    char c;
          254  +    int iIn = 1;
          255  +    int iOut = 0;
          256  +    if( q=='[' ) q = ']';
          257  +    while( (c = zIn[iIn++]) ){
          258  +      if( c==q ){
          259  +        if( zIn[iIn++]!=q ) break;
          260  +      }
          261  +      zIn[iOut++] = c;
          262  +    }
          263  +    zIn[iOut] = '\0';
          264  +  }
          265  +}
   200    266   
   201    267   /*
   202    268   ** Construct a new ZipfileTab virtual table object.
          269  +** 
          270  +**   argv[0]   -> module name  ("zipfile")
          271  +**   argv[1]   -> database name
          272  +**   argv[2]   -> table name
          273  +**   argv[...] -> "column name" and other module argument fields.
   203    274   */
   204    275   static int zipfileConnect(
   205    276     sqlite3 *db,
   206    277     void *pAux,
   207    278     int argc, const char *const*argv,
   208    279     sqlite3_vtab **ppVtab,
   209    280     char **pzErr
   210    281   ){
          282  +  int nByte = sizeof(ZipfileTab) + ZIPFILE_BUFFER_SIZE;
          283  +  int nFile = 0;
          284  +  const char *zFile = 0;
   211    285     ZipfileTab *pNew = 0;
   212    286     int rc;
          287  +
          288  +  if( argc>3 ){
          289  +    zFile = argv[3];
          290  +    nFile = strlen(zFile)+1;
          291  +  }
   213    292   
   214    293     rc = sqlite3_declare_vtab(db, ZIPFILE_SCHEMA);
   215    294     if( rc==SQLITE_OK ){
   216         -    pNew = (ZipfileTab*)sqlite3_malloc( sizeof(*pNew) );
          295  +    pNew = (ZipfileTab*)sqlite3_malloc(nByte+nFile);
   217    296       if( pNew==0 ) return SQLITE_NOMEM;
   218         -    memset(pNew, 0, sizeof(*pNew));
          297  +    memset(pNew, 0, nByte+nFile);
          298  +    pNew->aBuffer = (u8*)&pNew[1];
          299  +    if( zFile ){
          300  +      pNew->zFile = (char*)&pNew->aBuffer[ZIPFILE_BUFFER_SIZE];
          301  +      memcpy(pNew->zFile, zFile, nFile);
          302  +      zipfileDequote(pNew->zFile);
          303  +    }
   219    304     }
   220    305     *ppVtab = (sqlite3_vtab*)pNew;
   221    306     return rc;
   222    307   }
   223    308   
   224    309   /*
   225    310   ** This method is the destructor for zipfile vtab objects.
................................................................................
   230    315   }
   231    316   
   232    317   /*
   233    318   ** Constructor for a new ZipfileCsr object.
   234    319   */
   235    320   static int zipfileOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCsr){
   236    321     ZipfileCsr *pCsr;
   237         -  pCsr = sqlite3_malloc( sizeof(*pCsr) + ZIPFILE_BUFFER_SIZE);
   238         -  if( pCsr==0 ) return SQLITE_NOMEM;
          322  +  pCsr = sqlite3_malloc(sizeof(*pCsr));
          323  +  *ppCsr = (sqlite3_vtab_cursor*)pCsr;
          324  +  if( pCsr==0 ){
          325  +    return SQLITE_NOMEM;
          326  +  }
   239    327     memset(pCsr, 0, sizeof(*pCsr));
   240         -  pCsr->aBuffer = (u8*)&pCsr[1];
   241         -  *ppCsr = &pCsr->base;
   242    328     return SQLITE_OK;
   243    329   }
   244    330   
   245    331   /*
   246    332   ** Reset a cursor back to the state it was in when first returned
   247    333   ** by zipfileOpen().
   248    334   */
   249    335   static void zipfileResetCursor(ZipfileCsr *pCsr){
          336  +  sqlite3_free(pCsr->cds.zFile);
          337  +  pCsr->cds.zFile = 0;
   250    338     pCsr->iRowid = 0;
   251    339     pCsr->bEof = 0;
   252    340     if( pCsr->pFile ){
   253    341       fclose(pCsr->pFile);
   254    342       pCsr->pFile = 0;
   255    343     }
   256    344   }
................................................................................
   272    360   static void zipfileSetErrmsg(ZipfileCsr *pCsr, const char *zFmt, ...){
   273    361     va_list ap;
   274    362     va_start(ap, zFmt);
   275    363     pCsr->base.pVtab->zErrMsg = sqlite3_vmprintf(zFmt, ap);
   276    364     va_end(ap);
   277    365   }
   278    366   
   279         -static int zipfileReadData(ZipfileCsr *pCsr, u8 *aRead, int nRead, i64 iOff){
          367  +static int zipfileReadData(
          368  +  FILE *pFile,                    /* Read from this file */
          369  +  u8 *aRead,                      /* Read into this buffer */
          370  +  int nRead,                      /* Number of bytes to read */
          371  +  i64 iOff,                       /* Offset to read from */
          372  +  char **pzErrmsg                 /* OUT: Error message (from sqlite3_malloc) */
          373  +){
   280    374     size_t n;
   281         -  fseek(pCsr->pFile, iOff, SEEK_SET);
   282         -  n = fread(aRead, 1, nRead, pCsr->pFile);
          375  +  fseek(pFile, iOff, SEEK_SET);
          376  +  n = fread(aRead, 1, nRead, pFile);
   283    377     if( n!=nRead ){
   284         -    zipfileSetErrmsg(pCsr, "error in fread()");
          378  +    *pzErrmsg = sqlite3_mprintf("error in fread()");
          379  +    return SQLITE_ERROR;
          380  +  }
          381  +  return SQLITE_OK;
          382  +}
          383  +
          384  +static int zipfileAppendData(
          385  +  ZipfileTab *pTab,
          386  +  const u8 *aWrite,
          387  +  int nWrite
          388  +){
          389  +  size_t n;
          390  +  fseek(pTab->pWriteFd, pTab->szCurrent, SEEK_SET);
          391  +  n = fwrite(aWrite, 1, nWrite, pTab->pWriteFd);
          392  +  if( n!=nWrite ){
          393  +    pTab->base.zErrMsg = sqlite3_mprintf("error in fwrite()");
   285    394       return SQLITE_ERROR;
   286    395     }
          396  +  pTab->szCurrent += nWrite;
   287    397     return SQLITE_OK;
   288    398   }
   289    399   
   290    400   static u16 zipfileGetU16(const u8 *aBuf){
   291    401     return (aBuf[1] << 8) + aBuf[0];
   292    402   }
   293    403   static u32 zipfileGetU32(const u8 *aBuf){
   294    404     return ((u32)(aBuf[3]) << 24)
   295    405          + ((u32)(aBuf[2]) << 16)
   296    406          + ((u32)(aBuf[1]) <<  8)
   297    407          + ((u32)(aBuf[0]) <<  0);
   298    408   }
          409  +
          410  +static void zipfilePutU16(u8 *aBuf, u16 val){
          411  +  aBuf[0] = val & 0xFF;
          412  +  aBuf[1] = (val>>8) & 0xFF;
          413  +}
          414  +static void zipfilePutU32(u8 *aBuf, u32 val){
          415  +  aBuf[0] = val & 0xFF;
          416  +  aBuf[1] = (val>>8) & 0xFF;
          417  +  aBuf[2] = (val>>16) & 0xFF;
          418  +  aBuf[3] = (val>>24) & 0xFF;
          419  +}
   299    420   
   300    421   #define zipfileRead32(aBuf) ( aBuf+=4, zipfileGetU32(aBuf-4) )
   301    422   #define zipfileRead16(aBuf) ( aBuf+=2, zipfileGetU16(aBuf-2) )
          423  +
          424  +#define zipfileWrite32(aBuf,val) { zipfilePutU32(aBuf,val); aBuf+=4; }
          425  +#define zipfileWrite16(aBuf,val) { zipfilePutU16(aBuf,val); aBuf+=2; }
          426  +
          427  +static u8* zipfileCsrBuffer(ZipfileCsr *pCsr){
          428  +  return ((ZipfileTab*)(pCsr->base.pVtab))->aBuffer;
          429  +}
          430  +
          431  +/*
          432  +** Magic numbers used to read CDS records.
          433  +*/
          434  +#define ZIPFILE_CDS_FIXED_SZ  46
          435  +#define ZIPFILE_CDS_NFILE_OFF 28
   302    436   
   303    437   static int zipfileReadCDS(ZipfileCsr *pCsr){
   304         -  static const int szFix = 46;    /* Size of fixed-size part of CDS */
   305         -  u8 *aRead = pCsr->aBuffer;
          438  +  char **pzErr = &pCsr->base.pVtab->zErrMsg;
          439  +  u8 *aRead = zipfileCsrBuffer(pCsr);
   306    440     int rc;
   307    441   
   308         -  rc = zipfileReadData(pCsr, aRead, szFix, pCsr->iNextOff);
          442  +  sqlite3_free(pCsr->cds.zFile);
          443  +  pCsr->cds.zFile = 0;
          444  +
          445  +  rc = zipfileReadData(
          446  +      pCsr->pFile, aRead, ZIPFILE_CDS_FIXED_SZ, pCsr->iNextOff, pzErr
          447  +  );
   309    448     if( rc==SQLITE_OK ){
   310    449       u32 sig = zipfileRead32(aRead);
   311         -    if( sig!=0x02014b50 ){
          450  +    if( sig!=ZIPFILE_SIGNATURE_CDS ){
   312    451         zipfileSetErrmsg(pCsr,"failed to read CDS at offset %lld",pCsr->iNextOff);
   313    452         rc = SQLITE_ERROR;
   314    453       }else{
   315    454         int nRead;
   316    455         pCsr->cds.iVersionMadeBy = zipfileRead16(aRead);
   317    456         pCsr->cds.iVersionExtract = zipfileRead16(aRead);
   318    457         pCsr->cds.flags = zipfileRead16(aRead);
   319    458         pCsr->cds.iCompression = zipfileRead16(aRead);
   320    459         pCsr->cds.mTime = zipfileRead16(aRead);
   321    460         pCsr->cds.mDate = zipfileRead16(aRead);
   322    461         pCsr->cds.crc32 = zipfileRead32(aRead);
   323    462         pCsr->cds.szCompressed = zipfileRead32(aRead);
   324    463         pCsr->cds.szUncompressed = zipfileRead32(aRead);
          464  +      assert( aRead==zipfileCsrBuffer(pCsr)+ZIPFILE_CDS_NFILE_OFF );
   325    465         pCsr->cds.nFile = zipfileRead16(aRead);
   326    466         pCsr->cds.nExtra = zipfileRead16(aRead);
   327    467         pCsr->cds.nComment = zipfileRead16(aRead);
   328    468         pCsr->cds.iDiskStart = zipfileRead16(aRead);
   329    469         pCsr->cds.iInternalAttr = zipfileRead16(aRead);
   330    470         pCsr->cds.iExternalAttr = zipfileRead32(aRead);
   331    471         pCsr->cds.iOffset = zipfileRead32(aRead);
   332    472   
   333         -      assert( aRead==&pCsr->aBuffer[szFix] );
          473  +      assert( aRead==zipfileCsrBuffer(pCsr)+ZIPFILE_CDS_FIXED_SZ );
   334    474   
   335    475         nRead = pCsr->cds.nFile + pCsr->cds.nExtra;
   336         -      aRead = pCsr->aBuffer;
   337         -      rc = zipfileReadData(pCsr, aRead, nRead, pCsr->iNextOff+szFix);
          476  +      aRead = zipfileCsrBuffer(pCsr);
          477  +      pCsr->iNextOff += ZIPFILE_CDS_FIXED_SZ;
          478  +      rc = zipfileReadData(pCsr->pFile, aRead, nRead, pCsr->iNextOff, pzErr);
   338    479   
   339    480         if( rc==SQLITE_OK ){
   340    481           pCsr->cds.zFile = sqlite3_mprintf("%.*s", (int)pCsr->cds.nFile, aRead);
   341         -        pCsr->iNextOff += szFix;
   342    482           pCsr->iNextOff += pCsr->cds.nFile;
   343    483           pCsr->iNextOff += pCsr->cds.nExtra;
   344    484           pCsr->iNextOff += pCsr->cds.nComment;
   345    485         }
   346    486   
   347    487         /* Scan the cds.nExtra bytes of "extra" fields for any that can
   348    488         ** be interpreted. The general format of an extra field is:
................................................................................
   377    517       }
   378    518     }
   379    519   
   380    520     return rc;
   381    521   }
   382    522   
   383    523   static int zipfileReadLFH(ZipfileCsr *pCsr){
   384         -  static const int szFix = 30;    /* Size of fixed-size part of LFH */
   385         -  u8 *aRead = pCsr->aBuffer;
          524  +  char **pzErr = &pCsr->base.pVtab->zErrMsg;
          525  +  static const int szFix = ZIPFILE_LFH_FIXED_SZ;
          526  +  u8 *aRead = zipfileCsrBuffer(pCsr);
   386    527     int rc;
   387    528   
   388         -  rc = zipfileReadData(pCsr, aRead, szFix, pCsr->cds.iOffset);
          529  +  rc = zipfileReadData(pCsr->pFile, aRead, szFix, pCsr->cds.iOffset, pzErr);
   389    530     if( rc==SQLITE_OK ){
   390    531       u32 sig = zipfileRead32(aRead);
   391         -    if( sig!=0x04034b50 ){
          532  +    if( sig!=ZIPFILE_SIGNATURE_LFH ){
   392    533         zipfileSetErrmsg(pCsr, "failed to read LFH at offset %d", 
   393    534             (int)pCsr->cds.iOffset
   394    535         );
   395    536         rc = SQLITE_ERROR;
   396    537       }else{
   397    538         pCsr->lfh.iVersionExtract = zipfileRead16(aRead);
   398    539         pCsr->lfh.flags = zipfileRead16(aRead);
................................................................................
   400    541         pCsr->lfh.mTime = zipfileRead16(aRead);
   401    542         pCsr->lfh.mDate = zipfileRead16(aRead);
   402    543         pCsr->lfh.crc32 = zipfileRead32(aRead);
   403    544         pCsr->lfh.szCompressed = zipfileRead32(aRead);
   404    545         pCsr->lfh.szUncompressed = zipfileRead32(aRead);
   405    546         pCsr->lfh.nFile = zipfileRead16(aRead);
   406    547         pCsr->lfh.nExtra = zipfileRead16(aRead);
   407         -      assert( aRead==&pCsr->aBuffer[szFix] );
          548  +      assert( aRead==zipfileCsrBuffer(pCsr)+szFix );
   408    549         pCsr->iDataOff = pCsr->cds.iOffset+szFix+pCsr->lfh.nFile+pCsr->lfh.nExtra;
   409    550       }
   410    551     }
   411    552   
   412    553     return rc;
   413    554   }
   414    555   
................................................................................
   455    596   
   456    597     t.tm_mday = (pCsr->cds.mDate & 0x1F);
   457    598     t.tm_mon = ((pCsr->cds.mDate >> 5) & 0x0F) - 1;
   458    599     t.tm_year = 80 + ((pCsr->cds.mDate >> 9) & 0x7F);
   459    600   
   460    601     return mktime(&t);
   461    602   }
          603  +
          604  +static void zipfileMtimeToDos(ZipfileCDS *pCds, u32 mTime){
          605  +  time_t t = (time_t)mTime;
          606  +  struct tm res;
          607  +  localtime_r(&t, &res);
          608  +
          609  +  pCds->mTime = 
          610  +    (res.tm_sec / 2) + 
          611  +    (res.tm_min << 5) +
          612  +    (res.tm_hour << 11);
          613  +
          614  +  pCds->mDate = 
          615  +    (res.tm_mday-1) +
          616  +    ((res.tm_mon+1) << 5) +
          617  +    ((res.tm_year-80) << 9);
          618  +}
   462    619   
   463    620   /*
   464    621   ** Return values of columns for the row at which the series_cursor
   465    622   ** is currently pointing.
   466    623   */
   467    624   static int zipfileColumn(
   468    625     sqlite3_vtab_cursor *cur,   /* The cursor */
................................................................................
   495    652       case 4: { /* data */
   496    653         int sz = pCsr->cds.szCompressed;
   497    654         if( sz>0 ){
   498    655           u8 *aBuf = sqlite3_malloc(sz);
   499    656           if( aBuf==0 ){
   500    657             rc = SQLITE_NOMEM;
   501    658           }else{
   502         -          rc = zipfileReadData(pCsr, aBuf, sz, pCsr->iDataOff);
          659  +          rc = zipfileReadData(pCsr->pFile, aBuf, sz, pCsr->iDataOff,
          660  +              &pCsr->base.pVtab->zErrMsg
          661  +          );
   503    662           }
   504    663           if( rc==SQLITE_OK ){
   505    664             sqlite3_result_blob(ctx, aBuf, sz, SQLITE_TRANSIENT);
   506    665             sqlite3_free(aBuf);
   507    666           }
   508    667         }
   509    668         break;
................................................................................
   533    692   */
   534    693   static int zipfileEof(sqlite3_vtab_cursor *cur){
   535    694     ZipfileCsr *pCsr = (ZipfileCsr*)cur;
   536    695     return pCsr->bEof;
   537    696   }
   538    697   
   539    698   /*
   540         -** The zip file has been successfully opened (so pCsr->pFile is valid). 
   541         -** This function attempts to locate and read the End of central
   542         -** directory record from the file.
   543         -**
   544    699   */
   545         -static int zipfileReadEOCD(ZipfileCsr *pCsr, ZipfileEOCD *pEOCD){
   546         -  u8 *aRead = pCsr->aBuffer;
   547         -  int nRead = (int)(MIN(pCsr->nByte, ZIPFILE_BUFFER_SIZE));
   548         -  i64 iOff = pCsr->nByte - nRead;
          700  +static int zipfileReadEOCD(
          701  +  ZipfileTab *pTab,               /* Return errors here */
          702  +  FILE *pFile,                    /* Read from this file */
          703  +  ZipfileEOCD *pEOCD              /* Object to populate */
          704  +){
          705  +  u8 *aRead = pTab->aBuffer;      /* Temporary buffer */
          706  +  i64 szFile;                     /* Total size of file in bytes */
          707  +  int nRead;                      /* Bytes to read from file */
          708  +  i64 iOff;                       /* Offset to read from */
   549    709   
   550         -  int rc = zipfileReadData(pCsr, aRead, nRead, iOff);
          710  +  fseek(pFile, 0, SEEK_END);
          711  +  szFile = (i64)ftell(pFile);
          712  +  if( szFile==0 ){
          713  +    return SQLITE_EMPTY;
          714  +  }
          715  +  nRead = (int)(MIN(szFile, ZIPFILE_BUFFER_SIZE));
          716  +  iOff = szFile - nRead;
          717  +
          718  +  int rc = zipfileReadData(pFile, aRead, nRead, iOff, &pTab->base.zErrMsg);
   551    719     if( rc==SQLITE_OK ){
   552    720       int i;
   553    721   
   554    722       /* Scan backwards looking for the signature bytes */
   555    723       for(i=nRead-20; i>=0; i--){
   556    724         if( aRead[i]==0x50 && aRead[i+1]==0x4b 
   557    725          && aRead[i+2]==0x05 && aRead[i+3]==0x06 
   558    726         ){
   559    727           break;
   560    728         }
   561    729       }
   562    730       if( i<0 ){
   563         -      zipfileSetErrmsg(pCsr, "cannot find end of central directory record");
          731  +      pTab->base.zErrMsg = sqlite3_mprintf(
          732  +          "cannot find end of central directory record"
          733  +      );
   564    734         return SQLITE_ERROR;
   565    735       }
   566    736   
   567    737       aRead += i+4;
   568    738       pEOCD->iDisk = zipfileRead16(aRead);
   569    739       pEOCD->iFirstDisk = zipfileRead16(aRead);
   570    740       pEOCD->nEntry = zipfileRead16(aRead);
................................................................................
   588    758   ** xFilter callback.
   589    759   */
   590    760   static int zipfileFilter(
   591    761     sqlite3_vtab_cursor *cur, 
   592    762     int idxNum, const char *idxStr,
   593    763     int argc, sqlite3_value **argv
   594    764   ){
          765  +  ZipfileTab *pTab = (ZipfileTab*)cur->pVtab;
   595    766     ZipfileCsr *pCsr = (ZipfileCsr*)cur;
   596    767     const char *zFile;              /* Zip file to scan */
   597    768     int rc = SQLITE_OK;             /* Return Code */
   598    769   
   599    770     zipfileResetCursor(pCsr);
   600    771   
   601    772     assert( idxNum==argc && (idxNum==0 || idxNum==1) );
   602    773     if( idxNum==0 ){
   603         -    /* Error. User did not supply a file name. */
   604         -    zipfileSetErrmsg(pCsr, "table function zipfile() requires an argument");
   605         -    return SQLITE_ERROR;
          774  +    ZipfileTab *pTab = (ZipfileTab*)cur->pVtab;
          775  +    zFile = pTab->zFile;
          776  +    if( zFile==0 ){
          777  +      /* Error. This is an eponymous virtual table and the user has not 
          778  +      ** supplied a file name. */
          779  +      zipfileSetErrmsg(pCsr, "table function zipfile() requires an argument");
          780  +      return SQLITE_ERROR;
          781  +    }
          782  +  }else{
          783  +    zFile = (const char*)sqlite3_value_text(argv[0]);
   606    784     }
   607         -
   608         -  zFile = sqlite3_value_text(argv[0]);
   609    785     pCsr->pFile = fopen(zFile, "rb");
   610    786     if( pCsr->pFile==0 ){
   611    787       zipfileSetErrmsg(pCsr, "cannot open file: %s", zFile);
   612    788       rc = SQLITE_ERROR;
   613    789     }else{
   614         -    fseek(pCsr->pFile, 0, SEEK_END);
   615         -    pCsr->nByte = (i64)ftell(pCsr->pFile);
   616         -    rc = zipfileReadEOCD(pCsr, &pCsr->eocd);
          790  +    rc = zipfileReadEOCD(pTab, pCsr->pFile, &pCsr->eocd);
   617    791       if( rc==SQLITE_OK ){
   618    792         pCsr->iNextOff = pCsr->eocd.iOffset;
   619    793         rc = zipfileNext(cur);
          794  +    }else if( rc==SQLITE_EMPTY ){
          795  +      rc = SQLITE_OK;
          796  +      pCsr->bEof = 1;
   620    797       }
   621    798     }
   622    799   
   623    800     return rc;
   624    801   }
   625    802   
   626    803   /*
................................................................................
   648    825     }else{
   649    826       pIdxInfo->estimatedCost = (double)(((sqlite3_int64)1) << 50);
   650    827       pIdxInfo->idxNum = 0;
   651    828     }
   652    829   
   653    830     return SQLITE_OK;
   654    831   }
          832  +
          833  +static int zipfileLoadDirectory(ZipfileTab *pTab){
          834  +  ZipfileEOCD eocd;
          835  +  int rc;
          836  +
          837  +  rc = zipfileReadEOCD(pTab, pTab->pWriteFd, &eocd);
          838  +  if( rc==SQLITE_OK ){
          839  +    int i;
          840  +    int iOff = 0;
          841  +    u8 *aBuf = sqlite3_malloc(eocd.nSize);
          842  +    if( aBuf==0 ){
          843  +      rc = SQLITE_NOMEM;
          844  +    }else{
          845  +      rc = zipfileReadData(
          846  +          pTab->pWriteFd, aBuf, eocd.nSize, eocd.iOffset, &pTab->base.zErrMsg
          847  +      );
          848  +    }
          849  +
          850  +    for(i=0; rc==SQLITE_OK && i<eocd.nEntry; i++){
          851  +      u16 nFile;
          852  +      u16 nExtra;
          853  +      u16 nComment;
          854  +      ZipfileEntry *pNew;
          855  +      u8 *aRec = &aBuf[iOff];
          856  +
          857  +      nFile = zipfileGetU16(&aRec[ZIPFILE_CDS_NFILE_OFF]);
          858  +      nExtra = zipfileGetU16(&aRec[ZIPFILE_CDS_NFILE_OFF+2]);
          859  +      nComment = zipfileGetU16(&aRec[ZIPFILE_CDS_NFILE_OFF+4]);
          860  +
          861  +      pNew = sqlite3_malloc(
          862  +          sizeof(ZipfileEntry) 
          863  +        + nFile+1 
          864  +        + ZIPFILE_CDS_FIXED_SZ+nFile+nExtra+nComment
          865  +      );
          866  +      if( pNew==0 ){
          867  +        rc = SQLITE_NOMEM;
          868  +      }else{
          869  +        pNew->zPath = (char*)&pNew[1];
          870  +        memcpy(pNew->zPath, &aBuf[ZIPFILE_CDS_FIXED_SZ], nFile);
          871  +        pNew->zPath[nFile] = '\0';
          872  +        pNew->aCdsEntry = (u8*)&pNew->zPath[nFile+1];
          873  +        pNew->nCdsEntry = ZIPFILE_CDS_FIXED_SZ+nFile+nExtra+nComment;
          874  +        memcpy(pNew->aCdsEntry, aRec, pNew->nCdsEntry);
          875  +
          876  +        pNew->pNext = pTab->pEntry;
          877  +        pTab->pEntry = pNew;
          878  +      }
          879  +
          880  +      iOff += ZIPFILE_CDS_FIXED_SZ+nFile+nExtra+nComment;
          881  +    }
          882  +
          883  +    sqlite3_free(aBuf);
          884  +  }else if( rc==SQLITE_EMPTY ){
          885  +    rc = SQLITE_OK;
          886  +  }
          887  +
          888  +  return rc;
          889  +}
          890  +
          891  +static ZipfileEntry *zipfileNewEntry(
          892  +  ZipfileCDS *pCds,               /* Values for fixed size part of CDS */
          893  +  const char *zPath,              /* Path for new entry */
          894  +  int nPath,                      /* strlen(zPath) */
          895  +  u32 mTime                       /* Modification time (or 0) */
          896  +){
          897  +  u8 *aWrite;
          898  +  ZipfileEntry *pNew;
          899  +  pCds->nFile = nPath;
          900  +  pCds->nExtra = mTime ? 9 : 0;
          901  +  pNew = (ZipfileEntry*)sqlite3_malloc(
          902  +    sizeof(ZipfileEntry) + 
          903  +    nPath+1 + 
          904  +    ZIPFILE_CDS_FIXED_SZ + nPath + pCds->nExtra
          905  +  );
          906  +
          907  +  if( pNew ){
          908  +    pNew->zPath = (char*)&pNew[1];
          909  +    pNew->aCdsEntry = (u8*)&pNew->zPath[nPath+1];
          910  +    pNew->nCdsEntry = ZIPFILE_CDS_FIXED_SZ + nPath + pCds->nExtra;
          911  +    memcpy(pNew->zPath, zPath, nPath+1);
          912  +
          913  +    aWrite = pNew->aCdsEntry;
          914  +    zipfileWrite32(aWrite, ZIPFILE_SIGNATURE_CDS);
          915  +    zipfileWrite16(aWrite, pCds->iVersionMadeBy);
          916  +    zipfileWrite16(aWrite, pCds->iVersionExtract);
          917  +    zipfileWrite16(aWrite, pCds->flags);
          918  +    zipfileWrite16(aWrite, pCds->iCompression);
          919  +    zipfileWrite16(aWrite, pCds->mTime);
          920  +    zipfileWrite16(aWrite, pCds->mDate);
          921  +    zipfileWrite32(aWrite, pCds->crc32);
          922  +    zipfileWrite32(aWrite, pCds->szCompressed);
          923  +    zipfileWrite32(aWrite, pCds->szUncompressed);
          924  +    zipfileWrite16(aWrite, pCds->nFile);
          925  +    zipfileWrite16(aWrite, pCds->nExtra);
          926  +    zipfileWrite16(aWrite, pCds->nComment);      assert( pCds->nComment==0 );
          927  +    zipfileWrite16(aWrite, pCds->iDiskStart);
          928  +    zipfileWrite16(aWrite, pCds->iInternalAttr);
          929  +    zipfileWrite32(aWrite, pCds->iExternalAttr);
          930  +    zipfileWrite32(aWrite, pCds->iOffset);
          931  +    assert( aWrite==&pNew->aCdsEntry[ZIPFILE_CDS_FIXED_SZ] );
          932  +    memcpy(aWrite, zPath, nPath);
          933  +    if( pCds->nExtra ){
          934  +      aWrite += nPath;
          935  +      zipfileWrite16(aWrite, ZIPFILE_EXTRA_TIMESTAMP);
          936  +      zipfileWrite16(aWrite, 5);
          937  +      *aWrite++ = 0x01;
          938  +      zipfileWrite32(aWrite, mTime);
          939  +    }
          940  +  }
          941  +
          942  +  return pNew;
          943  +}
          944  +
          945  +static int zipfileAppendEntry(
          946  +  ZipfileTab *pTab,
          947  +  ZipfileCDS *pCds,
          948  +  const char *zPath,              /* Path for new entry */
          949  +  int nPath,                      /* strlen(zPath) */
          950  +  const u8 *pData,
          951  +  int nData,
          952  +  u32 mTime
          953  +){
          954  +  u8 *aBuf = pTab->aBuffer;
          955  +  int rc;
          956  +
          957  +  zipfileWrite32(aBuf, ZIPFILE_SIGNATURE_LFH);
          958  +  zipfileWrite16(aBuf, pCds->iVersionExtract);
          959  +  zipfileWrite16(aBuf, pCds->flags);
          960  +  zipfileWrite16(aBuf, pCds->iCompression);
          961  +  zipfileWrite16(aBuf, pCds->mTime);
          962  +  zipfileWrite16(aBuf, pCds->mDate);
          963  +  zipfileWrite32(aBuf, pCds->crc32);
          964  +  zipfileWrite32(aBuf, pCds->szCompressed);
          965  +  zipfileWrite32(aBuf, pCds->szUncompressed);
          966  +  zipfileWrite16(aBuf, nPath);
          967  +  zipfileWrite16(aBuf, pCds->nExtra);
          968  +  assert( aBuf==&pTab->aBuffer[ZIPFILE_LFH_FIXED_SZ] );
          969  +  rc = zipfileAppendData(pTab, pTab->aBuffer, aBuf - pTab->aBuffer);
          970  +  if( rc==SQLITE_OK ){
          971  +    rc = zipfileAppendData(pTab, (const u8*)zPath, nPath);
          972  +  }
          973  +
          974  +  if( rc==SQLITE_OK && pCds->nExtra ){
          975  +    aBuf = pTab->aBuffer;
          976  +    zipfileWrite16(aBuf, ZIPFILE_EXTRA_TIMESTAMP);
          977  +    zipfileWrite16(aBuf, 5);
          978  +    *aBuf++ = 0x01;
          979  +    zipfileWrite32(aBuf, mTime);
          980  +    rc = zipfileAppendData(pTab, pTab->aBuffer, 9);
          981  +  }
          982  +
          983  +  if( rc==SQLITE_OK ){
          984  +    rc = zipfileAppendData(pTab, pData, nData);
          985  +  }
          986  +
          987  +  return rc;
          988  +}
          989  +
          990  +static int zipfileGetMode(ZipfileTab *pTab, sqlite3_value *pVal, int *pMode){
          991  +  const char *z = (const char*)sqlite3_value_text(pVal);
          992  +  int mode = 0;
          993  +  if( z==0 || (z[0]>=0 && z[0]<=9) ){
          994  +    mode = sqlite3_value_int(pVal);
          995  +  }else{
          996  +    const char zTemplate[10] = "-rwxrwxrwx";
          997  +    int i;
          998  +    if( strlen(z)!=10 ) goto parse_error;
          999  +    switch( z[0] ){
         1000  +      case '-': mode |= S_IFREG; break;
         1001  +      case 'd': mode |= S_IFDIR; break;
         1002  +      case 'l': mode |= S_IFLNK; break;
         1003  +      default: goto parse_error;
         1004  +    }
         1005  +    for(i=1; i<10; i++){
         1006  +      if( z[i]==zTemplate[i] ) mode |= 1 << (9-i);
         1007  +      else if( z[i]!='-' ) goto parse_error;
         1008  +    }
         1009  +  }
         1010  +  *pMode = mode;
         1011  +  return SQLITE_OK;
         1012  +
         1013  + parse_error:
         1014  +  pTab->base.zErrMsg = sqlite3_mprintf("zipfile: parse error in mode: %s", z);
         1015  +  return SQLITE_ERROR;
         1016  +}
         1017  +
         1018  +/*
         1019  +** xUpdate method.
         1020  +*/
         1021  +static int zipfileUpdate(
         1022  +  sqlite3_vtab *pVtab, 
         1023  +  int nVal, 
         1024  +  sqlite3_value **apVal, 
         1025  +  sqlite_int64 *pRowid
         1026  +){
         1027  +  ZipfileTab *pTab = (ZipfileTab*)pVtab;
         1028  +  int rc = SQLITE_OK;             /* Return Code */
         1029  +  ZipfileEntry *pNew = 0;         /* New in-memory CDS entry */
         1030  +
         1031  +  int mode;                       /* Mode for new entry */
         1032  +  i64 mTime;                      /* Modification time for new entry */
         1033  +  i64 sz;                         /* Uncompressed size */
         1034  +  const char *zPath;              /* Path for new entry */
         1035  +  int nPath;                      /* strlen(zPath) */
         1036  +  const u8 *pData;                /* Pointer to buffer containing content */
         1037  +  int nData;                      /* Size of pData buffer in bytes */
         1038  +  int iMethod = 0;                /* Compression method for new entry */
         1039  +  u8 *pFree = 0;                  /* Free this */
         1040  +  ZipfileCDS cds;                 /* New Central Directory Structure entry */
         1041  +
         1042  +  if( sqlite3_value_type(apVal[0])!=SQLITE_NULL ){
         1043  +    pTab->base.zErrMsg = sqlite3_mprintf(
         1044  +        "zipfile: UPDATE/DELETE not yet supported"
         1045  +    );
         1046  +    return SQLITE_ERROR;
         1047  +  }
         1048  +
         1049  +  /* This table is only writable if a default archive path was specified 
         1050  +  ** as part of the CREATE VIRTUAL TABLE statement. */
         1051  +  if( pTab->zFile==0 ){
         1052  +    pTab->base.zErrMsg = sqlite3_mprintf(
         1053  +        "zipfile: writing requires a default archive"
         1054  +    );
         1055  +    return SQLITE_ERROR;
         1056  +  }
         1057  +
         1058  +  /* Open a write fd on the file. Also load the entire central directory
         1059  +  ** structure into memory. During the transaction any new file data is 
         1060  +  ** appended to the archive file, but the central directory is accumulated
         1061  +  ** in main-memory until the transaction is committed.  */
         1062  +  if( pTab->pWriteFd==0 ){
         1063  +    pTab->pWriteFd = fopen(pTab->zFile, "ab+");
         1064  +    if( pTab->pWriteFd==0 ){
         1065  +      pTab->base.zErrMsg = sqlite3_mprintf(
         1066  +          "zipfile: failed to open file %s for writing", pTab->zFile
         1067  +      );
         1068  +      return SQLITE_ERROR;
         1069  +    }
         1070  +    fseek(pTab->pWriteFd, 0, SEEK_END);
         1071  +    pTab->szCurrent = pTab->szOrig = (i64)ftell(pTab->pWriteFd);
         1072  +    rc = zipfileLoadDirectory(pTab);
         1073  +    if( rc!=SQLITE_OK ) return rc;
         1074  +  }
         1075  +
         1076  +  zPath = (const char*)sqlite3_value_text(apVal[2]);
         1077  +  nPath = strlen(zPath);
         1078  +  rc = zipfileGetMode(pTab, apVal[3], &mode);
         1079  +  if( rc!=SQLITE_OK ) return rc;
         1080  +  mTime = sqlite3_value_int64(apVal[4]);
         1081  +  sz = sqlite3_value_int(apVal[5]);
         1082  +  pData = sqlite3_value_blob(apVal[6]);
         1083  +  nData = sqlite3_value_bytes(apVal[6]);
         1084  +
         1085  +  /* If a NULL value is inserted into the 'method' column, do automatic
         1086  +  ** compression. */
         1087  +  if( nData>0 && sqlite3_value_type(apVal[7])==SQLITE_NULL ){
         1088  +    pFree = (u8*)sqlite3_malloc(nData);
         1089  +    if( pFree==0 ){
         1090  +      rc = SQLITE_NOMEM;
         1091  +    }else{
         1092  +      int res;
         1093  +      z_stream str;
         1094  +      memset(&str, 0, sizeof(str));
         1095  +      str.next_in = (z_const Bytef*)pData;
         1096  +      str.avail_in = nData;
         1097  +      str.next_out = pFree;
         1098  +      str.avail_out = nData;
         1099  +      deflateInit2(&str, 9, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY);
         1100  +      res = deflate(&str, Z_FINISH);
         1101  +      if( res==Z_STREAM_END ){
         1102  +        pData = pFree;
         1103  +        nData = str.total_out;
         1104  +        iMethod = 8;
         1105  +      }else if( res!=Z_OK ){
         1106  +        pTab->base.zErrMsg = sqlite3_mprintf("zipfile: deflate() error");
         1107  +        rc = SQLITE_ERROR;
         1108  +      }
         1109  +      deflateEnd(&str);
         1110  +    }
         1111  +  }else{
         1112  +    iMethod = sqlite3_value_int(apVal[7]);
         1113  +    if( iMethod<0 || iMethod>65535 ){
         1114  +      pTab->base.zErrMsg = sqlite3_mprintf(
         1115  +          "zipfile: invalid compression method: %d", iMethod
         1116  +      );
         1117  +      rc = SQLITE_ERROR;
         1118  +    }
         1119  +  }
         1120  +
         1121  +  if( rc==SQLITE_OK ){
         1122  +    /* Create the new CDS record. */
         1123  +    memset(&cds, 0, sizeof(cds));
         1124  +    cds.iVersionMadeBy = ZIPFILE_NEWENTRY_MADEBY;
         1125  +    cds.iVersionExtract = ZIPFILE_NEWENTRY_REQUIRED;
         1126  +    cds.flags = ZIPFILE_NEWENTRY_FLAGS;
         1127  +    cds.iCompression = iMethod;
         1128  +    zipfileMtimeToDos(&cds, (u32)mTime);
         1129  +    cds.crc32 = crc32(0, pData, nData);
         1130  +    cds.szCompressed = nData;
         1131  +    cds.szUncompressed = sz;
         1132  +    cds.iExternalAttr = (mode<<16);
         1133  +    cds.iOffset = pTab->szCurrent;
         1134  +    pNew = zipfileNewEntry(&cds, zPath, nPath, (u32)mTime);
         1135  +    if( pNew==0 ){
         1136  +      rc = SQLITE_NOMEM;
         1137  +    }else{
         1138  +      pNew->pNext = pTab->pEntry;
         1139  +      pTab->pEntry = pNew;
         1140  +    }
         1141  +  }
         1142  +
         1143  +  /* Append the new header+file to the archive */
         1144  +  if( rc==SQLITE_OK ){
         1145  +    rc = zipfileAppendEntry(pTab, &cds, zPath, nPath, pData, nData, (u32)mTime);
         1146  +  }
         1147  +
         1148  +  sqlite3_free(pFree);
         1149  +  return rc;
         1150  +}
         1151  +
         1152  +static int zipfileAppendEOCD(ZipfileTab *pTab, ZipfileEOCD *p){
         1153  +  u8 *aBuf = pTab->aBuffer;
         1154  +
         1155  +  zipfileWrite32(aBuf, ZIPFILE_SIGNATURE_EOCD);
         1156  +  zipfileWrite16(aBuf, p->iDisk);
         1157  +  zipfileWrite16(aBuf, p->iFirstDisk);
         1158  +  zipfileWrite16(aBuf, p->nEntry);
         1159  +  zipfileWrite16(aBuf, p->nEntryTotal);
         1160  +  zipfileWrite32(aBuf, p->nSize);
         1161  +  zipfileWrite32(aBuf, p->iOffset);
         1162  +  zipfileWrite16(aBuf, 0);        /* Size of trailing comment in bytes*/
         1163  +
         1164  +  assert( (aBuf-pTab->aBuffer)==22 );
         1165  +  return zipfileAppendData(pTab, pTab->aBuffer, aBuf - pTab->aBuffer);
         1166  +}
         1167  +
         1168  +static void zipfileCleanupTransaction(ZipfileTab *pTab){
         1169  +  ZipfileEntry *pEntry;
         1170  +  ZipfileEntry *pNext;
         1171  +
         1172  +  for(pEntry=pTab->pEntry; pEntry; pEntry=pNext){
         1173  +    pNext = pEntry->pNext;
         1174  +    sqlite3_free(pEntry);
         1175  +  }
         1176  +  pTab->pEntry = 0;
         1177  +  fclose(pTab->pWriteFd);
         1178  +  pTab->pWriteFd = 0;
         1179  +  pTab->szCurrent = 0;
         1180  +  pTab->szOrig = 0;
         1181  +}
         1182  +
         1183  +static int zipfileBegin(sqlite3_vtab *pVtab){
         1184  +  return SQLITE_OK;
         1185  +}
         1186  +
         1187  +static int zipfileCommit(sqlite3_vtab *pVtab){
         1188  +  ZipfileTab *pTab = (ZipfileTab*)pVtab;
         1189  +  int rc = SQLITE_OK;
         1190  +  if( pTab->pWriteFd ){
         1191  +    i64 iOffset = pTab->szCurrent;
         1192  +    ZipfileEntry *pEntry;
         1193  +    ZipfileEOCD eocd;
         1194  +    int nEntry = 0;
         1195  +
         1196  +    /* Write out all entries */
         1197  +    for(pEntry=pTab->pEntry; rc==SQLITE_OK && pEntry; pEntry=pEntry->pNext){
         1198  +      rc = zipfileAppendData(pTab, pEntry->aCdsEntry, pEntry->nCdsEntry);
         1199  +      nEntry++;
         1200  +    }
         1201  +
         1202  +    /* Write out the EOCD record */
         1203  +    eocd.iDisk = 0;
         1204  +    eocd.iFirstDisk = 0;
         1205  +    eocd.nEntry = nEntry;
         1206  +    eocd.nEntryTotal = nEntry;
         1207  +    eocd.nSize = pTab->szCurrent - iOffset;;
         1208  +    eocd.iOffset = iOffset;
         1209  +    rc = zipfileAppendEOCD(pTab, &eocd);
         1210  +
         1211  +    zipfileCleanupTransaction(pTab);
         1212  +  }
         1213  +  return rc;
         1214  +}
         1215  +
         1216  +static int zipfileRollback(sqlite3_vtab *pVtab){
         1217  +  return zipfileCommit(pVtab);
         1218  +}
   655   1219   
   656   1220   /*
   657   1221   ** Register the "zipfile" virtual table.
   658   1222   */
   659   1223   static int zipfileRegister(sqlite3 *db){
   660   1224     static sqlite3_module zipfileModule = {
   661         -    0,                         /* iVersion */
   662         -    0,                         /* xCreate */
         1225  +    1,                         /* iVersion */
         1226  +    zipfileConnect,            /* xCreate */
   663   1227       zipfileConnect,            /* xConnect */
   664   1228       zipfileBestIndex,          /* xBestIndex */
   665   1229       zipfileDisconnect,         /* xDisconnect */
   666         -    0,                         /* xDestroy */
         1230  +    zipfileDisconnect,         /* xDestroy */
   667   1231       zipfileOpen,               /* xOpen - open a cursor */
   668   1232       zipfileClose,              /* xClose - close a cursor */
   669   1233       zipfileFilter,             /* xFilter - configure scan constraints */
   670   1234       zipfileNext,               /* xNext - advance a cursor */
   671   1235       zipfileEof,                /* xEof - check for end of scan */
   672   1236       zipfileColumn,             /* xColumn - read data */
   673   1237       zipfileRowid,              /* xRowid - read data */
   674         -    0,                         /* xUpdate */
   675         -    0,                         /* xBegin */
         1238  +    zipfileUpdate,             /* xUpdate */
         1239  +    zipfileBegin,              /* xBegin */
   676   1240       0,                         /* xSync */
   677         -    0,                         /* xCommit */
   678         -    0,                         /* xRollback */
         1241  +    zipfileCommit,             /* xCommit */
         1242  +    zipfileRollback,           /* xRollback */
   679   1243       0,                         /* xFindMethod */
   680   1244       0,                         /* xRename */
   681   1245     };
   682   1246   
   683   1247     int rc = sqlite3_create_module(db, "zipfile"  , &zipfileModule, 0);
   684   1248     return rc;
   685   1249   }
   686   1250   #else         /* SQLITE_OMIT_VIRTUALTABLE */
   687   1251   # define zipfileRegister(x) SQLITE_OK
   688   1252   #endif
   689   1253   
   690         -#include <zlib.h>
   691         -
   692   1254   /*
   693   1255   ** zipfile_uncompress(DATA, SZ, METHOD)
   694   1256   */
   695   1257   static void zipfileUncompressFunc(
   696   1258     sqlite3_context *context,
   697   1259     int argc,
   698   1260     sqlite3_value **argv

Changes to main.mk.

   366    366     $(TOP)/ext/misc/remember.c \
   367    367     $(TOP)/ext/misc/series.c \
   368    368     $(TOP)/ext/misc/spellfix.c \
   369    369     $(TOP)/ext/misc/totype.c \
   370    370     $(TOP)/ext/misc/unionvtab.c \
   371    371     $(TOP)/ext/misc/wholenumber.c \
   372    372     $(TOP)/ext/misc/vfslog.c \
          373  +  $(TOP)/ext/misc/zipfile.c \
   373    374     $(TOP)/ext/fts5/fts5_tcl.c \
   374    375     $(TOP)/ext/fts5/fts5_test_mi.c \
   375    376     $(TOP)/ext/fts5/fts5_test_tok.c 
   376    377   
   377    378   
   378    379   #TESTSRC += $(TOP)/ext/fts2/fts2_tokenizer.c
   379    380   #TESTSRC += $(TOP)/ext/fts3/fts3_tokenizer.c

Changes to src/test1.c.

  6956   6956     extern int sqlite3_regexp_init(sqlite3*,char**,const sqlite3_api_routines*);
  6957   6957     extern int sqlite3_remember_init(sqlite3*,char**,const sqlite3_api_routines*);
  6958   6958     extern int sqlite3_series_init(sqlite3*,char**,const sqlite3_api_routines*);
  6959   6959     extern int sqlite3_spellfix_init(sqlite3*,char**,const sqlite3_api_routines*);
  6960   6960     extern int sqlite3_totype_init(sqlite3*,char**,const sqlite3_api_routines*);
  6961   6961     extern int sqlite3_wholenumber_init(sqlite3*,char**,const sqlite3_api_routines*);
  6962   6962     extern int sqlite3_unionvtab_init(sqlite3*,char**,const sqlite3_api_routines*);
         6963  +  extern int sqlite3_zipfile_init(sqlite3*,char**,const sqlite3_api_routines*);
  6963   6964     static const struct {
  6964   6965       const char *zExtName;
  6965   6966       int (*pInit)(sqlite3*,char**,const sqlite3_api_routines*);
  6966   6967     } aExtension[] = {
  6967   6968       { "amatch",                sqlite3_amatch_init               },
  6968   6969       { "carray",                sqlite3_carray_init               },
  6969   6970       { "closure",               sqlite3_closure_init              },
................................................................................
  6977   6978       { "regexp",                sqlite3_regexp_init               },
  6978   6979       { "remember",              sqlite3_remember_init             },
  6979   6980       { "series",                sqlite3_series_init               },
  6980   6981       { "spellfix",              sqlite3_spellfix_init             },
  6981   6982       { "totype",                sqlite3_totype_init               },
  6982   6983       { "unionvtab",             sqlite3_unionvtab_init            },
  6983   6984       { "wholenumber",           sqlite3_wholenumber_init          },
         6985  +    { "zipfile",               sqlite3_zipfile_init              },
  6984   6986     };
  6985   6987     sqlite3 *db;
  6986   6988     const char *zName;
  6987   6989     int i, j, rc;
  6988   6990     char *zErrMsg = 0;
  6989   6991     if( objc<3 ){
  6990   6992       Tcl_WrongNumArgs(interp, 1, objv, "DB NAME ...");

Added test/zipfile.test.

            1  +# 2017 December 9
            2  +#
            3  +# The author disclaims copyright to this source code.  In place of
            4  +# a legal notice, here is a blessing:
            5  +#
            6  +#    May you do good and not evil.
            7  +#    May you find forgiveness for yourself and forgive others.
            8  +#    May you share freely, never taking more than you give.
            9  +#
           10  +#***********************************************************************
           11  +#
           12  +
           13  +set testdir [file dirname $argv0]
           14  +source $testdir/tester.tcl
           15  +set testprefix zipfile
           16  +
           17  +load_static_extension db zipfile
           18  +
           19  +forcedelete test.zip
           20  +do_execsql_test 1.0 {
           21  +  CREATE VIRTUAL TABLE temp.zz USING zipfile('test.zip');
           22  +  PRAGMA table_info(zz);
           23  +} {
           24  +  0 name {} 0 {} 0 
           25  +  1 mode {} 0 {} 0 
           26  +  2 mtime {} 0 {} 0 
           27  +  3 sz {} 0 {} 0 
           28  +  4 data {} 0 {} 0
           29  +  5 method {} 0 {} 0
           30  +}
           31  +
           32  +do_execsql_test 1.1 {
           33  +  INSERT INTO zz VALUES('f.txt', '-rw-r--r--', 1000000000, 5, 'abcde', 0);
           34  +  INSERT INTO zz VALUES('g.txt', '-rw-r--r--', 1000000002, 5, '12345', 0);
           35  +}
           36  +
           37  +do_execsql_test 1.2 {
           38  +  SELECT name, mtime, data FROM zipfile('test.zip');
           39  +} {
           40  +  g.txt 1000000002 12345
           41  +  f.txt 1000000000 abcde 
           42  +}
           43  +
           44  +do_execsql_test 1.3 {
           45  +  INSERT INTO zz VALUES('h.txt', 
           46  +    '-rw-r--r--', 1000000004, 20, 'aaaaaaaaaabbbbbbbbbb', NULL
           47  +  );
           48  +}
           49  +
           50  +do_execsql_test 1.4 {
           51  +  SELECT name, mtime, zipfile_uncompress(data, sz, method), method
           52  +  FROM zipfile('test.zip');
           53  +} {
           54  +  h.txt 1000000004 aaaaaaaaaabbbbbbbbbb 8
           55  +  f.txt 1000000000 abcde 0
           56  +  g.txt 1000000002 12345 0
           57  +}
           58  +
           59  +finish_test
           60  +