/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to brzlib/plugins/grep/grep.py

  • Committer: Jelmer Vernooij
  • Date: 2017-05-21 12:41:27 UTC
  • mto: This revision was merged to the branch mainline in revision 6623.
  • Revision ID: jelmer@jelmer.uk-20170521124127-iv8etg0vwymyai6y
s/bzr/brz/ in apport config.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
# Copyright (C) 2010 Canonical Ltd
2
 
 
 
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
5
5
# the Free Software Foundation; either version 2 of the License, or
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
 
 
17
 
from io import BytesIO
18
 
import re
19
 
 
20
 
from .lazy_import import lazy_import
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
from __future__ import absolute_import
 
18
 
 
19
import sys
 
20
 
 
21
from brzlib.lazy_import import lazy_import
21
22
lazy_import(globals(), """
22
23
from fnmatch import fnmatch
23
 
 
24
 
from breezy._termcolor import color_string, FG
25
 
 
26
 
from breezy import (
 
24
import re
 
25
from cStringIO import StringIO
 
26
 
 
27
from brzlib._termcolor import color_string, re_color_string, FG
 
28
 
 
29
from brzlib.revisionspec import (
 
30
    RevisionSpec,
 
31
    RevisionSpec_revid,
 
32
    RevisionSpec_revno,
 
33
    )
 
34
from brzlib import (
 
35
    bzrdir,
27
36
    diff,
28
 
    )
29
 
""")
30
 
from . import (
31
 
    controldir,
32
37
    errors,
 
38
    lazy_regex,
33
39
    osutils,
34
40
    revision as _mod_revision,
35
41
    trace,
36
42
    )
37
 
from .revisionspec import (
38
 
    RevisionSpec,
39
 
    RevisionSpec_revid,
40
 
    RevisionSpec_revno,
41
 
    )
 
43
""")
42
44
 
43
45
_user_encoding = osutils.get_user_encoding()
44
46
 
47
49
    """Raised when a revision is not on left-hand history."""
48
50
 
49
51
 
50
 
class GrepOptions(object):
51
 
    """Container to pass around grep options.
52
 
 
53
 
    This class is used as a container to pass around user option and
54
 
    some other params (like outf) to processing functions. This makes
55
 
    it easier to add more options as grep evolves.
