1const fs = require('fs')
2const { promisify } = require('util')
3const { readFileSync } = fs
4const readFile = promisify(fs.readFile)
5
6const extractPath = (path, cmdshimContents) => {
7  if (/[.]cmd$/.test(path)) {
8    return extractPathFromCmd(cmdshimContents)
9  } else if (/[.]ps1$/.test(path)) {
10    return extractPathFromPowershell(cmdshimContents)
11  } else {
12    return extractPathFromCygwin(cmdshimContents)
13  }
14}
15
16const extractPathFromPowershell = cmdshimContents => {
17  const matches = cmdshimContents.match(/"[$]basedir[/]([^"]+?)"\s+[$]args/)
18  return matches && matches[1]
19}
20
21const extractPathFromCmd = cmdshimContents => {
22  const matches = cmdshimContents.match(/"%(?:~dp0|dp0%)\\([^"]+?)"\s+%[*]/)
23  return matches && matches[1]
24}
25
26const extractPathFromCygwin = cmdshimContents => {
27  const matches = cmdshimContents.match(/"[$]basedir[/]([^"]+?)"\s+"[$]@"/)
28  return matches && matches[1]
29}
30
31const wrapError = (thrown, newError) => {
32  newError.message = thrown.message
33  newError.code = thrown.code
34  newError.path = thrown.path
35  return newError
36}
37
38const notaShim = (path, er) => {
39  if (!er) {
40    er = new Error()
41    Error.captureStackTrace(er, notaShim)
42  }
43  er.code = 'ENOTASHIM'
44  er.message = `Can't read shim path from '${path}', ` +
45    `it doesn't appear to be a cmd-shim`
46  return er
47}
48
49const readCmdShim = path => {
50  // create a new error to capture the stack trace from this point,
51  // instead of getting some opaque stack into node's internals
52  const er = new Error()
53  Error.captureStackTrace(er, readCmdShim)
54  return readFile(path).then(contents => {
55    const destination = extractPath(path, contents.toString())
56    if (destination) {
57      return destination
58    }
59    throw notaShim(path, er)
60  }, readFileEr => {
61    throw wrapError(readFileEr, er)
62  })
63}
64
65const readCmdShimSync = path => {
66  const contents = readFileSync(path)
67  const destination = extractPath(path, contents.toString())
68  if (!destination) {
69    throw notaShim(path)
70  }
71  return destination
72}
73
74readCmdShim.sync = readCmdShimSync
75module.exports = readCmdShim
76