all files / lib/ reader.js

60.29% Statements 82/136
46.88% Branches 45/96
45.45% Functions 5/11
62.31% Lines 81/130
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250           23× 23×   18×     18× 17×                 16× 16×   16×     16×                             10× 10×                         16×                                                                           70×                                                                                     20×           20×         20× 20×   15× 15×                                                                      
module.exports = Reader
 
var fs = require('graceful-fs')
var Stream = require('stream').Stream
var inherits = require('inherits')
var path = require('path')
var getType = require('./get-type.js')
var hardLinks = Reader.hardLinks = {}
var Abstract = require('./abstract.js')
 
// Must do this *before* loading the child classes
inherits(Reader, Abstract)
 
var LinkReader = require('./link-reader.js')
 
function Reader (props, currentStat) {
  var self = this
  if (!(self instanceof Reader)) return new Reader(props, currentStat)
 
  if (typeof props === 'string') {
    props = { path: props }
  }
 
  if (!props) {
    self.error('Must provide properties or a path', null, true)
  } else if (!props.path) {
    self.error('Must provide a path', null, true)
  }
 
  // polymorphism.
  // call fstream.Reader(dir) to get a DirReader object, etc.
  // Note that, unlike in the Writer case, ProxyReader is going
  // to be the *normal* state of affairs, since we rarely know
  // the type of a file prior to reading it.
 
  var type = getType(props)
  var ClassType = Reader
 
  if (currentStat && !type) {
    type = getType(currentStat)
    props[type] = true
    props.type = type
  }
 
  switch (type) {
    case 'Directory':
      ClassType = require('./dir-reader.js')
      break
 
    case 'Link':
    // XXX hard links are just files.
    // However, it would be good to keep track of files' dev+inode
    // and nlink values, and create a HardLinkReader that emits
    // a linkpath value of the original copy, so that the tar
    // writer can preserve them.
    // ClassType = HardLinkReader
    // break
 
    case 'File':
      ClassType = require('./file-reader.js')
      break
 
    case 'SymbolicLink':
      ClassType = LinkReader
      break
 
    case 'Socket':
      ClassType = require('./socket-reader.js')
      break
 
    case null:
      ClassType = require('./proxy-reader.js')
      break
  }
 
  if (!(self instanceof ClassType)) {
    return new ClassType(props)
  }
 
  Abstract.call(self)
 
  self.readable = true
  self.writable = false
 
  self.type = type
  self.props = props
  self.depth = props.depth = props.depth || 0
  self.parent = props.parent || null
  self.root = props.root || (props.parent && props.parent.root) || self
 
  self._path = self.path = path.resolve(props.path)
  Iif (process.platform === 'win32') {
    self.path = self._path = self.path.replace(/\?/g, '_')
    if (self._path.length >= 260) {
      // how DOES one create files on the moon?
      // if the path has spaces in it, then UNC will fail.
      self._swallowErrors = true
      // if (self._path.indexOf(" ") === -1) {
      self._path = '\\\\?\\' + self.path.replace(/\//g, '\\')
    // }
    }
  }
  self.basename = props.basename = path.basename(self.path)
  self.dirname = props.dirname = path.dirname(self.path)
 
  // these have served their purpose, and are now just noisy clutter
  props.parent = props.root = null
 
  // console.error("\n\n\n%s setting size to", props.path, props.size)
  self.size = props.size
  self.filter = typeof props.filter === 'function' ? props.filter : null
  Iif (props.sort === 'alpha') props.sort = alphasort
 
  // start the ball rolling.
  // this will stat the thing, and then call self._read()
  // to start reading whatever it is.
  // console.error("calling stat", props.path, currentStat)
  self._stat(currentStat)
}
 
function alphasort (a, b) {
  return a === b ? 0
    : a.toLowerCase() > b.toLowerCase() ? 1
      : a.toLowerCase() < b.toLowerCase() ? -1
        : a > b ? 1
          : -1
}
 
Reader.prototype._stat = function (currentStat) {
  var self = this
  var props = self.props
  var stat = props.follow ? 'stat' : 'lstat'
  // console.error("Reader._stat", self._path, currentStat)
  Iif (currentStat) process.nextTick(statCb.bind(null, null, currentStat))
  else fs[stat](self._path, statCb)
 
  function statCb (er, props_) {
    // console.error("Reader._stat, statCb", self._path, props_, props_.nlink)
    Iif (er) return self.error(er)
 
    Object.keys(props_).forEach(function (k) {
      props[k] = props_[k]
    })
 
    // if it's not the expected size, then abort here.
    Iif (undefined !== self.size && props.size !== self.size) {
      return self.error('incorrect size')
    }
    self.size = props.size
 
    var type = getType(props)
    var handleHardlinks = props.hardlinks !== false
 
    // special little thing for handling hardlinks.
    Iif (handleHardlinks && type !== 'Directory' && props.nlink && props.nlink > 1) {
      var k = props.dev + ':' + props.ino
      // console.error("Reader has nlink", self._path, k)
      if (hardLinks[k] === self._path || !hardLinks[k]) {
        hardLinks[k] = self._path
      } else {
        // switch into hardlink mode.
        type = self.type = self.props.type = 'Link'
        self.Link = self.props.Link = true
        self.linkpath = self.props.linkpath = hardLinks[k]
        // console.error("Hardlink detected, switching mode", self._path, self.linkpath)
        // Setting __proto__ would arguably be the "correct"
        // approach here, but that just seems too wrong.
        self._stat = self._read = LinkReader.prototype._read
      }
    }
 
    Iif (self.type && self.type !== type) {
      self.error('Unexpected type: ' + type)
    }
 
    // if the filter doesn't pass, then just skip over this one.
    // still have to emit end so that dir-walking can move on.
    Iif (self.filter) {
      var who = self._proxy || self
      // special handling for ProxyReaders
      if (!self.filter.call(who, who, props)) {
        if (!self._disowned) {
          self.abort()
          self.emit('end')
          self.emit('close')
        }
        return
      }
    }
 
    // last chance to abort or disown before the flow starts!
    var events = ['_stat', 'stat', 'ready']
    var e = 0
    ;(function go () {
      Iif (self._aborted) {
        self.emit('end')
        self.emit('close')
        return
      }
 
      Iif (self._paused && self.type !== 'Directory') {
        self.once('resume', go)
        return
      }
 
      var ev = events[e++]
      if (!ev) {
        return self._read()
      }
      self.emit(ev, props)
      go()
    })()
  }
}
 
Reader.prototype.pipe = function (dest) {
  var self = this
  if (typeof dest.add === 'function') {
    // piping to a multi-compatible, and we've got directory entries.
    self.on('entry', function (entry) {
      var ret = dest.add(entry)
      if (ret === false) {
        self.pause()
      }
    })
  }
 
  // console.error("R Pipe apply Stream Pipe")
  return Stream.prototype.pipe.apply(this, arguments)
}
 
Reader.prototype.pause = function (who) {
  this._paused = true
  who = who || this
  this.emit('pause', who)
  if (this._stream) this._stream.pause(who)
}
 
Reader.prototype.resume = function (who) {
  this._paused = false
  who = who || this
  this.emit('resume', who)
  if (this._stream) this._stream.resume(who)
  this._read()
}
 
Reader.prototype._read = function () {
  this.error('Cannot read unknown type: ' + this.type)
}