UNPKG

3.44 kBJavaScriptView Raw
1module.exports = which
2which.sync = whichSync
3
4var isWindows = process.platform === 'win32' ||
5 process.env.OSTYPE === 'cygwin' ||
6 process.env.OSTYPE === 'msys'
7
8var path = require('path')
9var COLON = isWindows ? ';' : ':'
10var isExe
11var fs = require('fs')
12var isAbsolute = require('is-absolute')
13
14var G = parseInt('0010', 8)
15var U = parseInt('0100', 8)
16var UG = parseInt('0110', 8)
17
18if (isWindows) {
19 // On windows, there is no good way to check that a file is executable
20 isExe = function isExe () { return true }
21} else {
22 isExe = function isExe (mod, uid, gid) {
23 var ret = (mod & 1)
24 || (mod & G) && process.getgid && gid === process.getgid()
25 || (mod & U) && process.getuid && uid === process.getuid()
26 || (mod & UG) && process.getuid && 0 === process.getuid()
27
28 if (!ret && process.getgroups && (mod & G)) {
29 var groups = process.getgroups()
30 for (var g = 0; g < groups.length; g++) {
31 if (groups[g] === gid)
32 return true
33 }
34 }
35
36 return ret
37 }
38}
39
40function getPathInfo(cmd, opt) {
41 var colon = opt.colon || COLON
42 var pathEnv = opt.path || process.env.PATH || ''
43 var pathExt = ['']
44
45 pathEnv = pathEnv.split(colon)
46
47 if (isWindows) {
48 pathEnv.unshift(process.cwd())
49 pathExt = (opt.pathExt || process.env.PATHEXT || '.EXE').split(colon)
50 if (cmd.indexOf('.') !== -1 && pathExt[0] !== '')
51 pathExt.unshift('')
52 }
53
54 // If it's absolute, then we don't bother searching the pathenv.
55 // just check the file itself, and that's it.
56 if (isAbsolute(cmd))
57 pathEnv = ['']
58
59 return {env: pathEnv, ext: pathExt}
60}
61
62function which (cmd, opt, cb) {
63 if (typeof opt === 'function') {
64 cb = opt
65 opt = {}
66 }
67
68 var info = getPathInfo(cmd, opt)
69 var pathEnv = info.env
70 var pathExt = info.ext
71 var found = []
72
73 ;(function F (i, l) {
74 if (i === l) {
75 if (opt.all && found.length)
76 return cb(null, found)
77 else
78 return cb(new Error('not found: '+cmd))
79 }
80
81 var pathPart = pathEnv[i]
82 if (pathPart.charAt(0) === '"' && pathPart.slice(-1) === '"')
83 pathPart = pathPart.slice(1, -1)
84
85 var p = path.resolve(pathPart, cmd)
86 ;(function E (ii, ll) {
87 if (ii === ll) return F(i + 1, l)
88 var ext = pathExt[ii]
89 fs.stat(p + ext, function (er, stat) {
90 if (!er &&
91 stat.isFile() &&
92 isExe(stat.mode, stat.uid, stat.gid)) {
93 if (opt.all)
94 found.push(p + ext)
95 else
96 return cb(null, p + ext)
97 }
98 return E(ii + 1, ll)
99 })
100 })(0, pathExt.length)
101 })(0, pathEnv.length)
102}
103
104function whichSync (cmd, opt) {
105 opt = opt || {}
106
107 var info = getPathInfo(cmd, opt)
108 var pathEnv = info.env
109 var pathExt = info.ext
110 var found = []
111
112 for (var i = 0, l = pathEnv.length; i < l; i ++) {
113 var pathPart = pathEnv[i]
114 if (pathPart.charAt(0) === '"' && pathPart.slice(-1) === '"')
115 pathPart = pathPart.slice(1, -1)
116
117 var p = path.join(pathPart, cmd)
118 for (var j = 0, ll = pathExt.length; j < ll; j ++) {
119 var cur = p + pathExt[j]
120 var stat
121 try {
122 stat = fs.statSync(cur)
123 if (stat.isFile() && isExe(stat.mode, stat.uid, stat.gid)) {
124 if (opt.all)
125 found.push(cur)
126 else
127 return cur
128 }
129 } catch (ex) {}
130 }
131 }
132
133 if (opt.all && found.length)
134 return found
135
136 throw new Error('not found: '+cmd)
137}