/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 breezy/plugins/grep/grep.py

  • Committer: Jelmer Vernooij
  • Date: 2018-02-18 21:42:57 UTC
  • mto: This revision was merged to the branch mainline in revision 6859.
  • Revision ID: jelmer@jelmer.uk-20180218214257-jpevutp1wa30tz3v
Update TODO to reference Breezy, not Bazaar.

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
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
from __future__ import absolute_import
 
18
 
18
19
import re
 
20
import sys
19
21
 
20
 
from .lazy_import import lazy_import
 
22
from ...lazy_import import lazy_import
21
23
lazy_import(globals(), """
22
24
from fnmatch import fnmatch
23
25
 
24
26
from breezy._termcolor import color_string, FG
25
27
 
26
28
from breezy import (
 
29
    controldir,
27
30
    diff,
 
31
    errors,
 
32
    lazy_regex,
 
33
    revision as _mod_revision,
28
34
    )
29
35
""")
30
 
from . import (
31
 
    controldir,
32
 
    errors,
 
36
from breezy import (
33
37
    osutils,
34
 
    revision as _mod_revision,
35
38
    trace,
36
39
    )
37
 
from .revisionspec import (
 
40
from breezy.revisionspec import (
38
41
    RevisionSpec,
39
42
    RevisionSpec_revid,
40
43
    RevisionSpec_revno,
41
44
    )
 
45
from breezy.sixish import (
 
46
    BytesIO,
 
47
    )
42
48
 
43
49
_user_encoding = osutils.get_user_encoding()
44
50
 
47
53
    """Raised when a revision is not on left-hand history."""
48
54
 
49
55
 
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
56
def _rev_on_mainline(rev_tuple):
87
57
    """returns True is rev tuple is on mainline"""
88
58
    if len(rev_tuple) == 1:
151
121
 
152
122
 
153
123
def compile_pattern(pattern, flags=0):
 
124
    patternc = None
154
125
    try:
155
 
        return re.compile(pattern, flags)
 
126
        # use python's re.compile as we need to catch re.error in case of bad pattern
 
127
        lazy_regex.reset_compile()
 
128
        patternc = re.compile(pattern, flags)
156
129
    except re.error as e:
157
130
        raise errors.BzrError("Invalid pattern: '%s'" % pattern)
158
 
    return None
 
131
    return patternc
159
132
 
160
133
 
161
134
def is_fixed_string(s):
162
 
    if re.match("^([A-Za-z0-9_]|\\s)*$", s):
 
135
    if re.match("^([A-Za-z0-9_]|\s)*$", s):
163
136
        return True
164
137
    return False
165
138
 
172
145
        self.opts = opts
173
146
        self.outf = opts.outf
174
147
        if opts.show_color:
 
148
            pat = opts.pattern.encode(_user_encoding, 'replace')
175
149
            if opts.fixed_string:
176
 
                self._old = opts.pattern
177
 
                self._new = color_string(opts.pattern, FG.BOLD_RED)
 
150
                self._old = pat
 
151
                self._new = color_string(pat, FG.BOLD_RED)
178
152
                self.get_writer = self._get_writer_fixed_highlighted
179
153
            else:
180
154
                flags = opts.patternc.flags
181
 
                self._sub = re.compile(
182
 
                    opts.pattern.join(("((?:", ")+)")), flags).sub
 
155
                self._sub = re.compile(pat.join(("((?:", ")+)")), flags).sub
183
156
                self._highlight = color_string("\\1", FG.BOLD_RED)
184
157
                self.get_writer = self._get_writer_regexp_highlighted
185
158
        else:
189
162
        """Get function for writing file headers"""
190
163
        write = self.outf.write
191
164
        eol_marker = self.opts.eol_marker
192
 
 
193
165
        def _line_writer(line):
194
166
            write(line + eol_marker)
195
 
 
196
167
        def _line_writer_color(line):
197
168
            write(FG.BOLD_MAGENTA + line + FG.NONE + eol_marker)
198
169
        if self.opts.show_color:
205
176
        """Get function for writing revno lines"""
206
177
        write = self.outf.write
207
178
        eol_marker = self.opts.eol_marker
208
 
 
209
179
        def _line_writer(line):
210
180
            write(line + eol_marker)
211
 
 
212
181
        def _line_writer_color(line):
213
182
            write(FG.BOLD_BLUE + line + FG.NONE + eol_marker)
214
183
        if self.opts.show_color:
221
190
        """Get function for writing uncoloured output"""
222
191
        write = self.outf.write
223
192
        eol_marker = self.opts.eol_marker
224
 
 
225
193
        def _line_writer(line):
226
194
            write(line + eol_marker)
227
195
        return _line_writer
230
198
        """Get function for writing output with regexp match highlighted"""
231
199
        _line_writer = self._get_writer_plain()
232
200
        sub, highlight = self._sub, self._highlight
233
 
 
234
201
        def _line_writer_regexp_highlighted(line):
235
202
            """Write formatted line with matched pattern highlighted"""
236
203
            return _line_writer(line=sub(highlight, line))
240
207
        """Get function for writing output with search string highlighted"""
241
208
        _line_writer = self._get_writer_plain()
242
209
        old, new = self._old, self._new
243
 
 
244
210
        def _line_writer_fixed_highlighted(line):
245
211
            """Write formatted line with string searched for highlighted"""
246
212
            return _line_writer(line=line.replace(old, new))
256
222
        else:
257
223
            # if no revision is sepcified for diff grep we grep all changesets.
258
224
            opts.revision = [RevisionSpec.from_string('revno:1'),
259
 
                             RevisionSpec.from_string('last:1')]
 
225
                RevisionSpec.from_string('last:1')]
260
226
            start_rev = opts.revision[0]
261
227
        start_revid = start_rev.as_revision_id(branch)
262
 
        if start_revid == b'null:':
 
228
        if start_revid == 'null:':
263
229
            return
264
230
        srevno_tuple = branch.revision_id_to_dotted_revno(start_revid)
265
231
        if len(opts.revision) == 2:
269
235
                end_revno, end_revid = branch.last_revision_info()
270
236
            erevno_tuple = branch.revision_id_to_dotted_revno(end_revid)
271
237
 
272
 
            grep_mainline = (_rev_on_mainline(srevno_tuple)
273
 
                             and _rev_on_mainline(erevno_tuple))
 
238
            grep_mainline = (_rev_on_mainline(srevno_tuple) and
 
239
                _rev_on_mainline(erevno_tuple))
274
240
 
275
241
            # ensure that we go in reverse order
276
242
            if srevno_tuple > erevno_tuple:
281
247
            # faster when we don't want to look at merged revs. We try this
282
248
            # with _linear_view_revisions. If all revs are to be grepped we
283
249
            # 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)
 
250
            if opts.levels==1 and grep_mainline:
 
251
                given_revs = _linear_view_revisions(branch, start_revid, end_revid)
287
252
            else:
288
 
                given_revs = _graph_view_revisions(
289
 
                    branch, start_revid, end_revid)
 
253
                given_revs = _graph_view_revisions(branch, start_revid, end_revid)
290
254
        else:
291
255
            # We do an optimization below. For grepping a specific revison
292
256
            # We don't need to call _graph_view_revisions which is slow.
296
260
            start_rev_tuple = (start_revid, start_revno, 0)
297
261
            given_revs = [start_rev_tuple]
298
262
        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 '.*'")
 
263
        diff_pattern = re.compile("^[+\-].*(" + opts.pattern + ")")
 
264
        file_pattern = re.compile("=== (modified|added|removed) file '.*'", re.UNICODE)
302
265
        outputter = _GrepDiffOutputter(opts)
303
266
        writeline = outputter.get_writer()
304
267
        writerevno = outputter.get_revision_header_writer()
309
272
                # with level=1 show only top level
310
273
                continue
311
274
 
312
 
            rev_spec = RevisionSpec_revid.from_string(
313
 
                "revid:" + revid.decode('utf-8'))
 
275
            rev_spec = RevisionSpec_revid.from_string("revid:"+revid)
314
276
            new_rev = repo.get_revision(revid)
315
277
            new_tree = rev_spec.as_tree(branch)
316
278
            if len(new_rev.parent_ids) == 0:
320
282
            old_tree = repo.revision_tree(ancestor_id)
321
283
            s = BytesIO()
322
284
            diff.show_diff_trees(old_tree, new_tree, s,
323
 
                                 old_label='', new_label='')
 
285
                old_label='', new_label='')
