#!/usr/bin/python3

##  Copyright 2005 Drew Perttula
  
##  This program is free software; you can redistribute it and/or modify
##  it under the terms of the GNU General Public License as published by
##  the Free Software Foundation; either version 2 of the License, or
##  (at your option) any later version.

##  This program is distributed in the hope that it will be useful,
##  but WITHOUT ANY WARRANTY; without even the implied warranty of
##  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
##  GNU General Public License for more details.

##  You should have received a copy of the GNU General Public License
##  along with this program; if not, write to the Free Software
##  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

"""sometimes we grep for something in a bunch of files, and then we
want to make edits to the lines that were returned from the grep. It's
a hassle to edit all the files; this program puts the match lines in a
temp file for easy editing all at once.

EDITOR is used on the temporary file.

The cmdline arguments to grepedit are the same as those to grep. Do
not use arguments that alter the output line format; -C is ok.

grepedit also reads the following arguments:

  --sort-text sort lines by the text part of the line (this probably
    doesn't make sense with -C)



The grep output usually looks like this:
ez_setup.py:136:        import setuptools
ez_setup.py:138:        import tempfile, shutil

Output might be like this with -C 2:
--
ez_setup.py-174-    '''Update our built-in md5 registry'''
ez_setup.py-175-
ez_setup.py:176:    import re
ez_setup.py:177:    from md5 import md5
ez_setup.py-178-
ez_setup.py-179-    for name in filenames:
--

"""

import tempfile, sys, os, re

def grep_parse(filename):
    """parse grep output lines in given file into a dict of
    (filename, lineno) : text"""
    parse = {}
    for line in open(filename):
        if line == "--\n":
            continue
        m = re.match(r"(?P<filename>.*?)(?P<sep>[-:])(?P<lineno>\d+)(?P=sep)(?P<line>.*\n)$", line)
        if m is None:
            print("couldn't parse grep result line %r" % line)
            continue
        filename, lineno, text = (m.group('filename'), int(m.group('lineno')),
                                  m.group('line'))
        if (filename,lineno) in parse:
            raise ValueError("multiple results found for %s:%s" %
                             (filename, lineno))
        parse[(filename, lineno)] = text
    return parse

options = {}
passthru_args = []
for arg in sys.argv[1:]:
    if arg == "--sort-text":
        options['sort-text'] = True
        continue
    passthru_args.append(arg)

tf = tempfile.NamedTemporaryFile(prefix="grepedit_")
tf.close()

cmd = ("grep --with-filename --line-number --binary-files=without-match %s > %s" %
       (" ".join(['"%s"' % s.replace("\\","\\\\").replace('"','\\"')
                  for s in passthru_args]),tf.name))
os.system(cmd)

originals = grep_parse(tf.name)

if options.get('sort-text', False):
    orig = [(v,k) for k,v in list(originals.items())]
    orig.sort()
    f = open(tf.name, "w")
    for text, (filename,lineno) in orig:
        f.write("%s:%s:%s" % (filename, lineno, text))
    f.close()

os.system("%s %s" % (os.getenv("EDITOR"), tf.name))

corrections = grep_parse(tf.name)

files = set([filename for filename,lineno in list(corrections.keys())])
for orig_filename in files:
    (copy_fd, copy_filename) = tempfile.mkstemp(
        dir=os.path.dirname(orig_filename),
        prefix="_%s_tmp_grepedit" % os.path.basename(orig_filename))

    any_changes = False
    for lineno,line in enumerate(open(orig_filename)):
        lineno = lineno + 1 # grep is 1-based
        key = orig_filename,lineno
        if key in corrections:
            if line != originals[key]:
                print("%s:%s has changed since the grep command ran- not modifying this line" % key)
                print(repr(line))
                print(repr(originals[key]))
            elif corrections[key] == line:
                pass
            else:
                print("%s:%s substituting new line" % key)
                line = corrections[key]
                any_changes = True
        os.write(copy_fd, line.encode())

    os.close(copy_fd)
    if any_changes:
        os.rename(copy_filename, orig_filename)
    else:
        print("no changes made in file %s" % orig_filename)
        os.unlink(copy_filename)
        
    