56
 
    """
57
 
    verbose = False
58
 
    ignore_case = False
59
 
    no_recursive = False
60
 
    from_root = False
61
 
    null = False
62
 
    levels = None
63
 
    line_number = False
64
 
    path_list = None
65
 
    revision = None
66
 
    pattern = None
67
 
    include = None
68
 
    exclude = None
69
 
    fixed_string = False
70
 
    files_with_matches = False
71
 
    files_without_match = False
72
 
    color = None
73
 
    diff = False
74
 
 
75
 
    # derived options
76
 
    recursive = None
77
 
    eol_marker = None
78
 
    patternc = None
79
 
    sub_patternc = None
80
 
    print_revno = None
81
 
    fixed_string = None
82
 
    outf = None
83
 
    show_color = False
84
 
 
85
 
 
86
52
def _rev_on_mainline(rev_tuple):
87
53
    """returns True is rev tuple is on mainline"""
88
54
    if len(rev_tuple) == 1:
91
57
 
92
58
 
93
59
# NOTE: _linear_view_revisions is basided on
94
 
# breezy.log._linear_view_revisions.
 
60
# brzlib.log._linear_view_revisions.
95
61
# This should probably be a common public API
96
62
def _linear_view_revisions(branch, start_rev_id, end_rev_id):
97
63
    # requires that start is older than end
108
74
 
109
75
 
110
76
# NOTE: _graph_view_revisions is copied from
111
 
# breezy.log._graph_view_revisions.
 
77
# brzlib.log._graph_view_revisions.
112
78
# This should probably be a common public API
113
79
def _graph_view_revisions(branch, start_rev_id, end_rev_id,
114
80
                          rebase_initial_depths=True):
151
117
 
152
118
 
153
119
def compile_pattern(pattern, flags=0):
 
120
    patternc = None
154
121
    try:
155
 
        return re.compile(pattern, flags)
156
 
    except re.error as e:
 
122
        # use python's re.compile as we need to catch re.error in case of bad pattern
 
123
        lazy_regex.reset_compile()
 
124
        patternc = re.compile(pattern, flags)
 
125
    except re.error, e:
157
126
        raise errors.BzrError("Invalid pattern: '%s'" % pattern)
158
 
    return None
 
127
    return patternc
159
128
 
160
129
 
161
130
def is_fixed_string(s):
162
 
    if re.match("^([A-Za-z0-9_]|\\s)*$", s):
 
131
    if re.match("^([A-Za-z0-9_]|\s)*$", s):
163
132
        return True
164
133
    return False
165
134
 
172
141
        self.opts = opts
173
142
        self.outf = opts.outf
174
143
        if opts.show_color:
 
144
            pat = opts.pattern.encode(_user_encoding, 'replace')
175
145
            if opts.fixed_string:
176
 
                self._old = opts.pattern
177
 
                self._new = color_string(opts.pattern, FG.BOLD_RED)
 
146
                self._old = pat
 
147
                self._new = color_string(pat, FG.BOLD_RED)
178
148
                self.get_writer = self._get_writer_fixed_highlighted
179
149
            else:
180
150
                flags = opts.patternc.flags
181
 
                self._sub = re.compile(
182
 
                    opts.pattern.join(("((?:", ")+)")), flags).sub
 
151
                self._sub = re.compile(pat.join(("((?:",")+)")), flags).sub
183
152
                self._highlight = color_string("\\1", FG.BOLD_RED)
184
153
                self.get_writer = self._get_writer_regexp_highlighted
185
154
        else:
189
158
        """Get function for writing file headers"""
190
159
        write = self.outf.write
191
160
        eol_marker = self.opts.eol_marker
192
 
 
193
161
        def _line_writer(line):
194
162
            write(line + eol_marker)
195
 
 
196
163
        def _line_writer_color(line):
197
164
            write(FG.BOLD_MAGENTA + line + FG.NONE + eol_marker)
198
165
        if self.opts.show_color:
205
172
        """Get function for writing revno lines"""
206
173
        write = self.outf.write
207
174
        eol_marker = self.opts.eol_marker
208
 
 
209
175
        def _line_writer(line):
210
176
            write(line + eol_marker)
211
 
 
212
177
        def _line_writer_color(line):
213
178
            write(FG.BOLD_BLUE + line + FG.NONE + eol_marker)
214
179
        if self.opts.show_color:
221
186
        """Get function for writing uncoloured output"""
222
187
        write = self.outf.write
223
188
        eol_marker = self.opts.eol_marker
224
 
 
225
189
        def _line_writer(line):
226
190
            write(line + eol_marker)
227
191
        return _line_writer
230
194
        """Get function for writing output with regexp match highlighted"""
231
195
        _line_writer = self._get_writer_plain()
232
196
        sub, highlight = self._sub, self._highlight
233
 
 
234
197
        def _line_writer_regexp_highlighted(line):
235
198
            """Write formatted line with matched pattern highlighted"""
236
199
            return _line_writer(line=sub(highlight, line))
240
203
        """Get function for writing output with search string highlighted"""
241
204
        _line_writer = self._get_writer_plain()
242
205
        old, new = self._old, self._new
243
 
 
244
206
        def _line_writer_fixed_highlighted(line):
245
207
            """Write formatted line with string searched for highlighted"""
246
208
            return _line_writer(line=line.replace(old, new))
249
211
 
250
212
def grep_diff(opts):
251
213
    wt, branch, relpath = \
252
 
        controldir.ControlDir.open_containing_tree_or_branch('.')
253
 
    with branch.lock_read():
 
214
        bzrdir.BzrDir.open_containing_tree_or_branch('.')
 
215
    branch.lock_read()
 
216
    try:
254
217
        if opts.revision:
255
218
            start_rev = opts.revision[0]
256
219
        else:
257
220
            # if no revision is sepcified for diff grep we grep all changesets.
258
221
            opts.revision = [RevisionSpec.from_string('revno:1'),
259
 
                             RevisionSpec.from_string('last:1')]
 
222
                RevisionSpec.from_string('last:1')]
260
223
            start_rev = opts.revision[0]
261
224
        start_revid = start_rev.as_revision_id(branch)
262
 
        if start_revid == b'null:':
 
225
        if start_revid == 'null:':
263
226
            return
264
227
        srevno_tuple = branch.revision_id_to_dotted_revno(start_revid)
265
228
        if len(opts.revision) == 2:
269
232
                end_revno, end_revid = branch.last_revision_info()
270
233
            erevno_tuple = branch.revision_id_to_dotted_revno(end_revid)
271
234
 
272
 
            grep_mainline = (_rev_on_mainline(srevno_tuple)
273
 
                             and _rev_on_mainline(erevno_tuple))
 
235
            grep_mainline = (_rev_on_mainline(srevno_tuple) and
 
236
                _rev_on_mainline(erevno_tuple))
274
237
 
275
238
            # ensure that we go in reverse order
276
239
            if srevno_tuple > erevno_tuple:
281
244
            # faster when we don't want to look at merged revs. We try this
282
245
            # with _linear_view_revisions. If all revs are to be grepped we
283
246
            # use the slower _graph_view_revisions
284
 
            if opts.levels == 1 and grep_mainline:
285
 
                given_revs = _linear_view_revisions(
286
 
                    branch, start_revid, end_revid)
 
247
            if opts.levels==1 and grep_mainline:
 
248
                given_revs = _linear_view_revisions(branch, start_revid, end_revid)
287
249
            else:
288
 
                given_revs = _graph_view_revisions(
289
 
                    branch, start_revid, end_revid)
 
250
                given_revs = _graph_view_revisions(branch, start_revid, end_revid)
290
251
        else:
291
252
            # We do an optimization below. For grepping a specific revison
292
253
            # We don't need to call _graph_view_revisions which is slow.
296
257
            start_rev_tuple = (start_revid, start_revno, 0)
297
258
            given_revs = [start_rev_tuple]
298
259
        repo = branch.repository
299
 
        diff_pattern = re.compile(
300
 
            b"^[+\\-].*(" + opts.pattern.encode(_user_encoding) + b")")
301
 
        file_pattern = re.compile(b"=== (modified|added|removed) file '.*'")
 
260
        diff_pattern = re.compile("^[+\-].*(" + opts.pattern + ")")
 
261
        file_pattern = re.compile("=== (modified|added|removed) file '.*'", re.UNICODE)
302
262
        outputter = _GrepDiffOutputter(opts)
303
263
        writeline = outputter.get_writer()
304
264
        writerevno = outputter.get_revision_header_writer()
309
269
                # with level=1 show only top level
310
270
                continue
311
271
 
312
 
            rev_spec = RevisionSpec_revid.from_string(
313
 
                "revid:" + revid.decode('utf-8'))
 
272
            rev_spec = RevisionSpec_revid.from_string("revid:"+revid)
314
273
            new_rev = repo.get_revision(revid)
315
274
            new_tree = rev_spec.as_tree(branch)
316
275
            if len(new_rev.parent_ids) == 0:
318
277
            else:
319
278
                ancestor_id = new_rev.parent_ids[0]
320
279
            old_tree = repo.revision_tree(ancestor_id)
321
 
            s = BytesIO()
 
280
            s = StringIO()
322
281
            diff.show_diff_trees(old_tree, new_tree, s,
323
 
                                 old_label='', new_label='')
 
282
                old_label='', new_label='')
324
283
            display_revno = True
325
284
            display_file = False
326
285
            file_header = None
334
293
                        writerevno("=== revno:%s ===" % (revno,))
335
294
                        display_revno = False
336
295
                    if display_file:
337
 
                        writefileheader(
338
 
                            "  %s" % (file_header.decode(file_encoding, 'replace'),))
 
296
                        writefileheader("  %s" % (file_header,))
339
297
                        display_file = False
340
298
                    line = line.decode(file_encoding, 'replace')
341
299
                    writeline("    %s" % (line,))
 
300
    finally:
 
301
        branch.unlock()
342
302
 
343
303
 
344
304
def versioned_grep(opts):
345
305
    wt, branch, relpath = \
346
 
        controldir.ControlDir.open_containing_tree_or_branch('.')
347
 
    with branch.lock_read():
 
306
        bzrdir.BzrDir.open_containing_tree_or_branch('.')
 
307
    branch.lock_read()
 
308
    try:
348
309
        start_rev = opts.revision[0]
349
310
        start_revid = start_rev.as_revision_id(branch)
350
311
        if start_revid is None:
359
320
                end_revno, end_revid = branch.last_revision_info()
360
321
            erevno_tuple = branch.revision_id_to_dotted_revno(end_revid)
361
322
 
362
 
            grep_mainline = (_rev_on_mainline(srevno_tuple)
363
 
                             and _rev_on_mainline(erevno_tuple))
 
323
            grep_mainline = (_rev_on_mainline(srevno_tuple) and
 
324
                _rev_on_mainline(erevno_tuple))
364
325
 
365
326
            # ensure that we go in reverse order
366
327
            if srevno_tuple > erevno_tuple:
372
333
            # with _linear_view_revisions. If all revs are to be grepped we
373
334
            # use the slower _graph_view_revisions
374
335
            if opts.levels == 1 and grep_mainline:
375
 
                given_revs = _linear_view_revisions(
376
 
                    branch, start_revid, end_revid)
 
336
                given_revs = _linear_view_revisions(branch, start_revid, end_revid)
377
337
            else:
378
 
                given_revs = _graph_view_revisions(
379
 
                    branch, start_revid, end_revid)
 
338
                given_revs = _graph_view_revisions(branch, start_revid, end_revid)
380
339
        else:
381
340
            # We do an optimization below. For grepping a specific revison
382
341
            # We don't need to call _graph_view_revisions which is slow.
394
353
                # with level=1 show only top level
395
354
                continue
396
355
 
397
 
            rev = RevisionSpec_revid.from_string(
398
 
                "revid:" + revid.decode('utf-8'))
 
356
            rev = RevisionSpec_revid.from_string("revid:"+revid)
399
357
            tree = rev.as_tree(branch)
400
358
            for path in opts.path_list:
401
 
                tree_path = osutils.pathjoin(relpath, path)
402
 
                if not tree.has_filename(tree_path):
403
 
                    trace.warning("Skipped unknown file '%s'.", path)
 
359
                path_for_id = osutils.pathjoin(relpath, path)
 
360
                id = tree.path2id(path_for_id)
 
361
                if not id:
 
362
                    trace.warning("Skipped unknown file '%s'." % path)
404
363
                    continue
405
364
 
406
365
                if osutils.isdir(path):
407
366
                    path_prefix = path
408
367
                    dir_grep(tree, path, relpath, opts, revno, path_prefix)
409
368
                else:
410
 
                    versioned_file_grep(
411
 
                        tree, tree_path, '.', path, opts, revno)
 
369
                    versioned_file_grep(tree, id, '.', path, opts, revno)
 
370
    finally:
 
371
        branch.unlock()
412
372
 
413
373
 
414
374
def workingtree_grep(opts):
415
 
    revno = opts.print_revno = None  # for working tree set revno to None
 
375
    revno = opts.print_revno = None # for working tree set revno to None
416
376
 
417
377
    tree, branch, relpath = \
418
 
        controldir.ControlDir.open_containing_tree_or_branch('.')
 
378
        bzrdir.BzrDir.open_containing_tree_or_branch('.')
419
379
    if not tree:
420
380
        msg = ('Cannot search working tree. Working tree not found.\n'
421
 
               'To search for specific revision in history use the -r option.')
 
381
            'To search for specific revision in history use the -r option.')
422
382
        raise errors.BzrCommandError(msg)
423
383
 
424
384
    # GZ 2010-06-02: Shouldn't be smuggling this on opts, but easy for now
425
385
    opts.outputter = _Outputter(opts)
426
386
 
427
 
    with tree.lock_read():
 
387
    tree.lock_read()
 
388
    try:
428
389
        for path in opts.path_list:
429
390
            if osutils.isdir(path):
430
391
                path_prefix = path
431
392
                dir_grep(tree, path, relpath, opts, revno, path_prefix)
432
393
            else:
433
 
                with open(path, 'rb') as f:
434
 
                    _file_grep(f.read(), path, opts, revno)
 
394
                _file_grep(open(path).read(), path, opts, revno)
 
395
    finally:
 
396
        tree.unlock()
435
397
 
436
398
 
437
399
def _skip_file(include, exclude, path):
446
408
    # setup relpath to open files relative to cwd
447
409
    rpath = relpath
448
410
    if relpath:
449
 
        rpath = osutils.pathjoin('..', relpath)
 
411
        rpath = osutils.pathjoin('..',relpath)
450
412
 
451
413
    from_dir = osutils.pathjoin(relpath, path)
452
414
    if opts.from_root:
453
415
        # start searching recursively from root
454
 
        from_dir = None
455
 
        recursive = True
 
416
        from_dir=None
 
417
        recursive=True
456
418
 
457
419
    to_grep = []
458
420
    to_grep_append = to_grep.append
460
422
    #                and hits manually refilled. Could do this again if it was
461
423
    #                for a good reason, otherwise cache might want purging.
462
424
    outputter = opts.outputter
463
 
    for fp, fc, fkind, entry in tree.list_files(
464
 
            include_root=False, from_dir=from_dir, recursive=opts.recursive):
 
425
    for fp, fc, fkind, fid, entry in tree.list_files(include_root=False,
 
426
        from_dir=from_dir, recursive=opts.recursive):
465
427
 
466
428
        if _skip_file(opts.include, opts.exclude, fp):
467
429
            continue
468
430
 
469
431
        if fc == 'V' and fkind == 'file':
470
 
            tree_path = osutils.pathjoin(from_dir if from_dir else '', fp)
471
 
            if revno is not None:
 
432
            if revno != None:
472
433
                # If old result is valid, print results immediately.
473
434
                # Otherwise, add file info to to_grep so that the
474
435
                # loop later will get chunks and grep them
475
 
                cache_id = tree.get_file_revision(tree_path)
 
436
                cache_id = tree.get_file_revision(fid)
476
437
                if cache_id in outputter.cache:
477
438
                    # GZ 2010-06-05: Not really sure caching and re-outputting
478
439
                    #                the old path is really the right thing,
479
440
                    #                but it's what the old code seemed to do
480
441
                    outputter.write_cached_lines(cache_id, revno)
481
442
                else:
482
 
                    to_grep_append((tree_path, (fp, tree_path)))
 
443
                    to_grep_append((fid, (fp, fid)))
483
444
            else:
484
445
                # we are grepping working tree.
485
446
                if from_dir is None:
489
450
                if opts.files_with_matches or opts.files_without_match:
490
451
                    # Optimize for wtree list-only as we don't need to read the
491
452
                    # entire file
492
 
                    with open(path_for_file, 'rb', buffering=4096) as file:
493
 
                        _file_grep_list_only_wtree(file, fp, opts, path_prefix)
 
453
                    file = open(path_for_file, 'r', buffering=4096)
 
454
                    _file_grep_list_only_wtree(file, fp, opts, path_prefix)
494
455
                else:
495
 
                    with open(path_for_file, 'rb') as f:
496
 
                        _file_grep(f.read(), fp, opts, revno, path_prefix)
 
456
                    file_text = open(path_for_file, 'r').read()
 
457
                    _file_grep(file_text, fp, opts, revno, path_prefix)
497
458
 
498
 
    if revno is not None:  # grep versioned files
499
 
        for (path, tree_path), chunks in tree.iter_files_bytes(to_grep):
 
459
    if revno != None: # grep versioned files
 
460
        for (path, fid), chunks in tree.iter_files_bytes(to_grep):
500
461
            path = _make_display_path(relpath, path)
501
 
            _file_grep(b''.join(chunks), path, opts, revno, path_prefix,
502
 
                       tree.get_file_revision(tree_path))
 
462
            _file_grep(chunks[0], path, opts, revno, path_prefix,
 
463
                tree.get_file_revision(fid, path))
503
464
 
504
465
 
505
466
def _make_display_path(relpath, path):
517
478
    return path
518
479
 
519
480
 
520
 
def versioned_file_grep(tree, tree_path, relpath, path, opts, revno, path_prefix=None):
 
481
def versioned_file_grep(tree, id, relpath, path, opts, revno, path_prefix = None):
521
482
    """Create a file object for the specified id and pass it on to _file_grep.
