aboutsummaryrefslogtreecommitdiffstats
path: root/Library/Homebrew/cmd/search.rb
blob: f71a14ba10826076b75d6461599cb90787542b98 (plain)
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
#:  * `search`, `-S`:
#:    Display all locally available formulae for brewing (including tapped ones).
#:    No online search is performed if called without arguments.
#:
#:  * `search` [`--desc`] (<text>|`/`<text>`/`):
#:    Perform a substring search of formula names for <text>. If <text> is
#:    surrounded with slashes, then it is interpreted as a regular expression.
#:    The search for <text> is extended online to some popular taps.
#:
#:    If `--desc` is passed, browse available packages matching <text> including a
#:    description for each.
#:
#:  * `search` (`--debian`|`--fedora`|`--fink`|`--macports`|`--opensuse`|`--ubuntu`) <text>:
#:    Search for <text> in the given package manager's list.

require "formula"
require "missing_formula"
require "utils"
require "official_taps"
require "descriptions"

module Homebrew
  module_function

  def search
    if ARGV.empty?
      puts Formatter.columns(Formula.full_names)
    elsif 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&section=all"
    elsif ARGV.include? "--opensuse"
      exec_browser "https://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&section=all"
    elsif ARGV.include? "--desc"
      query = ARGV.next
      regex = query_regexp(query)
      Descriptions.search(regex, :desc).print
    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

      results = Array(result)
      puts Formatter.columns(results) unless results.empty?
    else
      query = ARGV.first
      regex = query_regexp(query)
      local_results = search_formulae(regex)
      puts Formatter.columns(local_results) unless local_results.empty?
      tap_results = search_taps(query)
      puts Formatter.columns(tap_results) unless tap_results.empty?

      if $stdout.tty?
        count = local_results.length + tap_results.length

        if reason = Homebrew::MissingFormula.reason(query, silent: true)
          if count > 0
            puts
            puts "If you meant #{query.inspect} specifically:"
          end
          puts reason
        elsif count.zero?
          puts "No formula found for #{query.inspect}."
          GitHub.print_pull_requests_matching(query)
        end
      end
    end

    return unless $stdout.tty?
    return if ARGV.empty?
    metacharacters = %w[\\ | ( ) [ ] { } ^ $ * + ?].freeze
    return unless metacharacters.any? do |char|
      ARGV.any? do |arg|
        arg.include?(char) && !arg.start_with?("/")
      end
    end
    ohai <<-EOS.undent
      Did you mean to perform a regular expression search?
      Surround your query with /slashes/ to search locally by regex.
    EOS
  end

  def query_regexp(query)
    case query
    when %r{^/(.*)/$} then Regexp.new($1)
    else /.*#{Regexp.escape(query)}.*/i
    end
  rescue RegexpError
    odie "#{query} is not a valid regex"
  end

  def search_taps(query)
    valid_dirnames = ["Formula", "HomebrewFormula", "Casks", "."].freeze
    matches = GitHub.search_code("user:Homebrew", "user:caskroom", "filename:#{query}", "extension:rb")
    [*matches].map do |match|
      dirname, filename = File.split(match["path"])
      next unless valid_dirnames.include?(dirname)
      tap = Tap.fetch(match["repository"]["full_name"])
      next if tap.installed?
      "#{tap.name}/#{File.basename(filename, ".rb")}"
    end.compact
  end

  def search_formulae(regex)
    aliases = Formula.alias_full_names
    results = (Formula.full_names+aliases).grep(regex).sort

    results.map do |name|
      begin
        formula = Formulary.factory(name)
        canonical_name = formula.name
        canonical_full_name = formula.full_name
      rescue
        canonical_name = canonical_full_name = name
      end

      # Ignore aliases from results when the full name was also found
      next if aliases.include?(name) && results.include?(canonical_full_name)

      if (HOMEBREW_CELLAR/canonical_name).directory?
        pretty_installed(name)
      else
        name
      end
    end.compact
  end
end