blob: 1157355d695e9f11ec75e7115fc69ca7bed66c2f [file] [log] [blame]
Shawn O. Pearceb812a362009-04-10 20:37:47 -07001#
2# Copyright (C) 2009 The Android Open Source Project
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
Sarah Owenscecd1d82012-11-01 22:59:27 -070016from __future__ import print_function
Shawn O. Pearceb812a362009-04-10 20:37:47 -070017import sys
Shawn O. Pearceb812a362009-04-10 20:37:47 -070018from color import Coloring
19from command import PagedCommand
Shawn O. Pearcef0d4c362009-06-12 09:33:48 -070020from git_command import git_require, GitCommand
Shawn O. Pearceb812a362009-04-10 20:37:47 -070021
22class GrepColoring(Coloring):
23 def __init__(self, config):
24 Coloring.__init__(self, config, 'grep')
25 self.project = self.printer('project', attr='bold')
26
27class Grep(PagedCommand):
28 common = True
29 helpSummary = "Print lines matching a pattern"
30 helpUsage = """
31%prog {pattern | -e pattern} [<project>...]
32"""
33 helpDescription = """
34Search for the specified patterns in all project files.
35
Mike Frysingerb8f7bb02018-10-10 01:05:11 -040036# Boolean Options
Shawn O. Pearceb812a362009-04-10 20:37:47 -070037
38The following options can appear as often as necessary to express
39the pattern to locate:
40
41 -e PATTERN
42 --and, --or, --not, -(, -)
43
44Further, the -r/--revision option may be specified multiple times
45in order to scan multiple trees. If the same file matches in more
46than one tree, only the first result is reported, prefixed by the
47revision name it was found under.
48
Mike Frysingerb8f7bb02018-10-10 01:05:11 -040049# Examples
Shawn O. Pearceb812a362009-04-10 20:37:47 -070050
51Look for a line that has '#define' and either 'MAX_PATH or 'PATH_MAX':
52
David Pursehouse1d947b32012-10-25 12:23:11 +090053 repo grep -e '#define' --and -\\( -e MAX_PATH -e PATH_MAX \\)
Shawn O. Pearceb812a362009-04-10 20:37:47 -070054
55Look for a line that has 'NODE' or 'Unexpected' in files that
56contain a line that matches both expressions:
57
58 repo grep --all-match -e NODE -e Unexpected
59
60"""
61
62 def _Options(self, p):
63 def carry(option,
64 opt_str,
65 value,
66 parser):
67 pt = getattr(parser.values, 'cmd_argv', None)
68 if pt is None:
69 pt = []
70 setattr(parser.values, 'cmd_argv', pt)
71
72 if opt_str == '-(':
73 pt.append('(')
74 elif opt_str == '-)':
75 pt.append(')')
76 else:
77 pt.append(opt_str)
78
79 if value is not None:
80 pt.append(value)
81
82 g = p.add_option_group('Sources')
83 g.add_option('--cached',
84 action='callback', callback=carry,
85 help='Search the index, instead of the work tree')
David Pursehouse8f62fb72012-11-14 12:09:38 +090086 g.add_option('-r', '--revision',
Shawn O. Pearceb812a362009-04-10 20:37:47 -070087 dest='revision', action='append', metavar='TREEish',
88 help='Search TREEish, instead of the work tree')
89
90 g = p.add_option_group('Pattern')
91 g.add_option('-e',
92 action='callback', callback=carry,
93 metavar='PATTERN', type='str',
94 help='Pattern to search for')
95 g.add_option('-i', '--ignore-case',
96 action='callback', callback=carry,
97 help='Ignore case differences')
David Pursehouse8f62fb72012-11-14 12:09:38 +090098 g.add_option('-a', '--text',
Shawn O. Pearceb812a362009-04-10 20:37:47 -070099 action='callback', callback=carry,
100 help="Process binary files as if they were text")
101 g.add_option('-I',
102 action='callback', callback=carry,
103 help="Don't match the pattern in binary files")
104 g.add_option('-w', '--word-regexp',
105 action='callback', callback=carry,
106 help='Match the pattern only at word boundaries')
107 g.add_option('-v', '--invert-match',
108 action='callback', callback=carry,
109 help='Select non-matching lines')
110 g.add_option('-G', '--basic-regexp',
111 action='callback', callback=carry,
112 help='Use POSIX basic regexp for patterns (default)')
113 g.add_option('-E', '--extended-regexp',
114 action='callback', callback=carry,
115 help='Use POSIX extended regexp for patterns')
116 g.add_option('-F', '--fixed-strings',
117 action='callback', callback=carry,
118 help='Use fixed strings (not regexp) for pattern')
119
120 g = p.add_option_group('Pattern Grouping')
121 g.add_option('--all-match',
122 action='callback', callback=carry,
123 help='Limit match to lines that have all patterns')
124 g.add_option('--and', '--or', '--not',
125 action='callback', callback=carry,
126 help='Boolean operators to combine patterns')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900127 g.add_option('-(', '-)',
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700128 action='callback', callback=carry,
129 help='Boolean operator grouping')
130
131 g = p.add_option_group('Output')
132 g.add_option('-n',
133 action='callback', callback=carry,
134 help='Prefix the line number to matching lines')
135 g.add_option('-C',
136 action='callback', callback=carry,
137 metavar='CONTEXT', type='str',
138 help='Show CONTEXT lines around match')
139 g.add_option('-B',
140 action='callback', callback=carry,
141 metavar='CONTEXT', type='str',
142 help='Show CONTEXT lines before match')
143 g.add_option('-A',
144 action='callback', callback=carry,
145 metavar='CONTEXT', type='str',
146 help='Show CONTEXT lines after match')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900147 g.add_option('-l', '--name-only', '--files-with-matches',
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700148 action='callback', callback=carry,
149 help='Show only file names containing matching lines')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900150 g.add_option('-L', '--files-without-match',
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700151 action='callback', callback=carry,
152 help='Show only file names not containing matching lines')
153
154
155 def Execute(self, opt, args):
156 out = GrepColoring(self.manifest.manifestProject.config)
157
158 cmd_argv = ['grep']
David Pursehouse8f62fb72012-11-14 12:09:38 +0900159 if out.is_on and git_require((1, 6, 3)):
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700160 cmd_argv.append('--color')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900161 cmd_argv.extend(getattr(opt, 'cmd_argv', []))
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700162
163 if '-e' not in cmd_argv:
164 if not args:
165 self.Usage()
166 cmd_argv.append('-e')
167 cmd_argv.append(args[0])
168 args = args[1:]
169
170 projects = self.GetProjects(args)
171
172 full_name = False
173 if len(projects) > 1:
174 cmd_argv.append('--full-name')
175 full_name = True
176
177 have_rev = False
178 if opt.revision:
179 if '--cached' in cmd_argv:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700180 print('fatal: cannot combine --cached and --revision', file=sys.stderr)
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700181 sys.exit(1)
182 have_rev = True
183 cmd_argv.extend(opt.revision)
184 cmd_argv.append('--')
185
186 bad_rev = False
187 have_match = False
188
189 for project in projects:
190 p = GitCommand(project,
191 cmd_argv,
192 bare = False,
193 capture_stdout = True,
194 capture_stderr = True)
195 if p.Wait() != 0:
196 # no results
197 #
198 if p.stderr:
199 if have_rev and 'fatal: ambiguous argument' in p.stderr:
200 bad_rev = True
201 else:
202 out.project('--- project %s ---' % project.relpath)
203 out.nl()
Sebastian Schmidtfeb39d62010-06-02 17:18:13 +0200204 out.write("%s", p.stderr)
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700205 out.nl()
206 continue
207 have_match = True
208
209 # We cut the last element, to avoid a blank line.
210 #
211 r = p.stdout.split('\n')
212 r = r[0:-1]
213
214 if have_rev and full_name:
215 for line in r:
216 rev, line = line.split(':', 1)
Sebastian Schmidtfeb39d62010-06-02 17:18:13 +0200217 out.write("%s", rev)
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700218 out.write(':')
219 out.project(project.relpath)
220 out.write('/')
Sebastian Schmidtfeb39d62010-06-02 17:18:13 +0200221 out.write("%s", line)
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700222 out.nl()
223 elif full_name:
224 for line in r:
225 out.project(project.relpath)
226 out.write('/')
Sebastian Schmidtfeb39d62010-06-02 17:18:13 +0200227 out.write("%s", line)
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700228 out.nl()
229 else:
230 for line in r:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700231 print(line)
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700232
233 if have_match:
234 sys.exit(0)
235 elif have_rev and bad_rev:
236 for r in opt.revision:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700237 print("error: can't search revision %s" % r, file=sys.stderr)
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700238 sys.exit(1)
239 else:
240 sys.exit(1)