X-Git-Url: http://git.droids-corp.org/?p=diff2html.git;a=blobdiff_plain;f=diff2html.py;h=92bbf362acf9a1b55b073c842181caa3112f2a59;hp=d616a5eb43ccdae53981c3fe4bb44ca939d256d4;hb=c23d783c8f55be1471c90c51b9bbab4dd302e7c9;hpb=b8d3cbfcf53806cc2045b818109feb6d009ca515 diff --git a/diff2html.py b/diff2html.py index d616a5e..92bbf36 100644 --- a/diff2html.py +++ b/diff2html.py @@ -1,4 +1,5 @@ #! /usr/bin/python +# coding=utf-8 # # 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 @@ -20,6 +21,8 @@ # # Authors: Olivier MATZ # Alan De Smet +# Sergey Satskiy +# scito # # Inspired by diff2html.rb from Dave Burt # (mainly for html theme) @@ -32,42 +35,56 @@ # and display those directly. -import sys, re, htmlentitydefs, getopt +import sys, re, htmlentitydefs, getopt, StringIO, codecs, datetime # minimum line size, we add a zero-sized breakable space every # LINESIZE characters linesize = 20 tabsize = 8 -inputfile = sys.stdin -outputfile = sys.stdout -exclude_headers = False show_CR = False -show_hunk_infos = False - - -html_hdr = """ - - - HTML Diff - - - +encoding = "utf-8" +lang = "en" + +desc = "File comparison" +dtnow = datetime.datetime.now() +modified_date = "%s+01:00"%dtnow.isoformat() + +html_hdr = """ + + + + + + HTML Diff{0} + + + + + + + + + """ html_footer = """ +
+

Modified at {1}. HTML formatting created by diff2html.