324
286
            display_revno = True
325
287
            display_file = False
326
288
            file_header = None
334
296
                        writerevno("=== revno:%s ===" % (revno,))
335
297
                        display_revno = False
336
298
                    if display_file:
337
 
                        writefileheader(
338
 
                            "  %s" % (file_header.decode(file_encoding, 'replace'),))
 
299
                        writefileheader("  %s" % (file_header,))
339
300
                        display_file = False
340
301
                    line = line.decode(file_encoding, 'replace')
341
302
                    writeline("    %s" % (line,))
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)
412
370
 
413
371
 
414
372
def workingtree_grep(opts):
415
 
    revno = opts.print_revno = None  # for working tree set revno to None
 
373
    revno = opts.print_revno = None # for working tree set revno to None
416
374
 
417
375
    tree, branch, relpath = \
418
376
        controldir.ControlDir.open_containing_tree_or_branch('.')
419
377
    if not tree:
420
378
        msg = ('Cannot search working tree. Working tree not found.\n'
421
 
               'To search for specific revision in history use the -r option.')
422
 
        raise errors.CommandError(msg)
 
379
            'To search for specific revision in history use the -r option.')
 
380
        raise errors.BzrCommandError(msg)
423
381
 
424
382
    # GZ 2010-06-02: Shouldn't be smuggling this on opts, but easy for now
