UNPKG

serialport-v5

Version:

fork with electron support of Node.js package to access serial ports. Linux, OSX and Windows. Welcome your robotic JavaScript overlords. Better yet, program them!

391 lines (319 loc) 11 kB
module.exports = Writer var fs = require('graceful-fs') var inherits = require('inherits') var rimraf = require('rimraf') var mkdir = require('mkdirp') var path = require('path') var umask = process.platform === 'win32' ? 0 : process.umask() var getType = require('./get-type.js') var Abstract = require('./abstract.js') // Must do this *before* loading the child classes inherits(Writer, Abstract) Writer.dirmode = parseInt('0777', 8) & (~umask) Writer.filemode = parseInt('0666', 8) & (~umask) var DirWriter = require('./dir-writer.js') var LinkWriter = require('./link-writer.js') var FileWriter = require('./file-writer.js') var ProxyWriter = require('./proxy-writer.js') // props is the desired state. current is optionally the current stat, // provided here so that subclasses can avoid statting the target // more than necessary. function Writer (props, current) { var self = this if (typeof props === 'string') { props = { path: props } } // polymorphism. // call fstream.Writer(dir) to get a DirWriter object, etc. var type = getType(props) var ClassType = Writer switch (type) { case 'Directory': ClassType = DirWriter break case 'File': ClassType = FileWriter break case 'Link': case 'SymbolicLink': ClassType = LinkWriter break case null: default: // Don't know yet what type to create, so we wrap in a proxy. ClassType = ProxyWriter break } if (!(self instanceof ClassType)) return new ClassType(props) // now get down to business. Abstract.call(self) if (!props.path) self.error('Must provide a path', null, true) // props is what we want to set. // set some convenience properties as well. self.type = props.type self.props = props self.depth = props.depth || 0 self.clobber = props.clobber === false ? props.clobber : true self.parent = props.parent || null self.root = props.root || (props.parent && props.parent.root) || self self._path = self.path = path.resolve(props.path) if (process.platform === 'win32') { self.path = self._path = self.path.replace(/\?/g, '_') if (self._path.length >= 260) { self._swallowErrors = true self._path = '\\\\?\\' + self.path.replace(/\//g, '\\') } } self.basename = path.basename(props.path) self.dirname = path.dirname(props.path) self.linkpath = props.linkpath || null props.parent = props.root = null // console.error("\n\n\n%s setting size to", props.path, props.size) self.size = props.size if (typeof props.mode === 'string') { props.mode = parseInt(props.mode, 8) } self.readable = false self.writable = true // buffer until ready, or while handling another entry self._buffer = [] self.ready = false self.filter = typeof props.filter === 'function' ? props.filter : null // start the ball rolling. // this checks what's there already, and then calls // self._create() to call the impl-specific creation stuff. self._stat(current) } // Calling this means that it's something we can't create. // Just assert that it's already there, otherwise raise a warning. Writer.prototype._create = function () { var self = this fs[self.props.follow ? 'stat' : 'lstat'](self._path, function (er) { if (er) { return self.warn('Cannot create ' + self._path + '\n' + 'Unsupported type: ' + self.type, 'ENOTSUP') } self._finish() }) } Writer.prototype._stat = function (current) { var self = this var props = self.props var stat = props.follow ? 'stat' : 'lstat' var who = self._proxy || self if (current) statCb(null, current) else fs[stat](self._path, statCb) function statCb (er, current) { if (self.filter && !self.filter.call(who, who, current)) { self._aborted = true self.emit('end') self.emit('close') return } // if it's not there, great. We'll just create it. // if it is there, then we'll need to change whatever differs if (er || !current) { return create(self) } self._old = current var currentType = getType(current) // if it's a type change, then we need to clobber or error. // if it's not a type change, then let the impl take care of it. if (currentType !== self.type) { return rimraf(self._path, function (er) { if (er) return self.error(er) self._old = null create(self) }) } // otherwise, just handle in the app-specific way // this creates a fs.WriteStream, or mkdir's, or whatever create(self) } } function create (self) { // console.error("W create", self._path, Writer.dirmode) // XXX Need to clobber non-dirs that are in the way, // unless { clobber: false } in the props. mkdir(path.dirname(self._path), Writer.dirmode, function (er, made) { // console.error("W created", path.dirname(self._path), er) if (er) return self.error(er) // later on, we have to set the mode and owner for these self._madeDir = made return self._create() }) } function endChmod (self, want, current, path, cb) { var wantMode = want.mode var chmod = want.follow || self.type !== 'SymbolicLink' ? 'chmod' : 'lchmod' if (!fs[chmod]) return cb() if (typeof wantMode !== 'number') return cb() var curMode = current.mode & parseInt('0777', 8) wantMode = wantMode & parseInt('0777', 8) if (wantMode === curMode) return cb() fs[chmod](path, wantMode, cb) } function endChown (self, want, current, path, cb) { // Don't even try it unless root. Too easy to EPERM. if (process.platform === 'win32') return cb() if (!process.getuid || process.getuid() !== 0) return cb() if (typeof want.uid !== 'number' && typeof want.gid !== 'number') return cb() if (current.uid === want.uid && current.gid === want.gid) return cb() var chown = (self.props.follow || self.type !== 'SymbolicLink') ? 'chown' : 'lchown' if (!fs[chown]) return cb() if (typeof want.uid !== 'number') want.uid = current.uid if (typeof want.gid !== 'number') want.gid = current.gid fs[chown](path, want.uid, want.gid, cb) } function endUtimes (self, want, current, path, cb) { if (!fs.utimes || process.platform === 'win32') return cb() var utimes = (want.follow || self.type !== 'SymbolicLink') ? 'utimes' : 'lutimes' if (utimes === 'lutimes' && !fs[utimes]) { utimes = 'utimes' } if (!fs[utimes]) return cb() var curA = current.atime var curM = current.mtime var meA = want.atime var meM = want.mtime if (meA === undefined) meA = curA if (meM === undefined) meM = curM if (!isDate(meA)) meA = new Date(meA) if (!isDate(meM)) meA = new Date(meM) if (meA.getTime() === curA.getTime() && meM.getTime() === curM.getTime()) return cb() fs[utimes](path, meA, meM, cb) } // XXX This function is beastly. Break it up! Writer.prototype._finish = function () { var self = this if (self._finishing) return self._finishing = true // console.error(" W Finish", self._path, self.size) // set up all the things. // At this point, we're already done writing whatever we've gotta write, // adding files to the dir, etc. var todo = 0 var errState = null var done = false if (self._old) { // the times will almost *certainly* have changed. // adds the utimes syscall, but remove another stat. self._old.atime = new Date(0) self._old.mtime = new Date(0) // console.error(" W Finish Stale Stat", self._path, self.size) setProps(self._old) } else { var stat = self.props.follow ? 'stat' : 'lstat' // console.error(" W Finish Stating", self._path, self.size) fs[stat](self._path, function (er, current) { // console.error(" W Finish Stated", self._path, self.size, current) if (er) { // if we're in the process of writing out a // directory, it's very possible that the thing we're linking to // doesn't exist yet (especially if it was intended as a symlink), // so swallow ENOENT errors here and just soldier on. if (er.code === 'ENOENT' && (self.type === 'Link' || self.type === 'SymbolicLink') && process.platform === 'win32') { self.ready = true self.emit('ready') self.emit('end') self.emit('close') self.end = self._finish = function () {} return } else return self.error(er) } setProps(self._old = current) }) } return function setProps (current) { todo += 3 endChmod(self, self.props, current, self._path, next('chmod')) endChown(self, self.props, current, self._path, next('chown')) endUtimes(self, self.props, current, self._path, next('utimes')) } function next (what) { return function (er) { // console.error(" W Finish", what, todo) if (errState) return if (er) { er.fstream_finish_call = what return self.error(errState = er) } if (--todo > 0) return if (done) return done = true // we may still need to set the mode/etc. on some parent dirs // that were created previously. delay end/close until then. if (!self._madeDir) return end() else endMadeDir(self, self._path, end) function end (er) { if (er) { er.fstream_finish_call = 'setupMadeDir' return self.error(er) } // all the props have been set, so we're completely done. self.emit('end') self.emit('close') } } } } function endMadeDir (self, p, cb) { var made = self._madeDir // everything *between* made and path.dirname(self._path) // needs to be set up. Note that this may just be one dir. var d = path.dirname(p) endMadeDir_(self, d, function (er) { if (er) return cb(er) if (d === made) { return cb() } endMadeDir(self, d, cb) }) } function endMadeDir_ (self, p, cb) { var dirProps = {} Object.keys(self.props).forEach(function (k) { dirProps[k] = self.props[k] // only make non-readable dirs if explicitly requested. if (k === 'mode' && self.type !== 'Directory') { dirProps[k] = dirProps[k] | parseInt('0111', 8) } }) var todo = 3 var errState = null fs.stat(p, function (er, current) { if (er) return cb(errState = er) endChmod(self, dirProps, current, p, next) endChown(self, dirProps, current, p, next) endUtimes(self, dirProps, current, p, next) }) function next (er) { if (errState) return if (er) return cb(errState = er) if (--todo === 0) return cb() } } Writer.prototype.pipe = function () { this.error("Can't pipe from writable stream") } Writer.prototype.add = function () { this.error("Can't add to non-Directory type") } Writer.prototype.write = function () { return true } function objectToString (d) { return Object.prototype.toString.call(d) } function isDate (d) { return typeof d === 'object' && objectToString(d) === '[object Date]' }