522
483
    """
523
484
 
524
485
    path = _make_display_path(relpath, path)
525
 
    file_text = tree.get_file_text(tree_path)
 
486
    file_text = tree.get_file_text(id)
526
487
    _file_grep(file_text, path, opts, revno, path_prefix)
527
488
 
528
489
 
535
496
 
536
497
def _file_grep_list_only_wtree(file, path, opts, path_prefix=None):
537
498
    # test and skip binary files
538
 
    if b'\x00' in file.read(1024):
 
499
    if '\x00' in file.read(1024):
539
500
        if opts.verbose:
540
 
            trace.warning("Binary file '%s' skipped.", path)
 
501
            trace.warning("Binary file '%s' skipped." % path)
541
502
        return
542
503
 
543
 
    file.seek(0)  # search from beginning
 
504
    file.seek(0) # search from beginning
544
505
 
545
506
    found = False
546
507
    if opts.fixed_string:
549
510
            if pattern in line:
550
511
                found = True
551
512
                break
552
 
    else:  # not fixed_string
 
513
    else: # not fixed_string
553
514
        for line in file:
554
515
            if opts.patternc.search(line):
555
516
                found = True
556
517
                break
557
518
 
558
519
    if (opts.files_with_matches and found) or \
559
 
            (opts.files_without_match and not found):
 
520
        (opts.files_without_match and not found):
560
521
        if path_prefix and path_prefix != '.':
561
522
            # user has passed a dir arg, show that as result prefix
562
523
            path = osutils.pathjoin(path_prefix, path)
569
530
    The idea here is to do this work only once per run, and finally return a
570
531
    function that will do the minimum amount possible for each match.
571
532
    """
