• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 = require('isexe')
11
12function getNotFoundError (cmd) {
13  var er = new Error('not found: ' + cmd)
14  er.code = 'ENOENT'
15
16  return er
17}
18
19function getPathInfo (cmd, opt) {
20  var colon = opt.colon || COLON
21  var pathEnv = opt.path || process.env.PATH || ''
22  var pathExt = ['']
23
24  pathEnv = pathEnv.split(colon)
25
26  var pathExtExe = ''
27  if (isWindows) {
28    pathEnv.unshift(process.cwd())
29    pathExtExe = (opt.pathExt || process.env.PATHEXT || '.EXE;.CMD;.BAT;.COM')
30    pathExt = pathExtExe.split(colon)
31
32
33    // Always test the cmd itself first.  isexe will check to make sure
34    // it's found in the pathExt set.
35    if (cmd.indexOf('.') !== -1 && pathExt[0] !== '')
36      pathExt.unshift('')
37  }
38
39  // If it has a slash, then we don't bother searching the pathenv.
40  // just check the file itself, and that's it.
41  if (cmd.match(/\//) || isWindows && cmd.match(/\\/))
42    pathEnv = ['']
43
44  return {
45    env: pathEnv,
46    ext: pathExt,
47    extExe: pathExtExe
48  }
49}
50
51function which (cmd, opt, cb) {
52  if (typeof opt === 'function') {
53    cb = opt
54    opt = {}
55  }
56
57  var info = getPathInfo(cmd, opt)
58  var pathEnv = info.env
59  var pathExt = info.ext
60  var pathExtExe = info.extExe
61  var found = []
62
63  ;(function F (i, l) {
64    if (i === l) {
65      if (opt.all && found.length)
66        return cb(null, found)
67      else
68        return cb(getNotFoundError(cmd))
69    }
70
71    var pathPart = pathEnv[i]
72    if (pathPart.charAt(0) === '"' && pathPart.slice(-1) === '"')
73      pathPart = pathPart.slice(1, -1)
74
75    var p = path.join(pathPart, cmd)
76    if (!pathPart && (/^\.[\\\/]/).test(cmd)) {
77      p = cmd.slice(0, 2) + p
78    }
79    ;(function E (ii, ll) {
80      if (ii === ll) return F(i + 1, l)
81      var ext = pathExt[ii]
82      isexe(p + ext, { pathExt: pathExtExe }, function (er, is) {
83        if (!er && is) {
84          if (opt.all)
85            found.push(p + ext)
86          else
87            return cb(null, p + ext)
88        }
89        return E(ii + 1, ll)
90      })
91    })(0, pathExt.length)
92  })(0, pathEnv.length)
93}
94
95function whichSync (cmd, opt) {
96  opt = opt || {}
97
98  var info = getPathInfo(cmd, opt)
99  var pathEnv = info.env
100  var pathExt = info.ext
101  var pathExtExe = info.extExe
102  var found = []
103
104  for (var i = 0, l = pathEnv.length; i < l; i ++) {
105    var pathPart = pathEnv[i]
106    if (pathPart.charAt(0) === '"' && pathPart.slice(-1) === '"')
107      pathPart = pathPart.slice(1, -1)
108
109    var p = path.join(pathPart, cmd)
110    if (!pathPart && /^\.[\\\/]/.test(cmd)) {
111      p = cmd.slice(0, 2) + p
112    }
113    for (var j = 0, ll = pathExt.length; j < ll; j ++) {
114      var cur = p + pathExt[j]
115      var is
116      try {
117        is = isexe.sync(cur, { pathExt: pathExtExe })
118        if (is) {
119          if (opt.all)
120            found.push(cur)
121          else
122            return cur
123        }
124      } catch (ex) {}
125    }
126  }
127
128  if (opt.all && found.length)
129    return found
130
131  if (opt.nothrow)
132    return null
133
134  throw getNotFoundError(cmd)
135}
136