/ Check-in [f2d2a5df]
Login
SQLite training in Houston TX on 2019-11-05 (details)
Part of the 2019 Tcl Conference

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

Overview
Comment:Support UPDATE statements against zipfile virtual tables.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: f2d2a5df4f29b47212fd2411eae6545087b901a270655640c87ceb472e02a24c
User & Date: dan 2018-01-13 19:08:24
Context
2018-01-13
23:28
Fix harmless compiler warnings in zipfile.c. check-in: 8f7a592f user: drh tags: trunk
19:08
Support UPDATE statements against zipfile virtual tables. check-in: f2d2a5df user: dan tags: trunk
14:28
Fully initialize the Mem object for serial-type 10, in case such a serial-type is found in a corrupt database file. check-in: bd70a07d user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Show Whitespace Changes Patch

Changes to ext/misc/zipfile.c.

463
464
465
466
467
468
469





































470
471
472
473
474
475
476
477
...
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522


523
524
525
526
527
528
529
...
566
567
568
569
570
571
572
573
574



575


576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602

603








604
605
606
607
608
609
610
...
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
...
766
767
768
769
770
771
772



773
774
775
776
777
778
779
...
985
986
987
988
989
990
991
992




993
994

995
996
997
998
999
1000
1001






1002
1003
1004
1005
1006
1007
1008
....
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
....
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
....
1227
1228
1229
1230
1231
1232
1233
1234

1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249

1250
1251
1252
1253

1254
1255
1256
1257







1258




















1259
1260
1261
1262
1263
1264
1265
....
1269
1270
1271
1272
1273
1274
1275


1276
1277
1278

1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
....
1301
1302
1303
1304
1305
1306
1307

1308
1309
1310
1311
1312
1313
1314
....
1340
1341
1342
1343
1344
1345
1346

1347
1348
1349
1350
1351
1352
1353
....
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379



1380
1381
1382
1383
1384
1385
1386

/*
** Magic numbers used to read CDS records.
*/
#define ZIPFILE_CDS_FIXED_SZ  46
#define ZIPFILE_CDS_NFILE_OFF 28






































static int zipfileReadCDS(ZipfileCsr *pCsr){
  char **pzErr = &pCsr->base.pVtab->zErrMsg;
  u8 *aRead;
  int rc = SQLITE_OK;

  sqlite3_free(pCsr->cds.zFile);
  pCsr->cds.zFile = 0;

................................................................................
        pCsr->pFile, aRead, ZIPFILE_CDS_FIXED_SZ, pCsr->iNextOff, pzErr
    );
  }else{
    aRead = pCsr->pCurrent->aCdsEntry;
  }

  if( rc==SQLITE_OK ){
    u32 sig = zipfileRead32(aRead);
    if( sig!=ZIPFILE_SIGNATURE_CDS ){
      assert( pCsr->pCurrent==0 );
      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);
      assert( pCsr->pCurrent 
           || aRead==zipfileCsrBuffer(pCsr)+ZIPFILE_CDS_NFILE_OFF 
      );
      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( pCsr->pCurrent 
           || aRead==zipfileCsrBuffer(pCsr)+ZIPFILE_CDS_FIXED_SZ
      );

      if( pCsr->pCurrent==0 ){
        nRead = pCsr->cds.nFile + pCsr->cds.nExtra;
        aRead = zipfileCsrBuffer(pCsr);
        pCsr->iNextOff += ZIPFILE_CDS_FIXED_SZ;
        rc = zipfileReadData(pCsr->pFile, aRead, nRead, pCsr->iNextOff, pzErr);


      }

      if( rc==SQLITE_OK ){
        pCsr->cds.zFile = sqlite3_mprintf("%.*s", (int)pCsr->cds.nFile, aRead);
        pCsr->iNextOff += pCsr->cds.nFile;
        pCsr->iNextOff += pCsr->cds.nExtra;
        pCsr->iNextOff += pCsr->cds.nComment;
................................................................................
}

static FILE *zipfileGetFd(ZipfileCsr *pCsr){
  if( pCsr->pFile ) return pCsr->pFile;
  return ((ZipfileTab*)(pCsr->base.pVtab))->pWriteFd;
}

static int zipfileReadLFH(ZipfileCsr *pCsr){
  FILE *pFile = zipfileGetFd(pCsr);



  char **pzErr = &pCsr->base.pVtab->zErrMsg;


  static const int szFix = ZIPFILE_LFH_FIXED_SZ;
  u8 *aRead = zipfileCsrBuffer(pCsr);
  int rc;

  rc = zipfileReadData(pFile, aRead, szFix, pCsr->cds.iOffset, pzErr);
  if( rc==SQLITE_OK ){
    u32 sig = zipfileRead32(aRead);
    if( sig!=ZIPFILE_SIGNATURE_LFH ){
      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==zipfileCsrBuffer(pCsr)+szFix );
      pCsr->iDataOff = pCsr->cds.iOffset+szFix+pCsr->lfh.nFile+pCsr->lfh.nExtra;
    }
  }










  return rc;
}