572
 
 
573
533
    def __init__(self, opts, use_cache=False):
574
534
        self.outf = opts.outf
575
535
        if use_cache:
584
544
        no_line = opts.files_with_matches or opts.files_without_match
585
545
 
586
546
        if opts.show_color:
 
547
            pat = opts.pattern.encode(_user_encoding, 'replace')
587
548
            if no_line:
588
549
                self.get_writer = self._get_writer_plain
589
550
            elif opts.fixed_string:
590
 
                self._old = opts.pattern
591
 
                self._new = color_string(opts.pattern, FG.BOLD_RED)
 
551
                self._old = pat
 
552
                self._new = color_string(pat, FG.BOLD_RED)
592
553
                self.get_writer = self._get_writer_fixed_highlighted
593
554
            else:
594
555
                flags = opts.patternc.flags
595
 
                self._sub = re.compile(
596
 
                    opts.pattern.join(("((?:", ")+)")), flags).sub
 
556
                self._sub = re.compile(pat.join(("((?:",")+)")), flags).sub
597
557
                self._highlight = color_string("\\1", FG.BOLD_RED)
598
558
                self.get_writer = self._get_writer_regexp_highlighted
599
559
            path_start = FG.MAGENTA
624
584
    def _get_writer_plain(self, path, revno, cache_id):
