[Pkg-javascript-commits] [node-yazl] 02/03: Imported Upstream version 2.2.2

Andrew Kelley andrewrk-guest at moszumanska.debian.org
Sat May 16 19:59:25 UTC 2015


This is an automated email from the git hooks/post-receive script.

andrewrk-guest pushed a commit to branch master
in repository node-yazl.

commit 5c7e3789666ef4fb6e94b98a3a3d0f2bf124f6d4
Author: Andrew Kelley <superjoe30 at gmail.com>
Date:   Sat May 16 19:57:14 2015 +0000

    Imported Upstream version 2.2.2
---
 README.md    | 122 ++++++++++++++++++++++++++++++++++++++++++-------------
 index.js     | 130 ++++++++++++++++++++++++++++++++++++++++++++---------------
 package.json |   7 +++-
 test/test.js |  55 +++++++++++++++++++++++++
 test/zip.js  |  48 +++++++++++++++++-----
 5 files changed, 291 insertions(+), 71 deletions(-)

diff --git a/README.md b/README.md
index a10f0d5..6d9b646 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,7 @@
 # yazl
 
-yet another zip library for node
+yet another zip library for node. For unzipping, see
+[yauzl](https://github.com/thejoshwolfe/yauzl).
 
 Design principles:
 
@@ -54,24 +55,25 @@ Typically `metadataPath` would be calculated as `path.relative(root, realPath)`.
 Unzip programs would extract the file from the zipfile as `metadataPath`.
 `realPath` is not stored in the zipfile.
 
-This function throws an error if `metadataPath` starts with `"/"` or `/[A-Za-z]:\//`
-or if it contains `".."` path segments or `"\\"`.
-These would be illegal file names according to the spec.
+A valid `metadataPath` must not be blank, must not start with `"/"` or `/[A-Za-z]:\//`,
+and must not contain `"\\"` or `".."` path segments.
+File paths must not end with `"/"`.
 
-`options` may be omitted or null has the following structure and default values:
+`options` may be omitted or null and has the following structure and default values:
 
 ```js
 {
-  mtime: stats.mtime, // optional
-  mode: stats.mode,   // optional
-  compress: true,     // optional
+  mtime: stats.mtime,
+  mode: stats.mode,
+  compress: true,
 }
 ```
 
 Use `options.mtime` and/or `options.mode` to override the values
 that would normally be obtained by the `fs.Stats` for the `realPath`.
-The mtime and mode (unix permission bits and file type) are stored in the zip file
-in the fields "last mod file time", "last mod file date", and "external file attributes".
+The mode is the unix permission bits and file type.
+The mtime and mode are stored in the zip file in the fields "last mod file time",
+"last mod file date", and "external file attributes".
 yazl does not store group and user ids in the zip file.
 
 Internally, `fs.stat()` is called immediately in the `addFile` function,
@@ -79,18 +81,18 @@ and `fs.createReadStream()` is used later when the file data is actually require
 Throughout adding and encoding `n` files with `addFile()`,
 the number of simultaneous open files is `O(1)`, probably just 1 at a time.
 
-#### addReadStream(readStream, metadataPath, options)
+#### addReadStream(readStream, metadataPath, [options])
 
 Adds a file to the zip file whose content is read from `readStream`.
 See `addFile()` for info about the `metadataPath` parameter.
-`options` is an `Object` and has the following structure:
+`options` may be omitted or null and has the following structure and default values:
 
 ```js
 {
-  mtime: new Date(), // required
-  mode: 0100664,     // required
-  compress: true,    // optional (default true)
-  size: 12345,       // optional
+  mtime: new Date(),
+  mode: 0100664,
+  compress: true,
+  size: 12345, // example value
 }
 ```
 
@@ -98,17 +100,49 @@ See `addFile()` for the meaning of `mtime` and `mode`.
 If `size` is given, it will be checked against the actual number of bytes in the `readStream`,
 and an error will be emitted if there is a mismatch.
 
-#### addBuffer(buffer, metadataPath, options)
+Note that yazl will `.pipe()` data from `readStream`, so be careful using `.on('data')`.
+In certain versions of node, `.on('data')` makes `.pipe()` behave incorrectly.
+
+#### addBuffer(buffer, metadataPath, [options])
 
 Adds a file to the zip file whose content is `buffer`.
 See `addFile()` for info about the `metadataPath` parameter.
-`options` is an `Object` and has the following structure:
+`options` may be omitted or null and has the following structure and default values:
 
 ```js
 {
-  mtime: new Date(), // required
-  mode: 0100664,     // required
-  compress: true,    // optional (default true)
+  mtime: new Date(),
+  mode: 0100664,
+  compress: true,
+}
+```
+
+See `addFile()` for the meaning of `mtime` and `mode`.
+
+This method has the unique property that General Purpose Bit `3` will not be used in the Local File Header.
+This doesn't matter for unzip implementations that conform to the Zip File Spec.
+However, 7-Zip 9.20 has a known bug where General Purpose Bit `3` is declared an unsupported compression method
+(note that it really has nothing to do with the compression method.).
+See [issue #11](https://github.com/thejoshwolfe/yazl/issues/11).
+If you would like to create zip files that 7-Zip 9.20 can understand,
+you must use `addBuffer()` instead of `addFile()` or `addReadStream()` for all entries in the zip file
+(and `addEmptyDirectory()` is fine too).
+
+#### addEmptyDirectory(metadataPath, [options])
+
+Adds an entry to the zip file that indicates a directory should be created,
+even if no other items in the zip file are contained in the directory.
+This method is only required if the zip file is intended to contain an empty directory.
+
+See `addFile()` for info about the `metadataPath` parameter.
+If `metadataPath` does not end with a `"/"`, a `"/"` will be appended.
+
+`options` may be omitted or null and has the following structure and default values:
+
+```js
+{
+  mtime: new Date(),
+  mode: 040775,
 }
 ```
 
@@ -147,6 +181,9 @@ Data becomes available in this stream soon after calling one of `addFile()`, `ad
 Clients can call `pipe()` on this stream at any time,
 such as immediately after getting a new `ZipFile` instance, or long after calling `end()`.
 
+As a reminder, be careful using both `.on('data')` and `.pipe()` with this stream.
+In certain versions of node, you cannot use both `.on('data')` and `.pipe()` successfully.
+
 ### dateToDosDateTime(jsDate)
 
 `jsDate` is a `Date` instance.
@@ -187,15 +224,19 @@ refuse to acknowledge General Purpose Bit `8`, which enables utf8 filename encod
 Bit `8` is always set.
 Filenames are always encoded in utf8, even if the result is indistinguishable from ascii.
 
-Bit `3` is set in the Local File Header.
+Bit `3` is usually set in the Local File Header.
 To support both a streaming input and streaming output api,
 it is impossible to know the crc32 before processing the file data.
-File Descriptors are given after each file data with this information, as per the spec.
+When bit `3` is set, file Descriptors are given after each file data with this information, as per the spec.
 But remember a complete metadata listing is still always available in the central directory record,
 so if unzip implementations are relying on that, like they should,
 none of this paragraph will matter anyway.
-Even so, Mac's Archive Utility requires File Descriptors to include the optional signature,
+Even so, some popular unzip implementations do not follow the spec.
+Mac's Archive Utility requires File Descriptors to include the optional signature,
 so yazl includes the optional file descriptor signature.
+When bit `3` is not used, Mac's Archive Utility requires there to be no file descriptor, so yazl skips it in that case.
+Additionally, 7-Zip 9.20 does not seem to support bit `3` at all
+(see [issue #11](https://github.com/thejoshwolfe/yazl/issues/11)).
 
 All other bits are unset.
 
@@ -211,11 +252,36 @@ and is probably not significant in any modern unzip implementation.
 Always `stats.mode << 16`.
 This is apparently the convention for "version made by" = `0x03xx` (UNIX).
 
+Note that for directory entries (see `addEmptyDirectory()`),
+it is conventional to use the lower 8 bits for the MS-DOS directory attribute byte.
+However, the spec says this is only required if the Version Made By is DOS,
+so this library does not do that.
+
 ### Directory Entries
 
-yazl does not record directories themselves as separate entries in the zipfile metadata.
-Instead, file entries with paths (such as "directory/file.txt") imply the need for their parent directories.
-Unzip clients seems to respect this style of pathing,
+When adding a `metadataPath` such as `"parent/file.txt"`, yazl does not add a directory entry for `"parent/"`,
+because file entries imply the need for their parent directories.
+Unzip clients seem to respect this style of pathing,
 and the zip file spec does not specify what is standard in this regard.
 
-Directory entries would be required to archive empty directories (see issue #4).
+In order to create empty directories, use `addEmptyDirectory()`.
+
+## Change History
+
+ * 2.2.2
+   * Fix 7-Zip compatibility issue. [pull request #17](https://github.com/thejoshwolfe/yazl/pull/17)
+ * 2.2.1
+   * Fix Mac Archive Utility compatibility issue. [issue #14](https://github.com/thejoshwolfe/yazl/issues/14)
+ * 2.2.0
+   * Avoid using general purpose bit 3 for `addBuffer()` calls. [issue #13](https://github.com/thejoshwolfe/yazl/issues/13)
+ * 2.1.3
+   * Fix bug when only addBuffer() and end() are called. [issue #12](https://github.com/thejoshwolfe/yazl/issues/12)
+ * 2.1.2
+   * Fixed typo in parameter validation. [pull request #10](https://github.com/thejoshwolfe/yazl/pull/10)
+ * 2.1.1
+   * Fixed stack overflow when using addBuffer() in certain ways. [issue #9](https://github.com/thejoshwolfe/yazl/issues/9)
+ * 2.1.0
+   * Added `addEmptyDirectory()`.
+   * `options` is now optional for `addReadStream()` and `addBuffer()`.
+ * 2.0.0
+   * Initial release.
diff --git a/index.js b/index.js
index 3f4b218..f3eec7a 100644
--- a/index.js
+++ b/index.js
@@ -14,22 +14,23 @@ function ZipFile() {
   this.outputStream = new PassThrough();
   this.entries = [];
   this.outputStreamCursor = 0;
-  this.ended = false;
+  this.ended = false; // .end() sets this
+  this.allDone = false; // set when we've written the last bytes
 }
 
 ZipFile.prototype.addFile = function(realPath, metadataPath, options) {
   var self = this;
-  validateMetadataPath(metadataPath);
+  metadataPath = validateMetadataPath(metadataPath, false);
   if (options == null) options = {};
 
-  var entry = new Entry(metadataPath, options);
+  var entry = new Entry(metadataPath, false, options);
   self.entries.push(entry);
   fs.stat(realPath, function(err, stats) {
     if (err) return self.emit("error", err);
     if (!stats.isFile()) return self.emit("error", new Error("not a file: " + realPath));
     entry.uncompressedSize = stats.size;
-    if (entry.lastModFileTime == null || entry.lastModFileDate == null) entry.setLastModDate(stats.mtime);
-    if (entry.externalFileAttributes == null) entry.setFileAttributesMode(stats.mode);
+    if (options.mtime == null) entry.setLastModDate(stats.mtime);
+    if (options.mode == null) entry.setFileAttributesMode(stats.mode);
     entry.setFileDataPumpFunction(function() {
       var readStream = fs.createReadStream(realPath);
       entry.state = Entry.FILE_DATA_IN_PROGRESS;
@@ -44,27 +45,26 @@ ZipFile.prototype.addFile = function(realPath, metadataPath, options) {
 
 ZipFile.prototype.addReadStream = function(readStream, metadataPath, options) {
   var self = this;
-  validateMetadataPath(metadataPath);
+  metadataPath = validateMetadataPath(metadataPath, false);
   if (options == null) options = {};
-  var entry = new Entry(metadataPath, options);
-  validateFilelessEntryProperties(entry);
+  var entry = new Entry(metadataPath, false, options);
   self.entries.push(entry);
   entry.setFileDataPumpFunction(function() {
     entry.state = Entry.FILE_DATA_IN_PROGRESS;
     pumpFileDataReadStream(self, entry, readStream);
-    pumpEntries(self);
   });
   pumpEntries(self);
 };
 
 ZipFile.prototype.addBuffer = function(buffer, metadataPath, options) {
   var self = this;
-  validateMetadataPath(metadataPath);
+  metadataPath = validateMetadataPath(metadataPath, false);
   if (options == null) options = {};
-  var entry = new Entry(metadataPath, options);
-  validateFilelessEntryProperties(entry, buffer.length);
+  if (options.size != null) throw new Error("options.size not allowed");
+  var entry = new Entry(metadataPath, false, options);
   entry.uncompressedSize = buffer.length;
   entry.crc32 = crc32.unsigned(buffer);
+  entry.crcAndFileSizeKnown = true;
   self.entries.push(entry);
   if (!entry.compress) {
     setCompressedBuffer(buffer);
@@ -79,12 +79,33 @@ ZipFile.prototype.addBuffer = function(buffer, metadataPath, options) {
       writeToOutputStream(self, compressedBuffer);
       writeToOutputStream(self, entry.getFileDescriptor());
       entry.state = Entry.FILE_DATA_DONE;
-      pumpEntries(self);
+
+      // don't call pumpEntries() recursively.
+      // (also, don't call process.nextTick recursively.)
+      setImmediate(function() {
+        pumpEntries(self);
+      });
     });
     pumpEntries(self);
   }
 };
 
+ZipFile.prototype.addEmptyDirectory = function(metadataPath, options) {
+  var self = this;
+  metadataPath = validateMetadataPath(metadataPath, true);
+  if (options == null) options = {};
+  if (options.size != null) throw new Error("options.size not allowed");
+  if (options.compress != null) throw new Error("options.compress not allowed");
+  var entry = new Entry(metadataPath, true, options);
+  self.entries.push(entry);
+  entry.setFileDataPumpFunction(function() {
+    writeToOutputStream(self, entry.getFileDescriptor());
+    entry.state = Entry.FILE_DATA_DONE;
+    pumpEntries(self);
+  });
+  pumpEntries(self);
+};
+
 ZipFile.prototype.end = function(finalSizeCallback) {
   if (this.ended) return;
   this.ended = true;
@@ -123,6 +144,7 @@ function pumpFileDataReadStream(self, entry, readStream) {
 }
 
 function pumpEntries(self) {
+  if (self.allDone) return;
   // first check if finalSize is finally known
   if (self.ended && self.finalSizeCallback != null) {
     var finalSize = calculateFinalSize(self);
@@ -162,6 +184,7 @@ function pumpEntries(self) {
       });
       writeToOutputStream(self, getEndOfCentralDirectoryRecord(self));
       self.outputStream.end();
+      self.allDone = true;
     }
   }
 }
@@ -181,8 +204,8 @@ function calculateFinalSize(self) {
     }
     result += LOCAL_FILE_HEADER_FIXED_SIZE + entry.utf8FileName.length +
               entry.uncompressedSize +
-              FILE_DESCRIPTOR_SIZE +
               CENTRAL_DIRECTORY_RECORD_FIXED_SIZE + entry.utf8FileName.length;
+    if (!entry.crcAndFileSizeKnown) result += FILE_DESCRIPTOR_SIZE;
   }
   result += END_OF_CENTRAL_DIRECTORY_RECORD_SIZE;
   return result;
@@ -203,28 +226,52 @@ function getEndOfCentralDirectoryRecord(self) {
   return buffer;
 }
 
-function validateMetadataPath(metadataPath) {
+function validateMetadataPath(metadataPath, isDirectory) {
+  if (metadataPath === "") throw new Error("empty metadataPath");
   if (metadataPath.indexOf("\\") !== -1) throw new Error("invalid characters in path: " + metadataPath);
   if (/^[a-zA-Z]:/.test(metadataPath) || /^\//.test(metadataPath)) throw new Error("absolute path: " + metadataPath);
   if (metadataPath.split("/").indexOf("..") !== -1) throw new Error("invalid relative path: " + metadataPath);
-}
-function validateFilelessEntryProperties(entry, length) {
-  if (entry.lastModFileTime == null || entry.lastModFileDate == null) throw new Error("missing options.mtime");
-  if (entry.externalFileAttributes == null) throw new Error("missing options.mode");
-  if (entry.uncompressedSize != null && length != null && entry.uncompressedSize !== length) throw new Error("invalid options.size");
+  var looksLikeDirectory = /\/$/.test(metadataPath);
+  if (isDirectory) {
+    // append a trailing '/' if necessary.
+    if (!looksLikeDirectory) metadataPath += "/";
+  } else {
+    if (looksLikeDirectory) throw new Error("file path cannot end with '/': " + metadataPath);
+  }
+  return metadataPath;
 }
 
 // this class is not part of the public API
-function Entry(metadataPath, options) {
+function Entry(metadataPath, isDirectory, options) {
   this.utf8FileName = new Buffer(metadataPath);
   if (this.utf8FileName.length > 0xffff) throw new Error("utf8 file name too long. " + utf8FileName.length + " > " + 0xffff);
+  this.isDirectory = isDirectory;
   this.state = Entry.WAITING_FOR_METADATA;
-  if (options.mtime != null) this.setLastModDate(options.mtime);
-  if (options.mode != null) this.setFileAttributesMode(options.mode);
-  this.uncompressedSize = null; // unknown
-  if (options.size != null) this.uncompressedSize = options.size;
-  this.compress = true; // default
-  if (options.compress != null) this.compress = !!options.compress;
+  this.setLastModDate(options.mtime != null ? options.mtime : new Date());
+  if (options.mode != null) {
+    this.setFileAttributesMode(options.mode);
+  } else {
+    this.setFileAttributesMode(isDirectory ? 040775 : 0100664);
+  }
+  if (isDirectory) {
+    this.crcAndFileSizeKnown = true;
+    this.crc32 = 0;
+    this.uncompressedSize = 0;
+    this.compressedSize = 0;
+  } else {
+    // unknown so far
+    this.crcAndFileSizeKnown = false;
+    this.crc32 = null;
+    this.uncompressedSize = null;
+    this.compressedSize = null;
+    if (options.size != null) this.uncompressedSize = options.size;
+  }
+  if (isDirectory) {
+    this.compress = false;
+  } else {
+    this.compress = true; // default
+    if (options.compress != null) this.compress = !!options.compress;
+  }
 }
 Entry.WAITING_FOR_METADATA = 0;
 Entry.READY_TO_PUMP_FILE_DATA = 1;
@@ -240,6 +287,7 @@ Entry.prototype.setFileAttributesMode = function(mode) {
   // http://unix.stackexchange.com/questions/14705/the-zip-formats-external-file-attribute/14727#14727
   this.externalFileAttributes = (mode << 16) >>> 0;
 };
+// doFileDataPump() should not call pumpEntries() directly. see issue #9.
 Entry.prototype.setFileDataPumpFunction = function(doFileDataPump) {
   this.doFileDataPump = doFileDataPump;
   this.state = Entry.READY_TO_PUMP_FILE_DATA;
@@ -252,17 +300,28 @@ var VERSION_MADE_BY_INFO_ZIP = 0x031e;
 var FILE_NAME_IS_UTF8 = 1 << 11;
 var UNKNOWN_CRC32_AND_FILE_SIZES = 1 << 3;
 Entry.prototype.getLocalFileHeader = function() {
+  var crc32 = 0;
+  var compressedSize = 0;
+  var uncompressedSize = 0;
+  if (this.crcAndFileSizeKnown) {
+    crc32 = this.crc32;
+    compressedSize = this.compressedSize;
+    uncompressedSize = this.uncompressedSize;
+  }
+
   var fixedSizeStuff = new Buffer(LOCAL_FILE_HEADER_FIXED_SIZE);
-  var generalPurposeBitFlag = UNKNOWN_CRC32_AND_FILE_SIZES | FILE_NAME_IS_UTF8;
+  var generalPurposeBitFlag = FILE_NAME_IS_UTF8;
+  if (!this.crcAndFileSizeKnown) generalPurposeBitFlag |= UNKNOWN_CRC32_AND_FILE_SIZES;
+
   fixedSizeStuff.writeUInt32LE(0x04034b50, 0);                  // local file header signature     4 bytes  (0x04034b50)
   fixedSizeStuff.writeUInt16LE(VERSION_NEEDED_TO_EXTRACT, 4);   // version needed to extract       2 bytes
   fixedSizeStuff.writeUInt16LE(generalPurposeBitFlag, 6);       // general purpose bit flag        2 bytes
   fixedSizeStuff.writeUInt16LE(this.getCompressionMethod(), 8); // compression method              2 bytes
   fixedSizeStuff.writeUInt16LE(this.lastModFileTime, 10);       // last mod file time              2 bytes
   fixedSizeStuff.writeUInt16LE(this.lastModFileDate, 12);       // last mod file date              2 bytes
-  fixedSizeStuff.writeUInt32LE(0, 14);                          // crc-32                          4 bytes
-  fixedSizeStuff.writeUInt32LE(0, 18);                          // compressed size                 4 bytes
-  fixedSizeStuff.writeUInt32LE(0, 22);                          // uncompressed size               4 bytes
+  fixedSizeStuff.writeUInt32LE(crc32, 14);                      // crc-32                          4 bytes
+  fixedSizeStuff.writeUInt32LE(compressedSize, 18);             // compressed size                 4 bytes
+  fixedSizeStuff.writeUInt32LE(uncompressedSize, 22);           // uncompressed size               4 bytes
   fixedSizeStuff.writeUInt16LE(this.utf8FileName.length, 26);   // file name length                2 bytes
   fixedSizeStuff.writeUInt16LE(0, 28);                          // extra field length              2 bytes
   return Buffer.concat([
@@ -273,6 +332,10 @@ Entry.prototype.getLocalFileHeader = function() {
 };
 var FILE_DESCRIPTOR_SIZE = 16
 Entry.prototype.getFileDescriptor = function() {
+  if (this.crcAndFileSizeKnown) {
+    // MAC's Archive Utility requires this not be present unless we set general purpose bit 3
+    return new Buffer(0);
+  }
   var buffer = new Buffer(FILE_DESCRIPTOR_SIZE);
   buffer.writeUInt32LE(0x08074b50, 0);             // optional signature (required according to Archive Utility)
   buffer.writeUInt32LE(this.crc32, 4);             // crc-32                          4 bytes
@@ -283,10 +346,13 @@ Entry.prototype.getFileDescriptor = function() {
 var CENTRAL_DIRECTORY_RECORD_FIXED_SIZE = 46;
 Entry.prototype.getCentralDirectoryRecord = function() {
   var fixedSizeStuff = new Buffer(CENTRAL_DIRECTORY_RECORD_FIXED_SIZE);
+  var generalPurposeBitFlag = FILE_NAME_IS_UTF8;
+  if (!this.crcAndFileSizeKnown) generalPurposeBitFlag |= UNKNOWN_CRC32_AND_FILE_SIZES;
+  
   fixedSizeStuff.writeUInt32LE(0x02014b50, 0);                        // central file header signature   4 bytes  (0x02014b50)
   fixedSizeStuff.writeUInt16LE(VERSION_MADE_BY_INFO_ZIP, 4);          // version made by                 2 bytes
   fixedSizeStuff.writeUInt16LE(VERSION_NEEDED_TO_EXTRACT, 6);         // version needed to extract       2 bytes
-  fixedSizeStuff.writeUInt16LE(FILE_NAME_IS_UTF8, 8);                 // general purpose bit flag        2 bytes
+  fixedSizeStuff.writeUInt16LE(generalPurposeBitFlag, 8);             // general purpose bit flag        2 bytes
   fixedSizeStuff.writeUInt16LE(this.getCompressionMethod(), 10);      // compression method              2 bytes
   fixedSizeStuff.writeUInt16LE(this.lastModFileTime, 12);             // last mod file time              2 bytes
   fixedSizeStuff.writeUInt16LE(this.lastModFileDate, 14);             // last mod file date              2 bytes
diff --git a/package.json b/package.json
index 4f9d55f..2ee9c37 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "yazl",
-  "version": "2.0.2",
+  "version": "2.2.2",
   "description": "yet another zip library for node",
   "main": "index.js",
   "scripts": {
@@ -11,7 +11,10 @@
     "url": "https://github.com/thejoshwolfe/yazl.git"
   },
   "keywords": [
-    "zip"
+    "zip",
+    "stream",
+    "archive",
+    "file"
   ],
   "author": "Josh Wolfe <thejoshwolfe at gmail.com>",
   "license": "MIT",
diff --git a/test/test.js b/test/test.js
index 61985a6..0e935f9 100644
--- a/test/test.js
+++ b/test/test.js
@@ -55,3 +55,58 @@ var BufferList = require("bl");
     }));
   });
 })();
+
+(function() {
+  var zipfile = new yazl.ZipFile();
+  // all options parameters are optional
+  zipfile.addFile(__filename, "a.txt");
+  zipfile.addBuffer(new Buffer("buffer"), "b.txt");
+  zipfile.addReadStream(new BufferList().append("stream"), "c.txt");
+  zipfile.addEmptyDirectory("d/");
+  zipfile.addEmptyDirectory("e");
+  zipfile.end(function(finalSize) {
+    if (finalSize !== -1) throw new Error("finalSize should be unknown");
+    zipfile.outputStream.pipe(new BufferList(function(err, data) {
+      if (err) throw err;
+      yauzl.fromBuffer(data, function(err, zipfile) {
+        if (err) throw err;
+        var entryNames = ["a.txt", "b.txt", "c.txt", "d/", "e/"];
+        zipfile.on("entry", function(entry) {
+          var expectedName = entryNames.shift();
+          if (entry.fileName !== expectedName) {
+            throw new Error("unexpected entry fileName: " + entry.fileName + ", expected: " + expectedName);
+          }
+        });
+        zipfile.on("end", function() {
+          if (entryNames.length === 0) console.log("optional parameters and directories: pass");
+        });
+      });
+    }));
+  });
+})();
+
+(function() {
+  var zipfile = new yazl.ZipFile();
+  // all options parameters are optional
+  zipfile.addBuffer(new Buffer("hello"), "hello.txt", {compress: false});
+  zipfile.end(function(finalSize) {
+    if (finalSize === -1) throw new Error("finalSize should be known");
+    zipfile.outputStream.pipe(new BufferList(function(err, data) {
+      if (err) throw err;
+      if (data.length !== finalSize) throw new Error("finalSize prediction is wrong. " + finalSize + " !== " + data.length);
+      yauzl.fromBuffer(data, function(err, zipfile) {
+        if (err) throw err;
+        var entryNames = ["hello.txt"];
+        zipfile.on("entry", function(entry) {
+          var expectedName = entryNames.shift();
+          if (entry.fileName !== expectedName) {
+            throw new Error("unexpected entry fileName: " + entry.fileName + ", expected: " + expectedName);
+          }
+        });
+        zipfile.on("end", function() {
+          if (entryNames.length === 0) console.log("justAddBuffer: pass");
+        });
+      });
+    }));
+  });
+})();
diff --git a/test/zip.js b/test/zip.js
index a8c6a96..24dce5d 100644
--- a/test/zip.js
+++ b/test/zip.js
@@ -1,24 +1,54 @@
 var usage = "node " + __filename.replace(/.*[\/\\]/, "") + " " +
-            "[FILE | --compress | --no-compress]... -o OUTPUT.zip";
+            "[FILE | --compress | --no-compress | --buffer | --no-buffer]... -o OUTPUT.zip" + "\n" +
+            "\n" +
+            "all arguments and switches are processed in order. for example:" + "\n" +
+            "  node zip.js --compress a.txt --no-compress b.txt -o out.zip" + "\n" +
+            "would result in compression for a.txt, but not for b.txt.";
 var yazl = require("../");
 var fs = require("fs");
 
 var zipfile = new yazl.ZipFile();
 var options = {compress: false};
+var use_buffer = false;
 
 var args = process.argv.slice(2);
-if (Math.max(args.indexOf("-h"), args.indexOf("--help")) !== -1) throw new Error("usage: " + usage);
-var outputFileIndex = args.indexOf("-o");
-if (outputFileIndex === -1) throw new Error("missing -o");
-zipfile.outputStream.pipe(fs.createWriteStream(args[outputFileIndex + 1]));
-args.splice(outputFileIndex, 2);
+if (Math.max(args.indexOf("-h"), args.indexOf("--help")) !== -1) {
+  console.log("usage: " + usage);
+  process.exit(1);
+}
+// this one's important
+if (args.indexOf("-o") === -1) throw new Error("missing -o");
+if (args.indexOf("-o") + 1 >= args.length) throw new Error("missing argument after -o");
+
+var its_the_dash_o = false;
 args.forEach(function(arg) {
-  if (/--compress/.test(arg)) {
+  if (its_the_dash_o) {
+    its_the_dash_o = false;
+    zipfile.outputStream.pipe(fs.createWriteStream(arg));
+  } else if (arg === "--compress") {
     options.compress = true;
-  } else if (/--no-compress/.test(arg)) {
+  } else if (arg === "--no-compress") {
     options.compress = false;
+  } else if (arg === "--buffer") {
+    use_buffer = true;
+  } else if (arg === "--no-buffer") {
+    use_buffer = false;
+  } else if (arg === "-o") {
+    its_the_dash_o = true;
   } else {
-    zipfile.addFile(arg, arg, options);
+    // file thing
+    var stats = fs.statSync(arg);
+    if (stats.isFile()) {
+      if (use_buffer) {
+        zipfile.addBuffer(fs.readFileSync(arg), arg, options);
+      } else {
+        zipfile.addFile(arg, arg, options);
+      }
+    } else if (stats.isDirectory()) {
+      zipfile.addEmptyDirectory(arg);
+    } else {
+      throw new Error("what is this: " + arg);
+    }
   }
 });
 zipfile.end();

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-yazl.git



More information about the Pkg-javascript-commits mailing list