| 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
 | require 'formula'
require 'blacklist'
require 'utils'
require 'thread'
module Homebrew
  SEARCH_ERROR_QUEUE = Queue.new
  def search
    if ARGV.include? '--macports'
      exec_browser "https://www.macports.org/ports.php?by=name&substr=#{ARGV.next}"
    elsif ARGV.include? '--fink'
      exec_browser "http://pdb.finkproject.org/pdb/browse.php?summary=#{ARGV.next}"
    elsif ARGV.include? '--debian'
      exec_browser "https://packages.debian.org/search?keywords=#{ARGV.next}&searchon=names&suite=all§ion=all"
    elsif ARGV.include? '--opensuse'
      exec_browser "http://software.opensuse.org/search?q=#{ARGV.next}"
    elsif ARGV.include? '--fedora'
      exec_browser "https://admin.fedoraproject.org/pkgdb/packages/%2A#{ARGV.next}%2A/"
    elsif ARGV.include? '--ubuntu'
      exec_browser "http://packages.ubuntu.com/search?keywords=#{ARGV.next}&searchon=names&suite=all§ion=all"
    elsif ARGV.empty?
      puts_columns Formula.names
    elsif ARGV.first =~ HOMEBREW_TAP_FORMULA_REGEX
      query = ARGV.first
      user, repo, name = query.split("/", 3)
      begin
        result = Formulary.factory(query).name
      rescue FormulaUnavailableError
        result = search_tap(user, repo, name)
      end
      puts_columns Array(result)
    else
      query = ARGV.first
      rx = query_regexp(query)
      local_results = search_formulae(rx)
      puts_columns(local_results)
      if not query.empty? and $stdout.tty? and msg = blacklisted?(query)
        unless local_results.empty?
          puts
          puts "If you meant #{query.inspect} precisely:"
          puts
        end
        puts msg
      end
      tap_results = search_taps(rx)
      puts_columns(tap_results)
      count = local_results.length + tap_results.length
      if count == 0 and not blacklisted? query
        puts "No formula found for #{query.inspect}."
        begin
          GitHub.print_pull_requests_matching(query)
        rescue GitHub::Error => e
          SEARCH_ERROR_QUEUE << e
        end
      end
    end
    metacharacters = %w[\\ | ( ) [ ] { } ^ $ * + ? .]
    bad_regex = metacharacters.any? do |char|
      ARGV.any? do |arg|
        arg.include?(char) && !arg.start_with?('/')
      end
    end
    if ARGV.any? && bad_regex
      ohai "Did you mean to perform a regular expression search?"
      ohai "Surround your query with /slashes/ to search by regex."
    end
    raise SEARCH_ERROR_QUEUE.pop unless SEARCH_ERROR_QUEUE.empty?
  end
  SEARCHABLE_TAPS = [
    %w{Homebrew nginx},
    %w{Homebrew apache},
    %w{Homebrew versions},
    %w{Homebrew dupes},
    %w{Homebrew games},
    %w{Homebrew science},
    %w{Homebrew completions},
    %w{Homebrew binary},
    %w{Homebrew python},
    %w{Homebrew php},
    %w{Homebrew x11},
    %w{Caskroom cask},
  ]
  def query_regexp(query)
    case query
    when %r{^/(.*)/$} then Regexp.new($1)
    else /.*#{Regexp.escape(query)}.*/i
    end
  end
  def search_taps(rx)
    SEARCHABLE_TAPS.map do |user, repo|
      Thread.new { search_tap(user, repo, rx) }
    end.inject([]) do |results, t|
      results.concat(t.value)
    end
  end
  def search_tap user, repo, rx
    if (HOMEBREW_LIBRARY/"Taps/#{user.downcase}/homebrew-#{repo.downcase}").directory? && \
       "#{user}/#{repo}" != "Caskroom/cask"
      return []
    end
    results = []
    tree = {}
    GitHub.open "https://api.github.com/repos/#{user}/homebrew-#{repo}/git/trees/HEAD?recursive=1" do |json|
      user = user.downcase if user == "Homebrew" # special handling for the Homebrew organization
      json["tree"].each do |object|
        next unless object["type"] == "blob"
        subtree, file = File.split(object["path"])
        if File.extname(file) == ".rb"
          tree[subtree] ||= []
          tree[subtree] << file
        end
      end
    end
    paths = tree["Formula"] || tree["HomebrewFormula"] || tree["Casks"] || tree["."] || []
    paths.each do |path|
      name = File.basename(path, ".rb")
      results << "#{user}/#{repo}/#{name}" if rx === name
    end
  rescue GitHub::HTTPNotFoundError => e
    opoo "Failed to search tap: #{user}/#{repo}. Please run `brew update`"
    []
  rescue GitHub::Error => e
    SEARCH_ERROR_QUEUE << e
    []
  else
    results
  end
  def search_formulae rx
    aliases = Formula.aliases
    results = (Formula.names+aliases).grep(rx).sort
    # Filter out aliases when the full name was also found
    results.reject do |name|
      canonical_name = Formulary.canonical_name(name)
      aliases.include?(name) && results.include?(canonical_name)
    end
  end
end
 |