Fossil

Check-in [932d351ea5]
Login

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

Overview
Comment:Add the -l and -x options to the test-filezip command.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 932d351ea5619f5d92d763e4b7a116183d0d755eddacf94f8cb80ee6661c78b8
User & Date: drh 2025-07-14 13:13:44.888
Context
2025-07-14
17:31
Add mentions in changes.wiki and signing.md that SSH-signed artifacts are "ignored" by fossil versions prior to 2.26, as suggested in [forum:9348885dd6 | forum post 9348885dd6]. check-in: 3f8d014f99 user: stephan tags: trunk
13:13
Add the -l and -x options to the test-filezip command. check-in: 932d351ea5 user: drh tags: trunk
2025-07-13
20:57
Update the (undocumented) test-filezip command so that the ZIP archvie it creates has correct timestamps. check-in: 6368fce274 user: drh tags: trunk
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/zip.c.
560
561
562
563
564
565
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
    fossil_free(azDir[i]);
  }
  fossil_free(azDir);
  nDir = 0;
  azDir = 0;
}





/*
** COMMAND: test-filezip
**




** Generate a ZIP archive specified by the first argument that
** contains files given in the second and subsequent arguments.













*/
void filezip_cmd(void){
  int i;
  Blob zip;
  Blob file;
  int eFType = SymFILE;
  Archive sArchive;
  memset(&sArchive, 0, sizeof(Archive));
  sArchive.eType = ARCHIVE_ZIP;

  sArchive.pBlob = &zip;
  if( find_option("dereference","h",0)!=0 ){
    eFType = ExtFILE;
  }









  if( g.argc<3 ){
    usage("ARCHIVE FILE....");
  }

  sqlite3_open(":memory:", &g.db);






















































  zip_open();
  for(i=3; i<g.argc; i++){
    double rDate;
    i64 iDate;
    blob_zero(&file);
    blob_read_from_file(&file, g.argv[i], eFType);
    iDate = file_mtime(g.argv[i], eFType);
    rDate = ((double)iDate)/86400.0 + 2440587.5;
    zip_set_timedate(rDate);
    zip_add_file(&sArchive, g.argv[i], &file, file_perm(0,eFType));
    blob_reset(&file);
  }
  zip_close(&sArchive);
  blob_write_to_file(&zip, g.argv[2]);

}

/*
** Given the RID for a manifest, construct a ZIP archive containing
** all files in the corresponding baseline.
**
** If RID is for an object that is not a real manifest, then the







>
>
>
>



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


<
<
<

<
<
|
>
|



>
>
>
>
>
>
>
>
>

|

>

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







560
561
562
563
564
565
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
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
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
    fossil_free(azDir[i]);
  }
  fossil_free(azDir);
  nDir = 0;
  azDir = 0;
}

/* Functions found in shell.c */
extern int sqlite3_fileio_init(sqlite3*,char**,const sqlite3_api_routines*);
extern int sqlite3_zipfile_init(sqlite3*,char**,const sqlite3_api_routines*);

/*
** COMMAND: test-filezip
**
** Usage: %fossil test-filezip [OPTIONS] ZIPFILE [FILENAME...]
**
** This command uses Fossil infrastructure or read or create a ZIP
** archive named by the ZIPFILE argument.  With no options, a new
** ZIP archive is created and there must be at least one FILENAME
** argument. If the -l option is used, the contents of the named ZIP
** archive are listed on standard output.  With the -x argument, the
** contents of the ZIP archive are extracted.
**
** There are two purposes for this command:  (1) To server as a test
** platform for the Fossil ZIP archive generator, and (2) to provide
** rudimentary ZIP archive creation capabilities on platforms that do
** not have the "zip" command installed.
**
** Options:
**
**    -h|--dereference    Follow symlinks
**    -l|--list           List the contents of the ZIP archive
**    -x|--extract        Extract files from a ZIP archive
*/
void filezip_cmd(void){



  int eFType = SymFILE;


  int doList = 0;
  int doExtract = 0;
  char *zArchiveName;
  if( find_option("dereference","h",0)!=0 ){
    eFType = ExtFILE;
  }
  if( find_option("list","l",0)!=0 ){
    doList = 1;
  }
  if( find_option("extract","x",0)!=0 ){
    if( doList ){
      fossil_fatal("incompatible options: -l and -x");
    }
    doExtract = 1;
  }
  if( g.argc<3 ){
    usage("ARCHIVE FILES...");
  }
  zArchiveName = g.argv[2];
  sqlite3_open(":memory:", &g.db);
  if( doList ){
    /* Do a content listing of a ZIP archive */
    Stmt q;
    int nRow = 0;
    i64 szTotal = 0;
    if( file_size(zArchiveName, eFType)<0 ){
      fossil_fatal("No such ZIP archive: %s", zArchiveName);
    }
    if( g.argc>3 ){
      fossil_fatal("extra arguments after \"fossil test-filezip -l ARCHIVE\"");
    }
    sqlite3_zipfile_init(g.db, 0, 0);
    db_multi_exec("CREATE VIRTUAL TABLE z1 USING zipfile(%Q)", zArchiveName);
    db_prepare(&q, "SELECT sz, datetime(mtime,'unixepoch'), name FROM z1");
    while( db_step(&q)==SQLITE_ROW ){
      int sz = db_column_int(&q, 0);
      szTotal += sz;
      if( nRow==0 ){
        fossil_print("  Length      Date    Time    Name\n");
        fossil_print("---------  ---------- -----   ----\n");
      }
      nRow++;
      fossil_print("%9d  %.16s   %s\n", sz, db_column_text(&q,1),
                   db_column_text(&q,2));
    }
    if( nRow ){
      fossil_print("---------                     --------\n");
      fossil_print("%9lld  %16s   %d files\n", szTotal, "", nRow);
    }
    db_finalize(&q);
  }else if( doExtract ){
    /* Extract files from an existing ZIP archive */
    if( file_size(zArchiveName, eFType)<0 ){
      fossil_fatal("No such ZIP archive: %s", zArchiveName);
    }
    if( g.argc>3 ){
      fossil_fatal("extra arguments after \"fossil test-filezip -x ARCHIVE\"");
    }
    sqlite3_zipfile_init(g.db, 0, 0);
    sqlite3_fileio_init(g.db, 0, 0);
    db_multi_exec("CREATE VIRTUAL TABLE z1 USING zipfile(%Q)", zArchiveName);
    db_multi_exec("SELECT writefile(name,data) FROM z1");
  }else{
    /* Without the -x or -l options, construct a new ZIP archive */
    int i;
    Blob zip;
    Blob file;
    Archive sArchive;
    memset(&sArchive, 0, sizeof(Archive));
    sArchive.eType = ARCHIVE_ZIP;
    sArchive.pBlob = &zip;
    if( file_size(zArchiveName, eFType)>0 ){
      fossil_fatal("ZIP archive %s already exists", zArchiveName);
    }
    zip_open();
    for(i=3; i<g.argc; i++){
      double rDate;
      i64 iDate;
      blob_zero(&file);
      blob_read_from_file(&file, g.argv[i], eFType);
      iDate = file_mtime(g.argv[i], eFType);
      rDate = ((double)iDate)/86400.0 + 2440587.5;
      zip_set_timedate(rDate);
      zip_add_file(&sArchive, g.argv[i], &file, file_perm(0,eFType));
      blob_reset(&file);
    }
    zip_close(&sArchive);
    blob_write_to_file(&zip, g.argv[2]);
  }
}

/*
** Given the RID for a manifest, construct a ZIP archive containing
** all files in the corresponding baseline.
**
** If RID is for an object that is not a real manifest, then the