/*
** Advance an ZipfileCsr to its next row of output.
*/
................................................................................
    }while( pCsr->pCurrent && pCsr->pCurrent->bDeleted );
    if( pCsr->pCurrent==0 ){
      pCsr->bEof = 1;
    }
  }

  if( pCsr->bEof==0 ){
    rc = zipfileReadCDS(pCsr);
    if( rc==SQLITE_OK ){
      rc = zipfileReadLFH(pCsr);
    }
  }

  return rc;
}

/*
................................................................................
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.  */
................................................................................
  return SQLITE_OK;
}

/*
** Add object pNew to the end of the linked list that begins at
** ZipfileTab.pFirstEntry and ends with pLastEntry.
*/
static void zipfileAddEntry(ZipfileTab *pTab, ZipfileEntry *pNew){




  assert( (pTab->pFirstEntry==0)==(pTab->pLastEntry==0) );
  assert( pNew->pNext==0 );

  if( pTab->pFirstEntry==0 ){
    pTab->pFirstEntry = pTab->pLastEntry = pNew;
  }else{
    assert( pTab->pLastEntry->pNext==0 );
    pTab->pLastEntry->pNext = pNew;
    pTab->pLastEntry = pNew;
  }






}

static int zipfileLoadDirectory(ZipfileTab *pTab){
  ZipfileEOCD eocd;
  int rc;

  rc = zipfileReadEOCD(pTab, pTab->pWriteFd, &eocd);
................................................................................
        memset(pNew, 0, sizeof(ZipfileEntry));
        pNew->zPath = (char*)&pNew[1];
        memcpy(pNew->zPath, &aRec[ZIPFILE_CDS_FIXED_SZ], nFile);
        pNew->zPath[nFile] = '\0';
        pNew->aCdsEntry = (u8*)&pNew->zPath[nFile+1];
        pNew->nCdsEntry = ZIPFILE_CDS_FIXED_SZ+nFile+nExtra+nComment;
        memcpy(pNew->aCdsEntry, aRec, pNew->nCdsEntry);
        zipfileAddEntry(pTab, pNew);
      }

      iOff += ZIPFILE_CDS_FIXED_SZ+nFile+nExtra+nComment;
    }

    sqlite3_free(aBuf);
  }else if( rc==SQLITE_EMPTY ){
................................................................................
  u32 defaultMode,                /* Value to use if pVal IS NULL */
  u32 *pMode
){
  const char *z = (const char*)sqlite3_value_text(pVal);
  u32 mode = 0;
  if( z==0 ){
    mode = defaultMode;
  }else if( z[0]>=0 && z[0]<=9 ){
    mode = (unsigned int)sqlite3_value_int(pVal);
  }else{
    const char zTemplate[11] = "-rwxrwxrwx";
    int i;
    if( strlen(z)!=10 ) goto parse_error;
    switch( z[0] ){
      case '-': mode |= S_IFREG; break;
................................................................................
  int nPath = 0;                  /* strlen(zPath) */
  const u8 *pData = 0;            /* Pointer to buffer containing content */
  int nData = 0;                  /* Size of pData buffer in bytes */
  int iMethod = 0;                /* Compression method for new entry */
  u8 *pFree = 0;                  /* Free this */
  char *zFree = 0;                /* Also free this */
  ZipfileCDS cds;                 /* New Central Directory Structure entry */


  int bIsDir = 0;

  int mNull;

  assert( pTab->zFile );
  assert( pTab->pWriteFd );

  if( sqlite3_value_type(apVal[0])!=SQLITE_NULL ){
    if( nVal>1 ){
      return SQLITE_CONSTRAINT;
    }else{
      const char *zDelete = (const char*)sqlite3_value_text(apVal[0]);
      int nDelete = strlen(zDelete);
      ZipfileEntry *p;
      for(p=pTab->pFirstEntry; p; p=p->pNext){

        if( zipfileComparePath(p->zPath, zDelete, nDelete)==0 ){
          p->bDeleted = 1;
          break;
        }

      }
      return SQLITE_OK;
    }
  }




























  mNull = (sqlite3_value_type(apVal[5])==SQLITE_NULL ? 0x0 : 0x8)  /* sz */
        + (sqlite3_value_type(apVal[6])==SQLITE_NULL ? 0x0 : 0x4)  /* rawdata */
        + (sqlite3_value_type(apVal[7])==SQLITE_NULL ? 0x0 : 0x2)  /* data */
        + (sqlite3_value_type(apVal[8])==SQLITE_NULL ? 0x0 : 0x1); /* method */
  if( mNull==0x00 ){     
    /* All four are NULL - this must be a directory */
    bIsDir = 1;
................................................................................
    ** a regular file or a symlink. */
    const u8 *aIn = sqlite3_value_blob(apVal[7]);
    int nIn = sqlite3_value_bytes(apVal[7]);
    int bAuto = sqlite3_value_type(apVal[8])==SQLITE_NULL;
    
    iMethod = sqlite3_value_int(apVal[8]);
    sz = nIn;


    if( iMethod!=0 && iMethod!=8 ){
      rc = SQLITE_CONSTRAINT;
    }else if( bAuto || iMethod ){

      rc = zipfileDeflate(pTab, aIn, nIn, &pFree, &nData);
      if( rc==SQLITE_OK ){
        if( iMethod || nData<nIn ){
          iMethod = 8;
          pData = pFree;
        }else{
          pData = aIn;
          nData = nIn;
        }
      }
    }
  }
  else if( mNull==0x0D ){
    /* Values specified for "sz", "rawdata" and "method". In other words,
    ** pre-compressed data is being inserted.  */
................................................................................
      );
      rc = SQLITE_ERROR;
    }
  }
  else{
    rc = SQLITE_CONSTRAINT;
  }


  if( rc==SQLITE_OK ){
    rc = zipfileGetMode(pTab, apVal[3], 
        (bIsDir ? (S_IFDIR + 0755) : (S_IFREG + 0644)), &mode
    );
    if( rc==SQLITE_OK && (bIsDir == ((mode & S_IFDIR)==0)) ){
      /* The "mode" attribute is a directory, but data has been specified.
................................................................................
    }
  }

  /* Check that we're not inserting a duplicate entry */
  if( rc==SQLITE_OK ){
    ZipfileEntry *p;
    for(p=pTab->pFirstEntry; p; p=p->pNext){

      if( zipfileComparePath(p->zPath, zPath, nPath)==0 ){
        rc = SQLITE_CONSTRAINT;
        break;
      }
    }
  }

................................................................................
    cds.szUncompressed = (u32)sz;
    cds.iExternalAttr = (mode<<16);
    cds.iOffset = (u32)pTab->szCurrent;
    pNew = zipfileNewEntry(&cds, zPath, nPath, (u32)mTime);
    if( pNew==0 ){
      rc = SQLITE_NOMEM;
    }else{
      zipfileAddEntry(pTab, pNew);
    }
  }

  /* Append the new header+file to the archive */
  if( rc==SQLITE_OK ){
    rc = zipfileAppendEntry(pTab, &cds, zPath, nPath, pData, nData, (u32)mTime);
  }




  sqlite3_free(pFree);
  sqlite3_free(zFree);
  return rc;
}

static int zipfileAppendEOCD(ZipfileTab *pTab, ZipfileEOCD *p){
  u8 *aBuf = pTab->aBuffer;







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|







 







|
|


<


<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<





>
>







 







|
|
>
>
>
|
>
>

<


|



|
<
<


|
|
|
|
|
|
|
|
|
|
|
<


>
|
>
>
>
>
>
>
>
>







 







|

|







 







>
>
>







 







|
>
>
>
>


>







>
>
>
>
>
>







 







|







 







|







 







<
>


<
<




<
<
<
|
|
<
|
>
|
|
|
|
>
|
|
|
|
>
>
>
>
>
>
>

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>
>



>
|
|
|
|
|
<
<
|







 







>







 







>







 







|








>
>
>







463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
...
518
519
520
521
522
523
524
525
526
527
528

529
530























531
532
533
534
535
536
537
538
539
540
541
542
543
544
...
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596

597
598
599
600
601
602
603


604
605
606
607
608
609
610
611
612
613
614
615
616

617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
...
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
...
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
....
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
....
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
....
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
....
1266
1267
1268
1269
1270
1271
1272

1273
1274
1275


1276
1277
1278
1279



1280
1281

1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
....
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348


1349
1350
1351
1352
1353
1354
1355
1356
....
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
....
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
....
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454

/*
** Magic numbers used to read CDS records.
*/
#define ZIPFILE_CDS_FIXED_SZ         46
#define ZIPFILE_CDS_NFILE_OFF        28

/*
** Decode the CDS record in buffer aBuf into (*pCDS). Return SQLITE_ERROR
** if the record is not well-formed, or SQLITE_OK otherwise.
*/
static int zipfileReadCDS(u8 *aBuf, ZipfileCDS *pCDS){
  u8 *aRead = aBuf;
  u32 sig = zipfileRead32(aRead);
  int rc = SQLITE_OK;
  if( sig!=ZIPFILE_SIGNATURE_CDS ){
    rc = SQLITE_ERROR;
  }else{
    pCDS->iVersionMadeBy = zipfileRead16(aRead);
    pCDS->iVersionExtract = zipfileRead16(aRead);
    pCDS->flags = zipfileRead16(aRead);
    pCDS->iCompression = zipfileRead16(aRead);
    pCDS->mTime = zipfileRead16(aRead);
    pCDS->mDate = zipfileRead16(aRead);
    pCDS->crc32 = zipfileRead32(aRead);
    pCDS->szCompressed = zipfileRead32(aRead);
    pCDS->szUncompressed = zipfileRead32(aRead);
    assert( aRead==&aBuf[ZIPFILE_CDS_NFILE_OFF] );
    pCDS->nFile = zipfileRead16(aRead);
    pCDS->nExtra = zipfileRead16(aRead);
    pCDS->nComment = zipfileRead16(aRead);
    pCDS->iDiskStart = zipfileRead16(aRead);
    pCDS->iInternalAttr = zipfileRead16(aRead);
    pCDS->iExternalAttr = zipfileRead32(aRead);
    pCDS->iOffset = zipfileRead32(aRead);
    assert( aRead==&aBuf[ZIPFILE_CDS_FIXED_SZ] );
  }

  return rc;
}

/*
** Read the CDS record for the current entry from disk into pCsr->cds.
*/
static int zipfileCsrReadCDS(ZipfileCsr *pCsr){
  char **pzErr = &pCsr->base.pVtab->zErrMsg;
  u8 *aRead;
  int rc = SQLITE_OK;

  sqlite3_free(pCsr->cds.zFile);
  pCsr->cds.zFile = 0;

................................................................................
        pCsr->pFile, aRead, ZIPFILE_CDS_FIXED_SZ, pCsr->iNextOff, pzErr
    );
  }else{
    aRead = pCsr->pCurrent->aCdsEntry;
  }

  if( rc==SQLITE_OK ){
    rc = zipfileReadCDS(aRead, &pCsr->cds);
    if( rc!=SQLITE_OK ){
      assert( pCsr->pCurrent==0 );
      zipfileSetErrmsg(pCsr,"failed to read CDS at offset %lld",pCsr->iNextOff);

    }else{
      int nRead;























      if( pCsr->pCurrent==0 ){
        nRead = pCsr->cds.nFile + pCsr->cds.nExtra;
        aRead = zipfileCsrBuffer(pCsr);
        pCsr->iNextOff += ZIPFILE_CDS_FIXED_SZ;
        rc = zipfileReadData(pCsr->pFile, aRead, nRead, pCsr->iNextOff, pzErr);
      }else{
        aRead = &aRead[ZIPFILE_CDS_FIXED_SZ];
      }

      if( rc==SQLITE_OK ){
        pCsr->cds.zFile = sqlite3_mprintf("%.*s", (int)pCsr->cds.nFile, aRead);
        pCsr->iNextOff += pCsr->cds.nFile;
        pCsr->iNextOff += pCsr->cds.nExtra;
        pCsr->iNextOff += pCsr->cds.nComment;
................................................................................
}

static FILE *zipfileGetFd(ZipfileCsr *pCsr){
  if( pCsr->pFile ) return pCsr->pFile;
  return ((ZipfileTab*)(pCsr->base.pVtab))->pWriteFd;
}

static int zipfileReadLFH(
  FILE *pFd, 
  i64 iOffset,
  u8 *aTmp, 
  ZipfileLFH *pLFH, 
  char **pzErr
){
  u8 *aRead = aTmp;
  static const int szFix = ZIPFILE_LFH_FIXED_SZ;

  int rc;

  rc = zipfileReadData(pFd, aRead, szFix, iOffset, pzErr);
  if( rc==SQLITE_OK ){
    u32 sig = zipfileRead32(aRead);
    if( sig!=ZIPFILE_SIGNATURE_LFH ){
      *pzErr = sqlite3_mprintf("failed to read LFH at offset %d", (int)iOffset);


      rc = SQLITE_ERROR;
    }else{
      pLFH->iVersionExtract = zipfileRead16(aRead);
      pLFH->flags = zipfileRead16(aRead);
      pLFH->iCompression = zipfileRead16(aRead);
      pLFH->mTime = zipfileRead16(aRead);
      pLFH->mDate = zipfileRead16(aRead);
      pLFH->crc32 = zipfileRead32(aRead);
      pLFH->szCompressed = zipfileRead32(aRead);
      pLFH->szUncompressed = zipfileRead32(aRead);
      pLFH->nFile = zipfileRead16(aRead);
      pLFH->nExtra = zipfileRead16(aRead);
      assert( aRead==&aTmp[szFix] );

    }
  }
  return rc;
}

static int zipfileCsrReadLFH(ZipfileCsr *pCsr){
  FILE *pFile = zipfileGetFd(pCsr);
  char **pzErr = &pCsr->base.pVtab->zErrMsg;
  u8 *aRead = zipfileCsrBuffer(pCsr);
  int rc = zipfileReadLFH(pFile, pCsr->cds.iOffset, aRead, &pCsr->lfh, pzErr);
  pCsr->iDataOff =  pCsr->cds.iOffset + ZIPFILE_LFH_FIXED_SZ;
  pCsr->iDataOff += pCsr->lfh.nFile+pCsr->lfh.nExtra;
  return rc;
}


/*
** Advance an ZipfileCsr to its next row of output.
*/
................................................................................
    }while( pCsr->pCurrent && pCsr->pCurrent->bDeleted );
    if( pCsr->pCurrent==0 ){
      pCsr->bEof = 1;
    }
  }

  if( pCsr->bEof==0 ){
    rc = zipfileCsrReadCDS(pCsr);
    if( rc==SQLITE_OK ){
      rc = zipfileCsrReadLFH(pCsr);
    }
  }

  return rc;
}

