docs: function processFile(filePath, callback) {
    /*jshint validthis:true */
    var processor = this;
    processor.emit('processing.start', {
      file: filePath
    });
  
    encoder.detectBom(filePath, function(err, bomName) {
      var status = 'good';
  
      if (err) {
        status = 'error';
        processor.emit('processing.error', {
          file: filePath,
          message: 'Skipping file with errors during encoding detection: ' + err
        });
        return callback(null, status);
      }
      else {
        var needsFixing = false;
        var lastByteSequenceWasCR = false;
        var bytesPerBom = encoder.getBytesPerBom(bomName);
        var bytesPerControlChar = encoder.getBytesPerControlChar(bomName);
  
        var transformBuffer = [];
        var tempBuffer = null;
  
        // Fast-forward past the BOM if present
        var onFirstReadable = function() {
          var bomBuffer = this.read(bytesPerBom);
          if (bomBuffer == null || bomBuffer.length !== bytesPerBom) {
            status = 'error';
            processor.emit('processing.error', {
              file: filePath,
              message: 'Skipping file with errors during read: Unable to read past the expected BOM'
            });
            return callback(null, status);
          }
        };
        var onReadable = function() {
          var data = this.read(),
              i = 0,
              len = data.length,
              cursor = i,
              buffer;
          for ( ; i < len; i += bytesPerControlChar) {
            buffer = slicer.call(data, i, i + bytesPerControlChar);
  
            // If we read less bytes than the minimum number necessary to register
            // as a complete character in this encoding, put them back on the front
            // of the read queue for the next read and then bail out.
            if (buffer.length < bytesPerControlChar) {
              if (!tempBuffer || !arraysMatch(buffer, tempBuffer)) {
                tempBuffer = buffer;
                // Save these rogue bytes to inspect on the next time around
                this.unshift(data.slice(i, i + buffer.length));
                // Break out of the loop
                break;
              }
              else {
                // This will prevent an infinite loop if the number of bytes in the file
                // is erroneous/irregular, e.g. a total of 7 bytes in a UTF-16 file.
                status = 'error';
  
                // Send a signal to cause early termination (i.e. trigger the 'end' event)
                this.push(null);
                // Break out of the loop
                break;
              }
            }
            tempBuffer = null;
  
            if (encoder.doesByteSequenceSuggestBinary(buffer, bomName)) {
              status = 'skip';
              processor.emit('processing.skip', {
                file: filePath,
                message: 'Skipping suspected binary file'
              });
              // Send a signal to cause early termination (i.e. trigger the 'end' event)
              this.push(null);
              // Break out of the loop
              break;
            }
            else {
              if (encoder.isByteSequenceCR(buffer, bomName)) {
                lastByteSequenceWasCR = true;
              }
              else {
                if (lastByteSequenceWasCR && encoder.isByteSequenceLF(buffer, bomName)) {
                  needsFixing = true;
  
                  // Push everything (except for CRs) into the outgoing buffer
                  transformBuffer.splice.apply(transformBuffer, [transformBuffer.length, 0].concat(slicer.call(data, cursor, i - bytesPerControlChar)));
                  cursor = i;
                }
  
                // If this is the last byte of the buffer, push the remaining data into the outgoing buffer
                if (i + bytesPerControlChar === len && needsFixing) {
                  // Push everything (except for CRs) into the outgoing buffer
                  transformBuffer.splice.apply(transformBuffer, [transformBuffer.length, 0].concat(slicer.call(data, cursor)));
                }
  
                // Reset
                lastByteSequenceWasCR = false;
              }
            }
          }
        };
        var onError = function(err) {
          status = 'error';
          processor.emit('processing.error', {
            file: filePath,
            message: 'Skipping file with errors during read: ' + err
          });
          return callback(null, status);
        };
        var onEnd = function() {
          if (status === 'good') {
            if (needsFixing) {
              status = 'bad';
              this.on('close', function() {
                // Overwrite the existing file with the fixed data
                process.nextTick(function() {
                  processor.emit('convert.start', {
                    file: filePath
                  });
  
                  //
                  // TODO: Is there a more optimal way to do this?
                  //       e.g. TransformStream, write to temp then move, etc.
                  //
                  fs.writeFile(filePath, new Buffer(transformBuffer), function(err) {
                    if (err) {
                      status = 'error';
                      processor.emit('convert.error', {
                        file: filePath,
                        message: 'Unexpected error during conversion process: ' + err
                      });
                    }
                    else {
                      status = 'fix';
                      processor.emit('convert.end', {
                        file: filePath
                      });
                    }
  
                    processor.emit('processing.end', {
                      file: filePath
                    });
                    return callback(null, status);
                  });
                });
              });
            }
            else {
              status = 'skip';
              processor.emit('processing.skip', {
                file: filePath,
                message: 'Skipping file with all correct line endings'
              });
            }
          }
  
          if (status === 'skip') {
            processor.emit('processing.end', {
              file: filePath
            });
          }
          if (status !== 'bad') {
            return callback(null, status);
          }
        };
  
        var reader = new Readable();
        reader.wrap(fs.createReadStream(filePath));
  
        if (bytesPerBom > 0) {
          reader.once('readable', onFirstReadable);
        }
        reader
          .on('readable', onReadable)
          .on('error', onError)
          .on('end', onEnd);
      }
    });
  }