Fossil SCM

fossil-scm / src / zip.c
Source Blame History 1144 lines
9b780d2… drh 1 /*
c19f34c… drh 2 ** Copyright (c) 2007 D. Richard Hipp
9b780d2… drh 3 **
9b780d2… drh 4 ** This program is free software; you can redistribute it and/or
c06edd2… drh 5 ** modify it under the terms of the Simplified BSD License (also
c06edd2… drh 6 ** known as the "2-Clause License" or "FreeBSD License".)
c06edd2… drh 7
9b780d2… drh 8 ** This program is distributed in the hope that it will be useful,
c06edd2… drh 9 ** but without any warranty; without even the implied warranty of
c06edd2… drh 10 ** merchantability or fitness for a particular purpose.
9b780d2… drh 11 **
9b780d2… drh 12 ** Author contact information:
9b780d2… drh 13 ** [email protected]
9b780d2… drh 14 ** http://www.hwaci.com/drh/
9b780d2… drh 15 **
9b780d2… drh 16 *******************************************************************************
9b780d2… drh 17 **
7eb5b0a… dan 18 ** This file contains code used to generate ZIP and SQLAR archives.
9b780d2… drh 19 */
c30cd93… jan.nijtmans 20 #include "config.h"
9b780d2… drh 21 #include <assert.h>
f9c2d23… stephan 22 #include <zlib.h>
9b780d2… drh 23 #include "zip.h"
7eb5b0a… dan 24
7eb5b0a… dan 25 /*
7eb5b0a… dan 26 ** Type of archive to build.
7eb5b0a… dan 27 */
7eb5b0a… dan 28 #define ARCHIVE_ZIP 0
7eb5b0a… dan 29 #define ARCHIVE_SQLAR 1
9b780d2… drh 30
9b780d2… drh 31 /*
9b780d2… drh 32 ** Write a 16- or 32-bit integer as little-endian into the given buffer.
9b780d2… drh 33 */
9b780d2… drh 34 static void put16(char *z, int v){
9b780d2… drh 35 z[0] = v & 0xff;
9b780d2… drh 36 z[1] = (v>>8) & 0xff;
9b780d2… drh 37 }
9b780d2… drh 38 static void put32(char *z, int v){
9b780d2… drh 39 z[0] = v & 0xff;
9b780d2… drh 40 z[1] = (v>>8) & 0xff;
9b780d2… drh 41 z[2] = (v>>16) & 0xff;
9b780d2… drh 42 z[3] = (v>>24) & 0xff;
9b780d2… drh 43 }
9b780d2… drh 44
9b780d2… drh 45 /*
9b780d2… drh 46 ** Variables in which to accumulate a growing ZIP archive.
9b780d2… drh 47 */
9b780d2… drh 48 static Blob body; /* The body of the ZIP archive */
9b780d2… drh 49 static Blob toc; /* The table of contents */
9b780d2… drh 50 static int nEntry; /* Number of files */
dead090… drh 51 static int dosTime; /* DOS-format time */
dead090… drh 52 static int dosDate; /* DOS-format date */
90048e0… drh 53 static int unixTime; /* Seconds since 1970 */
90048e0… drh 54 static int nDir; /* Number of entries in azDir[] */
90048e0… drh 55 static char **azDir; /* Directory names already added to the archive */
7eb5b0a… dan 56
7eb5b0a… dan 57 typedef struct Archive Archive;
7eb5b0a… dan 58 struct Archive {
7eb5b0a… dan 59 int eType; /* Type of archive (SQLAR or ZIP) */
7eb5b0a… dan 60 Blob *pBlob; /* Output blob */
7eb5b0a… dan 61 Blob tmp; /* Blob used as temp space for compression */
7eb5b0a… dan 62 sqlite3 *db; /* Db used to assemble sqlar archive */
7eb5b0a… dan 63 sqlite3_stmt *pInsert; /* INSERT statement for SQLAR */
7eb5b0a… dan 64 sqlite3_vfs vfs; /* VFS object */
7eb5b0a… dan 65 };
7eb5b0a… dan 66
7eb5b0a… dan 67 /*
7eb5b0a… dan 68 ** Ensure that blob pBlob is at least nMin bytes in size.
7eb5b0a… dan 69 */
7eb5b0a… dan 70 static void zip_blob_minsize(Blob *pBlob, int nMin){
53db40e… drh 71 if( (int)blob_size(pBlob)<nMin ){
7eb5b0a… dan 72 blob_resize(pBlob, nMin);
7eb5b0a… dan 73 }
7eb5b0a… dan 74 }
7eb5b0a… dan 75
7eb5b0a… dan 76 /*************************************************************************
7eb5b0a… dan 77 ** Implementation of "archive" VFS. A VFS designed to store the contents
7eb5b0a… dan 78 ** of a new database in a Blob. Used to construct sqlar archives in
7eb5b0a… dan 79 ** memory.
7eb5b0a… dan 80 */
7eb5b0a… dan 81 typedef struct ArchiveFile ArchiveFile;
7eb5b0a… dan 82 struct ArchiveFile {
7eb5b0a… dan 83 sqlite3_file base; /* Base class */
7eb5b0a… dan 84 Blob *pBlob;
7eb5b0a… dan 85 };
7eb5b0a… dan 86
7eb5b0a… dan 87 static int archiveClose(sqlite3_file *pFile){
7eb5b0a… dan 88 return SQLITE_OK;
7eb5b0a… dan 89 }
7eb5b0a… dan 90 static int archiveRead(
7eb5b0a… dan 91 sqlite3_file *pFile, void *pBuf, int iAmt, sqlite3_int64 iOfst
7eb5b0a… dan 92 ){
7eb5b0a… dan 93 assert( iOfst==0 || iOfst==24 );
7eb5b0a… dan 94 return SQLITE_IOERR_SHORT_READ;
7eb5b0a… dan 95 }
7eb5b0a… dan 96 static int archiveWrite(
7eb5b0a… dan 97 sqlite3_file *pFile, const void *pBuf, int iAmt, sqlite3_int64 iOfst
7eb5b0a… dan 98 ){
7eb5b0a… dan 99 ArchiveFile *pAF = (ArchiveFile*)pFile;
7eb5b0a… dan 100 int nMin = (int)iOfst + iAmt;
7eb5b0a… dan 101 char *aBlob; /* Output buffer */
7eb5b0a… dan 102
7eb5b0a… dan 103 zip_blob_minsize(pAF->pBlob, nMin);
7eb5b0a… dan 104 aBlob = blob_buffer(pAF->pBlob);
7eb5b0a… dan 105 memcpy(&aBlob[iOfst], pBuf, iAmt);
7eb5b0a… dan 106 return SQLITE_OK;
7eb5b0a… dan 107 }
7eb5b0a… dan 108 static int archiveTruncate(sqlite3_file *pFile, sqlite3_int64 size){
7eb5b0a… dan 109 return SQLITE_OK;
7eb5b0a… dan 110 }
7eb5b0a… dan 111 static int archiveSync(sqlite3_file *pFile, int flags){
7eb5b0a… dan 112 return SQLITE_OK;
7eb5b0a… dan 113 }
7eb5b0a… dan 114 static int archiveFileSize(sqlite3_file *pFile, sqlite3_int64 *pSize){
7eb5b0a… dan 115 *pSize = 0;
7eb5b0a… dan 116 return SQLITE_OK;
7eb5b0a… dan 117 }
7eb5b0a… dan 118 static int archiveLock(sqlite3_file *pFile, int eLock){
7eb5b0a… dan 119 return SQLITE_OK;
7eb5b0a… dan 120 }
7eb5b0a… dan 121 static int archiveUnlock(sqlite3_file *pFile, int eLock){
7eb5b0a… dan 122 return SQLITE_OK;
7eb5b0a… dan 123 }
7eb5b0a… dan 124 static int archiveCheckReservedLock(sqlite3_file *pFile, int *pResOut){
7eb5b0a… dan 125 *pResOut = 0;
7eb5b0a… dan 126 return SQLITE_OK;
7eb5b0a… dan 127 }
7eb5b0a… dan 128 static int archiveFileControl(sqlite3_file *pFile, int op, void *pArg){
7eb5b0a… dan 129 if( op==SQLITE_FCNTL_SIZE_HINT ){
7eb5b0a… dan 130 ArchiveFile *pAF = (ArchiveFile*)pFile;
7eb5b0a… dan 131 zip_blob_minsize(pAF->pBlob, (int)(*(sqlite3_int64*)pArg));
7eb5b0a… dan 132 }
7eb5b0a… dan 133 return SQLITE_NOTFOUND;
7eb5b0a… dan 134 }
7eb5b0a… dan 135 static int archiveSectorSize(sqlite3_file *pFile){
7eb5b0a… dan 136 return 512;
7eb5b0a… dan 137 }
7eb5b0a… dan 138 static int archiveDeviceCharacteristics(sqlite3_file *pFile){
7eb5b0a… dan 139 return 0;
7eb5b0a… dan 140 }
7eb5b0a… dan 141
7eb5b0a… dan 142 static int archiveOpen(
275da70… danield 143 sqlite3_vfs *pVfs, const char *zName,
7eb5b0a… dan 144 sqlite3_file *pFile, int flags, int *pOutFlags
7eb5b0a… dan 145 ){
7eb5b0a… dan 146 static struct sqlite3_io_methods methods = {
7eb5b0a… dan 147 1, /* iVersion */
7eb5b0a… dan 148 archiveClose,
7eb5b0a… dan 149 archiveRead,
7eb5b0a… dan 150 archiveWrite,
7eb5b0a… dan 151 archiveTruncate,
7eb5b0a… dan 152 archiveSync,
7eb5b0a… dan 153 archiveFileSize,
7eb5b0a… dan 154 archiveLock,
7eb5b0a… dan 155 archiveUnlock,
7eb5b0a… dan 156 archiveCheckReservedLock,
7eb5b0a… dan 157 archiveFileControl,
7eb5b0a… dan 158 archiveSectorSize,
7eb5b0a… dan 159 archiveDeviceCharacteristics,
7eb5b0a… dan 160 0, 0, 0, 0,
7eb5b0a… dan 161 0, 0
7eb5b0a… dan 162 };
7eb5b0a… dan 163
7eb5b0a… dan 164 ArchiveFile *pAF = (ArchiveFile*)pFile;
7eb5b0a… dan 165 assert( flags & SQLITE_OPEN_MAIN_DB );
7eb5b0a… dan 166
7eb5b0a… dan 167 pAF->base.pMethods = &methods;
7eb5b0a… dan 168 pAF->pBlob = (Blob*)pVfs->pAppData;
7eb5b0a… dan 169
7eb5b0a… dan 170 return SQLITE_OK;
7eb5b0a… dan 171 }
7eb5b0a… dan 172 static int archiveDelete(sqlite3_vfs *pVfs, const char *zName, int syncDir){
7eb5b0a… dan 173 return SQLITE_OK;
7eb5b0a… dan 174 }
7eb5b0a… dan 175 static int archiveAccess(
7eb5b0a… dan 176 sqlite3_vfs *pVfs, const char *zName, int flags, int *pResOut
7eb5b0a… dan 177 ){
7eb5b0a… dan 178 *pResOut = 0;
7eb5b0a… dan 179 return SQLITE_OK;
7eb5b0a… dan 180 }
7eb5b0a… dan 181 static int archiveFullPathname(
7eb5b0a… dan 182 sqlite3_vfs *pVfs, const char *zIn, int nOut, char *zOut
7eb5b0a… dan 183 ){
7eb5b0a… dan 184 int n = strlen(zIn);
7eb5b0a… dan 185 memcpy(zOut, zIn, n+1);
7eb5b0a… dan 186 return SQLITE_OK;
7eb5b0a… dan 187 }
7eb5b0a… dan 188 static int archiveRandomness(sqlite3_vfs *pVfs, int nByte, char *zOut){
7eb5b0a… dan 189 memset(zOut, 0, nByte);
7eb5b0a… dan 190 return SQLITE_OK;
7eb5b0a… dan 191 }
7eb5b0a… dan 192 static int archiveSleep(sqlite3_vfs *pVfs, int microseconds){
7eb5b0a… dan 193 return SQLITE_OK;
7eb5b0a… dan 194 }
7eb5b0a… dan 195 static int archiveCurrentTime(sqlite3_vfs *pVfs, double *prOut){
7eb5b0a… dan 196 return SQLITE_OK;
7eb5b0a… dan 197 }
7eb5b0a… dan 198 static int archiveGetLastError(sqlite3_vfs *pVfs, int nBuf, char *aBuf){
7eb5b0a… dan 199 return SQLITE_OK;
7eb5b0a… dan 200 }
7eb5b0a… dan 201 /*
7eb5b0a… dan 202 ** End of "archive" VFS.
7eb5b0a… dan 203 *************************************************************************/
9b780d2… drh 204
9b780d2… drh 205 /*
9b780d2… drh 206 ** Initialize a new ZIP archive.
9b780d2… drh 207 */
9b780d2… drh 208 void zip_open(void){
9b780d2… drh 209 blob_zero(&body);
9b780d2… drh 210 blob_zero(&toc);
9b780d2… drh 211 nEntry = 0;
dead090… drh 212 dosTime = 0;
dead090… drh 213 dosDate = 0;
90048e0… drh 214 unixTime = 0;
dead090… drh 215 }
dead090… drh 216
dead090… drh 217 /*
dead090… drh 218 ** Set the date and time values from an ISO8601 date string.
dead090… drh 219 */
dead090… drh 220 void zip_set_timedate_from_str(const char *zDate){
dead090… drh 221 int y, m, d;
dead090… drh 222 int H, M, S;
dead090… drh 223
dead090… drh 224 y = atoi(zDate);
dead090… drh 225 m = atoi(&zDate[5]);
dead090… drh 226 d = atoi(&zDate[8]);
dead090… drh 227 H = atoi(&zDate[11]);
dead090… drh 228 M = atoi(&zDate[14]);
dead090… drh 229 S = atoi(&zDate[17]);
39bbc60… drh 230 dosTime = (H<<11) + (M<<5) + (S>>1);
dead090… drh 231 dosDate = ((y-1980)<<9) + (m<<5) + d;
dead090… drh 232 }
dead090… drh 233
dead090… drh 234 /*
e2bdc10… danield 235 ** Set the date and time from a Julian day number.
dead090… drh 236 */
dead090… drh 237 void zip_set_timedate(double rDate){
dead090… drh 238 char *zDate = db_text(0, "SELECT datetime(%.17g)", rDate);
dead090… drh 239 zip_set_timedate_from_str(zDate);
8206ac9… stephan 240 fossil_free(zDate);
57f16ce… drh 241 unixTime = (int)((rDate - 2440587.5)*86400.0);
9b780d2… drh 242 }
9b780d2… drh 243
9b780d2… drh 244 /*
9b780d2… drh 245 ** Append a single file to a growing ZIP archive.
9b780d2… drh 246 **
9b780d2… drh 247 ** pFile is the file to be appended. zName is the name
9b780d2… drh 248 ** that the file should be saved as.
9b780d2… drh 249 */
7eb5b0a… dan 250 static void zip_add_file_to_zip(
7eb5b0a… dan 251 Archive *p,
275da70… danield 252 const char *zName,
275da70… danield 253 const Blob *pFile,
7eb5b0a… dan 254 int mPerm
7eb5b0a… dan 255 ){
9b780d2… drh 256 z_stream stream;
9b780d2… drh 257 int nameLen;
d78835d… drh 258 int toOut = 0;
9b780d2… drh 259 int iStart;
1bf6686… stephan 260 unsigned long iCRC = 0;
90048e0… drh 261 int nByte = 0;
90048e0… drh 262 int nByteCompr = 0;
90048e0… drh 263 int nBlob; /* Size of the blob */
90048e0… drh 264 int iMethod; /* Compression method. */
90048e0… drh 265 int iMode = 0644; /* Access permissions */
9b780d2… drh 266 char *z;
9b780d2… drh 267 char zHdr[30];
90048e0… drh 268 char zExTime[13];
9b780d2… drh 269 char zBuf[100];
9b780d2… drh 270 char zOutBuf[100000];
9b780d2… drh 271
e2bdc10… danield 272 /* Fill inasmuch of the header as we know.
9b780d2… drh 273 */
31b9822… drh 274 nameLen = (int)strlen(zName);
31b9822… drh 275 if( nameLen==0 ) return;
90048e0… drh 276 nBlob = pFile ? blob_size(pFile) : 0;
3d5cf48… mistachkin 277 if( pFile ){ /* This is a file, possibly empty... */
ecedaf9… mistachkin 278 iMethod = (nBlob>0) ? 8 : 0; /* Cannot compress zero bytes. */
e4f1c1f… drh 279 switch( mPerm ){
e4f1c1f… drh 280 case PERM_LNK: iMode = 0120755; break;
e4f1c1f… drh 281 case PERM_EXE: iMode = 0100755; break;
e4f1c1f… drh 282 default: iMode = 0100644; break;
e4f1c1f… drh 283 }
3d5cf48… mistachkin 284 }else{ /* This is a directory, no blob... */
90048e0… drh 285 iMethod = 0;
90048e0… drh 286 iMode = 040755;
90048e0… drh 287 }
90048e0… drh 288 memset(zHdr, 0, sizeof(zHdr));
9b780d2… drh 289 put32(&zHdr[0], 0x04034b50);
90048e0… drh 290 put16(&zHdr[4], 0x000a);
3ff5ca0… drh 291 put16(&zHdr[6], 0x0800);
90048e0… drh 292 put16(&zHdr[8], iMethod);
dead090… drh 293 put16(&zHdr[10], dosTime);
dead090… drh 294 put16(&zHdr[12], dosDate);
9b780d2… drh 295 put16(&zHdr[26], nameLen);
90048e0… drh 296 put16(&zHdr[28], 13);
52b35c8… jan.nijtmans 297
90048e0… drh 298 put16(&zExTime[0], 0x5455);
90048e0… drh 299 put16(&zExTime[2], 9);
90048e0… drh 300 zExTime[4] = 3;
90048e0… drh 301 put32(&zExTime[5], unixTime);
90048e0… drh 302 put32(&zExTime[9], unixTime);
52b35c8… jan.nijtmans 303
9b780d2… drh 304
9b780d2… drh 305 /* Write the header and filename.
9b780d2… drh 306 */
9b780d2… drh 307 iStart = blob_size(&body);
9b780d2… drh 308 blob_append(&body, zHdr, 30);
9b780d2… drh 309 blob_append(&body, zName, nameLen);
90048e0… drh 310 blob_append(&body, zExTime, 13);
90048e0… drh 311
90048e0… drh 312 if( nBlob>0 ){
90048e0… drh 313 /* Write the compressed file. Compute the CRC as we progress.
90048e0… drh 314 */
90048e0… drh 315 stream.zalloc = (alloc_func)0;
90048e0… drh 316 stream.zfree = (free_func)0;
90048e0… drh 317 stream.opaque = 0;
90048e0… drh 318 stream.avail_in = blob_size(pFile);
90048e0… drh 319 stream.next_in = (unsigned char*)blob_buffer(pFile);
90048e0… drh 320 stream.avail_out = sizeof(zOutBuf);
90048e0… drh 321 stream.next_out = (unsigned char*)zOutBuf;
d78835d… drh 322 deflateInit2(&stream, 9, Z_DEFLATED, -MAX_WBITS, 8, Z_DEFAULT_STRATEGY);
90048e0… drh 323 iCRC = crc32(0, stream.next_in, stream.avail_in);
90048e0… drh 324 while( stream.avail_in>0 ){
90048e0… drh 325 deflate(&stream, 0);
90048e0… drh 326 toOut = sizeof(zOutBuf) - stream.avail_out;
d78835d… drh 327 blob_append(&body, zOutBuf, toOut);
90048e0… drh 328 stream.avail_out = sizeof(zOutBuf);
90048e0… drh 329 stream.next_out = (unsigned char*)zOutBuf;
90048e0… drh 330 }
90048e0… drh 331 do{
90048e0… drh 332 stream.avail_out = sizeof(zOutBuf);
90048e0… drh 333 stream.next_out = (unsigned char*)zOutBuf;
90048e0… drh 334 deflate(&stream, Z_FINISH);
90048e0… drh 335 toOut = sizeof(zOutBuf) - stream.avail_out;
d78835d… drh 336 blob_append(&body, zOutBuf, toOut);
90048e0… drh 337 }while( stream.avail_out==0 );
90048e0… drh 338 nByte = stream.total_in;
d78835d… drh 339 nByteCompr = stream.total_out;
90048e0… drh 340 deflateEnd(&stream);
52b35c8… jan.nijtmans 341
90048e0… drh 342 /* Go back and write the header, now that we know the compressed file size.
90048e0… drh 343 */
90048e0… drh 344 z = &blob_buffer(&body)[iStart];
90048e0… drh 345 put32(&z[14], iCRC);
90048e0… drh 346 put32(&z[18], nByteCompr);
90048e0… drh 347 put32(&z[22], nByte);
90048e0… drh 348 }
52b35c8… jan.nijtmans 349
9b780d2… drh 350 /* Make an entry in the tables of contents
9b780d2… drh 351 */
9b780d2… drh 352 memset(zBuf, 0, sizeof(zBuf));
9b780d2… drh 353 put32(&zBuf[0], 0x02014b50);
9b780d2… drh 354 put16(&zBuf[4], 0x0317);
90048e0… drh 355 put16(&zBuf[6], 0x000a);
3ff5ca0… drh 356 put16(&zBuf[8], 0x0800);
90048e0… drh 357 put16(&zBuf[10], iMethod);
dead090… drh 358 put16(&zBuf[12], dosTime);
dead090… drh 359 put16(&zBuf[14], dosDate);
9b780d2… drh 360 put32(&zBuf[16], iCRC);
9b780d2… drh 361 put32(&zBuf[20], nByteCompr);
9b780d2… drh 362 put32(&zBuf[24], nByte);
9b780d2… drh 363 put16(&zBuf[28], nameLen);
90048e0… drh 364 put16(&zBuf[30], 9);
9b780d2… drh 365 put16(&zBuf[32], 0);
deb6f2a… drh 366 put16(&zBuf[34], 0);
9b780d2… drh 367 put16(&zBuf[36], 0);
90048e0… drh 368 put32(&zBuf[38], ((unsigned)iMode)<<16);
9b780d2… drh 369 put32(&zBuf[42], iStart);
9b780d2… drh 370 blob_append(&toc, zBuf, 46);
9b780d2… drh 371 blob_append(&toc, zName, nameLen);
90048e0… drh 372 put16(&zExTime[2], 5);
90048e0… drh 373 blob_append(&toc, zExTime, 9);
9b780d2… drh 374 nEntry++;
9b780d2… drh 375 }
9b780d2… drh 376
7eb5b0a… dan 377 static void zip_add_file_to_sqlar(
7eb5b0a… dan 378 Archive *p,
275da70… danield 379 const char *zName,
275da70… danield 380 const Blob *pFile,
7eb5b0a… dan 381 int mPerm
7eb5b0a… dan 382 ){
31b9822… drh 383 int nName = (int)strlen(zName);
7eb5b0a… dan 384
7eb5b0a… dan 385 if( p->db==0 ){
7eb5b0a… dan 386 assert( p->vfs.zName==0 );
7eb5b0a… dan 387 p->vfs.zName = (const char*)mprintf("archivevfs%p", (void*)p);
7eb5b0a… dan 388 p->vfs.iVersion = 1;
7eb5b0a… dan 389 p->vfs.szOsFile = sizeof(ArchiveFile);
7eb5b0a… dan 390 p->vfs.mxPathname = 512;
7eb5b0a… dan 391 p->vfs.pAppData = (void*)p->pBlob;
7eb5b0a… dan 392 p->vfs.xOpen = archiveOpen;
7eb5b0a… dan 393 p->vfs.xDelete = archiveDelete;
7eb5b0a… dan 394 p->vfs.xAccess = archiveAccess;
7eb5b0a… dan 395 p->vfs.xFullPathname = archiveFullPathname;
7eb5b0a… dan 396 p->vfs.xRandomness = archiveRandomness;
7eb5b0a… dan 397 p->vfs.xSleep = archiveSleep;
7eb5b0a… dan 398 p->vfs.xCurrentTime = archiveCurrentTime;
7eb5b0a… dan 399 p->vfs.xGetLastError = archiveGetLastError;
7eb5b0a… dan 400 sqlite3_vfs_register(&p->vfs, 0);
275da70… danield 401 sqlite3_open_v2("file:xyz.db", &p->db,
7eb5b0a… dan 402 SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE, p->vfs.zName
7eb5b0a… dan 403 );
7eb5b0a… dan 404 assert( p->db );
7eb5b0a… dan 405 blob_zero(&p->tmp);
275da70… danield 406 sqlite3_exec(p->db,
f5c81a6… drh 407 "PRAGMA page_size=512;"
7eb5b0a… dan 408 "PRAGMA journal_mode = off;"
7eb5b0a… dan 409 "PRAGMA cache_spill = off;"
7eb5b0a… dan 410 "BEGIN;"
2f9920a… drh 411 "CREATE TABLE sqlar(\n"
2f9920a… drh 412 " name TEXT PRIMARY KEY, -- name of the file\n"
2f9920a… drh 413 " mode INT, -- access permissions\n"
2f9920a… drh 414 " mtime INT, -- last modification time\n"
2f9920a… drh 415 " sz INT, -- original file size\n"
2f9920a… drh 416 " data BLOB -- compressed content\n"
7eb5b0a… dan 417 ");", 0, 0, 0
7eb5b0a… dan 418 );
275da70… danield 419 sqlite3_prepare(p->db,
275da70… danield 420 "INSERT INTO sqlar VALUES(?, ?, ?, ?, ?)", -1,
7eb5b0a… dan 421 &p->pInsert, 0
7eb5b0a… dan 422 );
7eb5b0a… dan 423 assert( p->pInsert );
7eb5b0a… dan 424
7eb5b0a… dan 425 sqlite3_bind_int64(p->pInsert, 3, unixTime);
7eb5b0a… dan 426 blob_zero(p->pBlob);
7eb5b0a… dan 427 }
7eb5b0a… dan 428
31b9822… drh 429 if( nName==0 ) return;
7eb5b0a… dan 430 if( pFile==0 ){
7eb5b0a… dan 431 /* Directory. */
7eb5b0a… dan 432 if( zName[nName-1]=='/' ) nName--;
7eb5b0a… dan 433 sqlite3_bind_text(p->pInsert, 1, zName, nName, SQLITE_STATIC);
7eb5b0a… dan 434 sqlite3_bind_int(p->pInsert, 2, 040755);
7eb5b0a… dan 435 sqlite3_bind_int(p->pInsert, 4, 0);
7eb5b0a… dan 436 sqlite3_bind_null(p->pInsert, 5);
7eb5b0a… dan 437 }else{
7eb5b0a… dan 438 sqlite3_bind_text(p->pInsert, 1, zName, nName, SQLITE_STATIC);
7eb5b0a… dan 439 if( mPerm==PERM_LNK ){
7eb5b0a… dan 440 sqlite3_bind_int(p->pInsert, 2, 0120755);
7eb5b0a… dan 441 sqlite3_bind_int(p->pInsert, 4, -1);
275da70… danield 442 sqlite3_bind_text(p->pInsert, 5,
7eb5b0a… dan 443 blob_buffer(pFile), blob_size(pFile), SQLITE_STATIC
7eb5b0a… dan 444 );
7eb5b0a… dan 445 }else{
53db40e… drh 446 unsigned int nIn = blob_size(pFile);
7eb5b0a… dan 447 unsigned long int nOut = nIn;
7eb5b0a… dan 448 sqlite3_bind_int(p->pInsert, 2, mPerm==PERM_EXE ? 0100755 : 0100644);
7eb5b0a… dan 449 sqlite3_bind_int(p->pInsert, 4, nIn);
7eb5b0a… dan 450 zip_blob_minsize(&p->tmp, nIn);
3f2cde2… drh 451 compress( (unsigned char*)
7eb5b0a… dan 452 blob_buffer(&p->tmp), &nOut, (unsigned char*)blob_buffer(pFile), nIn
7eb5b0a… dan 453 );
53db40e… drh 454 if( nOut>=(unsigned long)nIn ){
275da70… danield 455 sqlite3_bind_blob(p->pInsert, 5,
7eb5b0a… dan 456 blob_buffer(pFile), blob_size(pFile), SQLITE_STATIC
7eb5b0a… dan 457 );
7eb5b0a… dan 458 }else{
275da70… danield 459 sqlite3_bind_blob(p->pInsert, 5,
7eb5b0a… dan 460 blob_buffer(&p->tmp), nOut, SQLITE_STATIC
7eb5b0a… dan 461 );
7eb5b0a… dan 462 }
7eb5b0a… dan 463 }
7eb5b0a… dan 464 }
7eb5b0a… dan 465
7eb5b0a… dan 466 sqlite3_step(p->pInsert);
7eb5b0a… dan 467 sqlite3_reset(p->pInsert);
7eb5b0a… dan 468 }
7eb5b0a… dan 469
7eb5b0a… dan 470 static void zip_add_file(
7eb5b0a… dan 471 Archive *p,
275da70… danield 472 const char *zName,
275da70… danield 473 const Blob *pFile,
7eb5b0a… dan 474 int mPerm
7eb5b0a… dan 475 ){
7eb5b0a… dan 476 if( p->eType==ARCHIVE_ZIP ){
7eb5b0a… dan 477 zip_add_file_to_zip(p, zName, pFile, mPerm);
7eb5b0a… dan 478 }else{
7eb5b0a… dan 479 zip_add_file_to_sqlar(p, zName, pFile, mPerm);
7eb5b0a… dan 480 }
7eb5b0a… dan 481 }
7eb5b0a… dan 482
7eb5b0a… dan 483 /*
7eb5b0a… dan 484 ** If the given filename includes one or more directory entries, make
7eb5b0a… dan 485 ** sure the directories are already in the archive. If they are not
7eb5b0a… dan 486 ** in the archive, add them.
7eb5b0a… dan 487 */
7eb5b0a… dan 488 static void zip_add_folders(Archive *p, char *zName){
7eb5b0a… dan 489 int i, c;
7eb5b0a… dan 490 int j;
7eb5b0a… dan 491 for(i=0; zName[i]; i++){
7eb5b0a… dan 492 if( zName[i]=='/' ){
7eb5b0a… dan 493 c = zName[i+1];
7eb5b0a… dan 494 zName[i+1] = 0;
7eb5b0a… dan 495 for(j=0; j<nDir; j++){
7eb5b0a… dan 496 if( fossil_strcmp(zName, azDir[j])==0 ) break;
7eb5b0a… dan 497 }
7eb5b0a… dan 498 if( j>=nDir ){
7eb5b0a… dan 499 nDir++;
7eb5b0a… dan 500 azDir = fossil_realloc(azDir, sizeof(azDir[0])*nDir);
4c3e172… danield 501 azDir[j] = fossil_strdup(zName);
7eb5b0a… dan 502 zip_add_file(p, zName, 0, 0);
7eb5b0a… dan 503 }
7eb5b0a… dan 504 zName[i+1] = c;
7eb5b0a… dan 505 }
7eb5b0a… dan 506 }
7eb5b0a… dan 507 }
7eb5b0a… dan 508
7eb5b0a… dan 509 /*
7eb5b0a… dan 510 ** Free all the members of structure Archive allocated while processing
7eb5b0a… dan 511 ** an SQLAR request.
7eb5b0a… dan 512 */
7eb5b0a… dan 513 static void free_archive(Archive *p){
7eb5b0a… dan 514 if( p->vfs.zName ){
7eb5b0a… dan 515 sqlite3_vfs_unregister(&p->vfs);
7eb5b0a… dan 516 fossil_free((char*)p->vfs.zName);
7eb5b0a… dan 517 p->vfs.zName = 0;
7eb5b0a… dan 518 }
7eb5b0a… dan 519 sqlite3_finalize(p->pInsert);
7eb5b0a… dan 520 p->pInsert = 0;
7eb5b0a… dan 521 sqlite3_close(p->db);
7eb5b0a… dan 522 p->db = 0;
7eb5b0a… dan 523 }
9b780d2… drh 524
9b780d2… drh 525 /*
9b780d2… drh 526 ** Write the ZIP archive into the given BLOB.
9b780d2… drh 527 */
7eb5b0a… dan 528 static void zip_close(Archive *p){
90048e0… drh 529 int i;
7eb5b0a… dan 530 if( p->eType==ARCHIVE_ZIP ){
7eb5b0a… dan 531 int iTocStart;
7eb5b0a… dan 532 int iTocEnd;
7eb5b0a… dan 533 char zBuf[30];
7eb5b0a… dan 534
7eb5b0a… dan 535 iTocStart = blob_size(&body);
7eb5b0a… dan 536 blob_append(&body, blob_buffer(&toc), blob_size(&toc));
7eb5b0a… dan 537 iTocEnd = blob_size(&body);
7eb5b0a… dan 538
7eb5b0a… dan 539 memset(zBuf, 0, sizeof(zBuf));
7eb5b0a… dan 540 put32(&zBuf[0], 0x06054b50);
7eb5b0a… dan 541 put16(&zBuf[4], 0);
7eb5b0a… dan 542 put16(&zBuf[6], 0);
7eb5b0a… dan 543 put16(&zBuf[8], nEntry);
7eb5b0a… dan 544 put16(&zBuf[10], nEntry);
7eb5b0a… dan 545 put32(&zBuf[12], iTocEnd - iTocStart);
7eb5b0a… dan 546 put32(&zBuf[16], iTocStart);
7eb5b0a… dan 547 put16(&zBuf[20], 0);
7eb5b0a… dan 548 blob_append(&body, zBuf, 22);
7eb5b0a… dan 549 blob_reset(&toc);
7eb5b0a… dan 550 *(p->pBlob) = body;
7eb5b0a… dan 551 blob_zero(&body);
7eb5b0a… dan 552 }else{
7eb5b0a… dan 553 if( p->db ) sqlite3_exec(p->db, "COMMIT", 0, 0, 0);
7eb5b0a… dan 554 free_archive(p);
7eb5b0a… dan 555 blob_reset(&p->tmp);
7eb5b0a… dan 556 }
7eb5b0a… dan 557
9b780d2… drh 558 nEntry = 0;
90048e0… drh 559 for(i=0; i<nDir; i++){
8206ac9… stephan 560 fossil_free(azDir[i]);
90048e0… drh 561 }
8206ac9… stephan 562 fossil_free(azDir);
90048e0… drh 563 nDir = 0;
90048e0… drh 564 azDir = 0;
9b780d2… drh 565 }
90048e0… drh 566
932d351… drh 567 /* Functions found in shell.c */
932d351… drh 568 extern int sqlite3_fileio_init(sqlite3*,char**,const sqlite3_api_routines*);
932d351… drh 569 extern int sqlite3_zipfile_init(sqlite3*,char**,const sqlite3_api_routines*);
932d351… drh 570
9b780d2… drh 571 /*
9b780d2… drh 572 ** COMMAND: test-filezip
9b780d2… drh 573 **
932d351… drh 574 ** Usage: %fossil test-filezip [OPTIONS] ZIPFILE [FILENAME...]
932d351… drh 575 **
932d351… drh 576 ** This command uses Fossil infrastructure or read or create a ZIP
932d351… drh 577 ** archive named by the ZIPFILE argument. With no options, a new
932d351… drh 578 ** ZIP archive is created and there must be at least one FILENAME
932d351… drh 579 ** argument. If the -l option is used, the contents of the named ZIP
932d351… drh 580 ** archive are listed on standard output. With the -x argument, the
932d351… drh 581 ** contents of the ZIP archive are extracted.
932d351… drh 582 **
932d351… drh 583 ** There are two purposes for this command: (1) To server as a test
932d351… drh 584 ** platform for the Fossil ZIP archive generator, and (2) to provide
932d351… drh 585 ** rudimentary ZIP archive creation capabilities on platforms that do
932d351… drh 586 ** not have the "zip" command installed.
932d351… drh 587 **
932d351… drh 588 ** Options:
932d351… drh 589 **
932d351… drh 590 ** -h|--dereference Follow symlinks
932d351… drh 591 ** -l|--list List the contents of the ZIP archive
932d351… drh 592 ** -x|--extract Extract files from a ZIP archive
9b780d2… drh 593 */
9b780d2… drh 594 void filezip_cmd(void){
1772357… drh 595 int eFType = SymFILE;
932d351… drh 596 int doList = 0;
932d351… drh 597 int doExtract = 0;
932d351… drh 598 char *zArchiveName;
1772357… drh 599 if( find_option("dereference","h",0)!=0 ){
1772357… drh 600 eFType = ExtFILE;
1772357… drh 601 }
932d351… drh 602 if( find_option("list","l",0)!=0 ){
932d351… drh 603 doList = 1;
932d351… drh 604 }
932d351… drh 605 if( find_option("extract","x",0)!=0 ){
932d351… drh 606 if( doList ){
932d351… drh 607 fossil_fatal("incompatible options: -l and -x");
932d351… drh 608 }
932d351… drh 609 doExtract = 1;
932d351… drh 610 }
6368fce… drh 611 if( g.argc<3 ){
932d351… drh 612 usage("ARCHIVE FILES...");
6368fce… drh 613 }
932d351… drh 614 zArchiveName = g.argv[2];
6368fce… drh 615 sqlite3_open(":memory:", &g.db);
932d351… drh 616 if( doList ){
932d351… drh 617 /* Do a content listing of a ZIP archive */
932d351… drh 618 Stmt q;
932d351… drh 619 int nRow = 0;
932d351… drh 620 i64 szTotal = 0;
932d351… drh 621 if( file_size(zArchiveName, eFType)<0 ){
932d351… drh 622 fossil_fatal("No such ZIP archive: %s", zArchiveName);
932d351… drh 623 }
932d351… drh 624 if( g.argc>3 ){
932d351… drh 625 fossil_fatal("extra arguments after \"fossil test-filezip -l ARCHIVE\"");
932d351… drh 626 }
932d351… drh 627 sqlite3_zipfile_init(g.db, 0, 0);
932d351… drh 628 db_multi_exec("CREATE VIRTUAL TABLE z1 USING zipfile(%Q)", zArchiveName);
31ce0d3… drh 629 db_prepare(&q,
31ce0d3… drh 630 "SELECT sz, datetime(mtime,'unixepoch'),"
31ce0d3… drh 631 " if(((mode>>12)&15)==10,name||' -> '||data,name) FROM z1"
31ce0d3… drh 632 );
932d351… drh 633 while( db_step(&q)==SQLITE_ROW ){
932d351… drh 634 int sz = db_column_int(&q, 0);
932d351… drh 635 szTotal += sz;
932d351… drh 636 if( nRow==0 ){
932d351… drh 637 fossil_print(" Length Date Time Name\n");
932d351… drh 638 fossil_print("--------- ---------- ----- ----\n");
932d351… drh 639 }
932d351… drh 640 nRow++;
932d351… drh 641 fossil_print("%9d %.16s %s\n", sz, db_column_text(&q,1),
932d351… drh 642 db_column_text(&q,2));
932d351… drh 643 }
932d351… drh 644 if( nRow ){
932d351… drh 645 fossil_print("--------- --------\n");
932d351… drh 646 fossil_print("%9lld %16s %d files\n", szTotal, "", nRow);
932d351… drh 647 }
932d351… drh 648 db_finalize(&q);
932d351… drh 649 }else if( doExtract ){
932d351… drh 650 /* Extract files from an existing ZIP archive */
932d351… drh 651 if( file_size(zArchiveName, eFType)<0 ){
932d351… drh 652 fossil_fatal("No such ZIP archive: %s", zArchiveName);
932d351… drh 653 }
932d351… drh 654 if( g.argc>3 ){
932d351… drh 655 fossil_fatal("extra arguments after \"fossil test-filezip -x ARCHIVE\"");
932d351… drh 656 }
932d351… drh 657 sqlite3_zipfile_init(g.db, 0, 0);
932d351… drh 658 sqlite3_fileio_init(g.db, 0, 0);
932d351… drh 659 db_multi_exec("CREATE VIRTUAL TABLE z1 USING zipfile(%Q)", zArchiveName);
31ce0d3… drh 660 db_multi_exec(
31ce0d3… drh 661 "SELECT writefile(name,data) FROM z1"
31ce0d3… drh 662 " WHERE ((mode>>12)&15)!=10"
31ce0d3… drh 663 );
932d351… drh 664 }else{
932d351… drh 665 /* Without the -x or -l options, construct a new ZIP archive */
932d351… drh 666 int i;
932d351… drh 667 Blob zip;
932d351… drh 668 Blob file;
932d351… drh 669 Archive sArchive;
932d351… drh 670 memset(&sArchive, 0, sizeof(Archive));
932d351… drh 671 sArchive.eType = ARCHIVE_ZIP;
932d351… drh 672 sArchive.pBlob = &zip;
932d351… drh 673 if( file_size(zArchiveName, eFType)>0 ){
932d351… drh 674 fossil_fatal("ZIP archive %s already exists", zArchiveName);
932d351… drh 675 }
932d351… drh 676 zip_open();
932d351… drh 677 for(i=3; i<g.argc; i++){
932d351… drh 678 double rDate;
932d351… drh 679 i64 iDate;
932d351… drh 680 blob_zero(&file);
932d351… drh 681 blob_read_from_file(&file, g.argv[i], eFType);
932d351… drh 682 iDate = file_mtime(g.argv[i], eFType);
932d351… drh 683 rDate = ((double)iDate)/86400.0 + 2440587.5;
932d351… drh 684 zip_set_timedate(rDate);
932d351… drh 685 zip_add_file(&sArchive, g.argv[i], &file, file_perm(0,eFType));
932d351… drh 686 blob_reset(&file);
932d351… drh 687 }
932d351… drh 688 zip_close(&sArchive);
932d351… drh 689 blob_write_to_file(&zip, g.argv[2]);
932d351… drh 690 }
dead090… drh 691 }
dead090… drh 692
dead090… drh 693 /*
dead090… drh 694 ** Given the RID for a manifest, construct a ZIP archive containing
dead090… drh 695 ** all files in the corresponding baseline.
dead090… drh 696 **
dead090… drh 697 ** If RID is for an object that is not a real manifest, then the
dead090… drh 698 ** resulting ZIP archive contains a single file which is the RID
ef449a1… drh 699 ** object. The pInclude and pExclude parameters are ignored in this case.
dead090… drh 700 **
dead090… drh 701 ** If the RID object does not exist in the repository, then
dead090… drh 702 ** pZip is zeroed.
8672e24… drh 703 **
4bbb00a… drh 704 ** zDir is a "synthetic" subdirectory which all zipped files get
8672e24… drh 705 ** added to as part of the zip file. It may be 0 or an empty string,
8672e24… drh 706 ** in which case it is ignored. The intention is to create a zip which
8672e24… drh 707 ** politely expands into a subdir instead of filling your current dir
8ad5e46… wyoung 708 ** with source files. For example, pass a commit hash or "ProjectName".
4bbb00a… drh 709 **
4bbb00a… drh 710 */
7eb5b0a… dan 711 static void zip_of_checkin(
7eb5b0a… dan 712 int eType, /* Type of archive (ZIP or SQLAR) */
bc36fdc… danield 713 int rid, /* The RID of the check-in to build the archive from */
7eb5b0a… dan 714 Blob *pZip, /* Write the archive content into this blob */
7eb5b0a… dan 715 const char *zDir, /* Top-level directory of the archive */
ef449a1… drh 716 Glob *pInclude, /* Only include files that match this pattern */
c88880f… drh 717 Glob *pExclude, /* Exclude files that match this pattern */
c88880f… drh 718 int listFlag /* Print each file on stdout */
ef449a1… drh 719 ){
d13054c… drh 720 Blob mfile, hash, file;
d13054c… drh 721 Manifest *pManifest;
d13054c… drh 722 ManifestFile *pFile;
4bbb00a… drh 723 Blob filename;
4bbb00a… drh 724 int nPrefix;
52b35c8… jan.nijtmans 725
7eb5b0a… dan 726 Archive sArchive;
7eb5b0a… dan 727 memset(&sArchive, 0, sizeof(Archive));
7eb5b0a… dan 728 sArchive.eType = eType;
7eb5b0a… dan 729 sArchive.pBlob = pZip;
3e2b5c4… drh 730 blob_zero(&sArchive.tmp);
c88880f… drh 731 if( pZip ) blob_zero(pZip);
7eb5b0a… dan 732
4bbb00a… drh 733 content_get(rid, &mfile);
4bbb00a… drh 734 if( blob_size(&mfile)==0 ){
4bbb00a… drh 735 return;
4bbb00a… drh 736 }
fd9b7bd… drh 737 blob_set_dynamic(&hash, rid_to_uuid(rid));
4bbb00a… drh 738 blob_zero(&filename);
c88880f… drh 739 if( pZip ) zip_open();
4bbb00a… drh 740
4bbb00a… drh 741 if( zDir && zDir[0] ){
4bbb00a… drh 742 blob_appendf(&filename, "%s/", zDir);
4bbb00a… drh 743 }
4bbb00a… drh 744 nPrefix = blob_size(&filename);
4bbb00a… drh 745
ec81aee… jan.nijtmans 746 pManifest = manifest_get(rid, CFTYPE_MANIFEST, 0);
d13054c… drh 747 if( pManifest ){
189bfc2… jan 748 int flg, eflg = 0;
3f06618… mistachkin 749 char *zName = 0;
d13054c… drh 750 zip_set_timedate(pManifest->rDate);
dfc0f1b… drh 751 flg = db_get_manifest_setting(blob_str(&hash));
189bfc2… jan 752 if( flg ){
189bfc2… jan 753 /* eflg is the effective flags, taking include/exclude into account */
189bfc2… jan 754 if( (pInclude==0 || glob_match(pInclude, "manifest"))
189bfc2… jan 755 && !glob_match(pExclude, "manifest")
189bfc2… jan 756 && (flg & MFESTFLG_RAW) ){
189bfc2… jan 757 eflg |= MFESTFLG_RAW;
189bfc2… jan 758 }
189bfc2… jan 759 if( (pInclude==0 || glob_match(pInclude, "manifest.uuid"))
189bfc2… jan 760 && !glob_match(pExclude, "manifest.uuid")
189bfc2… jan 761 && (flg & MFESTFLG_UUID) ){
189bfc2… jan 762 eflg |= MFESTFLG_UUID;
189bfc2… jan 763 }
189bfc2… jan 764 if( (pInclude==0 || glob_match(pInclude, "manifest.tags"))
189bfc2… jan 765 && !glob_match(pExclude, "manifest.tags")
189bfc2… jan 766 && (flg & MFESTFLG_TAGS) ){
189bfc2… jan 767 eflg |= MFESTFLG_TAGS;
189bfc2… jan 768 }
189bfc2… jan 769
b9de604… andygoth 770 if( eflg & MFESTFLG_RAW ){
b9de604… andygoth 771 blob_append(&filename, "manifest", -1);
b9de604… andygoth 772 zName = blob_str(&filename);
c88880f… drh 773 if( listFlag ) fossil_print("%s\n", zName);
c88880f… drh 774 if( pZip ){
c88880f… drh 775 zip_add_folders(&sArchive, zName);
c88880f… drh 776 zip_add_file(&sArchive, zName, &mfile, 0);
c88880f… drh 777 }
fd9b7bd… drh 778 }
189bfc2… jan 779 if( eflg & MFESTFLG_UUID ){
189bfc2… jan 780 blob_append(&hash, "\n", 1);
189bfc2… jan 781 blob_resize(&filename, nPrefix);
189bfc2… jan 782 blob_append(&filename, "manifest.uuid", -1);
189bfc2… jan 783 zName = blob_str(&filename);
c88880f… drh 784 if( listFlag ) fossil_print("%s\n", zName);
c88880f… drh 785 if( pZip ){
c88880f… drh 786 zip_add_folders(&sArchive, zName);
c88880f… drh 787 zip_add_file(&sArchive, zName, &hash, 0);
c88880f… drh 788 }
189bfc2… jan 789 }
189bfc2… jan 790 if( eflg & MFESTFLG_TAGS ){
189bfc2… jan 791 blob_resize(&filename, nPrefix);
189bfc2… jan 792 blob_append(&filename, "manifest.tags", -1);
189bfc2… jan 793 zName = blob_str(&filename);
c88880f… drh 794 if( listFlag ) fossil_print("%s\n", zName);
c88880f… drh 795 if( pZip ){
c88880f… drh 796 Blob tagslist;
c88880f… drh 797 blob_zero(&tagslist);
c88880f… drh 798 get_checkin_taglist(rid, &tagslist);
c88880f… drh 799 zip_add_folders(&sArchive, zName);
c88880f… drh 800 zip_add_file(&sArchive, zName, &tagslist, 0);
c88880f… drh 801 blob_reset(&tagslist);
c88880f… drh 802 }
189bfc2… jan 803 }
d13054c… drh 804 }
d13054c… drh 805 manifest_file_rewind(pManifest);
c88880f… drh 806 if( pZip ) zip_add_file(&sArchive, "", 0, 0);
d13054c… drh 807 while( (pFile = manifest_file_next(pManifest,0))!=0 ){
ef449a1… drh 808 int fid;
ef449a1… drh 809 if( pInclude!=0 && !glob_match(pInclude, pFile->zName) ) continue;
ef449a1… drh 810 if( glob_match(pExclude, pFile->zName) ) continue;
ef449a1… drh 811 fid = uuid_to_rid(pFile->zUuid, 0);
d13054c… drh 812 if( fid ){
d13054c… drh 813 blob_resize(&filename, nPrefix);
d13054c… drh 814 blob_append(&filename, pFile->zName, -1);
d13054c… drh 815 zName = blob_str(&filename);
c88880f… drh 816 if( listFlag ) fossil_print("%s\n", zName);
c88880f… drh 817 if( pZip ){
c88880f… drh 818 content_get(fid, &file);
c88880f… drh 819 zip_add_folders(&sArchive, zName);
c88880f… drh 820 zip_add_file(&sArchive, zName, &file, manifest_file_mperm(pFile));
c88880f… drh 821 blob_reset(&file);
c88880f… drh 822 }
d13054c… drh 823 }
d13054c… drh 824 }
d13054c… drh 825 }
b9de604… andygoth 826 blob_reset(&mfile);
d13054c… drh 827 manifest_destroy(pManifest);
d13054c… drh 828 blob_reset(&filename);
fd9b7bd… drh 829 blob_reset(&hash);
c88880f… drh 830 if( pZip ){
c88880f… drh 831 zip_close(&sArchive);
c88880f… drh 832 }
7eb5b0a… dan 833 }
7eb5b0a… dan 834
7eb5b0a… dan 835 /*
7eb5b0a… dan 836 ** Implementation of zip_cmd and sqlar_cmd.
7eb5b0a… dan 837 */
7eb5b0a… dan 838 static void archive_cmd(int eType){
7eb5b0a… dan 839 int rid;
7eb5b0a… dan 840 Blob zip;
7eb5b0a… dan 841 const char *zName;
7eb5b0a… dan 842 Glob *pInclude = 0;
7eb5b0a… dan 843 Glob *pExclude = 0;
7eb5b0a… dan 844 const char *zInclude;
7eb5b0a… dan 845 const char *zExclude;
c88880f… drh 846 int listFlag = 0;
c88880f… drh 847 const char *zOut;
7eb5b0a… dan 848
7eb5b0a… dan 849 zName = find_option("name", 0, 1);
7eb5b0a… dan 850 zExclude = find_option("exclude", "X", 1);
7eb5b0a… dan 851 if( zExclude ) pExclude = glob_create(zExclude);
7eb5b0a… dan 852 zInclude = find_option("include", 0, 1);
7eb5b0a… dan 853 if( zInclude ) pInclude = glob_create(zInclude);
c88880f… drh 854 listFlag = find_option("list","l",0)!=0;
7eb5b0a… dan 855 db_find_and_open_repository(0, 0);
7eb5b0a… dan 856
7eb5b0a… dan 857 /* We should be done with options.. */
7eb5b0a… dan 858 verify_all_options();
7eb5b0a… dan 859
7eb5b0a… dan 860 if( g.argc!=4 ){
7eb5b0a… dan 861 usage("VERSION OUTPUTFILE");
7eb5b0a… dan 862 }
7eb5b0a… dan 863 g.zOpenRevision = g.argv[2];
7eb5b0a… dan 864 rid = name_to_typed_rid(g.argv[2], "ci");
7eb5b0a… dan 865 if( rid==0 ){
7eb5b0a… dan 866 fossil_fatal("Check-in not found: %s", g.argv[2]);
7eb5b0a… dan 867 return;
7eb5b0a… dan 868 }
c88880f… drh 869 zOut = g.argv[3];
c88880f… drh 870 if( fossil_strcmp(zOut,"")==0 || fossil_strcmp(zOut,"/dev/null")==0 ){
c88880f… drh 871 zOut = 0;
c88880f… drh 872 }
7eb5b0a… dan 873
7eb5b0a… dan 874 if( zName==0 ){
6ce705b… drh 875 zName = archive_base_name(rid);
275da70… danield 876 }
275da70… danield 877 zip_of_checkin(eType, rid, zOut ? &zip : 0,
c88880f… drh 878 zName, pInclude, pExclude, listFlag);
7eb5b0a… dan 879 glob_free(pInclude);
7eb5b0a… dan 880 glob_free(pExclude);
c88880f… drh 881 if( zOut ){
c88880f… drh 882 blob_write_to_file(&zip, zOut);
c88880f… drh 883 blob_reset(&zip);
c88880f… drh 884 }
ef449a1… drh 885 }
ef449a1… drh 886
ef449a1… drh 887 /*
ef449a1… drh 888 ** COMMAND: zip*
ef449a1… drh 889 **
ef449a1… drh 890 ** Usage: %fossil zip VERSION OUTPUTFILE [OPTIONS]
ef449a1… drh 891 **
ef449a1… drh 892 ** Generate a ZIP archive for a check-in. If the --name option is
ef449a1… drh 893 ** used, its argument becomes the name of the top-level directory in the
ef449a1… drh 894 ** resulting ZIP archive. If --name is omitted, the top-level directory
953cfbd… mistachkin 895 ** name is derived from the project name, the check-in date and time, and
953cfbd… mistachkin 896 ** the artifact ID of the check-in.
953cfbd… mistachkin 897 **
953cfbd… mistachkin 898 ** The GLOBLIST argument to --exclude and --include can be a comma-separated
953cfbd… mistachkin 899 ** list of glob patterns, where each glob pattern may optionally be enclosed
953cfbd… mistachkin 900 ** in "..." or '...' so that it may contain commas. If a file matches both
953cfbd… mistachkin 901 ** --include and --exclude then it is excluded.
953cfbd… mistachkin 902 **
c88880f… drh 903 ** If OUTPUTFILE is an empty string or "/dev/null" then no ZIP archive is
c88880f… drh 904 ** actually generated. This feature can be used in combination with
d708847… andybradford 905 ** the --list option to get a list of the filenames that would be in the
c88880f… drh 906 ** ZIP archive had it actually been generated.
c88880f… drh 907 **
953cfbd… mistachkin 908 ** Options:
953cfbd… mistachkin 909 ** -X|--exclude GLOBLIST Comma-separated list of GLOBs of files to exclude
953cfbd… mistachkin 910 ** --include GLOBLIST Comma-separated list of GLOBs of files to include
c88880f… drh 911 ** -l|--list Show archive content on stdout
953cfbd… mistachkin 912 ** --name DIRECTORYNAME The name of the top-level directory in the archive
953cfbd… mistachkin 913 ** -R REPOSITORY Specify a Fossil repository
953cfbd… mistachkin 914 */
953cfbd… mistachkin 915 void zip_cmd(void){
7eb5b0a… dan 916 archive_cmd(ARCHIVE_ZIP);
7eb5b0a… dan 917 }
7eb5b0a… dan 918
7eb5b0a… dan 919 /*
7eb5b0a… dan 920 ** COMMAND: sqlar*
7eb5b0a… dan 921 **
7eb5b0a… dan 922 ** Usage: %fossil sqlar VERSION OUTPUTFILE [OPTIONS]
7eb5b0a… dan 923 **
7eb5b0a… dan 924 ** Generate an SQLAR archive for a check-in. If the --name option is
7eb5b0a… dan 925 ** used, its argument becomes the name of the top-level directory in the
7eb5b0a… dan 926 ** resulting SQLAR archive. If --name is omitted, the top-level directory
7eb5b0a… dan 927 ** name is derived from the project name, the check-in date and time, and
7eb5b0a… dan 928 ** the artifact ID of the check-in.
7eb5b0a… dan 929 **
7eb5b0a… dan 930 ** The GLOBLIST argument to --exclude and --include can be a comma-separated
7eb5b0a… dan 931 ** list of glob patterns, where each glob pattern may optionally be enclosed
7eb5b0a… dan 932 ** in "..." or '...' so that it may contain commas. If a file matches both
7eb5b0a… dan 933 ** --include and --exclude then it is excluded.
7eb5b0a… dan 934 **
c88880f… drh 935 ** If OUTPUTFILE is an empty string or "/dev/null" then no SQLAR archive is
c88880f… drh 936 ** actually generated. This feature can be used in combination with
d708847… andybradford 937 ** the --list option to get a list of the filenames that would be in the
c88880f… drh 938 ** SQLAR archive had it actually been generated.
c88880f… drh 939 **
7eb5b0a… dan 940 ** Options:
7eb5b0a… dan 941 ** -X|--exclude GLOBLIST Comma-separated list of GLOBs of files to exclude
7eb5b0a… dan 942 ** --include GLOBLIST Comma-separated list of GLOBs of files to include
c88880f… drh 943 ** -l|--list Show archive content on stdout
7eb5b0a… dan 944 ** --name DIRECTORYNAME The name of the top-level directory in the archive
7eb5b0a… dan 945 ** -R REPOSITORY Specify a Fossil repository
7eb5b0a… dan 946 */
7eb5b0a… dan 947 void sqlar_cmd(void){
7eb5b0a… dan 948 archive_cmd(ARCHIVE_SQLAR);
7c2577b… drh 949 }
7c2577b… drh 950
7c2577b… drh 951 /*
768e192… drh 952 ** WEBPAGE: sqlar
7c2577b… drh 953 ** WEBPAGE: zip
768e192… drh 954 **
9eadac2… drh 955 ** URLs:
9eadac2… drh 956 **
4717db3… wyoung 957 ** /zip/[VERSION/]NAME.zip
4717db3… wyoung 958 ** /sqlar/[VERSION/]NAME.sqlar
9eadac2… drh 959 **
9eadac2… drh 960 ** Generate a ZIP Archive or an SQL Archive for the check-in specified by
9eadac2… drh 961 ** VERSION. The archive is called NAME.zip or NAME.sqlar and has a top-level
9eadac2… drh 962 ** directory called NAME.
9eadac2… drh 963 **
3a6dd83… drh 964 ** The optional VERSION element defaults to the name of the main branch
3a6dd83… drh 965 ** (usually "trunk") per the r= rules below.
9eadac2… drh 966 ** All of the following URLs are equivalent:
9eadac2… drh 967 **
9eadac2… drh 968 ** /zip/release/xyz.zip
9eadac2… drh 969 ** /zip?r=release&name=xyz.zip
9eadac2… drh 970 ** /zip/xyz.zip?r=release
9eadac2… drh 971 ** /zip?name=release/xyz.zip
ef449a1… drh 972 **
ef449a1… drh 973 ** Query parameters:
ef449a1… drh 974 **
9eadac2… drh 975 ** name=[CKIN/]NAME The optional CKIN component of the name= parameter
9eadac2… drh 976 ** identifies the check-in from which the archive is
9eadac2… drh 977 ** constructed. If CKIN is omitted and there is no
3a6dd83… drh 978 ** r= query parameter, then use the name of the main
3a6dd83… drh 979 ** branch (usually "trunk"). NAME is the
9eadac2… drh 980 ** name of the download file. The top-level directory
9eadac2… drh 981 ** in the generated archive is called by NAME with the
9eadac2… drh 982 ** file extension removed.
9eadac2… drh 983 **
9eadac2… drh 984 ** r=TAG TAG identifies the check-in that is turned into an
3a6dd83… drh 985 ** SQL or ZIP archive. The default value is the name
3a6dd83… drh 986 ** of the main branch (usually "trunk").
9eadac2… drh 987 ** If r= is omitted and if the name= query parameter
9eadac2… drh 988 ** contains one "/" character then the of part the
9eadac2… drh 989 ** name= value before the / becomes the TAG and the
9eadac2… drh 990 ** part of the name= value after the / is the download
9eadac2… drh 991 ** filename. If no check-in is specified by either
3a6dd83… drh 992 ** name= or r=, then the name of the main branch
3a6dd83… drh 993 ** (usually "trunk") is used.
9eadac2… drh 994 **
33d3bf3… km 995 ** in=PATTERN Only include files that match the comma-separated
ef449a1… drh 996 ** list of GLOB patterns in PATTERN, as with ex=
ef449a1… drh 997 **
ef449a1… drh 998 ** ex=PATTERN Omit any file that match PATTERN. PATTERN is a
ef449a1… drh 999 ** comma-separated list of GLOB patterns, where each
ef449a1… drh 1000 ** pattern can optionally be quoted using ".." or '..'.
ef449a1… drh 1001 ** Any file matching both ex= and in= is excluded.
4d198d0… drh 1002 **
4d198d0… drh 1003 ** Robot Defenses:
4d198d0… drh 1004 **
4d198d0… drh 1005 ** * If "zip" appears in the robot-restrict setting, then robots are
4d198d0… drh 1006 ** not allowed to access this page. Suspected robots will be
4d198d0… drh 1007 ** presented with a captcha.
4d198d0… drh 1008 **
4d198d0… drh 1009 ** * If "zipX" appears in the robot-restrict setting, then robots are
4d198d0… drh 1010 ** restricted in the same way as with "zip", but with exceptions.
4d198d0… drh 1011 ** If the check-in for which an archive is requested is a leaf check-in
4d198d0… drh 1012 ** and if the robot-zip-leaf setting is true, then the request is
4d198d0… drh 1013 ** allowed. Or if the check-in has a tag that matches any of the
4d198d0… drh 1014 ** GLOB patterns on the list in the robot-zip-tag setting, then the
4d198d0… drh 1015 ** request is allowed. Otherwise, the usual robot defenses are
4d198d0… drh 1016 ** activated.
7c2577b… drh 1017 */
7c2577b… drh 1018 void baseline_zip_page(void){
7c2577b… drh 1019 int rid;
ece33ee… drh 1020 const char *z;
953cfbd… mistachkin 1021 char *zName, *zRid, *zKey;
7c2577b… drh 1022 int nName, nRid;
ef449a1… drh 1023 const char *zInclude; /* The in= query parameter */
ef449a1… drh 1024 const char *zExclude; /* The ex= query parameter */
ef449a1… drh 1025 Blob cacheKey; /* The key to cache */
ef449a1… drh 1026 Glob *pInclude = 0; /* The compiled in= glob pattern */
ef449a1… drh 1027 Glob *pExclude = 0; /* The compiled ex= glob pattern */
953cfbd… mistachkin 1028 Blob zip; /* ZIP archive accumulated here */
768e192… drh 1029 int eType = ARCHIVE_ZIP; /* Type of archive to generate */
768e192… drh 1030 char *zType; /* Human-readable archive type */
7c2577b… drh 1031
7c2577b… drh 1032 login_check_credentials();
653dd40… drh 1033 if( !g.perm.Zip ){ login_needed(g.anon.Zip); return; }
16b3309… drh 1034 if( robot_restrict("zip") ) return;
768e192… drh 1035 if( fossil_strcmp(g.zPath, "sqlar")==0 ){
768e192… drh 1036 eType = ARCHIVE_SQLAR;
768e192… drh 1037 zType = "SQL";
768e192… drh 1038 }else{
768e192… drh 1039 eType = ARCHIVE_ZIP;
768e192… drh 1040 zType = "ZIP";
768e192… drh 1041 }
9413395… drh 1042 fossil_nice_default();
bb9233a… drh 1043 zName = fossil_strdup(PD("name",""));
ece33ee… drh 1044 z = P("r");
ece33ee… drh 1045 if( z==0 ) z = P("uuid");
3e94c7e… drh 1046 if( z==0 ) z = tar_uuid_from_name(&zName);
ca0d66b… danield 1047 if( z==0 ) z = fossil_strdup(db_main_branch());
3e94c7e… drh 1048 nName = strlen(zName);
da23bec… andygoth 1049 g.zOpenRevision = zRid = fossil_strdup(z);
7c2577b… drh 1050 nRid = strlen(zRid);
ef449a1… drh 1051 zInclude = P("in");
ef449a1… drh 1052 if( zInclude ) pInclude = glob_create(zInclude);
ef449a1… drh 1053 zExclude = P("ex");
ef449a1… drh 1054 if( zExclude ) pExclude = glob_create(zExclude);
44339d5… drh 1055 if( zInclude==0 && zExclude==0 ){
44339d5… drh 1056 etag_check_for_invariant_name(z);
44339d5… drh 1057 }
275da70… danield 1058 if( eType==ARCHIVE_ZIP
768e192… drh 1059 && nName>4
768e192… drh 1060 && fossil_strcmp(&zName[nName-4], ".zip")==0
768e192… drh 1061 ){
e0199bf… stephan 1062 /* Special case: Remove the ".zip" suffix. */
e0199bf… stephan 1063 nName -= 4;
e0199bf… stephan 1064 zName[nName] = 0;
275da70… danield 1065 }else if( eType==ARCHIVE_SQLAR
768e192… drh 1066 && nName>6
768e192… drh 1067 && fossil_strcmp(&zName[nName-6], ".sqlar")==0
768e192… drh 1068 ){
768e192… drh 1069 /* Special case: Remove the ".sqlar" suffix. */
768e192… drh 1070 nName -= 6;
768e192… drh 1071 zName[nName] = 0;
e0199bf… stephan 1072 }else{
768e192… drh 1073 /* If the file suffix is not ".zip" or ".sqlar" then just remove the
e0199bf… stephan 1074 ** suffix up to and including the last "." */
e0199bf… stephan 1075 for(nName=strlen(zName)-1; nName>5; nName--){
e0199bf… stephan 1076 if( zName[nName]=='.' ){
e0199bf… stephan 1077 zName[nName] = 0;
e0199bf… stephan 1078 break;
e0199bf… stephan 1079 }
7c2577b… drh 1080 }
7c2577b… drh 1081 }
a10fc44… drh 1082 rid = symbolic_name_to_rid(nRid?zRid:zName, "ci");
a10fc44… drh 1083 if( rid<=0 ){
a10fc44… drh 1084 cgi_set_status(404, "Not Found");
7c2577b… drh 1085 @ Not found
7c2577b… drh 1086 return;
7c2577b… drh 1087 }
4d198d0… drh 1088 if( robot_restrict_zip(rid) ) return;
ef449a1… drh 1089 if( nRid==0 && nName>10 ) zName[10] = 0;
ef449a1… drh 1090
ef449a1… drh 1091 /* Compute a unique key for the cache entry based on query parameters */
ef449a1… drh 1092 blob_init(&cacheKey, 0, 0);
768e192… drh 1093 blob_appendf(&cacheKey, "/%s/%z", g.zPath, rid_to_uuid(rid));
953cfbd… mistachkin 1094 blob_appendf(&cacheKey, "/%q", zName);
ef449a1… drh 1095 if( zInclude ) blob_appendf(&cacheKey, ",in=%Q", zInclude);
ef449a1… drh 1096 if( zExclude ) blob_appendf(&cacheKey, ",ex=%Q", zExclude);
ef449a1… drh 1097 zKey = blob_str(&cacheKey);
7383450… drh 1098 etag_check(ETAG_HASH, zKey);
ef449a1… drh 1099
112c713… drh 1100 style_set_current_feature("zip");
953cfbd… mistachkin 1101 if( P("debug")!=0 ){
768e192… drh 1102 style_header("%s Archive Generator Debug Screen", zType);
f5482a0… wyoung 1103 @ zName = "%h(zName)"<br>
f5482a0… wyoung 1104 @ rid = %d(rid)<br>
953cfbd… mistachkin 1105 if( zInclude ){
f5482a0… wyoung 1106 @ zInclude = "%h(zInclude)"<br>
953cfbd… mistachkin 1107 }
953cfbd… mistachkin 1108 if( zExclude ){
f5482a0… wyoung 1109 @ zExclude = "%h(zExclude)"<br>
953cfbd… mistachkin 1110 }
953cfbd… mistachkin 1111 @ zKey = "%h(zKey)"
112c713… drh 1112 style_finish_page();
953cfbd… mistachkin 1113 return;
953cfbd… mistachkin 1114 }
953cfbd… mistachkin 1115 if( referred_from_login() ){
768e192… drh 1116 style_header("%s Archive Download", zType);
768e192… drh 1117 @ <form action='%R/%s(g.zPath)/%h(zName).%s(g.zPath)'>
953cfbd… mistachkin 1118 cgi_query_parameters_to_hidden();
768e192… drh 1119 @ <p>%s(zType) Archive named <b>%h(zName).%s(g.zPath)</b>
768e192… drh 1120 @ holding the content of check-in <b>%h(zRid)</b>:
f5482a0… wyoung 1121 @ <input type="submit" value="Download">
953cfbd… mistachkin 1122 @ </form>
112c713… drh 1123 style_finish_page();
953cfbd… mistachkin 1124 return;
953cfbd… mistachkin 1125 }
57f1e87… drh 1126 cgi_check_for_malice();
450b62f… drh 1127 blob_zero(&zip);
450b62f… drh 1128 if( cache_read(&zip, zKey)==0 ){
c88880f… drh 1129 zip_of_checkin(eType, rid, &zip, zName, pInclude, pExclude, 0);
450b62f… drh 1130 cache_write(&zip, zKey);
450b62f… drh 1131 }
ef449a1… drh 1132 glob_free(pInclude);
ef449a1… drh 1133 glob_free(pExclude);
953cfbd… mistachkin 1134 fossil_free(zName);
953cfbd… mistachkin 1135 fossil_free(zRid);
dbe16d7… mistachkin 1136 g.zOpenRevision = 0;
953cfbd… mistachkin 1137 blob_reset(&cacheKey);
7c2577b… drh 1138 cgi_set_content(&zip);
768e192… drh 1139 if( eType==ARCHIVE_ZIP ){
768e192… drh 1140 cgi_set_content_type("application/zip");
768e192… drh 1141 }else{
768e192… drh 1142 cgi_set_content_type("application/sqlar");
768e192… drh 1143 }
9b780d2… drh 1144 }

Keyboard Shortcuts

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