which.js 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. module.exports = which
  2. which.sync = whichSync
  3. var path = require("path")
  4. , fs
  5. , COLON = process.platform === "win32" ? ";" : ":"
  6. , isExe
  7. , fs = require("fs")
  8. if (process.platform == "win32") {
  9. // On windows, there is no good way to check that a file is executable
  10. isExe = function isExe () { return true }
  11. } else {
  12. isExe = function isExe (mod, uid, gid) {
  13. //console.error(mod, uid, gid);
  14. //console.error("isExe?", (mod & 0111).toString(8))
  15. var ret = (mod & 0001)
  16. || (mod & 0010) && process.getgid && gid === process.getgid()
  17. || (mod & 0010) && process.getuid && 0 === process.getuid()
  18. || (mod & 0100) && process.getuid && uid === process.getuid()
  19. || (mod & 0100) && process.getuid && 0 === process.getuid()
  20. //console.error("isExe?", ret)
  21. return ret
  22. }
  23. }
  24. function which (cmd, cb) {
  25. if (isAbsolute(cmd)) return cb(null, cmd)
  26. var pathEnv = (process.env.PATH || "").split(COLON)
  27. , pathExt = [""]
  28. if (process.platform === "win32") {
  29. pathEnv.push(process.cwd())
  30. pathExt = (process.env.PATHEXT || ".EXE").split(COLON)
  31. if (cmd.indexOf(".") !== -1) pathExt.unshift("")
  32. }
  33. //console.error("pathEnv", pathEnv)
  34. ;(function F (i, l) {
  35. if (i === l) return cb(new Error("not found: "+cmd))
  36. var p = path.resolve(pathEnv[i], cmd)
  37. ;(function E (ii, ll) {
  38. if (ii === ll) return F(i + 1, l)
  39. var ext = pathExt[ii]
  40. //console.error(p + ext)
  41. fs.stat(p + ext, function (er, stat) {
  42. if (!er &&
  43. stat &&
  44. stat.isFile() &&
  45. isExe(stat.mode, stat.uid, stat.gid)) {
  46. //console.error("yes, exe!", p + ext)
  47. return cb(null, p + ext)
  48. }
  49. return E(ii + 1, ll)
  50. })
  51. })(0, pathExt.length)
  52. })(0, pathEnv.length)
  53. }
  54. function whichSync (cmd) {
  55. if (isAbsolute(cmd)) return cmd
  56. var pathEnv = (process.env.PATH || "").split(COLON)
  57. , pathExt = [""]
  58. if (process.platform === "win32") {
  59. pathEnv.push(process.cwd())
  60. pathExt = (process.env.PATHEXT || ".EXE").split(COLON)
  61. if (cmd.indexOf(".") !== -1) pathExt.unshift("")
  62. }
  63. for (var i = 0, l = pathEnv.length; i < l; i ++) {
  64. var p = path.join(pathEnv[i], cmd)
  65. for (var j = 0, ll = pathExt.length; j < ll; j ++) {
  66. var cur = p + pathExt[j]
  67. var stat
  68. try { stat = fs.statSync(cur) } catch (ex) {}
  69. if (stat &&
  70. stat.isFile() &&
  71. isExe(stat.mode, stat.uid, stat.gid)) return cur
  72. }
  73. }
  74. throw new Error("not found: "+cmd)
  75. }
  76. var isAbsolute = process.platform === "win32" ? absWin : absUnix
  77. function absWin (p) {
  78. if (absUnix(p)) return true
  79. // pull off the device/UNC bit from a windows path.
  80. // from node's lib/path.js
  81. var splitDeviceRe =
  82. /^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/][^\\\/]+)?([\\\/])?/
  83. , result = splitDeviceRe.exec(p)
  84. , device = result[1] || ''
  85. , isUnc = device && device.charAt(1) !== ':'
  86. , isAbsolute = !!result[2] || isUnc // UNC paths are always absolute
  87. return isAbsolute
  88. }
  89. function absUnix (p) {
  90. return p.charAt(0) === "/" || p === ""
  91. }