diff --git a/lib/encoding.js b/lib/encoding.js index 62a7b00..04b71d9 100644 --- a/lib/encoding.js +++ b/lib/encoding.js @@ -5,6 +5,8 @@ * http://code.google.com/p/stringencoding/source/browse/encoding.js * 09b44d71759d on Sep 19, 2013 * Used under Apache License 2.0 - http://code.google.com/p/stringencoding/ + * + * Filer: modified to remove non-utf8 aspects, converted to CommonJS */ (function(global) { 'use strict'; @@ -266,450 +268,8 @@ } ], "heading": "The Encoding" - }, - { - "encodings": [ - { - "labels": [ - "866", - "cp866", - "csibm866", - "ibm866" - ], - "name": "ibm866" - }, - { - "labels": [ - "csisolatin2", - "iso-8859-2", - "iso-ir-101", - "iso8859-2", - "iso88592", - "iso_8859-2", - "iso_8859-2:1987", - "l2", - "latin2" - ], - "name": "iso-8859-2" - }, - { - "labels": [ - "csisolatin3", - "iso-8859-3", - "iso-ir-109", - "iso8859-3", - "iso88593", - "iso_8859-3", - "iso_8859-3:1988", - "l3", - "latin3" - ], - "name": "iso-8859-3" - }, - { - "labels": [ - "csisolatin4", - "iso-8859-4", - "iso-ir-110", - "iso8859-4", - "iso88594", - "iso_8859-4", - "iso_8859-4:1988", - "l4", - "latin4" - ], - "name": "iso-8859-4" - }, - { - "labels": [ - "csisolatincyrillic", - "cyrillic", - "iso-8859-5", - "iso-ir-144", - "iso8859-5", - "iso88595", - "iso_8859-5", - "iso_8859-5:1988" - ], - "name": "iso-8859-5" - }, - { - "labels": [ - "arabic", - "asmo-708", - "csiso88596e", - "csiso88596i", - "csisolatinarabic", - "ecma-114", - "iso-8859-6", - "iso-8859-6-e", - "iso-8859-6-i", - "iso-ir-127", - "iso8859-6", - "iso88596", - "iso_8859-6", - "iso_8859-6:1987" - ], - "name": "iso-8859-6" - }, - { - "labels": [ - "csisolatingreek", - "ecma-118", - "elot_928", - "greek", - "greek8", - "iso-8859-7", - "iso-ir-126", - "iso8859-7", - "iso88597", - "iso_8859-7", - "iso_8859-7:1987", - "sun_eu_greek" - ], - "name": "iso-8859-7" - }, - { - "labels": [ - "csiso88598e", - "csisolatinhebrew", - "hebrew", - "iso-8859-8", - "iso-8859-8-e", - "iso-ir-138", - "iso8859-8", - "iso88598", - "iso_8859-8", - "iso_8859-8:1988", - "visual" - ], - "name": "iso-8859-8" - }, - { - "labels": [ - "csiso88598i", - "iso-8859-8-i", - "logical" - ], - "name": "iso-8859-8-i" - }, - { - "labels": [ - "csisolatin6", - "iso-8859-10", - "iso-ir-157", - "iso8859-10", - "iso885910", - "l6", - "latin6" - ], - "name": "iso-8859-10" - }, - { - "labels": [ - "iso-8859-13", - "iso8859-13", - "iso885913" - ], - "name": "iso-8859-13" - }, - { - "labels": [ - "iso-8859-14", - "iso8859-14", - "iso885914" - ], - "name": "iso-8859-14" - }, - { - "labels": [ - "csisolatin9", - "iso-8859-15", - "iso8859-15", - "iso885915", - "iso_8859-15", - "l9" - ], - "name": "iso-8859-15" - }, - { - "labels": [ - "iso-8859-16" - ], - "name": "iso-8859-16" - }, - { - "labels": [ - "cskoi8r", - "koi", - "koi8", - "koi8-r", - "koi8_r" - ], - "name": "koi8-r" - }, - { - "labels": [ - "koi8-u" - ], - "name": "koi8-u" - }, - { - "labels": [ - "csmacintosh", - "mac", - "macintosh", - "x-mac-roman" - ], - "name": "macintosh" - }, - { - "labels": [ - "dos-874", - "iso-8859-11", - "iso8859-11", - "iso885911", - "tis-620", - "windows-874" - ], - "name": "windows-874" - }, - { - "labels": [ - "cp1250", - "windows-1250", - "x-cp1250" - ], - "name": "windows-1250" - }, - { - "labels": [ - "cp1251", - "windows-1251", - "x-cp1251" - ], - "name": "windows-1251" - }, - { - "labels": [ - "ansi_x3.4-1968", - "ascii", - "cp1252", - "cp819", - "csisolatin1", - "ibm819", - "iso-8859-1", - "iso-ir-100", - "iso8859-1", - "iso88591", - "iso_8859-1", - "iso_8859-1:1987", - "l1", - "latin1", - "us-ascii", - "windows-1252", - "x-cp1252" - ], - "name": "windows-1252" - }, - { - "labels": [ - "cp1253", - "windows-1253", - "x-cp1253" - ], - "name": "windows-1253" - }, - { - "labels": [ - "cp1254", - "csisolatin5", - "iso-8859-9", - "iso-ir-148", - "iso8859-9", - "iso88599", - "iso_8859-9", - "iso_8859-9:1989", - "l5", - "latin5", - "windows-1254", - "x-cp1254" - ], - "name": "windows-1254" - }, - { - "labels": [ - "cp1255", - "windows-1255", - "x-cp1255" - ], - "name": "windows-1255" - }, - { - "labels": [ - "cp1256", - "windows-1256", - "x-cp1256" - ], - "name": "windows-1256" - }, - { - "labels": [ - "cp1257", - "windows-1257", - "x-cp1257" - ], - "name": "windows-1257" - }, - { - "labels": [ - "cp1258", - "windows-1258", - "x-cp1258" - ], - "name": "windows-1258" - }, - { - "labels": [ - "x-mac-cyrillic", - "x-mac-ukrainian" - ], - "name": "x-mac-cyrillic" - } - ], - "heading": "Legacy single-byte encodings" - }, - { - "encodings": [ - { - "labels": [ - "chinese", - "csgb2312", - "csiso58gb231280", - "gb2312", - "gb_2312", - "gb_2312-80", - "gbk", - "iso-ir-58", - "x-gbk" - ], - "name": "gbk" - }, - { - "labels": [ - "gb18030" - ], - "name": "gb18030" - }, - { - "labels": [ - "hz-gb-2312" - ], - "name": "hz-gb-2312" - } - ], - "heading": "Legacy multi-byte Chinese (simplified) encodings" - }, - { - "encodings": [ - { - "labels": [ - "big5", - "big5-hkscs", - "cn-big5", - "csbig5", - "x-x-big5" - ], - "name": "big5" - } - ], - "heading": "Legacy multi-byte Chinese (traditional) encodings" - }, - { - "encodings": [ - { - "labels": [ - "cseucpkdfmtjapanese", - "euc-jp", - "x-euc-jp" - ], - "name": "euc-jp" - }, - { - "labels": [ - "csiso2022jp", - "iso-2022-jp" - ], - "name": "iso-2022-jp" - }, - { - "labels": [ - "csshiftjis", - "ms_kanji", - "shift-jis", - "shift_jis", - "sjis", - "windows-31j", - "x-sjis" - ], - "name": "shift_jis" - } - ], - "heading": "Legacy multi-byte Japanese encodings" - }, - { - "encodings": [ - { - "labels": [ - "cseuckr", - "csksc56011987", - "euc-kr", - "iso-ir-149", - "korean", - "ks_c_5601-1987", - "ks_c_5601-1989", - "ksc5601", - "ksc_5601", - "windows-949" - ], - "name": "euc-kr" - } - ], - "heading": "Legacy multi-byte Korean encodings" - }, - { - "encodings": [ - { - "labels": [ - "csiso2022kr", - "iso-2022-kr", - "iso-2022-cn", - "iso-2022-cn-ext" - ], - "name": "replacement" - }, - { - "labels": [ - "utf-16be" - ], - "name": "utf-16be" - }, - { - "labels": [ - "utf-16", - "utf-16le" - ], - "name": "utf-16le" - }, - { - "labels": [ - "x-user-defined" - ], - "name": "x-user-defined" - } - ], - "heading": "Legacy miscellaneous encodings" } + // XXXfiler - removed non-utf8 aspects ]; var name_to_encoding = {}; @@ -723,88 +283,6 @@ }); }); - // - // 5. Indexes - // - - /** - * @param {number} pointer The |pointer| to search for. - * @param {Array.} index The |index| to search within. - * @return {?number} The code point corresponding to |pointer| in |index|, - * or null if |code point| is not in |index|. - */ - function indexCodePointFor(pointer, index) { - return (index || [])[pointer] || null; - } - - /** - * @param {number} code_point The |code point| to search for. - * @param {Array.} index The |index| to search within. - * @return {?number} The first pointer corresponding to |code point| in - * |index|, or null if |code point| is not in |index|. - */ - function indexPointerFor(code_point, index) { - var pointer = index.indexOf(code_point); - return pointer === -1 ? null : pointer; - } - - /** - * @param {string} name Name of the index. - * @return {(Array.|Array.>)} - * */ - function index(name) { - if (!('encoding-indexes' in global)) - throw new Error("Indexes missing. Did you forget to include encoding-indexes.js?"); - return global['encoding-indexes'][name]; - } - - /** - * @param {number} pointer The |pointer| to search for in the gb18030 index. - * @return {?number} The code point corresponding to |pointer| in |index|, - * or null if |code point| is not in the gb18030 index. - */ - function indexGB18030CodePointFor(pointer) { - if ((pointer > 39419 && pointer < 189000) || (pointer > 1237575)) { - return null; - } - var /** @type {number} */ offset = 0, - /** @type {number} */ code_point_offset = 0, - /** @type {Array.>} */ idx = index('gb18030'); - var i; - for (i = 0; i < idx.length; ++i) { - var entry = idx[i]; - if (entry[0] <= pointer) { - offset = entry[0]; - code_point_offset = entry[1]; - } else { - break; - } - } - return code_point_offset + pointer - offset; - } - - /** - * @param {number} code_point The |code point| to locate in the gb18030 index. - * @return {number} The first pointer corresponding to |code point| in the - * gb18030 index. - */ - function indexGB18030PointerFor(code_point) { - var /** @type {number} */ offset = 0, - /** @type {number} */ pointer_offset = 0, - /** @type {Array.>} */ idx = index('gb18030'); - var i; - for (i = 0; i < idx.length; ++i) { - var entry = idx[i]; - if (entry[1] <= code_point) { - offset = entry[1]; - pointer_offset = entry[0]; - } else { - break; - } - } - return pointer_offset + code_point - offset; - } - // // 7. The encoding // @@ -939,1212 +417,6 @@ return new UTF8Decoder(options); }; - // - // 8. Legacy single-byte encodings - // - - /** - * @constructor - * @param {Array.} index The encoding index. - * @param {{fatal: boolean}} options - */ - function SingleByteDecoder(index, options) { - var fatal = options.fatal; - /** - * @param {ByteInputStream} byte_pointer The byte stream to decode. - * @return {?number} The next code point decoded, or null if not enough - * data exists in the input stream to decode a complete code point. - */ - this.decode = function(byte_pointer) { - var bite = byte_pointer.get(); - if (bite === EOF_byte) { - return EOF_code_point; - } - byte_pointer.offset(1); - if (inRange(bite, 0x00, 0x7F)) { - return bite; - } - var code_point = index[bite - 0x80]; - if (code_point === null) { - return decoderError(fatal); - } - return code_point; - }; - } - - /** - * @constructor - * @param {Array.} index The encoding index. - * @param {{fatal: boolean}} options - */ - function SingleByteEncoder(index, options) { - var fatal = options.fatal; - /** - * @param {ByteOutputStream} output_byte_stream Output byte stream. - * @param {CodePointInputStream} code_point_pointer Input stream. - * @return {number} The last byte emitted. - */ - this.encode = function(output_byte_stream, code_point_pointer) { - var code_point = code_point_pointer.get(); - if (code_point === EOF_code_point) { - return EOF_byte; - } - code_point_pointer.offset(1); - if (inRange(code_point, 0x0000, 0x007F)) { - return output_byte_stream.emit(code_point); - } - var pointer = indexPointerFor(code_point, index); - if (pointer === null) { - encoderError(code_point); - } - return output_byte_stream.emit(pointer + 0x80); - }; - } - - (function() { - encodings.forEach(function(category) { - if (category.heading !== 'Legacy single-byte encodings') - return; - category.encodings.forEach(function(encoding) { - var idx = index(encoding.name); - encoding.getDecoder = function(options) { - return new SingleByteDecoder(idx, options); - }; - encoding.getEncoder = function(options) { - return new SingleByteEncoder(idx, options); - }; - }); - }); - }()); - - // - // 9. Legacy multi-byte Chinese (simplified) encodings - // - - // 9.1 gbk - - /** - * @constructor - * @param {boolean} gb18030 True if decoding gb18030, false otherwise. - * @param {{fatal: boolean}} options - */ - function GBKDecoder(gb18030, options) { - var fatal = options.fatal; - var /** @type {number} */ gbk_first = 0x00, - /** @type {number} */ gbk_second = 0x00, - /** @type {number} */ gbk_third = 0x00; - /** - * @param {ByteInputStream} byte_pointer The byte stream to decode. - * @return {?number} The next code point decoded, or null if not enough - * data exists in the input stream to decode a complete code point. - */ - this.decode = function(byte_pointer) { - var bite = byte_pointer.get(); - if (bite === EOF_byte && gbk_first === 0x00 && - gbk_second === 0x00 && gbk_third === 0x00) { - return EOF_code_point; - } - if (bite === EOF_byte && - (gbk_first !== 0x00 || gbk_second !== 0x00 || gbk_third !== 0x00)) { - gbk_first = 0x00; - gbk_second = 0x00; - gbk_third = 0x00; - decoderError(fatal); - } - byte_pointer.offset(1); - var code_point; - if (gbk_third !== 0x00) { - code_point = null; - if (inRange(bite, 0x30, 0x39)) { - code_point = indexGB18030CodePointFor( - (((gbk_first - 0x81) * 10 + (gbk_second - 0x30)) * 126 + - (gbk_third - 0x81)) * 10 + bite - 0x30); - } - gbk_first = 0x00; - gbk_second = 0x00; - gbk_third = 0x00; - if (code_point === null) { - byte_pointer.offset(-3); - return decoderError(fatal); - } - return code_point; - } - if (gbk_second !== 0x00) { - if (inRange(bite, 0x81, 0xFE)) { - gbk_third = bite; - return null; - } - byte_pointer.offset(-2); - gbk_first = 0x00; - gbk_second = 0x00; - return decoderError(fatal); - } - if (gbk_first !== 0x00) { - if (inRange(bite, 0x30, 0x39) && gb18030) { - gbk_second = bite; - return null; - } - var lead = gbk_first; - var pointer = null; - gbk_first = 0x00; - var offset = bite < 0x7F ? 0x40 : 0x41; - if (inRange(bite, 0x40, 0x7E) || inRange(bite, 0x80, 0xFE)) { - pointer = (lead - 0x81) * 190 + (bite - offset); - } - code_point = pointer === null ? null : - indexCodePointFor(pointer, index('gbk')); - if (pointer === null) { - byte_pointer.offset(-1); - } - if (code_point === null) { - return decoderError(fatal); - } - return code_point; - } - if (inRange(bite, 0x00, 0x7F)) { - return bite; - } - if (bite === 0x80) { - return 0x20AC; - } - if (inRange(bite, 0x81, 0xFE)) { - gbk_first = bite; - return null; - } - return decoderError(fatal); - }; - } - - /** - * @constructor - * @param {boolean} gb18030 True if decoding gb18030, false otherwise. - * @param {{fatal: boolean}} options - */ - function GBKEncoder(gb18030, options) { - var fatal = options.fatal; - /** - * @param {ByteOutputStream} output_byte_stream Output byte stream. - * @param {CodePointInputStream} code_point_pointer Input stream. - * @return {number} The last byte emitted. - */ - this.encode = function(output_byte_stream, code_point_pointer) { - var code_point = code_point_pointer.get(); - if (code_point === EOF_code_point) { - return EOF_byte; - } - code_point_pointer.offset(1); - if (inRange(code_point, 0x0000, 0x007F)) { - return output_byte_stream.emit(code_point); - } - var pointer = indexPointerFor(code_point, index('gbk')); - if (pointer !== null) { - var lead = div(pointer, 190) + 0x81; - var trail = pointer % 190; - var offset = trail < 0x3F ? 0x40 : 0x41; - return output_byte_stream.emit(lead, trail + offset); - } - if (pointer === null && !gb18030) { - return encoderError(code_point); - } - pointer = indexGB18030PointerFor(code_point); - var byte1 = div(div(div(pointer, 10), 126), 10); - pointer = pointer - byte1 * 10 * 126 * 10; - var byte2 = div(div(pointer, 10), 126); - pointer = pointer - byte2 * 10 * 126; - var byte3 = div(pointer, 10); - var byte4 = pointer - byte3 * 10; - return output_byte_stream.emit(byte1 + 0x81, - byte2 + 0x30, - byte3 + 0x81, - byte4 + 0x30); - }; - } - - name_to_encoding['gbk'].getEncoder = function(options) { - return new GBKEncoder(false, options); - }; - name_to_encoding['gbk'].getDecoder = function(options) { - return new GBKDecoder(false, options); - }; - - // 9.2 gb18030 - name_to_encoding['gb18030'].getEncoder = function(options) { - return new GBKEncoder(true, options); - }; - name_to_encoding['gb18030'].getDecoder = function(options) { - return new GBKDecoder(true, options); - }; - - // 9.3 hz-gb-2312 - - /** - * @constructor - * @param {{fatal: boolean}} options - */ - function HZGB2312Decoder(options) { - var fatal = options.fatal; - var /** @type {boolean} */ hzgb2312 = false, - /** @type {number} */ hzgb2312_lead = 0x00; - /** - * @param {ByteInputStream} byte_pointer The byte stream to decode. - * @return {?number} The next code point decoded, or null if not enough - * data exists in the input stream to decode a complete code point. - */ - this.decode = function(byte_pointer) { - var bite = byte_pointer.get(); - if (bite === EOF_byte && hzgb2312_lead === 0x00) { - return EOF_code_point; - } - if (bite === EOF_byte && hzgb2312_lead !== 0x00) { - hzgb2312_lead = 0x00; - return decoderError(fatal); - } - byte_pointer.offset(1); - if (hzgb2312_lead === 0x7E) { - hzgb2312_lead = 0x00; - if (bite === 0x7B) { - hzgb2312 = true; - return null; - } - if (bite === 0x7D) { - hzgb2312 = false; - return null; - } - if (bite === 0x7E) { - return 0x007E; - } - if (bite === 0x0A) { - return null; - } - byte_pointer.offset(-1); - return decoderError(fatal); - } - if (hzgb2312_lead !== 0x00) { - var lead = hzgb2312_lead; - hzgb2312_lead = 0x00; - var code_point = null; - if (inRange(bite, 0x21, 0x7E)) { - code_point = indexCodePointFor((lead - 1) * 190 + - (bite + 0x3F), index('gbk')); - } - if (bite === 0x0A) { - hzgb2312 = false; - } - if (code_point === null) { - return decoderError(fatal); - } - return code_point; - } - if (bite === 0x7E) { - hzgb2312_lead = 0x7E; - return null; - } - if (hzgb2312) { - if (inRange(bite, 0x20, 0x7F)) { - hzgb2312_lead = bite; - return null; - } - if (bite === 0x0A) { - hzgb2312 = false; - } - return decoderError(fatal); - } - if (inRange(bite, 0x00, 0x7F)) { - return bite; - } - return decoderError(fatal); - }; - } - - /** - * @constructor - * @param {{fatal: boolean}} options - */ - function HZGB2312Encoder(options) { - var fatal = options.fatal; - var hzgb2312 = false; - /** - * @param {ByteOutputStream} output_byte_stream Output byte stream. - * @param {CodePointInputStream} code_point_pointer Input stream. - * @return {number} The last byte emitted. - */ - this.encode = function(output_byte_stream, code_point_pointer) { - var code_point = code_point_pointer.get(); - if (code_point === EOF_code_point) { - return EOF_byte; - } - code_point_pointer.offset(1); - if (inRange(code_point, 0x0000, 0x007F) && hzgb2312) { - code_point_pointer.offset(-1); - hzgb2312 = false; - return output_byte_stream.emit(0x7E, 0x7D); - } - if (code_point === 0x007E) { - return output_byte_stream.emit(0x7E, 0x7E); - } - if (inRange(code_point, 0x0000, 0x007F)) { - return output_byte_stream.emit(code_point); - } - if (!hzgb2312) { - code_point_pointer.offset(-1); - hzgb2312 = true; - return output_byte_stream.emit(0x7E, 0x7B); - } - var pointer = indexPointerFor(code_point, index('gbk')); - if (pointer === null) { - return encoderError(code_point); - } - var lead = div(pointer, 190) + 1; - var trail = pointer % 190 - 0x3F; - if (!inRange(lead, 0x21, 0x7E) || !inRange(trail, 0x21, 0x7E)) { - return encoderError(code_point); - } - return output_byte_stream.emit(lead, trail); - }; - } - - name_to_encoding['hz-gb-2312'].getEncoder = function(options) { - return new HZGB2312Encoder(options); - }; - name_to_encoding['hz-gb-2312'].getDecoder = function(options) { - return new HZGB2312Decoder(options); - }; - - // - // 10. Legacy multi-byte Chinese (traditional) encodings - // - - // 10.1 big5 - - /** - * @constructor - * @param {{fatal: boolean}} options - */ - function Big5Decoder(options) { - var fatal = options.fatal; - var /** @type {number} */ big5_lead = 0x00, - /** @type {?number} */ big5_pending = null; - - /** - * @param {ByteInputStream} byte_pointer The byte steram to decode. - * @return {?number} The next code point decoded, or null if not enough - * data exists in the input stream to decode a complete code point. - */ - this.decode = function(byte_pointer) { - // NOTE: Hack to support emitting two code points - if (big5_pending !== null) { - var pending = big5_pending; - big5_pending = null; - return pending; - } - var bite = byte_pointer.get(); - if (bite === EOF_byte && big5_lead === 0x00) { - return EOF_code_point; - } - if (bite === EOF_byte && big5_lead !== 0x00) { - big5_lead = 0x00; - return decoderError(fatal); - } - byte_pointer.offset(1); - if (big5_lead !== 0x00) { - var lead = big5_lead; - var pointer = null; - big5_lead = 0x00; - var offset = bite < 0x7F ? 0x40 : 0x62; - if (inRange(bite, 0x40, 0x7E) || inRange(bite, 0xA1, 0xFE)) { - pointer = (lead - 0x81) * 157 + (bite - offset); - } - if (pointer === 1133) { - big5_pending = 0x0304; - return 0x00CA; - } - if (pointer === 1135) { - big5_pending = 0x030C; - return 0x00CA; - } - if (pointer === 1164) { - big5_pending = 0x0304; - return 0x00EA; - } - if (pointer === 1166) { - big5_pending = 0x030C; - return 0x00EA; - } - var code_point = (pointer === null) ? null : - indexCodePointFor(pointer, index('big5')); - if (pointer === null) { - byte_pointer.offset(-1); - } - if (code_point === null) { - return decoderError(fatal); - } - return code_point; - } - if (inRange(bite, 0x00, 0x7F)) { - return bite; - } - if (inRange(bite, 0x81, 0xFE)) { - big5_lead = bite; - return null; - } - return decoderError(fatal); - }; - } - - /** - * @constructor - * @param {{fatal: boolean}} options - */ - function Big5Encoder(options) { - var fatal = options.fatal; - /** - * @param {ByteOutputStream} output_byte_stream Output byte stream. - * @param {CodePointInputStream} code_point_pointer Input stream. - * @return {number} The last byte emitted. - */ - this.encode = function(output_byte_stream, code_point_pointer) { - var code_point = code_point_pointer.get(); - if (code_point === EOF_code_point) { - return EOF_byte; - } - code_point_pointer.offset(1); - if (inRange(code_point, 0x0000, 0x007F)) { - return output_byte_stream.emit(code_point); - } - var pointer = indexPointerFor(code_point, index('big5')); - if (pointer === null) { - return encoderError(code_point); - } - var lead = div(pointer, 157) + 0x81; - //if (lead < 0xA1) { - // return encoderError(code_point); - //} - var trail = pointer % 157; - var offset = trail < 0x3F ? 0x40 : 0x62; - return output_byte_stream.emit(lead, trail + offset); - }; - } - - name_to_encoding['big5'].getEncoder = function(options) { - return new Big5Encoder(options); - }; - name_to_encoding['big5'].getDecoder = function(options) { - return new Big5Decoder(options); - }; - - - // - // 11. Legacy multi-byte Japanese encodings - // - - // 11.1 euc.jp - - /** - * @constructor - * @param {{fatal: boolean}} options - */ - function EUCJPDecoder(options) { - var fatal = options.fatal; - var /** @type {number} */ eucjp_first = 0x00, - /** @type {number} */ eucjp_second = 0x00; - /** - * @param {ByteInputStream} byte_pointer The byte stream to decode. - * @return {?number} The next code point decoded, or null if not enough - * data exists in the input stream to decode a complete code point. - */ - this.decode = function(byte_pointer) { - var bite = byte_pointer.get(); - if (bite === EOF_byte) { - if (eucjp_first === 0x00 && eucjp_second === 0x00) { - return EOF_code_point; - } - eucjp_first = 0x00; - eucjp_second = 0x00; - return decoderError(fatal); - } - byte_pointer.offset(1); - - var lead, code_point; - if (eucjp_second !== 0x00) { - lead = eucjp_second; - eucjp_second = 0x00; - code_point = null; - if (inRange(lead, 0xA1, 0xFE) && inRange(bite, 0xA1, 0xFE)) { - code_point = indexCodePointFor((lead - 0xA1) * 94 + bite - 0xA1, - index('jis0212')); - } - if (!inRange(bite, 0xA1, 0xFE)) { - byte_pointer.offset(-1); - } - if (code_point === null) { - return decoderError(fatal); - } - return code_point; - } - if (eucjp_first === 0x8E && inRange(bite, 0xA1, 0xDF)) { - eucjp_first = 0x00; - return 0xFF61 + bite - 0xA1; - } - if (eucjp_first === 0x8F && inRange(bite, 0xA1, 0xFE)) { - eucjp_first = 0x00; - eucjp_second = bite; - return null; - } - if (eucjp_first !== 0x00) { - lead = eucjp_first; - eucjp_first = 0x00; - code_point = null; - if (inRange(lead, 0xA1, 0xFE) && inRange(bite, 0xA1, 0xFE)) { - code_point = indexCodePointFor((lead - 0xA1) * 94 + bite - 0xA1, - index('jis0208')); - } - if (!inRange(bite, 0xA1, 0xFE)) { - byte_pointer.offset(-1); - } - if (code_point === null) { - return decoderError(fatal); - } - return code_point; - } - if (inRange(bite, 0x00, 0x7F)) { - return bite; - } - if (bite === 0x8E || bite === 0x8F || (inRange(bite, 0xA1, 0xFE))) { - eucjp_first = bite; - return null; - } - return decoderError(fatal); - }; - } - - /** - * @constructor - * @param {{fatal: boolean}} options - */ - function EUCJPEncoder(options) { - var fatal = options.fatal; - /** - * @param {ByteOutputStream} output_byte_stream Output byte stream. - * @param {CodePointInputStream} code_point_pointer Input stream. - * @return {number} The last byte emitted. - */ - this.encode = function(output_byte_stream, code_point_pointer) { - var code_point = code_point_pointer.get(); - if (code_point === EOF_code_point) { - return EOF_byte; - } - code_point_pointer.offset(1); - if (inRange(code_point, 0x0000, 0x007F)) { - return output_byte_stream.emit(code_point); - } - if (code_point === 0x00A5) { - return output_byte_stream.emit(0x5C); - } - if (code_point === 0x203E) { - return output_byte_stream.emit(0x7E); - } - if (inRange(code_point, 0xFF61, 0xFF9F)) { - return output_byte_stream.emit(0x8E, code_point - 0xFF61 + 0xA1); - } - - var pointer = indexPointerFor(code_point, index('jis0208')); - if (pointer === null) { - return encoderError(code_point); - } - var lead = div(pointer, 94) + 0xA1; - var trail = pointer % 94 + 0xA1; - return output_byte_stream.emit(lead, trail); - }; - } - - name_to_encoding['euc-jp'].getEncoder = function(options) { - return new EUCJPEncoder(options); - }; - name_to_encoding['euc-jp'].getDecoder = function(options) { - return new EUCJPDecoder(options); - }; - - // 11.2 iso-2022-jp - - /** - * @constructor - * @param {{fatal: boolean}} options - */ - function ISO2022JPDecoder(options) { - var fatal = options.fatal; - /** @enum */ - var state = { - ASCII: 0, - escape_start: 1, - escape_middle: 2, - escape_final: 3, - lead: 4, - trail: 5, - Katakana: 6 - }; - var /** @type {number} */ iso2022jp_state = state.ASCII, - /** @type {boolean} */ iso2022jp_jis0212 = false, - /** @type {number} */ iso2022jp_lead = 0x00; - /** - * @param {ByteInputStream} byte_pointer The byte stream to decode. - * @return {?number} The next code point decoded, or null if not enough - * data exists in the input stream to decode a complete code point. - */ - this.decode = function(byte_pointer) { - var bite = byte_pointer.get(); - if (bite !== EOF_byte) { - byte_pointer.offset(1); - } - switch (iso2022jp_state) { - default: - case state.ASCII: - if (bite === 0x1B) { - iso2022jp_state = state.escape_start; - return null; - } - if (inRange(bite, 0x00, 0x7F)) { - return bite; - } - if (bite === EOF_byte) { - return EOF_code_point; - } - return decoderError(fatal); - - case state.escape_start: - if (bite === 0x24 || bite === 0x28) { - iso2022jp_lead = bite; - iso2022jp_state = state.escape_middle; - return null; - } - if (bite !== EOF_byte) { - byte_pointer.offset(-1); - } - iso2022jp_state = state.ASCII; - return decoderError(fatal); - - case state.escape_middle: - var lead = iso2022jp_lead; - iso2022jp_lead = 0x00; - if (lead === 0x24 && (bite === 0x40 || bite === 0x42)) { - iso2022jp_jis0212 = false; - iso2022jp_state = state.lead; - return null; - } - if (lead === 0x24 && bite === 0x28) { - iso2022jp_state = state.escape_final; - return null; - } - if (lead === 0x28 && (bite === 0x42 || bite === 0x4A)) { - iso2022jp_state = state.ASCII; - return null; - } - if (lead === 0x28 && bite === 0x49) { - iso2022jp_state = state.Katakana; - return null; - } - if (bite === EOF_byte) { - byte_pointer.offset(-1); - } else { - byte_pointer.offset(-2); - } - iso2022jp_state = state.ASCII; - return decoderError(fatal); - - case state.escape_final: - if (bite === 0x44) { - iso2022jp_jis0212 = true; - iso2022jp_state = state.lead; - return null; - } - if (bite === EOF_byte) { - byte_pointer.offset(-2); - } else { - byte_pointer.offset(-3); - } - iso2022jp_state = state.ASCII; - return decoderError(fatal); - - case state.lead: - if (bite === 0x0A) { - iso2022jp_state = state.ASCII; - return decoderError(fatal, 0x000A); - } - if (bite === 0x1B) { - iso2022jp_state = state.escape_start; - return null; - } - if (bite === EOF_byte) { - return EOF_code_point; - } - iso2022jp_lead = bite; - iso2022jp_state = state.trail; - return null; - - case state.trail: - iso2022jp_state = state.lead; - if (bite === EOF_byte) { - return decoderError(fatal); - } - var code_point = null; - var pointer = (iso2022jp_lead - 0x21) * 94 + bite - 0x21; - if (inRange(iso2022jp_lead, 0x21, 0x7E) && - inRange(bite, 0x21, 0x7E)) { - code_point = (iso2022jp_jis0212 === false) ? - indexCodePointFor(pointer, index('jis0208')) : - indexCodePointFor(pointer, index('jis0212')); - } - if (code_point === null) { - return decoderError(fatal); - } - return code_point; - - case state.Katakana: - if (bite === 0x1B) { - iso2022jp_state = state.escape_start; - return null; - } - if (inRange(bite, 0x21, 0x5F)) { - return 0xFF61 + bite - 0x21; - } - if (bite === EOF_byte) { - return EOF_code_point; - } - return decoderError(fatal); - } - }; - } - - /** - * @constructor - * @param {{fatal: boolean}} options - */ - function ISO2022JPEncoder(options) { - var fatal = options.fatal; - /** @enum */ - var state = { - ASCII: 0, - lead: 1, - Katakana: 2 - }; - var /** @type {number} */ iso2022jp_state = state.ASCII; - /** - * @param {ByteOutputStream} output_byte_stream Output byte stream. - * @param {CodePointInputStream} code_point_pointer Input stream. - * @return {number} The last byte emitted. - */ - this.encode = function(output_byte_stream, code_point_pointer) { - var code_point = code_point_pointer.get(); - if (code_point === EOF_code_point) { - return EOF_byte; - } - code_point_pointer.offset(1); - if ((inRange(code_point, 0x0000, 0x007F) || - code_point === 0x00A5 || code_point === 0x203E) && - iso2022jp_state !== state.ASCII) { - code_point_pointer.offset(-1); - iso2022jp_state = state.ASCII; - return output_byte_stream.emit(0x1B, 0x28, 0x42); - } - if (inRange(code_point, 0x0000, 0x007F)) { - return output_byte_stream.emit(code_point); - } - if (code_point === 0x00A5) { - return output_byte_stream.emit(0x5C); - } - if (code_point === 0x203E) { - return output_byte_stream.emit(0x7E); - } - if (inRange(code_point, 0xFF61, 0xFF9F) && - iso2022jp_state !== state.Katakana) { - code_point_pointer.offset(-1); - iso2022jp_state = state.Katakana; - return output_byte_stream.emit(0x1B, 0x28, 0x49); - } - if (inRange(code_point, 0xFF61, 0xFF9F)) { - return output_byte_stream.emit(code_point - 0xFF61 - 0x21); - } - if (iso2022jp_state !== state.lead) { - code_point_pointer.offset(-1); - iso2022jp_state = state.lead; - return output_byte_stream.emit(0x1B, 0x24, 0x42); - } - var pointer = indexPointerFor(code_point, index('jis0208')); - if (pointer === null) { - return encoderError(code_point); - } - var lead = div(pointer, 94) + 0x21; - var trail = pointer % 94 + 0x21; - return output_byte_stream.emit(lead, trail); - }; - } - - name_to_encoding['iso-2022-jp'].getEncoder = function(options) { - return new ISO2022JPEncoder(options); - }; - name_to_encoding['iso-2022-jp'].getDecoder = function(options) { - return new ISO2022JPDecoder(options); - }; - - // 11.3 shift_jis - - /** - * @constructor - * @param {{fatal: boolean}} options - */ - function ShiftJISDecoder(options) { - var fatal = options.fatal; - var /** @type {number} */ shiftjis_lead = 0x00; - /** - * @param {ByteInputStream} byte_pointer The byte stream to decode. - * @return {?number} The next code point decoded, or null if not enough - * data exists in the input stream to decode a complete code point. - */ - this.decode = function(byte_pointer) { - var bite = byte_pointer.get(); - if (bite === EOF_byte && shiftjis_lead === 0x00) { - return EOF_code_point; - } - if (bite === EOF_byte && shiftjis_lead !== 0x00) { - shiftjis_lead = 0x00; - return decoderError(fatal); - } - byte_pointer.offset(1); - if (shiftjis_lead !== 0x00) { - var lead = shiftjis_lead; - shiftjis_lead = 0x00; - if (inRange(bite, 0x40, 0x7E) || inRange(bite, 0x80, 0xFC)) { - var offset = (bite < 0x7F) ? 0x40 : 0x41; - var lead_offset = (lead < 0xA0) ? 0x81 : 0xC1; - var code_point = indexCodePointFor((lead - lead_offset) * 188 + - bite - offset, index('jis0208')); - if (code_point === null) { - return decoderError(fatal); - } - return code_point; - } - byte_pointer.offset(-1); - return decoderError(fatal); - } - if (inRange(bite, 0x00, 0x80)) { - return bite; - } - if (inRange(bite, 0xA1, 0xDF)) { - return 0xFF61 + bite - 0xA1; - } - if (inRange(bite, 0x81, 0x9F) || inRange(bite, 0xE0, 0xFC)) { - shiftjis_lead = bite; - return null; - } - return decoderError(fatal); - }; - } - - /** - * @constructor - * @param {{fatal: boolean}} options - */ - function ShiftJISEncoder(options) { - var fatal = options.fatal; - /** - * @param {ByteOutputStream} output_byte_stream Output byte stream. - * @param {CodePointInputStream} code_point_pointer Input stream. - * @return {number} The last byte emitted. - */ - this.encode = function(output_byte_stream, code_point_pointer) { - var code_point = code_point_pointer.get(); - if (code_point === EOF_code_point) { - return EOF_byte; - } - code_point_pointer.offset(1); - if (inRange(code_point, 0x0000, 0x0080)) { - return output_byte_stream.emit(code_point); - } - if (code_point === 0x00A5) { - return output_byte_stream.emit(0x5C); - } - if (code_point === 0x203E) { - return output_byte_stream.emit(0x7E); - } - if (inRange(code_point, 0xFF61, 0xFF9F)) { - return output_byte_stream.emit(code_point - 0xFF61 + 0xA1); - } - var pointer = indexPointerFor(code_point, index('jis0208')); - if (pointer === null) { - return encoderError(code_point); - } - var lead = div(pointer, 188); - var lead_offset = lead < 0x1F ? 0x81 : 0xC1; - var trail = pointer % 188; - var offset = trail < 0x3F ? 0x40 : 0x41; - return output_byte_stream.emit(lead + lead_offset, trail + offset); - }; - } - - name_to_encoding['shift_jis'].getEncoder = function(options) { - return new ShiftJISEncoder(options); - }; - name_to_encoding['shift_jis'].getDecoder = function(options) { - return new ShiftJISDecoder(options); - }; - - // - // 12. Legacy multi-byte Korean encodings - // - - // 12.1 euc-kr - - /** - * @constructor - * @param {{fatal: boolean}} options - */ - function EUCKRDecoder(options) { - var fatal = options.fatal; - var /** @type {number} */ euckr_lead = 0x00; - /** - * @param {ByteInputStream} byte_pointer The byte stream to decode. - * @return {?number} The next code point decoded, or null if not enough - * data exists in the input stream to decode a complete code point. - */ - this.decode = function(byte_pointer) { - var bite = byte_pointer.get(); - if (bite === EOF_byte && euckr_lead === 0) { - return EOF_code_point; - } - if (bite === EOF_byte && euckr_lead !== 0) { - euckr_lead = 0x00; - return decoderError(fatal); - } - byte_pointer.offset(1); - if (euckr_lead !== 0x00) { - var lead = euckr_lead; - var pointer = null; - euckr_lead = 0x00; - - if (inRange(lead, 0x81, 0xC6)) { - var temp = (26 + 26 + 126) * (lead - 0x81); - if (inRange(bite, 0x41, 0x5A)) { - pointer = temp + bite - 0x41; - } else if (inRange(bite, 0x61, 0x7A)) { - pointer = temp + 26 + bite - 0x61; - } else if (inRange(bite, 0x81, 0xFE)) { - pointer = temp + 26 + 26 + bite - 0x81; - } - } - - if (inRange(lead, 0xC7, 0xFD) && inRange(bite, 0xA1, 0xFE)) { - pointer = (26 + 26 + 126) * (0xC7 - 0x81) + (lead - 0xC7) * 94 + - (bite - 0xA1); - } - - var code_point = (pointer === null) ? null : - indexCodePointFor(pointer, index('euc-kr')); - if (pointer === null) { - byte_pointer.offset(-1); - } - if (code_point === null) { - return decoderError(fatal); - } - return code_point; - } - - if (inRange(bite, 0x00, 0x7F)) { - return bite; - } - - if (inRange(bite, 0x81, 0xFD)) { - euckr_lead = bite; - return null; - } - - return decoderError(fatal); - }; - } - - /** - * @constructor - * @param {{fatal: boolean}} options - */ - function EUCKREncoder(options) { - var fatal = options.fatal; - /** - * @param {ByteOutputStream} output_byte_stream Output byte stream. - * @param {CodePointInputStream} code_point_pointer Input stream. - * @return {number} The last byte emitted. - */ - this.encode = function(output_byte_stream, code_point_pointer) { - var code_point = code_point_pointer.get(); - if (code_point === EOF_code_point) { - return EOF_byte; - } - code_point_pointer.offset(1); - if (inRange(code_point, 0x0000, 0x007F)) { - return output_byte_stream.emit(code_point); - } - var pointer = indexPointerFor(code_point, index('euc-kr')); - if (pointer === null) { - return encoderError(code_point); - } - var lead, trail; - if (pointer < ((26 + 26 + 126) * (0xC7 - 0x81))) { - lead = div(pointer, (26 + 26 + 126)) + 0x81; - trail = pointer % (26 + 26 + 126); - var offset = trail < 26 ? 0x41 : trail < 26 + 26 ? 0x47 : 0x4D; - return output_byte_stream.emit(lead, trail + offset); - } - pointer = pointer - (26 + 26 + 126) * (0xC7 - 0x81); - lead = div(pointer, 94) + 0xC7; - trail = pointer % 94 + 0xA1; - return output_byte_stream.emit(lead, trail); - }; - } - - name_to_encoding['euc-kr'].getEncoder = function(options) { - return new EUCKREncoder(options); - }; - name_to_encoding['euc-kr'].getDecoder = function(options) { - return new EUCKRDecoder(options); - }; - - - // - // 13. Legacy utf-16 encodings - // - - // 13.1 utf-16 - - /** - * @constructor - * @param {boolean} utf16_be True if big-endian, false if little-endian. - * @param {{fatal: boolean}} options - */ - function UTF16Decoder(utf16_be, options) { - var fatal = options.fatal; - var /** @type {?number} */ utf16_lead_byte = null, - /** @type {?number} */ utf16_lead_surrogate = null; - /** - * @param {ByteInputStream} byte_pointer The byte stream to decode. - * @return {?number} The next code point decoded, or null if not enough - * data exists in the input stream to decode a complete code point. - */ - this.decode = function(byte_pointer) { - var bite = byte_pointer.get(); - if (bite === EOF_byte && utf16_lead_byte === null && - utf16_lead_surrogate === null) { - return EOF_code_point; - } - if (bite === EOF_byte && (utf16_lead_byte !== null || - utf16_lead_surrogate !== null)) { - return decoderError(fatal); - } - byte_pointer.offset(1); - if (utf16_lead_byte === null) { - utf16_lead_byte = bite; - return null; - } - var code_point; - if (utf16_be) { - code_point = (utf16_lead_byte << 8) + bite; - } else { - code_point = (bite << 8) + utf16_lead_byte; - } - utf16_lead_byte = null; - if (utf16_lead_surrogate !== null) { - var lead_surrogate = utf16_lead_surrogate; - utf16_lead_surrogate = null; - if (inRange(code_point, 0xDC00, 0xDFFF)) { - return 0x10000 + (lead_surrogate - 0xD800) * 0x400 + - (code_point - 0xDC00); - } - byte_pointer.offset(-2); - return decoderError(fatal); - } - if (inRange(code_point, 0xD800, 0xDBFF)) { - utf16_lead_surrogate = code_point; - return null; - } - if (inRange(code_point, 0xDC00, 0xDFFF)) { - return decoderError(fatal); - } - return code_point; - }; - } - - /** - * @constructor - * @param {boolean} utf16_be True if big-endian, false if little-endian. - * @param {{fatal: boolean}} options - */ - function UTF16Encoder(utf16_be, options) { - var fatal = options.fatal; - /** - * @param {ByteOutputStream} output_byte_stream Output byte stream. - * @param {CodePointInputStream} code_point_pointer Input stream. - * @return {number} The last byte emitted. - */ - this.encode = function(output_byte_stream, code_point_pointer) { - function convert_to_bytes(code_unit) { - var byte1 = code_unit >> 8; - var byte2 = code_unit & 0x00FF; - if (utf16_be) { - return output_byte_stream.emit(byte1, byte2); - } - return output_byte_stream.emit(byte2, byte1); - } - var code_point = code_point_pointer.get(); - if (code_point === EOF_code_point) { - return EOF_byte; - } - code_point_pointer.offset(1); - if (inRange(code_point, 0xD800, 0xDFFF)) { - encoderError(code_point); - } - if (code_point <= 0xFFFF) { - return convert_to_bytes(code_point); - } - var lead = div((code_point - 0x10000), 0x400) + 0xD800; - var trail = ((code_point - 0x10000) % 0x400) + 0xDC00; - convert_to_bytes(lead); - return convert_to_bytes(trail); - }; - } - - name_to_encoding['utf-16le'].getEncoder = function(options) { - return new UTF16Encoder(false, options); - }; - name_to_encoding['utf-16le'].getDecoder = function(options) { - return new UTF16Decoder(false, options); - }; - - // 13.2 utf-16be - name_to_encoding['utf-16be'].getEncoder = function(options) { - return new UTF16Encoder(true, options); - }; - name_to_encoding['utf-16be'].getDecoder = function(options) { - return new UTF16Decoder(true, options); - }; - - - // NOTE: currently unused - /** - * @param {string} label The encoding label. - * @param {ByteInputStream} input_stream The byte stream to test. - */ - function detectEncoding(label, input_stream) { - if (input_stream.match([0xFF, 0xFE])) { - input_stream.offset(2); - return 'utf-16le'; - } - if (input_stream.match([0xFE, 0xFF])) { - input_stream.offset(2); - return 'utf-16be'; - } - if (input_stream.match([0xEF, 0xBB, 0xBF])) { - input_stream.offset(3); - return 'utf-8'; - } - return label; - } - // // Implementation of Text Encoding Web API // @@ -2313,6 +585,9 @@ } }; - global['TextEncoder'] = global['TextEncoder'] || TextEncoder; - global['TextDecoder'] = global['TextDecoder'] || TextDecoder; + // Prefer native impl if available + module.exports = { + TextEncoder: global['TextEncoder'] || TextEncoder, + TextDecoder: global['TextDecoder'] || TextDecoder + }; }(this)); diff --git a/lib/eventemitter.js b/lib/eventemitter.js index 9d6fe8d..617abcd 100644 --- a/lib/eventemitter.js +++ b/lib/eventemitter.js @@ -1,74 +1,71 @@ -define(function(require) { +// Based on https://github.com/diy/intercom.js/blob/master/lib/events.js +// Copyright 2012 DIY Co Apache License, Version 2.0 +// http://www.apache.org/licenses/LICENSE-2.0 - // Based on https://github.com/diy/intercom.js/blob/master/lib/events.js - // Copyright 2012 DIY Co Apache License, Version 2.0 - // http://www.apache.org/licenses/LICENSE-2.0 +function removeItem(item, array) { + for (var i = array.length - 1; i >= 0; i--) { + if (array[i] === item) { + array.splice(i, 1); + } + } + return array; +} - function removeItem(item, array) { - for (var i = array.length - 1; i >= 0; i--) { - if (array[i] === item) { - array.splice(i, 1); +var EventEmitter = function() {}; + +EventEmitter.createInterface = function(space) { + var methods = {}; + + methods.on = function(name, fn) { + if (typeof this[space] === 'undefined') { + this[space] = {}; + } + if (!this[space].hasOwnProperty(name)) { + this[space][name] = []; + } + this[space][name].push(fn); + }; + + methods.off = function(name, fn) { + if (typeof this[space] === 'undefined') return; + if (this[space].hasOwnProperty(name)) { + removeItem(fn, this[space][name]); + } + }; + + methods.trigger = function(name) { + if (typeof this[space] !== 'undefined' && this[space].hasOwnProperty(name)) { + var args = Array.prototype.slice.call(arguments, 1); + for (var i = 0; i < this[space][name].length; i++) { + this[space][name][i].apply(this[space][name][i], args); } } - return array; - } - - var EventEmitter = function() {}; - - EventEmitter.createInterface = function(space) { - var methods = {}; - - methods.on = function(name, fn) { - if (typeof this[space] === 'undefined') { - this[space] = {}; - } - if (!this[space].hasOwnProperty(name)) { - this[space][name] = []; - } - this[space][name].push(fn); - }; - - methods.off = function(name, fn) { - if (typeof this[space] === 'undefined') return; - if (this[space].hasOwnProperty(name)) { - removeItem(fn, this[space][name]); - } - }; - - methods.trigger = function(name) { - if (typeof this[space] !== 'undefined' && this[space].hasOwnProperty(name)) { - var args = Array.prototype.slice.call(arguments, 1); - for (var i = 0; i < this[space][name].length; i++) { - this[space][name][i].apply(this[space][name][i], args); - } - } - }; - - methods.removeAllListeners = function(name) { - if (typeof this[space] === 'undefined') return; - var self = this; - self[space][name].forEach(function(fn) { - self.off(name, fn); - }); - }; - - return methods; }; - var pvt = EventEmitter.createInterface('_handlers'); - EventEmitter.prototype._on = pvt.on; - EventEmitter.prototype._off = pvt.off; - EventEmitter.prototype._trigger = pvt.trigger; - - var pub = EventEmitter.createInterface('handlers'); - EventEmitter.prototype.on = function() { - pub.on.apply(this, arguments); - Array.prototype.unshift.call(arguments, 'on'); - this._trigger.apply(this, arguments); + methods.removeAllListeners = function(name) { + if (typeof this[space] === 'undefined') return; + var self = this; + self[space][name].forEach(function(fn) { + self.off(name, fn); + }); }; - EventEmitter.prototype.off = pub.off; - EventEmitter.prototype.trigger = pub.trigger; - EventEmitter.prototype.removeAllListeners = pub.removeAllListeners; - return EventEmitter; -}); + return methods; +}; + +var pvt = EventEmitter.createInterface('_handlers'); +EventEmitter.prototype._on = pvt.on; +EventEmitter.prototype._off = pvt.off; +EventEmitter.prototype._trigger = pvt.trigger; + +var pub = EventEmitter.createInterface('handlers'); +EventEmitter.prototype.on = function() { + pub.on.apply(this, arguments); + Array.prototype.unshift.call(arguments, 'on'); + this._trigger.apply(this, arguments); +}; +EventEmitter.prototype.off = pub.off; +EventEmitter.prototype.trigger = pub.trigger; +EventEmitter.prototype.removeAllListeners = pub.removeAllListeners; + +module.exports = EventEmitter; diff --git a/lib/intercom.js b/lib/intercom.js index dfe52e0..b16b9b5 100644 --- a/lib/intercom.js +++ b/lib/intercom.js @@ -1,321 +1,318 @@ -define(function(require) { +// Based on https://github.com/diy/intercom.js/blob/master/lib/intercom.js +// Copyright 2012 DIY Co Apache License, Version 2.0 +// http://www.apache.org/licenses/LICENSE-2.0 - // Based on https://github.com/diy/intercom.js/blob/master/lib/intercom.js - // Copyright 2012 DIY Co Apache License, Version 2.0 - // http://www.apache.org/licenses/LICENSE-2.0 +var EventEmitter = require('./eventemitter.js'); +var guid = require('../src/shared.js').guid; - var EventEmitter = require('eventemitter'); - var guid = require('src/shared').guid; - - function throttle(delay, fn) { - var last = 0; - return function() { - var now = Date.now(); - if (now - last > delay) { - last = now; - fn.apply(this, arguments); - } - }; - } - - function extend(a, b) { - if (typeof a === 'undefined' || !a) { a = {}; } - if (typeof b === 'object') { - for (var key in b) { - if (b.hasOwnProperty(key)) { - a[key] = b[key]; - } - } - } - return a; - } - - var localStorage = (function(window) { - if (typeof window === 'undefined' || - typeof window.localStorage === 'undefined') { - return { - getItem : function() {}, - setItem : function() {}, - removeItem : function() {} - }; - } - return window.localStorage; - }(this)); - - function Intercom() { - var self = this; +function throttle(delay, fn) { + var last = 0; + return function() { var now = Date.now(); + if (now - last > delay) { + last = now; + fn.apply(this, arguments); + } + }; +} - this.origin = guid(); - this.lastMessage = now; - this.receivedIDs = {}; - this.previousValues = {}; +function extend(a, b) { + if (typeof a === 'undefined' || !a) { a = {}; } + if (typeof b === 'object') { + for (var key in b) { + if (b.hasOwnProperty(key)) { + a[key] = b[key]; + } + } + } + return a; +} - var storageHandler = function() { - self._onStorageEvent.apply(self, arguments); +var localStorage = (function(window) { + if (typeof window === 'undefined' || + typeof window.localStorage === 'undefined') { + return { + getItem : function() {}, + setItem : function() {}, + removeItem : function() {} }; + } + return window.localStorage; +}(this)); - // If we're in node.js, skip event registration - if (typeof window === 'undefined' || typeof document === 'undefined') { +function Intercom() { + var self = this; + var now = Date.now(); + + this.origin = guid(); + this.lastMessage = now; + this.receivedIDs = {}; + this.previousValues = {}; + + var storageHandler = function() { + self._onStorageEvent.apply(self, arguments); + }; + + // If we're in node.js, skip event registration + if (typeof window === 'undefined' || typeof document === 'undefined') { + return; + } + + if (document.attachEvent) { + document.attachEvent('onstorage', storageHandler); + } else { + window.addEventListener('storage', storageHandler, false); + } +} + +Intercom.prototype._transaction = function(fn) { + var TIMEOUT = 1000; + var WAIT = 20; + var self = this; + var executed = false; + var listening = false; + var waitTimer = null; + + function lock() { + if (executed) { return; } - if (document.attachEvent) { - document.attachEvent('onstorage', storageHandler); - } else { - window.addEventListener('storage', storageHandler, false); + var now = Date.now(); + var activeLock = localStorage.getItem(INDEX_LOCK)|0; + if (activeLock && now - activeLock < TIMEOUT) { + if (!listening) { + self._on('storage', lock); + listening = true; + } + waitTimer = setTimeout(lock, WAIT); + return; } + executed = true; + localStorage.setItem(INDEX_LOCK, now); + + fn(); + unlock(); } - Intercom.prototype._transaction = function(fn) { - var TIMEOUT = 1000; - var WAIT = 20; - var self = this; - var executed = false; - var listening = false; - var waitTimer = null; + function unlock() { + if (listening) { + self._off('storage', lock); + } + if (waitTimer) { + clearTimeout(waitTimer); + } + localStorage.removeItem(INDEX_LOCK); + } - function lock() { - if (executed) { - return; + lock(); +}; + +Intercom.prototype._cleanup_emit = throttle(100, function() { + var self = this; + + self._transaction(function() { + var now = Date.now(); + var threshold = now - THRESHOLD_TTL_EMIT; + var changed = 0; + var messages; + + try { + messages = JSON.parse(localStorage.getItem(INDEX_EMIT) || '[]'); + } catch(e) { + messages = []; + } + for (var i = messages.length - 1; i >= 0; i--) { + if (messages[i].timestamp < threshold) { + messages.splice(i, 1); + changed++; } + } + if (changed > 0) { + localStorage.setItem(INDEX_EMIT, JSON.stringify(messages)); + } + }); +}); - var now = Date.now(); - var activeLock = localStorage.getItem(INDEX_LOCK)|0; - if (activeLock && now - activeLock < TIMEOUT) { - if (!listening) { - self._on('storage', lock); - listening = true; - } - waitTimer = setTimeout(lock, WAIT); - return; +Intercom.prototype._cleanup_once = throttle(100, function() { + var self = this; + + self._transaction(function() { + var timestamp, ttl, key; + var table; + var now = Date.now(); + var changed = 0; + + try { + table = JSON.parse(localStorage.getItem(INDEX_ONCE) || '{}'); + } catch(e) { + table = {}; + } + for (key in table) { + if (self._once_expired(key, table)) { + delete table[key]; + changed++; } - executed = true; - localStorage.setItem(INDEX_LOCK, now); - - fn(); - unlock(); } - function unlock() { - if (listening) { - self._off('storage', lock); - } - if (waitTimer) { - clearTimeout(waitTimer); - } - localStorage.removeItem(INDEX_LOCK); + if (changed > 0) { + localStorage.setItem(INDEX_ONCE, JSON.stringify(table)); } + }); +}); - lock(); - }; +Intercom.prototype._once_expired = function(key, table) { + if (!table) { + return true; + } + if (!table.hasOwnProperty(key)) { + return true; + } + if (typeof table[key] !== 'object') { + return true; + } - Intercom.prototype._cleanup_emit = throttle(100, function() { - var self = this; + var ttl = table[key].ttl || THRESHOLD_TTL_ONCE; + var now = Date.now(); + var timestamp = table[key].timestamp; + return timestamp < now - ttl; +}; - self._transaction(function() { +Intercom.prototype._localStorageChanged = function(event, field) { + if (event && event.key) { + return event.key === field; + } + + var currentValue = localStorage.getItem(field); + if (currentValue === this.previousValues[field]) { + return false; + } + this.previousValues[field] = currentValue; + return true; +}; + +Intercom.prototype._onStorageEvent = function(event) { + event = event || window.event; + var self = this; + + if (this._localStorageChanged(event, INDEX_EMIT)) { + this._transaction(function() { var now = Date.now(); - var threshold = now - THRESHOLD_TTL_EMIT; - var changed = 0; + var data = localStorage.getItem(INDEX_EMIT); var messages; try { - messages = JSON.parse(localStorage.getItem(INDEX_EMIT) || '[]'); + messages = JSON.parse(data || '[]'); } catch(e) { messages = []; } - for (var i = messages.length - 1; i >= 0; i--) { - if (messages[i].timestamp < threshold) { - messages.splice(i, 1); - changed++; + for (var i = 0; i < messages.length; i++) { + if (messages[i].origin === self.origin) continue; + if (messages[i].timestamp < self.lastMessage) continue; + if (messages[i].id) { + if (self.receivedIDs.hasOwnProperty(messages[i].id)) continue; + self.receivedIDs[messages[i].id] = true; } + self.trigger(messages[i].name, messages[i].payload); } - if (changed > 0) { - localStorage.setItem(INDEX_EMIT, JSON.stringify(messages)); - } + self.lastMessage = now; }); + } + + this._trigger('storage', event); +}; + +Intercom.prototype._emit = function(name, message, id) { + id = (typeof id === 'string' || typeof id === 'number') ? String(id) : null; + if (id && id.length) { + if (this.receivedIDs.hasOwnProperty(id)) return; + this.receivedIDs[id] = true; + } + + var packet = { + id : id, + name : name, + origin : this.origin, + timestamp : Date.now(), + payload : message + }; + + var self = this; + this._transaction(function() { + var data = localStorage.getItem(INDEX_EMIT) || '[]'; + var delimiter = (data === '[]') ? '' : ','; + data = [data.substring(0, data.length - 1), delimiter, JSON.stringify(packet), ']'].join(''); + localStorage.setItem(INDEX_EMIT, data); + self.trigger(name, message); + + setTimeout(function() { + self._cleanup_emit(); + }, 50); }); +}; - Intercom.prototype._cleanup_once = throttle(100, function() { - var self = this; +Intercom.prototype.emit = function(name, message) { + this._emit.apply(this, arguments); + this._trigger('emit', name, message); +}; - self._transaction(function() { - var timestamp, ttl, key; - var table; - var now = Date.now(); - var changed = 0; +Intercom.prototype.once = function(key, fn, ttl) { + if (!Intercom.supported) { + return; + } - try { - table = JSON.parse(localStorage.getItem(INDEX_ONCE) || '{}'); - } catch(e) { - table = {}; - } - for (key in table) { - if (self._once_expired(key, table)) { - delete table[key]; - changed++; - } - } - - if (changed > 0) { - localStorage.setItem(INDEX_ONCE, JSON.stringify(table)); - } - }); - }); - - Intercom.prototype._once_expired = function(key, table) { - if (!table) { - return true; + var self = this; + this._transaction(function() { + var data; + try { + data = JSON.parse(localStorage.getItem(INDEX_ONCE) || '{}'); + } catch(e) { + data = {}; } - if (!table.hasOwnProperty(key)) { - return true; - } - if (typeof table[key] !== 'object') { - return true; - } - - var ttl = table[key].ttl || THRESHOLD_TTL_ONCE; - var now = Date.now(); - var timestamp = table[key].timestamp; - return timestamp < now - ttl; - }; - - Intercom.prototype._localStorageChanged = function(event, field) { - if (event && event.key) { - return event.key === field; - } - - var currentValue = localStorage.getItem(field); - if (currentValue === this.previousValues[field]) { - return false; - } - this.previousValues[field] = currentValue; - return true; - }; - - Intercom.prototype._onStorageEvent = function(event) { - event = event || window.event; - var self = this; - - if (this._localStorageChanged(event, INDEX_EMIT)) { - this._transaction(function() { - var now = Date.now(); - var data = localStorage.getItem(INDEX_EMIT); - var messages; - - try { - messages = JSON.parse(data || '[]'); - } catch(e) { - messages = []; - } - for (var i = 0; i < messages.length; i++) { - if (messages[i].origin === self.origin) continue; - if (messages[i].timestamp < self.lastMessage) continue; - if (messages[i].id) { - if (self.receivedIDs.hasOwnProperty(messages[i].id)) continue; - self.receivedIDs[messages[i].id] = true; - } - self.trigger(messages[i].name, messages[i].payload); - } - self.lastMessage = now; - }); - } - - this._trigger('storage', event); - }; - - Intercom.prototype._emit = function(name, message, id) { - id = (typeof id === 'string' || typeof id === 'number') ? String(id) : null; - if (id && id.length) { - if (this.receivedIDs.hasOwnProperty(id)) return; - this.receivedIDs[id] = true; - } - - var packet = { - id : id, - name : name, - origin : this.origin, - timestamp : Date.now(), - payload : message - }; - - var self = this; - this._transaction(function() { - var data = localStorage.getItem(INDEX_EMIT) || '[]'; - var delimiter = (data === '[]') ? '' : ','; - data = [data.substring(0, data.length - 1), delimiter, JSON.stringify(packet), ']'].join(''); - localStorage.setItem(INDEX_EMIT, data); - self.trigger(name, message); - - setTimeout(function() { - self._cleanup_emit(); - }, 50); - }); - }; - - Intercom.prototype.emit = function(name, message) { - this._emit.apply(this, arguments); - this._trigger('emit', name, message); - }; - - Intercom.prototype.once = function(key, fn, ttl) { - if (!Intercom.supported) { + if (!self._once_expired(key, data)) { return; } - var self = this; - this._transaction(function() { - var data; - try { - data = JSON.parse(localStorage.getItem(INDEX_ONCE) || '{}'); - } catch(e) { - data = {}; - } - if (!self._once_expired(key, data)) { - return; - } + data[key] = {}; + data[key].timestamp = Date.now(); + if (typeof ttl === 'number') { + data[key].ttl = ttl * 1000; + } - data[key] = {}; - data[key].timestamp = Date.now(); - if (typeof ttl === 'number') { - data[key].ttl = ttl * 1000; - } + localStorage.setItem(INDEX_ONCE, JSON.stringify(data)); + fn(); - localStorage.setItem(INDEX_ONCE, JSON.stringify(data)); - fn(); + setTimeout(function() { + self._cleanup_once(); + }, 50); + }); +}; - setTimeout(function() { - self._cleanup_once(); - }, 50); - }); +extend(Intercom.prototype, EventEmitter.prototype); + +Intercom.supported = (typeof localStorage !== 'undefined'); + +var INDEX_EMIT = 'intercom'; +var INDEX_ONCE = 'intercom_once'; +var INDEX_LOCK = 'intercom_lock'; + +var THRESHOLD_TTL_EMIT = 50000; +var THRESHOLD_TTL_ONCE = 1000 * 3600; + +Intercom.destroy = function() { + localStorage.removeItem(INDEX_LOCK); + localStorage.removeItem(INDEX_EMIT); + localStorage.removeItem(INDEX_ONCE); +}; + +Intercom.getInstance = (function() { + var intercom; + return function() { + if (!intercom) { + intercom = new Intercom(); + } + return intercom; }; +})(); - extend(Intercom.prototype, EventEmitter.prototype); - - Intercom.supported = (typeof localStorage !== 'undefined'); - - var INDEX_EMIT = 'intercom'; - var INDEX_ONCE = 'intercom_once'; - var INDEX_LOCK = 'intercom_lock'; - - var THRESHOLD_TTL_EMIT = 50000; - var THRESHOLD_TTL_ONCE = 1000 * 3600; - - Intercom.destroy = function() { - localStorage.removeItem(INDEX_LOCK); - localStorage.removeItem(INDEX_EMIT); - localStorage.removeItem(INDEX_ONCE); - }; - - Intercom.getInstance = (function() { - var intercom; - return function() { - if (!intercom) { - intercom = new Intercom(); - } - return intercom; - }; - })(); - - return Intercom; -}); +module.exports = Intercom; diff --git a/lib/nodash.js b/lib/nodash.js index 63b38ff..e7e19c9 100644 --- a/lib/nodash.js +++ b/lib/nodash.js @@ -7,96 +7,91 @@ * Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors * Available under MIT license */ +var ArrayProto = Array.prototype; +var nativeForEach = ArrayProto.forEach; +var nativeIndexOf = ArrayProto.indexOf; +var nativeSome = ArrayProto.some; -define(function(require) { +var ObjProto = Object.prototype; +var hasOwnProperty = ObjProto.hasOwnProperty; +var nativeKeys = Object.keys; - var ArrayProto = Array.prototype; - var nativeForEach = ArrayProto.forEach; - var nativeIndexOf = ArrayProto.indexOf; - var nativeSome = ArrayProto.some; +var breaker = {}; - var ObjProto = Object.prototype; - var hasOwnProperty = ObjProto.hasOwnProperty; - var nativeKeys = Object.keys; +function has(obj, key) { + return hasOwnProperty.call(obj, key); +} - var breaker = {}; +var keys = nativeKeys || function(obj) { + if (obj !== Object(obj)) throw new TypeError('Invalid object'); + var keys = []; + for (var key in obj) if (has(obj, key)) keys.push(key); + return keys; +}; - function has(obj, key) { - return hasOwnProperty.call(obj, key); - } +function size(obj) { + if (obj == null) return 0; + return (obj.length === +obj.length) ? obj.length : keys(obj).length; +} - var keys = nativeKeys || function(obj) { - if (obj !== Object(obj)) throw new TypeError('Invalid object'); - var keys = []; - for (var key in obj) if (has(obj, key)) keys.push(key); - return keys; - }; +function identity(value) { + return value; +} - function size(obj) { - if (obj == null) return 0; - return (obj.length === +obj.length) ? obj.length : keys(obj).length; - } - - function identity(value) { - return value; - } - - function each(obj, iterator, context) { - var i, length; - if (obj == null) return; - if (nativeForEach && obj.forEach === nativeForEach) { - obj.forEach(iterator, context); - } else if (obj.length === +obj.length) { - for (i = 0, length = obj.length; i < length; i++) { - if (iterator.call(context, obj[i], i, obj) === breaker) return; - } - } else { - var keys = keys(obj); - for (i = 0, length = keys.length; i < length; i++) { - if (iterator.call(context, obj[keys[i]], keys[i], obj) === breaker) return; - } +function each(obj, iterator, context) { + var i, length; + if (obj == null) return; + if (nativeForEach && obj.forEach === nativeForEach) { + obj.forEach(iterator, context); + } else if (obj.length === +obj.length) { + for (i = 0, length = obj.length; i < length; i++) { + if (iterator.call(context, obj[i], i, obj) === breaker) return; + } + } else { + var keys = keys(obj); + for (i = 0, length = keys.length; i < length; i++) { + if (iterator.call(context, obj[keys[i]], keys[i], obj) === breaker) return; } - }; - - function any(obj, iterator, context) { - iterator || (iterator = identity); - var result = false; - if (obj == null) return result; - if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context); - each(obj, function(value, index, list) { - if (result || (result = iterator.call(context, value, index, list))) return breaker; - }); - return !!result; - }; - - function contains(obj, target) { - if (obj == null) return false; - if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1; - return any(obj, function(value) { - return value === target; - }); - }; - - function Wrapped(value) { - this.value = value; } - Wrapped.prototype.has = function(key) { - return has(this.value, key); - }; - Wrapped.prototype.contains = function(target) { - return contains(this.value, target); - }; - Wrapped.prototype.size = function() { - return size(this.value); - }; +}; - function nodash(value) { - // don't wrap if already wrapped, even if wrapped by a different `lodash` constructor - return (value && typeof value == 'object' && !Array.isArray(value) && hasOwnProperty.call(value, '__wrapped__')) - ? value - : new Wrapped(value); - } +function any(obj, iterator, context) { + iterator || (iterator = identity); + var result = false; + if (obj == null) return result; + if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context); + each(obj, function(value, index, list) { + if (result || (result = iterator.call(context, value, index, list))) return breaker; + }); + return !!result; +}; - return nodash; +function contains(obj, target) { + if (obj == null) return false; + if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1; + return any(obj, function(value) { + return value === target; + }); +}; -}); +function Wrapped(value) { + this.value = value; +} +Wrapped.prototype.has = function(key) { + return has(this.value, key); +}; +Wrapped.prototype.contains = function(target) { + return contains(this.value, target); +}; +Wrapped.prototype.size = function() { + return size(this.value); +}; + +function nodash(value) { + // don't wrap if already wrapped, even if wrapped by a different `lodash` constructor + return (value && typeof value == 'object' && !Array.isArray(value) && hasOwnProperty.call(value, '__wrapped__')) + ? value + : new Wrapped(value); +} + +module.exports = nodash; diff --git a/lib/unzip.js b/lib/unzip.js deleted file mode 100644 index 1c1a364..0000000 --- a/lib/unzip.js +++ /dev/null @@ -1,31 +0,0 @@ -/** @license zlib.js 2012 - imaya [ https://github.com/imaya/zlib.js ] The MIT License */(function() {'use strict';function m(a){throw a;}var q=void 0,u,aa=this;function v(a,b){var c=a.split("."),d=aa;!(c[0]in d)&&d.execScript&&d.execScript("var "+c[0]);for(var f;c.length&&(f=c.shift());)!c.length&&b!==q?d[f]=b:d=d[f]?d[f]:d[f]={}};var w="undefined"!==typeof Uint8Array&&"undefined"!==typeof Uint16Array&&"undefined"!==typeof Uint32Array&&"undefined"!==typeof DataView;new (w?Uint8Array:Array)(256);var x;for(x=0;256>x;++x)for(var y=x,ba=7,y=y>>>1;y;y>>>=1)--ba;var z=[0,1996959894,3993919788,2567524794,124634137,1886057615,3915621685,2657392035,249268274,2044508324,3772115230,2547177864,162941995,2125561021,3887607047,2428444049,498536548,1789927666,4089016648,2227061214,450548861,1843258603,4107580753,2211677639,325883990,1684777152,4251122042,2321926636,335633487,1661365465,4195302755,2366115317,997073096,1281953886,3579855332,2724688242,1006888145,1258607687,3524101629,2768942443,901097722,1119000684,3686517206,2898065728,853044451,1172266101,3705015759, -2882616665,651767980,1373503546,3369554304,3218104598,565507253,1454621731,3485111705,3099436303,671266974,1594198024,3322730930,2970347812,795835527,1483230225,3244367275,3060149565,1994146192,31158534,2563907772,4023717930,1907459465,112637215,2680153253,3904427059,2013776290,251722036,2517215374,3775830040,2137656763,141376813,2439277719,3865271297,1802195444,476864866,2238001368,4066508878,1812370925,453092731,2181625025,4111451223,1706088902,314042704,2344532202,4240017532,1658658271,366619977, -2362670323,4224994405,1303535960,984961486,2747007092,3569037538,1256170817,1037604311,2765210733,3554079995,1131014506,879679996,2909243462,3663771856,1141124467,855842277,2852801631,3708648649,1342533948,654459306,3188396048,3373015174,1466479909,544179635,3110523913,3462522015,1591671054,702138776,2966460450,3352799412,1504918807,783551873,3082640443,3233442989,3988292384,2596254646,62317068,1957810842,3939845945,2647816111,81470997,1943803523,3814918930,2489596804,225274430,2053790376,3826175755, -2466906013,167816743,2097651377,4027552580,2265490386,503444072,1762050814,4150417245,2154129355,426522225,1852507879,4275313526,2312317920,282753626,1742555852,4189708143,2394877945,397917763,1622183637,3604390888,2714866558,953729732,1340076626,3518719985,2797360999,1068828381,1219638859,3624741850,2936675148,906185462,1090812512,3747672003,2825379669,829329135,1181335161,3412177804,3160834842,628085408,1382605366,3423369109,3138078467,570562233,1426400815,3317316542,2998733608,733239954,1555261956, -3268935591,3050360625,752459403,1541320221,2607071920,3965973030,1969922972,40735498,2617837225,3943577151,1913087877,83908371,2512341634,3803740692,2075208622,213261112,2463272603,3855990285,2094854071,198958881,2262029012,4057260610,1759359992,534414190,2176718541,4139329115,1873836001,414664567,2282248934,4279200368,1711684554,285281116,2405801727,4167216745,1634467795,376229701,2685067896,3608007406,1308918612,956543938,2808555105,3495958263,1231636301,1047427035,2932959818,3654703836,1088359270, -936918E3,2847714899,3736837829,1202900863,817233897,3183342108,3401237130,1404277552,615818150,3134207493,3453421203,1423857449,601450431,3009837614,3294710456,1567103746,711928724,3020668471,3272380065,1510334235,755167117],B=w?new Uint32Array(z):z;function C(a){var b=a.length,c=0,d=Number.POSITIVE_INFINITY,f,h,k,e,g,l,p,s,r,A;for(s=0;sc&&(c=a[s]),a[s]>=1;A=k<<16|s;for(r=l;rE;E++)switch(!0){case 143>=E:D.push([E+48,8]);break;case 255>=E:D.push([E-144+400,9]);break;case 279>=E:D.push([E-256+0,7]);break;case 287>=E:D.push([E-280+192,8]);break;default:m("invalid literal: "+E)} -var ca=function(){function a(a){switch(!0){case 3===a:return[257,a-3,0];case 4===a:return[258,a-4,0];case 5===a:return[259,a-5,0];case 6===a:return[260,a-6,0];case 7===a:return[261,a-7,0];case 8===a:return[262,a-8,0];case 9===a:return[263,a-9,0];case 10===a:return[264,a-10,0];case 12>=a:return[265,a-11,1];case 14>=a:return[266,a-13,1];case 16>=a:return[267,a-15,1];case 18>=a:return[268,a-17,1];case 22>=a:return[269,a-19,2];case 26>=a:return[270,a-23,2];case 30>=a:return[271,a-27,2];case 34>=a:return[272, -a-31,2];case 42>=a:return[273,a-35,3];case 50>=a:return[274,a-43,3];case 58>=a:return[275,a-51,3];case 66>=a:return[276,a-59,3];case 82>=a:return[277,a-67,4];case 98>=a:return[278,a-83,4];case 114>=a:return[279,a-99,4];case 130>=a:return[280,a-115,4];case 162>=a:return[281,a-131,5];case 194>=a:return[282,a-163,5];case 226>=a:return[283,a-195,5];case 257>=a:return[284,a-227,5];case 258===a:return[285,a-258,0];default:m("invalid length: "+a)}}var b=[],c,d;for(c=3;258>=c;c++)d=a(c),b[c]=d[2]<<24|d[1]<< -16|d[0];return b}();w&&new Uint32Array(ca);function F(a,b){this.l=[];this.m=32768;this.d=this.f=this.c=this.t=0;this.input=w?new Uint8Array(a):a;this.u=!1;this.n=G;this.L=!1;if(b||!(b={}))b.index&&(this.c=b.index),b.bufferSize&&(this.m=b.bufferSize),b.bufferType&&(this.n=b.bufferType),b.resize&&(this.L=b.resize);switch(this.n){case H:this.a=32768;this.b=new (w?Uint8Array:Array)(32768+this.m+258);break;case G:this.a=0;this.b=new (w?Uint8Array:Array)(this.m);this.e=this.X;this.B=this.S;this.q=this.W;break;default:m(Error("invalid inflate mode"))}} -var H=0,G=1; -F.prototype.r=function(){for(;!this.u;){var a=I(this,3);a&1&&(this.u=!0);a>>>=1;switch(a){case 0:var b=this.input,c=this.c,d=this.b,f=this.a,h=b.length,k=q,e=q,g=d.length,l=q;this.d=this.f=0;c+1>=h&&m(Error("invalid uncompressed block header: LEN"));k=b[c++]|b[c++]<<8;c+1>=h&&m(Error("invalid uncompressed block header: NLEN"));e=b[c++]|b[c++]<<8;k===~e&&m(Error("invalid uncompressed block header: length verify"));c+k>b.length&&m(Error("input buffer is broken"));switch(this.n){case H:for(;f+k>d.length;){l= -g-f;k-=l;if(w)d.set(b.subarray(c,c+l),f),f+=l,c+=l;else for(;l--;)d[f++]=b[c++];this.a=f;d=this.e();f=this.a}break;case G:for(;f+k>d.length;)d=this.e({H:2});break;default:m(Error("invalid inflate mode"))}if(w)d.set(b.subarray(c,c+k),f),f+=k,c+=k;else for(;k--;)d[f++]=b[c++];this.c=c;this.a=f;this.b=d;break;case 1:this.q(da,ea);break;case 2:fa(this);break;default:m(Error("unknown BTYPE: "+a))}}return this.B()}; -var J=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],K=w?new Uint16Array(J):J,L=[3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258,258,258],M=w?new Uint16Array(L):L,ga=[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0],O=w?new Uint8Array(ga):ga,ha=[1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577],ia=w?new Uint16Array(ha):ha,ja=[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11, -12,12,13,13],P=w?new Uint8Array(ja):ja,Q=new (w?Uint8Array:Array)(288),R,la;R=0;for(la=Q.length;R=R?8:255>=R?9:279>=R?7:8;var da=C(Q),S=new (w?Uint8Array:Array)(30),T,ma;T=0;for(ma=S.length;T=k&&m(Error("input buffer is broken")),c|=f[h++]<>>b;a.d=d-b;a.c=h;return e} -function U(a,b){for(var c=a.f,d=a.d,f=a.input,h=a.c,k=f.length,e=b[0],g=b[1],l,p;d=k);)c|=f[h++]<>>16;a.f=c>>p;a.d=d-p;a.c=h;return l&65535} -function fa(a){function b(a,b,c){var d,e=this.K,f,g;for(g=0;gh)d>=f&&(this.a=d,c=this.e(),d=this.a),c[d++]=h;else{k=h-257;g=M[k];0=f&&(this.a=d,c=this.e(),d=this.a);for(;g--;)c[d]=c[d++-e]}for(;8<=this.d;)this.d-=8,this.c--;this.a=d}; -u.W=function(a,b){var c=this.b,d=this.a;this.C=a;for(var f=c.length,h,k,e,g;256!==(h=U(this,a));)if(256>h)d>=f&&(c=this.e(),f=c.length),c[d++]=h;else{k=h-257;g=M[k];0f&&(c=this.e(),f=c.length);for(;g--;)c[d]=c[d++-e]}for(;8<=this.d;)this.d-=8,this.c--;this.a=d}; -u.e=function(){var a=new (w?Uint8Array:Array)(this.a-32768),b=this.a-32768,c,d,f=this.b;if(w)a.set(f.subarray(32768,a.length));else{c=0;for(d=a.length;cc;++c)f[c]=f[b+c];this.a=32768;return f}; -u.X=function(a){var b,c=this.input.length/this.c+1|0,d,f,h,k=this.input,e=this.b;a&&("number"===typeof a.H&&(c=a.H),"number"===typeof a.Q&&(c+=a.Q));2>c?(d=(k.length-this.c)/this.C[2],h=258*(d/2)|0,f=hb&&(this.b.length=b),a=this.b);return this.buffer=a};function V(a){a=a||{};this.files=[];this.v=a.comment}V.prototype.M=function(a){this.j=a};V.prototype.s=function(a){var b=a[2]&65535|2;return b*(b^1)>>8&255};V.prototype.k=function(a,b){a[0]=(B[(a[0]^b)&255]^a[0]>>>8)>>>0;a[1]=(6681*(20173*(a[1]+(a[0]&255))>>>0)>>>0)+1>>>0;a[2]=(B[(a[2]^a[1]>>>24)&255]^a[2]>>>8)>>>0};V.prototype.U=function(a){var b=[305419896,591751049,878082192],c,d;w&&(b=new Uint32Array(b));c=0;for(d=a.length;c>>0;this.z=(a[b++]|a[b++]<<8|a[b++]<<16|a[b++]<<24)>>>0;this.J=(a[b++]|a[b++]<<8|a[b++]<<16|a[b++]<<24)>>>0;this.h=a[b++]|a[b++]<< -8;this.g=a[b++]|a[b++]<<8;this.F=a[b++]|a[b++]<<8;this.fa=a[b++]|a[b++]<<8;this.ha=a[b++]|a[b++]<<8;this.ga=a[b++]|a[b++]<<8|a[b++]<<16|a[b++]<<24;this.aa=(a[b++]|a[b++]<<8|a[b++]<<16|a[b++]<<24)>>>0;this.filename=String.fromCharCode.apply(null,w?a.subarray(b,b+=this.h):a.slice(b,b+=this.h));this.Y=w?a.subarray(b,b+=this.g):a.slice(b,b+=this.g);this.v=w?a.subarray(b,b+this.F):a.slice(b,b+this.F);this.length=b-this.offset};function pa(a,b){this.input=a;this.offset=b}var qa={O:1,da:8,ea:2048}; -pa.prototype.parse=function(){var a=this.input,b=this.offset;(a[b++]!==Y[0]||a[b++]!==Y[1]||a[b++]!==Y[2]||a[b++]!==Y[3])&&m(Error("invalid local file header signature"));this.$=a[b++]|a[b++]<<8;this.I=a[b++]|a[b++]<<8;this.A=a[b++]|a[b++]<<8;this.time=a[b++]|a[b++]<<8;this.V=a[b++]|a[b++]<<8;this.p=(a[b++]|a[b++]<<8|a[b++]<<16|a[b++]<<24)>>>0;this.z=(a[b++]|a[b++]<<8|a[b++]<<16|a[b++]<<24)>>>0;this.J=(a[b++]|a[b++]<<8|a[b++]<<16|a[b++]<<24)>>>0;this.h=a[b++]|a[b++]<<8;this.g=a[b++]|a[b++]<<8;this.filename= -String.fromCharCode.apply(null,w?a.subarray(b,b+=this.h):a.slice(b,b+=this.h));this.Y=w?a.subarray(b,b+=this.g):a.slice(b,b+=this.g);this.length=b-this.offset}; -function $(a){var b=[],c={},d,f,h,k;if(!a.i){if(a.o===q){var e=a.input,g;if(!a.D)a:{var l=a.input,p;for(p=l.length-12;0>>0;a.o=(e[g++]| -e[g++]<<8|e[g++]<<16|e[g++]<<24)>>>0;a.w=e[g++]|e[g++]<<8;a.v=w?e.subarray(g,g+a.w):e.slice(g,g+a.w)}d=a.o;h=0;for(k=a.ba;h>>8^B[(n^l[t])&255];for(N=ka>>3;N--;t+=8)n=n>>>8^B[(n^l[t])&255],n=n>>>8^B[(n^l[t+1])&255],n=n>>>8^B[(n^l[t+2])&255],n=n>>>8^B[(n^l[t+3])&255],n=n>>>8^B[(n^l[t+4])&255],n=n>>>8^B[(n^l[t+5])&255],n=n>>>8^B[(n^l[t+6])&255],n=n>>>8^B[(n^l[t+7])&255];p=(n^4294967295)>>>0;k.p!==p&&m(Error("wrong crc: file=0x"+ -k.p.toString(16)+", data=0x"+p.toString(16)))}return l};u.M=function(a){this.j=a};function ra(a,b,c){c^=a.s(b);a.k(b,c);return c}u.k=V.prototype.k;u.T=V.prototype.U;u.s=V.prototype.s;v("Zlib.Unzip",W);v("Zlib.Unzip.prototype.decompress",W.prototype.r);v("Zlib.Unzip.prototype.getFilenames",W.prototype.Z);v("Zlib.Unzip.prototype.setPassword",W.prototype.M);}).call(this); diff --git a/lib/zip.js b/lib/zip-utils.js similarity index 54% rename from lib/zip.js rename to lib/zip-utils.js index e9e56a8..c8b6db1 100644 --- a/lib/zip.js +++ b/lib/zip-utils.js @@ -1,3 +1,5 @@ +var ZlibNamespace = {}; + /** @license zlib.js 2012 - imaya [ https://github.com/imaya/zlib.js ] The MIT License */(function() {'use strict';var n=void 0,y=!0,aa=this;function G(e,b){var a=e.split("."),d=aa;!(a[0]in d)&&d.execScript&&d.execScript("var "+a[0]);for(var c;a.length&&(c=a.shift());)!a.length&&b!==n?d[c]=b:d=d[c]?d[c]:d[c]={}};var H="undefined"!==typeof Uint8Array&&"undefined"!==typeof Uint16Array&&"undefined"!==typeof Uint32Array&&"undefined"!==typeof DataView;function ba(e,b){this.index="number"===typeof b?b:0;this.f=0;this.buffer=e instanceof(H?Uint8Array:Array)?e:new (H?Uint8Array:Array)(32768);if(2*this.buffer.length<=this.index)throw Error("invalid index");this.buffer.length<=this.index&&ca(this)}function ca(e){var b=e.buffer,a,d=b.length,c=new (H?Uint8Array:Array)(d<<1);if(H)c.set(b);else for(a=0;a>>8&255]<<16|L[e>>>16&255]<<8|L[e>>>24&255])>>32-b:L[e]>>8-b);if(8>b+f)l=l<>b-p-1&1,8===++f&&(f=0,d[c++]=L[l],l=0,c===d.length&&(d=ca(this)));d[c]=l;this.buffer=d;this.f=f;this.index=c};ba.prototype.finish=function(){var e=this.buffer,b=this.index,a;0ha;++ha){for(var U=ha,ja=U,ka=7,U=U>>>1;U;U>>>=1)ja<<=1,ja|=U&1,--ka;da[ha]=(ja<>>0}var L=da;function la(e){var b=n,a,d="number"===typeof b?b:b=0,c=e.length;a=-1;for(d=c&7;d--;++b)a=a>>>8^V[(a^e[b])&255];for(d=c>>3;d--;b+=8)a=a>>>8^V[(a^e[b])&255],a=a>>>8^V[(a^e[b+1])&255],a=a>>>8^V[(a^e[b+2])&255],a=a>>>8^V[(a^e[b+3])&255],a=a>>>8^V[(a^e[b+4])&255],a=a>>>8^V[(a^e[b+5])&255],a=a>>>8^V[(a^e[b+6])&255],a=a>>>8^V[(a^e[b+7])&255];return(a^4294967295)>>>0} @@ -34,4 +36,38 @@ W=n,X=n;H&&(O=new Uint32Array(O));W=0;for(X=ea.length;W>3;m=b.h;a[d++]=a[c++]=m&255;a[d++]=a[c++]=m>>8&255;a[d++]=a[c++]=m>>16&255;a[d++]=a[c++]=m>>24&255;h=b.buffer.length;a[d++]=a[c++]=h&255;a[d++]=a[c++]=h>>8&255;a[d++]=a[c++]=h>>16&255;a[d++]=a[c++]=h>>24&255;s=b.size;a[d++]=a[c++]=s&255;a[d++]=a[c++]=s>>8&255;a[d++]=a[c++]=s>>16&255;a[d++]=a[c++]=s>>24&255;a[d++]=a[c++]=t&255;a[d++]=a[c++]=t>>8&255;a[d++]=a[c++]=0;a[d++]=a[c++]=0;a[c++]=r&255;a[c++]=r>>8&255;a[c++]=0;a[c++]=0;a[c++]=0;a[c++]=0;a[c++]=0;a[c++]=0;a[c++]= 0;a[c++]=0;a[c++]=k&255;a[c++]=k>>8&255;a[c++]=k>>16&255;a[c++]=k>>24&255;if(Q=b.a.filename)if(H)a.set(Q,d),a.set(Q,c),d+=t,c+=t;else for(g=0;g>8&255;a[f++]=C&255;a[f++]=C>>8&255;a[f++]=p&255;a[f++]=p>>8&255;a[f++]=p>>16&255;a[f++]=p>>24&255;a[f++]=l&255;a[f++]=l>>8&255;a[f++]=l>>16&255;a[f++]=l>>24&255;r=this.d?this.d.length:0;a[f++]=r&255;a[f++]=r>>8&255;if(this.d)if(H)a.set(this.d,f);else{g=0;for(J=r;g>8&255;Pa(e,b);return a^b} -function Pa(e,b){e[0]=(V[(e[0]^b)&255]^e[0]>>>8)>>>0;e[1]=(6681*(20173*(e[1]+(e[0]&255))>>>0)>>>0)+1>>>0;e[2]=(V[(e[2]^e[1]>>>24)&255]^e[2]>>>8)>>>0};function Ra(e,b){var a,d,c,f;if(Object.keys)a=Object.keys(b);else for(d in a=[],c=0,b)a[c++]=d;c=0;for(f=a.length;c>>8)>>>0;e[1]=(6681*(20173*(e[1]+(e[0]&255))>>>0)>>>0)+1>>>0;e[2]=(V[(e[2]^e[1]>>>24)&255]^e[2]>>>8)>>>0};function Ra(e,b){var a,d,c,f;if(Object.keys)a=Object.keys(b);else for(d in a=[],c=0,b)a[c++]=d;c=0;for(f=a.length;cx;++x)for(var y=x,ba=7,y=y>>>1;y;y>>>=1)--ba;var z=[0,1996959894,3993919788,2567524794,124634137,1886057615,3915621685,2657392035,249268274,2044508324,3772115230,2547177864,162941995,2125561021,3887607047,2428444049,498536548,1789927666,4089016648,2227061214,450548861,1843258603,4107580753,2211677639,325883990,1684777152,4251122042,2321926636,335633487,1661365465,4195302755,2366115317,997073096,1281953886,3579855332,2724688242,1006888145,1258607687,3524101629,2768942443,901097722,1119000684,3686517206,2898065728,853044451,1172266101,3705015759, +2882616665,651767980,1373503546,3369554304,3218104598,565507253,1454621731,3485111705,3099436303,671266974,1594198024,3322730930,2970347812,795835527,1483230225,3244367275,3060149565,1994146192,31158534,2563907772,4023717930,1907459465,112637215,2680153253,3904427059,2013776290,251722036,2517215374,3775830040,2137656763,141376813,2439277719,3865271297,1802195444,476864866,2238001368,4066508878,1812370925,453092731,2181625025,4111451223,1706088902,314042704,2344532202,4240017532,1658658271,366619977, +2362670323,4224994405,1303535960,984961486,2747007092,3569037538,1256170817,1037604311,2765210733,3554079995,1131014506,879679996,2909243462,3663771856,1141124467,855842277,2852801631,3708648649,1342533948,654459306,3188396048,3373015174,1466479909,544179635,3110523913,3462522015,1591671054,702138776,2966460450,3352799412,1504918807,783551873,3082640443,3233442989,3988292384,2596254646,62317068,1957810842,3939845945,2647816111,81470997,1943803523,3814918930,2489596804,225274430,2053790376,3826175755, +2466906013,167816743,2097651377,4027552580,2265490386,503444072,1762050814,4150417245,2154129355,426522225,1852507879,4275313526,2312317920,282753626,1742555852,4189708143,2394877945,397917763,1622183637,3604390888,2714866558,953729732,1340076626,3518719985,2797360999,1068828381,1219638859,3624741850,2936675148,906185462,1090812512,3747672003,2825379669,829329135,1181335161,3412177804,3160834842,628085408,1382605366,3423369109,3138078467,570562233,1426400815,3317316542,2998733608,733239954,1555261956, +3268935591,3050360625,752459403,1541320221,2607071920,3965973030,1969922972,40735498,2617837225,3943577151,1913087877,83908371,2512341634,3803740692,2075208622,213261112,2463272603,3855990285,2094854071,198958881,2262029012,4057260610,1759359992,534414190,2176718541,4139329115,1873836001,414664567,2282248934,4279200368,1711684554,285281116,2405801727,4167216745,1634467795,376229701,2685067896,3608007406,1308918612,956543938,2808555105,3495958263,1231636301,1047427035,2932959818,3654703836,1088359270, +936918E3,2847714899,3736837829,1202900863,817233897,3183342108,3401237130,1404277552,615818150,3134207493,3453421203,1423857449,601450431,3009837614,3294710456,1567103746,711928724,3020668471,3272380065,1510334235,755167117],B=w?new Uint32Array(z):z;function C(a){var b=a.length,c=0,d=Number.POSITIVE_INFINITY,f,h,k,e,g,l,p,s,r,A;for(s=0;sc&&(c=a[s]),a[s]>=1;A=k<<16|s;for(r=l;rE;E++)switch(!0){case 143>=E:D.push([E+48,8]);break;case 255>=E:D.push([E-144+400,9]);break;case 279>=E:D.push([E-256+0,7]);break;case 287>=E:D.push([E-280+192,8]);break;default:m("invalid literal: "+E)} +var ca=function(){function a(a){switch(!0){case 3===a:return[257,a-3,0];case 4===a:return[258,a-4,0];case 5===a:return[259,a-5,0];case 6===a:return[260,a-6,0];case 7===a:return[261,a-7,0];case 8===a:return[262,a-8,0];case 9===a:return[263,a-9,0];case 10===a:return[264,a-10,0];case 12>=a:return[265,a-11,1];case 14>=a:return[266,a-13,1];case 16>=a:return[267,a-15,1];case 18>=a:return[268,a-17,1];case 22>=a:return[269,a-19,2];case 26>=a:return[270,a-23,2];case 30>=a:return[271,a-27,2];case 34>=a:return[272, +a-31,2];case 42>=a:return[273,a-35,3];case 50>=a:return[274,a-43,3];case 58>=a:return[275,a-51,3];case 66>=a:return[276,a-59,3];case 82>=a:return[277,a-67,4];case 98>=a:return[278,a-83,4];case 114>=a:return[279,a-99,4];case 130>=a:return[280,a-115,4];case 162>=a:return[281,a-131,5];case 194>=a:return[282,a-163,5];case 226>=a:return[283,a-195,5];case 257>=a:return[284,a-227,5];case 258===a:return[285,a-258,0];default:m("invalid length: "+a)}}var b=[],c,d;for(c=3;258>=c;c++)d=a(c),b[c]=d[2]<<24|d[1]<< +16|d[0];return b}();w&&new Uint32Array(ca);function F(a,b){this.l=[];this.m=32768;this.d=this.f=this.c=this.t=0;this.input=w?new Uint8Array(a):a;this.u=!1;this.n=G;this.L=!1;if(b||!(b={}))b.index&&(this.c=b.index),b.bufferSize&&(this.m=b.bufferSize),b.bufferType&&(this.n=b.bufferType),b.resize&&(this.L=b.resize);switch(this.n){case H:this.a=32768;this.b=new (w?Uint8Array:Array)(32768+this.m+258);break;case G:this.a=0;this.b=new (w?Uint8Array:Array)(this.m);this.e=this.X;this.B=this.S;this.q=this.W;break;default:m(Error("invalid inflate mode"))}} +var H=0,G=1; +F.prototype.r=function(){for(;!this.u;){var a=I(this,3);a&1&&(this.u=!0);a>>>=1;switch(a){case 0:var b=this.input,c=this.c,d=this.b,f=this.a,h=b.length,k=q,e=q,g=d.length,l=q;this.d=this.f=0;c+1>=h&&m(Error("invalid uncompressed block header: LEN"));k=b[c++]|b[c++]<<8;c+1>=h&&m(Error("invalid uncompressed block header: NLEN"));e=b[c++]|b[c++]<<8;k===~e&&m(Error("invalid uncompressed block header: length verify"));c+k>b.length&&m(Error("input buffer is broken"));switch(this.n){case H:for(;f+k>d.length;){l= +g-f;k-=l;if(w)d.set(b.subarray(c,c+l),f),f+=l,c+=l;else for(;l--;)d[f++]=b[c++];this.a=f;d=this.e();f=this.a}break;case G:for(;f+k>d.length;)d=this.e({H:2});break;default:m(Error("invalid inflate mode"))}if(w)d.set(b.subarray(c,c+k),f),f+=k,c+=k;else for(;k--;)d[f++]=b[c++];this.c=c;this.a=f;this.b=d;break;case 1:this.q(da,ea);break;case 2:fa(this);break;default:m(Error("unknown BTYPE: "+a))}}return this.B()}; +var J=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],K=w?new Uint16Array(J):J,L=[3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258,258,258],M=w?new Uint16Array(L):L,ga=[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0],O=w?new Uint8Array(ga):ga,ha=[1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577],ia=w?new Uint16Array(ha):ha,ja=[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11, +12,12,13,13],P=w?new Uint8Array(ja):ja,Q=new (w?Uint8Array:Array)(288),R,la;R=0;for(la=Q.length;R=R?8:255>=R?9:279>=R?7:8;var da=C(Q),S=new (w?Uint8Array:Array)(30),T,ma;T=0;for(ma=S.length;T=k&&m(Error("input buffer is broken")),c|=f[h++]<>>b;a.d=d-b;a.c=h;return e} +function U(a,b){for(var c=a.f,d=a.d,f=a.input,h=a.c,k=f.length,e=b[0],g=b[1],l,p;d=k);)c|=f[h++]<>>16;a.f=c>>p;a.d=d-p;a.c=h;return l&65535} +function fa(a){function b(a,b,c){var d,e=this.K,f,g;for(g=0;gh)d>=f&&(this.a=d,c=this.e(),d=this.a),c[d++]=h;else{k=h-257;g=M[k];0=f&&(this.a=d,c=this.e(),d=this.a);for(;g--;)c[d]=c[d++-e]}for(;8<=this.d;)this.d-=8,this.c--;this.a=d}; +u.W=function(a,b){var c=this.b,d=this.a;this.C=a;for(var f=c.length,h,k,e,g;256!==(h=U(this,a));)if(256>h)d>=f&&(c=this.e(),f=c.length),c[d++]=h;else{k=h-257;g=M[k];0f&&(c=this.e(),f=c.length);for(;g--;)c[d]=c[d++-e]}for(;8<=this.d;)this.d-=8,this.c--;this.a=d}; +u.e=function(){var a=new (w?Uint8Array:Array)(this.a-32768),b=this.a-32768,c,d,f=this.b;if(w)a.set(f.subarray(32768,a.length));else{c=0;for(d=a.length;cc;++c)f[c]=f[b+c];this.a=32768;return f}; +u.X=function(a){var b,c=this.input.length/this.c+1|0,d,f,h,k=this.input,e=this.b;a&&("number"===typeof a.H&&(c=a.H),"number"===typeof a.Q&&(c+=a.Q));2>c?(d=(k.length-this.c)/this.C[2],h=258*(d/2)|0,f=hb&&(this.b.length=b),a=this.b);return this.buffer=a};function V(a){a=a||{};this.files=[];this.v=a.comment}V.prototype.M=function(a){this.j=a};V.prototype.s=function(a){var b=a[2]&65535|2;return b*(b^1)>>8&255};V.prototype.k=function(a,b){a[0]=(B[(a[0]^b)&255]^a[0]>>>8)>>>0;a[1]=(6681*(20173*(a[1]+(a[0]&255))>>>0)>>>0)+1>>>0;a[2]=(B[(a[2]^a[1]>>>24)&255]^a[2]>>>8)>>>0};V.prototype.U=function(a){var b=[305419896,591751049,878082192],c,d;w&&(b=new Uint32Array(b));c=0;for(d=a.length;c>>0;this.z=(a[b++]|a[b++]<<8|a[b++]<<16|a[b++]<<24)>>>0;this.J=(a[b++]|a[b++]<<8|a[b++]<<16|a[b++]<<24)>>>0;this.h=a[b++]|a[b++]<< +8;this.g=a[b++]|a[b++]<<8;this.F=a[b++]|a[b++]<<8;this.fa=a[b++]|a[b++]<<8;this.ha=a[b++]|a[b++]<<8;this.ga=a[b++]|a[b++]<<8|a[b++]<<16|a[b++]<<24;this.aa=(a[b++]|a[b++]<<8|a[b++]<<16|a[b++]<<24)>>>0;this.filename=String.fromCharCode.apply(null,w?a.subarray(b,b+=this.h):a.slice(b,b+=this.h));this.Y=w?a.subarray(b,b+=this.g):a.slice(b,b+=this.g);this.v=w?a.subarray(b,b+this.F):a.slice(b,b+this.F);this.length=b-this.offset};function pa(a,b){this.input=a;this.offset=b}var qa={O:1,da:8,ea:2048}; +pa.prototype.parse=function(){var a=this.input,b=this.offset;(a[b++]!==Y[0]||a[b++]!==Y[1]||a[b++]!==Y[2]||a[b++]!==Y[3])&&m(Error("invalid local file header signature"));this.$=a[b++]|a[b++]<<8;this.I=a[b++]|a[b++]<<8;this.A=a[b++]|a[b++]<<8;this.time=a[b++]|a[b++]<<8;this.V=a[b++]|a[b++]<<8;this.p=(a[b++]|a[b++]<<8|a[b++]<<16|a[b++]<<24)>>>0;this.z=(a[b++]|a[b++]<<8|a[b++]<<16|a[b++]<<24)>>>0;this.J=(a[b++]|a[b++]<<8|a[b++]<<16|a[b++]<<24)>>>0;this.h=a[b++]|a[b++]<<8;this.g=a[b++]|a[b++]<<8;this.filename= +String.fromCharCode.apply(null,w?a.subarray(b,b+=this.h):a.slice(b,b+=this.h));this.Y=w?a.subarray(b,b+=this.g):a.slice(b,b+=this.g);this.length=b-this.offset}; +function $(a){var b=[],c={},d,f,h,k;if(!a.i){if(a.o===q){var e=a.input,g;if(!a.D)a:{var l=a.input,p;for(p=l.length-12;0>>0;a.o=(e[g++]| +e[g++]<<8|e[g++]<<16|e[g++]<<24)>>>0;a.w=e[g++]|e[g++]<<8;a.v=w?e.subarray(g,g+a.w):e.slice(g,g+a.w)}d=a.o;h=0;for(k=a.ba;h>>8^B[(n^l[t])&255];for(N=ka>>3;N--;t+=8)n=n>>>8^B[(n^l[t])&255],n=n>>>8^B[(n^l[t+1])&255],n=n>>>8^B[(n^l[t+2])&255],n=n>>>8^B[(n^l[t+3])&255],n=n>>>8^B[(n^l[t+4])&255],n=n>>>8^B[(n^l[t+5])&255],n=n>>>8^B[(n^l[t+6])&255],n=n>>>8^B[(n^l[t+7])&255];p=(n^4294967295)>>>0;k.p!==p&&m(Error("wrong crc: file=0x"+ +k.p.toString(16)+", data=0x"+p.toString(16)))}return l};u.M=function(a){this.j=a};function ra(a,b,c){c^=a.s(b);a.k(b,c);return c}u.k=V.prototype.k;u.T=V.prototype.U;u.s=V.prototype.s;v("Zlib.Unzip",W);v("Zlib.Unzip.prototype.decompress",W.prototype.r);v("Zlib.Unzip.prototype.getFilenames",W.prototype.Z);v("Zlib.Unzip.prototype.setPassword",W.prototype.M);}).call(ZlibNamespace); + +module.exports = ZlibNamespace; diff --git a/package.json b/package.json index d06dcc2..894fd9f 100644 --- a/package.json +++ b/package.json @@ -51,5 +51,5 @@ "mocha": "~1.18.2", "requirejs": "~2.1.11" }, - "main": "dist/filer_node.js" + "main": "./src/index.js" } diff --git a/src/constants.js b/src/constants.js index bad5944..83b5d43 100644 --- a/src/constants.js +++ b/src/constants.js @@ -1,83 +1,79 @@ -define(function(require) { +var O_READ = 'READ'; +var O_WRITE = 'WRITE'; +var O_CREATE = 'CREATE'; +var O_EXCLUSIVE = 'EXCLUSIVE'; +var O_TRUNCATE = 'TRUNCATE'; +var O_APPEND = 'APPEND'; +var XATTR_CREATE = 'CREATE'; +var XATTR_REPLACE = 'REPLACE'; - var O_READ = 'READ'; - var O_WRITE = 'WRITE'; - var O_CREATE = 'CREATE'; - var O_EXCLUSIVE = 'EXCLUSIVE'; - var O_TRUNCATE = 'TRUNCATE'; - var O_APPEND = 'APPEND'; - var XATTR_CREATE = 'CREATE'; - var XATTR_REPLACE = 'REPLACE'; +module.exports = { + FILE_SYSTEM_NAME: 'local', - return { - FILE_SYSTEM_NAME: 'local', + FILE_STORE_NAME: 'files', - FILE_STORE_NAME: 'files', + IDB_RO: 'readonly', + IDB_RW: 'readwrite', - IDB_RO: 'readonly', - IDB_RW: 'readwrite', + WSQL_VERSION: "1", + WSQL_SIZE: 5 * 1024 * 1024, + WSQL_DESC: "FileSystem Storage", - WSQL_VERSION: "1", - WSQL_SIZE: 5 * 1024 * 1024, - WSQL_DESC: "FileSystem Storage", + MODE_FILE: 'FILE', + MODE_DIRECTORY: 'DIRECTORY', + MODE_SYMBOLIC_LINK: 'SYMLINK', + MODE_META: 'META', - MODE_FILE: 'FILE', - MODE_DIRECTORY: 'DIRECTORY', - MODE_SYMBOLIC_LINK: 'SYMLINK', - MODE_META: 'META', + SYMLOOP_MAX: 10, - SYMLOOP_MAX: 10, + BINARY_MIME_TYPE: 'application/octet-stream', + JSON_MIME_TYPE: 'application/json', - BINARY_MIME_TYPE: 'application/octet-stream', - JSON_MIME_TYPE: 'application/json', + ROOT_DIRECTORY_NAME: '/', // basename(normalize(path)) - ROOT_DIRECTORY_NAME: '/', // basename(normalize(path)) + // FS Mount Flags + FS_FORMAT: 'FORMAT', + FS_NOCTIME: 'NOCTIME', + FS_NOMTIME: 'NOMTIME', - // FS Mount Flags - FS_FORMAT: 'FORMAT', - FS_NOCTIME: 'NOCTIME', - FS_NOMTIME: 'NOMTIME', + // FS File Open Flags + O_READ: O_READ, + O_WRITE: O_WRITE, + O_CREATE: O_CREATE, + O_EXCLUSIVE: O_EXCLUSIVE, + O_TRUNCATE: O_TRUNCATE, + O_APPEND: O_APPEND, - // FS File Open Flags - O_READ: O_READ, - O_WRITE: O_WRITE, - O_CREATE: O_CREATE, - O_EXCLUSIVE: O_EXCLUSIVE, - O_TRUNCATE: O_TRUNCATE, - O_APPEND: O_APPEND, + O_FLAGS: { + 'r': [O_READ], + 'r+': [O_READ, O_WRITE], + 'w': [O_WRITE, O_CREATE, O_TRUNCATE], + 'w+': [O_WRITE, O_READ, O_CREATE, O_TRUNCATE], + 'wx': [O_WRITE, O_CREATE, O_EXCLUSIVE, O_TRUNCATE], + 'wx+': [O_WRITE, O_READ, O_CREATE, O_EXCLUSIVE, O_TRUNCATE], + 'a': [O_WRITE, O_CREATE, O_APPEND], + 'a+': [O_WRITE, O_READ, O_CREATE, O_APPEND], + 'ax': [O_WRITE, O_CREATE, O_EXCLUSIVE, O_APPEND], + 'ax+': [O_WRITE, O_READ, O_CREATE, O_EXCLUSIVE, O_APPEND] + }, - O_FLAGS: { - 'r': [O_READ], - 'r+': [O_READ, O_WRITE], - 'w': [O_WRITE, O_CREATE, O_TRUNCATE], - 'w+': [O_WRITE, O_READ, O_CREATE, O_TRUNCATE], - 'wx': [O_WRITE, O_CREATE, O_EXCLUSIVE, O_TRUNCATE], - 'wx+': [O_WRITE, O_READ, O_CREATE, O_EXCLUSIVE, O_TRUNCATE], - 'a': [O_WRITE, O_CREATE, O_APPEND], - 'a+': [O_WRITE, O_READ, O_CREATE, O_APPEND], - 'ax': [O_WRITE, O_CREATE, O_EXCLUSIVE, O_APPEND], - 'ax+': [O_WRITE, O_READ, O_CREATE, O_EXCLUSIVE, O_APPEND] - }, + XATTR_CREATE: XATTR_CREATE, + XATTR_REPLACE: XATTR_REPLACE, - XATTR_CREATE: XATTR_CREATE, - XATTR_REPLACE: XATTR_REPLACE, + FS_READY: 'READY', + FS_PENDING: 'PENDING', + FS_ERROR: 'ERROR', - FS_READY: 'READY', - FS_PENDING: 'PENDING', - FS_ERROR: 'ERROR', + SUPER_NODE_ID: '00000000-0000-0000-0000-000000000000', - SUPER_NODE_ID: '00000000-0000-0000-0000-000000000000', + // Reserved File Descriptors for streams + STDIN: 0, + STDOUT: 1, + STDERR: 2, + FIRST_DESCRIPTOR: 3, - // Reserved File Descriptors for streams - STDIN: 0, - STDOUT: 1, - STDERR: 2, - FIRST_DESCRIPTOR: 3, - - ENVIRONMENT: { - TMP: '/tmp', - PATH: '' - } - }; - -}); + ENVIRONMENT: { + TMP: '/tmp', + PATH: '' + } +}; diff --git a/src/directory-entry.js b/src/directory-entry.js index d4d97a2..819d46a 100644 --- a/src/directory-entry.js +++ b/src/directory-entry.js @@ -1,8 +1,6 @@ -define(['src/constants'], function(Constants) { +var MODE_FILE = require('./constants.js').MODE_FILE; - return function DirectoryEntry(id, type) { - this.id = id; - this.type = type || Constants.MODE_FILE; - }; - -}); +module.exports = function DirectoryEntry(id, type) { + this.id = id; + this.type = type || MODE_FILE; +}; diff --git a/src/errors.js b/src/errors.js index 6d5e02b..8a6c585 100644 --- a/src/errors.js +++ b/src/errors.js @@ -1,94 +1,92 @@ -define(function(require) { - var errors = {}; - [ - /** - * node.js errors - */ - '-1:UNKNOWN:unknown error', - '0:OK:success', - '1:EOF:end of file', - '2:EADDRINFO:getaddrinfo error', - '3:EACCES:permission denied', - '4:EAGAIN:resource temporarily unavailable', - '5:EADDRINUSE:address already in use', - '6:EADDRNOTAVAIL:address not available', - '7:EAFNOSUPPORT:address family not supported', - '8:EALREADY:connection already in progress', - '9:EBADF:bad file descriptor', - '10:EBUSY:resource busy or locked', - '11:ECONNABORTED:software caused connection abort', - '12:ECONNREFUSED:connection refused', - '13:ECONNRESET:connection reset by peer', - '14:EDESTADDRREQ:destination address required', - '15:EFAULT:bad address in system call argument', - '16:EHOSTUNREACH:host is unreachable', - '17:EINTR:interrupted system call', - '18:EINVAL:invalid argument', - '19:EISCONN:socket is already connected', - '20:EMFILE:too many open files', - '21:EMSGSIZE:message too long', - '22:ENETDOWN:network is down', - '23:ENETUNREACH:network is unreachable', - '24:ENFILE:file table overflow', - '25:ENOBUFS:no buffer space available', - '26:ENOMEM:not enough memory', - '27:ENOTDIR:not a directory', - '28:EISDIR:illegal operation on a directory', - '29:ENONET:machine is not on the network', - // errno 30 skipped, as per https://github.com/rvagg/node-errno/blob/master/errno.js - '31:ENOTCONN:socket is not connected', - '32:ENOTSOCK:socket operation on non-socket', - '33:ENOTSUP:operation not supported on socket', - '34:ENOENT:no such file or directory', - '35:ENOSYS:function not implemented', - '36:EPIPE:broken pipe', - '37:EPROTO:protocol error', - '38:EPROTONOSUPPORT:protocol not supported', - '39:EPROTOTYPE:protocol wrong type for socket', - '40:ETIMEDOUT:connection timed out', - '41:ECHARSET:invalid Unicode character', - '42:EAIFAMNOSUPPORT:address family for hostname not supported', - // errno 43 skipped, as per https://github.com/rvagg/node-errno/blob/master/errno.js - '44:EAISERVICE:servname not supported for ai_socktype', - '45:EAISOCKTYPE:ai_socktype not supported', - '46:ESHUTDOWN:cannot send after transport endpoint shutdown', - '47:EEXIST:file already exists', - '48:ESRCH:no such process', - '49:ENAMETOOLONG:name too long', - '50:EPERM:operation not permitted', - '51:ELOOP:too many symbolic links encountered', - '52:EXDEV:cross-device link not permitted', - '53:ENOTEMPTY:directory not empty', - '54:ENOSPC:no space left on device', - '55:EIO:i/o error', - '56:EROFS:read-only file system', - '57:ENODEV:no such device', - '58:ESPIPE:invalid seek', - '59:ECANCELED:operation canceled', +var errors = {}; +[ + /** + * node.js errors + */ + '-1:UNKNOWN:unknown error', + '0:OK:success', + '1:EOF:end of file', + '2:EADDRINFO:getaddrinfo error', + '3:EACCES:permission denied', + '4:EAGAIN:resource temporarily unavailable', + '5:EADDRINUSE:address already in use', + '6:EADDRNOTAVAIL:address not available', + '7:EAFNOSUPPORT:address family not supported', + '8:EALREADY:connection already in progress', + '9:EBADF:bad file descriptor', + '10:EBUSY:resource busy or locked', + '11:ECONNABORTED:software caused connection abort', + '12:ECONNREFUSED:connection refused', + '13:ECONNRESET:connection reset by peer', + '14:EDESTADDRREQ:destination address required', + '15:EFAULT:bad address in system call argument', + '16:EHOSTUNREACH:host is unreachable', + '17:EINTR:interrupted system call', + '18:EINVAL:invalid argument', + '19:EISCONN:socket is already connected', + '20:EMFILE:too many open files', + '21:EMSGSIZE:message too long', + '22:ENETDOWN:network is down', + '23:ENETUNREACH:network is unreachable', + '24:ENFILE:file table overflow', + '25:ENOBUFS:no buffer space available', + '26:ENOMEM:not enough memory', + '27:ENOTDIR:not a directory', + '28:EISDIR:illegal operation on a directory', + '29:ENONET:machine is not on the network', + // errno 30 skipped, as per https://github.com/rvagg/node-errno/blob/master/errno.js + '31:ENOTCONN:socket is not connected', + '32:ENOTSOCK:socket operation on non-socket', + '33:ENOTSUP:operation not supported on socket', + '34:ENOENT:no such file or directory', + '35:ENOSYS:function not implemented', + '36:EPIPE:broken pipe', + '37:EPROTO:protocol error', + '38:EPROTONOSUPPORT:protocol not supported', + '39:EPROTOTYPE:protocol wrong type for socket', + '40:ETIMEDOUT:connection timed out', + '41:ECHARSET:invalid Unicode character', + '42:EAIFAMNOSUPPORT:address family for hostname not supported', + // errno 43 skipped, as per https://github.com/rvagg/node-errno/blob/master/errno.js + '44:EAISERVICE:servname not supported for ai_socktype', + '45:EAISOCKTYPE:ai_socktype not supported', + '46:ESHUTDOWN:cannot send after transport endpoint shutdown', + '47:EEXIST:file already exists', + '48:ESRCH:no such process', + '49:ENAMETOOLONG:name too long', + '50:EPERM:operation not permitted', + '51:ELOOP:too many symbolic links encountered', + '52:EXDEV:cross-device link not permitted', + '53:ENOTEMPTY:directory not empty', + '54:ENOSPC:no space left on device', + '55:EIO:i/o error', + '56:EROFS:read-only file system', + '57:ENODEV:no such device', + '58:ESPIPE:invalid seek', + '59:ECANCELED:operation canceled', - /** - * Filer specific errors - */ - '1000:ENOTMOUNTED:not mounted', - '1001:EFILESYSTEMERROR:missing super node, use \'FORMAT\' flag to format filesystem.', - '1002:ENOATTR:attribute does not exist' - ].forEach(function(e) { - e = e.split(':'); - var errno = e[0], - err = e[1], - message = e[2]; + /** + * Filer specific errors + */ + '1000:ENOTMOUNTED:not mounted', + '1001:EFILESYSTEMERROR:missing super node, use \'FORMAT\' flag to format filesystem.', + '1002:ENOATTR:attribute does not exist' +].forEach(function(e) { + e = e.split(':'); + var errno = e[0], + err = e[1], + message = e[2]; - function ctor(m) { - this.message = m || message; - } - var proto = ctor.prototype = new Error(); - proto.errno = errno; - proto.code = err; - proto.constructor = ctor; + function ctor(m) { + this.message = m || message; + } + var proto = ctor.prototype = new Error(); + proto.errno = errno; + proto.code = err; + proto.constructor = ctor; - // We expose the error as both Errors.EINVAL and Errors[18] - errors[err] = errors[errno] = ctor; - }); - - return errors; + // We expose the error as both Errors.EINVAL and Errors[18] + errors[err] = errors[errno] = ctor; }); + +module.exports = errors; diff --git a/src/filesystem/implementation.js b/src/filesystem/implementation.js index 84d8530..86ee92d 100644 --- a/src/filesystem/implementation.js +++ b/src/filesystem/implementation.js @@ -1,278 +1,269 @@ -define(function(require) { +var _ = require('../../lib/nodash.js'); - // TextEncoder and TextDecoder will either already be present, or use this shim. - // Because of the way the spec is defined, we need to get them off the global. - require('encoding'); +var TextDecoder = require('../../lib/encoding.js').TextDecoder; +var TextEncoder = require('../../lib/encoding.js').TextEncoder; - var _ = require('nodash'); +var Path = require('../path.js'); +var normalize = Path.normalize; +var dirname = Path.dirname; +var basename = Path.basename; +var isAbsolutePath = Path.isAbsolute; +var isNullPath = Path.isNull; - var normalize = require('src/path').normalize; - var dirname = require('src/path').dirname; - var basename = require('src/path').basename; - var isAbsolutePath = require('src/path').isAbsolute; - var isNullPath = require('src/path').isNull; +var Constants = require('../constants.js'); +var MODE_FILE = Constants.MODE_FILE; +var MODE_DIRECTORY = Constants.MODE_DIRECTORY; +var MODE_SYMBOLIC_LINK = Constants.MODE_SYMBOLIC_LINK; +var MODE_META = Constants.MODE_META; - var MODE_FILE = require('src/constants').MODE_FILE; - var MODE_DIRECTORY = require('src/constants').MODE_DIRECTORY; - var MODE_SYMBOLIC_LINK = require('src/constants').MODE_SYMBOLIC_LINK; - var MODE_META = require('src/constants').MODE_META; +var ROOT_DIRECTORY_NAME = Constants.ROOT_DIRECTORY_NAME; +var SUPER_NODE_ID = Constants.SUPER_NODE_ID; +var SYMLOOP_MAX = Constants.SYMLOOP_MAX; - var ROOT_DIRECTORY_NAME = require('src/constants').ROOT_DIRECTORY_NAME; - var SUPER_NODE_ID = require('src/constants').SUPER_NODE_ID; - var SYMLOOP_MAX = require('src/constants').SYMLOOP_MAX; +var O_READ = Constants.O_READ; +var O_WRITE = Constants.O_WRITE; +var O_CREATE = Constants.O_CREATE; +var O_EXCLUSIVE = Constants.O_EXCLUSIVE; +var O_TRUNCATE = Constants.O_TRUNCATE; +var O_APPEND = Constants.O_APPEND; +var O_FLAGS = Constants.O_FLAGS; - var O_READ = require('src/constants').O_READ; - var O_WRITE = require('src/constants').O_WRITE; - var O_CREATE = require('src/constants').O_CREATE; - var O_EXCLUSIVE = require('src/constants').O_EXCLUSIVE; - var O_TRUNCATE = require('src/constants').O_TRUNCATE; - var O_APPEND = require('src/constants').O_APPEND; - var O_FLAGS = require('src/constants').O_FLAGS; +var XATTR_CREATE = Constants.XATTR_CREATE; +var XATTR_REPLACE = Constants.XATTR_REPLACE; +var FS_NOMTIME = Constants.FS_NOMTIME; +var FS_NOCTIME = Constants.FS_NOCTIME; - var XATTR_CREATE = require('src/constants').XATTR_CREATE; - var XATTR_REPLACE = require('src/constants').XATTR_REPLACE; - var FS_NOMTIME = require('src/constants').FS_NOMTIME; - var FS_NOCTIME = require('src/constants').FS_NOCTIME; +var Errors = require('../errors.js'); +var DirectoryEntry = require('../directory-entry.js'); +var OpenFileDescription = require('../open-file-description.js'); +var SuperNode = require('../super-node.js'); +var Node = require('../node.js'); +var Stats = require('../stats.js'); - var Errors = require('src/errors'); - var DirectoryEntry = require('src/directory-entry'); - var OpenFileDescription = require('src/open-file-description'); - var SuperNode = require('src/super-node'); - var Node = require('src/node'); - var Stats = require('src/stats'); - - /** - * Many functions below use this callback pattern. If it's not - * re-defined, we use this to generate a callback. NOTE: this - * can be use for callbacks of both forms without problem (i.e., - * since result will be undefined if not returned): - * - callback(error) - * - callback(error, result) - */ - function standard_check_result_cb(callback) { - return function(error, result) { - if(error) { - callback(error); - } else { - callback(null, result); - } - }; - } - - /* - * Update node times. Only passed times are modified (undefined times are ignored) - * and filesystem flags are examined in order to override update logic. - */ - function update_node_times(context, path, node, times, callback) { - // Honour mount flags for how we update times - var flags = context.flags; - if(_(flags).contains(FS_NOCTIME)) { - delete times.ctime; - } - if(_(flags).contains(FS_NOMTIME)) { - delete times.mtime; - } - - // Only do the update if required (i.e., times are still present) - var update = false; - if(times.ctime) { - node.ctime = times.ctime; - // We don't do atime tracking for perf reasons, but do mirror ctime - node.atime = times.ctime; - update = true; - } - if(times.atime) { - // The only time we explicitly pass atime is when utimes(), futimes() is called. - // Override ctime mirror here if so - node.atime = times.atime; - update = true; - } - if(times.mtime) { - node.mtime = times.mtime; - update = true; - } - - function complete(error) { - // Queue this change so we can send watch events. - // Unlike node.js, we send the full path vs. basename/dirname only. - context.changes.push({ event: 'change', path: path }); +/** + * Many functions below use this callback pattern. If it's not + * re-defined, we use this to generate a callback. NOTE: this + * can be use for callbacks of both forms without problem (i.e., + * since result will be undefined if not returned): + * - callback(error) + * - callback(error, result) + */ +function standard_check_result_cb(callback) { + return function(error, result) { + if(error) { callback(error); - } - - if(update) { - context.put(node.id, node, complete); } else { - complete(); + callback(null, result); } + }; +} + +/** + * Update node times. Only passed times are modified (undefined times are ignored) + * and filesystem flags are examined in order to override update logic. + */ +function update_node_times(context, path, node, times, callback) { + // Honour mount flags for how we update times + var flags = context.flags; + if(_(flags).contains(FS_NOCTIME)) { + delete times.ctime; } - - /* - * make_node() - */ - // in: file or directory path - // out: new node representing file/directory - function make_node(context, path, mode, callback) { - if(mode !== MODE_DIRECTORY && mode !== MODE_FILE) { - return callback(new Errors.EINVAL('mode must be a directory or file')); - } - - path = normalize(path); - - var name = basename(path); - var parentPath = dirname(path); - var parentNode; - var parentNodeData; - var node; - - // Check if the parent node exists - function create_node_in_parent(error, parentDirectoryNode) { - if(error) { - callback(error); - } else if(parentDirectoryNode.mode !== MODE_DIRECTORY) { - callback(new Errors.ENOTDIR('a component of the path prefix is not a directory')); - } else { - parentNode = parentDirectoryNode; - find_node(context, path, check_if_node_exists); - } - } - - // Check if the node to be created already exists - function check_if_node_exists(error, result) { - if(!error && result) { - callback(new Errors.EEXIST('path name already exists')); - } else if(error && !(error instanceof Errors.ENOENT)) { - callback(error); - } else { - context.get(parentNode.data, create_node); - } - } - - // Create the new node - function create_node(error, result) { - if(error) { - callback(error); - } else { - parentNodeData = result; - node = new Node(undefined, mode); - node.nlinks += 1; - context.put(node.id, node, update_parent_node_data); - } - } - - // Update parent node time - function update_time(error) { - if(error) { - callback(error); - } else { - var now = Date.now(); - update_node_times(context, parentPath, node, { mtime: now, ctime: now }, callback); - } - } - - // Update the parent nodes data - function update_parent_node_data(error) { - if(error) { - callback(error); - } else { - parentNodeData[name] = new DirectoryEntry(node.id, mode); - context.put(parentNode.data, parentNodeData, update_time); - } - } - - // Find the parent node - find_node(context, parentPath, create_node_in_parent); + if(_(flags).contains(FS_NOMTIME)) { + delete times.mtime; } - /* - * find_node - */ + // Only do the update if required (i.e., times are still present) + var update = false; + if(times.ctime) { + node.ctime = times.ctime; + // We don't do atime tracking for perf reasons, but do mirror ctime + node.atime = times.ctime; + update = true; + } + if(times.atime) { + // The only time we explicitly pass atime is when utimes(), futimes() is called. + // Override ctime mirror here if so + node.atime = times.atime; + update = true; + } + if(times.mtime) { + node.mtime = times.mtime; + update = true; + } - // in: file or directory path - // out: node structure, or error - function find_node(context, path, callback) { - path = normalize(path); - if(!path) { - return callback(new Errors.ENOENT('path is an empty string')); + function complete(error) { + // Queue this change so we can send watch events. + // Unlike node.js, we send the full path vs. basename/dirname only. + context.changes.push({ event: 'change', path: path }); + callback(error); + } + + if(update) { + context.put(node.id, node, complete); + } else { + complete(); + } +} + +/** + * make_node() + */ +// in: file or directory path +// out: new node representing file/directory +function make_node(context, path, mode, callback) { + if(mode !== MODE_DIRECTORY && mode !== MODE_FILE) { + return callback(new Errors.EINVAL('mode must be a directory or file')); + } + + path = normalize(path); + + var name = basename(path); + var parentPath = dirname(path); + var parentNode; + var parentNodeData; + var node; + + // Check if the parent node exists + function create_node_in_parent(error, parentDirectoryNode) { + if(error) { + callback(error); + } else if(parentDirectoryNode.mode !== MODE_DIRECTORY) { + callback(new Errors.ENOTDIR('a component of the path prefix is not a directory')); + } else { + parentNode = parentDirectoryNode; + find_node(context, path, check_if_node_exists); } - var name = basename(path); - var parentPath = dirname(path); - var followedCount = 0; + } - function read_root_directory_node(error, superNode) { - if(error) { - callback(error); - } else if(!superNode || superNode.mode !== MODE_META || !superNode.rnode) { - callback(new Errors.EFILESYSTEMERROR()); - } else { - context.get(superNode.rnode, check_root_directory_node); - } + // Check if the node to be created already exists + function check_if_node_exists(error, result) { + if(!error && result) { + callback(new Errors.EEXIST('path name already exists')); + } else if(error && !(error instanceof Errors.ENOENT)) { + callback(error); + } else { + context.get(parentNode.data, create_node); } + } - function check_root_directory_node(error, rootDirectoryNode) { - if(error) { - callback(error); - } else if(!rootDirectoryNode) { + // Create the new node + function create_node(error, result) { + if(error) { + callback(error); + } else { + parentNodeData = result; + node = new Node(undefined, mode); + node.nlinks += 1; + context.put(node.id, node, update_parent_node_data); + } + } + + // Update parent node time + function update_time(error) { + if(error) { + callback(error); + } else { + var now = Date.now(); + update_node_times(context, parentPath, node, { mtime: now, ctime: now }, callback); + } + } + + // Update the parent nodes data + function update_parent_node_data(error) { + if(error) { + callback(error); + } else { + parentNodeData[name] = new DirectoryEntry(node.id, mode); + context.put(parentNode.data, parentNodeData, update_time); + } + } + + // Find the parent node + find_node(context, parentPath, create_node_in_parent); +} + +/** + * find_node + */ +// in: file or directory path +// out: node structure, or error +function find_node(context, path, callback) { + path = normalize(path); + if(!path) { + return callback(new Errors.ENOENT('path is an empty string')); + } + var name = basename(path); + var parentPath = dirname(path); + var followedCount = 0; + + function read_root_directory_node(error, superNode) { + if(error) { + callback(error); + } else if(!superNode || superNode.mode !== MODE_META || !superNode.rnode) { + callback(new Errors.EFILESYSTEMERROR()); + } else { + context.get(superNode.rnode, check_root_directory_node); + } + } + + function check_root_directory_node(error, rootDirectoryNode) { + if(error) { + callback(error); + } else if(!rootDirectoryNode) { + callback(new Errors.ENOENT()); + } else { + callback(null, rootDirectoryNode); + } + } + + // in: parent directory node + // out: parent directory data + function read_parent_directory_data(error, parentDirectoryNode) { + if(error) { + callback(error); + } else if(parentDirectoryNode.mode !== MODE_DIRECTORY || !parentDirectoryNode.data) { + callback(new Errors.ENOTDIR('a component of the path prefix is not a directory')); + } else { + context.get(parentDirectoryNode.data, get_node_from_parent_directory_data); + } + } + + // in: parent directory data + // out: searched node + function get_node_from_parent_directory_data(error, parentDirectoryData) { + if(error) { + callback(error); + } else { + if(!_(parentDirectoryData).has(name)) { callback(new Errors.ENOENT()); } else { - callback(null, rootDirectoryNode); + var nodeId = parentDirectoryData[name].id; + context.get(nodeId, is_symbolic_link); } } + } - // in: parent directory node - // out: parent directory data - function read_parent_directory_data(error, parentDirectoryNode) { - if(error) { - callback(error); - } else if(parentDirectoryNode.mode !== MODE_DIRECTORY || !parentDirectoryNode.data) { - callback(new Errors.ENOTDIR('a component of the path prefix is not a directory')); - } else { - context.get(parentDirectoryNode.data, get_node_from_parent_directory_data); - } - } - - // in: parent directory data - // out: searched node - function get_node_from_parent_directory_data(error, parentDirectoryData) { - if(error) { - callback(error); - } else { - if(!_(parentDirectoryData).has(name)) { - callback(new Errors.ENOENT()); + function is_symbolic_link(error, node) { + if(error) { + callback(error); + } else { + if(node.mode == MODE_SYMBOLIC_LINK) { + followedCount++; + if(followedCount > SYMLOOP_MAX){ + callback(new Errors.ELOOP()); } else { - var nodeId = parentDirectoryData[name].id; - context.get(nodeId, is_symbolic_link); + follow_symbolic_link(node.data); } - } - } - - function is_symbolic_link(error, node) { - if(error) { - callback(error); } else { - if(node.mode == MODE_SYMBOLIC_LINK) { - followedCount++; - if(followedCount > SYMLOOP_MAX){ - callback(new Errors.ELOOP()); - } else { - follow_symbolic_link(node.data); - } - } else { - callback(null, node); - } - } - } - - function follow_symbolic_link(data) { - data = normalize(data); - parentPath = dirname(data); - name = basename(data); - if(ROOT_DIRECTORY_NAME == name) { - context.get(SUPER_NODE_ID, read_root_directory_node); - } else { - find_node(context, parentPath, read_parent_directory_data); + callback(null, node); } } + } + function follow_symbolic_link(data) { + data = normalize(data); + parentPath = dirname(data); + name = basename(data); if(ROOT_DIRECTORY_NAME == name) { context.get(SUPER_NODE_ID, read_root_directory_node); } else { @@ -280,1729 +271,1731 @@ define(function(require) { } } - - /* - * set extended attribute (refactor) - */ - - function set_extended_attribute (context, path_or_fd, name, value, flag, callback) { - var path; - - function set_xattr (error, node) { - var xattr = (node ? node.xattrs[name] : null); - - function update_time(error) { - if(error) { - callback(error); - } else { - update_node_times(context, path, node, { ctime: Date.now() }, callback); - } - } - - if (error) { - callback(error); - } - else if (flag === XATTR_CREATE && node.xattrs.hasOwnProperty(name)) { - callback(new Errors.EEXIST('attribute already exists')); - } - else if (flag === XATTR_REPLACE && !node.xattrs.hasOwnProperty(name)) { - callback(new Errors.ENOATTR()); - } - else { - node.xattrs[name] = value; - context.put(node.id, node, update_time); - } - } - - if (typeof path_or_fd == 'string') { - path = path_or_fd; - find_node(context, path_or_fd, set_xattr); - } - else if (typeof path_or_fd == 'object' && typeof path_or_fd.id == 'string') { - path = path_or_fd.path; - context.get(path_or_fd.id, set_xattr); - } - else { - callback(new Errors.EINVAL('path or file descriptor of wrong type')); - } - } - - /* - * make_root_directory - */ - - // Note: this should only be invoked when formatting a new file system - function make_root_directory(context, callback) { - var superNode; - var directoryNode; - var directoryData; - - function write_super_node(error, existingNode) { - if(!error && existingNode) { - callback(new Errors.EEXIST()); - } else if(error && !(error instanceof Errors.ENOENT)) { - callback(error); - } else { - superNode = new SuperNode(); - context.put(superNode.id, superNode, write_directory_node); - } - } - - function write_directory_node(error) { - if(error) { - callback(error); - } else { - directoryNode = new Node(superNode.rnode, MODE_DIRECTORY); - directoryNode.nlinks += 1; - context.put(directoryNode.id, directoryNode, write_directory_data); - } - } - - function write_directory_data(error) { - if(error) { - callback(error); - } else { - directoryData = {}; - context.put(directoryNode.data, directoryData, callback); - } - } - - context.get(SUPER_NODE_ID, write_super_node); - } - - /* - * make_directory - */ - - function make_directory(context, path, callback) { - path = normalize(path); - var name = basename(path); - var parentPath = dirname(path); - - var directoryNode; - var directoryData; - var parentDirectoryNode; - var parentDirectoryData; - - function check_if_directory_exists(error, result) { - if(!error && result) { - callback(new Errors.EEXIST()); - } else if(error && !(error instanceof Errors.ENOENT)) { - callback(error); - } else { - find_node(context, parentPath, read_parent_directory_data); - } - } - - function read_parent_directory_data(error, result) { - if(error) { - callback(error); - } else { - parentDirectoryNode = result; - context.get(parentDirectoryNode.data, write_directory_node); - } - } - - function write_directory_node(error, result) { - if(error) { - callback(error); - } else { - parentDirectoryData = result; - directoryNode = new Node(undefined, MODE_DIRECTORY); - directoryNode.nlinks += 1; - context.put(directoryNode.id, directoryNode, write_directory_data); - } - } - - function write_directory_data(error) { - if(error) { - callback(error); - } else { - directoryData = {}; - context.put(directoryNode.data, directoryData, update_parent_directory_data); - } - } - - function update_time(error) { - if(error) { - callback(error); - } else { - var now = Date.now(); - update_node_times(context, parentPath, parentDirectoryNode, { mtime: now, ctime: now }, callback); - } - } - - function update_parent_directory_data(error) { - if(error) { - callback(error); - } else { - parentDirectoryData[name] = new DirectoryEntry(directoryNode.id, MODE_DIRECTORY); - context.put(parentDirectoryNode.data, parentDirectoryData, update_time); - } - } - - find_node(context, path, check_if_directory_exists); - } - - /* - * remove_directory - */ - - function remove_directory(context, path, callback) { - path = normalize(path); - var name = basename(path); - var parentPath = dirname(path); - - var directoryNode; - var directoryData; - var parentDirectoryNode; - var parentDirectoryData; - - function read_parent_directory_data(error, result) { - if(error) { - callback(error); - } else { - parentDirectoryNode = result; - context.get(parentDirectoryNode.data, check_if_node_exists); - } - } - - function check_if_node_exists(error, result) { - if(error) { - callback(error); - } else if(ROOT_DIRECTORY_NAME == name) { - callback(new Errors.EBUSY()); - } else if(!_(result).has(name)) { - callback(new Errors.ENOENT()); - } else { - parentDirectoryData = result; - directoryNode = parentDirectoryData[name].id; - context.get(directoryNode, check_if_node_is_directory); - } - } - - function check_if_node_is_directory(error, result) { - if(error) { - callback(error); - } else if(result.mode != MODE_DIRECTORY) { - callback(new Errors.ENOTDIR()); - } else { - directoryNode = result; - context.get(directoryNode.data, check_if_directory_is_empty); - } - } - - function check_if_directory_is_empty(error, result) { - if(error) { - callback(error); - } else { - directoryData = result; - if(_(directoryData).size() > 0) { - callback(new Errors.ENOTEMPTY()); - } else { - remove_directory_entry_from_parent_directory_node(); - } - } - } - - function update_time(error) { - if(error) { - callback(error); - } else { - var now = Date.now(); - update_node_times(context, parentPath, parentDirectoryNode, { mtime: now, ctime: now }, remove_directory_node); - } - } - - function remove_directory_entry_from_parent_directory_node() { - delete parentDirectoryData[name]; - context.put(parentDirectoryNode.data, parentDirectoryData, update_time); - } - - function remove_directory_node(error) { - if(error) { - callback(error); - } else { - context.delete(directoryNode.id, remove_directory_data); - } - } - - function remove_directory_data(error) { - if(error) { - callback(error); - } else { - context.delete(directoryNode.data, callback); - } - } - + if(ROOT_DIRECTORY_NAME == name) { + context.get(SUPER_NODE_ID, read_root_directory_node); + } else { find_node(context, parentPath, read_parent_directory_data); } +} - function open_file(context, path, flags, callback) { - path = normalize(path); - var name = basename(path); - var parentPath = dirname(path); - var directoryNode; - var directoryData; - var directoryEntry; - var fileNode; - var fileData; +/** + * set extended attribute (refactor) + */ +function set_extended_attribute (context, path_or_fd, name, value, flag, callback) { + var path; - var followedCount = 0; + function set_xattr (error, node) { + var xattr = (node ? node.xattrs[name] : null); + function update_time(error) { + if(error) { + callback(error); + } else { + update_node_times(context, path, node, { ctime: Date.now() }, callback); + } + } + + if (error) { + callback(error); + } + else if (flag === XATTR_CREATE && node.xattrs.hasOwnProperty(name)) { + callback(new Errors.EEXIST('attribute already exists')); + } + else if (flag === XATTR_REPLACE && !node.xattrs.hasOwnProperty(name)) { + callback(new Errors.ENOATTR()); + } + else { + node.xattrs[name] = value; + context.put(node.id, node, update_time); + } + } + + if (typeof path_or_fd == 'string') { + path = path_or_fd; + find_node(context, path_or_fd, set_xattr); + } + else if (typeof path_or_fd == 'object' && typeof path_or_fd.id == 'string') { + path = path_or_fd.path; + context.get(path_or_fd.id, set_xattr); + } + else { + callback(new Errors.EINVAL('path or file descriptor of wrong type')); + } +} + +/** + * make_root_directory + */ +// Note: this should only be invoked when formatting a new file system +function make_root_directory(context, callback) { + var superNode; + var directoryNode; + var directoryData; + + function write_super_node(error, existingNode) { + if(!error && existingNode) { + callback(new Errors.EEXIST()); + } else if(error && !(error instanceof Errors.ENOENT)) { + callback(error); + } else { + superNode = new SuperNode(); + context.put(superNode.id, superNode, write_directory_node); + } + } + + function write_directory_node(error) { + if(error) { + callback(error); + } else { + directoryNode = new Node(superNode.rnode, MODE_DIRECTORY); + directoryNode.nlinks += 1; + context.put(directoryNode.id, directoryNode, write_directory_data); + } + } + + function write_directory_data(error) { + if(error) { + callback(error); + } else { + directoryData = {}; + context.put(directoryNode.data, directoryData, callback); + } + } + + context.get(SUPER_NODE_ID, write_super_node); +} + +/** + * make_directory + */ +function make_directory(context, path, callback) { + path = normalize(path); + var name = basename(path); + var parentPath = dirname(path); + + var directoryNode; + var directoryData; + var parentDirectoryNode; + var parentDirectoryData; + + function check_if_directory_exists(error, result) { + if(!error && result) { + callback(new Errors.EEXIST()); + } else if(error && !(error instanceof Errors.ENOENT)) { + callback(error); + } else { + find_node(context, parentPath, read_parent_directory_data); + } + } + + function read_parent_directory_data(error, result) { + if(error) { + callback(error); + } else { + parentDirectoryNode = result; + context.get(parentDirectoryNode.data, write_directory_node); + } + } + + function write_directory_node(error, result) { + if(error) { + callback(error); + } else { + parentDirectoryData = result; + directoryNode = new Node(undefined, MODE_DIRECTORY); + directoryNode.nlinks += 1; + context.put(directoryNode.id, directoryNode, write_directory_data); + } + } + + function write_directory_data(error) { + if(error) { + callback(error); + } else { + directoryData = {}; + context.put(directoryNode.data, directoryData, update_parent_directory_data); + } + } + + function update_time(error) { + if(error) { + callback(error); + } else { + var now = Date.now(); + update_node_times(context, parentPath, parentDirectoryNode, { mtime: now, ctime: now }, callback); + } + } + + function update_parent_directory_data(error) { + if(error) { + callback(error); + } else { + parentDirectoryData[name] = new DirectoryEntry(directoryNode.id, MODE_DIRECTORY); + context.put(parentDirectoryNode.data, parentDirectoryData, update_time); + } + } + + find_node(context, path, check_if_directory_exists); +} + +/** + * remove_directory + */ +function remove_directory(context, path, callback) { + path = normalize(path); + var name = basename(path); + var parentPath = dirname(path); + + var directoryNode; + var directoryData; + var parentDirectoryNode; + var parentDirectoryData; + + function read_parent_directory_data(error, result) { + if(error) { + callback(error); + } else { + parentDirectoryNode = result; + context.get(parentDirectoryNode.data, check_if_node_exists); + } + } + + function check_if_node_exists(error, result) { + if(error) { + callback(error); + } else if(ROOT_DIRECTORY_NAME == name) { + callback(new Errors.EBUSY()); + } else if(!_(result).has(name)) { + callback(new Errors.ENOENT()); + } else { + parentDirectoryData = result; + directoryNode = parentDirectoryData[name].id; + context.get(directoryNode, check_if_node_is_directory); + } + } + + function check_if_node_is_directory(error, result) { + if(error) { + callback(error); + } else if(result.mode != MODE_DIRECTORY) { + callback(new Errors.ENOTDIR()); + } else { + directoryNode = result; + context.get(directoryNode.data, check_if_directory_is_empty); + } + } + + function check_if_directory_is_empty(error, result) { + if(error) { + callback(error); + } else { + directoryData = result; + if(_(directoryData).size() > 0) { + callback(new Errors.ENOTEMPTY()); + } else { + remove_directory_entry_from_parent_directory_node(); + } + } + } + + function update_time(error) { + if(error) { + callback(error); + } else { + var now = Date.now(); + update_node_times(context, parentPath, parentDirectoryNode, { mtime: now, ctime: now }, remove_directory_node); + } + } + + function remove_directory_entry_from_parent_directory_node() { + delete parentDirectoryData[name]; + context.put(parentDirectoryNode.data, parentDirectoryData, update_time); + } + + function remove_directory_node(error) { + if(error) { + callback(error); + } else { + context.delete(directoryNode.id, remove_directory_data); + } + } + + function remove_directory_data(error) { + if(error) { + callback(error); + } else { + context.delete(directoryNode.data, callback); + } + } + + find_node(context, parentPath, read_parent_directory_data); +} + +function open_file(context, path, flags, callback) { + path = normalize(path); + var name = basename(path); + var parentPath = dirname(path); + + var directoryNode; + var directoryData; + var directoryEntry; + var fileNode; + var fileData; + + var followedCount = 0; + + if(ROOT_DIRECTORY_NAME == name) { + if(_(flags).contains(O_WRITE)) { + callback(new Errors.EISDIR('the named file is a directory and O_WRITE is set')); + } else { + find_node(context, path, set_file_node); + } + } else { + find_node(context, parentPath, read_directory_data); + } + + function read_directory_data(error, result) { + if(error) { + callback(error); + } else { + directoryNode = result; + context.get(directoryNode.data, check_if_file_exists); + } + } + + function check_if_file_exists(error, result) { + if(error) { + callback(error); + } else { + directoryData = result; + if(_(directoryData).has(name)) { + if(_(flags).contains(O_EXCLUSIVE)) { + callback(new Errors.ENOENT('O_CREATE and O_EXCLUSIVE are set, and the named file exists')); + } else { + directoryEntry = directoryData[name]; + if(directoryEntry.type == MODE_DIRECTORY && _(flags).contains(O_WRITE)) { + callback(new Errors.EISDIR('the named file is a directory and O_WRITE is set')); + } else { + context.get(directoryEntry.id, check_if_symbolic_link); + } + } + } else { + if(!_(flags).contains(O_CREATE)) { + callback(new Errors.ENOENT('O_CREATE is not set and the named file does not exist')); + } else { + write_file_node(); + } + } + } + } + + function check_if_symbolic_link(error, result) { + if(error) { + callback(error); + } else { + var node = result; + if(node.mode == MODE_SYMBOLIC_LINK) { + followedCount++; + if(followedCount > SYMLOOP_MAX){ + callback(new Errors.ELOOP()); + } else { + follow_symbolic_link(node.data); + } + } else { + set_file_node(undefined, node); + } + } + } + + function follow_symbolic_link(data) { + data = normalize(data); + parentPath = dirname(data); + name = basename(data); if(ROOT_DIRECTORY_NAME == name) { if(_(flags).contains(O_WRITE)) { callback(new Errors.EISDIR('the named file is a directory and O_WRITE is set')); } else { find_node(context, path, set_file_node); } - } else { - find_node(context, parentPath, read_directory_data); } - - function read_directory_data(error, result) { - if(error) { - callback(error); - } else { - directoryNode = result; - context.get(directoryNode.data, check_if_file_exists); - } - } - - function check_if_file_exists(error, result) { - if(error) { - callback(error); - } else { - directoryData = result; - if(_(directoryData).has(name)) { - if(_(flags).contains(O_EXCLUSIVE)) { - callback(new Errors.ENOENT('O_CREATE and O_EXCLUSIVE are set, and the named file exists')); - } else { - directoryEntry = directoryData[name]; - if(directoryEntry.type == MODE_DIRECTORY && _(flags).contains(O_WRITE)) { - callback(new Errors.EISDIR('the named file is a directory and O_WRITE is set')); - } else { - context.get(directoryEntry.id, check_if_symbolic_link); - } - } - } else { - if(!_(flags).contains(O_CREATE)) { - callback(new Errors.ENOENT('O_CREATE is not set and the named file does not exist')); - } else { - write_file_node(); - } - } - } - } - - function check_if_symbolic_link(error, result) { - if(error) { - callback(error); - } else { - var node = result; - if(node.mode == MODE_SYMBOLIC_LINK) { - followedCount++; - if(followedCount > SYMLOOP_MAX){ - callback(new Errors.ELOOP()); - } else { - follow_symbolic_link(node.data); - } - } else { - set_file_node(undefined, node); - } - } - } - - function follow_symbolic_link(data) { - data = normalize(data); - parentPath = dirname(data); - name = basename(data); - if(ROOT_DIRECTORY_NAME == name) { - if(_(flags).contains(O_WRITE)) { - callback(new Errors.EISDIR('the named file is a directory and O_WRITE is set')); - } else { - find_node(context, path, set_file_node); - } - } - find_node(context, parentPath, read_directory_data); - } - - function set_file_node(error, result) { - if(error) { - callback(error); - } else { - fileNode = result; - callback(null, fileNode); - } - } - - function write_file_node() { - fileNode = new Node(undefined, MODE_FILE); - fileNode.nlinks += 1; - context.put(fileNode.id, fileNode, write_file_data); - } - - function write_file_data(error) { - if(error) { - callback(error); - } else { - fileData = new Uint8Array(0); - context.put(fileNode.data, fileData, update_directory_data); - } - } - - function update_time(error) { - if(error) { - callback(error); - } else { - var now = Date.now(); - update_node_times(context, parentPath, directoryNode, { mtime: now, ctime: now }, handle_update_result); - } - } - - function update_directory_data(error) { - if(error) { - callback(error); - } else { - directoryData[name] = new DirectoryEntry(fileNode.id, MODE_FILE); - context.put(directoryNode.data, directoryData, update_time); - } - } - - function handle_update_result(error) { - if(error) { - callback(error); - } else { - callback(null, fileNode); - } - } - } - - function replace_data(context, ofd, buffer, offset, length, callback) { - var fileNode; - - function return_nbytes(error) { - if(error) { - callback(error); - } else { - callback(null, length); - } - } - - function update_time(error) { - if(error) { - callback(error); - } else { - var now = Date.now(); - update_node_times(context, ofd.path, fileNode, { mtime: now, ctime: now }, return_nbytes); - } - } - - function update_file_node(error) { - if(error) { - callback(error); - } else { - context.put(fileNode.id, fileNode, update_time); - } - } - - function write_file_data(error, result) { - if(error) { - callback(error); - } else { - fileNode = result; - var newData = new Uint8Array(length); - var bufferWindow = buffer.subarray(offset, offset + length); - newData.set(bufferWindow); - ofd.position = length; - - fileNode.size = length; - fileNode.version += 1; - - context.put(fileNode.data, newData, update_file_node); - } - } - - context.get(ofd.id, write_file_data); - } - - function write_data(context, ofd, buffer, offset, length, position, callback) { - var fileNode; - var fileData; - - function return_nbytes(error) { - if(error) { - callback(error); - } else { - callback(null, length); - } - } - - function update_time(error) { - if(error) { - callback(error); - } else { - var now = Date.now(); - update_node_times(context, ofd.path, fileNode, { mtime: now, ctime: now }, return_nbytes); - } - } - - function update_file_node(error) { - if(error) { - callback(error); - } else { - context.put(fileNode.id, fileNode, update_time); - } - } - - function update_file_data(error, result) { - if(error) { - callback(error); - } else { - fileData = result; - var _position = (!(undefined === position || null === position)) ? position : ofd.position; - var newSize = Math.max(fileData.length, _position + length); - var newData = new Uint8Array(newSize); - if(fileData) { - newData.set(fileData); - } - var bufferWindow = buffer.subarray(offset, offset + length); - newData.set(bufferWindow, _position); - if(undefined === position) { - ofd.position += length; - } - - fileNode.size = newSize; - fileNode.version += 1; - - context.put(fileNode.data, newData, update_file_node); - } - } - - function read_file_data(error, result) { - if(error) { - callback(error); - } else { - fileNode = result; - context.get(fileNode.data, update_file_data); - } - } - - context.get(ofd.id, read_file_data); - } - - function read_data(context, ofd, buffer, offset, length, position, callback) { - var fileNode; - var fileData; - - function handle_file_data(error, result) { - if(error) { - callback(error); - } else { - fileData = result; - var _position = (!(undefined === position || null === position)) ? position : ofd.position; - length = (_position + length > buffer.length) ? length - _position : length; - var dataView = fileData.subarray(_position, _position + length); - buffer.set(dataView, offset); - if(undefined === position) { - ofd.position += length; - } - callback(null, length); - } - } - - function read_file_data(error, result) { - if(error) { - callback(error); - } else { - fileNode = result; - context.get(fileNode.data, handle_file_data); - } - } - - context.get(ofd.id, read_file_data); - } - - function stat_file(context, path, callback) { - path = normalize(path); - var name = basename(path); - find_node(context, path, standard_check_result_cb(callback)); - } - - function fstat_file(context, ofd, callback) { - context.get(ofd.id, standard_check_result_cb(callback)); - } - - function lstat_file(context, path, callback) { - path = normalize(path); - var name = basename(path); - var parentPath = dirname(path); - - var directoryNode; - var directoryData; - - if(ROOT_DIRECTORY_NAME == name) { - find_node(context, path, standard_check_result_cb(callback)); - } else { - find_node(context, parentPath, read_directory_data); - } - - function read_directory_data(error, result) { - if(error) { - callback(error); - } else { - directoryNode = result; - context.get(directoryNode.data, check_if_file_exists); - } - } - - function check_if_file_exists(error, result) { - if(error) { - callback(error); - } else { - directoryData = result; - if(!_(directoryData).has(name)) { - callback(new Errors.ENOENT('a component of the path does not name an existing file')); - } else { - context.get(directoryData[name].id, standard_check_result_cb(callback)); - } - } - } - } - - function link_node(context, oldpath, newpath, callback) { - oldpath = normalize(oldpath); - var oldname = basename(oldpath); - var oldParentPath = dirname(oldpath); - - newpath = normalize(newpath); - var newname = basename(newpath); - var newParentPath = dirname(newpath); - - var oldDirectoryNode; - var oldDirectoryData; - var newDirectoryNode; - var newDirectoryData; - var fileNode; - - function update_time(error) { - if(error) { - callback(error); - } else { - update_node_times(context, newpath, fileNode, { ctime: Date.now() }, callback); - } - } - - function update_file_node(error, result) { - if(error) { - callback(error); - } else { - fileNode = result; - fileNode.nlinks += 1; - context.put(fileNode.id, fileNode, update_time); - } - } - - function read_directory_entry(error, result) { - if(error) { - callback(error); - } else { - context.get(newDirectoryData[newname].id, update_file_node); - } - } - - function check_if_new_file_exists(error, result) { - if(error) { - callback(error); - } else { - newDirectoryData = result; - if(_(newDirectoryData).has(newname)) { - callback(new Errors.EEXIST('newpath resolves to an existing file')); - } else { - newDirectoryData[newname] = oldDirectoryData[oldname]; - context.put(newDirectoryNode.data, newDirectoryData, read_directory_entry); - } - } - } - - function read_new_directory_data(error, result) { - if(error) { - callback(error); - } else { - newDirectoryNode = result; - context.get(newDirectoryNode.data, check_if_new_file_exists); - } - } - - function check_if_old_file_exists(error, result) { - if(error) { - callback(error); - } else { - oldDirectoryData = result; - if(!_(oldDirectoryData).has(oldname)) { - callback(new Errors.ENOENT('a component of either path prefix does not exist')); - } else { - find_node(context, newParentPath, read_new_directory_data); - } - } - } - - function read_old_directory_data(error, result) { - if(error) { - callback(error); - } else { - oldDirectoryNode = result; - context.get(oldDirectoryNode.data, check_if_old_file_exists); - } - } - - find_node(context, oldParentPath, read_old_directory_data); - } - - function unlink_node(context, path, callback) { - path = normalize(path); - var name = basename(path); - var parentPath = dirname(path); - - var directoryNode; - var directoryData; - var fileNode; - - function update_directory_data(error) { - if(error) { - callback(error); - } else { - delete directoryData[name]; - context.put(directoryNode.data, directoryData, function(error) { - var now = Date.now(); - update_node_times(context, parentPath, directoryNode, { mtime: now, ctime: now }, callback); - }); - } - } - - function delete_file_data(error) { - if(error) { - callback(error); - } else { - context.delete(fileNode.data, update_directory_data); - } - } - - function update_file_node(error, result) { - if(error) { - callback(error); - } else { - fileNode = result; - fileNode.nlinks -= 1; - if(fileNode.nlinks < 1) { - context.delete(fileNode.id, delete_file_data); - } else { - context.put(fileNode.id, fileNode, function(error) { - update_node_times(context, path, fileNode, { ctime: Date.now() }, update_directory_data); - }); - } - } - } - - function check_if_file_exists(error, result) { - if(error) { - callback(error); - } else { - directoryData = result; - if(!_(directoryData).has(name)) { - callback(new Errors.ENOENT('a component of the path does not name an existing file')); - } else { - context.get(directoryData[name].id, update_file_node); - } - } - } - - function read_directory_data(error, result) { - if(error) { - callback(error); - } else { - directoryNode = result; - context.get(directoryNode.data, check_if_file_exists); - } - } - find_node(context, parentPath, read_directory_data); } - function read_directory(context, path, callback) { - path = normalize(path); - var name = basename(path); - - var directoryNode; - var directoryData; - - function handle_directory_data(error, result) { - if(error) { - callback(error); - } else { - directoryData = result; - var files = Object.keys(directoryData); - callback(null, files); - } + function set_file_node(error, result) { + if(error) { + callback(error); + } else { + fileNode = result; + callback(null, fileNode); } - - function read_directory_data(error, result) { - if(error) { - callback(error); - } else { - directoryNode = result; - context.get(directoryNode.data, handle_directory_data); - } - } - - find_node(context, path, read_directory_data); } - function make_symbolic_link(context, srcpath, dstpath, callback) { - dstpath = normalize(dstpath); - var name = basename(dstpath); - var parentPath = dirname(dstpath); + function write_file_node() { + fileNode = new Node(undefined, MODE_FILE); + fileNode.nlinks += 1; + context.put(fileNode.id, fileNode, write_file_data); + } - var directoryNode; - var directoryData; - var fileNode; - - if(ROOT_DIRECTORY_NAME == name) { - callback(new Errors.EEXIST()); + function write_file_data(error) { + if(error) { + callback(error); } else { - find_node(context, parentPath, read_directory_data); + fileData = new Uint8Array(0); + context.put(fileNode.data, fileData, update_directory_data); } + } - function read_directory_data(error, result) { - if(error) { - callback(error); - } else { - directoryNode = result; - context.get(directoryNode.data, check_if_file_exists); - } + function update_time(error) { + if(error) { + callback(error); + } else { + var now = Date.now(); + update_node_times(context, parentPath, directoryNode, { mtime: now, ctime: now }, handle_update_result); } + } - function check_if_file_exists(error, result) { - if(error) { - callback(error); - } else { - directoryData = result; - if(_(directoryData).has(name)) { - callback(new Errors.EEXIST()); - } else { - write_file_node(); + function update_directory_data(error) { + if(error) { + callback(error); + } else { + directoryData[name] = new DirectoryEntry(fileNode.id, MODE_FILE); + context.put(directoryNode.data, directoryData, update_time); + } + } + + function handle_update_result(error) { + if(error) { + callback(error); + } else { + callback(null, fileNode); + } + } +} + +function replace_data(context, ofd, buffer, offset, length, callback) { + var fileNode; + + function return_nbytes(error) { + if(error) { + callback(error); + } else { + callback(null, length); + } + } + + function update_time(error) { + if(error) { + callback(error); + } else { + var now = Date.now(); + update_node_times(context, ofd.path, fileNode, { mtime: now, ctime: now }, return_nbytes); + } + } + + function update_file_node(error) { + if(error) { + callback(error); + } else { + context.put(fileNode.id, fileNode, update_time); + } + } + + function write_file_data(error, result) { + if(error) { + callback(error); + } else { + fileNode = result; + var newData = new Uint8Array(length); + var bufferWindow = buffer.subarray(offset, offset + length); + newData.set(bufferWindow); + ofd.position = length; + + fileNode.size = length; + fileNode.version += 1; + + context.put(fileNode.data, newData, update_file_node); + } + } + + context.get(ofd.id, write_file_data); +} + +function write_data(context, ofd, buffer, offset, length, position, callback) { + var fileNode; + var fileData; + + function return_nbytes(error) { + if(error) { + callback(error); + } else { + callback(null, length); + } + } + + function update_time(error) { + if(error) { + callback(error); + } else { + var now = Date.now(); + update_node_times(context, ofd.path, fileNode, { mtime: now, ctime: now }, return_nbytes); + } + } + + function update_file_node(error) { + if(error) { + callback(error); + } else { + context.put(fileNode.id, fileNode, update_time); + } + } + + function update_file_data(error, result) { + if(error) { + callback(error); + } else { + fileData = result; + var _position = (!(undefined === position || null === position)) ? position : ofd.position; + var newSize = Math.max(fileData.length, _position + length); + var newData = new Uint8Array(newSize); + if(fileData) { + newData.set(fileData); } + var bufferWindow = buffer.subarray(offset, offset + length); + newData.set(bufferWindow, _position); + if(undefined === position) { + ofd.position += length; + } + + fileNode.size = newSize; + fileNode.version += 1; + + context.put(fileNode.data, newData, update_file_node); + } + } + + function read_file_data(error, result) { + if(error) { + callback(error); + } else { + fileNode = result; + context.get(fileNode.data, update_file_data); + } + } + + context.get(ofd.id, read_file_data); +} + +function read_data(context, ofd, buffer, offset, length, position, callback) { + var fileNode; + var fileData; + + function handle_file_data(error, result) { + if(error) { + callback(error); + } else { + fileData = result; + var _position = (!(undefined === position || null === position)) ? position : ofd.position; + length = (_position + length > buffer.length) ? length - _position : length; + var dataView = fileData.subarray(_position, _position + length); + buffer.set(dataView, offset); + if(undefined === position) { + ofd.position += length; + } + callback(null, length); + } + } + + function read_file_data(error, result) { + if(error) { + callback(error); + } else { + fileNode = result; + context.get(fileNode.data, handle_file_data); + } + } + + context.get(ofd.id, read_file_data); +} + +function stat_file(context, path, callback) { + path = normalize(path); + var name = basename(path); + find_node(context, path, standard_check_result_cb(callback)); +} + +function fstat_file(context, ofd, callback) { + context.get(ofd.id, standard_check_result_cb(callback)); +} + +function lstat_file(context, path, callback) { + path = normalize(path); + var name = basename(path); + var parentPath = dirname(path); + + var directoryNode; + var directoryData; + + if(ROOT_DIRECTORY_NAME == name) { + find_node(context, path, standard_check_result_cb(callback)); + } else { + find_node(context, parentPath, read_directory_data); + } + + function read_directory_data(error, result) { + if(error) { + callback(error); + } else { + directoryNode = result; + context.get(directoryNode.data, check_if_file_exists); + } + } + + function check_if_file_exists(error, result) { + if(error) { + callback(error); + } else { + directoryData = result; + if(!_(directoryData).has(name)) { + callback(new Errors.ENOENT('a component of the path does not name an existing file')); + } else { + context.get(directoryData[name].id, standard_check_result_cb(callback)); } } + } +} - function write_file_node() { - fileNode = new Node(undefined, MODE_SYMBOLIC_LINK); - fileNode.nlinks += 1; - fileNode.size = srcpath.length; - fileNode.data = srcpath; - context.put(fileNode.id, fileNode, update_directory_data); +function link_node(context, oldpath, newpath, callback) { + oldpath = normalize(oldpath); + var oldname = basename(oldpath); + var oldParentPath = dirname(oldpath); + + newpath = normalize(newpath); + var newname = basename(newpath); + var newParentPath = dirname(newpath); + + var oldDirectoryNode; + var oldDirectoryData; + var newDirectoryNode; + var newDirectoryData; + var fileNode; + + function update_time(error) { + if(error) { + callback(error); + } else { + update_node_times(context, newpath, fileNode, { ctime: Date.now() }, callback); } + } - function update_time(error) { - if(error) { - callback(error); + function update_file_node(error, result) { + if(error) { + callback(error); + } else { + fileNode = result; + fileNode.nlinks += 1; + context.put(fileNode.id, fileNode, update_time); + } + } + + function read_directory_entry(error, result) { + if(error) { + callback(error); + } else { + context.get(newDirectoryData[newname].id, update_file_node); + } + } + + function check_if_new_file_exists(error, result) { + if(error) { + callback(error); + } else { + newDirectoryData = result; + if(_(newDirectoryData).has(newname)) { + callback(new Errors.EEXIST('newpath resolves to an existing file')); } else { + newDirectoryData[newname] = oldDirectoryData[oldname]; + context.put(newDirectoryNode.data, newDirectoryData, read_directory_entry); + } + } + } + + function read_new_directory_data(error, result) { + if(error) { + callback(error); + } else { + newDirectoryNode = result; + context.get(newDirectoryNode.data, check_if_new_file_exists); + } + } + + function check_if_old_file_exists(error, result) { + if(error) { + callback(error); + } else { + oldDirectoryData = result; + if(!_(oldDirectoryData).has(oldname)) { + callback(new Errors.ENOENT('a component of either path prefix does not exist')); + } else { + find_node(context, newParentPath, read_new_directory_data); + } + } + } + + function read_old_directory_data(error, result) { + if(error) { + callback(error); + } else { + oldDirectoryNode = result; + context.get(oldDirectoryNode.data, check_if_old_file_exists); + } + } + + find_node(context, oldParentPath, read_old_directory_data); +} + +function unlink_node(context, path, callback) { + path = normalize(path); + var name = basename(path); + var parentPath = dirname(path); + + var directoryNode; + var directoryData; + var fileNode; + + function update_directory_data(error) { + if(error) { + callback(error); + } else { + delete directoryData[name]; + context.put(directoryNode.data, directoryData, function(error) { var now = Date.now(); update_node_times(context, parentPath, directoryNode, { mtime: now, ctime: now }, callback); - } + }); } + } - function update_directory_data(error) { - if(error) { - callback(error); + function delete_file_data(error) { + if(error) { + callback(error); + } else { + context.delete(fileNode.data, update_directory_data); + } + } + + function update_file_node(error, result) { + if(error) { + callback(error); + } else { + fileNode = result; + fileNode.nlinks -= 1; + if(fileNode.nlinks < 1) { + context.delete(fileNode.id, delete_file_data); } else { - directoryData[name] = new DirectoryEntry(fileNode.id, MODE_SYMBOLIC_LINK); - context.put(directoryNode.data, directoryData, update_time); + context.put(fileNode.id, fileNode, function(error) { + update_node_times(context, path, fileNode, { ctime: Date.now() }, update_directory_data); + }); } } } - function read_link(context, path, callback) { - path = normalize(path); - var name = basename(path); - var parentPath = dirname(path); + function check_if_file_exists(error, result) { + if(error) { + callback(error); + } else { + directoryData = result; + if(!_(directoryData).has(name)) { + callback(new Errors.ENOENT('a component of the path does not name an existing file')); + } else { + context.get(directoryData[name].id, update_file_node); + } + } + } - var directoryNode; - var directoryData; + function read_directory_data(error, result) { + if(error) { + callback(error); + } else { + directoryNode = result; + context.get(directoryNode.data, check_if_file_exists); + } + } + find_node(context, parentPath, read_directory_data); +} + +function read_directory(context, path, callback) { + path = normalize(path); + var name = basename(path); + + var directoryNode; + var directoryData; + + function handle_directory_data(error, result) { + if(error) { + callback(error); + } else { + directoryData = result; + var files = Object.keys(directoryData); + callback(null, files); + } + } + + function read_directory_data(error, result) { + if(error) { + callback(error); + } else { + directoryNode = result; + context.get(directoryNode.data, handle_directory_data); + } + } + + find_node(context, path, read_directory_data); +} + +function make_symbolic_link(context, srcpath, dstpath, callback) { + dstpath = normalize(dstpath); + var name = basename(dstpath); + var parentPath = dirname(dstpath); + + var directoryNode; + var directoryData; + var fileNode; + + if(ROOT_DIRECTORY_NAME == name) { + callback(new Errors.EEXIST()); + } else { find_node(context, parentPath, read_directory_data); + } - function read_directory_data(error, result) { - if(error) { - callback(error); - } else { - directoryNode = result; - context.get(directoryNode.data, check_if_file_exists); - } + function read_directory_data(error, result) { + if(error) { + callback(error); + } else { + directoryNode = result; + context.get(directoryNode.data, check_if_file_exists); } + } - function check_if_file_exists(error, result) { - if(error) { - callback(error); + function check_if_file_exists(error, result) { + if(error) { + callback(error); + } else { + directoryData = result; + if(_(directoryData).has(name)) { + callback(new Errors.EEXIST()); } else { - directoryData = result; - if(!_(directoryData).has(name)) { - callback(new Errors.ENOENT('a component of the path does not name an existing file')); - } else { - context.get(directoryData[name].id, check_if_symbolic); - } - } - } - - function check_if_symbolic(error, result) { - if(error) { - callback(error); - } else { - if(result.mode != MODE_SYMBOLIC_LINK) { - callback(new Errors.EINVAL("path not a symbolic link")); - } else { - callback(null, result.data); - } + write_file_node(); } } } - function truncate_file(context, path, length, callback) { - path = normalize(path); + function write_file_node() { + fileNode = new Node(undefined, MODE_SYMBOLIC_LINK); + fileNode.nlinks += 1; + fileNode.size = srcpath.length; + fileNode.data = srcpath; + context.put(fileNode.id, fileNode, update_directory_data); + } - var fileNode; - - function read_file_data (error, node) { - if (error) { - callback(error); - } else if(node.mode == MODE_DIRECTORY ) { - callback(new Errors.EISDIR()); - } else{ - fileNode = node; - context.get(fileNode.data, truncate_file_data); - } + function update_time(error) { + if(error) { + callback(error); + } else { + var now = Date.now(); + update_node_times(context, parentPath, directoryNode, { mtime: now, ctime: now }, callback); } + } - function truncate_file_data(error, fileData) { - if (error) { - callback(error); + function update_directory_data(error) { + if(error) { + callback(error); + } else { + directoryData[name] = new DirectoryEntry(fileNode.id, MODE_SYMBOLIC_LINK); + context.put(directoryNode.data, directoryData, update_time); + } + } +} + +function read_link(context, path, callback) { + path = normalize(path); + var name = basename(path); + var parentPath = dirname(path); + + var directoryNode; + var directoryData; + + find_node(context, parentPath, read_directory_data); + + function read_directory_data(error, result) { + if(error) { + callback(error); + } else { + directoryNode = result; + context.get(directoryNode.data, check_if_file_exists); + } + } + + function check_if_file_exists(error, result) { + if(error) { + callback(error); + } else { + directoryData = result; + if(!_(directoryData).has(name)) { + callback(new Errors.ENOENT('a component of the path does not name an existing file')); } else { - var data = new Uint8Array(length); - if(fileData) { - data.set(fileData.subarray(0, length)); - } - context.put(fileNode.data, data, update_file_node); + context.get(directoryData[name].id, check_if_symbolic); } } + } + + function check_if_symbolic(error, result) { + if(error) { + callback(error); + } else { + if(result.mode != MODE_SYMBOLIC_LINK) { + callback(new Errors.EINVAL("path not a symbolic link")); + } else { + callback(null, result.data); + } + } + } +} + +function truncate_file(context, path, length, callback) { + path = normalize(path); + + var fileNode; + + function read_file_data (error, node) { + if (error) { + callback(error); + } else if(node.mode == MODE_DIRECTORY ) { + callback(new Errors.EISDIR()); + } else{ + fileNode = node; + context.get(fileNode.data, truncate_file_data); + } + } + + function truncate_file_data(error, fileData) { + if (error) { + callback(error); + } else { + var data = new Uint8Array(length); + if(fileData) { + data.set(fileData.subarray(0, length)); + } + context.put(fileNode.data, data, update_file_node); + } + } + + function update_time(error) { + if(error) { + callback(error); + } else { + var now = Date.now(); + update_node_times(context, path, fileNode, { mtime: now, ctime: now }, callback); + } + } + + function update_file_node (error) { + if(error) { + callback(error); + } else { + fileNode.size = length; + fileNode.version += 1; + context.put(fileNode.id, fileNode, update_time); + } + } + + if(length < 0) { + callback(new Errors.EINVAL('length cannot be negative')); + } else { + find_node(context, path, read_file_data); + } +} + +function ftruncate_file(context, ofd, length, callback) { + var fileNode; + + function read_file_data (error, node) { + if (error) { + callback(error); + } else if(node.mode == MODE_DIRECTORY ) { + callback(new Errors.EISDIR()); + } else{ + fileNode = node; + context.get(fileNode.data, truncate_file_data); + } + } + + function truncate_file_data(error, fileData) { + if (error) { + callback(error); + } else { + var data = new Uint8Array(length); + if(fileData) { + data.set(fileData.subarray(0, length)); + } + context.put(fileNode.data, data, update_file_node); + } + } + + function update_time(error) { + if(error) { + callback(error); + } else { + var now = Date.now(); + update_node_times(context, ofd.path, fileNode, { mtime: now, ctime: now }, callback); + } + } + + function update_file_node (error) { + if(error) { + callback(error); + } else { + fileNode.size = length; + fileNode.version += 1; + context.put(fileNode.id, fileNode, update_time); + } + } + + if(length < 0) { + callback(new Errors.EINVAL('length cannot be negative')); + } else { + context.get(ofd.id, read_file_data); + } +} + +function utimes_file(context, path, atime, mtime, callback) { + path = normalize(path); + + function update_times(error, node) { + if (error) { + callback(error); + } else { + update_node_times(context, path, node, { atime: atime, ctime: mtime, mtime: mtime }, callback); + } + } + + if (typeof atime != 'number' || typeof mtime != 'number') { + callback(new Errors.EINVAL('atime and mtime must be number')); + } + else if (atime < 0 || mtime < 0) { + callback(new Errors.EINVAL('atime and mtime must be positive integers')); + } + else { + find_node(context, path, update_times); + } +} + +function futimes_file(context, ofd, atime, mtime, callback) { + + function update_times (error, node) { + if (error) { + callback(error); + } else { + update_node_times(context, ofd.path, node, { atime: atime, ctime: mtime, mtime: mtime }, callback); + } + } + + if (typeof atime != 'number' || typeof mtime != 'number') { + callback(new Errors.EINVAL('atime and mtime must be a number')); + } + else if (atime < 0 || mtime < 0) { + callback(new Errors.EINVAL('atime and mtime must be positive integers')); + } + else { + context.get(ofd.id, update_times); + } +} + +function setxattr_file(context, path, name, value, flag, callback) { + path = normalize(path); + + if (typeof name != 'string') { + callback(new Errors.EINVAL('attribute name must be a string')); + } + else if (!name) { + callback(new Errors.EINVAL('attribute name cannot be an empty string')); + } + else if (flag !== null && + flag !== XATTR_CREATE && flag !== XATTR_REPLACE) { + callback(new Errors.EINVAL('invalid flag, must be null, XATTR_CREATE or XATTR_REPLACE')); + } + else { + set_extended_attribute(context, path, name, value, flag, callback); + } +} + +function fsetxattr_file (context, ofd, name, value, flag, callback) { + if (typeof name != 'string') { + callback(new Errors.EINVAL('attribute name must be a string')); + } + else if (!name) { + callback(new Errors.EINVAL('attribute name cannot be an empty string')); + } + else if (flag !== null && + flag !== XATTR_CREATE && flag !== XATTR_REPLACE) { + callback(new Errors.EINVAL('invalid flag, must be null, XATTR_CREATE or XATTR_REPLACE')); + } + else { + set_extended_attribute(context, ofd, name, value, flag, callback); + } +} + +function getxattr_file (context, path, name, callback) { + path = normalize(path); + + function get_xattr(error, node) { + var xattr = (node ? node.xattrs[name] : null); + + if (error) { + callback (error); + } + else if (!node.xattrs.hasOwnProperty(name)) { + callback(new Errors.ENOATTR()); + } + else { + callback(null, node.xattrs[name]); + } + } + + if (typeof name != 'string') { + callback(new Errors.EINVAL('attribute name must be a string')); + } + else if (!name) { + callback(new Errors.EINVAL('attribute name cannot be an empty string')); + } + else { + find_node(context, path, get_xattr); + } +} + +function fgetxattr_file (context, ofd, name, callback) { + + function get_xattr (error, node) { + var xattr = (node ? node.xattrs[name] : null); + + if (error) { + callback(error); + } + else if (!node.xattrs.hasOwnProperty(name)) { + callback(new Errors.ENOATTR()); + } + else { + callback(null, node.xattrs[name]); + } + } + + if (typeof name != 'string') { + callback(new Errors.EINVAL()); + } + else if (!name) { + callback(new Errors.EINVAL('attribute name cannot be an empty string')); + } + else { + context.get(ofd.id, get_xattr); + } +} + +function removexattr_file (context, path, name, callback) { + path = normalize(path); + + function remove_xattr (error, node) { + var xattr = (node ? node.xattrs : null); function update_time(error) { if(error) { callback(error); } else { - var now = Date.now(); - update_node_times(context, path, fileNode, { mtime: now, ctime: now }, callback); + update_node_times(context, path, node, { ctime: Date.now() }, callback); } } - function update_file_node (error) { - if(error) { - callback(error); - } else { - fileNode.size = length; - fileNode.version += 1; - context.put(fileNode.id, fileNode, update_time); - } + if (error) { + callback(error); } - - if(length < 0) { - callback(new Errors.EINVAL('length cannot be negative')); - } else { - find_node(context, path, read_file_data); + else if (!xattr.hasOwnProperty(name)) { + callback(new Errors.ENOATTR()); + } + else { + delete node.xattrs[name]; + context.put(node.id, node, update_time); } } - function ftruncate_file(context, ofd, length, callback) { - var fileNode; + if (typeof name != 'string') { + callback(new Errors.EINVAL('attribute name must be a string')); + } + else if (!name) { + callback(new Errors.EINVAL('attribute name cannot be an empty string')); + } + else { + find_node(context, path, remove_xattr); + } +} - function read_file_data (error, node) { - if (error) { - callback(error); - } else if(node.mode == MODE_DIRECTORY ) { - callback(new Errors.EISDIR()); - } else{ - fileNode = node; - context.get(fileNode.data, truncate_file_data); - } - } - - function truncate_file_data(error, fileData) { - if (error) { - callback(error); - } else { - var data = new Uint8Array(length); - if(fileData) { - data.set(fileData.subarray(0, length)); - } - context.put(fileNode.data, data, update_file_node); - } - } +function fremovexattr_file (context, ofd, name, callback) { + function remove_xattr (error, node) { function update_time(error) { if(error) { callback(error); } else { - var now = Date.now(); - update_node_times(context, ofd.path, fileNode, { mtime: now, ctime: now }, callback); - } - } - function update_file_node (error) { - if(error) { - callback(error); - } else { - fileNode.size = length; - fileNode.version += 1; - context.put(fileNode.id, fileNode, update_time); + update_node_times(context, ofd.path, node, { ctime: Date.now() }, callback); } } - if(length < 0) { - callback(new Errors.EINVAL('length cannot be negative')); + if (error) { + callback(error); + } + else if (!node.xattrs.hasOwnProperty(name)) { + callback(new Errors.ENOATTR()); + } + else { + delete node.xattrs[name]; + context.put(node.id, node, update_time); + } + } + + if (typeof name != 'string') { + callback(new Errors.EINVAL('attribute name must be a string')); + } + else if (!name) { + callback(new Errors.EINVAL('attribute name cannot be an empty string')); + } + else { + context.get(ofd.id, remove_xattr); + } +} + +function validate_flags(flags) { + if(!_(O_FLAGS).has(flags)) { + return null; + } + return O_FLAGS[flags]; +} + +function validate_file_options(options, enc, fileMode){ + if(!options) { + options = { encoding: enc, flag: fileMode }; + } else if(typeof options === "function") { + options = { encoding: enc, flag: fileMode }; + } else if(typeof options === "string") { + options = { encoding: options, flag: fileMode }; + } + return options; +} + +function pathCheck(path, callback) { + var err; + if(isNullPath(path)) { + err = new Error('Path must be a string without null bytes.'); + } else if(!isAbsolutePath(path)) { + err = new Error('Path must be absolute.'); + } + + if(err) { + callback(err); + return false; + } + return true; +} + + +function open(fs, context, path, flags, mode, callback) { + // NOTE: we support the same signature as node with a `mode` arg, + // but ignore it. + callback = arguments[arguments.length - 1]; + if(!pathCheck(path, callback)) return; + + function check_result(error, fileNode) { + if(error) { + callback(error); } else { - context.get(ofd.id, read_file_data); - } - } - - function utimes_file(context, path, atime, mtime, callback) { - path = normalize(path); - - function update_times(error, node) { - if (error) { - callback(error); + var position; + if(_(flags).contains(O_APPEND)) { + position = fileNode.size; } else { - update_node_times(context, path, node, { atime: atime, ctime: mtime, mtime: mtime }, callback); + position = 0; } - } - - if (typeof atime != 'number' || typeof mtime != 'number') { - callback(new Errors.EINVAL('atime and mtime must be number')); - } - else if (atime < 0 || mtime < 0) { - callback(new Errors.EINVAL('atime and mtime must be positive integers')); - } - else { - find_node(context, path, update_times); + var openFileDescription = new OpenFileDescription(path, fileNode.id, flags, position); + var fd = fs.allocDescriptor(openFileDescription); + callback(null, fd); } } - function futimes_file(context, ofd, atime, mtime, callback) { + flags = validate_flags(flags); + if(!flags) { + callback(new Errors.EINVAL('flags is not valid')); + } - function update_times (error, node) { - if (error) { - callback(error); - } else { - update_node_times(context, ofd.path, node, { atime: atime, ctime: mtime, mtime: mtime }, callback); - } - } + open_file(context, path, flags, check_result); +} - if (typeof atime != 'number' || typeof mtime != 'number') { - callback(new Errors.EINVAL('atime and mtime must be a number')); - } - else if (atime < 0 || mtime < 0) { - callback(new Errors.EINVAL('atime and mtime must be positive integers')); - } - else { - context.get(ofd.id, update_times); +function close(fs, context, fd, callback) { + if(!_(fs.openFiles).has(fd)) { + callback(new Errors.EBADF()); + } else { + fs.releaseDescriptor(fd); + callback(null); + } +} + +function mknod(fs, context, path, mode, callback) { + if(!pathCheck(path, callback)) return; + make_node(context, path, mode, callback); +} + +function mkdir(fs, context, path, mode, callback) { + // NOTE: we support passing a mode arg, but we ignore it internally for now. + callback = arguments[arguments.length - 1]; + if(!pathCheck(path, callback)) return; + make_directory(context, path, standard_check_result_cb(callback)); +} + +function rmdir(fs, context, path, callback) { + if(!pathCheck(path, callback)) return; + remove_directory(context, path, standard_check_result_cb(callback)); +} + +function stat(fs, context, path, callback) { + if(!pathCheck(path, callback)) return; + + function check_result(error, result) { + if(error) { + callback(error); + } else { + var stats = new Stats(result, fs.name); + callback(null, stats); } } - function setxattr_file(context, path, name, value, flag, callback) { - path = normalize(path); + stat_file(context, path, check_result); +} - if (typeof name != 'string') { - callback(new Errors.EINVAL('attribute name must be a string')); - } - else if (!name) { - callback(new Errors.EINVAL('attribute name cannot be an empty string')); - } - else if (flag !== null && - flag !== XATTR_CREATE && flag !== XATTR_REPLACE) { - callback(new Errors.EINVAL('invalid flag, must be null, XATTR_CREATE or XATTR_REPLACE')); - } - else { - set_extended_attribute(context, path, name, value, flag, callback); +function fstat(fs, context, fd, callback) { + function check_result(error, result) { + if(error) { + callback(error); + } else { + var stats = new Stats(result, fs.name); + callback(null, stats); } } - function fsetxattr_file (context, ofd, name, value, flag, callback) { - if (typeof name != 'string') { - callback(new Errors.EINVAL('attribute name must be a string')); - } - else if (!name) { - callback(new Errors.EINVAL('attribute name cannot be an empty string')); - } - else if (flag !== null && - flag !== XATTR_CREATE && flag !== XATTR_REPLACE) { - callback(new Errors.EINVAL('invalid flag, must be null, XATTR_CREATE or XATTR_REPLACE')); - } - else { - set_extended_attribute(context, ofd, name, value, flag, callback); - } + var ofd = fs.openFiles[fd]; + if(!ofd) { + callback(new Errors.EBADF()); + } else { + fstat_file(context, ofd, check_result); + } +} + +function link(fs, context, oldpath, newpath, callback) { + if(!pathCheck(oldpath, callback)) return; + if(!pathCheck(newpath, callback)) return; + link_node(context, oldpath, newpath, standard_check_result_cb(callback)); +} + +function unlink(fs, context, path, callback) { + if(!pathCheck(path, callback)) return; + unlink_node(context, path, standard_check_result_cb(callback)); +} + +function read(fs, context, fd, buffer, offset, length, position, callback) { + // Follow how node.js does this + function wrapped_cb(err, bytesRead) { + // Retain a reference to buffer so that it can't be GC'ed too soon. + callback(err, bytesRead || 0, buffer); } - function getxattr_file (context, path, name, callback) { - path = normalize(path); + offset = (undefined === offset) ? 0 : offset; + length = (undefined === length) ? buffer.length - offset : length; + callback = arguments[arguments.length - 1]; - function get_xattr(error, node) { - var xattr = (node ? node.xattrs[name] : null); + var ofd = fs.openFiles[fd]; + if(!ofd) { + callback(new Errors.EBADF()); + } else if(!_(ofd.flags).contains(O_READ)) { + callback(new Errors.EBADF('descriptor does not permit reading')); + } else { + read_data(context, ofd, buffer, offset, length, position, standard_check_result_cb(wrapped_cb)); + } +} - if (error) { - callback (error); - } - else if (!node.xattrs.hasOwnProperty(name)) { - callback(new Errors.ENOATTR()); - } - else { - callback(null, node.xattrs[name]); - } - } +function readFile(fs, context, path, options, callback) { + callback = arguments[arguments.length - 1]; + options = validate_file_options(options, null, 'r'); - if (typeof name != 'string') { - callback(new Errors.EINVAL('attribute name must be a string')); - } - else if (!name) { - callback(new Errors.EINVAL('attribute name cannot be an empty string')); - } - else { - find_node(context, path, get_xattr); - } + if(!pathCheck(path, callback)) return; + + var flags = validate_flags(options.flag || 'r'); + if(!flags) { + callback(new Errors.EINVAL('flags is not valid')); } - function fgetxattr_file (context, ofd, name, callback) { - - function get_xattr (error, node) { - var xattr = (node ? node.xattrs[name] : null); - - if (error) { - callback(error); - } - else if (!node.xattrs.hasOwnProperty(name)) { - callback(new Errors.ENOATTR()); - } - else { - callback(null, node.xattrs[name]); - } - } - - if (typeof name != 'string') { - callback(new Errors.EINVAL()); - } - else if (!name) { - callback(new Errors.EINVAL('attribute name cannot be an empty string')); - } - else { - context.get(ofd.id, get_xattr); - } - } - - function removexattr_file (context, path, name, callback) { - path = normalize(path); - - function remove_xattr (error, node) { - var xattr = (node ? node.xattrs : null); - - function update_time(error) { - if(error) { - callback(error); - } else { - update_node_times(context, path, node, { ctime: Date.now() }, callback); - } - } - - if (error) { - callback(error); - } - else if (!xattr.hasOwnProperty(name)) { - callback(new Errors.ENOATTR()); - } - else { - delete node.xattrs[name]; - context.put(node.id, node, update_time); - } - } - - if (typeof name != 'string') { - callback(new Errors.EINVAL('attribute name must be a string')); - } - else if (!name) { - callback(new Errors.EINVAL('attribute name cannot be an empty string')); - } - else { - find_node(context, path, remove_xattr); - } - } - - function fremovexattr_file (context, ofd, name, callback) { - - function remove_xattr (error, node) { - function update_time(error) { - if(error) { - callback(error); - } else { - update_node_times(context, ofd.path, node, { ctime: Date.now() }, callback); - } - } - - if (error) { - callback(error); - } - else if (!node.xattrs.hasOwnProperty(name)) { - callback(new Errors.ENOATTR()); - } - else { - delete node.xattrs[name]; - context.put(node.id, node, update_time); - } - } - - if (typeof name != 'string') { - callback(new Errors.EINVAL('attribute name must be a string')); - } - else if (!name) { - callback(new Errors.EINVAL('attribute name cannot be an empty string')); - } - else { - context.get(ofd.id, remove_xattr); - } - } - - function validate_flags(flags) { - if(!_(O_FLAGS).has(flags)) { - return null; - } - return O_FLAGS[flags]; - } - - function validate_file_options(options, enc, fileMode){ - if(!options) { - options = { encoding: enc, flag: fileMode }; - } else if(typeof options === "function") { - options = { encoding: enc, flag: fileMode }; - } else if(typeof options === "string") { - options = { encoding: options, flag: fileMode }; - } - return options; - } - - function pathCheck(path, callback) { - var err; - if(isNullPath(path)) { - err = new Error('Path must be a string without null bytes.'); - } else if(!isAbsolutePath(path)) { - err = new Error('Path must be absolute.'); - } - + open_file(context, path, flags, function(err, fileNode) { if(err) { - callback(err); - return false; + return callback(err); } - return true; - } + var ofd = new OpenFileDescription(path, fileNode.id, flags, 0); + var fd = fs.allocDescriptor(ofd); - - function open(fs, context, path, flags, mode, callback) { - // NOTE: we support the same signature as node with a `mode` arg, - // but ignore it. - callback = arguments[arguments.length - 1]; - if(!pathCheck(path, callback)) return; - - function check_result(error, fileNode) { - if(error) { - callback(error); - } else { - var position; - if(_(flags).contains(O_APPEND)) { - position = fileNode.size; - } else { - position = 0; - } - var openFileDescription = new OpenFileDescription(path, fileNode.id, flags, position); - var fd = fs.allocDescriptor(openFileDescription); - callback(null, fd); + fstat_file(context, ofd, function(err2, fstatResult) { + if(err2) { + return callback(err2); } - } - flags = validate_flags(flags); - if(!flags) { - callback(new Errors.EINVAL('flags is not valid')); - } + var stats = new Stats(fstatResult, fs.name); + var size = stats.size; + var buffer = new Uint8Array(size); - open_file(context, path, flags, check_result); + read_data(context, ofd, buffer, 0, size, 0, function(err3, nbytes) { + if(err3) { + return callback(err3); + } + fs.releaseDescriptor(fd); + + var data; + if(options.encoding === 'utf8') { + data = new TextDecoder('utf-8').decode(buffer); + } else { + data = buffer; + } + callback(null, data); + }); + }); + }); +} + +function write(fs, context, fd, buffer, offset, length, position, callback) { + callback = arguments[arguments.length - 1]; + offset = (undefined === offset) ? 0 : offset; + length = (undefined === length) ? buffer.length - offset : length; + + var ofd = fs.openFiles[fd]; + if(!ofd) { + callback(new Errors.EBADF()); + } else if(!_(ofd.flags).contains(O_WRITE)) { + callback(new Errors.EBADF('descriptor does not permit writing')); + } else if(buffer.length - offset < length) { + callback(new Errors.EIO('intput buffer is too small')); + } else { + write_data(context, ofd, buffer, offset, length, position, standard_check_result_cb(callback)); + } +} + +function writeFile(fs, context, path, data, options, callback) { + callback = arguments[arguments.length - 1]; + options = validate_file_options(options, 'utf8', 'w'); + + if(!pathCheck(path, callback)) return; + + var flags = validate_flags(options.flag || 'w'); + if(!flags) { + callback(new Errors.EINVAL('flags is not valid')); } - function close(fs, context, fd, callback) { - if(!_(fs.openFiles).has(fd)) { - callback(new Errors.EBADF()); - } else { + data = data || ''; + if(typeof data === "number") { + data = '' + data; + } + if(typeof data === "string" && options.encoding === 'utf8') { + data = new TextEncoder('utf-8').encode(data); + } + + open_file(context, path, flags, function(err, fileNode) { + if(err) { + return callback(err); + } + var ofd = new OpenFileDescription(path, fileNode.id, flags, 0); + var fd = fs.allocDescriptor(ofd); + + replace_data(context, ofd, data, 0, data.length, function(err2, nbytes) { + if(err2) { + return callback(err2); + } fs.releaseDescriptor(fd); callback(null); - } - } - - function mknod(fs, context, path, mode, callback) { - if(!pathCheck(path, callback)) return; - make_node(context, path, mode, callback); - } - - function mkdir(fs, context, path, mode, callback) { - // NOTE: we support passing a mode arg, but we ignore it internally for now. - callback = arguments[arguments.length - 1]; - if(!pathCheck(path, callback)) return; - make_directory(context, path, standard_check_result_cb(callback)); - } - - function rmdir(fs, context, path, callback) { - if(!pathCheck(path, callback)) return; - remove_directory(context, path, standard_check_result_cb(callback)); - } - - function stat(fs, context, path, callback) { - if(!pathCheck(path, callback)) return; - - function check_result(error, result) { - if(error) { - callback(error); - } else { - var stats = new Stats(result, fs.name); - callback(null, stats); - } - } - - stat_file(context, path, check_result); - } - - function fstat(fs, context, fd, callback) { - function check_result(error, result) { - if(error) { - callback(error); - } else { - var stats = new Stats(result, fs.name); - callback(null, stats); - } - } - - var ofd = fs.openFiles[fd]; - if(!ofd) { - callback(new Errors.EBADF()); - } else { - fstat_file(context, ofd, check_result); - } - } - - function link(fs, context, oldpath, newpath, callback) { - if(!pathCheck(oldpath, callback)) return; - if(!pathCheck(newpath, callback)) return; - link_node(context, oldpath, newpath, standard_check_result_cb(callback)); - } - - function unlink(fs, context, path, callback) { - if(!pathCheck(path, callback)) return; - unlink_node(context, path, standard_check_result_cb(callback)); - } - - function read(fs, context, fd, buffer, offset, length, position, callback) { - // Follow how node.js does this - function wrapped_cb(err, bytesRead) { - // Retain a reference to buffer so that it can't be GC'ed too soon. - callback(err, bytesRead || 0, buffer); - } - - offset = (undefined === offset) ? 0 : offset; - length = (undefined === length) ? buffer.length - offset : length; - callback = arguments[arguments.length - 1]; - - var ofd = fs.openFiles[fd]; - if(!ofd) { - callback(new Errors.EBADF()); - } else if(!_(ofd.flags).contains(O_READ)) { - callback(new Errors.EBADF('descriptor does not permit reading')); - } else { - read_data(context, ofd, buffer, offset, length, position, standard_check_result_cb(wrapped_cb)); - } - } - - function readFile(fs, context, path, options, callback) { - callback = arguments[arguments.length - 1]; - options = validate_file_options(options, null, 'r'); - - if(!pathCheck(path, callback)) return; - - var flags = validate_flags(options.flag || 'r'); - if(!flags) { - callback(new Errors.EINVAL('flags is not valid')); - } - - open_file(context, path, flags, function(err, fileNode) { - if(err) { - return callback(err); - } - var ofd = new OpenFileDescription(path, fileNode.id, flags, 0); - var fd = fs.allocDescriptor(ofd); - - fstat_file(context, ofd, function(err2, fstatResult) { - if(err2) { - return callback(err2); - } - - var stats = new Stats(fstatResult, fs.name); - var size = stats.size; - var buffer = new Uint8Array(size); - - read_data(context, ofd, buffer, 0, size, 0, function(err3, nbytes) { - if(err3) { - return callback(err3); - } - fs.releaseDescriptor(fd); - - var data; - if(options.encoding === 'utf8') { - data = new TextDecoder('utf-8').decode(buffer); - } else { - data = buffer; - } - callback(null, data); - }); - }); }); + }); +} + +function appendFile(fs, context, path, data, options, callback) { + callback = arguments[arguments.length - 1]; + options = validate_file_options(options, 'utf8', 'a'); + + if(!pathCheck(path, callback)) return; + + var flags = validate_flags(options.flag || 'a'); + if(!flags) { + callback(new Errors.EINVAL('flags is not valid')); } - function write(fs, context, fd, buffer, offset, length, position, callback) { - callback = arguments[arguments.length - 1]; - offset = (undefined === offset) ? 0 : offset; - length = (undefined === length) ? buffer.length - offset : length; + data = data || ''; + if(typeof data === "number") { + data = '' + data; + } + if(typeof data === "string" && options.encoding === 'utf8') { + data = new TextEncoder('utf-8').encode(data); + } - var ofd = fs.openFiles[fd]; - if(!ofd) { - callback(new Errors.EBADF()); - } else if(!_(ofd.flags).contains(O_WRITE)) { - callback(new Errors.EBADF('descriptor does not permit writing')); - } else if(buffer.length - offset < length) { - callback(new Errors.EIO('intput buffer is too small')); + open_file(context, path, flags, function(err, fileNode) { + if(err) { + return callback(err); + } + var ofd = new OpenFileDescription(path, fileNode.id, flags, fileNode.size); + var fd = fs.allocDescriptor(ofd); + + write_data(context, ofd, data, 0, data.length, ofd.position, function(err2, nbytes) { + if(err2) { + return callback(err2); + } + fs.releaseDescriptor(fd); + callback(null); + }); + }); +} + +function exists(fs, context, path, callback) { + function cb(err, stats) { + callback(err ? false : true); + } + stat(fs, context, path, cb); +} + +function getxattr(fs, context, path, name, callback) { + if (!pathCheck(path, callback)) return; + getxattr_file(context, path, name, standard_check_result_cb(callback)); +} + +function fgetxattr(fs, context, fd, name, callback) { + var ofd = fs.openFiles[fd]; + if (!ofd) { + callback(new Errors.EBADF()); + } + else { + fgetxattr_file(context, ofd, name, standard_check_result_cb(callback)); + } +} + +function setxattr(fs, context, path, name, value, flag, callback) { + if(typeof flag === 'function') { + callback = flag; + flag = null; + } + + if (!pathCheck(path, callback)) return; + setxattr_file(context, path, name, value, flag, standard_check_result_cb(callback)); +} + +function fsetxattr(fs, context, fd, name, value, flag, callback) { + if(typeof flag === 'function') { + callback = flag; + flag = null; + } + + var ofd = fs.openFiles[fd]; + if (!ofd) { + callback(new Errors.EBADF()); + } + else if (!_(ofd.flags).contains(O_WRITE)) { + callback(new Errors.EBADF('descriptor does not permit writing')); + } + else { + fsetxattr_file(context, ofd, name, value, flag, standard_check_result_cb(callback)); + } +} + +function removexattr(fs, context, path, name, callback) { + if (!pathCheck(path, callback)) return; + removexattr_file(context, path, name, standard_check_result_cb(callback)); +} + +function fremovexattr(fs, context, fd, name, callback) { + var ofd = fs.openFiles[fd]; + if (!ofd) { + callback(new Errors.EBADF()); + } + else if (!_(ofd.flags).contains(O_WRITE)) { + callback(new Errors.EBADF('descriptor does not permit writing')); + } + else { + fremovexattr_file(context, ofd, name, standard_check_result_cb(callback)); + } +} + +function lseek(fs, context, fd, offset, whence, callback) { + function update_descriptor_position(error, stats) { + if(error) { + callback(error); } else { - write_data(context, ofd, buffer, offset, length, position, standard_check_result_cb(callback)); - } - } - - function writeFile(fs, context, path, data, options, callback) { - callback = arguments[arguments.length - 1]; - options = validate_file_options(options, 'utf8', 'w'); - - if(!pathCheck(path, callback)) return; - - var flags = validate_flags(options.flag || 'w'); - if(!flags) { - callback(new Errors.EINVAL('flags is not valid')); - } - - data = data || ''; - if(typeof data === "number") { - data = '' + data; - } - if(typeof data === "string" && options.encoding === 'utf8') { - data = new TextEncoder('utf-8').encode(data); - } - - open_file(context, path, flags, function(err, fileNode) { - if(err) { - return callback(err); - } - var ofd = new OpenFileDescription(path, fileNode.id, flags, 0); - var fd = fs.allocDescriptor(ofd); - - replace_data(context, ofd, data, 0, data.length, function(err2, nbytes) { - if(err2) { - return callback(err2); - } - fs.releaseDescriptor(fd); - callback(null); - }); - }); - } - - function appendFile(fs, context, path, data, options, callback) { - callback = arguments[arguments.length - 1]; - options = validate_file_options(options, 'utf8', 'a'); - - if(!pathCheck(path, callback)) return; - - var flags = validate_flags(options.flag || 'a'); - if(!flags) { - callback(new Errors.EINVAL('flags is not valid')); - } - - data = data || ''; - if(typeof data === "number") { - data = '' + data; - } - if(typeof data === "string" && options.encoding === 'utf8') { - data = new TextEncoder('utf-8').encode(data); - } - - open_file(context, path, flags, function(err, fileNode) { - if(err) { - return callback(err); - } - var ofd = new OpenFileDescription(path, fileNode.id, flags, fileNode.size); - var fd = fs.allocDescriptor(ofd); - - write_data(context, ofd, data, 0, data.length, ofd.position, function(err2, nbytes) { - if(err2) { - return callback(err2); - } - fs.releaseDescriptor(fd); - callback(null); - }); - }); - } - - function exists(fs, context, path, callback) { - function cb(err, stats) { - callback(err ? false : true); - } - stat(fs, context, path, cb); - } - - function getxattr(fs, context, path, name, callback) { - if (!pathCheck(path, callback)) return; - getxattr_file(context, path, name, standard_check_result_cb(callback)); - } - - function fgetxattr(fs, context, fd, name, callback) { - var ofd = fs.openFiles[fd]; - if (!ofd) { - callback(new Errors.EBADF()); - } - else { - fgetxattr_file(context, ofd, name, standard_check_result_cb(callback)); - } - } - - function setxattr(fs, context, path, name, value, flag, callback) { - if(typeof flag === 'function') { - callback = flag; - flag = null; - } - - if (!pathCheck(path, callback)) return; - setxattr_file(context, path, name, value, flag, standard_check_result_cb(callback)); - } - - function fsetxattr(fs, context, fd, name, value, flag, callback) { - if(typeof flag === 'function') { - callback = flag; - flag = null; - } - - var ofd = fs.openFiles[fd]; - if (!ofd) { - callback(new Errors.EBADF()); - } - else if (!_(ofd.flags).contains(O_WRITE)) { - callback(new Errors.EBADF('descriptor does not permit writing')); - } - else { - fsetxattr_file(context, ofd, name, value, flag, standard_check_result_cb(callback)); - } - } - - function removexattr(fs, context, path, name, callback) { - if (!pathCheck(path, callback)) return; - removexattr_file(context, path, name, standard_check_result_cb(callback)); - } - - function fremovexattr(fs, context, fd, name, callback) { - var ofd = fs.openFiles[fd]; - if (!ofd) { - callback(new Errors.EBADF()); - } - else if (!_(ofd.flags).contains(O_WRITE)) { - callback(new Errors.EBADF('descriptor does not permit writing')); - } - else { - fremovexattr_file(context, ofd, name, standard_check_result_cb(callback)); - } - } - - function lseek(fs, context, fd, offset, whence, callback) { - function update_descriptor_position(error, stats) { - if(error) { - callback(error); - } else { - if(stats.size + offset < 0) { - callback(new Errors.EINVAL('resulting file offset would be negative')); - } else { - ofd.position = stats.size + offset; - callback(null, ofd.position); - } - } - } - - var ofd = fs.openFiles[fd]; - if(!ofd) { - callback(new Errors.EBADF()); - } - - if('SET' === whence) { - if(offset < 0) { + if(stats.size + offset < 0) { callback(new Errors.EINVAL('resulting file offset would be negative')); } else { - ofd.position = offset; + ofd.position = stats.size + offset; callback(null, ofd.position); } - } else if('CUR' === whence) { - if(ofd.position + offset < 0) { - callback(new Errors.EINVAL('resulting file offset would be negative')); - } else { - ofd.position += offset; - callback(null, ofd.position); - } - } else if('END' === whence) { - fstat_file(context, ofd, update_descriptor_position); + } + } + + var ofd = fs.openFiles[fd]; + if(!ofd) { + callback(new Errors.EBADF()); + } + + if('SET' === whence) { + if(offset < 0) { + callback(new Errors.EINVAL('resulting file offset would be negative')); } else { - callback(new Errors.EINVAL('whence argument is not a proper value')); + ofd.position = offset; + callback(null, ofd.position); } - } - - function readdir(fs, context, path, callback) { - if(!pathCheck(path, callback)) return; - read_directory(context, path, standard_check_result_cb(callback)); - } - - function utimes(fs, context, path, atime, mtime, callback) { - if(!pathCheck(path, callback)) return; - - var currentTime = Date.now(); - atime = (atime) ? atime : currentTime; - mtime = (mtime) ? mtime : currentTime; - - utimes_file(context, path, atime, mtime, standard_check_result_cb(callback)); - } - - function futimes(fs, context, fd, atime, mtime, callback) { - var currentTime = Date.now(); - atime = (atime) ? atime : currentTime; - mtime = (mtime) ? mtime : currentTime; - - var ofd = fs.openFiles[fd]; - if(!ofd) { - callback(new Errors.EBADF()); - } else if(!_(ofd.flags).contains(O_WRITE)) { - callback(new Errors.EBADF('descriptor does not permit writing')); + } else if('CUR' === whence) { + if(ofd.position + offset < 0) { + callback(new Errors.EINVAL('resulting file offset would be negative')); } else { - futimes_file(context, ofd, atime, mtime, standard_check_result_cb(callback)); + ofd.position += offset; + callback(null, ofd.position); } + } else if('END' === whence) { + fstat_file(context, ofd, update_descriptor_position); + } else { + callback(new Errors.EINVAL('whence argument is not a proper value')); } +} - function rename(fs, context, oldpath, newpath, callback) { - if(!pathCheck(oldpath, callback)) return; - if(!pathCheck(newpath, callback)) return; +function readdir(fs, context, path, callback) { + if(!pathCheck(path, callback)) return; + read_directory(context, path, standard_check_result_cb(callback)); +} - function unlink_old_node(error) { - if(error) { - callback(error); - } else { - unlink_node(context, oldpath, standard_check_result_cb(callback)); - } - } +function utimes(fs, context, path, atime, mtime, callback) { + if(!pathCheck(path, callback)) return; - link_node(context, oldpath, newpath, unlink_old_node); + var currentTime = Date.now(); + atime = (atime) ? atime : currentTime; + mtime = (mtime) ? mtime : currentTime; + + utimes_file(context, path, atime, mtime, standard_check_result_cb(callback)); +} + +function futimes(fs, context, fd, atime, mtime, callback) { + var currentTime = Date.now(); + atime = (atime) ? atime : currentTime; + mtime = (mtime) ? mtime : currentTime; + + var ofd = fs.openFiles[fd]; + if(!ofd) { + callback(new Errors.EBADF()); + } else if(!_(ofd.flags).contains(O_WRITE)) { + callback(new Errors.EBADF('descriptor does not permit writing')); + } else { + futimes_file(context, ofd, atime, mtime, standard_check_result_cb(callback)); } +} - function symlink(fs, context, srcpath, dstpath, type, callback) { - // NOTE: we support passing the `type` arg, but ignore it. - callback = arguments[arguments.length - 1]; - if(!pathCheck(srcpath, callback)) return; - if(!pathCheck(dstpath, callback)) return; - make_symbolic_link(context, srcpath, dstpath, standard_check_result_cb(callback)); - } +function rename(fs, context, oldpath, newpath, callback) { + if(!pathCheck(oldpath, callback)) return; + if(!pathCheck(newpath, callback)) return; - function readlink(fs, context, path, callback) { - if(!pathCheck(path, callback)) return; - read_link(context, path, standard_check_result_cb(callback)); - } - - function lstat(fs, context, path, callback) { - if(!pathCheck(path, callback)) return; - - function check_result(error, result) { - if(error) { - callback(error); - } else { - var stats = new Stats(result, fs.name); - callback(null, stats); - } - } - - lstat_file(context, path, check_result); - } - - function truncate(fs, context, path, length, callback) { - // NOTE: length is optional - callback = arguments[arguments.length - 1]; - length = length || 0; - - if(!pathCheck(path, callback)) return; - truncate_file(context, path, length, standard_check_result_cb(callback)); - } - - function ftruncate(fs, context, fd, length, callback) { - // NOTE: length is optional - callback = arguments[arguments.length - 1]; - length = length || 0; - - var ofd = fs.openFiles[fd]; - if(!ofd) { - callback(new Errors.EBADF()); - } else if(!_(ofd.flags).contains(O_WRITE)) { - callback(new Errors.EBADF('descriptor does not permit writing')); + function unlink_old_node(error) { + if(error) { + callback(error); } else { - ftruncate_file(context, ofd, length, standard_check_result_cb(callback)); + unlink_node(context, oldpath, standard_check_result_cb(callback)); } } - return { - makeRootDirectory: make_root_directory, - open: open, - close: close, - mknod: mknod, - mkdir: mkdir, - rmdir: rmdir, - unlink: unlink, - stat: stat, - fstat: fstat, - link: link, - read: read, - readFile: readFile, - write: write, - writeFile: writeFile, - appendFile: appendFile, - exists: exists, - getxattr: getxattr, - fgetxattr: fgetxattr, - setxattr: setxattr, - fsetxattr: fsetxattr, - removexattr: removexattr, - fremovexattr: fremovexattr, - lseek: lseek, - readdir: readdir, - utimes: utimes, - futimes: futimes, - rename: rename, - symlink: symlink, - readlink: readlink, - lstat: lstat, - truncate: truncate, - ftruncate: ftruncate - }; + link_node(context, oldpath, newpath, unlink_old_node); +} -}); +function symlink(fs, context, srcpath, dstpath, type, callback) { + // NOTE: we support passing the `type` arg, but ignore it. + callback = arguments[arguments.length - 1]; + if(!pathCheck(srcpath, callback)) return; + if(!pathCheck(dstpath, callback)) return; + make_symbolic_link(context, srcpath, dstpath, standard_check_result_cb(callback)); +} + +function readlink(fs, context, path, callback) { + if(!pathCheck(path, callback)) return; + read_link(context, path, standard_check_result_cb(callback)); +} + +function lstat(fs, context, path, callback) { + if(!pathCheck(path, callback)) return; + + function check_result(error, result) { + if(error) { + callback(error); + } else { + var stats = new Stats(result, fs.name); + callback(null, stats); + } + } + + lstat_file(context, path, check_result); +} + +function truncate(fs, context, path, length, callback) { + // NOTE: length is optional + callback = arguments[arguments.length - 1]; + length = length || 0; + + if(!pathCheck(path, callback)) return; + truncate_file(context, path, length, standard_check_result_cb(callback)); +} + +function ftruncate(fs, context, fd, length, callback) { + // NOTE: length is optional + callback = arguments[arguments.length - 1]; + length = length || 0; + + var ofd = fs.openFiles[fd]; + if(!ofd) { + callback(new Errors.EBADF()); + } else if(!_(ofd.flags).contains(O_WRITE)) { + callback(new Errors.EBADF('descriptor does not permit writing')); + } else { + ftruncate_file(context, ofd, length, standard_check_result_cb(callback)); + } +} + +module.exports = { + makeRootDirectory: make_root_directory, + open: open, + close: close, + mknod: mknod, + mkdir: mkdir, + rmdir: rmdir, + unlink: unlink, + stat: stat, + fstat: fstat, + link: link, + read: read, + readFile: readFile, + write: write, + writeFile: writeFile, + appendFile: appendFile, + exists: exists, + getxattr: getxattr, + fgetxattr: fgetxattr, + setxattr: setxattr, + fsetxattr: fsetxattr, + removexattr: removexattr, + fremovexattr: fremovexattr, + lseek: lseek, + readdir: readdir, + utimes: utimes, + futimes: futimes, + rename: rename, + symlink: symlink, + readlink: readlink, + lstat: lstat, + truncate: truncate, + ftruncate: ftruncate +}; diff --git a/src/filesystem/interface.js b/src/filesystem/interface.js index 9e14385..0f46e43 100644 --- a/src/filesystem/interface.js +++ b/src/filesystem/interface.js @@ -1,302 +1,302 @@ -define(function(require) { +var _ = require('../../lib/nodash.js'); - var _ = require('nodash'); +var isNullPath = require('../path.js').isNull; +var nop = require('../shared.js').nop; - var isNullPath = require('src/path').isNull; - var nop = require('src/shared').nop; +var Constants = require('../constants.js'); +var FILE_SYSTEM_NAME = Constants.FILE_SYSTEM_NAME; +var FS_FORMAT = Constants.FS_FORMAT; +var FS_READY = Constants.FS_READY; +var FS_PENDING = Constants.FS_PENDING; +var FS_ERROR = Constants.FS_ERROR; - var FILE_SYSTEM_NAME = require('src/constants').FILE_SYSTEM_NAME; - var FS_FORMAT = require('src/constants').FS_FORMAT; - var FS_READY = require('src/constants').FS_READY; - var FS_PENDING = require('src/constants').FS_PENDING; - var FS_ERROR = require('src/constants').FS_ERROR; +// TODO: fix adapters + providers for node.js... +//var providers = require('../providers/providers.js'); +var providers = {}; +//var adapters = require('../adapters/adapters.js'); +var adapters = {}; - var providers = require('src/providers/providers'); - var adapters = require('src/adapters/adapters'); +var Shell = require('../shell/shell.js'); +var Intercom = require('../../lib/intercom.js'); +var FSWatcher = require('../fs-watcher.js'); +var Errors = require('../errors.js'); - var Shell = require('src/shell/shell'); - var Intercom = require('intercom'); - var FSWatcher = require('src/fs-watcher'); - var Errors = require('src/errors'); - - var STDIN = require('src/constants').STDIN; - var STDOUT = require('src/constants').STDOUT; - var STDERR = require('src/constants').STDERR; - var FIRST_DESCRIPTOR = require('src/constants').FIRST_DESCRIPTOR; +var STDIN = Constants.STDIN; +var STDOUT = Constants.STDOUT; +var STDERR = Constants.STDERR; +var FIRST_DESCRIPTOR = Constants.FIRST_DESCRIPTOR; // The core fs operations live on impl - var impl = require('src/filesystem/implementation'); +var impl = require('./implementation.js'); - // node.js supports a calling pattern that leaves off a callback. - function maybeCallback(callback) { - if(typeof callback === "function") { - return callback; - } - return function(err) { - if(err) { - throw err; - } - }; +// node.js supports a calling pattern that leaves off a callback. +function maybeCallback(callback) { + if(typeof callback === "function") { + return callback; } + return function(err) { + if(err) { + throw err; + } + }; +} - /** - * FileSystem - * - * A FileSystem takes an `options` object, which can specify a number of, - * options. All options are optional, and include: - * - * name: the name of the file system, defaults to "local" - * - * flags: one or more flags to use when creating/opening the file system. - * For example: "FORMAT" will cause the file system to be formatted. - * No explicit flags are set by default. - * - * provider: an explicit storage provider to use for the file - * system's database context provider. A number of context - * providers are included (see /src/providers), and users - * can write one of their own and pass it in to be used. - * By default an IndexedDB provider is used. - * - * callback: a callback function to be executed when the file system becomes - * ready for use. Depending on the context provider used, this might - * be right away, or could take some time. The callback should expect - * an `error` argument, which will be null if everything worked. Also - * users should check the file system's `readyState` and `error` - * properties to make sure it is usable. - */ - function FileSystem(options, callback) { - options = options || {}; +/** + * FileSystem + * + * A FileSystem takes an `options` object, which can specify a number of, + * options. All options are optional, and include: + * + * name: the name of the file system, defaults to "local" + * + * flags: one or more flags to use when creating/opening the file system. + * For example: "FORMAT" will cause the file system to be formatted. + * No explicit flags are set by default. + * + * provider: an explicit storage provider to use for the file + * system's database context provider. A number of context + * providers are included (see /src/providers), and users + * can write one of their own and pass it in to be used. + * By default an IndexedDB provider is used. + * + * callback: a callback function to be executed when the file system becomes + * ready for use. Depending on the context provider used, this might + * be right away, or could take some time. The callback should expect + * an `error` argument, which will be null if everything worked. Also + * users should check the file system's `readyState` and `error` + * properties to make sure it is usable. + */ +function FileSystem(options, callback) { + options = options || {}; callback = callback || nop; - var flags = options.flags; - var provider = options.provider || new providers.Default(options.name || FILE_SYSTEM_NAME); - // If we're given a provider, match its name unless we get an explicit name - var name = options.name || provider.name; - var forceFormatting = _(flags).contains(FS_FORMAT); + var flags = options.flags; + var provider = options.provider || new providers.Default(options.name || FILE_SYSTEM_NAME); + // If we're given a provider, match its name unless we get an explicit name + var name = options.name || provider.name; + var forceFormatting = _(flags).contains(FS_FORMAT); - var fs = this; - fs.readyState = FS_PENDING; - fs.name = name; - fs.error = null; + var fs = this; + fs.readyState = FS_PENDING; + fs.name = name; + fs.error = null; - fs.stdin = STDIN; - fs.stdout = STDOUT; - fs.stderr = STDERR; + fs.stdin = STDIN; + fs.stdout = STDOUT; + fs.stderr = STDERR; - // Safely expose the list of open files and file - // descriptor management functions - var openFiles = {}; - var nextDescriptor = FIRST_DESCRIPTOR; - Object.defineProperty(this, "openFiles", { - get: function() { return openFiles; } - }); - this.allocDescriptor = function(openFileDescription) { - var fd = nextDescriptor ++; - openFiles[fd] = openFileDescription; - return fd; - }; - this.releaseDescriptor = function(fd) { - delete openFiles[fd]; - }; + // Safely expose the list of open files and file + // descriptor management functions + var openFiles = {}; + var nextDescriptor = FIRST_DESCRIPTOR; + Object.defineProperty(this, "openFiles", { + get: function() { return openFiles; } + }); + this.allocDescriptor = function(openFileDescription) { + var fd = nextDescriptor ++; + openFiles[fd] = openFileDescription; + return fd; + }; + this.releaseDescriptor = function(fd) { + delete openFiles[fd]; + }; - // Safely expose the operation queue - var queue = []; - this.queueOrRun = function(operation) { - var error; + // Safely expose the operation queue + var queue = []; + this.queueOrRun = function(operation) { + var error; - if(FS_READY == fs.readyState) { - operation.call(fs); - } else if(FS_ERROR == fs.readyState) { - error = new Errors.EFILESYSTEMERROR('unknown error'); - } else { - queue.push(operation); - } - - return error; - }; - function runQueued() { - queue.forEach(function(operation) { - operation.call(this); - }.bind(fs)); - queue = null; + if(FS_READY == fs.readyState) { + operation.call(fs); + } else if(FS_ERROR == fs.readyState) { + error = new Errors.EFILESYSTEMERROR('unknown error'); + } else { + queue.push(operation); } - // We support the optional `options` arg from node, but ignore it - this.watch = function(filename, options, listener) { - if(isNullPath(filename)) { - throw new Error('Path must be a string without null bytes.'); - } - if(typeof options === 'function') { - listener = options; - options = {}; - } - options = options || {}; - listener = listener || nop; + return error; + }; + function runQueued() { + queue.forEach(function(operation) { + operation.call(this); + }.bind(fs)); + queue = null; + } - var watcher = new FSWatcher(); - watcher.start(filename, false, options.recursive); - watcher.on('change', listener); - - return watcher; - }; - - // Let other instances (in this or other windows) know about - // any changes to this fs instance. - function broadcastChanges(changes) { - if(!changes.length) { - return; - } - var intercom = Intercom.getInstance(); - changes.forEach(function(change) { - intercom.emit(change.event, change.path); - }); + // We support the optional `options` arg from node, but ignore it + this.watch = function(filename, options, listener) { + if(isNullPath(filename)) { + throw new Error('Path must be a string without null bytes.'); } + if(typeof options === 'function') { + listener = options; + options = {}; + } + options = options || {}; + listener = listener || nop; - // Open file system storage provider - provider.open(function(err, needsFormatting) { - function complete(error) { + var watcher = new FSWatcher(); + watcher.start(filename, false, options.recursive); + watcher.on('change', listener); - function wrappedContext(methodName) { - var context = provider[methodName](); - context.flags = flags; - context.changes = []; + return watcher; + }; - // When the context is finished, let the fs deal with any change events - context.close = function() { - var changes = context.changes; - broadcastChanges(changes); - changes.length = 0; - }; - - return context; - } - - // Wrap the provider so we can extend the context with fs flags and - // an array of changes (e.g., watch event 'change' and 'rename' events - // for paths updated during the lifetime of the context). From this - // point forward we won't call open again, so it's safe to drop it. - fs.provider = { - openReadWriteContext: function() { - return wrappedContext('getReadWriteContext'); - }, - openReadOnlyContext: function() { - return wrappedContext('getReadOnlyContext'); - } - }; - - if(error) { - fs.readyState = FS_ERROR; - } else { - fs.readyState = FS_READY; - runQueued(); - } - callback(error, fs); - } - - if(err) { - return complete(err); - } - - // If we don't need or want formatting, we're done - if(!(forceFormatting || needsFormatting)) { - return complete(null); - } - // otherwise format the fs first - var context = provider.getReadWriteContext(); - context.clear(function(err) { - if(err) { - complete(err); - return; - } - impl.makeRootDirectory(context, complete); - }); + // Let other instances (in this or other windows) know about + // any changes to this fs instance. + function broadcastChanges(changes) { + if(!changes.length) { + return; + } + var intercom = Intercom.getInstance(); + changes.forEach(function(change) { + intercom.emit(change.event, change.path); }); } - // Expose storage providers on FileSystem constructor - FileSystem.providers = providers; + // Open file system storage provider + provider.open(function(err, needsFormatting) { + function complete(error) { - // Expose adatpers on FileSystem constructor - FileSystem.adapters = adapters; + function wrappedContext(methodName) { + var context = provider[methodName](); + context.flags = flags; + context.changes = []; - /** - * Public API for FileSystem - */ - [ - 'open', - 'close', - 'mknod', - 'mkdir', - 'rmdir', - 'stat', - 'fstat', - 'link', - 'unlink', - 'read', - 'readFile', - 'write', - 'writeFile', - 'appendFile', - 'exists', - 'lseek', - 'readdir', - 'rename', - 'readlink', - 'symlink', - 'lstat', - 'truncate', - 'ftruncate', - 'utimes', - 'futimes', - 'setxattr', - 'getxattr', - 'fsetxattr', - 'fgetxattr', - 'removexattr', - 'fremovexattr' - ].forEach(function(methodName) { - FileSystem.prototype[methodName] = function() { - var fs = this; - var args = Array.prototype.slice.call(arguments, 0); - var lastArgIndex = args.length - 1; + // When the context is finished, let the fs deal with any change events + context.close = function() { + var changes = context.changes; + broadcastChanges(changes); + changes.length = 0; + }; - // We may or may not get a callback, and since node.js supports - // fire-and-forget style fs operations, we have to dance a bit here. - var missingCallback = typeof args[lastArgIndex] !== 'function'; - var callback = maybeCallback(args[lastArgIndex]); - - var error = fs.queueOrRun(function() { - var context = fs.provider.openReadWriteContext(); - - // Wrap the callback so we can explicitly close the context - function complete() { - context.close(); - callback.apply(fs, arguments); - } - - // Either add or replace the callback with our wrapper complete() - if(missingCallback) { - args.push(complete); - } else { - args[lastArgIndex] = complete; - } - - // Forward this call to the impl's version, using the following - // call signature, with complete() as the callback/last-arg now: - // fn(fs, context, arg0, arg1, ... , complete); - var fnArgs = [fs, context].concat(args); - impl[methodName].apply(null, fnArgs); - }); - if(error) { - callback(error); + return context; } - }; + + // Wrap the provider so we can extend the context with fs flags and + // an array of changes (e.g., watch event 'change' and 'rename' events + // for paths updated during the lifetime of the context). From this + // point forward we won't call open again, so it's safe to drop it. + fs.provider = { + openReadWriteContext: function() { + return wrappedContext('getReadWriteContext'); + }, + openReadOnlyContext: function() { + return wrappedContext('getReadOnlyContext'); + } + }; + + if(error) { + fs.readyState = FS_ERROR; + } else { + fs.readyState = FS_READY; + runQueued(); + } + callback(error, fs); + } + + if(err) { + return complete(err); + } + + // If we don't need or want formatting, we're done + if(!(forceFormatting || needsFormatting)) { + return complete(null); + } + // otherwise format the fs first + var context = provider.getReadWriteContext(); + context.clear(function(err) { + if(err) { + complete(err); + return; + } + impl.makeRootDirectory(context, complete); + }); }); +} - FileSystem.prototype.Shell = function(options) { - return new Shell(this, options); +// Expose storage providers on FileSystem constructor +FileSystem.providers = providers; + +// Expose adatpers on FileSystem constructor +FileSystem.adapters = adapters; + +/** + * Public API for FileSystem + */ +[ + 'open', + 'close', + 'mknod', + 'mkdir', + 'rmdir', + 'stat', + 'fstat', + 'link', + 'unlink', + 'read', + 'readFile', + 'write', + 'writeFile', + 'appendFile', + 'exists', + 'lseek', + 'readdir', + 'rename', + 'readlink', + 'symlink', + 'lstat', + 'truncate', + 'ftruncate', + 'utimes', + 'futimes', + 'setxattr', + 'getxattr', + 'fsetxattr', + 'fgetxattr', + 'removexattr', + 'fremovexattr' +].forEach(function(methodName) { + FileSystem.prototype[methodName] = function() { + var fs = this; + var args = Array.prototype.slice.call(arguments, 0); + var lastArgIndex = args.length - 1; + + // We may or may not get a callback, and since node.js supports + // fire-and-forget style fs operations, we have to dance a bit here. + var missingCallback = typeof args[lastArgIndex] !== 'function'; + var callback = maybeCallback(args[lastArgIndex]); + + var error = fs.queueOrRun(function() { + var context = fs.provider.openReadWriteContext(); + + // Wrap the callback so we can explicitly close the context + function complete() { + context.close(); + callback.apply(fs, arguments); + } + + // Either add or replace the callback with our wrapper complete() + if(missingCallback) { + args.push(complete); + } else { + args[lastArgIndex] = complete; + } + + // Forward this call to the impl's version, using the following + // call signature, with complete() as the callback/last-arg now: + // fn(fs, context, arg0, arg1, ... , complete); + var fnArgs = [fs, context].concat(args); + impl[methodName].apply(null, fnArgs); + }); + if(error) { + callback(error); + } }; - - return FileSystem; - }); + +FileSystem.prototype.Shell = function(options) { + return new Shell(this, options); +}; + +module.exports = FileSystem; diff --git a/src/fs-watcher.js b/src/fs-watcher.js index 26238ce..fcc47e2 100644 --- a/src/fs-watcher.js +++ b/src/fs-watcher.js @@ -1,54 +1,51 @@ -define(function(require) { +var EventEmitter = require('../lib/eventemitter.js'); +var isNullPath = require('./path.js').isNull; +var Intercom = require('../lib/intercom.js'); - var EventEmitter = require('eventemitter'); - var isNullPath = require('src/path').isNull; - var Intercom = require('intercom'); +/** + * FSWatcher based on node.js' FSWatcher + * see https://github.com/joyent/node/blob/master/lib/fs.js + */ +function FSWatcher() { + EventEmitter.call(this); + var self = this; + var recursive = false; + var filename; - /** - * FSWatcher based on node.js' FSWatcher - * see https://github.com/joyent/node/blob/master/lib/fs.js - */ - function FSWatcher() { - EventEmitter.call(this); - var self = this; - var recursive = false; - var filename; + function onchange(path) { + // Watch for exact filename, or parent path when recursive is true + if(filename === path || (recursive && path.indexOf(filename + '/') === 0)) { + self.trigger('change', 'change', path); + } + } - function onchange(path) { - // Watch for exact filename, or parent path when recursive is true - if(filename === path || (recursive && path.indexOf(filename + '/') === 0)) { - self.trigger('change', 'change', path); - } + // We support, but ignore the second arg, which node.js uses. + self.start = function(filename_, persistent_, recursive_) { + // Bail if we've already started (and therefore have a filename); + if(filename) { + return; } - // We support, but ignore the second arg, which node.js uses. - self.start = function(filename_, persistent_, recursive_) { - // Bail if we've already started (and therefore have a filename); - if(filename) { - return; - } + if(isNullPath(filename_)) { + throw new Error('Path must be a string without null bytes.'); + } + // TODO: get realpath for symlinks on filename... + filename = filename_; - if(isNullPath(filename_)) { - throw new Error('Path must be a string without null bytes.'); - } - // TODO: get realpath for symlinks on filename... - filename = filename_; + // Whether to watch beneath this path or not + recursive = recursive_ === true; - // Whether to watch beneath this path or not - recursive = recursive_ === true; + var intercom = Intercom.getInstance(); + intercom.on('change', onchange); + }; - var intercom = Intercom.getInstance(); - intercom.on('change', onchange); - }; + self.close = function() { + var intercom = Intercom.getInstance(); + intercom.off('change', onchange); + self.removeAllListeners('change'); + }; +} +FSWatcher.prototype = new EventEmitter(); +FSWatcher.prototype.constructor = FSWatcher; - self.close = function() { - var intercom = Intercom.getInstance(); - intercom.off('change', onchange); - self.removeAllListeners('change'); - }; - } - FSWatcher.prototype = new EventEmitter(); - FSWatcher.prototype.constructor = FSWatcher; - - return FSWatcher; -}); +module.exports = FSWatcher; diff --git a/src/index.js b/src/index.js index 91364ae..584008a 100644 --- a/src/index.js +++ b/src/index.js @@ -1,7 +1,5 @@ -define(function(require) { - return { - FileSystem: require('src/filesystem/interface'), - Path: require('src/path'), - Errors: require('src/errors') - }; -}); +module.exports = { + FileSystem: require('./filesystem/interface.js'), + Path: require('./path.js'), + Errors: require('./errors.js') +}; diff --git a/src/network.js b/src/network.js index 2a8d46a..d6b097b 100644 --- a/src/network.js +++ b/src/network.js @@ -1,82 +1,55 @@ -define(function(require) { - // Pull in node's request module if possible/needed - if (typeof XMLHttpRequest === 'undefined') { - // This is a stupid workaround for the fact that - // the r.js optimizer checks every require() call - // during optimization and throws an error if it - // can't find the module. - // - // This is only an issue with our browser build - // using `almond` (https://github.com/jrburke/almond) - // which doesn't fallback to node's require when we - // need it to. - var node_req = require; - var request = node_req('request'); - } +function browserDownload(uri, callback) { + var query = new XMLHttpRequest(); + query.onload = function() { + var err = query.status != 200 ? { message: query.statusText, code: query.status } : null, + data = err ? null : new Uint8Array(query.response); - function browserDownload(uri, callback) { - var query = new XMLHttpRequest(); - query.onload = function() { - var err = query.status != 200 ? { message: query.statusText, code: query.status } : null, - data = err ? null : new Uint8Array(query.response); - - callback(err, data); - }; - query.open("GET", uri); - if("withCredentials" in query) { - query.withCredentials = true; - } - - query.responseType = "arraybuffer"; - query.send(); - } - - function nodeDownload(uri, callback) { - request({ - url: uri, - method: "GET", - encoding: null - }, function(err, msg, body) { - var data = null, - arrayBuffer, - statusCode, - arrayLength = body && body.length, - error; - - msg = msg || null; - statusCode = msg && msg.statusCode; - - error = statusCode != 200 ? { message: err || 'Not found!', code: statusCode } : null; - - arrayBuffer = arrayLength && new ArrayBuffer(arrayLength); - - // Convert buffer to Uint8Array - if (arrayBuffer && (statusCode == 200)) { - data = new Uint8Array(arrayBuffer); - for (var i = 0; i < body.length; ++i) { - data[i] = body[i]; - } - } - - callback(error, data); - }); - } - - return { - download: function(uri, callback) { - if (!uri) { - throw('Uri required!'); - } - - if (!callback) { - throw('Callback required'); - } - - if (typeof XMLHttpRequest === "undefined") { - nodeDownload(uri, callback); - } else { - browserDownload(uri, callback); - } - } + callback(err, data); }; -}); + query.open("GET", uri); + if("withCredentials" in query) { + query.withCredentials = true; + } + + query.responseType = "arraybuffer"; + query.send(); +} + +function nodeDownload(uri, callback) { + require('request')({ + url: uri, + method: "GET", + encoding: null + }, function(err, msg, body) { + var data = null, + arrayBuffer, + statusCode, + arrayLength = body && body.length, + error; + + msg = msg || null; + statusCode = msg && msg.statusCode; + + error = statusCode != 200 ? { message: err || 'Not found!', code: statusCode } : null; + + arrayBuffer = arrayLength && new ArrayBuffer(arrayLength); + + // Convert buffer to Uint8Array + if (arrayBuffer && (statusCode == 200)) { + data = new Uint8Array(arrayBuffer); + for (var i = 0; i < body.length; ++i) { + data[i] = body[i]; + } + } + + callback(error, data); + }); +} + +module.exports = function(uri, callback) { + if (typeof XMLHttpRequest === 'undefined') { + nodeDownload(uri, callback); + } else { + browserDownload(uri, callback); + } +}; diff --git a/src/node.js b/src/node.js index d1f25a6..86bdf32 100644 --- a/src/node.js +++ b/src/node.js @@ -1,21 +1,20 @@ -define(['src/constants', 'src/shared'], function(Constants, Shared) { +var MODE_FILE = require('./constants.js').MODE_FILE; +var guid = require('./shared.js').guid; - return function Node(id, mode, size, atime, ctime, mtime, flags, xattrs, nlinks, version) { - var now = Date.now(); +module.exports = function Node(id, mode, size, atime, ctime, mtime, flags, xattrs, nlinks, version) { + var now = Date.now(); - this.id = id || Shared.guid(); - this.mode = mode || Constants.MODE_FILE; // node type (file, directory, etc) - this.size = size || 0; // size (bytes for files, entries for directories) - this.atime = atime || now; // access time (will mirror ctime after creation) - this.ctime = ctime || now; // creation/change time - this.mtime = mtime || now; // modified time - this.flags = flags || []; // file flags - this.xattrs = xattrs || {}; // extended attributes - this.nlinks = nlinks || 0; // links count - this.version = version || 0; // node version - this.blksize = undefined; // block size - this.nblocks = 1; // blocks count - this.data = Shared.guid(); // id for data object - }; - -}); + this.id = id || guid(); + this.mode = mode || MODE_FILE; // node type (file, directory, etc) + this.size = size || 0; // size (bytes for files, entries for directories) + this.atime = atime || now; // access time (will mirror ctime after creation) + this.ctime = ctime || now; // creation/change time + this.mtime = mtime || now; // modified time + this.flags = flags || []; // file flags + this.xattrs = xattrs || {}; // extended attributes + this.nlinks = nlinks || 0; // links count + this.version = version || 0; // node version + this.blksize = undefined; // block size + this.nblocks = 1; // blocks count + this.data = guid(); // id for data object +}; diff --git a/src/open-file-description.js b/src/open-file-description.js index 61e7bcd..e401822 100644 --- a/src/open-file-description.js +++ b/src/open-file-description.js @@ -1,10 +1,6 @@ -define(function(require) { - - return function OpenFileDescription(path, id, flags, position) { - this.path = path; - this.id = id; - this.flags = flags; - this.position = position; - }; - -}); +module.exports = function OpenFileDescription(path, id, flags, position) { + this.path = path; + this.id = id; + this.flags = flags; + this.position = position; +}; diff --git a/src/path.js b/src/path.js index ab99cce..a75659a 100644 --- a/src/path.js +++ b/src/path.js @@ -20,208 +20,205 @@ // USE OR OTHER DEALINGS IN THE SOFTWARE. // Based on https://github.com/joyent/node/blob/41e53e557992a7d552a8e23de035f9463da25c99/lib/path.js -define(function() { - // resolves . and .. elements in a path array with directory names there - // must be no slashes, empty elements, or device names (c:\) in the array - // (so also no leading and trailing slashes - it does not distinguish - // relative and absolute paths) - function normalizeArray(parts, allowAboveRoot) { - // if the path tries to go above the root, `up` ends up > 0 - var up = 0; - for (var i = parts.length - 1; i >= 0; i--) { - var last = parts[i]; - if (last === '.') { - parts.splice(i, 1); - } else if (last === '..') { - parts.splice(i, 1); - up++; - } else if (up) { - parts.splice(i, 1); - up--; - } +// resolves . and .. elements in a path array with directory names there +// must be no slashes, empty elements, or device names (c:\) in the array +// (so also no leading and trailing slashes - it does not distinguish +// relative and absolute paths) +function normalizeArray(parts, allowAboveRoot) { + // if the path tries to go above the root, `up` ends up > 0 + var up = 0; + for (var i = parts.length - 1; i >= 0; i--) { + var last = parts[i]; + if (last === '.') { + parts.splice(i, 1); + } else if (last === '..') { + parts.splice(i, 1); + up++; + } else if (up) { + parts.splice(i, 1); + up--; } - - // if the path is allowed to go above the root, restore leading ..s - if (allowAboveRoot) { - for (; up--; up) { - parts.unshift('..'); - } - } - - return parts; } - // Split a filename into [root, dir, basename, ext], unix version - // 'root' is just a slash, or nothing. - var splitPathRe = - /^(\/?)([\s\S]+\/(?!$)|\/)?((?:\.{1,2}$|[\s\S]+?)?(\.[^.\/]*)?)$/; - var splitPath = function(filename) { - var result = splitPathRe.exec(filename); - return [result[1] || '', result[2] || '', result[3] || '', result[4] || '']; - }; - - // path.resolve([from ...], to) - function resolve() { - var resolvedPath = '', - resolvedAbsolute = false; - - for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) { - // XXXidbfs: we don't have process.cwd() so we use '/' as a fallback - var path = (i >= 0) ? arguments[i] : '/'; - - // Skip empty and invalid entries - if (typeof path !== 'string' || !path) { - continue; - } - - resolvedPath = path + '/' + resolvedPath; - resolvedAbsolute = path.charAt(0) === '/'; + // if the path is allowed to go above the root, restore leading ..s + if (allowAboveRoot) { + for (; up--; up) { + parts.unshift('..'); } - - // At this point the path should be resolved to a full absolute path, but - // handle relative paths to be safe (might happen when process.cwd() fails) - - // Normalize the path - resolvedPath = normalizeArray(resolvedPath.split('/').filter(function(p) { - return !!p; - }), !resolvedAbsolute).join('/'); - - return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.'; } - // path.normalize(path) - function normalize(path) { - var isAbsolute = path.charAt(0) === '/', - trailingSlash = path.substr(-1) === '/'; + return parts; +} - // Normalize the path - path = normalizeArray(path.split('/').filter(function(p) { - return !!p; - }), !isAbsolute).join('/'); +// Split a filename into [root, dir, basename, ext], unix version +// 'root' is just a slash, or nothing. +var splitPathRe = + /^(\/?)([\s\S]+\/(?!$)|\/)?((?:\.{1,2}$|[\s\S]+?)?(\.[^.\/]*)?)$/; +var splitPath = function(filename) { + var result = splitPathRe.exec(filename); + return [result[1] || '', result[2] || '', result[3] || '', result[4] || '']; +}; - if (!path && !isAbsolute) { - path = '.'; +// path.resolve([from ...], to) +function resolve() { + var resolvedPath = '', + resolvedAbsolute = false; + + for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) { + // XXXidbfs: we don't have process.cwd() so we use '/' as a fallback + var path = (i >= 0) ? arguments[i] : '/'; + + // Skip empty and invalid entries + if (typeof path !== 'string' || !path) { + continue; } - /* - if (path && trailingSlash) { - path += '/'; - } - */ - return (isAbsolute ? '/' : '') + path; + resolvedPath = path + '/' + resolvedPath; + resolvedAbsolute = path.charAt(0) === '/'; } - function join() { - var paths = Array.prototype.slice.call(arguments, 0); - return normalize(paths.filter(function(p, index) { - return p && typeof p === 'string'; - }).join('/')); + // At this point the path should be resolved to a full absolute path, but + // handle relative paths to be safe (might happen when process.cwd() fails) + + // Normalize the path + resolvedPath = normalizeArray(resolvedPath.split('/').filter(function(p) { + return !!p; + }), !resolvedAbsolute).join('/'); + + return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.'; +} + +// path.normalize(path) +function normalize(path) { + var isAbsolute = path.charAt(0) === '/', + trailingSlash = path.substr(-1) === '/'; + + // Normalize the path + path = normalizeArray(path.split('/').filter(function(p) { + return !!p; + }), !isAbsolute).join('/'); + + if (!path && !isAbsolute) { + path = '.'; + } + /* + if (path && trailingSlash) { + path += '/'; + } + */ + + return (isAbsolute ? '/' : '') + path; +} + +function join() { + var paths = Array.prototype.slice.call(arguments, 0); + return normalize(paths.filter(function(p, index) { + return p && typeof p === 'string'; + }).join('/')); +} + +// path.relative(from, to) +function relative(from, to) { + from = exports.resolve(from).substr(1); + to = exports.resolve(to).substr(1); + + function trim(arr) { + var start = 0; + for (; start < arr.length; start++) { + if (arr[start] !== '') break; + } + + var end = arr.length - 1; + for (; end >= 0; end--) { + if (arr[end] !== '') break; + } + + if (start > end) return []; + return arr.slice(start, end - start + 1); } - // path.relative(from, to) - function relative(from, to) { - from = exports.resolve(from).substr(1); - to = exports.resolve(to).substr(1); + var fromParts = trim(from.split('/')); + var toParts = trim(to.split('/')); - function trim(arr) { - var start = 0; - for (; start < arr.length; start++) { - if (arr[start] !== '') break; - } - - var end = arr.length - 1; - for (; end >= 0; end--) { - if (arr[end] !== '') break; - } - - if (start > end) return []; - return arr.slice(start, end - start + 1); + var length = Math.min(fromParts.length, toParts.length); + var samePartsLength = length; + for (var i = 0; i < length; i++) { + if (fromParts[i] !== toParts[i]) { + samePartsLength = i; + break; } - - var fromParts = trim(from.split('/')); - var toParts = trim(to.split('/')); - - var length = Math.min(fromParts.length, toParts.length); - var samePartsLength = length; - for (var i = 0; i < length; i++) { - if (fromParts[i] !== toParts[i]) { - samePartsLength = i; - break; - } - } - - var outputParts = []; - for (var i = samePartsLength; i < fromParts.length; i++) { - outputParts.push('..'); - } - - outputParts = outputParts.concat(toParts.slice(samePartsLength)); - - return outputParts.join('/'); } - function dirname(path) { - var result = splitPath(path), - root = result[0], - dir = result[1]; - - if (!root && !dir) { - // No dirname whatsoever - return '.'; - } - - if (dir) { - // It has a dirname, strip trailing slash - dir = dir.substr(0, dir.length - 1); - } - - return root + dir; + var outputParts = []; + for (var i = samePartsLength; i < fromParts.length; i++) { + outputParts.push('..'); } - function basename(path, ext) { - var f = splitPath(path)[2]; - // TODO: make this comparison case-insensitive on windows? - if (ext && f.substr(-1 * ext.length) === ext) { - f = f.substr(0, f.length - ext.length); - } - // XXXidbfs: node.js just does `return f` - return f === "" ? "/" : f; + outputParts = outputParts.concat(toParts.slice(samePartsLength)); + + return outputParts.join('/'); +} + +function dirname(path) { + var result = splitPath(path), + root = result[0], + dir = result[1]; + + if (!root && !dir) { + // No dirname whatsoever + return '.'; } - function extname(path) { - return splitPath(path)[3]; + if (dir) { + // It has a dirname, strip trailing slash + dir = dir.substr(0, dir.length - 1); } - function isAbsolute(path) { - if(path.charAt(0) === '/') { - return true; - } - return false; + return root + dir; +} + +function basename(path, ext) { + var f = splitPath(path)[2]; + // TODO: make this comparison case-insensitive on windows? + if (ext && f.substr(-1 * ext.length) === ext) { + f = f.substr(0, f.length - ext.length); } + // XXXidbfs: node.js just does `return f` + return f === "" ? "/" : f; +} - function isNull(path) { - if (('' + path).indexOf('\u0000') !== -1) { - return true; - } - return false; +function extname(path) { + return splitPath(path)[3]; +} + +function isAbsolute(path) { + if(path.charAt(0) === '/') { + return true; } + return false; +} - // XXXidbfs: we don't support path.exists() or path.existsSync(), which - // are deprecated, and need a FileSystem instance to work. Use fs.stat(). +function isNull(path) { + if (('' + path).indexOf('\u0000') !== -1) { + return true; + } + return false; +} - return { - normalize: normalize, - resolve: resolve, - join: join, - relative: relative, - sep: '/', - delimiter: ':', - dirname: dirname, - basename: basename, - extname: extname, - isAbsolute: isAbsolute, - isNull: isNull - }; +// XXXidbfs: we don't support path.exists() or path.existsSync(), which +// are deprecated, and need a FileSystem instance to work. Use fs.stat(). -}); +module.exports = { + normalize: normalize, + resolve: resolve, + join: join, + relative: relative, + sep: '/', + delimiter: ':', + dirname: dirname, + basename: basename, + extname: extname, + isAbsolute: isAbsolute, + isNull: isNull +}; diff --git a/src/shared.js b/src/shared.js index 16d9439..8dd0229 100644 --- a/src/shared.js +++ b/src/shared.js @@ -1,37 +1,26 @@ -define(function(require) { +function guid() { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { + var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8); + return v.toString(16); + }).toUpperCase(); +} - require("crypto-js/rollups/sha256"); var Crypto = CryptoJS; +function nop() {} - function guid() { - return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { - var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8); - return v.toString(16); - }).toUpperCase(); +/** + * Convert a Uint8Array to a regular array + */ +function u8toArray(u8) { + var array = []; + var len = u8.length; + for(var i = 0; i < len; i++) { + array[i] = u8[i]; } + return array; +} - function hash(string) { - return Crypto.SHA256(string).toString(Crypto.enc.hex); - } - - function nop() {} - - /** - * Convert a Uint8Array to a regular array - */ - function u8toArray(u8) { - var array = []; - var len = u8.length; - for(var i = 0; i < len; i++) { - array[i] = u8[i]; - } - return array; - } - - return { - guid: guid, - hash: hash, - u8toArray: u8toArray, - nop: nop - }; - -}); +module.exports = { + guid: guid, + u8toArray: u8toArray, + nop: nop +}; diff --git a/src/shell/environment.js b/src/shell/environment.js index 976e9e2..a86a625 100644 --- a/src/shell/environment.js +++ b/src/shell/environment.js @@ -1,20 +1,15 @@ -define(function(require) { +var defaults = require('../constants.js').ENVIRONMENT; - var defaults = require('src/constants').ENVIRONMENT; +module.exports = function Environment(env) { + env = env || {}; + env.TMP = env.TMP || defaults.TMP; + env.PATH = env.PATH || defaults.PATH; - function Environment(env) { - env = env || {}; - env.TMP = env.TMP || defaults.TMP; - env.PATH = env.PATH || defaults.PATH; + this.get = function(name) { + return env[name]; + }; - this.get = function(name) { - return env[name]; - }; - - this.set = function(name, value) { - env[name] = value; - }; - } - - return Environment; -}); + this.set = function(name, value) { + env[name] = value; + }; +}; diff --git a/src/shell/shell.js b/src/shell/shell.js index 545ec95..61dd3cc 100644 --- a/src/shell/shell.js +++ b/src/shell/shell.js @@ -1,400 +1,409 @@ -/* jshint evil:true */ -define(function(require) { +var Path = require('../path.js'); +var Errors = require('../errors.js'); +var Environment = require('./environment.js'); +var async = require('../../lib/async.js'); +var Network = require('../network.js'); +var Zlib = require('../../lib/zip-utils.js'); +var TextEncoder = require('../../lib/encoding.js').TextEncoder; - var Path = require('src/path'); - var Errors = require('src/errors'); - var Environment = require('src/shell/environment'); - var async = require('async'); - var Network = require('src/network'); +function Shell(fs, options) { + options = options || {}; - require('zip'); - require('unzip'); - - function Shell(fs, options) { - options = options || {}; - - var env = new Environment(options.env); - var cwd = '/'; - - /** - * The bound FileSystem (cannot be changed) - */ - Object.defineProperty(this, 'fs', { - get: function() { return fs; }, - enumerable: true - }); - - /** - * The shell's environment (e.g., for things like - * path, tmp, and other env vars). Use env.get() - * and env.set() to work with variables. - */ - Object.defineProperty(this, 'env', { - get: function() { return env; }, - enumerable: true - }); - - /** - * Change the current working directory. We - * include `cd` on the `this` vs. proto so that - * we can access cwd without exposing it externally. - */ - this.cd = function(path, callback) { - path = Path.resolve(this.cwd, path); - // Make sure the path actually exists, and is a dir - fs.stat(path, function(err, stats) { - if(err) { - callback(new Errors.ENOTDIR()); - return; - } - if(stats.type === 'DIRECTORY') { - cwd = path; - callback(); - } else { - callback(new Errors.ENOTDIR()); - } - }); - }; - - /** - * Get the current working directory (changed with `cd()`) - */ - this.pwd = function() { - return cwd; - }; - } + var env = new Environment(options.env); + var cwd = '/'; /** - * Execute the .js command located at `path`. Such commands - * should assume the existence of 3 arguments, which will be - * defined at runtime: - * - * * fs - the current shell's bound filesystem object - * * args - a list of arguments for the command, or an empty list if none - * * callback - a callback function(error, result) to call when done. - * - * The .js command's contents should be the body of a function - * that looks like this: - * - * function(fs, args, callback) { - * // .js code here - * } + * The bound FileSystem (cannot be changed) */ - Shell.prototype.exec = function(path, args, callback) { - var fs = this.fs; - if(typeof args === 'function') { - callback = args; - args = []; - } - args = args || []; - callback = callback || function(){}; - path = Path.resolve(this.cwd, path); + Object.defineProperty(this, 'fs', { + get: function() { return fs; }, + enumerable: true + }); - fs.readFile(path, "utf8", function(error, data) { + /** + * The shell's environment (e.g., for things like + * path, tmp, and other env vars). Use env.get() + * and env.set() to work with variables. + */ + Object.defineProperty(this, 'env', { + get: function() { return env; }, + enumerable: true + }); + + /** + * Change the current working directory. We + * include `cd` on the `this` vs. proto so that + * we can access cwd without exposing it externally. + */ + this.cd = function(path, callback) { + path = Path.resolve(this.cwd, path); + // Make sure the path actually exists, and is a dir + fs.stat(path, function(err, stats) { + if(err) { + callback(new Errors.ENOTDIR()); + return; + } + if(stats.type === 'DIRECTORY') { + cwd = path; + callback(); + } else { + callback(new Errors.ENOTDIR()); + } + }); + }; + + /** + * Get the current working directory (changed with `cd()`) + */ + this.pwd = function() { + return cwd; + }; +} + +/** + * Execute the .js command located at `path`. Such commands + * should assume the existence of 3 arguments, which will be + * defined at runtime: + * + * * fs - the current shell's bound filesystem object + * * args - a list of arguments for the command, or an empty list if none + * * callback - a callback function(error, result) to call when done. + * + * The .js command's contents should be the body of a function + * that looks like this: + * + * function(fs, args, callback) { + * // .js code here + * } + */ +Shell.prototype.exec = function(path, args, callback) { + /* jshint evil:true */ + var fs = this.fs; + if(typeof args === 'function') { + callback = args; + args = []; + } + args = args || []; + callback = callback || function(){}; + path = Path.resolve(this.cwd, path); + + fs.readFile(path, "utf8", function(error, data) { + if(error) { + callback(error); + return; + } + try { + var cmd = new Function('fs', 'args', 'callback', data); + cmd(fs, args, callback); + } catch(e) { + callback(e); + } + }); +}; + +/** + * Create a file if it does not exist, or update access and + * modified times if it does. Valid options include: + * + * * updateOnly - whether to create the file if missing (defaults to false) + * * date - use the provided Date value instead of current date/time + */ +Shell.prototype.touch = function(path, options, callback) { + var fs = this.fs; + if(typeof options === 'function') { + callback = options; + options = {}; + } + options = options || {}; + callback = callback || function(){}; + path = Path.resolve(this.cwd, path); + + function createFile(path) { + fs.writeFile(path, '', callback); + } + + function updateTimes(path) { + var now = Date.now(); + var atime = options.date || now; + var mtime = options.date || now; + + fs.utimes(path, atime, mtime, callback); + } + + fs.stat(path, function(error, stats) { + if(error) { + if(options.updateOnly === true) { + callback(); + } else { + createFile(path); + } + } else { + updateTimes(path); + } + }); +}; + +/** + * Concatenate multiple files into a single String, with each + * file separated by a newline. The `files` argument should + * be a String (path to single file) or an Array of Strings + * (multiple file paths). + */ +Shell.prototype.cat = function(files, callback) { + var fs = this.fs; + var all = ''; + callback = callback || function(){}; + + if(!files) { + callback(new Errors.EINVAL("Missing files argument")); + return; + } + + files = typeof files === 'string' ? [ files ] : files; + + function append(item, callback) { + var filename = Path.resolve(this.cwd, item); + fs.readFile(filename, 'utf8', function(error, data) { if(error) { callback(error); return; } - try { - var cmd = new Function('fs', 'args', 'callback', data); - cmd(fs, args, callback); - } catch(e) { - callback(e); - } + all += data + '\n'; + callback(); }); - }; + } - /** - * Create a file if it does not exist, or update access and - * modified times if it does. Valid options include: - * - * * updateOnly - whether to create the file if missing (defaults to false) - * * date - use the provided Date value instead of current date/time - */ - Shell.prototype.touch = function(path, options, callback) { - var fs = this.fs; - if(typeof options === 'function') { - callback = options; - options = {}; + async.eachSeries(files, append, function(error) { + if(error) { + callback(error); + } else { + callback(null, all.replace(/\n$/, '')); } - options = options || {}; - callback = callback || function(){}; - path = Path.resolve(this.cwd, path); + }); +}; - function createFile(path) { - fs.writeFile(path, '', callback); - } +/** + * Get the listing of a directory, returning an array of + * file entries in the following form: + * + * { + * path: the basename of the directory entry + * links: the number of links to the entry + * size: the size in bytes of the entry + * modified: the last modified date/time + * type: the type of the entry + * contents: an optional array of child entries + * } + * + * By default ls() gives a shallow listing. If you want + * to follow directories as they are encountered, use + * the `recursive=true` option. + */ +Shell.prototype.ls = function(dir, options, callback) { + var fs = this.fs; + if(typeof options === 'function') { + callback = options; + options = {}; + } + options = options || {}; + callback = callback || function(){}; - function updateTimes(path) { - var now = Date.now(); - var atime = options.date || now; - var mtime = options.date || now; + if(!dir) { + callback(new Errors.EINVAL("Missing dir argument")); + return; + } - fs.utimes(path, atime, mtime, callback); - } + function list(path, callback) { + var pathname = Path.resolve(this.cwd, path); + var result = []; - fs.stat(path, function(error, stats) { - if(error) { - if(options.updateOnly === true) { - callback(); - } else { - createFile(path); - } - } else { - updateTimes(path); - } - }); - }; - - /** - * Concatenate multiple files into a single String, with each - * file separated by a newline. The `files` argument should - * be a String (path to single file) or an Array of Strings - * (multiple file paths). - */ - Shell.prototype.cat = function(files, callback) { - var fs = this.fs; - var all = ''; - callback = callback || function(){}; - - if(!files) { - callback(new Errors.EINVAL("Missing files argument")); - return; - } - - files = typeof files === 'string' ? [ files ] : files; - - function append(item, callback) { - var filename = Path.resolve(this.cwd, item); - fs.readFile(filename, 'utf8', function(error, data) { - if(error) { - callback(error); - return; - } - all += data + '\n'; - callback(); - }); - } - - async.eachSeries(files, append, function(error) { + fs.readdir(pathname, function(error, entries) { if(error) { callback(error); - } else { - callback(null, all.replace(/\n$/, '')); + return; } + + function getDirEntry(name, callback) { + name = Path.join(pathname, name); + fs.stat(name, function(error, stats) { + if(error) { + callback(error); + return; + } + var entry = { + path: Path.basename(name), + links: stats.nlinks, + size: stats.size, + modified: stats.mtime, + type: stats.type + }; + + if(options.recursive && stats.type === 'DIRECTORY') { + list(Path.join(pathname, entry.path), function(error, items) { + if(error) { + callback(error); + return; + } + entry.contents = items; + result.push(entry); + callback(); + }); + } else { + result.push(entry); + callback(); + } + }); + } + + async.each(entries, getDirEntry, function(error) { + callback(error, result); + }); }); - }; + } - /** - * Get the listing of a directory, returning an array of - * file entries in the following form: - * - * { - * path: the basename of the directory entry - * links: the number of links to the entry - * size: the size in bytes of the entry - * modified: the last modified date/time - * type: the type of the entry - * contents: an optional array of child entries - * } - * - * By default ls() gives a shallow listing. If you want - * to follow directories as they are encountered, use - * the `recursive=true` option. - */ - Shell.prototype.ls = function(dir, options, callback) { - var fs = this.fs; - if(typeof options === 'function') { - callback = options; - options = {}; - } - options = options || {}; - callback = callback || function(){}; + list(dir, callback); +}; - if(!dir) { - callback(new Errors.EINVAL("Missing dir argument")); - return; - } +/** + * Removes the file or directory at `path`. If `path` is a file + * it will be removed. If `path` is a directory, it will be + * removed if it is empty, otherwise the callback will receive + * an error. In order to remove non-empty directories, use the + * `recursive=true` option. + */ +Shell.prototype.rm = function(path, options, callback) { + var fs = this.fs; + if(typeof options === 'function') { + callback = options; + options = {}; + } + options = options || {}; + callback = callback || function(){}; - function list(path, callback) { - var pathname = Path.resolve(this.cwd, path); - var result = []; + if(!path) { + callback(new Errors.EINVAL("Missing path argument")); + return; + } + function remove(pathname, callback) { + pathname = Path.resolve(this.cwd, pathname); + fs.stat(pathname, function(error, stats) { + if(error) { + callback(error); + return; + } + + // If this is a file, delete it and we're done + if(stats.type === 'FILE') { + fs.unlink(pathname, callback); + return; + } + + // If it's a dir, check if it's empty fs.readdir(pathname, function(error, entries) { if(error) { callback(error); return; } - function getDirEntry(name, callback) { - name = Path.join(pathname, name); - fs.stat(name, function(error, stats) { - if(error) { - callback(error); - return; - } - var entry = { - path: Path.basename(name), - links: stats.nlinks, - size: stats.size, - modified: stats.mtime, - type: stats.type - }; - - if(options.recursive && stats.type === 'DIRECTORY') { - list(Path.join(pathname, entry.path), function(error, items) { - if(error) { - callback(error); - return; - } - entry.contents = items; - result.push(entry); - callback(); - }); - } else { - result.push(entry); - callback(); - } - }); + // If dir is empty, delete it and we're done + if(entries.length === 0) { + fs.rmdir(pathname, callback); + return; } - async.each(entries, getDirEntry, function(error) { - callback(error, result); + // If not, see if we're allowed to delete recursively + if(!options.recursive) { + callback(new Errors.ENOTEMPTY()); + return; + } + + // Remove each dir entry recursively, then delete the dir. + entries = entries.map(function(filename) { + // Root dir entries absolutely + return Path.join(pathname, filename); }); - }); - } - - list(dir, callback); - }; - - /** - * Removes the file or directory at `path`. If `path` is a file - * it will be removed. If `path` is a directory, it will be - * removed if it is empty, otherwise the callback will receive - * an error. In order to remove non-empty directories, use the - * `recursive=true` option. - */ - Shell.prototype.rm = function(path, options, callback) { - var fs = this.fs; - if(typeof options === 'function') { - callback = options; - options = {}; - } - options = options || {}; - callback = callback || function(){}; - - if(!path) { - callback(new Errors.EINVAL("Missing path argument")); - return; - } - - function remove(pathname, callback) { - pathname = Path.resolve(this.cwd, pathname); - fs.stat(pathname, function(error, stats) { - if(error) { - callback(error); - return; - } - - // If this is a file, delete it and we're done - if(stats.type === 'FILE') { - fs.unlink(pathname, callback); - return; - } - - // If it's a dir, check if it's empty - fs.readdir(pathname, function(error, entries) { + async.each(entries, remove, function(error) { if(error) { callback(error); return; } - - // If dir is empty, delete it and we're done - if(entries.length === 0) { - fs.rmdir(pathname, callback); - return; - } - - // If not, see if we're allowed to delete recursively - if(!options.recursive) { - callback(new Errors.ENOTEMPTY()); - return; - } - - // Remove each dir entry recursively, then delete the dir. - entries = entries.map(function(filename) { - // Root dir entries absolutely - return Path.join(pathname, filename); - }); - async.each(entries, remove, function(error) { - if(error) { - callback(error); - return; - } - fs.rmdir(pathname, callback); - }); + fs.rmdir(pathname, callback); }); }); - } - - remove(path, callback); - }; - - /** - * Gets the path to the temporary directory, creating it if not - * present. The directory used is the one specified in - * env.TMP. The callback receives (error, tempDirName). - */ - Shell.prototype.tempDir = function(callback) { - var fs = this.fs; - var tmp = this.env.get('TMP'); - callback = callback || function(){}; - - // Try and create it, and it will either work or fail - // but either way it's now there. - fs.mkdir(tmp, function(err) { - callback(null, tmp); }); - }; + } - /** - * Recursively creates the directory at `path`. If the parent - * of `path` does not exist, it will be created. - * Based off EnsureDir by Sam X. Xu - * https://www.npmjs.org/package/ensureDir - * MIT License - */ - Shell.prototype.mkdirp = function(path, callback) { - var fs = this.fs; - callback = callback || function(){}; + remove(path, callback); +}; - if(!path) { - callback(new Errors.EINVAL("Missing path argument")); - return; - } - else if (path === '/') { - callback(); - return; - } - function _mkdirp(path, callback) { - fs.stat(path, function (err, stat) { - if(stat) { - if(stat.isDirectory()) { - callback(); - return; - } - else if (stat.isFile()) { - callback(new Errors.ENOTDIR()); - return; - } - } - else if (err && err.code !== 'ENOENT') { - callback(err); +/** + * Gets the path to the temporary directory, creating it if not + * present. The directory used is the one specified in + * env.TMP. The callback receives (error, tempDirName). + */ +Shell.prototype.tempDir = function(callback) { + var fs = this.fs; + var tmp = this.env.get('TMP'); + callback = callback || function(){}; + + // Try and create it, and it will either work or fail + // but either way it's now there. + fs.mkdir(tmp, function(err) { + callback(null, tmp); + }); +}; + +/** + * Recursively creates the directory at `path`. If the parent + * of `path` does not exist, it will be created. + * Based off EnsureDir by Sam X. Xu + * https://www.npmjs.org/package/ensureDir + * MIT License + */ +Shell.prototype.mkdirp = function(path, callback) { + var fs = this.fs; + callback = callback || function(){}; + + if(!path) { + callback(new Errors.EINVAL("Missing path argument")); + return; + } + else if (path === '/') { + callback(); + return; + } + function _mkdirp(path, callback) { + fs.stat(path, function (err, stat) { + if(stat) { + if(stat.isDirectory()) { + callback(); return; } + else if (stat.isFile()) { + callback(new Errors.ENOTDIR()); + return; + } + } + else if (err && err.code !== 'ENOENT') { + callback(err); + return; + } + else { + var parent = Path.dirname(path); + if(parent === '/') { + fs.mkdir(path, function (err) { + if (err && err.code != 'EEXIST') { + callback(err); + return; + } + callback(); + return; + }); + } else { - var parent = Path.dirname(path); - if(parent === '/') { + _mkdirp(parent, function (err) { + if (err) return callback(err); fs.mkdir(path, function (err) { if (err && err.code != 'EEXIST') { callback(err); @@ -403,208 +412,193 @@ define(function(require) { callback(); return; }); - } - else { - _mkdirp(parent, function (err) { - if (err) return callback(err); - fs.mkdir(path, function (err) { - if (err && err.code != 'EEXIST') { - callback(err); - return; - } - callback(); - return; - }); - }); - } + }); } - - }); - } - - _mkdirp(path, callback); - }; - - /** - * Downloads the file at `url` and saves it to the filesystem. - * The file is saved to a file named with the current date/time - * unless the `options.filename` is present, in which case that - * filename is used instead. The callback receives (error, path). - */ - Shell.prototype.wget = function(url, options, callback) { - var fs = this.fs; - if(typeof options === 'function') { - callback = options; - options = {}; - } - options = options || {}; - callback = callback || function(){}; - - if(!url) { - callback(new Errors.EINVAL('missing url argument')); - return; - } - - // Grab whatever is after the last / (assuming there is one). Like the real - // wget, we leave query string or hash portions in tact. This assumes a - // properly encoded URL. - // i.e. instead of "/foo?bar/" we would expect "/foo?bar%2F" - var path = options.filename || url.split('/').pop(); - - path = Path.resolve(fs.cwd, path); - - function onerror() { - callback(new Error('unable to get resource')); - } - - Network.download(url, function(err, data) { - if (err || !data) { - return onerror(); } - - fs.writeFile(path, data, function(err) { - if(err) { - callback(err); - } else { - callback(null, path); - } - }); }); - }; + } - Shell.prototype.unzip = function(zipfile, options, callback) { - var fs = this.fs; - var sh = this; - if(typeof options === 'function') { - callback = options; - options = {}; - } - options = options || {}; - callback = callback || function(){}; + _mkdirp(path, callback); +}; - if(!zipfile) { - callback(new Errors.EINVAL('missing zipfile argument')); - return; +/** + * Downloads the file at `url` and saves it to the filesystem. + * The file is saved to a file named with the current date/time + * unless the `options.filename` is present, in which case that + * filename is used instead. The callback receives (error, path). + */ +Shell.prototype.wget = function(url, options, callback) { + var fs = this.fs; + if(typeof options === 'function') { + callback = options; + options = {}; + } + options = options || {}; + callback = callback || function(){}; + + if(!url) { + callback(new Errors.EINVAL('missing url argument')); + return; + } + + // Grab whatever is after the last / (assuming there is one). Like the real + // wget, we leave query string or hash portions in tact. This assumes a + // properly encoded URL. + // i.e. instead of "/foo?bar/" we would expect "/foo?bar%2F" + var path = options.filename || url.split('/').pop(); + + path = Path.resolve(fs.cwd, path); + + function onerror() { + callback(new Error('unable to get resource')); + } + + Network.download(url, function(err, data) { + if (err || !data) { + return onerror(); } - var path = Path.resolve(this.cwd, zipfile); - var destination = Path.resolve(options.destination || this.cwd); + fs.writeFile(path, data, function(err) { + if(err) { + callback(err); + } else { + callback(null, path); + } + }); + }); +}; +Shell.prototype.unzip = function(zipfile, options, callback) { + var fs = this.fs; + var sh = this; + if(typeof options === 'function') { + callback = options; + options = {}; + } + options = options || {}; + callback = callback || function(){}; + + if(!zipfile) { + callback(new Errors.EINVAL('missing zipfile argument')); + return; + } + + var path = Path.resolve(this.cwd, zipfile); + var destination = Path.resolve(options.destination || this.cwd); + + fs.readFile(path, function(err, data) { + if(err) return callback(err); + + var unzip = new Zlib.Unzip(data); + + // Separate filenames within the zip archive with what will go in fs. + // Also mark any directories (i.e., paths with a trailing '/') + var filenames = unzip.getFilenames().map(function(filename) { + return { + zipFilename: filename, + fsFilename: Path.join(destination, filename), + isDirectory: /\/$/.test(filename) + }; + }); + + function decompress(path, callback) { + var data = unzip.decompress(path.zipFilename); + if(path.isDirectory) { + sh.mkdirp(path.fsFilename, callback); + } else { + fs.writeFile(path.fsFilename, data, callback); + } + } + + async.eachSeries(filenames, decompress, callback); + }); +}; + +Shell.prototype.zip = function(zipfile, paths, options, callback) { + var fs = this.fs; + var sh = this; + if(typeof options === 'function') { + callback = options; + options = {}; + } + options = options || {}; + callback = callback || function(){}; + + if(!zipfile) { + callback(new Errors.EINVAL('missing zipfile argument')); + return; + } + if(!paths) { + callback(new Errors.EINVAL('missing paths argument')); + return; + } + if(typeof paths === 'string') { + paths = [ paths ]; + } + zipfile = Path.resolve(this.cwd, zipfile); + + function encode(s) { + return new TextEncoder('utf8').encode(s); + } + + function addFile(path, callback) { fs.readFile(path, function(err, data) { if(err) return callback(err); - var unzip = new Zlib.Unzip(data); + // Make path relative within the zip + var relpath = path.replace(/^\//, ''); + zip.addFile(data, { filename: encode(relpath) }); + callback(); + }); + } - // Separate filenames within the zip archive with what will go in fs. - // Also mark any directories (i.e., paths with a trailing '/') - var filenames = unzip.getFilenames().map(function(filename) { - return { - zipFilename: filename, - fsFilename: Path.join(destination, filename), - isDirectory: /\/$/.test(filename) - }; + function addDir(path, callback) { + fs.readdir(path, function(err, list) { + // Add the directory itself (with no data) and a trailing / + zip.addFile([], { + filename: encode(path + '/'), + compressionMethod: Zlib.Zip.CompressionMethod.STORE }); - function decompress(path, callback) { - var data = unzip.decompress(path.zipFilename); - if(path.isDirectory) { - sh.mkdirp(path.fsFilename, callback); - } else { - fs.writeFile(path.fsFilename, data, callback); - } - } - - async.eachSeries(filenames, decompress, callback); - }); - }; - - Shell.prototype.zip = function(zipfile, paths, options, callback) { - var fs = this.fs; - var sh = this; - if(typeof options === 'function') { - callback = options; - options = {}; - } - options = options || {}; - callback = callback || function(){}; - - if(!zipfile) { - callback(new Errors.EINVAL('missing zipfile argument')); - return; - } - if(!paths) { - callback(new Errors.EINVAL('missing paths argument')); - return; - } - if(typeof paths === 'string') { - paths = [ paths ]; - } - zipfile = Path.resolve(this.cwd, zipfile); - - function encode(s) { - return new TextEncoder('utf8').encode(s); - } - - function addFile(path, callback) { - fs.readFile(path, function(err, data) { - if(err) return callback(err); - - // Make path relative within the zip - var relpath = path.replace(/^\//, ''); - zip.addFile(data, { filename: encode(relpath) }); + if(!options.recursive) { callback(); - }); - } - - function addDir(path, callback) { - fs.readdir(path, function(err, list) { - // Add the directory itself (with no data) and a trailing / - zip.addFile([], { - filename: encode(path + '/'), - compressionMethod: Zlib.Zip.CompressionMethod.STORE - }); - - if(!options.recursive) { - callback(); - } - - // Add all children of this dir, too - async.eachSeries(list, function(entry, callback) { - add(Path.join(path, entry), callback); - }, callback); - }); - } - - function add(path, callback) { - path = Path.resolve(sh.cwd, path); - fs.stat(path, function(err, stats) { - if(err) return callback(err); - - if(stats.isDirectory()) { - addDir(path, callback); - } else { - addFile(path, callback); - } - }); - } - - var zip = new Zlib.Zip(); - - // Make sure the zipfile doesn't already exist. - fs.stat(zipfile, function(err, stats) { - if(stats) { - return callback(new Errors.EEXIST('zipfile already exists')); } - async.eachSeries(paths, add, function(err) { - if(err) return callback(err); - - var compressed = zip.compress(); - fs.writeFile(zipfile, compressed, callback); - }); + // Add all children of this dir, too + async.eachSeries(list, function(entry, callback) { + add(Path.join(path, entry), callback); + }, callback); }); - }; + } - return Shell; + function add(path, callback) { + path = Path.resolve(sh.cwd, path); + fs.stat(path, function(err, stats) { + if(err) return callback(err); -}); + if(stats.isDirectory()) { + addDir(path, callback); + } else { + addFile(path, callback); + } + }); + } + + var zip = new Zlib.Zip(); + + // Make sure the zipfile doesn't already exist. + fs.stat(zipfile, function(err, stats) { + if(stats) { + return callback(new Errors.EEXIST('zipfile already exists')); + } + + async.eachSeries(paths, add, function(err) { + if(err) return callback(err); + + var compressed = zip.compress(); + fs.writeFile(zipfile, compressed, callback); + }); + }); +}; + +module.exports = Shell; diff --git a/src/stats.js b/src/stats.js index b0c962d..15e6868 100644 --- a/src/stats.js +++ b/src/stats.js @@ -1,37 +1,35 @@ -define(['src/constants'], function(Constants) { +var Constants = require('./constants.js'); - function Stats(fileNode, devName) { - this.node = fileNode.id; - this.dev = devName; - this.size = fileNode.size; - this.nlinks = fileNode.nlinks; - this.atime = fileNode.atime; - this.mtime = fileNode.mtime; - this.ctime = fileNode.ctime; - this.type = fileNode.mode; - } +function Stats(fileNode, devName) { + this.node = fileNode.id; + this.dev = devName; + this.size = fileNode.size; + this.nlinks = fileNode.nlinks; + this.atime = fileNode.atime; + this.mtime = fileNode.mtime; + this.ctime = fileNode.ctime; + this.type = fileNode.mode; +} - Stats.prototype.isFile = function() { - return this.type === Constants.MODE_FILE; - }; +Stats.prototype.isFile = function() { + return this.type === Constants.MODE_FILE; +}; - Stats.prototype.isDirectory = function() { - return this.type === Constants.MODE_DIRECTORY; - }; +Stats.prototype.isDirectory = function() { + return this.type === Constants.MODE_DIRECTORY; +}; - Stats.prototype.isSymbolicLink = function() { - return this.type === Constants.MODE_SYMBOLIC_LINK; - }; +Stats.prototype.isSymbolicLink = function() { + return this.type === Constants.MODE_SYMBOLIC_LINK; +}; - // These will always be false in Filer. - Stats.prototype.isSocket = - Stats.prototype.isFIFO = - Stats.prototype.isCharacterDevice = - Stats.prototype.isBlockDevice = - function() { - return false; - }; +// These will always be false in Filer. +Stats.prototype.isSocket = +Stats.prototype.isFIFO = +Stats.prototype.isCharacterDevice = +Stats.prototype.isBlockDevice = +function() { + return false; +}; - return Stats; - -}); +module.exports = Stats; diff --git a/src/super-node.js b/src/super-node.js index 617f430..4770a24 100644 --- a/src/super-node.js +++ b/src/super-node.js @@ -1,14 +1,13 @@ -define(['src/constants', 'src/shared'], function(Constants, Shared) { +var Constants = require('./constants.js'); +var guid = require('./shared.js').guid; - return function SuperNode(atime, ctime, mtime) { - var now = Date.now(); +module.exports = function SuperNode(atime, ctime, mtime) { + var now = Date.now(); - this.id = Constants.SUPER_NODE_ID; - this.mode = Constants.MODE_META; - this.atime = atime || now; - this.ctime = ctime || now; - this.mtime = mtime || now; - this.rnode = Shared.guid(); // root node id (randomly generated) - }; - -}); + this.id = Constants.SUPER_NODE_ID; + this.mode = Constants.MODE_META; + this.atime = atime || now; + this.ctime = ctime || now; + this.mtime = mtime || now; + this.rnode = guid(); // root node id (randomly generated) +};