mirror of
https://github.com/zen-browser/surfer.git
synced 2025-07-07 08:55:33 +02:00
120 lines
3.6 KiB
TypeScript
120 lines
3.6 KiB
TypeScript
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
// This file was originally under the MIT license, but is now under the MPL 2.0
|
|
// license. The following license notice applies to only this file
|
|
//
|
|
// The MIT License (MIT)
|
|
//
|
|
// Copyright (c) 2014 Matthew Conlen
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in all
|
|
// copies or substantial portions of the Software.
|
|
//
|
|
// Adapted from the `command-exists` node module
|
|
// https://github.com/mathisonian/command-exists
|
|
|
|
import { execSync } from 'node:child_process'
|
|
import { accessSync, constants } from 'node:fs'
|
|
import path from 'node:path'
|
|
|
|
const onWindows = process.platform == 'win32'
|
|
|
|
const fileNotExistsSync = (commandName: string): boolean => {
|
|
try {
|
|
accessSync(commandName, constants.F_OK)
|
|
return false
|
|
} catch {
|
|
return true
|
|
}
|
|
}
|
|
|
|
const localExecutableSync = (commandName: string): boolean => {
|
|
try {
|
|
accessSync(commandName, constants.F_OK | constants.X_OK)
|
|
return true
|
|
} catch {
|
|
return false
|
|
}
|
|
}
|
|
|
|
const commandExistsUnixSync = function (
|
|
commandName: string,
|
|
cleanedCommandName: string
|
|
): boolean {
|
|
if (fileNotExistsSync(commandName)) {
|
|
try {
|
|
const stdout = execSync(
|
|
'command -v ' +
|
|
cleanedCommandName +
|
|
' 2>/dev/null' +
|
|
' && { echo >&1 ' +
|
|
cleanedCommandName +
|
|
'; exit 0; }'
|
|
)
|
|
return !!stdout
|
|
} catch {
|
|
return false
|
|
}
|
|
}
|
|
return localExecutableSync(commandName)
|
|
}
|
|
|
|
const commandExistsWindowsSync = function (
|
|
commandName: string,
|
|
cleanedCommandName: string
|
|
): boolean {
|
|
// Regex from Julio from: https://stackoverflow.com/questions/51494579/regex-windows-path-validator
|
|
if (
|
|
!/^(?!(?:.*\s|.*\.|\W+)$)(?:[A-Za-z]:)?(?:[^\n"*:<>?|]+(?:\/\/|\/|\\\\|\\)?)+$/m.test(
|
|
commandName
|
|
)
|
|
) {
|
|
return false
|
|
}
|
|
try {
|
|
const stdout = execSync('where ' + cleanedCommandName, { stdio: [] })
|
|
return !!stdout
|
|
} catch {
|
|
return false
|
|
}
|
|
}
|
|
|
|
function cleanInput(toBeCleaned: string): string {
|
|
// Windows has a different cleaning process to Unix, so we should go through
|
|
// that process first
|
|
if (onWindows) {
|
|
const isPathName = /\\/.test(toBeCleaned)
|
|
if (isPathName) {
|
|
const dirname = '"' + path.dirname(toBeCleaned) + '"'
|
|
const basename = '"' + path.basename(toBeCleaned) + '"'
|
|
return `${dirname}:${basename}`
|
|
}
|
|
|
|
return `"${toBeCleaned}"`
|
|
}
|
|
|
|
// Otherwise go through the unix cleaning process
|
|
if (/[^\w/:=\\-]/.test(toBeCleaned)) {
|
|
toBeCleaned = "'" + toBeCleaned.replace(/'/g, "'\\''") + "'"
|
|
toBeCleaned = toBeCleaned
|
|
.replace(/^(?:'')+/g, '') // unduplicate single-quote at the beginning
|
|
.replace(/\\'''/g, "\\'") // remove non-escaped single-quote if there are enclosed between 2 escaped
|
|
}
|
|
|
|
return toBeCleaned
|
|
}
|
|
|
|
export function commandExistsSync(commandName: string): boolean {
|
|
const cleanedCommandName = cleanInput(commandName)
|
|
return onWindows
|
|
? commandExistsWindowsSync(commandName, cleanedCommandName)
|
|
: commandExistsUnixSync(commandName, cleanedCommandName)
|
|
}
|