11cb0ef41Sopenharmony_ci// check to see if a bin is allowed to be overwritten
21cb0ef41Sopenharmony_ci// either rejects or resolves to nothing.  return value not relevant.
31cb0ef41Sopenharmony_ciconst isWindows = require('./is-windows.js')
41cb0ef41Sopenharmony_ciconst binTarget = require('./bin-target.js')
51cb0ef41Sopenharmony_ciconst { resolve, dirname } = require('path')
61cb0ef41Sopenharmony_ciconst readCmdShim = require('read-cmd-shim')
71cb0ef41Sopenharmony_ciconst { readlink } = require('fs/promises')
81cb0ef41Sopenharmony_ci
91cb0ef41Sopenharmony_ciconst checkBin = async ({ bin, path, top, global, force }) => {
101cb0ef41Sopenharmony_ci  // always ok to clobber when forced
111cb0ef41Sopenharmony_ci  // always ok to clobber local bins, or when forced
121cb0ef41Sopenharmony_ci  if (force || !global || !top) {
131cb0ef41Sopenharmony_ci    return
141cb0ef41Sopenharmony_ci  }
151cb0ef41Sopenharmony_ci
161cb0ef41Sopenharmony_ci  // ok, need to make sure, then
171cb0ef41Sopenharmony_ci  const target = resolve(binTarget({ path, top }), bin)
181cb0ef41Sopenharmony_ci  path = resolve(path)
191cb0ef41Sopenharmony_ci  return isWindows ? checkShim({ target, path }) : checkLink({ target, path })
201cb0ef41Sopenharmony_ci}
211cb0ef41Sopenharmony_ci
221cb0ef41Sopenharmony_ci// only enoent is allowed.  anything else is a problem.
231cb0ef41Sopenharmony_ciconst handleReadLinkError = async ({ er, target }) =>
241cb0ef41Sopenharmony_ci  er.code === 'ENOENT' ? null
251cb0ef41Sopenharmony_ci  : failEEXIST({ target })
261cb0ef41Sopenharmony_ci
271cb0ef41Sopenharmony_ciconst checkLink = async ({ target, path }) => {
281cb0ef41Sopenharmony_ci  const current = await readlink(target)
291cb0ef41Sopenharmony_ci    .catch(er => handleReadLinkError({ er, target }))
301cb0ef41Sopenharmony_ci
311cb0ef41Sopenharmony_ci  if (!current) {
321cb0ef41Sopenharmony_ci    return
331cb0ef41Sopenharmony_ci  }
341cb0ef41Sopenharmony_ci
351cb0ef41Sopenharmony_ci  const resolved = resolve(dirname(target), current)
361cb0ef41Sopenharmony_ci
371cb0ef41Sopenharmony_ci  if (resolved.toLowerCase().indexOf(path.toLowerCase()) !== 0) {
381cb0ef41Sopenharmony_ci    return failEEXIST({ target })
391cb0ef41Sopenharmony_ci  }
401cb0ef41Sopenharmony_ci}
411cb0ef41Sopenharmony_ci
421cb0ef41Sopenharmony_ciconst handleReadCmdShimError = ({ er, target }) =>
431cb0ef41Sopenharmony_ci  er.code === 'ENOENT' ? null
441cb0ef41Sopenharmony_ci  : failEEXIST({ target })
451cb0ef41Sopenharmony_ci
461cb0ef41Sopenharmony_ciconst failEEXIST = ({ target }) =>
471cb0ef41Sopenharmony_ci  Promise.reject(Object.assign(new Error('EEXIST: file already exists'), {
481cb0ef41Sopenharmony_ci    path: target,
491cb0ef41Sopenharmony_ci    code: 'EEXIST',
501cb0ef41Sopenharmony_ci  }))
511cb0ef41Sopenharmony_ci
521cb0ef41Sopenharmony_ciconst checkShim = async ({ target, path }) => {
531cb0ef41Sopenharmony_ci  const shims = [
541cb0ef41Sopenharmony_ci    target,
551cb0ef41Sopenharmony_ci    target + '.cmd',
561cb0ef41Sopenharmony_ci    target + '.ps1',
571cb0ef41Sopenharmony_ci  ]
581cb0ef41Sopenharmony_ci  await Promise.all(shims.map(async shim => {
591cb0ef41Sopenharmony_ci    const current = await readCmdShim(shim)
601cb0ef41Sopenharmony_ci      .catch(er => handleReadCmdShimError({ er, target: shim }))
611cb0ef41Sopenharmony_ci
621cb0ef41Sopenharmony_ci    if (!current) {
631cb0ef41Sopenharmony_ci      return
641cb0ef41Sopenharmony_ci    }
651cb0ef41Sopenharmony_ci
661cb0ef41Sopenharmony_ci    const resolved = resolve(dirname(shim), current.replace(/\\/g, '/'))
671cb0ef41Sopenharmony_ci
681cb0ef41Sopenharmony_ci    if (resolved.toLowerCase().indexOf(path.toLowerCase()) !== 0) {
691cb0ef41Sopenharmony_ci      return failEEXIST({ target: shim })
701cb0ef41Sopenharmony_ci    }
711cb0ef41Sopenharmony_ci  }))
721cb0ef41Sopenharmony_ci}
731cb0ef41Sopenharmony_ci
741cb0ef41Sopenharmony_cimodule.exports = checkBin
75