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 os
53 import re
54 import sys
55 import tempfile
56
57 from sets import Set
58
59
60 def grep_parse(filename):
61     """parse grep output lines in given file into a dict of
62     (filename, lineno) : text"""
63     parse = {}
64     for line in open(filename):
65         if line == "--\n":
66             continue
67         m = re.match(
68             r"(?P<filename>.*?)(?P<sep>[-:])(?P<lineno>\d+)(?P=sep)(?P<line>.*\n)$",
69             line,
70         )
71         if m is None:
72             print("couldn't parse grep result line %r" % line)
73             continue
74         filename, lineno, text = (
75             m.group("filename"),
76             int(m.group("lineno")),
77             m.group("line"),
78         )
79         if (filename, lineno) in parse:
80             raise ValueError("multiple results found for %s:%s" % (filename, lineno))
81         parse[(filename, lineno)] = text
82     return parse
83
84
85 options = {}
86 passthru_args = []
87 for arg in sys.argv[1:]:
88     if arg == "--sort-text":
89         options["sort-text"] = True
90         continue
91     passthru_args.append(arg)
92
93 tf = tempfile.NamedTemporaryFile(prefix="grepedit_")
94 tf.close()
95
96 cmd = "grep --with-filename --line-number --binary-files=without-match %s > %s" % (
97     " ".join(
98         ['"%s"' % s.replace("\\", "\\\\").replace('"', '\\"') for s in passthru_args]
99     ),
100     tf.name,
101 )
102 os.system(cmd)
103
104 originals = grep_parse(tf.name)
105
106 if options.get("sort-text", False):
107     orig = [(v, k) for k, v in list(originals.items())]
108     orig.sort()
109     f = open(tf.name, "w")
110     for text, (filename, lineno) in orig:
111         f.write("%s:%s:%s" % (filename, lineno, text))
112     f.close()
113
114 os.system("%s %s" % (os.getenv("EDITOR"), tf.name))
115
116 corrections = grep_parse(tf.name)
117
118 files = Set([filename for filename, lineno in list(corrections.keys())])
119 for orig_filename in files:
120     (copy_fd, copy_filename) = tempfile.mkstemp(
121         dir=os.path.dirname(orig_filename),
122         prefix="_%s_tmp_grepedit" % os.path.basename(orig_filename),
123     )
124
125     any_changes = False
126     for lineno, line in enumerate(open(orig_filename)):
127         lineno = lineno + 1  # grep is 1-based
128         key = orig_filename, lineno
129         if key in corrections:
130             if line != originals[key]:
131                 print(
132                     "%s:%s has changed since the grep command ran- "
133                     "not modifying this line" % key
134                 )
135                 print(repr(line))
136                 print(repr(originals[key]))
137             elif corrections[key] == line:
138                 pass
139             else:
140                 print("%s:%s substituting new line" % key)
141                 line = corrections[key]
142                 any_changes = True
143         os.write(copy_fd, line)
144
145     os.close(copy_fd)
146     if any_changes:
147         os.rename(copy_filename, orig_filename)
148     else:
149         print("no changes made in file %s" % orig_filename)
150         os.unlink(copy_filename)