text: // TODO: async. Should probably be done with NSFileHandle and some notifications
  // TODO: file descriptor. Needs to be done with NSFileHandle
  var Buffer = require("buffer").Buffer;
  var utils = require("./utils");
  var parseStat = utils.parseStat;
  var fsError = utils.fsError;
  var fsErrorForPath = utils.fsErrorForPath;
  var encodingFromOptions = utils.encodingFromOptions;
  var NOT_IMPLEMENTED = utils.NOT_IMPLEMENTED;
  
  module.exports.constants = {
    F_OK: 0,
    R_OK: 4,
    W_OK: 2,
    X_OK: 1,
  };
  
  module.exports.access = NOT_IMPLEMENTED("access");
  
  module.exports.accessSync = function (path, mode) {
    mode = mode | 0;
    var fileManager = NSFileManager.defaultManager();
  
    switch (mode) {
      case 0:
        canAccess = module.exports.existsSync(path);
        break;
      case 1:
        canAccess = Boolean(Number(fileManager.isExecutableFileAtPath(path)));
        break;
      case 2:
        canAccess = Boolean(Number(fileManager.isWritableFileAtPath(path)));
        break;
      case 3:
        canAccess =
          Boolean(Number(fileManager.isExecutableFileAtPath(path))) &&
          Boolean(Number(fileManager.isWritableFileAtPath(path)));
        break;
      case 4:
        canAccess = Boolean(Number(fileManager.isReadableFileAtPath(path)));
        break;
      case 5:
        canAccess =
          Boolean(Number(fileManager.isReadableFileAtPath(path))) &&
          Boolean(Number(fileManager.isExecutableFileAtPath(path)));
        break;
      case 6:
        canAccess =
          Boolean(Number(fileManager.isReadableFileAtPath(path))) &&
          Boolean(Number(fileManager.isWritableFileAtPath(path)));
        break;
      case 7:
        canAccess =
          Boolean(Number(fileManager.isReadableFileAtPath(path))) &&
          Boolean(Number(fileManager.isWritableFileAtPath(path))) &&
          Boolean(Number(fileManager.isExecutableFileAtPath(path)));
        break;
    }
  
    if (!canAccess) {
      throw new Error("Can't access " + String(path));
    }
  };
  
  module.exports.appendFile = NOT_IMPLEMENTED("appendFile");
  
  module.exports.appendFileSync = function (file, data, options) {
    if (!module.exports.existsSync(file)) {
      return module.exports.writeFileSync(file, data, options);
    }
  
    var handle = NSFileHandle.fileHandleForWritingAtPath(file);
    handle.seekToEndOfFile();
  
    var encoding = encodingFromOptions(options, "utf8");
  
    var nsdata = Buffer.from(
      data,
      encoding === "NSData" || encoding === "buffer" ? undefined : encoding
    ).toNSData();
  
    handle.writeData(nsdata);
  };
  
  module.exports.chmod = NOT_IMPLEMENTED("chmod");
  
  module.exports.chmodSync = function (path, mode) {
    var err = MOPointer.alloc().init();
    var fileManager = NSFileManager.defaultManager();
    fileManager.setAttributes_ofItemAtPath_error(
      {
        NSFilePosixPermissions: mode,
      },
      path,
      err
    );
  
    if (err.value() !== null) {
      throw fsErrorForPath(path, undefined, err.value());
    }
  };
  
  module.exports.chown = NOT_IMPLEMENTED("chown");
  module.exports.chownSync = NOT_IMPLEMENTED("chownSync");
  
  module.exports.close = NOT_IMPLEMENTED("close");
  module.exports.closeSync = NOT_IMPLEMENTED("closeSync");
  
  module.exports.copyFile = NOT_IMPLEMENTED("copyFile");
  
  module.exports.copyFileSync = function (path, dest, flags) {
    var err = MOPointer.alloc().init();
    var fileManager = NSFileManager.defaultManager();
    fileManager.copyItemAtPath_toPath_error(path, dest, err);
  
    if (err.value() !== null) {
      throw fsErrorForPath(path, false, err.value());
    }
  };
  
  module.exports.createReadStream = NOT_IMPLEMENTED("createReadStream");
  module.exports.createWriteStream = NOT_IMPLEMENTED("createWriteStream");
  
  module.exports.exists = NOT_IMPLEMENTED("exists");
  
  module.exports.existsSync = function (path) {
    var fileManager = NSFileManager.defaultManager();
    return Boolean(Number(fileManager.fileExistsAtPath(path)));
  };
  
  module.exports.fchmod = NOT_IMPLEMENTED("fchmod");
  module.exports.fchmodSync = NOT_IMPLEMENTED("fchmodSync");
  module.exports.fchown = NOT_IMPLEMENTED("fchown");
  module.exports.fchownSync = NOT_IMPLEMENTED("fchownSync");
  module.exports.fdatasync = NOT_IMPLEMENTED("fdatasync");
  module.exports.fdatasyncSync = NOT_IMPLEMENTED("fdatasyncSync");
  module.exports.fstat = NOT_IMPLEMENTED("fstat");
  module.exports.fstatSync = NOT_IMPLEMENTED("fstatSync");
  module.exports.fsync = NOT_IMPLEMENTED("fsync");
  module.exports.fsyncSync = NOT_IMPLEMENTED("fsyncSync");
  module.exports.ftruncate = NOT_IMPLEMENTED("ftruncate");
  module.exports.ftruncateSync = NOT_IMPLEMENTED("ftruncateSync");
  module.exports.futimes = NOT_IMPLEMENTED("futimes");
  module.exports.futimesSync = NOT_IMPLEMENTED("futimesSync");
  
  module.exports.lchmod = NOT_IMPLEMENTED("lchmod");
  module.exports.lchmodSync = NOT_IMPLEMENTED("lchmodSync");
  module.exports.lchown = NOT_IMPLEMENTED("lchown");
  module.exports.lchownSync = NOT_IMPLEMENTED("lchownSync");
  
  module.exports.link = NOT_IMPLEMENTED("link");
  
  module.exports.linkSync = function (existingPath, newPath) {
    var err = MOPointer.alloc().init();
    var fileManager = NSFileManager.defaultManager();
    fileManager.linkItemAtPath_toPath_error(existingPath, newPath, err);
  
    if (err.value() !== null) {
      throw fsErrorForPath(existingPath, undefined, err.value());
    }
  };
  
  module.exports.lstat = NOT_IMPLEMENTED("lstat");
  
  module.exports.lstatSync = function (path) {
    var err = MOPointer.alloc().init();
    var fileManager = NSFileManager.defaultManager();
    var result = fileManager.attributesOfItemAtPath_error(path, err);
  
    if (err.value() !== null) {
      throw fsErrorForPath(path, undefined, err.value());
    }
  
    return parseStat(result);
  };
  
  module.exports.mkdir = NOT_IMPLEMENTED("mkdir");
  
  module.exports.mkdirSync = function (path, options) {
    var mode = 0o777;
    var recursive = false;
    if (options && options.mode) {
      mode = options.mode;
    }
    if (options && options.recursive) {
      recursive = options.recursive;
    }
    if (typeof options === "number") {
      mode = options;
    }
    var err = MOPointer.alloc().init();
    var fileManager = NSFileManager.defaultManager();
    fileManager.createDirectoryAtPath_withIntermediateDirectories_attributes_error(
      path,
      recursive,
      {
        NSFilePosixPermissions: mode,
      },
      err
    );
  
    if (err.value() !== null) {
      throw new Error(err.value());
    }
  };
  
  module.exports.mkdtemp = NOT_IMPLEMENTED("mkdtemp");
  
  module.exports.mkdtempSync = function (path) {
    function makeid() {
      var text = "";
      var possible =
        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
  
      for (var i = 0; i < 6; i++)
        text += possible.charAt(Math.floor(Math.random() * possible.length));
  
      return text;
    }
    var tempPath = path + makeid();
    module.exports.mkdirSync(tempPath);
    return tempPath;
  };
  
  module.exports.open = NOT_IMPLEMENTED("open");
  module.exports.openSync = NOT_IMPLEMENTED("openSync");
  
  module.exports.read = NOT_IMPLEMENTED("read");
  
  module.exports.readdir = NOT_IMPLEMENTED("readdir");
  
  module.exports.readdirSync = function (path, options) {
    var encoding = encodingFromOptions(options, "utf8");
    var fileManager = NSFileManager.defaultManager();
    var paths = fileManager.subpathsAtPath(path);
    var arr = [];
    for (var i = 0; i < paths.length; i++) {
      var pathName = paths[i];
      arr.push(encoding === "buffer" ? Buffer.from(pathName) : String(pathName));
    }
    return arr;
  };
  
  module.exports.readFile = NOT_IMPLEMENTED("readFile");
  
  module.exports.readFileSync = function (path, options) {
    var encoding = encodingFromOptions(options, "buffer");
    var fileManager = NSFileManager.defaultManager();
    var data = fileManager.contentsAtPath(path);
    if (!data) {
      throw fsErrorForPath(path, false);
    }
  
    var buffer = Buffer.from(data);
  
    if (encoding === "buffer") {
      return buffer;
    } else if (encoding === "NSData") {
      return buffer.toNSData();
    } else {
      return buffer.toString(encoding);
    }
  };
  
  module.exports.readlink = NOT_IMPLEMENTED("readlink");
  
  module.exports.readlinkSync = function (path) {
    var err = MOPointer.alloc().init();
    var fileManager = NSFileManager.defaultManager();
    var result = fileManager.destinationOfSymbolicLinkAtPath_error(path, err);
  
    if (err.value() !== null) {
      throw fsErrorForPath(path, undefined, err.value());
    }
  
    return String(result);
  };
  
  module.exports.readSync = NOT_IMPLEMENTED("readSync");
  
  module.exports.realpath = NOT_IMPLEMENTED("realpath");
  module.exports.realpath.native = NOT_IMPLEMENTED("realpath.native");
  
  module.exports.realpathSync = function (path) {
    return String(
      NSString.stringWithString(path).stringByResolvingSymlinksInPath()
    );
  };
  
  module.exports.realpathSync.native = NOT_IMPLEMENTED("realpathSync.native");
  
  module.exports.rename = NOT_IMPLEMENTED("rename");
  
  module.exports.renameSync = function (oldPath, newPath) {
    var err = MOPointer.alloc().init();
    var fileManager = NSFileManager.defaultManager();
    fileManager.moveItemAtPath_toPath_error(oldPath, newPath, err);
  
    var error = err.value();
  
    if (error !== null) {
      // if there is already a file, we need to overwrite it
      if (
        String(error.domain()) === "NSCocoaErrorDomain" &&
        Number(error.code()) === 516
      ) {
        var err2 = MOPointer.alloc().init();
        fileManager.replaceItemAtURL_withItemAtURL_backupItemName_options_resultingItemURL_error(
          NSURL.fileURLWithPath(newPath),
          NSURL.fileURLWithPath(oldPath),
          null,
          NSFileManagerItemReplacementUsingNewMetadataOnly,
          null,
          err2
        );
        if (err2.value() !== null) {
          throw fsErrorForPath(oldPath, undefined, err2.value());
        }
      } else {
        throw fsErrorForPath(oldPath, undefined, error);
      }
    }
  };
  
  module.exports.rmdir = NOT_IMPLEMENTED("rmdir");
  
  module.exports.rmdirSync = function (path) {
    var err = MOPointer.alloc().init();
    var fileManager = NSFileManager.defaultManager();
    var isDirectory = module.exports.lstatSync(path).isDirectory();
    if (!isDirectory) {
      throw fsError("ENOTDIR", {
        path: path,
        syscall: "rmdir",
      });
    }
    fileManager.removeItemAtPath_error(path, err);
  
    if (err.value() !== null) {
      throw fsErrorForPath(path, true, err.value(), "rmdir");
    }
  };
  
  module.exports.stat = NOT_IMPLEMENTED("stat");
  
  // the only difference with lstat is that we resolve symlinks
  //
  // > lstat() is identical to stat(), except that if pathname is a symbolic
  // > link, then it returns information about the link itself, not the file
  // > that it refers to.
  // http://man7.org/linux/man-pages/man2/lstat.2.html
  module.exports.statSync = function (path) {
    return module.exports.lstatSync(module.exports.realpathSync(path));
  };
  
  module.exports.symlink = NOT_IMPLEMENTED("symlink");
  
  module.exports.symlinkSync = function (target, path) {
    var err = MOPointer.alloc().init();
    var fileManager = NSFileManager.defaultManager();
    var result = fileManager.createSymbolicLinkAtPath_withDestinationPath_error(
      path,
      target,
      err
    );
  
    if (err.value() !== null) {
      throw new Error(err.value());
    }
  };
  
  module.exports.truncate = NOT_IMPLEMENTED("truncate");
  
  module.exports.truncateSync = function (path, len) {
    var hFile = NSFileHandle.fileHandleForUpdatingAtPath(sFilePath);
    hFile.truncateFileAtOffset(len || 0);
    hFile.closeFile();
  };
  
  module.exports.unlink = NOT_IMPLEMENTED("unlink");
  
  module.exports.unlinkSync = function (path) {
    var err = MOPointer.alloc().init();
    var fileManager = NSFileManager.defaultManager();
    var isDirectory = module.exports.lstatSync(path).isDirectory();
    if (isDirectory) {
      throw fsError("EPERM", {
        path: path,
        syscall: "unlink",
      });
    }
    var result = fileManager.removeItemAtPath_error(path, err);
  
    if (err.value() !== null) {
      throw fsErrorForPath(path, false, err.value());
    }
  };
  
  module.exports.unwatchFile = NOT_IMPLEMENTED("unwatchFile");
  
  module.exports.utimes = NOT_IMPLEMENTED("utimes");
  
  module.exports.utimesSync = function (path, aTime, mTime) {
    var err = MOPointer.alloc().init();
    var fileManager = NSFileManager.defaultManager();
    var result = fileManager.setAttributes_ofItemAtPath_error(
      {
        NSFileModificationDate: aTime,
      },
      path,
      err
    );
  
    if (err.value() !== null) {
      throw fsErrorForPath(path, undefined, err.value());
    }
  };
  
  module.exports.watch = NOT_IMPLEMENTED("watch");
  module.exports.watchFile = NOT_IMPLEMENTED("watchFile");
  
  module.exports.write = NOT_IMPLEMENTED("write");
  
  module.exports.writeFile = NOT_IMPLEMENTED("writeFile");
  
  module.exports.writeFileSync = function (path, data, options) {
    var encoding = encodingFromOptions(options, "utf8");
  
    var nsdata = Buffer.from(
      data,
      encoding === "NSData" || encoding === "buffer" ? undefined : encoding
    ).toNSData();
  
    nsdata.writeToFile_atomically(path, true);
  };
  
  module.exports.writeSync = NOT_IMPLEMENTED("writeSync");
  