UNPKG

45.5 kBJavaScriptView Raw
1// Copyright Joyent, Inc. and other Node contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a
4// copy of this software and associated documentation files (the
5// "Software"), to deal in the Software without restriction, including
6// without limitation the rights to use, copy, modify, merge, publish,
7// distribute, sublicense, and/or sell copies of the Software, and to permit
8// persons to whom the Software is furnished to do so, subject to the
9// following conditions:
10//
11// The above copyright notice and this permission notice shall be included
12// in all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20// USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22// Maintainers, keep in mind that octal literals are not allowed
23// in strict mode. Use the decimal value and add a comment with
24// the octal value. Example:
25//
26// var mode = 438; /* mode=0666 */
27
28var util = require('util');
29var pathModule = require('path');
30
31var binding = process.binding('fs');
32var constants = process.binding('constants');
33var fs = exports;
34var Stream = require('stream').Stream;
35var EventEmitter = require('events').EventEmitter;
36
37var Readable = Stream.Readable;
38var Writable = Stream.Writable;
39
40var kMinPoolSpace = 128;
41
42var O_APPEND = constants.O_APPEND || 0;
43var O_CREAT = constants.O_CREAT || 0;
44var O_DIRECTORY = constants.O_DIRECTORY || 0;
45var O_EXCL = constants.O_EXCL || 0;
46var O_NOCTTY = constants.O_NOCTTY || 0;
47var O_NOFOLLOW = constants.O_NOFOLLOW || 0;
48var O_RDONLY = constants.O_RDONLY || 0;
49var O_RDWR = constants.O_RDWR || 0;
50var O_SYMLINK = constants.O_SYMLINK || 0;
51var O_SYNC = constants.O_SYNC || 0;
52var O_TRUNC = constants.O_TRUNC || 0;
53var O_WRONLY = constants.O_WRONLY || 0;
54
55var isWindows = process.platform === 'win32';
56
57var DEBUG = process.env.NODE_DEBUG && /fs/.test(process.env.NODE_DEBUG);
58
59function rethrow() {
60 // Only enable in debug mode. A backtrace uses ~1000 bytes of heap space and
61 // is fairly slow to generate.
62 var callback;
63 if (DEBUG) {
64 var backtrace = new Error;
65 callback = debugCallback;
66 } else
67 callback = missingCallback;
68
69 return callback;
70
71 function debugCallback(err) {
72 if (err) {
73 backtrace.message = err.message;
74 err = backtrace;
75 missingCallback(err);
76 }
77 }
78
79 function missingCallback(err) {
80 if (err) {
81 if (process.throwDeprecation)
82 throw err; // Forgot a callback but don't know where? Use NODE_DEBUG=fs
83 else if (!process.noDeprecation) {
84 var msg = 'fs: missing callback ' + (err.stack || err.message);
85 if (process.traceDeprecation)
86 console.trace(msg);
87 else
88 console.error(msg);
89 }
90 }
91 }
92}
93
94function maybeCallback(cb) {
95 return typeof cb === 'function' ? cb : rethrow();
96}
97
98// Ensure that callbacks run in the global context. Only use this function
99// for callbacks that are passed to the binding layer, callbacks that are
100// invoked from JS already run in the proper scope.
101function makeCallback(cb) {
102 if (typeof cb !== 'function') {
103 return rethrow();
104 }
105
106 return function() {
107 return cb.apply(null, arguments);
108 };
109}
110
111function assertEncoding(encoding) {
112 if (encoding && !Buffer.isEncoding(encoding)) {
113 throw new Error('Unknown encoding: ' + encoding);
114 }
115}
116
117function nullCheck(path, callback) {
118 if (('' + path).indexOf('\u0000') !== -1) {
119 var er = new Error('Path must be a string without null bytes.');
120 if (!callback)
121 throw er;
122 process.nextTick(function() {
123 callback(er);
124 });
125 return false;
126 }
127 return true;
128}
129
130fs.Stats = binding.Stats;
131
132fs.Stats.prototype._checkModeProperty = function(property) {
133 return ((this.mode & constants.S_IFMT) === property);
134};
135
136fs.Stats.prototype.isDirectory = function() {
137 return this._checkModeProperty(constants.S_IFDIR);
138};
139
140fs.Stats.prototype.isFile = function() {
141 return this._checkModeProperty(constants.S_IFREG);
142};
143
144fs.Stats.prototype.isBlockDevice = function() {
145 return this._checkModeProperty(constants.S_IFBLK);
146};
147
148fs.Stats.prototype.isCharacterDevice = function() {
149 return this._checkModeProperty(constants.S_IFCHR);
150};
151
152fs.Stats.prototype.isSymbolicLink = function() {
153 return this._checkModeProperty(constants.S_IFLNK);
154};
155
156fs.Stats.prototype.isFIFO = function() {
157 return this._checkModeProperty(constants.S_IFIFO);
158};
159
160fs.Stats.prototype.isSocket = function() {
161 return this._checkModeProperty(constants.S_IFSOCK);
162};
163
164fs.exists = function(path, callback) {
165 if (!nullCheck(path, cb)) return;
166 binding.stat(pathModule._makeLong(path), cb);
167 function cb(err, stats) {
168 if (callback) callback(err ? false : true);
169 }
170};
171
172fs.existsSync = function(path) {
173 try {
174 nullCheck(path);
175 binding.stat(pathModule._makeLong(path));
176 return true;
177 } catch (e) {
178 return false;
179 }
180};
181
182fs.readFile = function(path, options, callback_) {
183 var callback = maybeCallback(arguments[arguments.length - 1]);
184
185 if (typeof options === 'function' || !options) {
186 options = { encoding: null, flag: 'r' };
187 } else if (typeof options === 'string') {
188 options = { encoding: options, flag: 'r' };
189 } else if (!options) {
190 options = { encoding: null, flag: 'r' };
191 } else if (typeof options !== 'object') {
192 throw new TypeError('Bad arguments');
193 }
194
195 var encoding = options.encoding;
196 assertEncoding(encoding);
197
198 // first, stat the file, so we know the size.
199 var size;
200 var buffer; // single buffer with file data
201 var buffers; // list for when size is unknown
202 var pos = 0;
203 var fd;
204
205 var flag = options.flag || 'r';
206 fs.open(path, flag, 438 /*=0666*/, function(er, fd_) {
207 if (er) return callback(er);
208 fd = fd_;
209
210 fs.fstat(fd, function(er, st) {
211 if (er) return callback(er);
212 size = st.size;
213 if (size === 0) {
214 // the kernel lies about many files.
215 // Go ahead and try to read some bytes.
216 buffers = [];
217 return read();
218 }
219
220 buffer = new Buffer(size);
221 read();
222 });
223 });
224
225 function read() {
226 if (size === 0) {
227 buffer = new Buffer(8192);
228 fs.read(fd, buffer, 0, 8192, -1, afterRead);
229 } else {
230 fs.read(fd, buffer, pos, size - pos, -1, afterRead);
231 }
232 }
233
234 function afterRead(er, bytesRead) {
235 if (er) {
236 return fs.close(fd, function(er2) {
237 return callback(er);
238 });
239 }
240
241 if (bytesRead === 0) {
242 return close();
243 }
244
245 pos += bytesRead;
246 if (size !== 0) {
247 if (pos === size) close();
248 else read();
249 } else {
250 // unknown size, just read until we don't get bytes.
251 buffers.push(buffer.slice(0, bytesRead));
252 read();
253 }
254 }
255
256 function close() {
257 fs.close(fd, function(er) {
258 if (size === 0) {
259 // collected the data into the buffers list.
260 buffer = Buffer.concat(buffers, pos);
261 } else if (pos < size) {
262 buffer = buffer.slice(0, pos);
263 }
264
265 if (encoding) buffer = buffer.toString(encoding);
266 return callback(er, buffer);
267 });
268 }
269};
270
271fs.readFileSync = function(path, options) {
272 if (!options) {
273 options = { encoding: null, flag: 'r' };
274 } else if (typeof options === 'string') {
275 options = { encoding: options, flag: 'r' };
276 } else if (typeof options !== 'object') {
277 throw new TypeError('Bad arguments');
278 }
279
280 var encoding = options.encoding;
281 assertEncoding(encoding);
282
283 var flag = options.flag || 'r';
284 var fd = fs.openSync(path, flag, 438 /*=0666*/);
285
286 var size;
287 var threw = true;
288 try {
289 size = fs.fstatSync(fd).size;
290 threw = false;
291 } finally {
292 if (threw) fs.closeSync(fd);
293 }
294
295 var pos = 0;
296 var buffer; // single buffer with file data
297 var buffers; // list for when size is unknown
298
299 if (size === 0) {
300 buffers = [];
301 } else {
302 buffer = new Buffer(size);
303 }
304
305 var done = false;
306 while (!done) {
307 var threw = true;
308 try {
309 if (size !== 0) {
310 var bytesRead = fs.readSync(fd, buffer, pos, size - pos);
311 } else {
312 // the kernel lies about many files.
313 // Go ahead and try to read some bytes.
314 buffer = new Buffer(8192);
315 var bytesRead = fs.readSync(fd, buffer, 0, 8192);
316 if (bytesRead) {
317 buffers.push(buffer.slice(0, bytesRead));
318 }
319 }
320 threw = false;
321 } finally {
322 if (threw) fs.closeSync(fd);
323 }
324
325 pos += bytesRead;
326 done = (bytesRead === 0) || (size !== 0 && pos >= size);
327 }
328
329 fs.closeSync(fd);
330
331 if (size === 0) {
332 // data was collected into the buffers list.
333 buffer = Buffer.concat(buffers, pos);
334 } else if (pos < size) {
335 buffer = buffer.slice(0, pos);
336 }
337
338 if (encoding) buffer = buffer.toString(encoding);
339 return buffer;
340};
341
342
343// Used by binding.open and friends
344function stringToFlags(flag) {
345 // Only mess with strings
346 if (typeof flag !== 'string') {
347 return flag;
348 }
349
350 // O_EXCL is mandated by POSIX, Windows supports it too.
351 // Let's add a check anyway, just in case.
352 if (!O_EXCL && ~flag.indexOf('x')) {
353 throw errnoException('ENOSYS', 'fs.open(O_EXCL)');
354 }
355
356 switch (flag) {
357 case 'r' : return O_RDONLY;
358 case 'rs' : return O_RDONLY | O_SYNC;
359 case 'r+' : return O_RDWR;
360 case 'rs+' : return O_RDWR | O_SYNC;
361
362 case 'w' : return O_TRUNC | O_CREAT | O_WRONLY;
363 case 'wx' : // fall through
364 case 'xw' : return O_TRUNC | O_CREAT | O_WRONLY | O_EXCL;
365
366 case 'w+' : return O_TRUNC | O_CREAT | O_RDWR;
367 case 'wx+': // fall through
368 case 'xw+': return O_TRUNC | O_CREAT | O_RDWR | O_EXCL;
369
370 case 'a' : return O_APPEND | O_CREAT | O_WRONLY;
371 case 'ax' : // fall through
372 case 'xa' : return O_APPEND | O_CREAT | O_WRONLY | O_EXCL;
373
374 case 'a+' : return O_APPEND | O_CREAT | O_RDWR;
375 case 'ax+': // fall through
376 case 'xa+': return O_APPEND | O_CREAT | O_RDWR | O_EXCL;
377 }
378
379 throw new Error('Unknown file open flag: ' + flag);
380}
381
382// exported but hidden, only used by test/simple/test-fs-open-flags.js
383Object.defineProperty(exports, '_stringToFlags', {
384 enumerable: false,
385 value: stringToFlags
386});
387
388
389// Yes, the follow could be easily DRYed up but I provide the explicit
390// list to make the arguments clear.
391
392fs.close = function(fd, callback) {
393 binding.close(fd, makeCallback(callback));
394};
395
396fs.closeSync = function(fd) {
397 return binding.close(fd);
398};
399
400function modeNum(m, def) {
401 switch (typeof m) {
402 case 'number': return m;
403 case 'string': return parseInt(m, 8);
404 default:
405 if (def) {
406 return modeNum(def);
407 } else {
408 return undefined;
409 }
410 }
411}
412
413fs.open = function(path, flags, mode, callback) {
414 callback = makeCallback(arguments[arguments.length - 1]);
415 mode = modeNum(mode, 438 /*=0666*/);
416
417 if (!nullCheck(path, callback)) return;
418 binding.open(pathModule._makeLong(path),
419 stringToFlags(flags),
420 mode,
421 callback);
422};
423
424fs.openSync = function(path, flags, mode) {
425 mode = modeNum(mode, 438 /*=0666*/);
426 nullCheck(path);
427 return binding.open(pathModule._makeLong(path), stringToFlags(flags), mode);
428};
429
430fs.read = function(fd, buffer, offset, length, position, callback) {
431 if (!Buffer.isBuffer(buffer)) {
432 // legacy string interface (fd, length, position, encoding, callback)
433 var cb = arguments[4],
434 encoding = arguments[3];
435
436 assertEncoding(encoding);
437
438 position = arguments[2];
439 length = arguments[1];
440 buffer = new Buffer(length);
441 offset = 0;
442
443 callback = function(err, bytesRead) {
444 if (!cb) return;
445
446 var str = (bytesRead > 0) ? buffer.toString(encoding, 0, bytesRead) : '';
447
448 (cb)(err, str, bytesRead);
449 };
450 }
451
452 function wrapper(err, bytesRead) {
453 // Retain a reference to buffer so that it can't be GC'ed too soon.
454 callback && callback(err, bytesRead || 0, buffer);
455 }
456
457 binding.read(fd, buffer, offset, length, position, wrapper);
458};
459
460fs.readSync = function(fd, buffer, offset, length, position) {
461 var legacy = false;
462 if (!Buffer.isBuffer(buffer)) {
463 // legacy string interface (fd, length, position, encoding, callback)
464 legacy = true;
465 var encoding = arguments[3];
466
467 assertEncoding(encoding);
468
469 position = arguments[2];
470 length = arguments[1];
471 buffer = new Buffer(length);
472
473 offset = 0;
474 }
475
476 var r = binding.read(fd, buffer, offset, length, position);
477 if (!legacy) {
478 return r;
479 }
480
481 var str = (r > 0) ? buffer.toString(encoding, 0, r) : '';
482 return [str, r];
483};
484
485fs.write = function(fd, buffer, offset, length, position, callback) {
486 if (!Buffer.isBuffer(buffer)) {
487 // legacy string interface (fd, data, position, encoding, callback)
488 callback = arguments[4];
489 position = arguments[2];
490 assertEncoding(arguments[3]);
491
492 buffer = new Buffer('' + arguments[1], arguments[3]);
493 offset = 0;
494 length = buffer.length;
495 }
496
497 if (!length) {
498 if (typeof callback == 'function') {
499 process.nextTick(function() {
500 callback(undefined, 0);
501 });
502 }
503 return;
504 }
505
506 callback = maybeCallback(callback);
507
508 function wrapper(err, written) {
509 // Retain a reference to buffer so that it can't be GC'ed too soon.
510 callback(err, written || 0, buffer);
511 }
512
513 binding.write(fd, buffer, offset, length, position, wrapper);
514};
515
516fs.writeSync = function(fd, buffer, offset, length, position) {
517 if (!Buffer.isBuffer(buffer)) {
518 // legacy string interface (fd, data, position, encoding)
519 position = arguments[2];
520 assertEncoding(arguments[3]);
521
522 buffer = new Buffer('' + arguments[1], arguments[3]);
523 offset = 0;
524 length = buffer.length;
525 }
526 if (!length) return 0;
527
528 return binding.write(fd, buffer, offset, length, position);
529};
530
531fs.rename = function(oldPath, newPath, callback) {
532 callback = makeCallback(callback);
533 if (!nullCheck(oldPath, callback)) return;
534 if (!nullCheck(newPath, callback)) return;
535 binding.rename(pathModule._makeLong(oldPath),
536 pathModule._makeLong(newPath),
537 callback);
538};
539
540fs.renameSync = function(oldPath, newPath) {
541 nullCheck(oldPath);
542 nullCheck(newPath);
543 return binding.rename(pathModule._makeLong(oldPath),
544 pathModule._makeLong(newPath));
545};
546
547fs.truncate = function(path, len, callback) {
548 if (typeof path === 'number') {
549 // legacy
550 return fs.ftruncate(path, len, callback);
551 }
552 if (typeof len === 'function') {
553 callback = len;
554 len = 0;
555 } else if (typeof len === 'undefined') {
556 len = 0;
557 }
558 callback = maybeCallback(callback);
559 fs.open(path, 'r+', function(er, fd) {
560 if (er) return callback(er);
561 binding.ftruncate(fd, len, function(er) {
562 fs.close(fd, function(er2) {
563 callback(er || er2);
564 });
565 });
566 });
567};
568
569fs.truncateSync = function(path, len) {
570 if (typeof path === 'number') {
571 // legacy
572 return fs.ftruncateSync(path, len);
573 }
574 if (typeof len === 'undefined') {
575 len = 0;
576 }
577 // allow error to be thrown, but still close fd.
578 var fd = fs.openSync(path, 'r+');
579 try {
580 var ret = fs.ftruncateSync(fd, len);
581 } finally {
582 fs.closeSync(fd);
583 }
584 return ret;
585};
586
587fs.ftruncate = function(fd, len, callback) {
588 if (typeof len === 'function') {
589 callback = len;
590 len = 0;
591 } else if (typeof len === 'undefined') {
592 len = 0;
593 }
594 binding.ftruncate(fd, len, makeCallback(callback));
595};
596
597fs.ftruncateSync = function(fd, len) {
598 if (typeof len === 'undefined') {
599 len = 0;
600 }
601 return binding.ftruncate(fd, len);
602};
603
604fs.rmdir = function(path, callback) {
605 callback = makeCallback(callback);
606 if (!nullCheck(path, callback)) return;
607 binding.rmdir(pathModule._makeLong(path), callback);
608};
609
610fs.rmdirSync = function(path) {
611 nullCheck(path);
612 return binding.rmdir(pathModule._makeLong(path));
613};
614
615fs.fdatasync = function(fd, callback) {
616 binding.fdatasync(fd, makeCallback(callback));
617};
618
619fs.fdatasyncSync = function(fd) {
620 return binding.fdatasync(fd);
621};
622
623fs.fsync = function(fd, callback) {
624 binding.fsync(fd, makeCallback(callback));
625};
626
627fs.fsyncSync = function(fd) {
628 return binding.fsync(fd);
629};
630
631fs.mkdir = function(path, mode, callback) {
632 if (typeof mode === 'function') callback = mode;
633 callback = makeCallback(callback);
634 if (!nullCheck(path, callback)) return;
635 binding.mkdir(pathModule._makeLong(path),
636 modeNum(mode, 511 /*=0777*/),
637 callback);
638};
639
640fs.mkdirSync = function(path, mode) {
641 nullCheck(path);
642 return binding.mkdir(pathModule._makeLong(path),
643 modeNum(mode, 511 /*=0777*/));
644};
645
646fs.readdir = function(path, callback) {
647 callback = makeCallback(callback);
648 if (!nullCheck(path, callback)) return;
649 binding.readdir(pathModule._makeLong(path), callback);
650};
651
652fs.readdirSync = function(path) {
653 nullCheck(path);
654 return binding.readdir(pathModule._makeLong(path));
655};
656
657fs.fstat = function(fd, callback) {
658 binding.fstat(fd, makeCallback(callback));
659};
660
661fs.lstat = function(path, callback) {
662 callback = makeCallback(callback);
663 if (!nullCheck(path, callback)) return;
664 binding.lstat(pathModule._makeLong(path), callback);
665};
666
667fs.stat = function(path, callback) {
668 callback = makeCallback(callback);
669 if (!nullCheck(path, callback)) return;
670 binding.stat(pathModule._makeLong(path), callback);
671};
672
673fs.fstatSync = function(fd) {
674 return binding.fstat(fd);
675};
676
677fs.lstatSync = function(path) {
678 nullCheck(path);
679 return binding.lstat(pathModule._makeLong(path));
680};
681
682fs.statSync = function(path) {
683 nullCheck(path);
684 return binding.stat(pathModule._makeLong(path));
685};
686
687fs.readlink = function(path, callback) {
688 callback = makeCallback(callback);
689 if (!nullCheck(path, callback)) return;
690 binding.readlink(pathModule._makeLong(path), callback);
691};
692
693fs.readlinkSync = function(path) {
694 nullCheck(path);
695 return binding.readlink(pathModule._makeLong(path));
696};
697
698function preprocessSymlinkDestination(path, type) {
699 if (!isWindows) {
700 // No preprocessing is needed on Unix.
701 return path;
702 } else if (type === 'junction') {
703 // Junctions paths need to be absolute and \\?\-prefixed.
704 return pathModule._makeLong(path);
705 } else {
706 // Windows symlinks don't tolerate forward slashes.
707 return ('' + path).replace(/\//g, '\\');
708 }
709}
710
711fs.symlink = function(destination, path, type_, callback) {
712 var type = (typeof type_ === 'string' ? type_ : null);
713 var callback = makeCallback(arguments[arguments.length - 1]);
714
715 if (!nullCheck(destination, callback)) return;
716 if (!nullCheck(path, callback)) return;
717
718 binding.symlink(preprocessSymlinkDestination(destination, type),
719 pathModule._makeLong(path),
720 type,
721 callback);
722};
723
724fs.symlinkSync = function(destination, path, type) {
725 type = (typeof type === 'string' ? type : null);
726
727 nullCheck(destination);
728 nullCheck(path);
729
730 return binding.symlink(preprocessSymlinkDestination(destination, type),
731 pathModule._makeLong(path),
732 type);
733};
734
735fs.link = function(srcpath, dstpath, callback) {
736 callback = makeCallback(callback);
737 if (!nullCheck(srcpath, callback)) return;
738 if (!nullCheck(dstpath, callback)) return;
739
740 binding.link(pathModule._makeLong(srcpath),
741 pathModule._makeLong(dstpath),
742 callback);
743};
744
745fs.linkSync = function(srcpath, dstpath) {
746 nullCheck(srcpath);
747 nullCheck(dstpath);
748 return binding.link(pathModule._makeLong(srcpath),
749 pathModule._makeLong(dstpath));
750};
751
752fs.unlink = function(path, callback) {
753 callback = makeCallback(callback);
754 if (!nullCheck(path, callback)) return;
755 binding.unlink(pathModule._makeLong(path), callback);
756};
757
758fs.unlinkSync = function(path) {
759 nullCheck(path);
760 return binding.unlink(pathModule._makeLong(path));
761};
762
763fs.fchmod = function(fd, mode, callback) {
764 binding.fchmod(fd, modeNum(mode), makeCallback(callback));
765};
766
767fs.fchmodSync = function(fd, mode) {
768 return binding.fchmod(fd, modeNum(mode));
769};
770
771if (constants.hasOwnProperty('O_SYMLINK')) {
772 fs.lchmod = function(path, mode, callback) {
773 callback = maybeCallback(callback);
774 fs.open(path, constants.O_WRONLY | constants.O_SYMLINK, function(err, fd) {
775 if (err) {
776 callback(err);
777 return;
778 }
779 // prefer to return the chmod error, if one occurs,
780 // but still try to close, and report closing errors if they occur.
781 fs.fchmod(fd, mode, function(err) {
782 fs.close(fd, function(err2) {
783 callback(err || err2);
784 });
785 });
786 });
787 };
788
789 fs.lchmodSync = function(path, mode) {
790 var fd = fs.openSync(path, constants.O_WRONLY | constants.O_SYMLINK);
791
792 // prefer to return the chmod error, if one occurs,
793 // but still try to close, and report closing errors if they occur.
794 var err, err2;
795 try {
796 var ret = fs.fchmodSync(fd, mode);
797 } catch (er) {
798 err = er;
799 }
800 try {
801 fs.closeSync(fd);
802 } catch (er) {
803 err2 = er;
804 }
805 if (err || err2) throw (err || err2);
806 return ret;
807 };
808}
809
810
811fs.chmod = function(path, mode, callback) {
812 callback = makeCallback(callback);
813 if (!nullCheck(path, callback)) return;
814 binding.chmod(pathModule._makeLong(path),
815 modeNum(mode),
816 callback);
817};
818
819fs.chmodSync = function(path, mode) {
820 nullCheck(path);
821 return binding.chmod(pathModule._makeLong(path), modeNum(mode));
822};
823
824if (constants.hasOwnProperty('O_SYMLINK')) {
825 fs.lchown = function(path, uid, gid, callback) {
826 callback = maybeCallback(callback);
827 fs.open(path, constants.O_WRONLY | constants.O_SYMLINK, function(err, fd) {
828 if (err) {
829 callback(err);
830 return;
831 }
832 fs.fchown(fd, uid, gid, callback);
833 });
834 };
835
836 fs.lchownSync = function(path, uid, gid) {
837 var fd = fs.openSync(path, constants.O_WRONLY | constants.O_SYMLINK);
838 return fs.fchownSync(fd, uid, gid);
839 };
840}
841
842fs.fchown = function(fd, uid, gid, callback) {
843 binding.fchown(fd, uid, gid, makeCallback(callback));
844};
845
846fs.fchownSync = function(fd, uid, gid) {
847 return binding.fchown(fd, uid, gid);
848};
849
850fs.chown = function(path, uid, gid, callback) {
851 callback = makeCallback(callback);
852 if (!nullCheck(path, callback)) return;
853 binding.chown(pathModule._makeLong(path), uid, gid, callback);
854};
855
856fs.chownSync = function(path, uid, gid) {
857 nullCheck(path);
858 return binding.chown(pathModule._makeLong(path), uid, gid);
859};
860
861// converts Date or number to a fractional UNIX timestamp
862function toUnixTimestamp(time) {
863 if (typeof time == 'number') {
864 return time;
865 }
866 if (time instanceof Date) {
867 // convert to 123.456 UNIX timestamp
868 return time.getTime() / 1000;
869 }
870 throw new Error('Cannot parse time: ' + time);
871}
872
873// exported for unit tests, not for public consumption
874fs._toUnixTimestamp = toUnixTimestamp;
875
876fs.utimes = function(path, atime, mtime, callback) {
877 callback = makeCallback(callback);
878 if (!nullCheck(path, callback)) return;
879 binding.utimes(pathModule._makeLong(path),
880 toUnixTimestamp(atime),
881 toUnixTimestamp(mtime),
882 callback);
883};
884
885fs.utimesSync = function(path, atime, mtime) {
886 nullCheck(path);
887 atime = toUnixTimestamp(atime);
888 mtime = toUnixTimestamp(mtime);
889 binding.utimes(pathModule._makeLong(path), atime, mtime);
890};
891
892fs.futimes = function(fd, atime, mtime, callback) {
893 atime = toUnixTimestamp(atime);
894 mtime = toUnixTimestamp(mtime);
895 binding.futimes(fd, atime, mtime, makeCallback(callback));
896};
897
898fs.futimesSync = function(fd, atime, mtime) {
899 atime = toUnixTimestamp(atime);
900 mtime = toUnixTimestamp(mtime);
901 binding.futimes(fd, atime, mtime);
902};
903
904function writeAll(fd, buffer, offset, length, position, callback) {
905 callback = maybeCallback(arguments[arguments.length - 1]);
906
907 // write(fd, buffer, offset, length, position, callback)
908 fs.write(fd, buffer, offset, length, position, function(writeErr, written) {
909 if (writeErr) {
910 fs.close(fd, function() {
911 if (callback) callback(writeErr);
912 });
913 } else {
914 if (written === length) {
915 fs.close(fd, callback);
916 } else {
917 offset += written;
918 length -= written;
919 position += written;
920 writeAll(fd, buffer, offset, length, position, callback);
921 }
922 }
923 });
924}
925
926fs.writeFile = function(path, data, options, callback) {
927 var callback = maybeCallback(arguments[arguments.length - 1]);
928
929 if (typeof options === 'function' || !options) {
930 options = { encoding: 'utf8', mode: 438 /*=0666*/, flag: 'w' };
931 } else if (typeof options === 'string') {
932 options = { encoding: options, mode: 438, flag: 'w' };
933 } else if (!options) {
934 options = { encoding: 'utf8', mode: 438 /*=0666*/, flag: 'w' };
935 } else if (typeof options !== 'object') {
936 throw new TypeError('Bad arguments');
937 }
938
939 assertEncoding(options.encoding);
940
941 var flag = options.flag || 'w';
942 fs.open(path, options.flag || 'w', options.mode, function(openErr, fd) {
943 if (openErr) {
944 if (callback) callback(openErr);
945 } else {
946 var buffer = Buffer.isBuffer(data) ? data : new Buffer('' + data,
947 options.encoding || 'utf8');
948 var position = /a/.test(flag) ? null : 0;
949 writeAll(fd, buffer, 0, buffer.length, position, callback);
950 }
951 });
952};
953
954fs.writeFileSync = function(path, data, options) {
955 if (!options) {
956 options = { encoding: 'utf8', mode: 438 /*=0666*/, flag: 'w' };
957 } else if (typeof options === 'string') {
958 options = { encoding: options, mode: 438, flag: 'w' };
959 } else if (typeof options !== 'object') {
960 throw new TypeError('Bad arguments');
961 }
962
963 assertEncoding(options.encoding);
964
965 var flag = options.flag || 'w';
966 var fd = fs.openSync(path, flag, options.mode);
967 if (!Buffer.isBuffer(data)) {
968 data = new Buffer('' + data, options.encoding || 'utf8');
969 }
970 var written = 0;
971 var length = data.length;
972 var position = /a/.test(flag) ? null : 0;
973 try {
974 while (written < length) {
975 written += fs.writeSync(fd, data, written, length - written, position);
976 position += written;
977 }
978 } finally {
979 fs.closeSync(fd);
980 }
981};
982
983fs.appendFile = function(path, data, options, callback_) {
984 var callback = maybeCallback(arguments[arguments.length - 1]);
985
986 if (typeof options === 'function' || !options) {
987 options = { encoding: 'utf8', mode: 438 /*=0666*/, flag: 'a' };
988 } else if (typeof options === 'string') {
989 options = { encoding: options, mode: 438, flag: 'a' };
990 } else if (!options) {
991 options = { encoding: 'utf8', mode: 438 /*=0666*/, flag: 'a' };
992 } else if (typeof options !== 'object') {
993 throw new TypeError('Bad arguments');
994 }
995
996 if (!options.flag)
997 options = util._extend({ flag: 'a' }, options);
998 fs.writeFile(path, data, options, callback);
999};
1000
1001fs.appendFileSync = function(path, data, options) {
1002 if (!options) {
1003 options = { encoding: 'utf8', mode: 438 /*=0666*/, flag: 'a' };
1004 } else if (typeof options === 'string') {
1005 options = { encoding: options, mode: 438, flag: 'a' };
1006 } else if (typeof options !== 'object') {
1007 throw new TypeError('Bad arguments');
1008 }
1009 if (!options.flag)
1010 options = util._extend({ flag: 'a' }, options);
1011
1012 fs.writeFileSync(path, data, options);
1013};
1014
1015function errnoException(errorno, syscall) {
1016 // TODO make this more compatible with ErrnoException from src/node.cc
1017 // Once all of Node is using this function the ErrnoException from
1018 // src/node.cc should be removed.
1019 var e = new Error(syscall + ' ' + errorno);
1020 e.errno = e.code = errorno;
1021 e.syscall = syscall;
1022 return e;
1023}
1024
1025
1026function FSWatcher() {
1027 EventEmitter.call(this);
1028
1029 var self = this;
1030 var FSEvent = process.binding('fs_event_wrap').FSEvent;
1031 this._handle = new FSEvent();
1032 this._handle.owner = this;
1033
1034 this._handle.onchange = function(status, event, filename) {
1035 if (status) {
1036 self._handle.close();
1037 self.emit('error', errnoException(process._errno, 'watch'));
1038 } else {
1039 self.emit('change', event, filename);
1040 }
1041 };
1042}
1043util.inherits(FSWatcher, EventEmitter);
1044
1045FSWatcher.prototype.start = function(filename, persistent) {
1046 nullCheck(filename);
1047 var r = this._handle.start(pathModule._makeLong(filename), persistent);
1048
1049 if (r) {
1050 this._handle.close();
1051 throw errnoException(process._errno, 'watch');
1052 }
1053};
1054
1055FSWatcher.prototype.close = function() {
1056 this._handle.close();
1057};
1058
1059fs.watch = function(filename) {
1060 nullCheck(filename);
1061 var watcher;
1062 var options;
1063 var listener;
1064
1065 if ('object' == typeof arguments[1]) {
1066 options = arguments[1];
1067 listener = arguments[2];
1068 } else {
1069 options = {};
1070 listener = arguments[1];
1071 }
1072
1073 if (options.persistent === undefined) options.persistent = true;
1074
1075 watcher = new FSWatcher();
1076 watcher.start(filename, options.persistent);
1077
1078 if (listener) {
1079 watcher.addListener('change', listener);
1080 }
1081
1082 return watcher;
1083};
1084
1085
1086// Stat Change Watchers
1087
1088function StatWatcher() {
1089 EventEmitter.call(this);
1090
1091 var self = this;
1092 this._handle = new binding.StatWatcher();
1093
1094 // uv_fs_poll is a little more powerful than ev_stat but we curb it for
1095 // the sake of backwards compatibility
1096 var oldStatus = -1;
1097
1098 this._handle.onchange = function(current, previous, newStatus) {
1099 if (oldStatus === -1 &&
1100 newStatus === -1 &&
1101 current.nlink === previous.nlink) return;
1102
1103 oldStatus = newStatus;
1104 self.emit('change', current, previous);
1105 };
1106
1107 this._handle.onstop = function() {
1108 self.emit('stop');
1109 };
1110}
1111util.inherits(StatWatcher, EventEmitter);
1112
1113
1114StatWatcher.prototype.start = function(filename, persistent, interval) {
1115 nullCheck(filename);
1116 this._handle.start(pathModule._makeLong(filename), persistent, interval);
1117};
1118
1119
1120StatWatcher.prototype.stop = function() {
1121 this._handle.stop();
1122};
1123
1124
1125var statWatchers = {};
1126function inStatWatchers(filename) {
1127 return Object.prototype.hasOwnProperty.call(statWatchers, filename) &&
1128 statWatchers[filename];
1129}
1130
1131
1132fs.watchFile = function(filename) {
1133 nullCheck(filename);
1134 filename = pathModule.resolve(filename);
1135 var stat;
1136 var listener;
1137
1138 var options = {
1139 // Poll interval in milliseconds. 5007 is what libev used to use. It's
1140 // a little on the slow side but let's stick with it for now to keep
1141 // behavioral changes to a minimum.
1142 interval: 5007,
1143 persistent: true
1144 };
1145
1146 if ('object' == typeof arguments[1]) {
1147 options = util._extend(options, arguments[1]);
1148 listener = arguments[2];
1149 } else {
1150 listener = arguments[1];
1151 }
1152
1153 if (!listener) {
1154 throw new Error('watchFile requires a listener function');
1155 }
1156
1157 if (inStatWatchers(filename)) {
1158 stat = statWatchers[filename];
1159 } else {
1160 stat = statWatchers[filename] = new StatWatcher();
1161 stat.start(filename, options.persistent, options.interval);
1162 }
1163 stat.addListener('change', listener);
1164 return stat;
1165};
1166
1167fs.unwatchFile = function(filename, listener) {
1168 nullCheck(filename);
1169 filename = pathModule.resolve(filename);
1170 if (!inStatWatchers(filename)) return;
1171
1172 var stat = statWatchers[filename];
1173
1174 if (typeof listener === 'function') {
1175 stat.removeListener('change', listener);
1176 } else {
1177 stat.removeAllListeners('change');
1178 }
1179
1180 if (EventEmitter.listenerCount(stat, 'change') === 0) {
1181 stat.stop();
1182 statWatchers[filename] = undefined;
1183 }
1184};
1185
1186// Realpath
1187// Not using realpath(2) because it's bad.
1188// See: http://insanecoding.blogspot.com/2007/11/pathmax-simply-isnt.html
1189
1190var normalize = pathModule.normalize;
1191
1192// Regexp that finds the next partion of a (partial) path
1193// result is [base_with_slash, base], e.g. ['somedir/', 'somedir']
1194if (isWindows) {
1195 var nextPartRe = /(.*?)(?:[\/\\]+|$)/g;
1196} else {
1197 var nextPartRe = /(.*?)(?:[\/]+|$)/g;
1198}
1199
1200// Regex to find the device root, including trailing slash. E.g. 'c:\\'.
1201if (isWindows) {
1202 var splitRootRe = /^(?:[a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/][^\\\/]+)?[\\\/]*/;
1203} else {
1204 var splitRootRe = /^[\/]*/;
1205}
1206
1207fs.realpathSync = function realpathSync(p, cache) {
1208 // make p is absolute
1209 p = pathModule.resolve(p);
1210
1211 if (cache && Object.prototype.hasOwnProperty.call(cache, p)) {
1212 return cache[p];
1213 }
1214
1215 var original = p,
1216 seenLinks = {},
1217 knownHard = {};
1218
1219 // current character position in p
1220 var pos;
1221 // the partial path so far, including a trailing slash if any
1222 var current;
1223 // the partial path without a trailing slash (except when pointing at a root)
1224 var base;
1225 // the partial path scanned in the previous round, with slash
1226 var previous;
1227
1228 start();
1229
1230 function start() {
1231 // Skip over roots
1232 var m = splitRootRe.exec(p);
1233 pos = m[0].length;
1234 current = m[0];
1235 base = m[0];
1236 previous = '';
1237
1238 // On windows, check that the root exists. On unix there is no need.
1239 if (isWindows && !knownHard[base]) {
1240 fs.lstatSync(base);
1241 knownHard[base] = true;
1242 }
1243 }
1244
1245 // walk down the path, swapping out linked pathparts for their real
1246 // values
1247 // NB: p.length changes.
1248 while (pos < p.length) {
1249 // find the next part
1250 nextPartRe.lastIndex = pos;
1251 var result = nextPartRe.exec(p);
1252 previous = current;
1253 current += result[0];
1254 base = previous + result[1];
1255 pos = nextPartRe.lastIndex;
1256
1257 // continue if not a symlink
1258 if (knownHard[base] || (cache && cache[base] === base)) {
1259 continue;
1260 }
1261
1262 var resolvedLink;
1263 if (cache && Object.prototype.hasOwnProperty.call(cache, base)) {
1264 // some known symbolic link. no need to stat again.
1265 resolvedLink = cache[base];
1266 } else {
1267 var stat = fs.lstatSync(base);
1268 if (!stat.isSymbolicLink()) {
1269 knownHard[base] = true;
1270 if (cache) cache[base] = base;
1271 continue;
1272 }
1273
1274 // read the link if it wasn't read before
1275 // dev/ino always return 0 on windows, so skip the check.
1276 var linkTarget = null;
1277 if (!isWindows) {
1278 var id = stat.dev.toString(32) + ':' + stat.ino.toString(32);
1279 if (seenLinks.hasOwnProperty(id)) {
1280 linkTarget = seenLinks[id];
1281 }
1282 }
1283 if (linkTarget === null) {
1284 fs.statSync(base);
1285 linkTarget = fs.readlinkSync(base);
1286 }
1287 resolvedLink = pathModule.resolve(previous, linkTarget);
1288 // track this, if given a cache.
1289 if (cache) cache[base] = resolvedLink;
1290 if (!isWindows) seenLinks[id] = linkTarget;
1291 }
1292
1293 // resolve the link, then start over
1294 p = pathModule.resolve(resolvedLink, p.slice(pos));
1295 start();
1296 }
1297
1298 if (cache) cache[original] = p;
1299
1300 return p;
1301};
1302
1303
1304fs.realpath = function realpath(p, cache, cb) {
1305 if (typeof cb !== 'function') {
1306 cb = maybeCallback(cache);
1307 cache = null;
1308 }
1309
1310 // make p is absolute
1311 p = pathModule.resolve(p);
1312
1313 if (cache && Object.prototype.hasOwnProperty.call(cache, p)) {
1314 return process.nextTick(cb.bind(null, null, cache[p]));
1315 }
1316
1317 var original = p,
1318 seenLinks = {},
1319 knownHard = {};
1320
1321 // current character position in p
1322 var pos;
1323 // the partial path so far, including a trailing slash if any
1324 var current;
1325 // the partial path without a trailing slash (except when pointing at a root)
1326 var base;
1327 // the partial path scanned in the previous round, with slash
1328 var previous;
1329
1330 start();
1331
1332 function start() {
1333 // Skip over roots
1334 var m = splitRootRe.exec(p);
1335 pos = m[0].length;
1336 current = m[0];
1337 base = m[0];
1338 previous = '';
1339
1340 // On windows, check that the root exists. On unix there is no need.
1341 if (isWindows && !knownHard[base]) {
1342 fs.lstat(base, function(err) {
1343 if (err) return cb(err);
1344 knownHard[base] = true;
1345 LOOP();
1346 });
1347 } else {
1348 process.nextTick(LOOP);
1349 }
1350 }
1351
1352 // walk down the path, swapping out linked pathparts for their real
1353 // values
1354 function LOOP() {
1355 // stop if scanned past end of path
1356 if (pos >= p.length) {
1357 if (cache) cache[original] = p;
1358 return cb(null, p);
1359 }
1360
1361 // find the next part
1362 nextPartRe.lastIndex = pos;
1363 var result = nextPartRe.exec(p);
1364 previous = current;
1365 current += result[0];
1366 base = previous + result[1];
1367 pos = nextPartRe.lastIndex;
1368
1369 // continue if not a symlink
1370 if (knownHard[base] || (cache && cache[base] === base)) {
1371 return process.nextTick(LOOP);
1372 }
1373
1374 if (cache && Object.prototype.hasOwnProperty.call(cache, base)) {
1375 // known symbolic link. no need to stat again.
1376 return gotResolvedLink(cache[base]);
1377 }
1378
1379 return fs.lstat(base, gotStat);
1380 }
1381
1382 function gotStat(err, stat) {
1383 if (err) return cb(err);
1384
1385 // if not a symlink, skip to the next path part
1386 if (!stat.isSymbolicLink()) {
1387 knownHard[base] = true;
1388 if (cache) cache[base] = base;
1389 return process.nextTick(LOOP);
1390 }
1391
1392 // stat & read the link if not read before
1393 // call gotTarget as soon as the link target is known
1394 // dev/ino always return 0 on windows, so skip the check.
1395 if (!isWindows) {
1396 var id = stat.dev.toString(32) + ':' + stat.ino.toString(32);
1397 if (seenLinks.hasOwnProperty(id)) {
1398 return gotTarget(null, seenLinks[id], base);
1399 }
1400 }
1401 fs.stat(base, function(err) {
1402 if (err) return cb(err);
1403
1404 fs.readlink(base, function(err, target) {
1405 if (!isWindows) seenLinks[id] = target;
1406 gotTarget(err, target);
1407 });
1408 });
1409 }
1410
1411 function gotTarget(err, target, base) {
1412 if (err) return cb(err);
1413
1414 var resolvedLink = pathModule.resolve(previous, target);
1415 if (cache) cache[base] = resolvedLink;
1416 gotResolvedLink(resolvedLink);
1417 }
1418
1419 function gotResolvedLink(resolvedLink) {
1420 // resolve the link, then start over
1421 p = pathModule.resolve(resolvedLink, p.slice(pos));
1422 start();
1423 }
1424};
1425
1426
1427
1428var pool;
1429
1430function allocNewPool(poolSize) {
1431 pool = new Buffer(poolSize);
1432 pool.used = 0;
1433}
1434
1435
1436
1437fs.createReadStream = function(path, options) {
1438 return new ReadStream(path, options);
1439};
1440
1441util.inherits(ReadStream, Readable);
1442fs.ReadStream = ReadStream;
1443
1444function ReadStream(path, options) {
1445 if (!(this instanceof ReadStream))
1446 return new ReadStream(path, options);
1447
1448 // a little bit bigger buffer and water marks by default
1449 options = util._extend({
1450 highWaterMark: 64 * 1024
1451 }, options || {});
1452
1453 Readable.call(this, options);
1454
1455 this.path = path;
1456 this.fd = options.hasOwnProperty('fd') ? options.fd : null;
1457 this.flags = options.hasOwnProperty('flags') ? options.flags : 'r';
1458 this.mode = options.hasOwnProperty('mode') ? options.mode : 438; /*=0666*/
1459
1460 this.start = options.hasOwnProperty('start') ? options.start : undefined;
1461 this.end = options.hasOwnProperty('end') ? options.end : undefined;
1462 this.autoClose = options.hasOwnProperty('autoClose') ?
1463 options.autoClose : true;
1464 this.pos = undefined;
1465
1466 if (this.start !== undefined) {
1467 if ('number' !== typeof this.start) {
1468 throw TypeError('start must be a Number');
1469 }
1470 if (this.end === undefined) {
1471 this.end = Infinity;
1472 } else if ('number' !== typeof this.end) {
1473 throw TypeError('end must be a Number');
1474 }
1475
1476 if (this.start > this.end) {
1477 throw new Error('start must be <= end');
1478 }
1479
1480 this.pos = this.start;
1481 }
1482
1483 if (typeof this.fd !== 'number')
1484 this.open();
1485
1486 this.on('end', function() {
1487 if (this.autoClose) {
1488 this.destroy();
1489 }
1490 });
1491}
1492
1493fs.FileReadStream = fs.ReadStream; // support the legacy name
1494
1495ReadStream.prototype.open = function() {
1496 var self = this;
1497 fs.open(this.path, this.flags, this.mode, function(er, fd) {
1498 if (er) {
1499 if (self.autoClose) {
1500 self.destroy();
1501 }
1502 self.emit('error', er);
1503 return;
1504 }
1505
1506 self.fd = fd;
1507 self.emit('open', fd);
1508 // start the flow of data.
1509 self.read();
1510 });
1511};
1512
1513ReadStream.prototype._read = function(n) {
1514 if (typeof this.fd !== 'number')
1515 return this.once('open', function() {
1516 this._read(n);
1517 });
1518
1519 if (this.destroyed)
1520 return;
1521
1522 if (!pool || pool.length - pool.used < kMinPoolSpace) {
1523 // discard the old pool.
1524 pool = null;
1525 allocNewPool(this._readableState.highWaterMark);
1526 }
1527
1528 // Grab another reference to the pool in the case that while we're
1529 // in the thread pool another read() finishes up the pool, and
1530 // allocates a new one.
1531 var thisPool = pool;
1532 var toRead = Math.min(pool.length - pool.used, n);
1533 var start = pool.used;
1534
1535 if (this.pos !== undefined)
1536 toRead = Math.min(this.end - this.pos + 1, toRead);
1537
1538 // already read everything we were supposed to read!
1539 // treat as EOF.
1540 if (toRead <= 0)
1541 return this.push(null);
1542
1543 // the actual read.
1544 var self = this;
1545 fs.read(this.fd, pool, pool.used, toRead, this.pos, onread);
1546
1547 // move the pool positions, and internal position for reading.
1548 if (this.pos !== undefined)
1549 this.pos += toRead;
1550 pool.used += toRead;
1551
1552 function onread(er, bytesRead) {
1553 if (er) {
1554 if (self.autoClose) {
1555 self.destroy();
1556 }
1557 self.emit('error', er);
1558 } else {
1559 var b = null;
1560 if (bytesRead > 0)
1561 b = thisPool.slice(start, start + bytesRead);
1562
1563 self.push(b);
1564 }
1565 }
1566};
1567
1568
1569ReadStream.prototype.destroy = function() {
1570 if (this.destroyed)
1571 return;
1572 this.destroyed = true;
1573
1574 if ('number' === typeof this.fd)
1575 this.close();
1576};
1577
1578
1579ReadStream.prototype.close = function(cb) {
1580 var self = this;
1581 if (cb)
1582 this.once('close', cb);
1583 if (this.closed || 'number' !== typeof this.fd) {
1584 if ('number' !== typeof this.fd) {
1585 this.once('open', close);
1586 return;
1587 }
1588 return process.nextTick(this.emit.bind(this, 'close'));
1589 }
1590 this.closed = true;
1591 close();
1592
1593 function close(fd) {
1594 fs.close(fd || self.fd, function(er) {
1595 if (er)
1596 self.emit('error', er);
1597 else
1598 self.emit('close');
1599 });
1600 self.fd = null;
1601 }
1602};
1603
1604
1605
1606
1607fs.createWriteStream = function(path, options) {
1608 return new WriteStream(path, options);
1609};
1610
1611util.inherits(WriteStream, Writable);
1612fs.WriteStream = WriteStream;
1613function WriteStream(path, options) {
1614 if (!(this instanceof WriteStream))
1615 return new WriteStream(path, options);
1616
1617 options = options || {};
1618
1619 Writable.call(this, options);
1620
1621 this.path = path;
1622 this.fd = null;
1623
1624 this.fd = options.hasOwnProperty('fd') ? options.fd : null;
1625 this.flags = options.hasOwnProperty('flags') ? options.flags : 'w';
1626 this.mode = options.hasOwnProperty('mode') ? options.mode : 438; /*=0666*/
1627
1628 this.start = options.hasOwnProperty('start') ? options.start : undefined;
1629 this.pos = undefined;
1630 this.bytesWritten = 0;
1631
1632 if (this.start !== undefined) {
1633 if ('number' !== typeof this.start) {
1634 throw TypeError('start must be a Number');
1635 }
1636 if (this.start < 0) {
1637 throw new Error('start must be >= zero');
1638 }
1639
1640 this.pos = this.start;
1641 }
1642
1643 if ('number' !== typeof this.fd)
1644 this.open();
1645
1646 // dispose on finish.
1647 this.once('finish', this.close);
1648}
1649
1650fs.FileWriteStream = fs.WriteStream; // support the legacy name
1651
1652
1653WriteStream.prototype.open = function() {
1654 fs.open(this.path, this.flags, this.mode, function(er, fd) {
1655 if (er) {
1656 this.destroy();
1657 this.emit('error', er);
1658 return;
1659 }
1660
1661 this.fd = fd;
1662 this.emit('open', fd);
1663 }.bind(this));
1664};
1665
1666
1667WriteStream.prototype._write = function(data, encoding, cb) {
1668 if (!Buffer.isBuffer(data))
1669 return this.emit('error', new Error('Invalid data'));
1670
1671 if (typeof this.fd !== 'number')
1672 return this.once('open', function() {
1673 this._write(data, encoding, cb);
1674 });
1675
1676 var self = this;
1677 fs.write(this.fd, data, 0, data.length, this.pos, function(er, bytes) {
1678 if (er) {
1679 self.destroy();
1680 return cb(er);
1681 }
1682 self.bytesWritten += bytes;
1683 cb();
1684 });
1685
1686 if (this.pos !== undefined)
1687 this.pos += data.length;
1688};
1689
1690
1691WriteStream.prototype.destroy = ReadStream.prototype.destroy;
1692WriteStream.prototype.close = ReadStream.prototype.close;
1693
1694// There is no shutdown() for files.
1695WriteStream.prototype.destroySoon = WriteStream.prototype.end;
1696
1697
1698// SyncWriteStream is internal. DO NOT USE.
1699// Temporary hack for process.stdout and process.stderr when piped to files.
1700function SyncWriteStream(fd, options) {
1701 Stream.call(this);
1702
1703 options = options || {};
1704
1705 this.fd = fd;
1706 this.writable = true;
1707 this.readable = false;
1708 this.autoClose = options.hasOwnProperty('autoClose') ?
1709 options.autoClose : true;
1710}
1711
1712util.inherits(SyncWriteStream, Stream);
1713
1714
1715// Export
1716fs.SyncWriteStream = SyncWriteStream;
1717
1718
1719SyncWriteStream.prototype.write = function(data, arg1, arg2) {
1720 var encoding, cb;
1721
1722 // parse arguments
1723 if (arg1) {
1724 if (typeof arg1 === 'string') {
1725 encoding = arg1;
1726 cb = arg2;
1727 } else if (typeof arg1 === 'function') {
1728 cb = arg1;
1729 } else {
1730 throw new Error('bad arg');
1731 }
1732 }
1733 assertEncoding(encoding);
1734
1735 // Change strings to buffers. SLOW
1736 if (typeof data == 'string') {
1737 data = new Buffer(data, encoding);
1738 }
1739
1740 fs.writeSync(this.fd, data, 0, data.length);
1741
1742 if (cb) {
1743 process.nextTick(cb);
1744 }
1745
1746 return true;
1747};
1748
1749
1750SyncWriteStream.prototype.end = function(data, arg1, arg2) {
1751 if (data) {
1752 this.write(data, arg1, arg2);
1753 }
1754 this.destroy();
1755};
1756
1757
1758SyncWriteStream.prototype.destroy = function() {
1759 if (this.autoClose)
1760 fs.closeSync(this.fd);
1761 this.fd = null;
1762 this.emit('close');
1763 return true;
1764};
1765
1766SyncWriteStream.prototype.destroySoon = SyncWriteStream.prototype.destroy;