+
""" @@ -95,7 +112,7 @@ def sane(x): r = "" for i in x: j = ord(i) - if i not in ['\t', '\n'] and ((j < 32) or (j >= 127)): + if i not in ['\t', '\n'] and (j < 32): r = r + "." else: r = r + i @@ -103,13 +120,12 @@ def sane(x): def linediff(s, t): if len(s): - s = str(reduce(lambda x, y:x+y, [ sane(c) for c in s ])) + s = unicode(reduce(lambda x, y:x+y, [ sane(c) for c in s ])) if len(t): - t = str(reduce(lambda x, y:x+y, [ sane(c) for c in t ])) + t = unicode(reduce(lambda x, y:x+y, [ sane(c) for c in t ])) m, n = len(s), len(t) d = [[(0, 0) for i in range(n+1)] for i in range(m+1)] - x = [[(0, 0) for i in range(n+1)] for i in range(m+1)] d[0][0] = (0, (0, 0)) @@ -167,18 +183,17 @@ def linediff(s, t): def convert(s, linesize=0, ponct=0): i = 0 - t = "" - l=[] + t = u"" for c in s: # used by diffs if c == DIFFON: - t += '' + t += u'' elif c == DIFFOFF: - t += "" + t += u"" # special html chars elif htmlentitydefs.codepoint2name.has_key(ord(c)): - t += "&%s;" % (htmlentitydefs.codepoint2name[ord(c)]) + t += u"&%s;" % (htmlentitydefs.codepoint2name[ord(c)]) i += 1 # special highlighted chars @@ -186,45 +201,45 @@ def convert(s, linesize=0, ponct=0): n = tabsize-(i%tabsize) if n == 0: n = tabsize - t += ('»'+' '*(n-1)) + t += (u'»'+' '*(n-1)) elif c == " " and ponct == 1: - t += '·' + t += u'·' elif c == "\n" and ponct == 1: if show_CR: - t += '\' + t += u'\' else: t += c i += 1 if linesize and (WORDBREAK.count(c) == 1): - t += '​' + t += u'​' i = 0 if linesize and i > linesize: i = 0 - t += "​" + t += u"​" return t -def add_comment(s): - outputfile.write('%s\n'%convert(s)) +def add_comment(s, output_file): + output_file.write(('%s\n'%convert(s)).encode(encoding)) -def add_filename(f1, f2): - outputfile.write("%s"%convert(f1, linesize=linesize)) - outputfile.write("%s\n"%convert(f2, linesize=linesize)) -def add_hunk(): - global hunk_off1, hunk_size1, hunk_off2, hunk_size2 - global show_hunk_infos +def add_filename(f1, f2, output_file): + output_file.write(("%s"%convert(f1, linesize=linesize)).encode(encoding)) + output_file.write(("%s\n"%convert(f2, linesize=linesize)).encode(encoding)) + + +def add_hunk(output_file, show_hunk_infos): if show_hunk_infos: - outputfile.write('Offset %d, %d lines modified'%(hunk_off1, hunk_size1)) - outputfile.write('Offset %d, %d lines modified\n'%(hunk_off2, hunk_size2)) + output_file.write('Offset %d, %d lines modified'%(hunk_off1, hunk_size1)) + output_file.write('Offset %d, %d lines modified\n'%(hunk_off2, hunk_size2)) else: # ⋮ - vertical ellipsis - outputfile.write('⋮⋮') + output_file.write('⋮⋮') -def add_line(s1, s2): +def add_line(s1, s2, output_file): global line1 global line2 @@ -240,26 +255,26 @@ def add_line(s1, s2): type_name = "changed" s1, s2 = linediff(s1, s2) - outputfile.write('' % type_name) + output_file.write(('' % type_name).encode(encoding)) if s1 != None and s1 != "": - outputfile.write('%d ' % line1) - outputfile.write('') - outputfile.write(convert(s1, linesize=linesize, ponct=1)) - outputfile.write('') + output_file.write(('%d ' % line1).encode(encoding)) + output_file.write(''.encode(encoding)) + output_file.write(convert(s1, linesize=linesize, ponct=1).encode(encoding)) + output_file.write('') else: s1 = "" - outputfile.write(' ') + output_file.write(' ') if s2 != None and s2 != "": - outputfile.write('%d '%line2) - outputfile.write('') - outputfile.write(convert(s2, linesize=linesize, ponct=1)) - outputfile.write('') + output_file.write(('%d '%line2).encode(encoding)) + output_file.write('') + output_file.write(convert(s2, linesize=linesize, ponct=1).encode(encoding)) + output_file.write('') else: s2 = "" - outputfile.write('') + output_file.write('') - outputfile.write('\n') + output_file.write('\n') if s1 != "": line1 += 1 @@ -267,14 +282,14 @@ def add_line(s1, s2): line2 += 1 -def empty_buffer(): +def empty_buffer(output_file): global buf global add_cpt global del_cpt if del_cpt == 0 or add_cpt == 0: for l in buf: - add_line(l[0], l[1]) + add_line(l[0], l[1], output_file) elif del_cpt != 0 and add_cpt != 0: l0, l1 = [], [] @@ -290,50 +305,54 @@ def empty_buffer(): s0 = l0[i] if i < len(l1): s1 = l1[i] - add_line(s0, s1) + add_line(s0, s1, output_file) add_cpt, del_cpt = 0, 0 buf = [] -def parse_input(): - global buf, add_cpt, del_cpt +def parse_input(input_file, output_file, input_file_name, output_file_name, + exclude_headers, show_hunk_infos): + global add_cpt, del_cpt global line1, line2 global hunk_off1, hunk_size1, hunk_off2, hunk_size2 if not exclude_headers: - outputfile.write(html_hdr) - outputfile.write(table_hdr) + title_suffix = ' ' + input_file_name + output_file.write(html_hdr.format(title_suffix, encoding, desc, "", modified_date, lang).encode(encoding)) + output_file.write(table_hdr.encode(encoding)) while True: - l = inputfile.readline() + l = input_file.readline() if l == "": break m = re.match('^--- ([^\s]*)', l) if m: - empty_buffer() + empty_buffer(output_file) file1 = m.groups()[0] - l = inputfile.readline() - m = re.match('^\+\+\+ ([^\s]*)', l) - if m: - file2 = m.groups()[0] - add_filename(file1, file2) + while True: + l = input_file.readline() + m = re.match('^\+\+\+ ([^\s]*)', l) + if m: + file2 = m.groups()[0] + break + add_filename(file1, file2, output_file) hunk_off1, hunk_size1, hunk_off2, hunk_size2 = 0, 0, 0, 0 continue m = re.match("@@ -(\d+),?(\d*) \+(\d+),?(\d*)", l) if m: - empty_buffer() + empty_buffer(output_file) hunk_data = map(lambda x:x=="" and 1 or int(x), m.groups()) hunk_off1, hunk_size1, hunk_off2, hunk_size2 = hunk_data line1, line2 = hunk_off1, hunk_off2 - add_hunk() + add_hunk(output_file, show_hunk_infos) continue if hunk_size1 == 0 and hunk_size2 == 0: - empty_buffer() - add_comment(l) + empty_buffer(output_file) + add_comment(l, output_file) continue if re.match("^\+", l): @@ -349,30 +368,32 @@ def parse_input(): continue if re.match("^\ ", l) and hunk_size1 and hunk_size2: - empty_buffer() + empty_buffer(output_file) hunk_size1 -= 1 hunk_size2 -= 1 buf.append((l[1:], l[1:])) continue - empty_buffer() - add_comment(l) + empty_buffer(output_file) + add_comment(l, output_file) - empty_buffer() - outputfile.write(table_footer) + empty_buffer(output_file) + output_file.write(table_footer.encode(encoding)) if not exclude_headers: - outputfile.write(html_footer) + output_file.write(html_footer.format("", dtnow.strftime("%d.%m.%Y")).encode(encoding)) def usage(): print ''' -diff2html.py [-i file] [-o file] [-x] +diff2html.py [-e encoding] [-i file] [-o file] [-x] diff2html.py -h Transform a unified diff from stdin to a colored side-by-side HTML page on stdout. +stdout may not work with UTF-8, instead use -o option. -i file set input file, else use stdin + -e encoding set file encoding (default utf-8) -o file set output file, else use stdout -x exclude html header and footer -t tabsize set tab size (default 8) @@ -384,28 +405,37 @@ page on stdout. def main(): global linesize, tabsize - global inputfile, outputfile - global exclude_headers, show_CR, show_hunk_infos + global show_CR + global encoding + + input_file_name = '' + output_file_name = '' + + exclude_headers = False + show_hunk_infos = False try: - opts, args = getopt.getopt(sys.argv[1:], "hi:o:xt:l:rk", - ["help", "input=", "output=", + opts, args = getopt.getopt(sys.argv[1:], "he:i:o:xt:l:rk", + ["help", "encoding=", "input=", "output=", "exclude-html-headers", "tabsize=", "linesize=", "show-cr", "show-hunk-infos"]) except getopt.GetoptError, err: - print str(err) # will print something like "option -a not recognized" + print unicode(err) # will print something like "option -a not recognized" usage() sys.exit(2) - output = None verbose = False for o, a in opts: if o in ("-h", "--help"): usage() sys.exit() + elif o in ("-e", "--encoding"): + encoding = a elif o in ("-i", "--input"): - inputfile = open(a, "r") + input_file = codecs.open(a, "r", encoding) + input_file_name = a elif o in ("-o", "--output"): - outputfile = open(a, "w") + output_file = codecs.open(a, "w") + output_file_name = a elif o in ("-x", "--exclude-html-headers"): exclude_headers = True elif o in ("-t", "--tabsize"): @@ -418,7 +448,25 @@ def main(): show_hunk_infos = True else: assert False, "unhandled option" - parse_input() + + # Use stdin if not input file is set + if not ('input_file' in locals()): + input_file = codecs.getreader(encoding)(sys.stdin) + + # Use stdout if not output file is set + if not ('output_file' in locals()): + output_file = codecs.getwriter(encoding)(sys.stdout) + + parse_input(input_file, output_file, input_file_name, output_file_name, + exclude_headers, show_hunk_infos) + +def parse_from_memory(txt, exclude_headers, show_hunk_infos): + " Parses diff from memory and returns a string with html " + input_stream = StringIO.StringIO(txt) + output_stream = StringIO.StringIO() + parse_input(input_stream, output_stream, '', '', exclude_headers, show_hunk_infos) + return output_stream.getvalue() + if __name__ == "__main__": main()