Fossil SCM

Add the "fossil sqlar" command. Like "fossil zip", except generates sqlar archives.

dan 2017-12-04 20:39 trunk
Commit 7eb5b0a2ae297c2c0d495fd27faa8fddd0a632611e8238001eb57487b4d868ed
1 file changed +417 -88
+417 -88
--- src/zip.c
+++ src/zip.c
@@ -13,11 +13,11 @@
1313
** [email protected]
1414
** http://www.hwaci.com/drh/
1515
**
1616
*******************************************************************************
1717
**
18
-** This file contains code used to generate ZIP archives.
18
+** This file contains code used to generate ZIP and SQLAR archives.
1919
*/
2020
#include "config.h"
2121
#include <assert.h>
2222
#if defined(FOSSIL_ENABLE_MINIZ)
2323
# define MINIZ_HEADER_FILE_ONLY
@@ -25,10 +25,16 @@
2525
#else
2626
# include <zlib.h>
2727
#endif
2828
#include "zip.h"
2929
30
+/*
31
+** Type of archive to build.
32
+*/
33
+#define ARCHIVE_ZIP 0
34
+#define ARCHIVE_SQLAR 1
35
+
3036
/*
3137
** Write a 16- or 32-bit integer as little-endian into the given buffer.
3238
*/
3339
static void put16(char *z, int v){
3440
z[0] = v & 0xff;
@@ -51,10 +57,158 @@
5157
static int dosDate; /* DOS-format date */
5258
static int unixTime; /* Seconds since 1970 */
5359
static int nDir; /* Number of entries in azDir[] */
5460
static char **azDir; /* Directory names already added to the archive */
5561
62
+typedef struct Archive Archive;
63
+struct Archive {
64
+ int eType; /* Type of archive (SQLAR or ZIP) */
65
+ Blob *pBlob; /* Output blob */
66
+ Blob tmp; /* Blob used as temp space for compression */
67
+ sqlite3 *db; /* Db used to assemble sqlar archive */
68
+ sqlite3_stmt *pInsert; /* INSERT statement for SQLAR */
69
+ sqlite3_vfs vfs; /* VFS object */
70
+};
71
+
72
+/*
73
+** Ensure that blob pBlob is at least nMin bytes in size.
74
+*/
75
+static void zip_blob_minsize(Blob *pBlob, int nMin){
76
+ if( blob_size(pBlob)<nMin ){
77
+ blob_resize(pBlob, nMin);
78
+ }
79
+}
80
+
81
+/*************************************************************************
82
+** Implementation of "archive" VFS. A VFS designed to store the contents
83
+** of a new database in a Blob. Used to construct sqlar archives in
84
+** memory.
85
+*/
86
+typedef struct ArchiveFile ArchiveFile;
87
+struct ArchiveFile {
88
+ sqlite3_file base; /* Base class */
89
+ Blob *pBlob;
90
+};
91
+
92
+static int archiveClose(sqlite3_file *pFile){
93
+ return SQLITE_OK;
94
+}
95
+static int archiveRead(
96
+ sqlite3_file *pFile, void *pBuf, int iAmt, sqlite3_int64 iOfst
97
+){
98
+ assert( iOfst==0 || iOfst==24 );
99
+ return SQLITE_IOERR_SHORT_READ;
100
+}
101
+static int archiveWrite(
102
+ sqlite3_file *pFile, const void *pBuf, int iAmt, sqlite3_int64 iOfst
103
+){
104
+ ArchiveFile *pAF = (ArchiveFile*)pFile;
105
+ int nMin = (int)iOfst + iAmt;
106
+ char *aBlob; /* Output buffer */
107
+
108
+ zip_blob_minsize(pAF->pBlob, nMin);
109
+ aBlob = blob_buffer(pAF->pBlob);
110
+ memcpy(&aBlob[iOfst], pBuf, iAmt);
111
+ return SQLITE_OK;
112
+}
113
+static int archiveTruncate(sqlite3_file *pFile, sqlite3_int64 size){
114
+ return SQLITE_OK;
115
+}
116
+static int archiveSync(sqlite3_file *pFile, int flags){
117
+ return SQLITE_OK;
118
+}
119
+static int archiveFileSize(sqlite3_file *pFile, sqlite3_int64 *pSize){
120
+ *pSize = 0;
121
+ return SQLITE_OK;
122
+}
123
+static int archiveLock(sqlite3_file *pFile, int eLock){
124
+ return SQLITE_OK;
125
+}
126
+static int archiveUnlock(sqlite3_file *pFile, int eLock){
127
+ return SQLITE_OK;
128
+}
129
+static int archiveCheckReservedLock(sqlite3_file *pFile, int *pResOut){
130
+ *pResOut = 0;
131
+ return SQLITE_OK;
132
+}
133
+static int archiveFileControl(sqlite3_file *pFile, int op, void *pArg){
134
+ if( op==SQLITE_FCNTL_SIZE_HINT ){
135
+ ArchiveFile *pAF = (ArchiveFile*)pFile;
136
+ zip_blob_minsize(pAF->pBlob, (int)(*(sqlite3_int64*)pArg));
137
+ }
138
+ return SQLITE_NOTFOUND;
139
+}
140
+static int archiveSectorSize(sqlite3_file *pFile){
141
+ return 512;
142
+}
143
+static int archiveDeviceCharacteristics(sqlite3_file *pFile){
144
+ return 0;
145
+}
146
+
147
+static int archiveOpen(
148
+ sqlite3_vfs *pVfs, const char *zName,
149
+ sqlite3_file *pFile, int flags, int *pOutFlags
150
+){
151
+ static struct sqlite3_io_methods methods = {
152
+ 1, /* iVersion */
153
+ archiveClose,
154
+ archiveRead,
155
+ archiveWrite,
156
+ archiveTruncate,
157
+ archiveSync,
158
+ archiveFileSize,
159
+ archiveLock,
160
+ archiveUnlock,
161
+ archiveCheckReservedLock,
162
+ archiveFileControl,
163
+ archiveSectorSize,
164
+ archiveDeviceCharacteristics,
165
+ 0, 0, 0, 0,
166
+ 0, 0
167
+ };
168
+
169
+ ArchiveFile *pAF = (ArchiveFile*)pFile;
170
+ assert( flags & SQLITE_OPEN_MAIN_DB );
171
+
172
+ pAF->base.pMethods = &methods;
173
+ pAF->pBlob = (Blob*)pVfs->pAppData;
174
+
175
+ return SQLITE_OK;
176
+}
177
+static int archiveDelete(sqlite3_vfs *pVfs, const char *zName, int syncDir){
178
+ return SQLITE_OK;
179
+}
180
+static int archiveAccess(
181
+ sqlite3_vfs *pVfs, const char *zName, int flags, int *pResOut
182
+){
183
+ *pResOut = 0;
184
+ return SQLITE_OK;
185
+}
186
+static int archiveFullPathname(
187
+ sqlite3_vfs *pVfs, const char *zIn, int nOut, char *zOut
188
+){
189
+ int n = strlen(zIn);
190
+ memcpy(zOut, zIn, n+1);
191
+ return SQLITE_OK;
192
+}
193
+static int archiveRandomness(sqlite3_vfs *pVfs, int nByte, char *zOut){
194
+ memset(zOut, 0, nByte);
195
+ return SQLITE_OK;
196
+}
197
+static int archiveSleep(sqlite3_vfs *pVfs, int microseconds){
198
+ return SQLITE_OK;
199
+}
200
+static int archiveCurrentTime(sqlite3_vfs *pVfs, double *prOut){
201
+ return SQLITE_OK;
202
+}
203
+static int archiveGetLastError(sqlite3_vfs *pVfs, int nBuf, char *aBuf){
204
+ return SQLITE_OK;
205
+}
206
+/*
207
+** End of "archive" VFS.
208
+*************************************************************************/
209
+
56210
/*
57211
** Initialize a new ZIP archive.
58212
*/
59213
void zip_open(void){
60214
blob_zero(&body);
@@ -90,43 +244,22 @@
90244
zip_set_timedate_from_str(zDate);
91245
fossil_free(zDate);
92246
unixTime = (rDate - 2440587.5)*86400.0;
93247
}
94248
95
-/*
96
-** If the given filename includes one or more directory entries, make
97
-** sure the directories are already in the archive. If they are not
98
-** in the archive, add them.
99
-*/
100
-void zip_add_folders(char *zName){
101
- int i, c;
102
- int j;
103
- for(i=0; zName[i]; i++){
104
- if( zName[i]=='/' ){
105
- c = zName[i+1];
106
- zName[i+1] = 0;
107
- for(j=0; j<nDir; j++){
108
- if( fossil_strcmp(zName, azDir[j])==0 ) break;
109
- }
110
- if( j>=nDir ){
111
- nDir++;
112
- azDir = fossil_realloc(azDir, sizeof(azDir[0])*nDir);
113
- azDir[j] = mprintf("%s", zName);
114
- zip_add_file(zName, 0, 0);
115
- }
116
- zName[i+1] = c;
117
- }
118
- }
119
-}
120
-
121249
/*
122250
** Append a single file to a growing ZIP archive.
123251
**
124252
** pFile is the file to be appended. zName is the name
125253
** that the file should be saved as.
126254
*/
127
-void zip_add_file(const char *zName, const Blob *pFile, int mPerm){
255
+static void zip_add_file_to_zip(
256
+ Archive *p,
257
+ const char *zName,
258
+ const Blob *pFile,
259
+ int mPerm
260
+){
128261
z_stream stream;
129262
int nameLen;
130263
int toOut = 0;
131264
int iStart;
132265
int iCRC = 0;
@@ -243,37 +376,189 @@
243376
put16(&zExTime[2], 5);
244377
blob_append(&toc, zExTime, 9);
245378
nEntry++;
246379
}
247380
381
+static void zip_add_file_to_sqlar(
382
+ Archive *p,
383
+ const char *zName,
384
+ const Blob *pFile,
385
+ int mPerm
386
+){
387
+ int nName = strlen(zName);
388
+
389
+ if( p->db==0 ){
390
+ assert( p->vfs.zName==0 );
391
+ p->vfs.zName = (const char*)mprintf("archivevfs%p", (void*)p);
392
+ p->vfs.iVersion = 1;
393
+ p->vfs.szOsFile = sizeof(ArchiveFile);
394
+ p->vfs.mxPathname = 512;
395
+ p->vfs.pAppData = (void*)p->pBlob;
396
+ p->vfs.xOpen = archiveOpen;
397
+ p->vfs.xDelete = archiveDelete;
398
+ p->vfs.xAccess = archiveAccess;
399
+ p->vfs.xFullPathname = archiveFullPathname;
400
+ p->vfs.xRandomness = archiveRandomness;
401
+ p->vfs.xSleep = archiveSleep;
402
+ p->vfs.xCurrentTime = archiveCurrentTime;
403
+ p->vfs.xGetLastError = archiveGetLastError;
404
+ sqlite3_vfs_register(&p->vfs, 0);
405
+ sqlite3_open_v2("file:xyz.db", &p->db,
406
+ SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE, p->vfs.zName
407
+ );
408
+ assert( p->db );
409
+ blob_zero(&p->tmp);
410
+ sqlite3_exec(p->db,
411
+ "PRAGMA journal_mode = off;"
412
+ "PRAGMA cache_spill = off;"
413
+ "BEGIN;"
414
+ "CREATE TABLE sqlar("
415
+ "name TEXT PRIMARY KEY, -- name of the file\n"
416
+ "mode INT, -- access permissions\n"
417
+ "mtime INT, -- last modification time\n"
418
+ "sz INT, -- original file size\n"
419
+ "data BLOB -- compressed content\n"
420
+ ");", 0, 0, 0
421
+ );
422
+ sqlite3_prepare(p->db,
423
+ "INSERT INTO sqlar VALUES(?, ?, ?, ?, ?)", -1,
424
+ &p->pInsert, 0
425
+ );
426
+ assert( p->pInsert );
427
+
428
+ sqlite3_bind_int64(p->pInsert, 3, unixTime);
429
+ blob_zero(p->pBlob);
430
+ }
431
+
432
+ if( pFile==0 ){
433
+ /* Directory. */
434
+ if( zName[nName-1]=='/' ) nName--;
435
+ sqlite3_bind_text(p->pInsert, 1, zName, nName, SQLITE_STATIC);
436
+ sqlite3_bind_int(p->pInsert, 2, 040755);
437
+ sqlite3_bind_int(p->pInsert, 4, 0);
438
+ sqlite3_bind_null(p->pInsert, 5);
439
+ }else{
440
+ sqlite3_bind_text(p->pInsert, 1, zName, nName, SQLITE_STATIC);
441
+ if( mPerm==PERM_LNK ){
442
+ sqlite3_bind_int(p->pInsert, 2, 0120755);
443
+ sqlite3_bind_int(p->pInsert, 4, -1);
444
+ sqlite3_bind_text(p->pInsert, 5,
445
+ blob_buffer(pFile), blob_size(pFile), SQLITE_STATIC
446
+ );
447
+ }else{
448
+ int nIn = blob_size(pFile);
449
+ unsigned long int nOut = nIn;
450
+ sqlite3_bind_int(p->pInsert, 2, mPerm==PERM_EXE ? 0100755 : 0100644);
451
+ sqlite3_bind_int(p->pInsert, 4, nIn);
452
+ zip_blob_minsize(&p->tmp, nIn);
453
+ compress(
454
+ blob_buffer(&p->tmp), &nOut, (unsigned char*)blob_buffer(pFile), nIn
455
+ );
456
+ if( nOut>=nIn ){
457
+ sqlite3_bind_blob(p->pInsert, 5,
458
+ blob_buffer(pFile), blob_size(pFile), SQLITE_STATIC
459
+ );
460
+ }else{
461
+ sqlite3_bind_blob(p->pInsert, 5,
462
+ blob_buffer(&p->tmp), nOut, SQLITE_STATIC
463
+ );
464
+ }
465
+ }
466
+ }
467
+
468
+ sqlite3_step(p->pInsert);
469
+ sqlite3_reset(p->pInsert);
470
+}
471
+
472
+static void zip_add_file(
473
+ Archive *p,
474
+ const char *zName,
475
+ const Blob *pFile,
476
+ int mPerm
477
+){
478
+ if( p->eType==ARCHIVE_ZIP ){
479
+ zip_add_file_to_zip(p, zName, pFile, mPerm);
480
+ }else{
481
+ zip_add_file_to_sqlar(p, zName, pFile, mPerm);
482
+ }
483
+}
484
+
485
+/*
486
+** If the given filename includes one or more directory entries, make
487
+** sure the directories are already in the archive. If they are not
488
+** in the archive, add them.
489
+*/
490
+static void zip_add_folders(Archive *p, char *zName){
491
+ int i, c;
492
+ int j;
493
+ for(i=0; zName[i]; i++){
494
+ if( zName[i]=='/' ){
495
+ c = zName[i+1];
496
+ zName[i+1] = 0;
497
+ for(j=0; j<nDir; j++){
498
+ if( fossil_strcmp(zName, azDir[j])==0 ) break;
499
+ }
500
+ if( j>=nDir ){
501
+ nDir++;
502
+ azDir = fossil_realloc(azDir, sizeof(azDir[0])*nDir);
503
+ azDir[j] = mprintf("%s", zName);
504
+ zip_add_file(p, zName, 0, 0);
505
+ }
506
+ zName[i+1] = c;
507
+ }
508
+ }
509
+}
510
+
511
+/*
512
+** Free all the members of structure Archive allocated while processing
513
+** an SQLAR request.
514
+*/
515
+static void free_archive(Archive *p){
516
+ if( p->vfs.zName ){
517
+ sqlite3_vfs_unregister(&p->vfs);
518
+ fossil_free((char*)p->vfs.zName);
519
+ p->vfs.zName = 0;
520
+ }
521
+ sqlite3_finalize(p->pInsert);
522
+ p->pInsert = 0;
523
+ sqlite3_close(p->db);
524
+ p->db = 0;
525
+}
248526
249527
/*
250528
** Write the ZIP archive into the given BLOB.
251529
*/
252
-void zip_close(Blob *pZip){
253
- int iTocStart;
254
- int iTocEnd;
530
+static void zip_close(Archive *p){
255531
int i;
256
- char zBuf[30];
257
-
258
- iTocStart = blob_size(&body);
259
- blob_append(&body, blob_buffer(&toc), blob_size(&toc));
260
- iTocEnd = blob_size(&body);
261
-
262
- memset(zBuf, 0, sizeof(zBuf));
263
- put32(&zBuf[0], 0x06054b50);
264
- put16(&zBuf[4], 0);
265
- put16(&zBuf[6], 0);
266
- put16(&zBuf[8], nEntry);
267
- put16(&zBuf[10], nEntry);
268
- put32(&zBuf[12], iTocEnd - iTocStart);
269
- put32(&zBuf[16], iTocStart);
270
- put16(&zBuf[20], 0);
271
- blob_append(&body, zBuf, 22);
272
- blob_reset(&toc);
273
- *pZip = body;
274
- blob_zero(&body);
532
+ if( p->eType==ARCHIVE_ZIP ){
533
+ int iTocStart;
534
+ int iTocEnd;
535
+ char zBuf[30];
536
+
537
+ iTocStart = blob_size(&body);
538
+ blob_append(&body, blob_buffer(&toc), blob_size(&toc));
539
+ iTocEnd = blob_size(&body);
540
+
541
+ memset(zBuf, 0, sizeof(zBuf));
542
+ put32(&zBuf[0], 0x06054b50);
543
+ put16(&zBuf[4], 0);
544
+ put16(&zBuf[6], 0);
545
+ put16(&zBuf[8], nEntry);
546
+ put16(&zBuf[10], nEntry);
547
+ put32(&zBuf[12], iTocEnd - iTocStart);
548
+ put32(&zBuf[16], iTocStart);
549
+ put16(&zBuf[20], 0);
550
+ blob_append(&body, zBuf, 22);
551
+ blob_reset(&toc);
552
+ *(p->pBlob) = body;
553
+ blob_zero(&body);
554
+ }else{
555
+ if( p->db ) sqlite3_exec(p->db, "COMMIT", 0, 0, 0);
556
+ free_archive(p);
557
+ blob_reset(&p->tmp);
558
+ }
559
+
275560
nEntry = 0;
276561
for(i=0; i<nDir; i++){
277562
fossil_free(azDir[i]);
278563
}
279564
fossil_free(azDir);
@@ -290,10 +575,14 @@
290575
void filezip_cmd(void){
291576
int i;
292577
Blob zip;
293578
Blob file;
294579
int eFType = SymFILE;
580
+ Archive sArchive;
581
+ memset(&sArchive, 0, sizeof(Archive));
582
+ sArchive.eType = ARCHIVE_ZIP;
583
+ sArchive.pBlob = &zip;
295584
if( g.argc<3 ){
296585
usage("ARCHIVE FILE....");
297586
}
298587
if( find_option("dereference","h",0)!=0 ){
299588
eFType = ExtFILE;
@@ -300,14 +589,14 @@
300589
}
301590
zip_open();
302591
for(i=3; i<g.argc; i++){
303592
blob_zero(&file);
304593
blob_read_from_file(&file, g.argv[i], eFType);
305
- zip_add_file(g.argv[i], &file, file_perm(0,0));
594
+ zip_add_file(&sArchive, g.argv[i], &file, file_perm(0,0));
306595
blob_reset(&file);
307596
}
308
- zip_close(&zip);
597
+ zip_close(&sArchive);
309598
blob_write_to_file(&zip, g.argv[2]);
310599
}
311600
312601
/*
313602
** Given the RID for a manifest, construct a ZIP archive containing
@@ -325,23 +614,29 @@
325614
** in which case it is ignored. The intention is to create a zip which
326615
** politely expands into a subdir instead of filling your current dir
327616
** with source files. For example, pass a UUID or "ProjectName".
328617
**
329618
*/
330
-void zip_of_checkin(
331
- int rid, /* The RID of the checkin to construct the ZIP archive from */
332
- Blob *pZip, /* Write the ZIP archive content into this blob */
333
- const char *zDir, /* Top-level directory of the ZIP archive */
619
+static void zip_of_checkin(
620
+ int eType, /* Type of archive (ZIP or SQLAR) */
621
+ int rid, /* The RID of the checkin to build the archive from */
622
+ Blob *pZip, /* Write the archive content into this blob */
623
+ const char *zDir, /* Top-level directory of the archive */
334624
Glob *pInclude, /* Only include files that match this pattern */
335625
Glob *pExclude /* Exclude files that match this pattern */
336626
){
337627
Blob mfile, hash, file;
338628
Manifest *pManifest;
339629
ManifestFile *pFile;
340630
Blob filename;
341631
int nPrefix;
342632
633
+ Archive sArchive;
634
+ memset(&sArchive, 0, sizeof(Archive));
635
+ sArchive.eType = eType;
636
+ sArchive.pBlob = pZip;
637
+
343638
content_get(rid, &mfile);
344639
if( blob_size(&mfile)==0 ){
345640
blob_zero(pZip);
346641
return;
347642
}
@@ -379,31 +674,31 @@
379674
}
380675
381676
if( eflg & MFESTFLG_RAW ){
382677
blob_append(&filename, "manifest", -1);
383678
zName = blob_str(&filename);
384
- zip_add_folders(zName);
679
+ zip_add_folders(&sArchive, zName);
385680
sterilize_manifest(&mfile);
386
- zip_add_file(zName, &mfile, 0);
681
+ zip_add_file(&sArchive, zName, &mfile, 0);
387682
}
388683
if( eflg & MFESTFLG_UUID ){
389684
blob_append(&hash, "\n", 1);
390685
blob_resize(&filename, nPrefix);
391686
blob_append(&filename, "manifest.uuid", -1);
392687
zName = blob_str(&filename);
393
- zip_add_folders(zName);
394
- zip_add_file(zName, &hash, 0);
688
+ zip_add_folders(&sArchive, zName);
689
+ zip_add_file(&sArchive, zName, &hash, 0);
395690
}
396691
if( eflg & MFESTFLG_TAGS ){
397692
Blob tagslist;
398693
blob_zero(&tagslist);
399694
get_checkin_taglist(rid, &tagslist);
400695
blob_resize(&filename, nPrefix);
401696
blob_append(&filename, "manifest.tags", -1);
402697
zName = blob_str(&filename);
403
- zip_add_folders(zName);
404
- zip_add_file(zName, &tagslist, 0);
698
+ zip_add_folders(&sArchive, zName);
699
+ zip_add_file(&sArchive, zName, &tagslist, 0);
405700
blob_reset(&tagslist);
406701
}
407702
}
408703
manifest_file_rewind(pManifest);
409704
while( (pFile = manifest_file_next(pManifest,0))!=0 ){
@@ -414,53 +709,35 @@
414709
if( fid ){
415710
content_get(fid, &file);
416711
blob_resize(&filename, nPrefix);
417712
blob_append(&filename, pFile->zName, -1);
418713
zName = blob_str(&filename);
419
- zip_add_folders(zName);
420
- zip_add_file(zName, &file, manifest_file_mperm(pFile));
714
+ zip_add_folders(&sArchive, zName);
715
+ zip_add_file(&sArchive, zName, &file, manifest_file_mperm(pFile));
421716
blob_reset(&file);
422717
}
423718
}
424719
}
425720
blob_reset(&mfile);
426721
manifest_destroy(pManifest);
427722
blob_reset(&filename);
428723
blob_reset(&hash);
429
- zip_close(pZip);
724
+ zip_close(&sArchive);
430725
}
431726
432727
/*
433
-** COMMAND: zip*
434
-**
435
-** Usage: %fossil zip VERSION OUTPUTFILE [OPTIONS]
436
-**
437
-** Generate a ZIP archive for a check-in. If the --name option is
438
-** used, its argument becomes the name of the top-level directory in the
439
-** resulting ZIP archive. If --name is omitted, the top-level directory
440
-** name is derived from the project name, the check-in date and time, and
441
-** the artifact ID of the check-in.
442
-**
443
-** The GLOBLIST argument to --exclude and --include can be a comma-separated
444
-** list of glob patterns, where each glob pattern may optionally be enclosed
445
-** in "..." or '...' so that it may contain commas. If a file matches both
446
-** --include and --exclude then it is excluded.
447
-**
448
-** Options:
449
-** -X|--exclude GLOBLIST Comma-separated list of GLOBs of files to exclude
450
-** --include GLOBLIST Comma-separated list of GLOBs of files to include
451
-** --name DIRECTORYNAME The name of the top-level directory in the archive
452
-** -R REPOSITORY Specify a Fossil repository
728
+** Implementation of zip_cmd and sqlar_cmd.
453729
*/
454
-void zip_cmd(void){
730
+static void archive_cmd(int eType){
455731
int rid;
456732
Blob zip;
457733
const char *zName;
458734
Glob *pInclude = 0;
459735
Glob *pExclude = 0;
460736
const char *zInclude;
461737
const char *zExclude;
738
+
462739
zName = find_option("name", 0, 1);
463740
zExclude = find_option("exclude", "X", 1);
464741
if( zExclude ) pExclude = glob_create(zExclude);
465742
zInclude = find_option("include", 0, 1);
466743
if( zInclude ) pInclude = glob_create(zInclude);
@@ -488,16 +765,68 @@
488765
" WHERE event.objid=%d"
489766
" AND blob.rid=%d",
490767
db_get("project-name", "unnamed"), rid, rid
491768
);
492769
}
493
- zip_of_checkin(rid, &zip, zName, pInclude, pExclude);
770
+ zip_of_checkin(eType, rid, &zip, zName, pInclude, pExclude);
494771
glob_free(pInclude);
495772
glob_free(pExclude);
496773
blob_write_to_file(&zip, g.argv[3]);
497774
blob_reset(&zip);
498775
}
776
+
777
+/*
778
+** COMMAND: zip*
779
+**
780
+** Usage: %fossil zip VERSION OUTPUTFILE [OPTIONS]
781
+**
782
+** Generate a ZIP archive for a check-in. If the --name option is
783
+** used, its argument becomes the name of the top-level directory in the
784
+** resulting ZIP archive. If --name is omitted, the top-level directory
785
+** name is derived from the project name, the check-in date and time, and
786
+** the artifact ID of the check-in.
787
+**
788
+** The GLOBLIST argument to --exclude and --include can be a comma-separated
789
+** list of glob patterns, where each glob pattern may optionally be enclosed
790
+** in "..." or '...' so that it may contain commas. If a file matches both
791
+** --include and --exclude then it is excluded.
792
+**
793
+** Options:
794
+** -X|--exclude GLOBLIST Comma-separated list of GLOBs of files to exclude
795
+** --include GLOBLIST Comma-separated list of GLOBs of files to include
796
+** --name DIRECTORYNAME The name of the top-level directory in the archive
797
+** -R REPOSITORY Specify a Fossil repository
798
+*/
799
+void zip_cmd(void){
800
+ archive_cmd(ARCHIVE_ZIP);
801
+}
802
+
803
+/*
804
+** COMMAND: sqlar*
805
+**
806
+** Usage: %fossil sqlar VERSION OUTPUTFILE [OPTIONS]
807
+**
808
+** Generate an SQLAR archive for a check-in. If the --name option is
809
+** used, its argument becomes the name of the top-level directory in the
810
+** resulting SQLAR archive. If --name is omitted, the top-level directory
811
+** name is derived from the project name, the check-in date and time, and
812
+** the artifact ID of the check-in.
813
+**
814
+** The GLOBLIST argument to --exclude and --include can be a comma-separated
815
+** list of glob patterns, where each glob pattern may optionally be enclosed
816
+** in "..." or '...' so that it may contain commas. If a file matches both
817
+** --include and --exclude then it is excluded.
818
+**
819
+** Options:
820
+** -X|--exclude GLOBLIST Comma-separated list of GLOBs of files to exclude
821
+** --include GLOBLIST Comma-separated list of GLOBs of files to include
822
+** --name DIRECTORYNAME The name of the top-level directory in the archive
823
+** -R REPOSITORY Specify a Fossil repository
824
+*/
825
+void sqlar_cmd(void){
826
+ archive_cmd(ARCHIVE_SQLAR);
827
+}
499828
500829
/*
501830
** WEBPAGE: zip
502831
** URL: /zip
503832
**
@@ -607,11 +936,11 @@
607936
style_footer();
608937
return;
609938
}
610939
blob_zero(&zip);
611940
if( cache_read(&zip, zKey)==0 ){
612
- zip_of_checkin(rid, &zip, zName, pInclude, pExclude);
941
+ zip_of_checkin(ARCHIVE_ZIP, rid, &zip, zName, pInclude, pExclude);
613942
cache_write(&zip, zKey);
614943
}
615944
glob_free(pInclude);
616945
glob_free(pExclude);
617946
fossil_free(zName);
618947
--- src/zip.c
+++ src/zip.c
@@ -13,11 +13,11 @@
13 ** [email protected]
14 ** http://www.hwaci.com/drh/
15 **
16 *******************************************************************************
17 **
18 ** This file contains code used to generate ZIP archives.
19 */
20 #include "config.h"
21 #include <assert.h>
22 #if defined(FOSSIL_ENABLE_MINIZ)
23 # define MINIZ_HEADER_FILE_ONLY
@@ -25,10 +25,16 @@
25 #else
26 # include <zlib.h>
27 #endif
28 #include "zip.h"
29
 
 
 
 
 
 
30 /*
31 ** Write a 16- or 32-bit integer as little-endian into the given buffer.
32 */
33 static void put16(char *z, int v){
34 z[0] = v & 0xff;
@@ -51,10 +57,158 @@
51 static int dosDate; /* DOS-format date */
52 static int unixTime; /* Seconds since 1970 */
53 static int nDir; /* Number of entries in azDir[] */
54 static char **azDir; /* Directory names already added to the archive */
55
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56 /*
57 ** Initialize a new ZIP archive.
58 */
59 void zip_open(void){
60 blob_zero(&body);
@@ -90,43 +244,22 @@
90 zip_set_timedate_from_str(zDate);
91 fossil_free(zDate);
92 unixTime = (rDate - 2440587.5)*86400.0;
93 }
94
95 /*
96 ** If the given filename includes one or more directory entries, make
97 ** sure the directories are already in the archive. If they are not
98 ** in the archive, add them.
99 */
100 void zip_add_folders(char *zName){
101 int i, c;
102 int j;
103 for(i=0; zName[i]; i++){
104 if( zName[i]=='/' ){
105 c = zName[i+1];
106 zName[i+1] = 0;
107 for(j=0; j<nDir; j++){
108 if( fossil_strcmp(zName, azDir[j])==0 ) break;
109 }
110 if( j>=nDir ){
111 nDir++;
112 azDir = fossil_realloc(azDir, sizeof(azDir[0])*nDir);
113 azDir[j] = mprintf("%s", zName);
114 zip_add_file(zName, 0, 0);
115 }
116 zName[i+1] = c;
117 }
118 }
119 }
120
121 /*
122 ** Append a single file to a growing ZIP archive.
123 **
124 ** pFile is the file to be appended. zName is the name
125 ** that the file should be saved as.
126 */
127 void zip_add_file(const char *zName, const Blob *pFile, int mPerm){
 
 
 
 
 
128 z_stream stream;
129 int nameLen;
130 int toOut = 0;
131 int iStart;
132 int iCRC = 0;
@@ -243,37 +376,189 @@
243 put16(&zExTime[2], 5);
244 blob_append(&toc, zExTime, 9);
245 nEntry++;
246 }
247
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
248
249 /*
250 ** Write the ZIP archive into the given BLOB.
251 */
252 void zip_close(Blob *pZip){
253 int iTocStart;
254 int iTocEnd;
255 int i;
256 char zBuf[30];
257
258 iTocStart = blob_size(&body);
259 blob_append(&body, blob_buffer(&toc), blob_size(&toc));
260 iTocEnd = blob_size(&body);
261
262 memset(zBuf, 0, sizeof(zBuf));
263 put32(&zBuf[0], 0x06054b50);
264 put16(&zBuf[4], 0);
265 put16(&zBuf[6], 0);
266 put16(&zBuf[8], nEntry);
267 put16(&zBuf[10], nEntry);
268 put32(&zBuf[12], iTocEnd - iTocStart);
269 put32(&zBuf[16], iTocStart);
270 put16(&zBuf[20], 0);
271 blob_append(&body, zBuf, 22);
272 blob_reset(&toc);
273 *pZip = body;
274 blob_zero(&body);
 
 
 
 
 
 
 
 
 
275 nEntry = 0;
276 for(i=0; i<nDir; i++){
277 fossil_free(azDir[i]);
278 }
279 fossil_free(azDir);
@@ -290,10 +575,14 @@
290 void filezip_cmd(void){
291 int i;
292 Blob zip;
293 Blob file;
294 int eFType = SymFILE;
 
 
 
 
295 if( g.argc<3 ){
296 usage("ARCHIVE FILE....");
297 }
298 if( find_option("dereference","h",0)!=0 ){
299 eFType = ExtFILE;
@@ -300,14 +589,14 @@
300 }
301 zip_open();
302 for(i=3; i<g.argc; i++){
303 blob_zero(&file);
304 blob_read_from_file(&file, g.argv[i], eFType);
305 zip_add_file(g.argv[i], &file, file_perm(0,0));
306 blob_reset(&file);
307 }
308 zip_close(&zip);
309 blob_write_to_file(&zip, g.argv[2]);
310 }
311
312 /*
313 ** Given the RID for a manifest, construct a ZIP archive containing
@@ -325,23 +614,29 @@
325 ** in which case it is ignored. The intention is to create a zip which
326 ** politely expands into a subdir instead of filling your current dir
327 ** with source files. For example, pass a UUID or "ProjectName".
328 **
329 */
330 void zip_of_checkin(
331 int rid, /* The RID of the checkin to construct the ZIP archive from */
332 Blob *pZip, /* Write the ZIP archive content into this blob */
333 const char *zDir, /* Top-level directory of the ZIP archive */
 
334 Glob *pInclude, /* Only include files that match this pattern */
335 Glob *pExclude /* Exclude files that match this pattern */
336 ){
337 Blob mfile, hash, file;
338 Manifest *pManifest;
339 ManifestFile *pFile;
340 Blob filename;
341 int nPrefix;
342
 
 
 
 
 
343 content_get(rid, &mfile);
344 if( blob_size(&mfile)==0 ){
345 blob_zero(pZip);
346 return;
347 }
@@ -379,31 +674,31 @@
379 }
380
381 if( eflg & MFESTFLG_RAW ){
382 blob_append(&filename, "manifest", -1);
383 zName = blob_str(&filename);
384 zip_add_folders(zName);
385 sterilize_manifest(&mfile);
386 zip_add_file(zName, &mfile, 0);
387 }
388 if( eflg & MFESTFLG_UUID ){
389 blob_append(&hash, "\n", 1);
390 blob_resize(&filename, nPrefix);
391 blob_append(&filename, "manifest.uuid", -1);
392 zName = blob_str(&filename);
393 zip_add_folders(zName);
394 zip_add_file(zName, &hash, 0);
395 }
396 if( eflg & MFESTFLG_TAGS ){
397 Blob tagslist;
398 blob_zero(&tagslist);
399 get_checkin_taglist(rid, &tagslist);
400 blob_resize(&filename, nPrefix);
401 blob_append(&filename, "manifest.tags", -1);
402 zName = blob_str(&filename);
403 zip_add_folders(zName);
404 zip_add_file(zName, &tagslist, 0);
405 blob_reset(&tagslist);
406 }
407 }
408 manifest_file_rewind(pManifest);
409 while( (pFile = manifest_file_next(pManifest,0))!=0 ){
@@ -414,53 +709,35 @@
414 if( fid ){
415 content_get(fid, &file);
416 blob_resize(&filename, nPrefix);
417 blob_append(&filename, pFile->zName, -1);
418 zName = blob_str(&filename);
419 zip_add_folders(zName);
420 zip_add_file(zName, &file, manifest_file_mperm(pFile));
421 blob_reset(&file);
422 }
423 }
424 }
425 blob_reset(&mfile);
426 manifest_destroy(pManifest);
427 blob_reset(&filename);
428 blob_reset(&hash);
429 zip_close(pZip);
430 }
431
432 /*
433 ** COMMAND: zip*
434 **
435 ** Usage: %fossil zip VERSION OUTPUTFILE [OPTIONS]
436 **
437 ** Generate a ZIP archive for a check-in. If the --name option is
438 ** used, its argument becomes the name of the top-level directory in the
439 ** resulting ZIP archive. If --name is omitted, the top-level directory
440 ** name is derived from the project name, the check-in date and time, and
441 ** the artifact ID of the check-in.
442 **
443 ** The GLOBLIST argument to --exclude and --include can be a comma-separated
444 ** list of glob patterns, where each glob pattern may optionally be enclosed
445 ** in "..." or '...' so that it may contain commas. If a file matches both
446 ** --include and --exclude then it is excluded.
447 **
448 ** Options:
449 ** -X|--exclude GLOBLIST Comma-separated list of GLOBs of files to exclude
450 ** --include GLOBLIST Comma-separated list of GLOBs of files to include
451 ** --name DIRECTORYNAME The name of the top-level directory in the archive
452 ** -R REPOSITORY Specify a Fossil repository
453 */
454 void zip_cmd(void){
455 int rid;
456 Blob zip;
457 const char *zName;
458 Glob *pInclude = 0;
459 Glob *pExclude = 0;
460 const char *zInclude;
461 const char *zExclude;
 
462 zName = find_option("name", 0, 1);
463 zExclude = find_option("exclude", "X", 1);
464 if( zExclude ) pExclude = glob_create(zExclude);
465 zInclude = find_option("include", 0, 1);
466 if( zInclude ) pInclude = glob_create(zInclude);
@@ -488,16 +765,68 @@
488 " WHERE event.objid=%d"
489 " AND blob.rid=%d",
490 db_get("project-name", "unnamed"), rid, rid
491 );
492 }
493 zip_of_checkin(rid, &zip, zName, pInclude, pExclude);
494 glob_free(pInclude);
495 glob_free(pExclude);
496 blob_write_to_file(&zip, g.argv[3]);
497 blob_reset(&zip);
498 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
499
500 /*
501 ** WEBPAGE: zip
502 ** URL: /zip
503 **
@@ -607,11 +936,11 @@
607 style_footer();
608 return;
609 }
610 blob_zero(&zip);
611 if( cache_read(&zip, zKey)==0 ){
612 zip_of_checkin(rid, &zip, zName, pInclude, pExclude);
613 cache_write(&zip, zKey);
614 }
615 glob_free(pInclude);
616 glob_free(pExclude);
617 fossil_free(zName);
618
--- src/zip.c
+++ src/zip.c
@@ -13,11 +13,11 @@
13 ** [email protected]
14 ** http://www.hwaci.com/drh/
15 **
16 *******************************************************************************
17 **
18 ** This file contains code used to generate ZIP and SQLAR archives.
19 */
20 #include "config.h"
21 #include <assert.h>
22 #if defined(FOSSIL_ENABLE_MINIZ)
23 # define MINIZ_HEADER_FILE_ONLY
@@ -25,10 +25,16 @@
25 #else
26 # include <zlib.h>
27 #endif
28 #include "zip.h"
29
30 /*
31 ** Type of archive to build.
32 */
33 #define ARCHIVE_ZIP 0
34 #define ARCHIVE_SQLAR 1
35
36 /*
37 ** Write a 16- or 32-bit integer as little-endian into the given buffer.
38 */
39 static void put16(char *z, int v){
40 z[0] = v & 0xff;
@@ -51,10 +57,158 @@
57 static int dosDate; /* DOS-format date */
58 static int unixTime; /* Seconds since 1970 */
59 static int nDir; /* Number of entries in azDir[] */
60 static char **azDir; /* Directory names already added to the archive */
61
62 typedef struct Archive Archive;
63 struct Archive {
64 int eType; /* Type of archive (SQLAR or ZIP) */
65 Blob *pBlob; /* Output blob */
66 Blob tmp; /* Blob used as temp space for compression */
67 sqlite3 *db; /* Db used to assemble sqlar archive */
68 sqlite3_stmt *pInsert; /* INSERT statement for SQLAR */
69 sqlite3_vfs vfs; /* VFS object */
70 };
71
72 /*
73 ** Ensure that blob pBlob is at least nMin bytes in size.
74 */
75 static void zip_blob_minsize(Blob *pBlob, int nMin){
76 if( blob_size(pBlob)<nMin ){
77 blob_resize(pBlob, nMin);
78 }
79 }
80
81 /*************************************************************************
82 ** Implementation of "archive" VFS. A VFS designed to store the contents
83 ** of a new database in a Blob. Used to construct sqlar archives in
84 ** memory.
85 */
86 typedef struct ArchiveFile ArchiveFile;
87 struct ArchiveFile {
88 sqlite3_file base; /* Base class */
89 Blob *pBlob;
90 };
91
92 static int archiveClose(sqlite3_file *pFile){
93 return SQLITE_OK;
94 }
95 static int archiveRead(
96 sqlite3_file *pFile, void *pBuf, int iAmt, sqlite3_int64 iOfst
97 ){
98 assert( iOfst==0 || iOfst==24 );
99 return SQLITE_IOERR_SHORT_READ;
100 }
101 static int archiveWrite(
102 sqlite3_file *pFile, const void *pBuf, int iAmt, sqlite3_int64 iOfst
103 ){
104 ArchiveFile *pAF = (ArchiveFile*)pFile;
105 int nMin = (int)iOfst + iAmt;
106 char *aBlob; /* Output buffer */
107
108 zip_blob_minsize(pAF->pBlob, nMin);
109 aBlob = blob_buffer(pAF->pBlob);
110 memcpy(&aBlob[iOfst], pBuf, iAmt);
111 return SQLITE_OK;
112 }
113 static int archiveTruncate(sqlite3_file *pFile, sqlite3_int64 size){
114 return SQLITE_OK;
115 }
116 static int archiveSync(sqlite3_file *pFile, int flags){
117 return SQLITE_OK;
118 }
119 static int archiveFileSize(sqlite3_file *pFile, sqlite3_int64 *pSize){
120 *pSize = 0;
121 return SQLITE_OK;
122 }
123 static int archiveLock(sqlite3_file *pFile, int eLock){
124 return SQLITE_OK;
125 }
126 static int archiveUnlock(sqlite3_file *pFile, int eLock){
127 return SQLITE_OK;
128 }
129 static int archiveCheckReservedLock(sqlite3_file *pFile, int *pResOut){
130 *pResOut = 0;
131 return SQLITE_OK;
132 }
133 static int archiveFileControl(sqlite3_file *pFile, int op, void *pArg){
134 if( op==SQLITE_FCNTL_SIZE_HINT ){
135 ArchiveFile *pAF = (ArchiveFile*)pFile;
136 zip_blob_minsize(pAF->pBlob, (int)(*(sqlite3_int64*)pArg));
137 }
138 return SQLITE_NOTFOUND;
139 }
140 static int archiveSectorSize(sqlite3_file *pFile){
141 return 512;
142 }
143 static int archiveDeviceCharacteristics(sqlite3_file *pFile){
144 return 0;
145 }
146
147 static int archiveOpen(
148 sqlite3_vfs *pVfs, const char *zName,
149 sqlite3_file *pFile, int flags, int *pOutFlags
150 ){
151 static struct sqlite3_io_methods methods = {
152 1, /* iVersion */
153 archiveClose,
154 archiveRead,
155 archiveWrite,
156 archiveTruncate,
157 archiveSync,
158 archiveFileSize,
159 archiveLock,
160 archiveUnlock,
161 archiveCheckReservedLock,
162 archiveFileControl,
163 archiveSectorSize,
164 archiveDeviceCharacteristics,
165 0, 0, 0, 0,
166 0, 0
167 };
168
169 ArchiveFile *pAF = (ArchiveFile*)pFile;
170 assert( flags & SQLITE_OPEN_MAIN_DB );
171
172 pAF->base.pMethods = &methods;
173 pAF->pBlob = (Blob*)pVfs->pAppData;
174
175 return SQLITE_OK;
176 }
177 static int archiveDelete(sqlite3_vfs *pVfs, const char *zName, int syncDir){
178 return SQLITE_OK;
179 }
180 static int archiveAccess(
181 sqlite3_vfs *pVfs, const char *zName, int flags, int *pResOut
182 ){
183 *pResOut = 0;
184 return SQLITE_OK;
185 }
186 static int archiveFullPathname(
187 sqlite3_vfs *pVfs, const char *zIn, int nOut, char *zOut
188 ){
189 int n = strlen(zIn);
190 memcpy(zOut, zIn, n+1);
191 return SQLITE_OK;
192 }
193 static int archiveRandomness(sqlite3_vfs *pVfs, int nByte, char *zOut){
194 memset(zOut, 0, nByte);
195 return SQLITE_OK;
196 }
197 static int archiveSleep(sqlite3_vfs *pVfs, int microseconds){
198 return SQLITE_OK;
199 }
200 static int archiveCurrentTime(sqlite3_vfs *pVfs, double *prOut){
201 return SQLITE_OK;
202 }
203 static int archiveGetLastError(sqlite3_vfs *pVfs, int nBuf, char *aBuf){
204 return SQLITE_OK;
205 }
206 /*
207 ** End of "archive" VFS.
208 *************************************************************************/
209
210 /*
211 ** Initialize a new ZIP archive.
212 */
213 void zip_open(void){
214 blob_zero(&body);
@@ -90,43 +244,22 @@
244 zip_set_timedate_from_str(zDate);
245 fossil_free(zDate);
246 unixTime = (rDate - 2440587.5)*86400.0;
247 }
248
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
249 /*
250 ** Append a single file to a growing ZIP archive.
251 **
252 ** pFile is the file to be appended. zName is the name
253 ** that the file should be saved as.
254 */
255 static void zip_add_file_to_zip(
256 Archive *p,
257 const char *zName,
258 const Blob *pFile,
259 int mPerm
260 ){
261 z_stream stream;
262 int nameLen;
263 int toOut = 0;
264 int iStart;
265 int iCRC = 0;
@@ -243,37 +376,189 @@
376 put16(&zExTime[2], 5);
377 blob_append(&toc, zExTime, 9);
378 nEntry++;
379 }
380
381 static void zip_add_file_to_sqlar(
382 Archive *p,
383 const char *zName,
384 const Blob *pFile,
385 int mPerm
386 ){
387 int nName = strlen(zName);
388
389 if( p->db==0 ){
390 assert( p->vfs.zName==0 );
391 p->vfs.zName = (const char*)mprintf("archivevfs%p", (void*)p);
392 p->vfs.iVersion = 1;
393 p->vfs.szOsFile = sizeof(ArchiveFile);
394 p->vfs.mxPathname = 512;
395 p->vfs.pAppData = (void*)p->pBlob;
396 p->vfs.xOpen = archiveOpen;
397 p->vfs.xDelete = archiveDelete;
398 p->vfs.xAccess = archiveAccess;
399 p->vfs.xFullPathname = archiveFullPathname;
400 p->vfs.xRandomness = archiveRandomness;
401 p->vfs.xSleep = archiveSleep;
402 p->vfs.xCurrentTime = archiveCurrentTime;
403 p->vfs.xGetLastError = archiveGetLastError;
404 sqlite3_vfs_register(&p->vfs, 0);
405 sqlite3_open_v2("file:xyz.db", &p->db,
406 SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE, p->vfs.zName
407 );
408 assert( p->db );
409 blob_zero(&p->tmp);
410 sqlite3_exec(p->db,
411 "PRAGMA journal_mode = off;"
412 "PRAGMA cache_spill = off;"
413 "BEGIN;"
414 "CREATE TABLE sqlar("
415 "name TEXT PRIMARY KEY, -- name of the file\n"
416 "mode INT, -- access permissions\n"
417 "mtime INT, -- last modification time\n"
418 "sz INT, -- original file size\n"
419 "data BLOB -- compressed content\n"
420 ");", 0, 0, 0
421 );
422 sqlite3_prepare(p->db,
423 "INSERT INTO sqlar VALUES(?, ?, ?, ?, ?)", -1,
424 &p->pInsert, 0
425 );
426 assert( p->pInsert );
427
428 sqlite3_bind_int64(p->pInsert, 3, unixTime);
429 blob_zero(p->pBlob);
430 }
431
432 if( pFile==0 ){
433 /* Directory. */
434 if( zName[nName-1]=='/' ) nName--;
435 sqlite3_bind_text(p->pInsert, 1, zName, nName, SQLITE_STATIC);
436 sqlite3_bind_int(p->pInsert, 2, 040755);
437 sqlite3_bind_int(p->pInsert, 4, 0);
438 sqlite3_bind_null(p->pInsert, 5);
439 }else{
440 sqlite3_bind_text(p->pInsert, 1, zName, nName, SQLITE_STATIC);
441 if( mPerm==PERM_LNK ){
442 sqlite3_bind_int(p->pInsert, 2, 0120755);
443 sqlite3_bind_int(p->pInsert, 4, -1);
444 sqlite3_bind_text(p->pInsert, 5,
445 blob_buffer(pFile), blob_size(pFile), SQLITE_STATIC
446 );
447 }else{
448 int nIn = blob_size(pFile);
449 unsigned long int nOut = nIn;
450 sqlite3_bind_int(p->pInsert, 2, mPerm==PERM_EXE ? 0100755 : 0100644);
451 sqlite3_bind_int(p->pInsert, 4, nIn);
452 zip_blob_minsize(&p->tmp, nIn);
453 compress(
454 blob_buffer(&p->tmp), &nOut, (unsigned char*)blob_buffer(pFile), nIn
455 );
456 if( nOut>=nIn ){
457 sqlite3_bind_blob(p->pInsert, 5,
458 blob_buffer(pFile), blob_size(pFile), SQLITE_STATIC
459 );
460 }else{
461 sqlite3_bind_blob(p->pInsert, 5,
462 blob_buffer(&p->tmp), nOut, SQLITE_STATIC
463 );
464 }
465 }
466 }
467
468 sqlite3_step(p->pInsert);
469 sqlite3_reset(p->pInsert);
470 }
471
472 static void zip_add_file(
473 Archive *p,
474 const char *zName,
475 const Blob *pFile,
476 int mPerm
477 ){
478 if( p->eType==ARCHIVE_ZIP ){
479 zip_add_file_to_zip(p, zName, pFile, mPerm);
480 }else{
481 zip_add_file_to_sqlar(p, zName, pFile, mPerm);
482 }
483 }
484
485 /*
486 ** If the given filename includes one or more directory entries, make
487 ** sure the directories are already in the archive. If they are not
488 ** in the archive, add them.
489 */
490 static void zip_add_folders(Archive *p, char *zName){
491 int i, c;
492 int j;
493 for(i=0; zName[i]; i++){
494 if( zName[i]=='/' ){
495 c = zName[i+1];
496 zName[i+1] = 0;
497 for(j=0; j<nDir; j++){
498 if( fossil_strcmp(zName, azDir[j])==0 ) break;
499 }
500 if( j>=nDir ){
501 nDir++;
502 azDir = fossil_realloc(azDir, sizeof(azDir[0])*nDir);
503 azDir[j] = mprintf("%s", zName);
504 zip_add_file(p, zName, 0, 0);
505 }
506 zName[i+1] = c;
507 }
508 }
509 }
510
511 /*
512 ** Free all the members of structure Archive allocated while processing
513 ** an SQLAR request.
514 */
515 static void free_archive(Archive *p){
516 if( p->vfs.zName ){
517 sqlite3_vfs_unregister(&p->vfs);
518 fossil_free((char*)p->vfs.zName);
519 p->vfs.zName = 0;
520 }
521 sqlite3_finalize(p->pInsert);
522 p->pInsert = 0;
523 sqlite3_close(p->db);
524 p->db = 0;
525 }
526
527 /*
528 ** Write the ZIP archive into the given BLOB.
529 */
530 static void zip_close(Archive *p){
 
 
531 int i;
532 if( p->eType==ARCHIVE_ZIP ){
533 int iTocStart;
534 int iTocEnd;
535 char zBuf[30];
536
537 iTocStart = blob_size(&body);
538 blob_append(&body, blob_buffer(&toc), blob_size(&toc));
539 iTocEnd = blob_size(&body);
540
541 memset(zBuf, 0, sizeof(zBuf));
542 put32(&zBuf[0], 0x06054b50);
543 put16(&zBuf[4], 0);
544 put16(&zBuf[6], 0);
545 put16(&zBuf[8], nEntry);
546 put16(&zBuf[10], nEntry);
547 put32(&zBuf[12], iTocEnd - iTocStart);
548 put32(&zBuf[16], iTocStart);
549 put16(&zBuf[20], 0);
550 blob_append(&body, zBuf, 22);
551 blob_reset(&toc);
552 *(p->pBlob) = body;
553 blob_zero(&body);
554 }else{
555 if( p->db ) sqlite3_exec(p->db, "COMMIT", 0, 0, 0);
556 free_archive(p);
557 blob_reset(&p->tmp);
558 }
559
560 nEntry = 0;
561 for(i=0; i<nDir; i++){
562 fossil_free(azDir[i]);
563 }
564 fossil_free(azDir);
@@ -290,10 +575,14 @@
575 void filezip_cmd(void){
576 int i;
577 Blob zip;
578 Blob file;
579 int eFType = SymFILE;
580 Archive sArchive;
581 memset(&sArchive, 0, sizeof(Archive));
582 sArchive.eType = ARCHIVE_ZIP;
583 sArchive.pBlob = &zip;
584 if( g.argc<3 ){
585 usage("ARCHIVE FILE....");
586 }
587 if( find_option("dereference","h",0)!=0 ){
588 eFType = ExtFILE;
@@ -300,14 +589,14 @@
589 }
590 zip_open();
591 for(i=3; i<g.argc; i++){
592 blob_zero(&file);
593 blob_read_from_file(&file, g.argv[i], eFType);
594 zip_add_file(&sArchive, g.argv[i], &file, file_perm(0,0));
595 blob_reset(&file);
596 }
597 zip_close(&sArchive);
598 blob_write_to_file(&zip, g.argv[2]);
599 }
600
601 /*
602 ** Given the RID for a manifest, construct a ZIP archive containing
@@ -325,23 +614,29 @@
614 ** in which case it is ignored. The intention is to create a zip which
615 ** politely expands into a subdir instead of filling your current dir
616 ** with source files. For example, pass a UUID or "ProjectName".
617 **
618 */
619 static void zip_of_checkin(
620 int eType, /* Type of archive (ZIP or SQLAR) */
621 int rid, /* The RID of the checkin to build the archive from */
622 Blob *pZip, /* Write the archive content into this blob */
623 const char *zDir, /* Top-level directory of the archive */
624 Glob *pInclude, /* Only include files that match this pattern */
625 Glob *pExclude /* Exclude files that match this pattern */
626 ){
627 Blob mfile, hash, file;
628 Manifest *pManifest;
629 ManifestFile *pFile;
630 Blob filename;
631 int nPrefix;
632
633 Archive sArchive;
634 memset(&sArchive, 0, sizeof(Archive));
635 sArchive.eType = eType;
636 sArchive.pBlob = pZip;
637
638 content_get(rid, &mfile);
639 if( blob_size(&mfile)==0 ){
640 blob_zero(pZip);
641 return;
642 }
@@ -379,31 +674,31 @@
674 }
675
676 if( eflg & MFESTFLG_RAW ){
677 blob_append(&filename, "manifest", -1);
678 zName = blob_str(&filename);
679 zip_add_folders(&sArchive, zName);
680 sterilize_manifest(&mfile);
681 zip_add_file(&sArchive, zName, &mfile, 0);
682 }
683 if( eflg & MFESTFLG_UUID ){
684 blob_append(&hash, "\n", 1);
685 blob_resize(&filename, nPrefix);
686 blob_append(&filename, "manifest.uuid", -1);
687 zName = blob_str(&filename);
688 zip_add_folders(&sArchive, zName);
689 zip_add_file(&sArchive, zName, &hash, 0);
690 }
691 if( eflg & MFESTFLG_TAGS ){
692 Blob tagslist;
693 blob_zero(&tagslist);
694 get_checkin_taglist(rid, &tagslist);
695 blob_resize(&filename, nPrefix);
696 blob_append(&filename, "manifest.tags", -1);
697 zName = blob_str(&filename);
698 zip_add_folders(&sArchive, zName);
699 zip_add_file(&sArchive, zName, &tagslist, 0);
700 blob_reset(&tagslist);
701 }
702 }
703 manifest_file_rewind(pManifest);
704 while( (pFile = manifest_file_next(pManifest,0))!=0 ){
@@ -414,53 +709,35 @@
709 if( fid ){
710 content_get(fid, &file);
711 blob_resize(&filename, nPrefix);
712 blob_append(&filename, pFile->zName, -1);
713 zName = blob_str(&filename);
714 zip_add_folders(&sArchive, zName);
715 zip_add_file(&sArchive, zName, &file, manifest_file_mperm(pFile));
716 blob_reset(&file);
717 }
718 }
719 }
720 blob_reset(&mfile);
721 manifest_destroy(pManifest);
722 blob_reset(&filename);
723 blob_reset(&hash);
724 zip_close(&sArchive);
725 }
726
727 /*
728 ** Implementation of zip_cmd and sqlar_cmd.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
729 */
730 static void archive_cmd(int eType){
731 int rid;
732 Blob zip;
733 const char *zName;
734 Glob *pInclude = 0;
735 Glob *pExclude = 0;
736 const char *zInclude;
737 const char *zExclude;
738
739 zName = find_option("name", 0, 1);
740 zExclude = find_option("exclude", "X", 1);
741 if( zExclude ) pExclude = glob_create(zExclude);
742 zInclude = find_option("include", 0, 1);
743 if( zInclude ) pInclude = glob_create(zInclude);
@@ -488,16 +765,68 @@
765 " WHERE event.objid=%d"
766 " AND blob.rid=%d",
767 db_get("project-name", "unnamed"), rid, rid
768 );
769 }
770 zip_of_checkin(eType, rid, &zip, zName, pInclude, pExclude);
771 glob_free(pInclude);
772 glob_free(pExclude);
773 blob_write_to_file(&zip, g.argv[3]);
774 blob_reset(&zip);
775 }
776
777 /*
778 ** COMMAND: zip*
779 **
780 ** Usage: %fossil zip VERSION OUTPUTFILE [OPTIONS]
781 **
782 ** Generate a ZIP archive for a check-in. If the --name option is
783 ** used, its argument becomes the name of the top-level directory in the
784 ** resulting ZIP archive. If --name is omitted, the top-level directory
785 ** name is derived from the project name, the check-in date and time, and
786 ** the artifact ID of the check-in.
787 **
788 ** The GLOBLIST argument to --exclude and --include can be a comma-separated
789 ** list of glob patterns, where each glob pattern may optionally be enclosed
790 ** in "..." or '...' so that it may contain commas. If a file matches both
791 ** --include and --exclude then it is excluded.
792 **
793 ** Options:
794 ** -X|--exclude GLOBLIST Comma-separated list of GLOBs of files to exclude
795 ** --include GLOBLIST Comma-separated list of GLOBs of files to include
796 ** --name DIRECTORYNAME The name of the top-level directory in the archive
797 ** -R REPOSITORY Specify a Fossil repository
798 */
799 void zip_cmd(void){
800 archive_cmd(ARCHIVE_ZIP);
801 }
802
803 /*
804 ** COMMAND: sqlar*
805 **
806 ** Usage: %fossil sqlar VERSION OUTPUTFILE [OPTIONS]
807 **
808 ** Generate an SQLAR archive for a check-in. If the --name option is
809 ** used, its argument becomes the name of the top-level directory in the
810 ** resulting SQLAR archive. If --name is omitted, the top-level directory
811 ** name is derived from the project name, the check-in date and time, and
812 ** the artifact ID of the check-in.
813 **
814 ** The GLOBLIST argument to --exclude and --include can be a comma-separated
815 ** list of glob patterns, where each glob pattern may optionally be enclosed
816 ** in "..." or '...' so that it may contain commas. If a file matches both
817 ** --include and --exclude then it is excluded.
818 **
819 ** Options:
820 ** -X|--exclude GLOBLIST Comma-separated list of GLOBs of files to exclude
821 ** --include GLOBLIST Comma-separated list of GLOBs of files to include
822 ** --name DIRECTORYNAME The name of the top-level directory in the archive
823 ** -R REPOSITORY Specify a Fossil repository
824 */
825 void sqlar_cmd(void){
826 archive_cmd(ARCHIVE_SQLAR);
827 }
828
829 /*
830 ** WEBPAGE: zip
831 ** URL: /zip
832 **
@@ -607,11 +936,11 @@
936 style_footer();
937 return;
938 }
939 blob_zero(&zip);
940 if( cache_read(&zip, zKey)==0 ){
941 zip_of_checkin(ARCHIVE_ZIP, rid, &zip, zName, pInclude, pExclude);
942 cache_write(&zip, zKey);
943 }
944 glob_free(pInclude);
945 glob_free(pExclude);
946 fossil_free(zName);
947

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button