/*
................................................................................
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;
  if( i>=3 && sqlite3_vtab_nochange(ctx) ){
    return 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.  */
................................................................................
  return SQLITE_OK;
}

/*
** Add object pNew to the end of the linked list that begins at
** ZipfileTab.pFirstEntry and ends with pLastEntry.
*/
static void zipfileAddEntry(
  ZipfileTab *pTab, 
  ZipfileEntry *pBefore, 
  ZipfileEntry *pNew
){
  assert( (pTab->pFirstEntry==0)==(pTab->pLastEntry==0) );
  assert( pNew->pNext==0 );
  if( pBefore==0 ){
    if( pTab->pFirstEntry==0 ){
      pTab->pFirstEntry = pTab->pLastEntry = pNew;
    }else{
      assert( pTab->pLastEntry->pNext==0 );
      pTab->pLastEntry->pNext = pNew;
      pTab->pLastEntry = pNew;
    }
  }else{
    ZipfileEntry **pp;
    for(pp=&pTab->pFirstEntry; *pp!=pBefore; pp=&((*pp)->pNext));
    pNew->pNext = pBefore;
    *pp = pNew;
  }
}

static int zipfileLoadDirectory(ZipfileTab *pTab){
  ZipfileEOCD eocd;
  int rc;

  rc = zipfileReadEOCD(pTab, pTab->pWriteFd, &eocd);
................................................................................
        memset(pNew, 0, sizeof(ZipfileEntry));
        pNew->zPath = (char*)&pNew[1];
        memcpy(pNew->zPath, &aRec[ZIPFILE_CDS_FIXED_SZ], nFile);
        pNew->zPath[nFile] = '\0';
        pNew->aCdsEntry = (u8*)&pNew->zPath[nFile+1];
        pNew->nCdsEntry = ZIPFILE_CDS_FIXED_SZ+nFile+nExtra+nComment;
        memcpy(pNew->aCdsEntry, aRec, pNew->nCdsEntry);
        zipfileAddEntry(pTab, 0, pNew);
      }

      iOff += ZIPFILE_CDS_FIXED_SZ+nFile+nExtra+nComment;
    }

    sqlite3_free(aBuf);
  }else if( rc==SQLITE_EMPTY ){
................................................................................
  u32 defaultMode,                /* Value to use if pVal IS NULL */
  u32 *pMode
){
  const char *z = (const char*)sqlite3_value_text(pVal);
  u32 mode = 0;
  if( z==0 ){
    mode = defaultMode;
  }else if( z[0]>='0' && z[0]<='9' ){
    mode = (unsigned int)sqlite3_value_int(pVal);
  }else{
    const char zTemplate[11] = "-rwxrwxrwx";
    int i;
    if( strlen(z)!=10 ) goto parse_error;
    switch( z[0] ){
      case '-': mode |= S_IFREG; break;
................................................................................
  int nPath = 0;                  /* strlen(zPath) */
  const u8 *pData = 0;            /* Pointer to buffer containing content */
  int nData = 0;                  /* Size of pData buffer in bytes */
  int iMethod = 0;                /* Compression method for new entry */
  u8 *pFree = 0;                  /* Free this */
  char *zFree = 0;                /* Also free this */
  ZipfileCDS cds;                 /* New Central Directory Structure entry */

  ZipfileEntry *pOld = 0;
  int bIsDir = 0;



  assert( pTab->zFile );
  assert( pTab->pWriteFd );

  if( sqlite3_value_type(apVal[0])!=SQLITE_NULL ){



    const char *zDelete = (const char*)sqlite3_value_text(apVal[0]);
    int nDelete = strlen(zDelete);

    for(pOld=pTab->pFirstEntry; 1; pOld=pOld->pNext){
      if( pOld->bDeleted ) continue;
      if( zipfileComparePath(pOld->zPath, zDelete, nDelete)==0 ){
        pOld->bDeleted = 1;
        break;
      }
      assert( pOld->pNext );
    }
    if( nVal==1 ) return SQLITE_OK;
  }

  if( sqlite3_value_nochange(apVal[5]) && sqlite3_value_nochange(apVal[6])
   && sqlite3_value_nochange(apVal[7]) && sqlite3_value_nochange(apVal[8])
  ){
    /* Reuse the data from the existing entry. */
    FILE *pFile = pTab->pWriteFd;
    ZipfileCDS cds;
    zipfileReadCDS(pOld->aCdsEntry, &cds);

    bIsDir = ((cds.iExternalAttr>>16) & S_IFDIR) ? 1 : 0;
    sz = cds.szUncompressed;
    iMethod = cds.iCompression;
    if( sz>0 ){
      char **pzErr = &pTab->base.zErrMsg;
      ZipfileLFH lfh;
      rc = zipfileReadLFH(pFile, cds.iOffset, pTab->aBuffer, &lfh, pzErr);
      if( rc==SQLITE_OK ){
        nData = lfh.szCompressed;
        pData = pFree = sqlite3_malloc(nData);
        if( pFree==NULL ){
          rc = SQLITE_NOMEM;
        }else{
          i64 iRead = cds.iOffset + ZIPFILE_LFH_FIXED_SZ + lfh.nFile+lfh.nExtra;
          rc = zipfileReadData(pFile, pFree, nData, iRead, pzErr);
        }
      }
    }
  }else{
    int mNull;
    mNull = (sqlite3_value_type(apVal[5])==SQLITE_NULL ? 0x0 : 0x8)  /* sz */
      + (sqlite3_value_type(apVal[6])==SQLITE_NULL ? 0x0 : 0x4)  /* rawdata */
      + (sqlite3_value_type(apVal[7])==SQLITE_NULL ? 0x0 : 0x2)  /* data */
      + (sqlite3_value_type(apVal[8])==SQLITE_NULL ? 0x0 : 0x1); /* method */
    if( mNull==0x00 ){     
      /* All four are NULL - this must be a directory */
      bIsDir = 1;
................................................................................
      ** a regular file or a symlink. */
      const u8 *aIn = sqlite3_value_blob(apVal[7]);
      int nIn = sqlite3_value_bytes(apVal[7]);
      int bAuto = sqlite3_value_type(apVal[8])==SQLITE_NULL;

      iMethod = sqlite3_value_int(apVal[8]);
      sz = nIn;
      pData = aIn;
      nData = nIn;
      if( iMethod!=0 && iMethod!=8 ){
        rc = SQLITE_CONSTRAINT;
      }else if( bAuto || iMethod ){
        int nCmp;
        rc = zipfileDeflate(pTab, aIn, nIn, &pFree, &nCmp);
        if( rc==SQLITE_OK ){
          if( iMethod || nCmp<nIn ){
            iMethod = 8;
            pData = pFree;


            nData = nCmp;
          }
        }
      }
    }
    else if( mNull==0x0D ){
      /* Values specified for "sz", "rawdata" and "method". In other words,
      ** pre-compressed data is being inserted.  */
................................................................................
            );
        rc = SQLITE_ERROR;
      }
    }
    else{
      rc = SQLITE_CONSTRAINT;
    }
  }

  if( rc==SQLITE_OK ){
    rc = zipfileGetMode(pTab, apVal[3], 
        (bIsDir ? (S_IFDIR + 0755) : (S_IFREG + 0644)), &mode
    );
    if( rc==SQLITE_OK && (bIsDir == ((mode & S_IFDIR)==0)) ){
      /* The "mode" attribute is a directory, but data has been specified.
................................................................................
    }
  }

  /* Check that we're not inserting a duplicate entry */
  if( rc==SQLITE_OK ){
    ZipfileEntry *p;
    for(p=pTab->pFirstEntry; p; p=p->pNext){
      if( p->bDeleted ) continue;
      if( zipfileComparePath(p->zPath, zPath, nPath)==0 ){
        rc = SQLITE_CONSTRAINT;
        break;
      }
    }
  }

................................................................................
    cds.szUncompressed = (u32)sz;
    cds.iExternalAttr = (mode<<16);
    cds.iOffset = (u32)pTab->szCurrent;
    pNew = zipfileNewEntry(&cds, zPath, nPath, (u32)mTime);
    if( pNew==0 ){
      rc = SQLITE_NOMEM;
    }else{
      zipfileAddEntry(pTab, pOld, pNew);
    }
  }

  /* Append the new header+file to the archive */
  if( rc==SQLITE_OK ){
    rc = zipfileAppendEntry(pTab, &cds, zPath, nPath, pData, nData, (u32)mTime);
  }

  if( rc!=SQLITE_OK && pOld ){
    pOld->bDeleted = 0;
  }
  sqlite3_free(pFree);
  sqlite3_free(zFree);
  return rc;
}

static int zipfileAppendEOCD(ZipfileTab *pTab, ZipfileEOCD *p){
  u8 *aBuf = pTab->aBuffer;

Changes to test/zipfile.test.

72
73
74
75
76
77
78



79
80
81
82
83
































































84
85
86
87
88
89
90
...
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
    VALUES('i.txt', '-rw-r--r--', 1000000006, 'zxcvb', 0);
    SELECT name FROM zz;
  COMMIT;
} {f.txt g.txt h.txt i.txt}
do_execsql_test 1.5.2 {
  SELECT name FROM zz;
} {f.txt g.txt h.txt i.txt}




do_execsql_test 1.6.0 {
  DELETE FROM zz WHERE name='g.txt';
  SELECT name FROM zz;
} {f.txt h.txt i.txt}

































































#-------------------------------------------------------------------------
db close
forcedelete test.zip
reset_db
load_static_extension db zipfile
do_execsql_test 2.1 {
................................................................................
  dirname/ 16877 {}
  dirname2/ 16877 {}
  dirname2/file1.txt 33188 abcdefghijklmnop
}

do_catchsql_test 2.3 {
  UPDATE zzz SET name = 'dirname3' WHERE name = 'dirname/';
} {1 {constraint failed}}
do_execsql_test 2.4 {
  SELECT name, mode, data FROM zzz;
} {
  dirname/ 16877 {}
  dirname2/ 16877 {}
  dirname2/file1.txt 33188 abcdefghijklmnop
}

# If on unix, check that the [unzip] utility can unpack our archive.
#
if {$::tcl_platform(platform)=="unix"} {
  do_test 2.5.1 {
    forcedelete dirname
      forcedelete dirname2
      set rc [catch { exec unzip test.zip > /dev/null } msg]
      list $rc $msg
  } {0 {}}
  do_test 2.5.2 { file isdir dirname } 1
  do_test 2.5.3 { file isdir dirname2 } 1
  do_test 2.5.4 { file isdir dirname2/file1.txt } 0
  do_test 2.5.5 { 
    set fd [open dirname2/file1.txt]
    set data [read $fd]
    close $fd
    set data







>
>
>





>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







|



|













|







72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
...
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
    VALUES('i.txt', '-rw-r--r--', 1000000006, 'zxcvb', 0);
    SELECT name FROM zz;
  COMMIT;
} {f.txt g.txt h.txt i.txt}
do_execsql_test 1.5.2 {
  SELECT name FROM zz;
} {f.txt g.txt h.txt i.txt}
do_execsql_test 1.5.3 {
  SELECT data FROM zz WHERE name='i.txt';
} {zxcvb}

do_execsql_test 1.6.0 {
  DELETE FROM zz WHERE name='g.txt';
  SELECT name FROM zz;
} {f.txt h.txt i.txt}

do_execsql_test 1.6.1 {
  SELECT name, mode, mtime, data, method FROM zipfile('test.zip');
} {
  f.txt 33188 1000000000 abcde 0
  h.txt 33188 1000000004 aaaaaaaaaabbbbbbbbbb 8
  i.txt 33188 1000000006 zxcvb 0
}

do_execsql_test 1.6.2 {
  UPDATE zz SET mtime=4 WHERE name='i.txt';
  SELECT name, mode, mtime, data, method FROM zipfile('test.zip');
} {
  f.txt 33188 1000000000 abcde 0
  h.txt 33188 1000000004 aaaaaaaaaabbbbbbbbbb 8
  i.txt 33188 4 zxcvb 0
}

do_execsql_test 1.6.3 {
  UPDATE zz SET mode='-rw-r--r-x' WHERE name='h.txt';
  SELECT name, mode, mtime, data, method FROM zipfile('test.zip');
} {
  f.txt 33188 1000000000 abcde 0
  h.txt 33189 1000000004 aaaaaaaaaabbbbbbbbbb 8
  i.txt 33188 4 zxcvb 0
}

do_execsql_test 1.6.4 {
  UPDATE zz SET name = 'blue.txt' WHERE name='f.txt';
  SELECT name, mode, mtime, data, method FROM zipfile('test.zip');
} {
  blue.txt 33188 1000000000 abcde 0
  h.txt 33189 1000000004 aaaaaaaaaabbbbbbbbbb 8
  i.txt 33188 4 zxcvb 0
}

do_execsql_test 1.6.5 {
  UPDATE zz SET data = 'edcba' WHERE name='blue.txt';
  SELECT name, mode, mtime, data, method FROM zipfile('test.zip');
} {
  blue.txt 33188 1000000000 edcba 0
  h.txt 33189 1000000004 aaaaaaaaaabbbbbbbbbb 8
  i.txt 33188 4 zxcvb 0
}

do_execsql_test 1.6.6 {
  UPDATE zz SET mode=NULL, data = NULL WHERE name='blue.txt';
  SELECT name, mode, mtime, data, method FROM zipfile('test.zip');
} {
  blue.txt/ 16877 1000000000 {} 0
  h.txt 33189 1000000004 aaaaaaaaaabbbbbbbbbb 8
  i.txt 33188 4 zxcvb 0
}

do_catchsql_test 1.6.7 {
  UPDATE zz SET data=NULL WHERE name='i.txt'
} {1 {constraint failed}}
do_execsql_test 1.6.8 {
  SELECT name, mode, mtime, data, method FROM zipfile('test.zip');
} {
  blue.txt/ 16877 1000000000 {} 0
  h.txt 33189 1000000004 aaaaaaaaaabbbbbbbbbb 8
  i.txt 33188 4 zxcvb 0
}

#-------------------------------------------------------------------------
db close
forcedelete test.zip
reset_db
load_static_extension db zipfile
do_execsql_test 2.1 {
................................................................................
  dirname/ 16877 {}
  dirname2/ 16877 {}
  dirname2/file1.txt 33188 abcdefghijklmnop
}

do_catchsql_test 2.3 {
  UPDATE zzz SET name = 'dirname3' WHERE name = 'dirname/';
} {0 {}}
do_execsql_test 2.4 {
  SELECT name, mode, data FROM zzz;
} {
  dirname3/ 16877 {}
  dirname2/ 16877 {}
  dirname2/file1.txt 33188 abcdefghijklmnop
}

# If on unix, check that the [unzip] utility can unpack our archive.
#
if {$::tcl_platform(platform)=="unix"} {
  do_test 2.5.1 {
    forcedelete dirname
    forcedelete dirname2
    set rc [catch { exec unzip test.zip > /dev/null } msg]
    list $rc $msg
  } {0 {}}
  do_test 2.5.2 { file isdir dirname3 } 1
  do_test 2.5.3 { file isdir dirname2 } 1
  do_test 2.5.4 { file isdir dirname2/file1.txt } 0
  do_test 2.5.5 { 
    set fd [open dirname2/file1.txt]
    set data [read $fd]
    close $fd
    set data