425
383
    opts.outputter = _Outputter(opts)
430
388
                path_prefix = path
431
389
                dir_grep(tree, path, relpath, opts, revno, path_prefix)
432
390
            else:
433
 
                with open(path, 'rb') as f:
434
 
                    _file_grep(f.read(), path, opts, revno)
 
391
                _file_grep(open(path).read(), path, opts, revno)
435
392
 
436
393
 
437
394
def _skip_file(include, exclude, path):
451
408
    from_dir = osutils.pathjoin(relpath, path)
452
409
    if opts.from_root:
453
410
        # start searching recursively from root
454
 
        from_dir = None
455
 
        recursive = True
 
411
        from_dir=None
 
412
        recursive=True
456
413
 
457
414
    to_grep = []
458
415
    to_grep_append = to_grep.append
460
417
    #                and hits manually refilled. Could do this again if it was
461
418
    #                for a good reason, otherwise cache might want purging.
462
419
    outputter = opts.outputter
463
 
    for fp, fc, fkind, entry in tree.list_files(
464
 
            include_root=False, from_dir=from_dir, recursive=opts.recursive):
 
420
    for fp, fc, fkind, fid, entry in tree.list_files(include_root=False,
 
421
        from_dir=from_dir, recursive=opts.recursive):
465
422
 
466
423
        if _skip_file(opts.include, opts.exclude, fp):
467
424
            continue
468
425
 
469
426
        if fc == 'V' and fkind == 'file':
470
 
            tree_path = osutils.pathjoin(from_dir if from_dir else '', fp)
471
 
            if revno is not None:
 
427
            if revno != None:
472
428
                # If old result is valid, print results immediately.
473
429
                # Otherwise, add file info to to_grep so that the
474
430
                # loop later will get chunks and grep them
475
 
                cache_id = tree.get_file_revision(tree_path)
 
431
                cache_id = tree.get_file_revision(fp, fid)
476
432
                if cache_id in outputter.cache:
477
433
                    # GZ 2010-06-05: Not really sure caching and re-outputting
478
434
                    #                the old path is really the right thing,
479
435
                    #                but it's what the old code seemed to do
480
436
                    outputter.write_cached_lines(cache_id, revno)
481
437
                else:
482
 
                    to_grep_append((tree_path, (fp, tree_path)))
 
438
                    to_grep_append((fid, (fp, fid)))
483
439
            else:
484
440
                # we are grepping working tree.
485
441
                if from_dir is None:
489
445
                if opts.files_with_matches or opts.files_without_match:
490
446
                    # Optimize for wtree list-only as we don't need to read the
491
447
                    # entire file
492
 
                    with open(path_for_file, 'rb', buffering=4096) as file:
493
 
                        _file_grep_list_only_wtree(file, fp, opts, path_prefix)
 
448
                    file = open(path_for_file, 'r', buffering=4096)
 
449
                    _file_grep_list_only_wtree(file, fp, opts, path_prefix)
494
450
                else:
495
 
                    with open(path_for_file, 'rb') as f:
496
 
                        _file_grep(f.read(), fp, opts, revno, path_prefix)
 
451
                    file_text = open(path_for_file, 'r').read()
 
452
                    _file_grep(file_text, fp, opts, revno, path_prefix)
497
453
 
498
 
    if revno is not None:  # grep versioned files
499
 
        for (path, tree_path), chunks in tree.iter_files_bytes(to_grep):
 
454
    if revno != None: # grep versioned files
 
455
        for (path, fid), chunks in tree.iter_files_bytes(to_grep):
