text: 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 }
    }
  
    // 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
    var ClassType
  
    if (props.type && typeof props.type === 'function') {
      type = props.type
      ClassType = type
    } else {
      type = getType(props)
      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)
  
    if (!props.path) {
      self.error('Must provide a path', null, true)
    }
  
    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)
    if (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
    if (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)
    if (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)
      if (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.
      if (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.
      if (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
        }
      }
  
      if (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.
      if (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 () {
        if (self._aborted) {
          self.emit('end')
          self.emit('close')
          return
        }
  
        if (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)
  }
  