searchlink 2.3.59
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/searchlink +84 -0
- data/lib/searchlink/array.rb +7 -0
- data/lib/searchlink/config.rb +230 -0
- data/lib/searchlink/curl/html.rb +482 -0
- data/lib/searchlink/curl/json.rb +90 -0
- data/lib/searchlink/curl.rb +7 -0
- data/lib/searchlink/help.rb +103 -0
- data/lib/searchlink/output.rb +270 -0
- data/lib/searchlink/parse.rb +668 -0
- data/lib/searchlink/plist.rb +213 -0
- data/lib/searchlink/search.rb +70 -0
- data/lib/searchlink/searches/amazon.rb +25 -0
- data/lib/searchlink/searches/applemusic.rb +123 -0
- data/lib/searchlink/searches/bitly.rb +50 -0
- data/lib/searchlink/searches/definition.rb +67 -0
- data/lib/searchlink/searches/duckduckgo.rb +167 -0
- data/lib/searchlink/searches/github.rb +245 -0
- data/lib/searchlink/searches/google.rb +67 -0
- data/lib/searchlink/searches/helpers/chromium.rb +318 -0
- data/lib/searchlink/searches/helpers/firefox.rb +135 -0
- data/lib/searchlink/searches/helpers/safari.rb +133 -0
- data/lib/searchlink/searches/history.rb +166 -0
- data/lib/searchlink/searches/hook.rb +77 -0
- data/lib/searchlink/searches/itunes.rb +97 -0
- data/lib/searchlink/searches/lastfm.rb +41 -0
- data/lib/searchlink/searches/lyrics.rb +91 -0
- data/lib/searchlink/searches/pinboard.rb +183 -0
- data/lib/searchlink/searches/social.rb +105 -0
- data/lib/searchlink/searches/software.rb +27 -0
- data/lib/searchlink/searches/spelling.rb +59 -0
- data/lib/searchlink/searches/spotlight.rb +28 -0
- data/lib/searchlink/searches/stackoverflow.rb +31 -0
- data/lib/searchlink/searches/tmdb.rb +52 -0
- data/lib/searchlink/searches/twitter.rb +46 -0
- data/lib/searchlink/searches/wikipedia.rb +33 -0
- data/lib/searchlink/searches/youtube.rb +48 -0
- data/lib/searchlink/searches.rb +194 -0
- data/lib/searchlink/semver.rb +140 -0
- data/lib/searchlink/string.rb +469 -0
- data/lib/searchlink/url.rb +153 -0
- data/lib/searchlink/util.rb +87 -0
- data/lib/searchlink/version.rb +93 -0
- data/lib/searchlink/which.rb +175 -0
- data/lib/searchlink.rb +66 -0
- data/lib/tokens.rb +3 -0
- metadata +299 -0
@@ -0,0 +1,153 @@
|
|
1
|
+
module SL
|
2
|
+
module URL
|
3
|
+
class << self
|
4
|
+
# Validates that a link exists and returns 200
|
5
|
+
def valid_link?(uri_str, limit = 5)
|
6
|
+
return false unless uri_str
|
7
|
+
|
8
|
+
SL.notify('Validating', uri_str)
|
9
|
+
return false if limit.zero?
|
10
|
+
|
11
|
+
url = URI(uri_str)
|
12
|
+
return true unless url.scheme
|
13
|
+
|
14
|
+
url.path = '/' if url.path == ''
|
15
|
+
# response = Net::HTTP.get_response(URI(uri_str))
|
16
|
+
response = false
|
17
|
+
|
18
|
+
Net::HTTP.start(url.host, url.port, use_ssl: url.scheme == 'https') do |http|
|
19
|
+
response = http.request_head(url.path)
|
20
|
+
end
|
21
|
+
|
22
|
+
case response
|
23
|
+
when Net::HTTPMethodNotAllowed, Net::HTTPServiceUnavailable
|
24
|
+
unless /amazon\.com/ =~ url.host
|
25
|
+
SL.add_error('link validation', "Validation blocked: #{uri_str} (#{e})")
|
26
|
+
end
|
27
|
+
SL.notify('Error validating', uri_str)
|
28
|
+
true
|
29
|
+
when Net::HTTPSuccess
|
30
|
+
true
|
31
|
+
when Net::HTTPRedirection
|
32
|
+
location = response['location']
|
33
|
+
valid_link?(location, limit - 1)
|
34
|
+
else
|
35
|
+
SL.notify('Error validating', uri_str)
|
36
|
+
false
|
37
|
+
end
|
38
|
+
rescue StandardError => e
|
39
|
+
SL.notify('Error validating', uri_str)
|
40
|
+
SL.add_error('link validation', "Possibly invalid => #{uri_str} (#{e})")
|
41
|
+
true
|
42
|
+
end
|
43
|
+
|
44
|
+
def url?(input)
|
45
|
+
input =~ %r{^(#.*|https?://\S+|/\S+|\S+/|[^!]\S+\.\S+)(\s+".*?")?$}
|
46
|
+
end
|
47
|
+
|
48
|
+
def only_url?(input)
|
49
|
+
input =~ %r{(?i)^((http|https)://)?([\w\-_]+(\.[\w\-_]+)+)([\w\-.,@?^=%&:/~+#]*[\w\-@^=%&/~+#])?$}
|
50
|
+
end
|
51
|
+
|
52
|
+
def ref_title_for_url(url)
|
53
|
+
url = URI.parse(url) if url.is_a?(String)
|
54
|
+
|
55
|
+
parts = url.hostname.split(/\./)
|
56
|
+
domain = if parts.count > 1
|
57
|
+
parts.slice(-2, 1).join('')
|
58
|
+
else
|
59
|
+
parts.join('')
|
60
|
+
end
|
61
|
+
|
62
|
+
path = url.path.split(%r{/}).last
|
63
|
+
if path
|
64
|
+
path.gsub!(/-/, ' ').gsub!(/\.\w{2-4}$/, '')
|
65
|
+
else
|
66
|
+
path = domain
|
67
|
+
end
|
68
|
+
|
69
|
+
path.length > domain.length ? path : domain
|
70
|
+
end
|
71
|
+
|
72
|
+
def url_to_link(url, type)
|
73
|
+
input = url.dup
|
74
|
+
|
75
|
+
if only_url?(input)
|
76
|
+
input.sub!(%r{(?mi)^(?!https?://)(.*?)$}, 'https://\1')
|
77
|
+
url = URI.parse(input.downcase)
|
78
|
+
|
79
|
+
title = if type == :ref_title
|
80
|
+
ref_title_for_url(url)
|
81
|
+
else
|
82
|
+
title(url.to_s) || input.sub(%r{^https?://}, '')
|
83
|
+
end
|
84
|
+
|
85
|
+
return [url.to_s, title] if url.hostname
|
86
|
+
end
|
87
|
+
false
|
88
|
+
end
|
89
|
+
|
90
|
+
def amazon_affiliatize(url, amazon_partner)
|
91
|
+
return url if amazon_partner.nil? || amazon_partner.empty?
|
92
|
+
|
93
|
+
unless url =~ %r{https?://(?<subdomain>.*?)amazon.com/(?:(?<title>.*?)/)?(?<type>[dg])p/(?<id>[^?]+)}
|
94
|
+
return [url, '']
|
95
|
+
end
|
96
|
+
|
97
|
+
m = Regexp.last_match
|
98
|
+
sd = m['subdomain']
|
99
|
+
title = m['title'].gsub(/-/, ' ')
|
100
|
+
t = m['type']
|
101
|
+
id = m['id']
|
102
|
+
["https://#{sd}amazon.com/#{t}p/#{id}/?ref=as_li_ss_tl&ie=UTF8&linkCode=sl1&tag=#{amazon_partner}", title]
|
103
|
+
end
|
104
|
+
|
105
|
+
def title(url)
|
106
|
+
title = nil
|
107
|
+
|
108
|
+
## Gather proving too inexact
|
109
|
+
# gather = false
|
110
|
+
# ['/usr/local/bin', '/opt/homebrew/bin'].each do |root|
|
111
|
+
# if File.exist?(File.join(root, 'gather')) && File.executable?(File.join(root, 'gather'))
|
112
|
+
# gather = File.join(root, 'gather')
|
113
|
+
# break
|
114
|
+
# end
|
115
|
+
# end
|
116
|
+
|
117
|
+
# if gather
|
118
|
+
# cmd = %(#{gather} --title-only '#{url.strip}' --fallback-title 'Unknown')
|
119
|
+
# title = SL::Util.exec_with_timeout(cmd, 15)
|
120
|
+
# if title
|
121
|
+
# title = title.strip.gsub(/\n+/, ' ').gsub(/ +/, ' ')
|
122
|
+
# title.remove_seo!(url) if SL.config['remove_seo']
|
123
|
+
# return title.remove_protocol
|
124
|
+
# else
|
125
|
+
# SL.add_error('Error retrieving title', "Gather timed out on #{url}")
|
126
|
+
# SL.notify('Error retrieving title', 'Gather timed out')
|
127
|
+
# end
|
128
|
+
# end
|
129
|
+
|
130
|
+
begin
|
131
|
+
page = Curl::Html.new(url)
|
132
|
+
|
133
|
+
title = page.title || nil
|
134
|
+
|
135
|
+
if title.nil? || title =~ /^\s*$/
|
136
|
+
SL.add_error('Title not found', "Warning: missing title for #{url.strip}")
|
137
|
+
title = url.gsub(%r{(^https?://|/.*$)}, '').gsub(/-/, ' ').strip
|
138
|
+
else
|
139
|
+
title = title.gsub(/\n/, ' ').gsub(/\s+/, ' ').strip # .sub(/[^a-z]*$/i,'')
|
140
|
+
title.remove_seo!(url) if SL.config['remove_seo']
|
141
|
+
end
|
142
|
+
title.gsub!(/\|/, '—')
|
143
|
+
title.remove_seo!(url.strip) if SL.config['remove_seo']
|
144
|
+
title.remove_protocol
|
145
|
+
rescue StandardError
|
146
|
+
SL.add_error('Error retrieving title', "Error determining title for #{url.strip}")
|
147
|
+
warn "Error retrieving title for #{url.strip}"
|
148
|
+
url.remove_protocol
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module SL
|
2
|
+
module Util
|
3
|
+
class << self
|
4
|
+
## Execute system command with deadman's switch
|
5
|
+
##
|
6
|
+
## <https://stackoverflow.com/questions/8292031/ruby-timeouts-and-system-commands>
|
7
|
+
## <https://stackoverflow.com/questions/12189904/fork-child-process-with-timeout-and-capture-output>
|
8
|
+
##
|
9
|
+
## @param cmd The command to execute
|
10
|
+
## @param timeout The timeout
|
11
|
+
##
|
12
|
+
## @return [String] STDOUT output
|
13
|
+
##
|
14
|
+
def exec_with_timeout(cmd, timeout)
|
15
|
+
begin
|
16
|
+
# stdout, stderr pipes
|
17
|
+
rout, wout = IO.pipe
|
18
|
+
rerr, werr = IO.pipe
|
19
|
+
stdout, stderr = nil
|
20
|
+
|
21
|
+
pid = Process.spawn(cmd, pgroup: true, out: wout, err: werr)
|
22
|
+
|
23
|
+
Timeout.timeout(timeout) do
|
24
|
+
Process.waitpid(pid)
|
25
|
+
|
26
|
+
# close write ends so we can read from them
|
27
|
+
wout.close
|
28
|
+
werr.close
|
29
|
+
|
30
|
+
stdout = rout.readlines.join
|
31
|
+
stderr = rerr.readlines.join
|
32
|
+
end
|
33
|
+
rescue Timeout::Error
|
34
|
+
Process.kill(-9, pid)
|
35
|
+
Process.detach(pid)
|
36
|
+
ensure
|
37
|
+
wout.close unless wout.closed?
|
38
|
+
werr.close unless werr.closed?
|
39
|
+
# dispose the read ends of the pipes
|
40
|
+
rout.close
|
41
|
+
rerr.close
|
42
|
+
end
|
43
|
+
|
44
|
+
stdout&.strip
|
45
|
+
end
|
46
|
+
|
47
|
+
##
|
48
|
+
## Execute a search with deadman's switch
|
49
|
+
##
|
50
|
+
## @param search [Proc] The search command
|
51
|
+
## @param timeout [Number] The timeout
|
52
|
+
##
|
53
|
+
## @return [Array] url, title, link_text
|
54
|
+
##
|
55
|
+
def search_with_timeout(search, timeout)
|
56
|
+
url = nil
|
57
|
+
title = nil
|
58
|
+
link_text = nil
|
59
|
+
|
60
|
+
begin
|
61
|
+
Timeout.timeout(timeout) do
|
62
|
+
url, title, link_text = search.call
|
63
|
+
end
|
64
|
+
rescue Timeout::Error
|
65
|
+
SL.add_error('Timeout', 'Search timed out')
|
66
|
+
url, title, link_text = false
|
67
|
+
end
|
68
|
+
|
69
|
+
[url, title, link_text]
|
70
|
+
end
|
71
|
+
|
72
|
+
##
|
73
|
+
## Get the path for a cache file
|
74
|
+
##
|
75
|
+
## @param filename [String] The filename to
|
76
|
+
## generate the cache for
|
77
|
+
##
|
78
|
+
## @return [String] path to new cache file
|
79
|
+
##
|
80
|
+
def cache_file_for(filename)
|
81
|
+
cache_folder = File.expand_path('~/.local/share/searchlink/cache')
|
82
|
+
FileUtils.mkdir_p(cache_folder) unless File.directory?(cache_folder)
|
83
|
+
File.join(cache_folder, filename.sub(/(\.cache)?$/, '.cache'))
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module SL
|
2
|
+
VERSION = '2.3.59'
|
3
|
+
end
|
4
|
+
|
5
|
+
module SL
|
6
|
+
class << self
|
7
|
+
def version_check
|
8
|
+
cachefile = File.expand_path('~/.searchlink_update_check')
|
9
|
+
if File.exist?(cachefile)
|
10
|
+
last_check, latest_tag = IO.read(cachefile).strip.split(/\|/)
|
11
|
+
last_time = Time.parse(last_check)
|
12
|
+
else
|
13
|
+
latest_tag = new_version?
|
14
|
+
last_time = Time.now
|
15
|
+
end
|
16
|
+
|
17
|
+
if last_time + (24 * 60 * 60) < Time.now
|
18
|
+
latest_tag = new_version?
|
19
|
+
last_time = Time.now
|
20
|
+
end
|
21
|
+
|
22
|
+
latest_tag ||= SL::VERSION
|
23
|
+
latest = SemVer.new(latest_tag)
|
24
|
+
current = SemVer.new(SL::VERSION)
|
25
|
+
|
26
|
+
File.open(cachefile, 'w') { |f| f.puts("#{last_time.strftime('%c')}|#{latest.to_s}") }
|
27
|
+
|
28
|
+
return "SearchLink v#{current.to_s}, #{latest.to_s} available. Run 'update' to download." if latest_tag && current.older_than(latest)
|
29
|
+
|
30
|
+
"SearchLink v#{current.to_s}"
|
31
|
+
end
|
32
|
+
|
33
|
+
# Check for a newer version than local copy using GitHub release tag
|
34
|
+
#
|
35
|
+
# @return false if no new version, or semantic version of latest release
|
36
|
+
def new_version?
|
37
|
+
headers = {
|
38
|
+
'Accept' => 'application/vnd.github+json',
|
39
|
+
'X-GitHub-Api-Version' => '2022-11-28'
|
40
|
+
}
|
41
|
+
headers['Authorization'] = "Bearer #{Secrets::GH_AUTH_TOKEN}" if defined? Secrets::GH_AUTH_TOKEN
|
42
|
+
|
43
|
+
url = 'https://api.github.com/repos/ttscoff/searchlink/releases/latest'
|
44
|
+
page = Curl::Json.new(url, headers: headers)
|
45
|
+
result = page.json
|
46
|
+
|
47
|
+
if result
|
48
|
+
latest_tag = result['tag_name']
|
49
|
+
|
50
|
+
return false unless latest_tag
|
51
|
+
|
52
|
+
return false if latest_tag =~ /^#{Regexp.escape(SL::VERSION)}$/
|
53
|
+
|
54
|
+
latest = SemVer.new(latest_tag)
|
55
|
+
current = SemVer.new(SL::VERSION)
|
56
|
+
|
57
|
+
return latest_tag if current.older_than(latest)
|
58
|
+
else
|
59
|
+
warn 'Check for new version failed.'
|
60
|
+
end
|
61
|
+
|
62
|
+
false
|
63
|
+
end
|
64
|
+
|
65
|
+
def update_searchlink
|
66
|
+
new_version = SL.new_version?
|
67
|
+
if new_version
|
68
|
+
folder = File.expand_path('~/Downloads')
|
69
|
+
services = File.expand_path('~/Library/Services')
|
70
|
+
dl = File.join(folder, 'SearchLink.zip')
|
71
|
+
curl = TTY::Which.which('curl')
|
72
|
+
`#{curl} -SsL -o "#{dl}" https://github.com/ttscoff/searchlink/releases/latest/download/SearchLink.zip`
|
73
|
+
Dir.chdir(folder)
|
74
|
+
`unzip -qo #{dl} -d #{folder}`
|
75
|
+
FileUtils.rm(dl)
|
76
|
+
|
77
|
+
['SearchLink', 'SearchLink File', 'Jump to SearchLink Error'].each do |workflow|
|
78
|
+
wflow = "#{workflow}.workflow"
|
79
|
+
src = File.join(folder, 'SearchLink Services', wflow)
|
80
|
+
dest = File.join(services, wflow)
|
81
|
+
if File.exist?(src) && File.exist?(dest)
|
82
|
+
FileUtils.rm_rf(dest)
|
83
|
+
FileUtils.mv(src, dest, force: true)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
add_output("Installed SearchLink #{new_version}")
|
87
|
+
FileUtils.rm_rf('SearchLink Services')
|
88
|
+
else
|
89
|
+
add_output('Already up to date.')
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,175 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# From <https://github.com/piotrmurach/tty-which/tree/master>
|
4
|
+
# [MIT License](https://github.com/piotrmurach/tty-which/blob/master/LICENSE.txt)
|
5
|
+
|
6
|
+
module TTY
|
7
|
+
module Which
|
8
|
+
VERSION = "0.5.0"
|
9
|
+
end # Which
|
10
|
+
end # TTY
|
11
|
+
|
12
|
+
module TTY
|
13
|
+
# A class responsible for finding an executable in the PATH
|
14
|
+
module Which
|
15
|
+
# Find an executable in a platform independent way
|
16
|
+
#
|
17
|
+
# @param [String] cmd
|
18
|
+
# the command to search for
|
19
|
+
# @param [Array<String>] paths
|
20
|
+
# the paths to look through
|
21
|
+
#
|
22
|
+
# @example
|
23
|
+
# which("ruby") # => "/usr/local/bin/ruby"
|
24
|
+
# which("/usr/local/bin/ruby") # => "/usr/local/bin/ruby"
|
25
|
+
# which("foo") # => nil
|
26
|
+
#
|
27
|
+
# @example
|
28
|
+
# which("ruby", paths: ["/usr/locale/bin", "/usr/bin", "/bin"])
|
29
|
+
#
|
30
|
+
# @return [String, nil]
|
31
|
+
# the absolute path to executable if found, `nil` otherwise
|
32
|
+
#
|
33
|
+
# @api public
|
34
|
+
def which(cmd, paths: search_paths)
|
35
|
+
if file_with_path?(cmd)
|
36
|
+
return cmd if executable_file?(cmd)
|
37
|
+
|
38
|
+
extensions.each do |ext|
|
39
|
+
exe = "#{cmd}#{ext}"
|
40
|
+
return ::File.absolute_path(exe) if executable_file?(exe)
|
41
|
+
end
|
42
|
+
return nil
|
43
|
+
end
|
44
|
+
|
45
|
+
paths.each do |path|
|
46
|
+
if file_with_exec_ext?(cmd)
|
47
|
+
exe = ::File.join(path, cmd)
|
48
|
+
return ::File.absolute_path(exe) if executable_file?(exe)
|
49
|
+
end
|
50
|
+
extensions.each do |ext|
|
51
|
+
exe = ::File.join(path, "#{cmd}#{ext}")
|
52
|
+
return ::File.absolute_path(exe) if executable_file?(exe)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
nil
|
56
|
+
end
|
57
|
+
module_function :which
|
58
|
+
|
59
|
+
# Check if executable exists in the path
|
60
|
+
#
|
61
|
+
# @param [String] cmd
|
62
|
+
# the executable to check
|
63
|
+
#
|
64
|
+
# @param [Array<String>] paths
|
65
|
+
# paths to check
|
66
|
+
#
|
67
|
+
# @return [Boolean]
|
68
|
+
#
|
69
|
+
# @api public
|
70
|
+
def exist?(cmd, paths: search_paths)
|
71
|
+
!which(cmd, paths: paths).nil?
|
72
|
+
end
|
73
|
+
module_function :exist?
|
74
|
+
|
75
|
+
# Find default system paths
|
76
|
+
#
|
77
|
+
# @param [String] path
|
78
|
+
# the path to search through
|
79
|
+
#
|
80
|
+
# @example
|
81
|
+
# search_paths("/usr/local/bin:/bin")
|
82
|
+
# # => ["/bin"]
|
83
|
+
#
|
84
|
+
# @return [Array<String>]
|
85
|
+
# the array of paths to search
|
86
|
+
#
|
87
|
+
# @api private
|
88
|
+
def search_paths(path = ENV["PATH"])
|
89
|
+
paths = if path && !path.empty?
|
90
|
+
path.split(::File::PATH_SEPARATOR)
|
91
|
+
else
|
92
|
+
%w[/usr/local/bin /usr/ucb /usr/bin /bin /opt/homebrew/bin]
|
93
|
+
end
|
94
|
+
paths.select(&Dir.method(:exist?))
|
95
|
+
end
|
96
|
+
module_function :search_paths
|
97
|
+
|
98
|
+
# All possible file extensions
|
99
|
+
#
|
100
|
+
# @example
|
101
|
+
# extensions(".exe;cmd;.bat")
|
102
|
+
# # => [".exe", ".bat"]
|
103
|
+
#
|
104
|
+
# @param [String] path_ext
|
105
|
+
# a string of semicolon separated filename extensions
|
106
|
+
#
|
107
|
+
# @return [Array<String>]
|
108
|
+
# an array with valid file extensions
|
109
|
+
#
|
110
|
+
# @api private
|
111
|
+
def extensions(path_ext = ENV["PATHEXT"])
|
112
|
+
return [""] unless path_ext
|
113
|
+
|
114
|
+
path_ext.split(::File::PATH_SEPARATOR).select { |part| part.include?(".") }
|
115
|
+
end
|
116
|
+
module_function :extensions
|
117
|
+
|
118
|
+
# Determines if filename is an executable file
|
119
|
+
#
|
120
|
+
# @example Basic usage
|
121
|
+
# executable_file?("/usr/bin/less") # => true
|
122
|
+
#
|
123
|
+
# @example Executable in directory
|
124
|
+
# executable_file?("less", "/usr/bin") # => true
|
125
|
+
# executable_file?("less", "/usr") # => false
|
126
|
+
#
|
127
|
+
# @param [String] filename
|
128
|
+
# the path to file
|
129
|
+
# @param [String] dir
|
130
|
+
# the directory within which to search for filename
|
131
|
+
#
|
132
|
+
# @return [Boolean]
|
133
|
+
#
|
134
|
+
# @api private
|
135
|
+
def executable_file?(filename, dir = nil)
|
136
|
+
path = ::File.join(dir, filename) if dir
|
137
|
+
path ||= filename
|
138
|
+
::File.file?(path) && ::File.executable?(path)
|
139
|
+
end
|
140
|
+
module_function :executable_file?
|
141
|
+
|
142
|
+
# Check if command itself has executable extension
|
143
|
+
#
|
144
|
+
# @param [String] filename
|
145
|
+
# the path to executable file
|
146
|
+
#
|
147
|
+
# @example
|
148
|
+
# file_with_exec_ext?("file.bat")
|
149
|
+
# # => true
|
150
|
+
#
|
151
|
+
# @return [Boolean]
|
152
|
+
#
|
153
|
+
# @api private
|
154
|
+
def file_with_exec_ext?(filename)
|
155
|
+
extension = ::File.extname(filename)
|
156
|
+
return false if extension.empty?
|
157
|
+
|
158
|
+
extensions.any? { |ext| extension.casecmp(ext).zero? }
|
159
|
+
end
|
160
|
+
module_function :file_with_exec_ext?
|
161
|
+
|
162
|
+
# Check if executable file is part of absolute/relative path
|
163
|
+
#
|
164
|
+
# @param [String] cmd
|
165
|
+
# the executable to check
|
166
|
+
#
|
167
|
+
# @return [Boolean]
|
168
|
+
#
|
169
|
+
# @api private
|
170
|
+
def file_with_path?(cmd)
|
171
|
+
::File.expand_path(cmd) == cmd
|
172
|
+
end
|
173
|
+
module_function :file_with_path?
|
174
|
+
end # Which
|
175
|
+
end # TTY
|
data/lib/searchlink.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# SearchLink by Brett Terpstra 2015 <http://brettterpstra.com/projects/searchlink/>
|
4
|
+
# MIT License, please maintain attribution
|
5
|
+
require 'net/https'
|
6
|
+
require 'uri'
|
7
|
+
require 'rexml/document'
|
8
|
+
require 'shellwords'
|
9
|
+
require 'yaml'
|
10
|
+
require 'cgi'
|
11
|
+
require 'fileutils'
|
12
|
+
require 'tempfile'
|
13
|
+
require 'zlib'
|
14
|
+
require 'time'
|
15
|
+
require 'json'
|
16
|
+
require 'erb'
|
17
|
+
|
18
|
+
# import
|
19
|
+
require 'tokens' if File.exist?('lib/tokens.rb')
|
20
|
+
|
21
|
+
# import
|
22
|
+
require 'searchlink/util'
|
23
|
+
|
24
|
+
# import
|
25
|
+
require 'searchlink/curl'
|
26
|
+
|
27
|
+
# import
|
28
|
+
require 'searchlink/semver'
|
29
|
+
|
30
|
+
# import
|
31
|
+
require 'searchlink/version'
|
32
|
+
|
33
|
+
# import
|
34
|
+
require 'searchlink/array'
|
35
|
+
|
36
|
+
# import
|
37
|
+
require 'searchlink/string'
|
38
|
+
|
39
|
+
# import
|
40
|
+
require 'searchlink/plist'
|
41
|
+
|
42
|
+
# import
|
43
|
+
require 'searchlink/config'
|
44
|
+
|
45
|
+
# import
|
46
|
+
require 'searchlink/searches'
|
47
|
+
|
48
|
+
# import
|
49
|
+
require 'searchlink/url'
|
50
|
+
|
51
|
+
# import
|
52
|
+
require 'searchlink/search'
|
53
|
+
|
54
|
+
# import
|
55
|
+
require 'searchlink/help'
|
56
|
+
|
57
|
+
# import
|
58
|
+
require 'searchlink/parse'
|
59
|
+
|
60
|
+
# import
|
61
|
+
require 'searchlink/output'
|
62
|
+
|
63
|
+
# import
|
64
|
+
require 'searchlink/which'
|
65
|
+
|
66
|
+
module Secrets; end
|
data/lib/tokens.rb
ADDED