500
456
            path = _make_display_path(relpath, path)
501
 
            _file_grep(b''.join(chunks), path, opts, revno, path_prefix,
502
 
                       tree.get_file_revision(tree_path))
 
457
            _file_grep(chunks[0], path, opts, revno, path_prefix,
 
458
                tree.get_file_revision(path, fid))
503
459
 
504
460
 
505
461
def _make_display_path(relpath, path):
517
473
    return path
518
474
 
519
475
 
520
 
def versioned_file_grep(tree, tree_path, relpath, path, opts, revno, path_prefix=None):
 
476
def versioned_file_grep(tree, id, relpath, path, opts, revno, path_prefix = None):
521
477
    """Create a file object for the specified id and pass it on to _file_grep.
522
478
    """
523
479
 
524
480
    path = _make_display_path(relpath, path)
525
 
    file_text = tree.get_file_text(tree_path)
 
481
    file_text = tree.get_file_text(relpath, id)
526
482
    _file_grep(file_text, path, opts, revno, path_prefix)
527
483
 
528
484
 
535
491
 
536
492
def _file_grep_list_only_wtree(file, path, opts, path_prefix=None):
537
493
    # test and skip binary files
538
 
    if b'\x00' in file.read(1024):
 
494
    if '\x00' in file.read(1024):
539
495
        if opts.verbose:
540
 
            trace.warning("Binary file '%s' skipped.", path)
 
496
            trace.warning("Binary file '%s' skipped." % path)
541
497
        return
542
498
 
543
 
    file.seek(0)  # search from beginning
 
499
    file.seek(0) # search from beginning
544
500
 
545
501
    found = False
546
502
    if opts.fixed_string:
549
505
            if pattern in line:
550
506
                found = True
551
507
                break
552
 
    else:  # not fixed_string
 
508
    else: # not fixed_string
553
509
        for line in file:
554
510
            if opts.patternc.search(line):
555
511
                found = True
556
512
                break
557
513
 
558
514
    if (opts.files_with_matches and found) or \
559
 
            (opts.files_without_match and not found):
 
515
        (opts.files_without_match and not found):
560
516
        if path_prefix and path_prefix != '.':
561
517
            # user has passed a dir arg, show that as result prefix
562
518
            path = osutils.pathjoin(path_prefix, path)
569
525
    The idea here is to do this work only once per run, and finally return a
570
526
    function that will do the minimum amount possible for each match.
