1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
|
# frozen_string_literal: true
require_relative "which/version"
module TTY
# A class responsible for finding an executable in the PATH
module Which
# Find an executable in a platform independent way
#
# @param [String] cmd
# the command to search for
# @param [Array<String>] paths
# the paths to look through
#
# @example
# which("ruby") # => "/usr/local/bin/ruby"
# which("/usr/local/bin/ruby") # => "/usr/local/bin/ruby"
# which("foo") # => nil
#
# @example
# which("ruby", paths: ["/usr/locale/bin", "/usr/bin", "/bin"])
#
# @return [String, nil]
# the absolute path to executable if found, `nil` otherwise
#
# @api public
def which(cmd, paths: search_paths)
if file_with_path?(cmd)
return cmd if executable_file?(cmd)
extensions.each do |ext|
exe = "#{cmd}#{ext}"
return ::File.absolute_path(exe) if executable_file?(exe)
end
return nil
end
paths.each do |path|
if file_with_exec_ext?(cmd)
exe = ::File.join(path, cmd)
return ::File.absolute_path(exe) if executable_file?(exe)
end
extensions.each do |ext|
exe = ::File.join(path, "#{cmd}#{ext}")
return ::File.absolute_path(exe) if executable_file?(exe)
end
end
nil
end
module_function :which
# Check if executable exists in the path
#
# @param [String] cmd
# the executable to check
#
# @param [Array<String>] paths
# paths to check
#
# @return [Boolean]
#
# @api public
def exist?(cmd, paths: search_paths)
!which(cmd, paths: paths).nil?
end
module_function :exist?
# Find default system paths
#
# @param [String] path
# the path to search through
#
# @example
# search_paths("/usr/local/bin:/bin")
# # => ["/bin"]
#
# @return [Array<String>]
# the array of paths to search
#
# @api private
def search_paths(path = ENV["PATH"])
paths = if path && !path.empty?
path.split(::File::PATH_SEPARATOR)
else
%w[/usr/local/bin /usr/ucb /usr/bin /bin]
end
paths.select(&Dir.method(:exist?))
end
module_function :search_paths
# All possible file extensions
#
# @example
# extensions(".exe;cmd;.bat")
# # => [".exe", ".bat"]
#
# @param [String] path_ext
# a string of semicolon separated filename extensions
#
# @return [Array<String>]
# an array with valid file extensions
#
# @api private
def extensions(path_ext = ENV["PATHEXT"])
return [""] unless path_ext
path_ext.split(::File::PATH_SEPARATOR).select { |part| part.include?(".") }
end
module_function :extensions
# Determines if filename is an executable file
#
# @example Basic usage
# executable_file?("/usr/bin/less") # => true
#
# @example Executable in directory
# executable_file?("less", "/usr/bin") # => true
# executable_file?("less", "/usr") # => false
#
# @param [String] filename
# the path to file
# @param [String] dir
# the directory within which to search for filename
#
# @return [Boolean]
#
# @api private
def executable_file?(filename, dir = nil)
path = ::File.join(dir, filename) if dir
path ||= filename
::File.file?(path) && ::File.executable?(path)
end
module_function :executable_file?
# Check if command itself has executable extension
#
# @param [String] filename
# the path to executable file
#
# @example
# file_with_exec_ext?("file.bat")
# # => true
#
# @return [Boolean]
#
# @api private
def file_with_exec_ext?(filename)
extension = ::File.extname(filename)
return false if extension.empty?
extensions.any? { |ext| extension.casecmp(ext).zero? }
end
module_function :file_with_exec_ext?
# Check if executable file is part of absolute/relative path
#
# @param [String] cmd
# the executable to check
#
# @return [Boolean]
#
# @api private
def file_with_path?(cmd)
::File.expand_path(cmd) == cmd
end
module_function :file_with_path?
end # Which
end # TTY
|