625
585
        """Get function for writing uncoloured output"""
626
586
        per_line = self._format_perline
627
 
        start = self._format_initial % {"path": path, "revno": revno}
 
587
        start = self._format_initial % {"path":path, "revno":revno}
628
588
        write = self.outf.write
629
589
        if self.cache is not None and cache_id is not None:
630
590
            result_list = []
631
591
            self.cache[cache_id] = path, result_list
632
592
            add_to_cache = result_list.append
633
 
 
634
593
            def _line_cache_and_writer(**kwargs):
635
594
                """Write formatted line and cache arguments"""
636
595
                end = per_line % kwargs
637
596
                add_to_cache(end)
638
597
                write(start + end)
639
598
            return _line_cache_and_writer
640
 
 
641
599
        def _line_writer(**kwargs):
642
600
            """Write formatted line from arguments given by underlying opts"""
643
601
            write(start + per_line % kwargs)
646
604
    def write_cached_lines(self, cache_id, revno):
647
605
        """Write cached results out again for new revision"""
648
606
        cached_path, cached_matches = self.cache[cache_id]
649
 
        start = self._format_initial % {"path": cached_path, "revno": revno}
 
607
        start = self._format_initial % {"path":cached_path, "revno":revno}
650
608
        write = self.outf.write
651
609
        for end in cached_matches:
