11cb0ef41Sopenharmony_ci// On windows, create a .cmd file.
21cb0ef41Sopenharmony_ci// Read the #! in the file to see what it uses.  The vast majority
31cb0ef41Sopenharmony_ci// of the time, this will be either:
41cb0ef41Sopenharmony_ci// "#!/usr/bin/env <prog> <args...>"
51cb0ef41Sopenharmony_ci// or:
61cb0ef41Sopenharmony_ci// "#!<prog> <args...>"
71cb0ef41Sopenharmony_ci//
81cb0ef41Sopenharmony_ci// Write a binroot/pkg.bin + ".cmd" file that has this line in it:
91cb0ef41Sopenharmony_ci// @<prog> <args...> %dp0%<target> %*
101cb0ef41Sopenharmony_ci
111cb0ef41Sopenharmony_ciconst {
121cb0ef41Sopenharmony_ci  chmod,
131cb0ef41Sopenharmony_ci  mkdir,
141cb0ef41Sopenharmony_ci  readFile,
151cb0ef41Sopenharmony_ci  stat,
161cb0ef41Sopenharmony_ci  unlink,
171cb0ef41Sopenharmony_ci  writeFile,
181cb0ef41Sopenharmony_ci} = require('fs/promises')
191cb0ef41Sopenharmony_ci
201cb0ef41Sopenharmony_ciconst { dirname, relative } = require('path')
211cb0ef41Sopenharmony_ciconst toBatchSyntax = require('./to-batch-syntax')
221cb0ef41Sopenharmony_ci// linting disabled because this regex is really long
231cb0ef41Sopenharmony_ci// eslint-disable-next-line max-len
241cb0ef41Sopenharmony_ciconst shebangExpr = /^#!\s*(?:\/usr\/bin\/env\s+(?:-S\s+)?((?:[^ \t=]+=[^ \t=]+\s+)*))?([^ \t]+)(.*)$/
251cb0ef41Sopenharmony_ci
261cb0ef41Sopenharmony_ciconst cmdShimIfExists = (from, to) =>
271cb0ef41Sopenharmony_ci  stat(from).then(() => cmdShim(from, to), () => {})
281cb0ef41Sopenharmony_ci
291cb0ef41Sopenharmony_ci// Try to unlink, but ignore errors.
301cb0ef41Sopenharmony_ci// Any problems will surface later.
311cb0ef41Sopenharmony_ciconst rm = path => unlink(path).catch(() => {})
321cb0ef41Sopenharmony_ci
331cb0ef41Sopenharmony_ciconst cmdShim = (from, to) =>
341cb0ef41Sopenharmony_ci  stat(from).then(() => cmdShim_(from, to))
351cb0ef41Sopenharmony_ci
361cb0ef41Sopenharmony_ciconst cmdShim_ = (from, to) => Promise.all([
371cb0ef41Sopenharmony_ci  rm(to),
381cb0ef41Sopenharmony_ci  rm(to + '.cmd'),
391cb0ef41Sopenharmony_ci  rm(to + '.ps1'),
401cb0ef41Sopenharmony_ci]).then(() => writeShim(from, to))
411cb0ef41Sopenharmony_ci
421cb0ef41Sopenharmony_ciconst writeShim = (from, to) =>
431cb0ef41Sopenharmony_ci  // make a cmd file and a sh script
441cb0ef41Sopenharmony_ci  // First, check if the bin is a #! of some sort.
451cb0ef41Sopenharmony_ci  // If not, then assume it's something that'll be compiled, or some other
461cb0ef41Sopenharmony_ci  // sort of script, and just call it directly.
471cb0ef41Sopenharmony_ci  mkdir(dirname(to), { recursive: true })
481cb0ef41Sopenharmony_ci    .then(() => readFile(from, 'utf8'))
491cb0ef41Sopenharmony_ci    .then(data => {
501cb0ef41Sopenharmony_ci      const firstLine = data.trim().split(/\r*\n/)[0]
511cb0ef41Sopenharmony_ci      const shebang = firstLine.match(shebangExpr)
521cb0ef41Sopenharmony_ci      if (!shebang) {
531cb0ef41Sopenharmony_ci        return writeShim_(from, to)
541cb0ef41Sopenharmony_ci      }
551cb0ef41Sopenharmony_ci      const vars = shebang[1] || ''
561cb0ef41Sopenharmony_ci      const prog = shebang[2]
571cb0ef41Sopenharmony_ci      const args = shebang[3] || ''
581cb0ef41Sopenharmony_ci      return writeShim_(from, to, prog, args, vars)
591cb0ef41Sopenharmony_ci    }, er => writeShim_(from, to))
601cb0ef41Sopenharmony_ci
611cb0ef41Sopenharmony_ciconst writeShim_ = (from, to, prog, args, variables) => {
621cb0ef41Sopenharmony_ci  let shTarget = relative(dirname(to), from)
631cb0ef41Sopenharmony_ci  let target = shTarget.split('/').join('\\')
641cb0ef41Sopenharmony_ci  let longProg
651cb0ef41Sopenharmony_ci  let shProg = prog && prog.split('\\').join('/')
661cb0ef41Sopenharmony_ci  let shLongProg
671cb0ef41Sopenharmony_ci  let pwshProg = shProg && `"${shProg}$exe"`
681cb0ef41Sopenharmony_ci  let pwshLongProg
691cb0ef41Sopenharmony_ci  shTarget = shTarget.split('\\').join('/')
701cb0ef41Sopenharmony_ci  args = args || ''
711cb0ef41Sopenharmony_ci  variables = variables || ''
721cb0ef41Sopenharmony_ci  if (!prog) {
731cb0ef41Sopenharmony_ci    prog = `"%dp0%\\${target}"`
741cb0ef41Sopenharmony_ci    shProg = `"$basedir/${shTarget}"`
751cb0ef41Sopenharmony_ci    pwshProg = shProg
761cb0ef41Sopenharmony_ci    args = ''
771cb0ef41Sopenharmony_ci    target = ''
781cb0ef41Sopenharmony_ci    shTarget = ''
791cb0ef41Sopenharmony_ci  } else {
801cb0ef41Sopenharmony_ci    longProg = `"%dp0%\\${prog}.exe"`
811cb0ef41Sopenharmony_ci    shLongProg = `"$basedir/${prog}"`
821cb0ef41Sopenharmony_ci    pwshLongProg = `"$basedir/${prog}$exe"`
831cb0ef41Sopenharmony_ci    target = `"%dp0%\\${target}"`
841cb0ef41Sopenharmony_ci    shTarget = `"$basedir/${shTarget}"`
851cb0ef41Sopenharmony_ci  }
861cb0ef41Sopenharmony_ci
871cb0ef41Sopenharmony_ci  // Subroutine trick to fix https://github.com/npm/cmd-shim/issues/10
881cb0ef41Sopenharmony_ci  // and https://github.com/npm/cli/issues/969
891cb0ef41Sopenharmony_ci  const head = '@ECHO off\r\n' +
901cb0ef41Sopenharmony_ci    'GOTO start\r\n' +
911cb0ef41Sopenharmony_ci    ':find_dp0\r\n' +
921cb0ef41Sopenharmony_ci    'SET dp0=%~dp0\r\n' +
931cb0ef41Sopenharmony_ci    'EXIT /b\r\n' +
941cb0ef41Sopenharmony_ci    ':start\r\n' +
951cb0ef41Sopenharmony_ci    'SETLOCAL\r\n' +
961cb0ef41Sopenharmony_ci    'CALL :find_dp0\r\n'
971cb0ef41Sopenharmony_ci
981cb0ef41Sopenharmony_ci  let cmd
991cb0ef41Sopenharmony_ci  if (longProg) {
1001cb0ef41Sopenharmony_ci    shLongProg = shLongProg.trim()
1011cb0ef41Sopenharmony_ci    args = args.trim()
1021cb0ef41Sopenharmony_ci    const variablesBatch = toBatchSyntax.convertToSetCommands(variables)
1031cb0ef41Sopenharmony_ci    cmd = head
1041cb0ef41Sopenharmony_ci        + variablesBatch
1051cb0ef41Sopenharmony_ci        + '\r\n'
1061cb0ef41Sopenharmony_ci        + `IF EXIST ${longProg} (\r\n`
1071cb0ef41Sopenharmony_ci        + `  SET "_prog=${longProg.replace(/(^")|("$)/g, '')}"\r\n`
1081cb0ef41Sopenharmony_ci        + ') ELSE (\r\n'
1091cb0ef41Sopenharmony_ci        + `  SET "_prog=${prog.replace(/(^")|("$)/g, '')}"\r\n`
1101cb0ef41Sopenharmony_ci        + '  SET PATHEXT=%PATHEXT:;.JS;=;%\r\n'
1111cb0ef41Sopenharmony_ci        + ')\r\n'
1121cb0ef41Sopenharmony_ci        + '\r\n'
1131cb0ef41Sopenharmony_ci        // prevent "Terminate Batch Job? (Y/n)" message
1141cb0ef41Sopenharmony_ci        // https://github.com/npm/cli/issues/969#issuecomment-737496588
1151cb0ef41Sopenharmony_ci        + 'endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & '
1161cb0ef41Sopenharmony_ci        + `"%_prog%" ${args} ${target} %*\r\n`
1171cb0ef41Sopenharmony_ci  } else {
1181cb0ef41Sopenharmony_ci    cmd = `${head}${prog} ${args} ${target} %*\r\n`
1191cb0ef41Sopenharmony_ci  }
1201cb0ef41Sopenharmony_ci
1211cb0ef41Sopenharmony_ci  // #!/bin/sh
1221cb0ef41Sopenharmony_ci  // basedir=`dirname "$0"`
1231cb0ef41Sopenharmony_ci  //
1241cb0ef41Sopenharmony_ci  // case `uname` in
1251cb0ef41Sopenharmony_ci  //     *CYGWIN*|*MINGW*|*MSYS*)
1261cb0ef41Sopenharmony_ci  //       if command -v cygpath > /dev/null 2>&1; then
1271cb0ef41Sopenharmony_ci  //           basedir=`cygpath -w "$basedir"`
1281cb0ef41Sopenharmony_ci  //       fi
1291cb0ef41Sopenharmony_ci  //     ;;
1301cb0ef41Sopenharmony_ci  // esac
1311cb0ef41Sopenharmony_ci  //
1321cb0ef41Sopenharmony_ci  // if [ -x "$basedir/node.exe" ]; then
1331cb0ef41Sopenharmony_ci  //   exec "$basedir/node.exe" "$basedir/node_modules/npm/bin/npm-cli.js" "$@"
1341cb0ef41Sopenharmony_ci  // else
1351cb0ef41Sopenharmony_ci  //   exec node "$basedir/node_modules/npm/bin/npm-cli.js" "$@"
1361cb0ef41Sopenharmony_ci  // fi
1371cb0ef41Sopenharmony_ci
1381cb0ef41Sopenharmony_ci  let sh = '#!/bin/sh\n'
1391cb0ef41Sopenharmony_ci
1401cb0ef41Sopenharmony_ci  sh = sh
1411cb0ef41Sopenharmony_ci      + `basedir=$(dirname "$(echo "$0" | sed -e 's,\\\\,/,g')")\n`
1421cb0ef41Sopenharmony_ci      + '\n'
1431cb0ef41Sopenharmony_ci      + 'case `uname` in\n'
1441cb0ef41Sopenharmony_ci      + '    *CYGWIN*|*MINGW*|*MSYS*)\n'
1451cb0ef41Sopenharmony_ci      + '        if command -v cygpath > /dev/null 2>&1; then\n'
1461cb0ef41Sopenharmony_ci      + '            basedir=`cygpath -w "$basedir"`\n'
1471cb0ef41Sopenharmony_ci      + '        fi\n'
1481cb0ef41Sopenharmony_ci      + '    ;;\n'
1491cb0ef41Sopenharmony_ci      + 'esac\n'
1501cb0ef41Sopenharmony_ci      + '\n'
1511cb0ef41Sopenharmony_ci
1521cb0ef41Sopenharmony_ci  if (shLongProg) {
1531cb0ef41Sopenharmony_ci    sh = sh
1541cb0ef41Sopenharmony_ci       + `if [ -x ${shLongProg} ]; then\n`
1551cb0ef41Sopenharmony_ci       + `  exec ${variables}${shLongProg} ${args} ${shTarget} "$@"\n`
1561cb0ef41Sopenharmony_ci       + 'else \n'
1571cb0ef41Sopenharmony_ci       + `  exec ${variables}${shProg} ${args} ${shTarget} "$@"\n`
1581cb0ef41Sopenharmony_ci       + 'fi\n'
1591cb0ef41Sopenharmony_ci  } else {
1601cb0ef41Sopenharmony_ci    sh = sh
1611cb0ef41Sopenharmony_ci       + `exec ${shProg} ${args} ${shTarget} "$@"\n`
1621cb0ef41Sopenharmony_ci  }
1631cb0ef41Sopenharmony_ci
1641cb0ef41Sopenharmony_ci  // #!/usr/bin/env pwsh
1651cb0ef41Sopenharmony_ci  // $basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
1661cb0ef41Sopenharmony_ci  //
1671cb0ef41Sopenharmony_ci  // $ret=0
1681cb0ef41Sopenharmony_ci  // $exe = ""
1691cb0ef41Sopenharmony_ci  // if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
1701cb0ef41Sopenharmony_ci  //   # Fix case when both the Windows and Linux builds of Node
1711cb0ef41Sopenharmony_ci  //   # are installed in the same directory
1721cb0ef41Sopenharmony_ci  //   $exe = ".exe"
1731cb0ef41Sopenharmony_ci  // }
1741cb0ef41Sopenharmony_ci  // if (Test-Path "$basedir/node") {
1751cb0ef41Sopenharmony_ci  //   # Suport pipeline input
1761cb0ef41Sopenharmony_ci  //   if ($MyInvocation.ExpectingInput) {
1771cb0ef41Sopenharmony_ci  //     input | & "$basedir/node$exe" "$basedir/node_modules/npm/bin/npm-cli.js" $args
1781cb0ef41Sopenharmony_ci  //   } else {
1791cb0ef41Sopenharmony_ci  //     & "$basedir/node$exe" "$basedir/node_modules/npm/bin/npm-cli.js" $args
1801cb0ef41Sopenharmony_ci  //   }
1811cb0ef41Sopenharmony_ci  //   $ret=$LASTEXITCODE
1821cb0ef41Sopenharmony_ci  // } else {
1831cb0ef41Sopenharmony_ci  //   # Support pipeline input
1841cb0ef41Sopenharmony_ci  //   if ($MyInvocation.ExpectingInput) {
1851cb0ef41Sopenharmony_ci  //     $input | & "node$exe" "$basedir/node_modules/npm/bin/npm-cli.js" $args
1861cb0ef41Sopenharmony_ci  //   } else {
1871cb0ef41Sopenharmony_ci  //     & "node$exe" "$basedir/node_modules/npm/bin/npm-cli.js" $args
1881cb0ef41Sopenharmony_ci  //   }
1891cb0ef41Sopenharmony_ci  //   $ret=$LASTEXITCODE
1901cb0ef41Sopenharmony_ci  // }
1911cb0ef41Sopenharmony_ci  // exit $ret
1921cb0ef41Sopenharmony_ci  let pwsh = '#!/usr/bin/env pwsh\n'
1931cb0ef41Sopenharmony_ci           + '$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent\n'
1941cb0ef41Sopenharmony_ci           + '\n'
1951cb0ef41Sopenharmony_ci           + '$exe=""\n'
1961cb0ef41Sopenharmony_ci           + 'if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {\n'
1971cb0ef41Sopenharmony_ci           + '  # Fix case when both the Windows and Linux builds of Node\n'
1981cb0ef41Sopenharmony_ci           + '  # are installed in the same directory\n'
1991cb0ef41Sopenharmony_ci           + '  $exe=".exe"\n'
2001cb0ef41Sopenharmony_ci           + '}\n'
2011cb0ef41Sopenharmony_ci  if (shLongProg) {
2021cb0ef41Sopenharmony_ci    pwsh = pwsh
2031cb0ef41Sopenharmony_ci         + '$ret=0\n'
2041cb0ef41Sopenharmony_ci         + `if (Test-Path ${pwshLongProg}) {\n`
2051cb0ef41Sopenharmony_ci         + '  # Support pipeline input\n'
2061cb0ef41Sopenharmony_ci         + '  if ($MyInvocation.ExpectingInput) {\n'
2071cb0ef41Sopenharmony_ci         + `    $input | & ${pwshLongProg} ${args} ${shTarget} $args\n`
2081cb0ef41Sopenharmony_ci         + '  } else {\n'
2091cb0ef41Sopenharmony_ci         + `    & ${pwshLongProg} ${args} ${shTarget} $args\n`
2101cb0ef41Sopenharmony_ci         + '  }\n'
2111cb0ef41Sopenharmony_ci         + '  $ret=$LASTEXITCODE\n'
2121cb0ef41Sopenharmony_ci         + '} else {\n'
2131cb0ef41Sopenharmony_ci         + '  # Support pipeline input\n'
2141cb0ef41Sopenharmony_ci         + '  if ($MyInvocation.ExpectingInput) {\n'
2151cb0ef41Sopenharmony_ci         + `    $input | & ${pwshProg} ${args} ${shTarget} $args\n`
2161cb0ef41Sopenharmony_ci         + '  } else {\n'
2171cb0ef41Sopenharmony_ci         + `    & ${pwshProg} ${args} ${shTarget} $args\n`
2181cb0ef41Sopenharmony_ci         + '  }\n'
2191cb0ef41Sopenharmony_ci         + '  $ret=$LASTEXITCODE\n'
2201cb0ef41Sopenharmony_ci         + '}\n'
2211cb0ef41Sopenharmony_ci         + 'exit $ret\n'
2221cb0ef41Sopenharmony_ci  } else {
2231cb0ef41Sopenharmony_ci    pwsh = pwsh
2241cb0ef41Sopenharmony_ci         + '# Support pipeline input\n'
2251cb0ef41Sopenharmony_ci         + 'if ($MyInvocation.ExpectingInput) {\n'
2261cb0ef41Sopenharmony_ci         + `  $input | & ${pwshProg} ${args} ${shTarget} $args\n`
2271cb0ef41Sopenharmony_ci         + '} else {\n'
2281cb0ef41Sopenharmony_ci         + `  & ${pwshProg} ${args} ${shTarget} $args\n`
2291cb0ef41Sopenharmony_ci         + '}\n'
2301cb0ef41Sopenharmony_ci         + 'exit $LASTEXITCODE\n'
2311cb0ef41Sopenharmony_ci  }
2321cb0ef41Sopenharmony_ci
2331cb0ef41Sopenharmony_ci  return Promise.all([
2341cb0ef41Sopenharmony_ci    writeFile(to + '.ps1', pwsh, 'utf8'),
2351cb0ef41Sopenharmony_ci    writeFile(to + '.cmd', cmd, 'utf8'),
2361cb0ef41Sopenharmony_ci    writeFile(to, sh, 'utf8'),
2371cb0ef41Sopenharmony_ci  ]).then(() => chmodShim(to))
2381cb0ef41Sopenharmony_ci}
2391cb0ef41Sopenharmony_ci
2401cb0ef41Sopenharmony_ciconst chmodShim = to => Promise.all([
2411cb0ef41Sopenharmony_ci  chmod(to, 0o755),
2421cb0ef41Sopenharmony_ci  chmod(to + '.cmd', 0o755),
2431cb0ef41Sopenharmony_ci  chmod(to + '.ps1', 0o755),
2441cb0ef41Sopenharmony_ci])
2451cb0ef41Sopenharmony_ci
2461cb0ef41Sopenharmony_cimodule.exports = cmdShim
2471cb0ef41Sopenharmony_cicmdShim.ifExists = cmdShimIfExists
248