571
527
    """
572
 
 
573
528
    def __init__(self, opts, use_cache=False):
574
529
        self.outf = opts.outf
575
530
        if use_cache:
584
539
        no_line = opts.files_with_matches or opts.files_without_match
585
540
 
586
541
        if opts.show_color:
 
542
            pat = opts.pattern.encode(_user_encoding, 'replace')
587
543
            if no_line:
588
544
                self.get_writer = self._get_writer_plain
589
545
            elif opts.fixed_string:
590
 
                self._old = opts.pattern
591
 
                self._new = color_string(opts.pattern, FG.BOLD_RED)
 
546
                self._old = pat
 
547
                self._new = color_string(pat, FG.BOLD_RED)
592
548
                self.get_writer = self._get_writer_fixed_highlighted
593
549
            else:
594
550
                flags = opts.patternc.flags
595
 
                self._sub = re.compile(
596
 
                    opts.pattern.join(("((?:", ")+)")), flags).sub
 
551
                self._sub = re.compile(pat.join(("((?:", ")+)")), flags).sub
597
552
                self._highlight = color_string("\\1", FG.BOLD_RED)
598
553
                self.get_writer = self._get_writer_regexp_highlighted
599
554
            path_start = FG.MAGENTA
624
579
    def _get_writer_plain(self, path, revno, cache_id):
625
580
        """Get function for writing uncoloured output"""
626
581
        per_line = self._format_perline
627
 
        start = self._format_initial % {"path": path, "revno": revno}
 
582
        start = self._format_initial % {"path":path, "revno":revno}
628
583
        write = self.outf.write
629
584
        if self.cache is not None and cache_id is not None:
630
585
            result_list = []
631
586
            self.cache[cache_id] = path, result_list
632
587
            add_to_cache = result_list.append
633
 
 
634
588
            def _line_cache_and_writer(**kwargs):
635
589
                """Write formatted line and cache arguments"""
636
590
                end = per_line % kwargs
637
591
                add_to_cache(end)
638
592
                write(start + end)
639
593
            return _line_cache_and_writer
640
 
 
641
594
        def _line_writer(**kwargs):
642
595
            """Write formatted line from arguments given by underlying opts"""
643
596
            write(start + per_line % kwargs)
646
599
    def write_cached_lines(self, cache_id, revno):
647
600
        """Write cached results out again for new revision"""
648
601
        cached_path, cached_matches = self.cache[cache_id]
649
 
        start = self._format_initial % {"path": cached_path, "revno": revno}
 
602
        start = self._format_initial % {"path":cached_path, "revno":revno}
650
603
        write = self.outf.write
651
604
        for end in cached_matches:
652
605
            write(start + end)
655
608
        """Get function for writing output with regexp match highlighted"""
656
609
        _line_writer = self._get_writer_plain(path, revno, cache_id)
657
610
        sub, highlight = self._sub, self._highlight
658
 
 
659
611
        def _line_writer_regexp_highlighted(line, **kwargs):
660
612
            """Write formatted line with matched pattern highlighted"""
661
613
            return _line_writer(line=sub(highlight, line), **kwargs)
665
617
        """Get function for writing output with search string highlighted"""
666
618
        _line_writer = self._get_writer_plain(path, revno, cache_id)
667
619
        old, new = self._old, self._new
668
 
 
669
620
        def _line_writer_fixed_highlighted(line, **kwargs):
670
621
            """Write formatted line with string searched for highlighted"""
671
622
            return _line_writer(line=line.replace(old, new), **kwargs)
674
625
 
675
626
def _file_grep(file_text, path, opts, revno, path_prefix=None, cache_id=None):
676
627
    # test and skip binary files
677
 
    if b'\x00' in file_text[:1024]:
 
628
    if '\x00' in file_text[:1024]:
678
629
        if opts.verbose:
679
 
            trace.warning("Binary file '%s' skipped.", path)
 
630
            trace.warning("Binary file '%s' skipped." % path)
680
631
        return
681
632
 
682
633
    if path_prefix and path_prefix != '.':
696
647
            found = pattern in file_text
697
648
        else:
698
649
            search = opts.patternc.search
699
 
            if b"$" not in pattern:
 
650
            if "$" not in pattern:
700
651
                found = search(file_text) is not None
701
652
            else:
702
653
                for line in file_text.splitlines():
714
665
        i = file_text.find(pattern)
715
666
        if i == -1:
716
667
            return
717
 
        b = file_text.rfind(b"\n", 0, i) + 1
 
668
        b = file_text.rfind("\n", 0, i) + 1
718
669
        if opts.line_number:
719
 
            start = file_text.count(b"\n", 0, b) + 1
 
670
            start = file_text.count("\n", 0, b) + 1
720
671
        file_text = file_text[b:]
721
672
        if opts.line_number:
722
673
            for index, line in enumerate(file_text.splitlines()):
723
674
                if pattern in line:
724
675
                    line = line.decode(file_encoding, 'replace')
725
 
                    writeline(lineno=index + start, line=line)
 
676
                    writeline(lineno=index+start, line=line)
726
677
        else:
727
678
            for line in file_text.splitlines():
728
679
                if pattern in line:
733
684
        # standard cases, but perhaps could try and detect backtracking
734
685
        # patterns here and avoid whole text search in those cases
735
686
        search = opts.patternc.search
736
 
        if b"$" not in pattern:
 
687
        if "$" not in pattern:
737
688
            # GZ 2010-06-05: Grr, re.MULTILINE can't save us when searching
738
689
            #                through revisions as bazaar returns binary mode
739
690
            #                and trailing \r breaks $ as line ending match
740
691
            m = search(file_text)
741
692
            if m is None:
742
693
                return
743
 
            b = file_text.rfind(b"\n", 0, m.start()) + 1
 
694
            b = file_text.rfind("\n", 0, m.start()) + 1
744
695
            if opts.line_number:
745
 
                start = file_text.count(b"\n", 0, b) + 1
 
696
                start = file_text.count("\n", 0, b) + 1
746
697
            file_text = file_text[b:]
747
698
        else:
748
699
            start = 1
750
701
            for index, line in enumerate(file_text.splitlines()):
751
702
                if search(line):
752
703
                    line = line.decode(file_encoding, 'replace')
753
 
                    writeline(lineno=index + start, line=line)
 
704
                    writeline(lineno=index+start, line=line)
754
705
        else:
755
706
            for line in file_text.splitlines():
756
707
                if search(line):
757
708
                    line = line.decode(file_encoding, 'replace')
758
709
                    writeline(line=line)
 
710