652
610
            write(start + end)
655
613
        """Get function for writing output with regexp match highlighted"""
656
614
        _line_writer = self._get_writer_plain(path, revno, cache_id)
657
615
        sub, highlight = self._sub, self._highlight
658
 
 
659
616
        def _line_writer_regexp_highlighted(line, **kwargs):
660
617
            """Write formatted line with matched pattern highlighted"""
661
618
            return _line_writer(line=sub(highlight, line), **kwargs)
665
622
        """Get function for writing output with search string highlighted"""
666
623
        _line_writer = self._get_writer_plain(path, revno, cache_id)
667
624
        old, new = self._old, self._new
668
 
 
669
625
        def _line_writer_fixed_highlighted(line, **kwargs):
670
626
            """Write formatted line with string searched for highlighted"""
671
627
            return _line_writer(line=line.replace(old, new), **kwargs)
674
630
 
675
631
def _file_grep(file_text, path, opts, revno, path_prefix=None, cache_id=None):
676
632
    # test and skip binary files
677
 
    if b'\x00' in file_text[:1024]:
 
633
    if '\x00' in file_text[:1024]:
678
634
        if opts.verbose:
679
 
            trace.warning("Binary file '%s' skipped.", path)
 
635
            trace.warning("Binary file '%s' skipped." % path)
