#! /usr/bin/env python import difflib, getopt, os, re, stat, sys, time PROG_NAME = "srep" PROG_VERSION = "0.01 (2005/10/02)" PROG_COPYRIGHT = "Copyright (C) Laurence Tratt 2003-2005" PATCH_NONE = 0 PATCH_UNIFIED = 1 PATCH_NDIFF = 2 class SREP: def __init__(self): opts, args = getopt.getopt(sys.argv[1 : ], "rivnuh") self._recurse = 0 self._interactive = 0 self._verbose = 0 self._produce_patch = PATCH_NONE for opt in opts: if opt[0] == "-r": self._recurse = 1 elif opt[0] == "-i": self._interactive = 1 elif opt[0] == "-v": self._verbose = 1 elif opt[0] == "-n": self._produce_patch = PATCH_NDIFF elif opt[0] == "-u": self._produce_patch = PATCH_UNIFIED elif opt[0] == "-h": self.usage() else: self.usage("Error: Unknown arg '%s'" % opt[0]) if len(args) < 3: self.usage("Insufficient number of args") self._pattern = re.compile(args[0], re.DOTALL | re.MULTILINE) self._replace = args[1] self.log("Replacing '%s' with '%s' in %s.\n" % (sys.argv[1], self._replace, args[2 : ])) for file_name in args[2 : ]: if os.path.isdir(file_name): self.recurse(file_name) else: self.process_file(file_name) def usage(self, msg = None): if msg is None: msg = "" else: msg = msg + "\n\n" sys.stderr.write("""%s%s [-rivnu] ... \n""" % (msg, PROG_NAME)) sys.exit(1) def log(self, msg): if self._verbose: sys.stderr.write("%s" % msg) def recurse(self, dir_name): self.log("Recursing into %s.\n" % dir_name) for leaf_name in os.listdir(dir_name): file_name = os.path.join(dir_name, leaf_name) if os.path.isdir(file_name): self.recurse(file_name) continue else: self.process_file(file_name) def process_file(self, file_name): self.log("Processing %s.\n" % file_name) file = open(file_name, "r") file_data = file.read(-1) file.close() if self._produce_patch: original_file_data = file_data i = 0 changed = 0 while 1: match = self._pattern.search(file_data, i) if not match: break file_data = file_data[ : match.start()] + self._replace + file_data[match.end() : ] i = match.start() + len(self._replace) changed += 1 file_mdate = time.ctime(os.stat(file_name)[stat.ST_MTIME]) if changed: self.log("(%s replace operations).\n" % `changed`) if self._produce_patch == PATCH_NDIFF: line = "--- %s " % file_name line += " " * (15 - len(line)) line += file_mdate print line line = "+++ %s " % file_name line += " " * (15 - len(line)) line += file_mdate print line ndiff = ''.join(difflib.ndiff(original_file_data.splitlines(1), file_data.splitlines(1))) print self.ndiff_to_unified_diff(ndiff) elif self._produce_patch == PATCH_UNIFIED: print ''.join(difflib.unified_diff(original_file_data.splitlines(1), file_data.splitlines(1), file_name, file_name, file_mdate, file_mdate)) else: file = open(file_name, "w") file.write(file_data) file.close() else: self.log("(not changed).\n") def ndiff_to_unified_diff(self, ndiff): output = [] # ndiff adds an extra space after the "+- " chars which confuses patch. They need to be # removed; we also strip the extra space after "?" to ensure that things line up # correctly. tlines = ndiff.splitlines(1) i = 0 lines = [] while i < len(tlines): if tlines[i][0 : 2] == "+ ": lines.append("+" + tlines[i][2 : ]) elif tlines[i][0 : 2] == "- ": lines.append("-" + tlines[i][2 : ]) elif tlines[i][0 : 2] == " ": lines.append(" " + tlines[i][2 : ]) elif tlines[i][0 : 2] == "? ": lines.append("?" + tlines[i][2 : ]) else: lines.append(tlines[i]) i += 1 i = 0 last = 0 in_real_line = 1 out_real_line = 1 while i < len(lines): if lines[i][0] in ["+", "-"]: j = i - 1 while j > i - 3 and lines[j][0] == " ": j -= 1 context = i - j while in_real_line - context < 0 or out_real_line - context < 0: context -= 1 chunk = [] chunk.extend(lines[i - context : i]) original_in_real_line = in_real_line original_out_real_line = out_real_line while i < len(lines) and lines[i][0] in ["+", "-", "?"]: if lines[i][0] == "+": out_real_line += 1 elif lines[i][0] == "-": in_real_line += 1 chunk.append(lines[i]) i += 1 while i < len(lines): j = i while i < len(lines) and i < j + 3 and lines[i][0] == " ": in_real_line += 1 out_real_line += 1 chunk.append(lines[i]) i += 1 if not (i < len(lines) and i < j + 3): k = i while k < len(lines) and k < j + 6 and lines[k][0] == " ": k += 1 if k == j + 6: break j = i while i < len(lines) and i < j + 3 and lines[i][0] == " ": in_real_line += 1 out_real_line += 1 chunk.append(lines[i]) i += 1 while i < len(lines) and lines[i][0] != " ": if lines[i][0] == "+": out_real_line += 1 elif lines[i][0] == "-": in_real_line += 1 chunk.append(lines[i]) i += 1 output.append("@@ -%s,%s +%s,%s @@\n" % (original_in_real_line - context, in_real_line - original_in_real_line + context, original_out_real_line - context, out_real_line - original_out_real_line + context)) output.extend(chunk) last = in_real_line elif len(lines[i]) > 2 and lines[i][0] == " ": in_real_line += 1 out_real_line += 1 i += 1 else: i += 1 return "".join(output) if __name__ == "__main__": SREP()