coderev-0.1/codediff.py
coderev-0.2/codediff.py
f1#!/usr/bin/env pythonf1#!/usr/bin/env python
2#2#
3# Homepage: http://code.google.com/p/coderev3# Homepage: http://code.google.com/p/coderev
4# License: GPLv2, see "COPYING"4# License: GPLv2, see "COPYING"
5#5#
n6# $Id: codediff.py 3 2008-08-19 04:06:17Z mattwyl $n6# $Id: codediff.py 9 2008-08-21 14:08:53Z mattwyl $
77
8'''Diff two files/directories and produce HTML pages.'''8'''Diff two files/directories and produce HTML pages.'''
99
10import sys, os, stat, errno, time, re, difflib, filecmp10import sys, os, stat, errno, time, re, difflib, filecmp
1111
12_myname = os.path.basename(sys.argv[0])12_myname = os.path.basename(sys.argv[0])
n13_revision = '$Revision: 3 $'.split()[1]n13_revision = '$Revision: 9 $'.split()[1]
1414
1515
16########## globals & templates begin ##########16########## globals & templates begin ##########
17# index page layout:17# index page layout:
18#18#
19# h1: dir1 vs dir219# h1: dir1 vs dir2
20# summary_info: Files changed/added/deleted: xx/yy/zz20# summary_info: Files changed/added/deleted: xx/yy/zz
21#21#
22# Filename C/D/A Summary      Diffs                  Sources22# Filename C/D/A Summary      Diffs                  Sources
23# Pathname x/y/z         Cdiffs Udiffs Sdiffs Fdiffs Old New23# Pathname x/y/z         Cdiffs Udiffs Sdiffs Fdiffs Old New
24# Pathname x/y/z         Cdiffs Udiffs Sdiffs Fdiffs Old New24# Pathname x/y/z         Cdiffs Udiffs Sdiffs Fdiffs Old New
25# Pathname x/y/z         -      -      -      -      -   New25# Pathname x/y/z         -      -      -      -      -   New
26# Pathname x/y/z         -      -      -      -      Old -26# Pathname x/y/z         -      -      -      -      Old -
27#27#
28# Legends:28# Legends:
29# Changed Deleted Added29# Changed Deleted Added
30# <hr>30# <hr>
31# footer_info31# footer_info
32#32#
3333
34_file_template = """34_file_template = """
35<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"35<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
36          "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">36          "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
37<html>37<html>
3838
39<head>39<head>
40    <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />40    <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
41    <title>41    <title>
42        %(title)s42        %(title)s
43    </title>43    </title>
44    <style type="text/css">44    <style type="text/css">
45        %(styles)s45        %(styles)s
46    </style>46    </style>
47</head>47</head>
4848
49<body>49<body>
50    %(header_info)s50    %(header_info)s
51    %(summary_info)s51    %(summary_info)s
52    %(data_rows)s52    %(data_rows)s
53    %(legend)s53    %(legend)s
54    <hr>54    <hr>
55    %(footer_info)s55    %(footer_info)s
56</body>56</body>
5757
58</html>"""58</html>"""
5959
60_styles = """60_styles = """
61    body {font-family: monospace; font-size: 9pt;}61    body {font-family: monospace; font-size: 9pt;}
62    #summary_info {font-style: italic;}62    #summary_info {font-style: italic;}
63    #summary_table {63    #summary_table {
64        text-align:left;font-family:monospace;64        text-align:left;font-family:monospace;
65        border: 1px solid #ccc; border-collapse: collapse65        border: 1px solid #ccc; border-collapse: collapse
66    }66    }
67    td {padding-left:5px;padding-right:5px;}67    td {padding-left:5px;padding-right:5px;}
68    #legend {border:medium;text-align:center;}68    #legend {border:medium;text-align:center;}
69    #footer_info {color:#333; font-size:8pt;}69    #footer_info {color:#333; font-size:8pt;}
70    .diff {background-color:#ffd;}70    .diff {background-color:#ffd;}
71    .added {background-color:#afa;}71    .added {background-color:#afa;}
72    .deleted {background-color:#faa;}72    .deleted {background-color:#faa;}
73    .table_header th {73    .table_header th {
74        text-align:center;74        text-align:center;
75        background-color:#f0f0f0;75        background-color:#f0f0f0;
76        border-bottom: 1px solid #aaa;76        border-bottom: 1px solid #aaa;
77        padding: 4px 4px 4px 4px;77        padding: 4px 4px 4px 4px;
78    }78    }
79    """79    """
8080
81_header_info_template = """81_header_info_template = """
82    <h1>%(dir1)s vs %(dir2)s</h1>"""82    <h1>%(dir1)s vs %(dir2)s</h1>"""
8383
84_summary_info_template = """84_summary_info_template = """
85    <p id="summary_info">85    <p id="summary_info">
86        Files Changed/Deleted/Added: %(changed)d/%(deleted)d/%(added)d86        Files Changed/Deleted/Added: %(changed)d/%(deleted)d/%(added)d
87    </p>"""87    </p>"""
8888
89_data_rows_template = """89_data_rows_template = """
90    <table id="summary_table" cellspacing="1" border="1" nowrap="nowrap">90    <table id="summary_table" cellspacing="1" border="1" nowrap="nowrap">
91    <tr class="table_header">91    <tr class="table_header">
92        <th>Filename</th>92        <th>Filename</th>
93        <th><abbr title="Changed/Deleted/Added">C/D/A</abbr> Summary</th>93        <th><abbr title="Changed/Deleted/Added">C/D/A</abbr> Summary</th>
94        <th colspan="4">Diffs</th>94        <th colspan="4">Diffs</th>
95        <th colspan="2">Sources</th>95        <th colspan="2">Sources</th>
96    </tr>96    </tr>
97    %(data_rows)s97    %(data_rows)s
98    </table>"""98    </table>"""
9999
100_diff_data_row_template = """100_diff_data_row_template = """
101    <tr class="diff">101    <tr class="diff">
102        <td>%(pathname)s</td>102        <td>%(pathname)s</td>
103        <td><abbr title="Changed/Deleted/Added">%(changed)s/%(deleted)s/%(added)s</abbr></td>103        <td><abbr title="Changed/Deleted/Added">%(changed)s/%(deleted)s/%(added)s</abbr></td>
104        <td><a href=%(pathname)s.cdiff.html title="context diffs">Cdiffs</a></td>104        <td><a href=%(pathname)s.cdiff.html title="context diffs">Cdiffs</a></td>
105        <td><a href=%(pathname)s.udiff.html title="unified diffs">Udiffs</a></td>105        <td><a href=%(pathname)s.udiff.html title="unified diffs">Udiffs</a></td>
106        <td><a href=%(pathname)s.sdiff.html title="side-by-side context diffs">Sdiffs</a></td>106        <td><a href=%(pathname)s.sdiff.html title="side-by-side context diffs">Sdiffs</a></td>
107        <td><a href=%(pathname)s.fdiff.html title="side-by-side full diffs">Fdiffs</a></td>107        <td><a href=%(pathname)s.fdiff.html title="side-by-side full diffs">Fdiffs</a></td>
108        <td><a href=%(pathname)s-.html title="old file">Old</a></td>108        <td><a href=%(pathname)s-.html title="old file">Old</a></td>
109        <td><a href=%(pathname)s.html title="new file">New</a></td>109        <td><a href=%(pathname)s.html title="new file">New</a></td>
110    </tr>"""110    </tr>"""
111111
112_deleted_data_row_template = """112_deleted_data_row_template = """
113    <tr class="deleted">113    <tr class="deleted">
114        <td>%(pathname)s</td>114        <td>%(pathname)s</td>
115        <td>-/-/-</td>115        <td>-/-/-</td>
116        <td>-</td>116        <td>-</td>
117        <td>-</td>117        <td>-</td>
118        <td>-</td>118        <td>-</td>
119        <td>-</td>119        <td>-</td>
120        <td><a href=%(pathname)s-.html title="old file">Old</a></td>120        <td><a href=%(pathname)s-.html title="old file">Old</a></td>
121        <td>-</td>121        <td>-</td>
122    </tr>"""122    </tr>"""
123123
124_added_data_row_template = """124_added_data_row_template = """
125    <tr class="added">125    <tr class="added">
126        <td>%(pathname)s</td>126        <td>%(pathname)s</td>
127        <td>-/-/-</td>127        <td>-/-/-</td>
128        <td>-</td>128        <td>-</td>
129        <td>-</td>129        <td>-</td>
130        <td>-</td>130        <td>-</td>
131        <td>-</td>131        <td>-</td>
132        <td>-</td>132        <td>-</td>
133        <td><a href=%(pathname)s.html title="new file">New</a></td>133        <td><a href=%(pathname)s.html title="new file">New</a></td>
134    </tr>"""134    </tr>"""
135135
136_legend = """136_legend = """
137    <br><em>Legends:</em>137    <br><em>Legends:</em>
138    <table id="legend">138    <table id="legend">
139        <tr>139        <tr>
140            <td width="20%%" class="diff">Changed</td>140            <td width="20%%" class="diff">Changed</td>
141            <td width="20%%" class="deleted">Deleted</td>141            <td width="20%%" class="deleted">Deleted</td>
142            <td width="20%%" class="added">Added</td>142            <td width="20%%" class="added">Added</td>
143        </tr>143        </tr>
144    </table>"""144    </table>"""
145145
146_footer_info_template = """146_footer_info_template = """
147    <i id="footer_info">147    <i id="footer_info">
148        Generated by %(myname)s r%(revision)s at %(time)s148        Generated by %(myname)s r%(revision)s at %(time)s
149    </i>"""149    </i>"""
150150
n151_global_dir_ignore_list = [n151_global_dir_ignore_list = (
152    '\bCVS$',152    r'^CVS$',
153    '\bSCCS$',153    r'^SCCS$',
154    '\b\\.svn$',154    r'^\.svn$',
155]155)
156156
n157_global_file_ignore_list = [n157_global_file_ignore_list = (
158    '.*\\.o$',158    r'.*\.o$',
159    '.*\\.swp$',159    r'.*\.swp$',
160    '.*\\.bak$',160    r'.*\.bak$',
161    '.*\\.old$',161    r'.*\.old$',
162    '.*\\~$',162    r'.*~$',
163    '\\.cvsignore$',163    r'^\.cvsignore$',
164]164)
165########## globals & templates end ##########165########## globals & templates end ##########
166166
167167
168def makeTitle(pathname, width):168def makeTitle(pathname, width):
169    'Wrap long pathname to abbreviate name to fit the text width'169    'Wrap long pathname to abbreviate name to fit the text width'
170    if not pathname:170    if not pathname:
171        return 'None'171        return 'None'
172172
173    if not width or width <= 0:173    if not width or width <= 0:
174        title = pathname174        title = pathname
175    elif len(pathname) > width:175    elif len(pathname) > width:
176        if width > 3:176        if width > 3:
177            title = '...' + pathname[-(width-3):]177            title = '...' + pathname[-(width-3):]
178        else:178        else:
179            title = pathname[-width:]179            title = pathname[-width:]
180    else:180    else:
181        title = pathname181        title = pathname
182    return title182    return title
183183
184184
185def getLines(file):185def getLines(file):
186    '''Return content of file (a list, each is a line)'''186    '''Return content of file (a list, each is a line)'''
187    if not file:187    if not file:
188        return []188        return []
189    try:189    try:
190        fp = open(file, 'r')190        fp = open(file, 'r')
191        lines = fp.readlines()191        lines = fp.readlines()
192        fp.close()192        fp.close()
193    except IOError, dig:193    except IOError, dig:
194        sys.stderr.write(str(dig) + '\n')194        sys.stderr.write(str(dig) + '\n')
195        sys.exit(2)195        sys.exit(2)
196    return lines196    return lines
197197
198198
199def sdiffLines(fomeLines, toLines, outFile, fromTitle, toTitle,199def sdiffLines(fomeLines, toLines, outFile, fromTitle, toTitle,
200               context, wrapnum, lines):200               context, wrapnum, lines):
201    '''diff two texts and return side-by-side html page, write to outFile.201    '''diff two texts and return side-by-side html page, write to outFile.
202    Return code indicating whether there are differences'''202    Return code indicating whether there are differences'''
203    d = difflib.HtmlDiff(wrapcolumn=wrapnum)203    d = difflib.HtmlDiff(wrapcolumn=wrapnum)
204    d._styles += '''204    d._styles += '''
205        /* customized style */205        /* customized style */
206        body { font-family:monospace; font-size: 9pt; }206        body { font-family:monospace; font-size: 9pt; }
207        table.diff {font-family:monospace; border:medium;}'''207        table.diff {font-family:monospace; border:medium;}'''
208    html = d.make_file(fomeLines, toLines, fromTitle, toTitle, context, lines)208    html = d.make_file(fomeLines, toLines, fromTitle, toTitle, context, lines)
209209
210    try:210    try:
211        fp = open(outFile, 'w')211        fp = open(outFile, 'w')
212        fp.write(html)212        fp.write(html)
213        fp.close()213        fp.close()
214    except IOError, dig:214    except IOError, dig:
215        sys.stderr.write(str(dig) + '\n')215        sys.stderr.write(str(dig) + '\n')
216        sys.exit(2)216        sys.exit(2)
217217
218218
219def cdiffLines(fomeLines, toLines, outFile, fromName, toName,219def cdiffLines(fomeLines, toLines, outFile, fromName, toName,
220               fromDate, toDate, context):220               fromDate, toDate, context):
221    '''cdiff two texts and return summary info, write to outFile.221    '''cdiff two texts and return summary info, write to outFile.
222    Return code indicating whether there are differences'''222    Return code indicating whether there are differences'''
223    d = difflib.context_diff(fomeLines, toLines, fromName, toName,223    d = difflib.context_diff(fomeLines, toLines, fromName, toName,
224                             fromDate, toDate, n=context)224                             fromDate, toDate, n=context)
225    title = 'Cdiffs of %s and %s' % (fromName, toName)225    title = 'Cdiffs of %s and %s' % (fromName, toName)
226    summary, html = cdiffToHtml(d, title)226    summary, html = cdiffToHtml(d, title)
227227
228    try:228    try:
229        fp = open(outFile, 'w')229        fp = open(outFile, 'w')
230        fp.write(html)230        fp.write(html)
231        fp.close()231        fp.close()
232    except IOError, dig:232    except IOError, dig:
233        sys.stderr.write(str(dig) + '\n')233        sys.stderr.write(str(dig) + '\n')
234        sys.exit(2)234        sys.exit(2)
235235
236    return summary236    return summary
237237
238238
239def udiffLines(fomeLines, toLines, outFile, fromName, toName,239def udiffLines(fomeLines, toLines, outFile, fromName, toName,
240               fromDate, toDate, n):240               fromDate, toDate, n):
241    '''udiff two texts and return html page, write to outFile.241    '''udiff two texts and return html page, write to outFile.
242    Return code indicating whether there are differences'''242    Return code indicating whether there are differences'''
243    d = difflib.unified_diff(fomeLines, toLines, fromName, toName,243    d = difflib.unified_diff(fomeLines, toLines, fromName, toName,
244                             fromDate, toDate, n)244                             fromDate, toDate, n)
245    title = 'Udiffs of %s and %s' % (fromName, toName)245    title = 'Udiffs of %s and %s' % (fromName, toName)
246    html = udiffToHtml(d, title)246    html = udiffToHtml(d, title)
247247
248    try:248    try:
249        fp = open(outFile, 'w')249        fp = open(outFile, 'w')
250        fp.write(html)250        fp.write(html)
251        fp.close()251        fp.close()
252    except IOError, dig:252    except IOError, dig:
253        sys.stderr.write(str(dig) + '\n')253        sys.stderr.write(str(dig) + '\n')
254        sys.exit(2)254        sys.exit(2)
255255
256256
257# This is only called when diff two files specified from command line257# This is only called when diff two files specified from command line
258def diffFile(file1, file2, outFile, context=False, wrapnum=None, lines=3):258def diffFile(file1, file2, outFile, context=False, wrapnum=None, lines=3):
259    '''diff two files and return html page, write to outFile.259    '''diff two files and return html page, write to outFile.
260    Return code indicating whether there are differences'''260    Return code indicating whether there are differences'''
261    #print 'call diffFile(%s, %s, %s, ...)' % (file1, file2, outFile)261    #print 'call diffFile(%s, %s, %s, ...)' % (file1, file2, outFile)
262    #print '-' * 32262    #print '-' * 32
263263
264    fomeLines = getLines(file1)264    fomeLines = getLines(file1)
265    toLines = getLines(file2)265    toLines = getLines(file2)
266    fromTitle = makeTitle(file1, wrapnum)266    fromTitle = makeTitle(file1, wrapnum)
267    toTitle = makeTitle(file2, wrapnum)267    toTitle = makeTitle(file2, wrapnum)
268    sdiffLines(fomeLines, toLines, outFile, fromTitle, toTitle, context,268    sdiffLines(fomeLines, toLines, outFile, fromTitle, toTitle, context,
269               wrapnum, lines)269               wrapnum, lines)
270270
271271
272def inIgnoreList(name, ignorelist):272def inIgnoreList(name, ignorelist):
273    for pat in ignorelist:273    for pat in ignorelist:
274        if re.match(pat, name):274        if re.match(pat, name):
275            return True275            return True
276    return False276    return False
277277
278278
279def grabDir(dir):279def grabDir(dir):
280    'Get file list of dir, and remove unwanted file from the list'280    'Get file list of dir, and remove unwanted file from the list'
281    flist = []281    flist = []
282    while dir[-1] == '/':   # remove unwanted trailling slash282    while dir[-1] == '/':   # remove unwanted trailling slash
283        dir = dir[:-1]283        dir = dir[:-1]
284    prefix = dir + '/'      # os.path.sep284    prefix = dir + '/'      # os.path.sep
285    plen = len(prefix)285    plen = len(prefix)
286286
287    for root, dirs, files in os.walk(dir):287    for root, dirs, files in os.walk(dir):
t288        for d in dirs:t288        for d in [k for k in dirs]:
289            if inIgnoreList(d, _global_dir_ignore_list):289            if inIgnoreList(d, _global_dir_ignore_list):
290                dirs.remove(d)290                dirs.remove(d)
291        for f in files:291        for f in files:
292            if not inIgnoreList(f, _global_file_ignore_list):292            if not inIgnoreList(f, _global_file_ignore_list):
293                name = os.path.join(root, f)293                name = os.path.join(root, f)
294                flist.append(name[plen:])294                flist.append(name[plen:])
295    return flist295    return flist
296296
297297
298def mergeList(list1, list2):298def mergeList(list1, list2):
299    # important: should make a new list! don't refer to list1299    # important: should make a new list! don't refer to list1
300    list = [ i for i in list1 ] # XXX: list = list1300    list = [ i for i in list1 ] # XXX: list = list1
301    for i in list2:301    for i in list2:
302        if i not in list1: list.append(i)302        if i not in list1: list.append(i)
303    return list303    return list
304304
305305
306def sourceToHtml(src, outfile):306def sourceToHtml(src, outfile):
307    """Read file `src' and convert to html, write to file `outfile'"""307    """Read file `src' and convert to html, write to file `outfile'"""
308    #print 'call sourceToHtml(src=%s, outfile=%s)' % (src, outfile)308    #print 'call sourceToHtml(src=%s, outfile=%s)' % (src, outfile)
309    #print '-' * 32309    #print '-' * 32
310    try:310    try:
311        infp = open(src, 'r')311        infp = open(src, 'r')
312        body = infp.read()312        body = infp.read()
313        infp.close()313        infp.close()
314        body = body.replace("&","&amp;").replace(">","&gt;").replace("<","&lt;")314        body = body.replace("&","&amp;").replace(">","&gt;").replace("<","&lt;")
315315
316        outfp = open(outfile, 'w')316        outfp = open(outfile, 'w')
317        # TODO: convert to syntax highlighted html317        # TODO: convert to syntax highlighted html
318        outfp.write('''<html><head><title>%s</title></head><body>318        outfp.write('''<html><head><title>%s</title></head><body>
319            <pre style="font-family:monospace; font-size:9pt;">%s</pre>319            <pre style="font-family:monospace; font-size:9pt;">%s</pre>
320            </body></html>''' % (src, body))320            </body></html>''' % (src, body))
321        outfp.close()321        outfp.close()
322    except IOError, dig:322    except IOError, dig:
323        sys.stderr.write(str(dig) + '\n')323        sys.stderr.write(str(dig) + '\n')
324        sys.exit(2)324        sys.exit(2)
325325
326326
327def diffDir(dir1, dir2, outdir, wrapnum=None, lines=3):327def diffDir(dir1, dir2, outdir, wrapnum=None, lines=3):
328    '''diff two directories and generate an index page.  Return code328    '''diff two directories and generate an index page.  Return code
329    indicating whether there are differences and the summary info'''329    indicating whether there are differences and the summary info'''
330330
331    #print 'call diffDir(%s, %s, %s, ...)' % (dir1, dir2, outdir)331    #print 'call diffDir(%s, %s, %s, ...)' % (dir1, dir2, outdir)
332    #print '_global_dirseq:', _global_dirseq332    #print '_global_dirseq:', _global_dirseq
333    #print '=' * 72333    #print '=' * 72
334334
335    flist1 = grabDir(dir1)  # already filtered335    flist1 = grabDir(dir1)  # already filtered
336    flist2 = grabDir(dir2)336    flist2 = grabDir(dir2)
337    flist = mergeList(flist1, flist2)337    flist = mergeList(flist1, flist2)
338    return diffDirByList(dir1, dir2, outdir, flist, wrapnum, lines)338    return diffDirByList(dir1, dir2, outdir, flist, wrapnum, lines)
339339
340340
341def lstatFile(obj):341def lstatFile(obj):
342    'return None in case ENOENT, else a lstat info'342    'return None in case ENOENT, else a lstat info'
343    try:343    try:
344        statinfo = os.lstat(obj)344        statinfo = os.lstat(obj)
345    except OSError, dig:345    except OSError, dig:
346        if dig.errno == errno.ENOENT:346        if dig.errno == errno.ENOENT:
347            statinfo = None347            statinfo = None
348        else:348        else:
349            sys.stderr.write(str(dig) + '\n')349            sys.stderr.write(str(dig) + '\n')
350            sys.exit(2)350            sys.exit(2)
351    return statinfo351    return statinfo
352352
353353
354def diffDirByList(dir1, dir2, outdir, flist, wrapnum=None, lines=3):354def diffDirByList(dir1, dir2, outdir, flist, wrapnum=None, lines=3):
355    '''diff two directories and generate an index page.  Return code355    '''diff two directories and generate an index page.  Return code
356    indicating whether there are differences and the summary info'''356    indicating whether there are differences and the summary info'''
357357
358    data_rows = ''358    data_rows = ''
359    data_row = ''359    data_row = ''
360    summary = { 'changed': 0, 'added': 0, 'deleted': 0 }360    summary = { 'changed': 0, 'added': 0, 'deleted': 0 }
361    file_summary = { 'changed': 0, 'added': 0, 'deleted': 0 }361    file_summary = { 'changed': 0, 'added': 0, 'deleted': 0 }
362    has_diff = False362    has_diff = False
363363
364    flist.sort()364    flist.sort()
365365
366    for f in flist:366    for f in flist:
367        # set default values367        # set default values
368        fromLines = ''368        fromLines = ''
369        toLines = ''369        toLines = ''
370        fromDate = ''370        fromDate = ''
371        toDate = ''371        toDate = ''
372372
373        target = os.path.join(outdir, f)373        target = os.path.join(outdir, f)
374        obj1 = os.path.join(dir1, f)374        obj1 = os.path.join(dir1, f)
375        obj2 = os.path.join(dir2, f)375        obj2 = os.path.join(dir2, f)
376376
377        # make output dir and sub dir377        # make output dir and sub dir
378        try:378        try:
379            os.makedirs(os.path.join(outdir, os.path.dirname(f)))379            os.makedirs(os.path.join(outdir, os.path.dirname(f)))
380        except OSError, dig:380        except OSError, dig:
381            if dig.errno != errno.EEXIST:381            if dig.errno != errno.EEXIST:
382                sys.stderr.write(str(dig) + '\n')382                sys.stderr.write(str(dig) + '\n')
383                sys.exit(2)383                sys.exit(2)
384384
385        stat1 = lstatFile(obj1)385        stat1 = lstatFile(obj1)
386        stat2 = lstatFile(obj2)386        stat2 = lstatFile(obj2)
387387
388        if stat1 and not stat2: # deleted388        if stat1 and not stat2: # deleted
389            print '%-40s |' % f,389            print '%-40s |' % f,
390            print 'File removed',390            print 'File removed',
391            #st_mtime = time.localtime(stat1[8])391            #st_mtime = time.localtime(stat1[8])
392            if not stat.S_ISREG(stat1[0]) or isBinaryFile(obj1):392            if not stat.S_ISREG(stat1[0]) or isBinaryFile(obj1):
393                print '(skipped dir/special/binary)'393                print '(skipped dir/special/binary)'
394                continue394                continue
395            print395            print
396            old = sourceToHtml(obj1, target + '-.html')396            old = sourceToHtml(obj1, target + '-.html')
397            data_row = _deleted_data_row_template % {'pathname': f}397            data_row = _deleted_data_row_template % {'pathname': f}
398            summary['deleted'] += 1398            summary['deleted'] += 1
399            has_diff = True399            has_diff = True
400400
401        elif not stat1 and stat2: # added401        elif not stat1 and stat2: # added
402            print '%-40s |' % f,402            print '%-40s |' % f,
403            print 'New file',403            print 'New file',
404            if not stat.S_ISREG(stat2[0]) or isBinaryFile(obj2):404            if not stat.S_ISREG(stat2[0]) or isBinaryFile(obj2):
405                print '(skipped special/binary)'405                print '(skipped special/binary)'
406                continue406                continue
407            print407            print
408            new = sourceToHtml(obj2, target + '.html')408            new = sourceToHtml(obj2, target + '.html')
409            data_row = _added_data_row_template % {'pathname': f}409            data_row = _added_data_row_template % {'pathname': f}
410            summary['added'] += 1410            summary['added'] += 1
411            has_diff = True411            has_diff = True
412412
413        elif stat1 and stat2: # same or diff413        elif stat1 and stat2: # same or diff
414            # do not compare special or binary file414            # do not compare special or binary file
415            if not stat.S_ISREG(stat1[0]) or isBinaryFile(obj1):415            if not stat.S_ISREG(stat1[0]) or isBinaryFile(obj1):
416                print '%-40s |' % f,416                print '%-40s |' % f,
417                print '(skipped, former file is special)'417                print '(skipped, former file is special)'
418                continue418                continue
419            if not stat.S_ISREG(stat2[0]) or isBinaryFile(obj2):419            if not stat.S_ISREG(stat2[0]) or isBinaryFile(obj2):
420                print '%-40s |' % f,420                print '%-40s |' % f,
421                print '(skipped, latter file is binary)'421                print '(skipped, latter file is binary)'
422                continue422                continue
423            if filecmp.cmp(obj1, obj2):423            if filecmp.cmp(obj1, obj2):
424                continue424                continue
425425
426            has_diff = True426            has_diff = True
427            fromDate = time.ctime(stat1[8])427            fromDate = time.ctime(stat1[8])
428            toDate = time.ctime(stat2[8])428            toDate = time.ctime(stat2[8])
429            fromLines = getLines(obj1)429            fromLines = getLines(obj1)
430            toLines = getLines(obj2)430            toLines = getLines(obj2)
431431
432            # Cdiffs432            # Cdiffs
433            file_summary = cdiffLines(fromLines, toLines,433            file_summary = cdiffLines(fromLines, toLines,
434                target+'.cdiff.html', obj1, obj2, fromDate, toDate, lines)434                target+'.cdiff.html', obj1, obj2, fromDate, toDate, lines)
435435
436            # Udiffs436            # Udiffs
437            udiffLines(fromLines, toLines, target+'.udiff.html', obj1, obj2,437            udiffLines(fromLines, toLines, target+'.udiff.html', obj1, obj2,
438                       fromDate, toDate, lines)438                       fromDate, toDate, lines)
439439
440            # Sdiffs440            # Sdiffs
441            sdiffLines(fromLines, toLines, target+'.sdiff.html', obj1, obj2,441            sdiffLines(fromLines, toLines, target+'.sdiff.html', obj1, obj2,
442                       True, wrapnum, lines)442                       True, wrapnum, lines)
443443
444            # Fdiffs444            # Fdiffs
445            sdiffLines(fromLines, toLines, target+'.fdiff.html', obj1, obj2,445            sdiffLines(fromLines, toLines, target+'.fdiff.html', obj1, obj2,
446                       False, wrapnum, lines)446                       False, wrapnum, lines)
447447
448            print '%-40s |' % f,448            print '%-40s |' % f,
449            print 'Changed/Deleted/Added: %d/%d/%d' % (\449            print 'Changed/Deleted/Added: %d/%d/%d' % (\
450                file_summary['changed'],450                file_summary['changed'],
451                file_summary['deleted'],451                file_summary['deleted'],
452                file_summary['added'])452                file_summary['added'])
453453
454            old = sourceToHtml(obj1, target + '-.html')454            old = sourceToHtml(obj1, target + '-.html')
455            new = sourceToHtml(obj2, target + '.html')455            new = sourceToHtml(obj2, target + '.html')
456            data_row = _diff_data_row_template % dict(456            data_row = _diff_data_row_template % dict(
457                pathname = f,457                pathname = f,
458                changed = file_summary['changed'],458                changed = file_summary['changed'],
459                deleted = file_summary['deleted'],459                deleted = file_summary['deleted'],
460                added = file_summary['added'],460                added = file_summary['added'],
461            )461            )
462            summary['changed'] += 1462            summary['changed'] += 1
463        else: # this case occured when controlled by master file list463        else: # this case occured when controlled by master file list
464            print '%-40s |' % f,464            print '%-40s |' % f,
465            print 'Not found'465            print 'Not found'
466            data_row = ''466            data_row = ''
467467
468        data_rows += data_row468        data_rows += data_row
469469
470    if not has_diff:470    if not has_diff:
471        return False471        return False
472472
473    # Generate footer info473    # Generate footer info
474    footer_info = _footer_info_template % dict(474    footer_info = _footer_info_template % dict(
475        time = time.strftime('%a %b %d %X %Z %Y', time.localtime()),475        time = time.strftime('%a %b %d %X %Z %Y', time.localtime()),
476        myname = _myname,476        myname = _myname,
477        revision = _revision477        revision = _revision
478    )478    )
479479
480    # now wirte index page480    # now wirte index page
481    try:481    try:
482        index = open(os.path.join(outdir, 'index.html'), 'w')482        index = open(os.path.join(outdir, 'index.html'), 'w')
483    except IOError, dig:483    except IOError, dig:
484        sys.stderr.write(str(dig) + '\n')484        sys.stderr.write(str(dig) + '\n')
485        sys.exit(2)485        sys.exit(2)
486    index.write(_file_template % dict(486    index.write(_file_template % dict(
487        title = '%s vs %s' % (dir1, dir2),487        title = '%s vs %s' % (dir1, dir2),
488        styles = _styles,488        styles = _styles,
489        header_info = _header_info_template % dict(dir1=dir1, dir2=dir2),489        header_info = _header_info_template % dict(dir1=dir1, dir2=dir2),
490        summary_info = _summary_info_template % summary,490        summary_info = _summary_info_template % summary,
491        data_rows = _data_rows_template % {'data_rows': data_rows},491        data_rows = _data_rows_template % {'data_rows': data_rows},
492        legend = _legend,492        legend = _legend,
493        footer_info = footer_info,493        footer_info = footer_info,
494    ))494    ))
495    index.close()495    index.close()
496    return True496    return True
497497
498498
499def warnOverwrite(pathname):499def warnOverwrite(pathname):
500    'Warnning for overwriting, return True if answered yes, False if no'500    'Warnning for overwriting, return True if answered yes, False if no'
501    msg = "`%s' exists, are you sure you want to overwrite it (yes/no)? "501    msg = "`%s' exists, are you sure you want to overwrite it (yes/no)? "
502    while True:502    while True:
503        sys.stderr.write(msg % pathname)503        sys.stderr.write(msg % pathname)
504        answer = raw_input('')504        answer = raw_input('')
505        if answer == 'yes':505        if answer == 'yes':
506            return True506            return True
507        elif answer == 'no':507        elif answer == 'no':
508            return False508            return False
509        # else: prompt again509        # else: prompt again
510510
511511
512def isBinaryFile(file):512def isBinaryFile(file):
513    '''I determine a binary file by reading the first 1024 bytes of the file513    '''I determine a binary file by reading the first 1024 bytes of the file
514    and counting the non-text characters, if the number is great than 8, then514    and counting the non-text characters, if the number is great than 8, then
515    the file is considered as binary file.  This is not very reliable but is515    the file is considered as binary file.  This is not very reliable but is
516    effective'''516    effective'''
517    if not file:517    if not file:
518        return False518        return False
519519
520    non_text = 0520    non_text = 0
521    target_count = 8521    target_count = 8
522    try:522    try:
523        fp = open(file, 'rb')523        fp = open(file, 'rb')
524        data = fp.read(1024)524        data = fp.read(1024)
525        for c in data:525        for c in data:
526            a = ord(c)526            a = ord(c)
527            if a < 8 or (a > 13 and a < 32): # not printable527            if a < 8 or (a > 13 and a < 32): # not printable
528                non_text += 1528                non_text += 1
529                if non_text >= target_count: break529                if non_text >= target_count: break
530        fp.close()530        fp.close()
531    except IOError, dig:531    except IOError, dig:
532        sys.stderr.write(str(dig) + '\n')532        sys.stderr.write(str(dig) + '\n')
533        sys.exit(2)533        sys.exit(2)
534    return non_text >= target_count534    return non_text >= target_count
535535
536536
537def cdiffToHtml(cdiff, title):537def cdiffToHtml(cdiff, title):
538    '''cdiff is context diff (a list) that generated by difflib.context_diff,538    '''cdiff is context diff (a list) that generated by difflib.context_diff,
539    return summary and html page'''539    return summary and html page'''
540    summary = { 'changed': 0, 'added': 0, 'deleted': 0 }540    summary = { 'changed': 0, 'added': 0, 'deleted': 0 }
541    line_pattern = '<span class="%s">%s</span>'541    line_pattern = '<span class="%s">%s</span>'
542542
543    body = ''543    body = ''
544    old_group = False544    old_group = False
545    for line in cdiff:545    for line in cdiff:
546        n = len(line)546        n = len(line)
547        line = line.replace("&","&amp;").replace(">","&gt;").replace("<","&lt;")547        line = line.replace("&","&amp;").replace(">","&gt;").replace("<","&lt;")
548        if n >= 4 and line[0:4] == '*** ':548        if n >= 4 and line[0:4] == '*** ':
549            old_group = True549            old_group = True
550            body += line_pattern % ('fromtitle', line)550            body += line_pattern % ('fromtitle', line)
551        elif n >= 4 and line[0:4] == '--- ':551        elif n >= 4 and line[0:4] == '--- ':
552            old_group = False552            old_group = False
553            body += line_pattern % ('totitle', line)553            body += line_pattern % ('totitle', line)
554        elif n >= 2 and line[0:2] == '  ':554        elif n >= 2 and line[0:2] == '  ':
555            body += line_pattern % ('same', line)555            body += line_pattern % ('same', line)
556        elif n >= 2 and line[0:2] == '! ':556        elif n >= 2 and line[0:2] == '! ':
557            body += line_pattern % ('change', line)557            body += line_pattern % ('change', line)
558            if old_group:558            if old_group:
559                summary['changed'] += 1559                summary['changed'] += 1
560        elif n >= 2 and line[0:2] == '- ':560        elif n >= 2 and line[0:2] == '- ':
561            body += line_pattern % ('delete', line)561            body += line_pattern % ('delete', line)
562            summary['deleted'] += 1562            summary['deleted'] += 1
563        elif n >= 2 and line[0:2] == '+ ':563        elif n >= 2 and line[0:2] == '+ ':
564            body += line_pattern % ('insert', line)564            body += line_pattern % ('insert', line)
565            summary['added'] += 1565            summary['added'] += 1
566        elif n >= 15 and line[0:15] == '*' * 15:566        elif n >= 15 and line[0:15] == '*' * 15:
567            body += '<hr>'567            body += '<hr>'
568        else: # shouldn't happen568        else: # shouldn't happen
569            body += line569            body += line
570570
571    html = '''<html><head>571    html = '''<html><head>
572        <title>%s</title>572        <title>%s</title>
573        <style type="text/css">573        <style type="text/css">
574            .fromtitle {color:brown; font:bold 11pt;}574            .fromtitle {color:brown; font:bold 11pt;}
575            .totitle {color:green; font:bold 11pt;}575            .totitle {color:green; font:bold 11pt;}
576            .same {color:black; font:9pt;}576            .same {color:black; font:9pt;}
577            .change {color:blue; font:9pt;}577            .change {color:blue; font:9pt;}
578            .delete {color:brown; font:9pt;}578            .delete {color:brown; font:9pt;}
579            .insert {color:green; font:9pt;}579            .insert {color:green; font:9pt;}
580        </style>580        </style>
581        <body>581        <body>
582            <pre>%s</pre>582            <pre>%s</pre>
583        </body>583        </body>
584        </head></html>''' % (title, body)584        </head></html>''' % (title, body)
585    return summary, html585    return summary, html
586586
587587
588def udiffToHtml(udiff, title):588def udiffToHtml(udiff, title):
589    '''udiff is uniform diff (a list) that generated by difflib.uniform_diff,589    '''udiff is uniform diff (a list) that generated by difflib.uniform_diff,
590    return html page'''590    return html page'''
591591
592    line_pattern = '<span class="%s">%s</span>'592    line_pattern = '<span class="%s">%s</span>'
593    body = ''593    body = ''
594    for line in udiff:594    for line in udiff:
595        n = len(line)595        n = len(line)
596        line = line.replace("&","&amp;").replace(">","&gt;").replace("<","&lt;")596        line = line.replace("&","&amp;").replace(">","&gt;").replace("<","&lt;")
597        if n >= 4 and line[0:4] == '--- ':597        if n >= 4 and line[0:4] == '--- ':
598            body += line_pattern % ('fromtitle', line)598            body += line_pattern % ('fromtitle', line)
599        elif n >= 4 and line[0:4] == '+++ ':599        elif n >= 4 and line[0:4] == '+++ ':
600            body += line_pattern % ('totitle', line)600            body += line_pattern % ('totitle', line)
601        elif n >= 1 and line[0] == ' ':601        elif n >= 1 and line[0] == ' ':
602            body += line_pattern % ('same', line)602            body += line_pattern % ('same', line)
603        elif n >= 1 and line[0] == '-':603        elif n >= 1 and line[0] == '-':
604            body += line_pattern % ('old', line)604            body += line_pattern % ('old', line)
605        elif n >= 1 and line[0] == '+':605        elif n >= 1 and line[0] == '+':
606            body += line_pattern % ('new', line)606            body += line_pattern % ('new', line)
607        elif n >= 4 and line[0:4] == '@@ -':607        elif n >= 4 and line[0:4] == '@@ -':
608            body += '<hr>'608            body += '<hr>'
609            body += line_pattern % ('head', line)609            body += line_pattern % ('head', line)
610        else: # shouldn't happen610        else: # shouldn't happen
611            body += line611            body += line
612612
613    html = '''<html><head>613    html = '''<html><head>
614        <title>%s</title>614        <title>%s</title>
615        <style type="text/css">615        <style type="text/css">
616            .fromtitle {color:brown; font:bold 11pt;}616            .fromtitle {color:brown; font:bold 11pt;}
617            .totitle {color:green; font:bold 11pt;}617            .totitle {color:green; font:bold 11pt;}
618            .head {color:blue; font:bold 9pt;}618            .head {color:blue; font:bold 9pt;}
619            .same {color:black; font:9pt;}619            .same {color:black; font:9pt;}
620            .old {color:brown; font:9pt;}620            .old {color:brown; font:9pt;}
621            .new {color:green; font:9pt;}621            .new {color:green; font:9pt;}
622        </style>622        </style>
623        <body>623        <body>
624            <pre>%s</pre>624            <pre>%s</pre>
625        </body>625        </body>
626        </head></html>''' % (title, body)626        </head></html>''' % (title, body)
627    return html627    return html
628628
629629
630def stripPrefix(name, p=0):630def stripPrefix(name, p=0):
631    '''strip NUM slashes, like patch(1) -pNUM631    '''strip NUM slashes, like patch(1) -pNUM
632    eg1: /foo/bar/a/b/x.c632    eg1: /foo/bar/a/b/x.c
633    -p0 gives orignal name (no change)633    -p0 gives orignal name (no change)
634    -p1 gives foo/bar/a/b/x.c634    -p1 gives foo/bar/a/b/x.c
635    -p2 gives bar/a/b/x.c635    -p2 gives bar/a/b/x.c
636    -p9 gives x.c636    -p9 gives x.c
637637
638    eg2: foo/bar/a/b/x.c638    eg2: foo/bar/a/b/x.c
639    -p0 gives orignal name (no change)639    -p0 gives orignal name (no change)
640    -p1 gives bar/a/b/x.c640    -p1 gives bar/a/b/x.c
641    -p2 gives a/b/x.c641    -p2 gives a/b/x.c
642    -p9 gives x.c642    -p9 gives x.c
643643
644    eg3: ./foo/bar/a/b/x.c644    eg3: ./foo/bar/a/b/x.c
645    -p0 gives orignal name (no change)645    -p0 gives orignal name (no change)
646    -p1 gives foo/bar/a/b/x.c646    -p1 gives foo/bar/a/b/x.c
647    -p2 gives bar/a/b/x.c647    -p2 gives bar/a/b/x.c
648    -p9 gives x.c648    -p9 gives x.c
649    '''649    '''
650    cur = 0650    cur = 0
651    tail = len(name) - 1651    tail = len(name) - 1
652    while p > 0:652    while p > 0:
653        index = name.find('/', cur)653        index = name.find('/', cur)
654        #print 'p:', p, 'cur:', cur, 'index:', index654        #print 'p:', p, 'cur:', cur, 'index:', index
655        if index == -1: break655        if index == -1: break
656        while index <= tail and name[index] == '/':656        while index <= tail and name[index] == '/':
657            index += 1657            index += 1
658        cur = index658        cur = index
659        p -= 1659        p -= 1
660    return name[cur:]660    return name[cur:]
661661
662662
663def readFileList(file, p=0):663def readFileList(file, p=0):
664    '''Return content of filelist (a file, each line is a filename)'''664    '''Return content of filelist (a file, each line is a filename)'''
665    flist = []665    flist = []
666    if not file:666    if not file:
667        return []667        return []
668    if file == '-':  # read from stdin668    if file == '-':  # read from stdin
669        flist = sys.stdin.readlines()669        flist = sys.stdin.readlines()
670    else:670    else:
671        try:671        try:
672            fp = open(file, 'r')672            fp = open(file, 'r')
673            flist = fp.readlines()673            flist = fp.readlines()
674            fp.close()674            fp.close()
675        except IOError, dig:675        except IOError, dig:
676            sys.stderr.write(str(dig) + '\n')676            sys.stderr.write(str(dig) + '\n')
677            sys.exit(2)677            sys.exit(2)
678678
679    outlist = []679    outlist = []
680    for i in flist:680    for i in flist:
681        outlist.append(stripPrefix(i, p).rstrip())681        outlist.append(stripPrefix(i, p).rstrip())
682    #print outlist682    #print outlist
683    return outlist683    return outlist
684684
685685
686if __name__ == '__main__':686if __name__ == '__main__':
687    import optparse687    import optparse
688688
689    usage = '''689    usage = '''
690    %(name)s [options] OLD NEW690    %(name)s [options] OLD NEW
691    %(name)s OLD NEW [options]691    %(name)s OLD NEW [options]
692692
693    Diff two files/directories and produce HTML pages.''' % \693    Diff two files/directories and produce HTML pages.''' % \
694    {'name': os.path.basename(sys.argv[0])}694    {'name': os.path.basename(sys.argv[0])}
695695
696    parser = optparse.OptionParser(usage)696    parser = optparse.OptionParser(usage)
697    parser.add_option('-o', '--outupt', dest='output',697    parser.add_option('-o', '--outupt', dest='output',
698                      help='specify output file or directory name')698                      help='specify output file or directory name')
699    parser.add_option('-c', '--context', action='store_true',699    parser.add_option('-c', '--context', action='store_true',
700                      dest='context', default=False,700                      dest='context', default=False,
701                      help='generate context diff (default is full diff),' + \701                      help='generate context diff (default is full diff),' + \
702                           ' only take effect when diffing two files')702                           ' only take effect when diffing two files')
703    parser.add_option('-f', '--filelist', dest='filelist', metavar='FILE',703    parser.add_option('-f', '--filelist', dest='filelist', metavar='FILE',
704                      help='specify a file list to read from, filelist can ' + \704                      help='specify a file list to read from, filelist can ' + \
705                           'be generated by find -type f, specify - to read' + \705                           'be generated by find -type f, specify - to read' + \
706                           ' from stdin')706                           ' from stdin')
707    parser.add_option('-p', '--striplevel', dest='striplevel',707    parser.add_option('-p', '--striplevel', dest='striplevel',
708                      type='int', metavar='NUM',708                      type='int', metavar='NUM',
709                      help='for all pathnames in the filelist, delete NUM ' + \709                      help='for all pathnames in the filelist, delete NUM ' + \
710                           'path name components from the beginning of each' + \710                           'path name components from the beginning of each' + \
711                           ' path name, it is similar to patch(1) -p')711                           ' path name, it is similar to patch(1) -p')
712    parser.add_option('-n', '--lines', dest='lines',712    parser.add_option('-n', '--lines', dest='lines',
713                      type='int', metavar='NUM', default=3,713                      type='int', metavar='NUM', default=3,
714                      help='specify context line count when generating ' + \714                      help='specify context line count when generating ' + \
715                           'context diffs or unified diffs, default is 3')715                           'context diffs or unified diffs, default is 3')
716    parser.add_option('-w', '--wrap', dest='wrapnum',716    parser.add_option('-w', '--wrap', dest='wrapnum',
717                      type='int', metavar='WIDTH',717                      type='int', metavar='WIDTH',
718                      help='specify column number where lines are broken ' + \718                      help='specify column number where lines are broken ' + \
719                      'and wrapped for sdiff, default is no line wrapping')719                      'and wrapped for sdiff, default is no line wrapping')
720    parser.add_option('-y', '--yes', action='store_true',720    parser.add_option('-y', '--yes', action='store_true',
721                      dest='overwrite', default=False,721                      dest='overwrite', default=False,
722                      help='do not prompt for overwriting')722                      help='do not prompt for overwriting')
723723
724    opts, args = parser.parse_args()724    opts, args = parser.parse_args()
725725
726    if len(args) != 2:726    if len(args) != 2:
727        sys.stderr.write("Sorry, you must specify two file/directory names\n" \727        sys.stderr.write("Sorry, you must specify two file/directory names\n" \
728                         + "type `%s -h' for help\n" % _myname)728                         + "type `%s -h' for help\n" % _myname)
729        sys.exit(1)729        sys.exit(1)
730730
731    if not opts.output:731    if not opts.output:
732        sys.stderr.write("Sorry, you must specify output name (use `-o')\n")732        sys.stderr.write("Sorry, you must specify output name (use `-o')\n")
733        sys.exit(2)733        sys.exit(2)
734734
735    try:735    try:
736        # Note: use stat instead lstat to permit symbolic links736        # Note: use stat instead lstat to permit symbolic links
737        stat1 = os.stat(args[0])[0]737        stat1 = os.stat(args[0])[0]
738        stat2 = os.stat(args[1])[0]738        stat2 = os.stat(args[1])[0]
739    except OSError, dig:739    except OSError, dig:
740        sys.stderr.write(str(dig) + '\n')740        sys.stderr.write(str(dig) + '\n')
741        sys.exit(2)741        sys.exit(2)
742742
743    # Compare two files743    # Compare two files
744    #744    #
745    if stat.S_ISREG(stat1) and stat.S_ISREG(stat2):745    if stat.S_ISREG(stat1) and stat.S_ISREG(stat2):
746        if not opts.overwrite and os.path.exists(opts.output):746        if not opts.overwrite and os.path.exists(opts.output):
747            if not warnOverwrite(opts.output): sys.exit(1)747            if not warnOverwrite(opts.output): sys.exit(1)
748        url = 'file://%s\n' % os.path.realpath(opts.output)748        url = 'file://%s\n' % os.path.realpath(opts.output)
749749
750        if filecmp.cmp(args[0], arg2[1]):750        if filecmp.cmp(args[0], arg2[1]):
751            print 'No difference found.'751            print 'No difference found.'
752        else:752        else:
753            diffFile(args[0], args[1], opts.output, opts.context,753            diffFile(args[0], args[1], opts.output, opts.context,
754                    opts.wrapnum, opts.lines)754                    opts.wrapnum, opts.lines)
755            print '\nURL:\n%s' % url755            print '\nURL:\n%s' % url
756756
757    # Compare two dirs757    # Compare two dirs
758    #758    #
759    elif stat.S_ISDIR(stat1) and stat.S_ISDIR(stat2):759    elif stat.S_ISDIR(stat1) and stat.S_ISDIR(stat2):
760        if not opts.overwrite and os.path.exists(opts.output):760        if not opts.overwrite and os.path.exists(opts.output):
761            if opts.filelist == '-':761            if opts.filelist == '-':
762                # stdin redirected, so we cannot read answer from stdin762                # stdin redirected, so we cannot read answer from stdin
763                print "`%s' exists, please select another output directory, " \763                print "`%s' exists, please select another output directory, " \
764                      "or specify '-y' to force overwriting." % opts.output764                      "or specify '-y' to force overwriting." % opts.output
765                sys.exit(1)765                sys.exit(1)
766            else:766            else:
767                if not warnOverwrite(opts.output):767                if not warnOverwrite(opts.output):
768                    sys.exit(1)768                    sys.exit(1)
769        url = 'file://%s/index.html\n' % os.path.realpath(opts.output)769        url = 'file://%s/index.html\n' % os.path.realpath(opts.output)
770770
771        if opts.filelist:771        if opts.filelist:
772            # filelist mode, contolled by master file list772            # filelist mode, contolled by master file list
773            # read file list, ignore lines with first char is '/'773            # read file list, ignore lines with first char is '/'
774            # if see 'foo/bar/abc', makedirs foo/bar/, then put abc.diffs to it774            # if see 'foo/bar/abc', makedirs foo/bar/, then put abc.diffs to it
775            flist = readFileList(opts.filelist, opts.striplevel)775            flist = readFileList(opts.filelist, opts.striplevel)
776            if diffDirByList(args[0], args[1], opts.output,776            if diffDirByList(args[0], args[1], opts.output,
777                             flist, opts.wrapnum, opts.lines):777                             flist, opts.wrapnum, opts.lines):
778                print '\nURL:\n%s' % url778                print '\nURL:\n%s' % url
779            else:779            else:
780                print 'No difference found.'780                print 'No difference found.'
781        else:781        else:
782            if diffDir(args[0], args[1], opts.output, opts.wrapnum,782            if diffDir(args[0], args[1], opts.output, opts.wrapnum,
783                       opts.lines):783                       opts.lines):
784                print '\nURL:\n%s' % url784                print '\nURL:\n%s' % url
785            else:785            else:
786                print 'No difference found.'786                print 'No difference found.'
787787
788    else:788    else:
789        sys.stderr.write("Sorry, I don't know how to compare `%s' and `%s'\n" \789        sys.stderr.write("Sorry, I don't know how to compare `%s' and `%s'\n" \
790                         % (args[0], args[1]))790                         % (args[0], args[1]))
791        sys.exit(2)791        sys.exit(2)
792792
793# vim:set et sts=4 sw=4:793# vim:set et sts=4 sw=4:
Legends
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op