680
636
        return
681
637
 
682
638
    if path_prefix and path_prefix != '.':
693
649
 
694
650
    if opts.files_with_matches or opts.files_without_match:
695
651
        if opts.fixed_string:
696
 
            found = pattern in file_text
 
652
            if sys.platform > (2, 5):
 
653
                found = pattern in file_text
 
654
            else:
 
655
                for line in file_text.splitlines():
 
656
                    if pattern in line:
 
657
                        found = True
 
658
                        break
 
659
                else:
 
660
                    found = False
697
661
        else:
698
662
            search = opts.patternc.search
699
 
            if b"$" not in pattern:
 
663
            if "$" not in pattern:
700
664
                found = search(file_text) is not None
701
665
            else:
702
666
                for line in file_text.splitlines():
710
674
            writeline()
711
675
    elif opts.fixed_string:
712
676
        # Fast path for no match, search through the entire file at once rather
713
 
        # than a line at a time. <http://effbot.org/zone/stringlib.htm>
714
 
        i = file_text.find(pattern)
715
 
        if i == -1:
716
 
            return
717
 
        b = file_text.rfind(b"\n", 0, i) + 1
718
 
        if opts.line_number:
719
 
            start = file_text.count(b"\n", 0, b) + 1
720
 
        file_text = file_text[b:]
 
677
        # than a line at a time. However, we don't want this without Python 2.5
 
678
        # as the quick string search algorithm wasn't implemented till then:
 
679
        # <http://effbot.org/zone/stringlib.htm>
 
680
        if sys.version_info > (2, 5):
 
681
            i = file_text.find(pattern)
 
682
            if i == -1:
 
683
                return
 
684
            b = file_text.rfind("\n", 0, i) + 1
 
685
            if opts.line_number:
 
686
                start = file_text.count("\n", 0, b) + 1
 
687
            file_text = file_text[b:]
 
688
        else:
 
689
            start = 1
721
690
        if opts.line_number:
722
691
            for index, line in enumerate(file_text.splitlines()):
723
692
                if pattern in line:
724
693
                    line = line.decode(file_encoding, 'replace')
725
 
                    writeline(lineno=index + start, line=line)
 
694
                    writeline(lineno=index+start, line=line)
726
695
        else:
727
696
            for line in file_text.splitlines():
728
697
                if pattern in line:
733
702
        # standard cases, but perhaps could try and detect backtracking
734
703
        # patterns here and avoid whole text search in those cases
735
704
        search = opts.patternc.search
736
 
        if b"$" not in pattern:
 
705
        if "$" not in pattern:
737
706
            # GZ 2010-06-05: Grr, re.MULTILINE can't save us when searching
738
707
            #                through revisions as bazaar returns binary mode
739
708
            #                and trailing \r breaks $ as line ending match
740
709
            m = search(file_text)
741
710
            if m is None:
742
711
                return
743
 
            b = file_text.rfind(b"\n", 0, m.start()) + 1
 
712
            b = file_text.rfind("\n", 0, m.start()) + 1
744
713
            if opts.line_number:
745
 
                start = file_text.count(b"\n", 0, b) + 1
 
714
                start = file_text.count("\n", 0, b) + 1
746
715
            file_text = file_text[b:]
747
716
        else:
748
717
            start = 1
750
719
            for index, line in enumerate(file_text.splitlines()):
751
720
                if search(line):
752
721
                    line = line.decode(file_encoding, 'replace')
753
 
                    writeline(lineno=index + start, line=line)
 
722
                    writeline(lineno=index+start, line=line)
754
723
        else:
755
724
            for line in file_text.splitlines():
756
725
                if search(line):
757
726
                    line = line.decode(file_encoding, 'replace')
758
727
                    writeline(line=line)
 
728