1 | module.exports = which
|
2 | which.sync = whichSync
|
3 |
|
4 | var isWindows = process.platform === 'win32' ||
|
5 | process.env.OSTYPE === 'cygwin' ||
|
6 | process.env.OSTYPE === 'msys'
|
7 |
|
8 | var path = require('path')
|
9 | var COLON = isWindows ? ';' : ':'
|
10 | var isExe
|
11 | var fs = require('fs')
|
12 | var isAbsolute = require('is-absolute')
|
13 |
|
14 | var G = parseInt('0010', 8)
|
15 | var U = parseInt('0100', 8)
|
16 | var UG = parseInt('0110', 8)
|
17 |
|
18 | if (isWindows) {
|
19 |
|
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 |
|
40 | function 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 |
|
55 |
|
56 | if (isAbsolute(cmd))
|
57 | pathEnv = ['']
|
58 |
|
59 | return {env: pathEnv, ext: pathExt}
|
60 | }
|
61 |
|
62 | function 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 |
|
104 | function 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 | }
|