Release new version 2.13.0
[grml-scripts.git] / usr_bin / grepedit
1 #!/usr/bin/python
2
3 ##  Copyright 2005 Drew Perttula
4   
5 ##  This program is free software; you can redistribute it and/or modify
6 ##  it under the terms of the GNU General Public License as published by
7 ##  the Free Software Foundation; either version 2 of the License, or
8 ##  (at your option) any later version.
9
10 ##  This program is distributed in the hope that it will be useful,
11 ##  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ##  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 ##  GNU General Public License for more details.
14
15 ##  You should have received a copy of the GNU General Public License
16 ##  along with this program; if not, write to the Free Software
17 ##  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18
19 """sometimes we grep for something in a bunch of files, and then we
20 want to make edits to the lines that were returned from the grep. It's
21 a hassle to edit all the files; this program puts the match lines in a
22 temp file for easy editing all at once.
23
24 EDITOR is used on the temporary file.
25
26 The cmdline arguments to grepedit are the same as those to grep. Do
27 not use arguments that alter the output line format; -C is ok.
28
29 grepedit also reads the following arguments:
30
31   --sort-text sort lines by the text part of the line (this probably
32     doesn't make sense with -C)
33
34
35
36 The grep output usually looks like this:
37 ez_setup.py:136:        import setuptools
38 ez_setup.py:138:        import tempfile, shutil
39
40 Output might be like this with -C 2:
41 --
42 ez_setup.py-174-    '''Update our built-in md5 registry'''
43 ez_setup.py-175-
44 ez_setup.py:176:    import re
45 ez_setup.py:177:    from md5 import md5
46 ez_setup.py-178-
47 ez_setup.py-179-    for name in filenames:
48 --
49
50 """
51
52 import tempfile, sys, os, re
53 from sets import Set
54
55 def grep_parse(filename):
56     """parse grep output lines in given file into a dict of
57     (filename, lineno) : text"""
58     parse = {}
59     for line in open(filename):
60         if line == "--\n":
61             continue
62         m = re.match(r"(?P<filename>.*?)(?P<sep>[-:])(?P<lineno>\d+)(?P=sep)(?P<line>.*\n)$", line)
63         if m is None:
64             print "couldn't parse grep result line %r" % line
65             continue
66         filename, lineno, text = (m.group('filename'), int(m.group('lineno')),
67                                   m.group('line'))
68         if (filename,lineno) in parse:
69             raise ValueError("multiple results found for %s:%s" %
70                              (filename, lineno))
71         parse[(filename, lineno)] = text
72     return parse
73
74 options = {}
75 passthru_args = []
76 for arg in sys.argv[1:]:
77     if arg == "--sort-text":
78         options['sort-text'] = True
79         continue
80     passthru_args.append(arg)
81
82 tf = tempfile.NamedTemporaryFile(prefix="grepedit_")
83 tf.close()
84
85 cmd = ("grep --with-filename --line-number --binary-files=without-match %s > %s" %
86        (" ".join(['"%s"' % s.replace("\\","\\\\").replace('"','\\"')
87                   for s in passthru_args]),tf.name))
88 os.system(cmd)
89
90 originals = grep_parse(tf.name)
91
92 if options.get('sort-text', False):
93     orig = [(v,k) for k,v in originals.items()]
94     orig.sort()
95     f = open(tf.name, "w")
96     for text, (filename,lineno) in orig:
97         f.write("%s:%s:%s" % (filename, lineno, text))
98     f.close()
99
100 os.system("%s %s" % (os.getenv("EDITOR"), tf.name))
101
102 corrections = grep_parse(tf.name)
103
104 files = Set([filename for filename,lineno in corrections.keys()])
105 for orig_filename in files:
106     (copy_fd, copy_filename) = tempfile.mkstemp(
107         dir=os.path.dirname(orig_filename),
108         prefix="_%s_tmp_grepedit" % os.path.basename(orig_filename))
109
110     any_changes = False
111     for lineno,line in enumerate(open(orig_filename)):
112         lineno = lineno + 1 # grep is 1-based
113         key = orig_filename,lineno
114         if key in corrections:
115             if line != originals[key]:
116                 print "%s:%s has changed since the grep command ran- not modifying this line" % key
117                 print repr(line)
118                 print repr(originals[key])
119             elif corrections[key] == line:
120                 pass
121             else:
122                 print "%s:%s substituting new line" % key
123                 line = corrections[key]
124                 any_changes = True
125         os.write(copy_fd, line)
126
127     os.close(copy_fd)
128     if any_changes:
129         os.rename(copy_filename, orig_filename)
130     else:
131         print "no changes made in file %s" % orig_filename
132         os.unlink(copy_filename)
133         
134