| 'use strict' | 
| const fs = require('fs') | 
| const path = require('path') | 
|   | 
| /* istanbul ignore next */ | 
| const LCHOWN = fs.lchown ? 'lchown' : 'chown' | 
| /* istanbul ignore next */ | 
| const LCHOWNSYNC = fs.lchownSync ? 'lchownSync' : 'chownSync' | 
|   | 
| /* istanbul ignore next */ | 
| const needEISDIRHandled = fs.lchown && | 
|   !process.version.match(/v1[1-9]+\./) && | 
|   !process.version.match(/v10\.[6-9]/) | 
|   | 
| const lchownSync = (path, uid, gid) => { | 
|   try { | 
|     return fs[LCHOWNSYNC](path, uid, gid) | 
|   } catch (er) { | 
|     if (er.code !== 'ENOENT') | 
|       throw er | 
|   } | 
| } | 
|   | 
| /* istanbul ignore next */ | 
| const chownSync = (path, uid, gid) => { | 
|   try { | 
|     return fs.chownSync(path, uid, gid) | 
|   } catch (er) { | 
|     if (er.code !== 'ENOENT') | 
|       throw er | 
|   } | 
| } | 
|   | 
| /* istanbul ignore next */ | 
| const handleEISDIR = | 
|   needEISDIRHandled ? (path, uid, gid, cb) => er => { | 
|     // Node prior to v10 had a very questionable implementation of | 
|     // fs.lchown, which would always try to call fs.open on a directory | 
|     // Fall back to fs.chown in those cases. | 
|     if (!er || er.code !== 'EISDIR') | 
|       cb(er) | 
|     else | 
|       fs.chown(path, uid, gid, cb) | 
|   } | 
|   : (_, __, ___, cb) => cb | 
|   | 
| /* istanbul ignore next */ | 
| const handleEISDirSync = | 
|   needEISDIRHandled ? (path, uid, gid) => { | 
|     try { | 
|       return lchownSync(path, uid, gid) | 
|     } catch (er) { | 
|       if (er.code !== 'EISDIR') | 
|         throw er | 
|       chownSync(path, uid, gid) | 
|     } | 
|   } | 
|   : (path, uid, gid) => lchownSync(path, uid, gid) | 
|   | 
| // fs.readdir could only accept an options object as of node v6 | 
| const nodeVersion = process.version | 
| let readdir = (path, options, cb) => fs.readdir(path, options, cb) | 
| let readdirSync = (path, options) => fs.readdirSync(path, options) | 
| /* istanbul ignore next */ | 
| if (/^v4\./.test(nodeVersion)) | 
|   readdir = (path, options, cb) => fs.readdir(path, cb) | 
|   | 
| const chown = (cpath, uid, gid, cb) => { | 
|   fs[LCHOWN](cpath, uid, gid, handleEISDIR(cpath, uid, gid, er => { | 
|     // Skip ENOENT error | 
|     cb(er && er.code !== 'ENOENT' ? er : null) | 
|   })) | 
| } | 
|   | 
| const chownrKid = (p, child, uid, gid, cb) => { | 
|   if (typeof child === 'string') | 
|     return fs.lstat(path.resolve(p, child), (er, stats) => { | 
|       // Skip ENOENT error | 
|       if (er) | 
|         return cb(er.code !== 'ENOENT' ? er : null) | 
|       stats.name = child | 
|       chownrKid(p, stats, uid, gid, cb) | 
|     }) | 
|   | 
|   if (child.isDirectory()) { | 
|     chownr(path.resolve(p, child.name), uid, gid, er => { | 
|       if (er) | 
|         return cb(er) | 
|       const cpath = path.resolve(p, child.name) | 
|       chown(cpath, uid, gid, cb) | 
|     }) | 
|   } else { | 
|     const cpath = path.resolve(p, child.name) | 
|     chown(cpath, uid, gid, cb) | 
|   } | 
| } | 
|   | 
|   | 
| const chownr = (p, uid, gid, cb) => { | 
|   readdir(p, { withFileTypes: true }, (er, children) => { | 
|     // any error other than ENOTDIR or ENOTSUP means it's not readable, | 
|     // or doesn't exist.  give up. | 
|     if (er) { | 
|       if (er.code === 'ENOENT') | 
|         return cb() | 
|       else if (er.code !== 'ENOTDIR' && er.code !== 'ENOTSUP') | 
|         return cb(er) | 
|     } | 
|     if (er || !children.length) | 
|       return chown(p, uid, gid, cb) | 
|   | 
|     let len = children.length | 
|     let errState = null | 
|     const then = er => { | 
|       if (errState) | 
|         return | 
|       if (er) | 
|         return cb(errState = er) | 
|       if (-- len === 0) | 
|         return chown(p, uid, gid, cb) | 
|     } | 
|   | 
|     children.forEach(child => chownrKid(p, child, uid, gid, then)) | 
|   }) | 
| } | 
|   | 
| const chownrKidSync = (p, child, uid, gid) => { | 
|   if (typeof child === 'string') { | 
|     try { | 
|       const stats = fs.lstatSync(path.resolve(p, child)) | 
|       stats.name = child | 
|       child = stats | 
|     } catch (er) { | 
|       if (er.code === 'ENOENT') | 
|         return | 
|       else | 
|         throw er | 
|     } | 
|   } | 
|   | 
|   if (child.isDirectory()) | 
|     chownrSync(path.resolve(p, child.name), uid, gid) | 
|   | 
|   handleEISDirSync(path.resolve(p, child.name), uid, gid) | 
| } | 
|   | 
| const chownrSync = (p, uid, gid) => { | 
|   let children | 
|   try { | 
|     children = readdirSync(p, { withFileTypes: true }) | 
|   } catch (er) { | 
|     if (er.code === 'ENOENT') | 
|       return | 
|     else if (er.code === 'ENOTDIR' || er.code === 'ENOTSUP') | 
|       return handleEISDirSync(p, uid, gid) | 
|     else | 
|       throw er | 
|   } | 
|   | 
|   if (children && children.length) | 
|     children.forEach(child => chownrKidSync(p, child, uid, gid)) | 
|   | 
|   return handleEISDirSync(p, uid, gid) | 
| } | 
|   | 
| module.exports = chownr | 
| chownr.sync = chownrSync |