You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by gs...@locus.apache.org on 2000/05/12 14:01:50 UTC
cvs commit: apache-site/websrc viewcvs.cgi viewcvs.conf
gstein 00/05/12 05:01:50
Modified: websrc viewcvs.cgi
Removed: websrc viewcvs.conf
Log:
upgrade to ViewCVS 0.5. plenty o' fixes. annotation support. better
colorization. initial work on Bonsai-like query support.
ViewCVS has turned into more than a single CGI, so it is now installed
outside the CVS repository. (my home dir pending a shift to /usr/local)
This CGI script may disappear, too.
Revision Changes Path
1.2 +215 -311 apache-site/websrc/viewcvs.cgi
Index: viewcvs.cgi
===================================================================
RCS file: /home/cvs/apache-site/websrc/viewcvs.cgi,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- viewcvs.cgi 2000/03/24 11:40:00 1.1
+++ viewcvs.cgi 2000/05/12 12:01:49 1.2
@@ -1,40 +1,19 @@
#!/usr/local/bin/python
# -*-python-*-
#
-# viewcvs: View CVS repositories via a web browser
-#
-# Copyright (C) 1999-2000 Greg Stein. All Rights Reserved.
-#
-# By using this file, you agree to the terms and conditions set forth below:
+# Copyright (C) 1999-2000 The ViewCVS Group. All Rights Reserved.
#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-# 1. Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# 2. Redistributions in binary form must reproduce the above copyright
-# notice, this list of conditions and the following disclaimer in the
-# documentation and/or other materials provided with the distribution.
+# By using this file, you agree to the terms and conditions set forth in
+# the LICENSE.html file which can be found at the top level of the ViewCVS
+# distribution or at http://www.lyra.org/viewcvs/license-1.html.
+#
+# Contact information:
+# Greg Stein, PO Box 760, Palo Alto, CA, 94302
+# gstein@lyra.org, http://www.lyra.org/viewcvs/
#
-# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-# SUCH DAMAGE.
-#
# -----------------------------------------------------------------------
-#
-# This module is maintained by Greg and is available at:
-# http://www.lyra.org/greg/python/viewcvs/
#
-# For tracking purposes, this software is identified by:
-# viewcvs.cgi,v 1.30 2000/03/24 11:09:14 gstein Exp
+# viewcvs: View CVS repositories via a web browser
#
# -----------------------------------------------------------------------
#
@@ -47,205 +26,19 @@
# -----------------------------------------------------------------------
#
-__version__ = '0.4'
+__version__ = '0.5'
#########################################################################
-#
-# CONFIGURATION
#
-# There are three forms of configuration:
+# INSTALL-TIME CONFIGURATION
#
-# 1) copy viewcvs.conf.dist to viewcvs.conf and edit
-# 2) as (1), but delete all unchanged entries from viewcvs.conf
-# 3) do not use viewcvs.conf and just edit the defaults in this file
+# These values will be set during the installation process. During
+# development, they will remain None.
#
-# Most users will want to use (1), but there are slight speed advantages
-# to the other two options. Note that viewcvs.conf values are a bit easier
-# to work with since it is raw text, rather than python literal values.
-#
-
-class Config:
- _sections = ('general', 'images', 'options', 'colors', 'text')
- _force_multi_value = ('cvs_roots', 'forbidden')
-
- def __init__(self):
- for section in self._sections:
- setattr(self, section, _sub_config())
-
- def load_config(self, fname):
- this_dir = os.path.dirname(sys.argv[0])
- pathname = os.path.join(this_dir, fname)
- parser = ConfigParser.ConfigParser()
- parser.read(pathname)
-
- for section in self._sections:
- if not parser.has_section(section):
- continue
-
- sc = getattr(self, section)
-
- for opt in parser.options(section):
- value = parser.get(section, opt)
- if (section != 'text' and ',' in value) or \
- opt in self._force_multi_value:
- value = map(string.strip, string.split(value, ','))
- else:
- try:
- value = int(value)
- except ValueError:
- pass
-
- if opt == 'cvs_roots':
- roots = { }
- for root in value:
- name, path = map(string.strip, string.split(root, ':'))
- roots[name] = path
- value = roots
- setattr(sc, opt, value)
-
-class _sub_config:
- def get_image(self, which):
- text = '[%s]' % string.upper(which)
- path, width, height = getattr(self, which)
- if path:
- return '<img src="%s" alt="%s" border=0 width=%s height=%s>' % \
- (path, text, width, height)
- return text
-cfg = Config()
+LIBRARY_DIR = "/home/gstein/viewcvs/lib"
+CONF_PATHNAME = "/home/gstein/viewcvs/viewcvs.conf"
-cfg.general.cvs_roots = {
- # user-visible-name : path
- "Development" : "/home/cvsroot",
- }
-cfg.general.default_root = "Development"
-cfg.general.rcs_path = ''
-cfg.general.mime_types_file = ''
-cfg.general.address = '<a href="mailto:gstein@lyra.org">gstein@lyra.org</a>'
-cfg.general.main_title = 'CVS Repository'
-cfg.general.forbidden = ()
-
-cfg.images.logo = "/icons/apache_pb.gif", 259, 32
-cfg.images.back_icon = "/icons/small/back.gif", 16, 16
-cfg.images.dir_icon = "/icons/small/dir.gif", 16, 16
-cfg.images.file_icon = "/icons/small/text.gif", 16, 16
-
-cfg.colors.markup_log = "#ffffff"
-
-cfg.colors.diff_heading = "#99cccc"
-cfg.colors.diff_empty = "#cccccc"
-cfg.colors.diff_remove = "#ff9999"
-cfg.colors.diff_change = "#99ff99"
-cfg.colors.diff_add = "#ccccff"
-cfg.colors.diff_dark_change = "#99cc99"
-
-cfg.colors.even_odd = ("#ccccee", "#ffffff")
-
-cfg.colors.nav_header = "#9999ee"
-
-cfg.colors.text = "#000000"
-cfg.colors.background = "#ffffff"
-cfg.colors.alt_background = "#eeeeee"
-
-cfg.colors.column_header_normal = "#cccccc"
-cfg.colors.column_header_sorted = "#88ff88"
-
-cfg.colors.table_border = None # no border
-
-cfg.options.sort_by = 'file'
-cfg.options.hide_attic = 1
-cfg.options.log_sort = 'date'
-cfg.options.diff_format = 'h'
-cfg.options.hide_cvsroot = 1
-cfg.options.hide_non_readable = 1
-cfg.options.show_author = 1
-cfg.options.hr_breakable = 1
-cfg.options.hr_funout = 1
-cfg.options.hr_ignore_white = 1
-cfg.options.hr_ignore_keyword_subst = 1
-cfg.options.allow_annotate = 0 ### doesn't work yet!
-cfg.options.allow_markup = 1
-cfg.options.allow_compress = 1
-cfg.options.use_java_script = 1
-cfg.options.open_extern_window = 1
-cfg.options.extern_window_width = 600
-cfg.options.extern_window_height = 440
-cfg.options.checkout_magic = 1
-cfg.options.show_subdir_lastmod = 0
-cfg.options.show_logs = 1
-cfg.options.show_log_in_markup = 1
-cfg.options.allow_version_select = 1
-cfg.options.py2html_path = '.'
-cfg.options.short_log_len = 80
-cfg.options.table_padding = 2
-cfg.options.diff_font_face = 'Helvetica,Arial'
-cfg.options.diff_font_size = -1
-cfg.options.input_text_size = 12
-
-cfg.text.long_intro = """\
-<p>
-This is a WWW interface for CVS Repositories.
-You can browse the file hierarchy by picking directories
-(which have slashes after them, <i>e.g.</i>, <b>src/</b>).
-If you pick a file, you will see the revision history
-for that file.
-Selecting a revision number will download that revision of
-the file. There is a link at each revision to display
-diffs between that revision and the previous one, and
-a form at the bottom of the page that allows you to
-display diffs between arbitrary revisions.
-</p>
-<p>
-This script
-(<a href="http://www.lyra.org/greg/python/viewcvs/">ViewCVS</a>)
-has been written by Greg Stein
-<<a href="mailto:gstein@lyra.org">gstein@lyra.org</a>>
-based on the
-<a href="http://linux.fh-heilbronn.de/~zeller/cgi/cvsweb.cgi">cvsweb</a>
-script by Henner Zeller
-<<a href="mailto:zeller@think.de">zeller@think.de</a>>;
-it is covered by the
-<a href="http://www.opensource.org/licenses/bsd-license.html">BSD-Licence</a>.
-If you would like to use this CGI script on your own web server and
-CVS tree, see Greg's
-<a href="http://www.lyra.org/greg/python/viewcvs/">ViewCVS distribution
-site</a>.
-Please send any suggestions, comments, etc. to
-<a href="mailto:gstein@lyra.org">Greg Stein</a>.
-</p>
-"""
-# ' stupid emacs...
-
-cfg.text.doc_info = """
-<h3>CVS Documentation</h3>
-<blockquote>
-<p>
- <a href="http://www.loria.fr/~molli/cvs/doc/cvs_toc.html">CVS
- User's Guide</a><br>
- <a href="http://www.arc.unm.edu/~rsahu/cvs.html">CVS Tutorial</a><br>
- <a href="http://cellworks.washington.edu/pub/docs/cvs/tutorial/cvs_tutorial_1.html">Another CVS tutorial</a><br>
- <a href="http://www.csc.calpoly.edu/~dbutler/tutorials/winter96/cvs/">Yet another CVS tutorial (a little old, but nice)</a><br>
- <a href="http://www.cs.utah.edu/dept/old/texinfo/cvs/FAQ.txt">An old but very useful FAQ about CVS</a>
-</p>
-</blockquote>
-"""
-
-# Fill in stuff on (say) anonymous pserver access here. For example, what
-# access mechanism, login, path, etc should be used.
-cfg.text.repository_info = """
-<!-- insert repository access instructions here -->
-"""
-
-cfg.text.short_intro = """\
-<p>
-Click on a directory to enter that directory. Click on a file to display
-its revision history and to get a chance to display diffs between revisions.
-</p>
-"""
-
-#
-# CONFIGURATION END
-#
#########################################################################
import sys
@@ -257,9 +50,25 @@
import time
import re
import stat
-import ConfigParser
+
+#########################################################################
+#
+# Adjust sys.path to include our library directory
+#
+
+if LIBRARY_DIR:
+ sys.path.insert(0, LIBRARY_DIR)
+else:
+ sys.path[:0] = ['../lib'] # any other places to look?
+
+# time for our imports now
+import compat
+import config
+import popen
+#########################################################################
+
checkout_magic_path = '~checkout~/'
viewcvs_mime_type = 'text/vnd.viewcvs-markup'
@@ -284,12 +93,19 @@
_FILE_HAD_ERROR = 'could not read file'
header_comment = '''\
-<!-- ViewCVS -- http://www.lyra.org/greg/python/viewcvs/
+<!-- ViewCVS -- http://www.lyra.org/viewcvs/
by Greg Stein -- mailto:gstein@lyra.org
-->
'''
+# for reading/writing between a couple descriptors
+CHUNK_SIZE = 8192
+
+# if your rlog doesn't use 77 '=' characters, then this must change
+LOG_END_MARKER = '=' * 77 + '\n'
+ENTRY_END_MARKER = '-' * 28 + '\n'
+
class Request:
def __init__(self):
where = os.environ.get('PATH_INFO', '')
@@ -393,34 +209,6 @@
self.log = log
-#
-# Compatibility stuff for pre-1.5.2 versions of Python
-#
-# Two items: urllib.urlencode and time.strptime
-#
-try:
- my_urlencode = urllib.urlencode
-except AttributeError:
- def my_urlencode(dict):
- if not dict:
- return ''
- quote = urllib.quote_plus
- keyvalue = [ ]
- for key, value in dict.items():
- keyvalue.append(quote(key) + '=' + quote(str(value)))
- return '?' + string.join(keyvalue, '&')
-
-if hasattr(time, 'strptime'):
- def my_strptime(timestr):
- return time.strptime(timestr, '%Y/%m/%d %H:%M:%S')
-else:
- _re_rev_date = re.compile('([0-9]{4})/([0-9][0-9])/([0-9][0-9]) '
- '([0-9][0-9]):([0-9][0-9]):([0-9][0-9])')
- def my_strptime(timestr):
- matches = _re_rev_date.match(timestr).groups()
- return tuple(map(int, matches)) + (0, 1, -1)
-
-
def redirect(location):
print 'Status: 301 Moved'
print 'Location:', location
@@ -462,7 +250,7 @@
def html_footer():
print '<hr noshade><table width="100%" border=0 cellpadding=0 cellspacing=0><tr>'
print '<td align=left><address>%s</address></td>' % cfg.general.address
- print '<td align=right><a href="http://www.lyra.org/greg/python/viewcvs/">ViewCVS %s</a><br>' % __version__
+ print '<td align=right><a href="http://www.lyra.org/viewcvs/">ViewCVS %s</a><br>' % __version__
print 'by <a href="mailto:gstein@lyra.org">Greg Stein</a>'
print '</td></tr></table>'
print '</body></html>'
@@ -473,7 +261,7 @@
value = dict.get(varname)
if value is not None and value != default_settings.get(varname, ''):
sticky_dict[varname] = value
- return my_urlencode(sticky_dict)
+ return compat.urlencode(sticky_dict)
def toggle_query(query_dict, which, value=None):
dict = query_dict.copy()
@@ -571,7 +359,7 @@
print ' target="cvs_checkout"'
if cfg.options.use_java_script:
print " onClick=\"window.open('%s','cvs_checkout'," \
- "'resizeable,scrollbars" % full_url,
+ "'resizeable=1,scrollbars=1" % full_url,
if mime_type == 'text/html':
print ',status,toolbar',
print "');\""
@@ -667,7 +455,7 @@
while 1:
### technically, the htmlify() could fail if something falls across
### the chunk boundary. TFB.
- chunk = fp.read(8192)
+ chunk = fp.read(CHUNK_SIZE)
if not chunk:
break
sys.stdout.write(htmlify(chunk))
@@ -694,8 +482,83 @@
html = re.sub(_re_rewrite_email, r'<a href="mailto:\1">\1</a>', html)
sys.stdout.write(html)
+def markup_stream_enscript(lang, fp):
+ sys.stdout.flush()
+ cmd = "%senscript --color -W html -E%s -o - - 2> /dev/null " \
+ "| sed -n '/^<PRE>$/,/<\\/PRE>$/p'" % \
+ (cfg.options.enscript_path, lang,)
+ enscript = os.popen(cmd, "w")
+ while 1:
+ chunk = fp.read(CHUNK_SIZE)
+ if not chunk:
+ break
+ enscript.write(chunk)
+ enscript.close()
+
markup_streamers = {
- '.py' : markup_stream_python,
+# '.py' : markup_stream_python,
+ }
+
+### this sucks... we have to duplicate the extensions defined by enscript
+enscript_extensions = {
+ '.c' : 'c',
+ '.h' : 'c',
+ '.c++' : 'cpp',
+ '.C' : 'cpp',
+ '.H' : 'cpp',
+ '.cpp' : 'cpp',
+ '.cc' : 'cpp',
+ '.cxx' : 'cpp',
+ '.m' : 'objc',
+ '.scm' : 'scheme',
+ '.scheme' : 'scheme',
+ '.el' : 'elisp',
+ '.ada' : 'ada',
+ '.adb' : 'ada',
+ '.ads' : 'ada',
+ '.s' : 'asm',
+ '.S' : 'asm',
+ '.st' : 'states',
+ '.tcl' : 'tcl',
+ '.v' : 'verilog',
+ '.vh' : 'verilog',
+ '.htm' : 'html',
+ '.html' : 'html',
+ '.shtml' : 'html',
+ '.vhd' : 'vhdl',
+ '.vhdl' : 'vhdl',
+ '.scr' : 'synopsys',
+ '.syn' : 'synopsys',
+ '.synth' : 'synopsys',
+ '.idl' : 'idl',
+ '.hs' : 'haskell',
+ '.lhs' : 'haskell',
+ '.gs' : 'haskell',
+ '.lgs' : 'haskell',
+ '.pm' : 'perl',
+ '.pl' : 'perl',
+ '.eps' : 'postscript',
+ '.EPS' : 'postscript',
+ '.ps' : 'postscript',
+ '.PS' : 'postscript',
+ '.js' : 'javascript',
+ '.java' : 'java',
+ '.pas' : 'pascal',
+ '.pp' : 'pascal',
+ '.p' : 'pascal',
+ '.f' : 'fortran',
+ '.F' : 'fortran',
+ '.awk' : 'awk',
+ '.sh' : 'sh',
+ '.vba' : 'vba',
+
+ ### use enscript or py2html?
+ '.py' : 'python',
+ }
+enscript_filenames = {
+ '.emacs' : 'elisp',
+ 'Makefile' : 'makefile',
+ 'makefile' : 'makefile',
}
def markup_stream(request, fp, revision, mime_type):
@@ -730,14 +593,26 @@
print 'Tag: <b>%s</b><br>' % tag
print '</td></tr></table>'
- url = download_url(request, file_url, revision, mime_type)
print '<hr noshade>'
if mime_type[:6] == 'image/':
+ url = download_url(request, file_url, revision, mime_type)
print '<img src="%s%s"><br>' % (url, request.amp_query)
else:
basename, ext = os.path.splitext(filename)
- streamer = markup_streamers.get(ext, markup_stream_default)
- streamer(fp)
+ streamer = markup_streamers.get(ext)
+ if streamer:
+ streamer(fp)
+ elif not cfg.options.use_enscript:
+ markup_stream_default(fp)
+ else:
+ lang = enscript_extensions.get(ext)
+ if not lang:
+ lang = enscript_filenames.get(basename)
+ if lang and lang not in cfg.options.disable_enscript_lang:
+ markup_stream_enscript(lang, fp)
+ else:
+ markup_stream_default(fp)
+ html_footer()
def get_file_data(full_name):
"""Return a sequence of tuples containing various data about the files.
@@ -836,10 +711,10 @@
elif line[:14] == 'symbolic names':
# start parsing the tag information
parsing_tags = 1
- elif line == '----------------------------\n':
+ elif line == ENTRY_END_MARKER:
# end of the headers
break
- elif line[:10] == '==========':
+ elif line == LOG_END_MARKER:
# end of this file's log information
eof = _EOF_FILE
break
@@ -899,9 +774,9 @@
break
if line[:9] == 'branches:':
continue
- if line == '----------------------------\n':
+ if line == ENTRY_END_MARKER:
break
- if line[:10] == '==========':
+ if line == LOG_END_MARKER:
# end of this file's log information
eof = _EOF_FILE
break
@@ -912,7 +787,7 @@
# there was a parsing error
return None, eof
- date = int(time.mktime(my_strptime(match.group(1)))) - time.timezone
+ date = int(time.mktime(compat.cvs_strptime(match.group(1)))) - time.timezone
return LogEntry(rev, date,
# author, state, lines changed
@@ -925,7 +800,7 @@
line = fp.readline()
if not line:
break
- if line[:10] == '==========':
+ if line == LOG_END_MARKER:
break
def process_rlog_output(rlog, full_name, view_tag, fileinfo, alltags):
@@ -1062,18 +937,18 @@
chunk = files[:chunk_size]
del files[:chunk_size]
- arglist = string.join(chunk, "' '" + full_name + '/')
+ # prepend the full pathname for each file
+ for i in range(len(chunk)):
+ chunk[i] = full_name + '/' + chunk[i]
+
if view_tag:
# NOTE: can't pass tag on command line since a tag may contain "-"
# we'll search the output for the appropriate revision
- rlog = os.popen("%srlog '%s/%s' 2>&1" %
- (cfg.general.rcs_path, full_name, arglist),
- "r")
+ rlog = popen.popen(cfg.general.rcs_path + 'rlog', chunk, 'r')
else:
# fetch the latest revision on the default branch
- rlog = os.popen("%srlog -r '%s/%s' 2>&1" %
- (cfg.general.rcs_path, full_name, arglist),
- "r")
+ chunk = ('-r',) + tuple(chunk)
+ rlog = popen.popen(cfg.general.rcs_path + 'rlog', chunk, 'r')
process_rlog_output(rlog, full_name, view_tag, fileinfo, alltags)
@@ -1086,6 +961,10 @@
###
### more work for later...
+ if rlog.close():
+ ### what to do?
+ pass
+
return fileinfo, alltags.keys()
def revcmp(rev1, rev2):
@@ -1277,7 +1156,7 @@
if isdir:
if not hideattic and file == 'Attic':
continue
- if where == '' and (file == 'CVSROOT' or file in cfg.general.forbidden):
+ if where == '' and (file == 'CVSROOT' or cfg.is_forbidden(file)):
continue
print '<tr bgcolor="%s"><td>' % cfg.colors.even_odd[cur_row % 2]
@@ -1412,12 +1291,10 @@
def fetch_log(full_name, which_rev=None):
if which_rev:
- rev_flag = '-r' + which_rev
+ args = ('-r' + which_rev, full_name)
else:
- rev_flag = ''
- rlog = os.popen("%srlog %s '%s' 2>&1" %
- (cfg.general.rcs_path, rev_flag, full_name),
- "r")
+ args = (full_name,)
+ rlog = popen.popen(cfg.general.rcs_path + 'rlog', args, 'r')
header, eof = parse_log_header(rlog)
filename = header.filename
@@ -1905,18 +1782,14 @@
### validate it?
pass
else:
- mime_type, encoding = mimetypes.guess_type(where)
- if not mime_type:
- mime_type = 'text/plain'
+ mime_type = request.mime_type
if rev:
rev_flag = '-p' + rev
else:
rev_flag = '-p'
- fp = os.popen("%sco '%s' '%s' 2>&1" %
- (cfg.general.rcs_path, rev_flag, full_name),
- 'r')
+ fp = popen.popen(cfg.general.rcs_path + 'co', (rev_flag, full_name), 'r')
# header from co:
@@ -1945,7 +1818,8 @@
(header, filename, where))
if mime_type == viewcvs_mime_type:
- markup_stream(request, fp, revision, mime_type)
+ # use the "real" MIME type
+ markup_stream(request, fp, revision, request.mime_type)
else:
http_header(mime_type)
while 1:
@@ -1955,14 +1829,22 @@
sys.stdout.write(chunk)
def view_annotate(request):
- ### dunno what this is for... check against cvsweb
- some_value = request.query_dict['annotate']
+ rev = request.query_dict['annotate']
- ### testing
- html_header('annotate')
- print "annotate"
+ pathname, filename = os.path.split(request.where)
+ if pathname[-6:] == '/Attic':
+ pathname = pathname[:-6]
+
+ http_header()
+ navigate_header(request, request.url, pathname, filename, rev, 'view')
+ print '<hr noshade>'
+
+ import blame
+ blame.make_html(request.cvsroot, request.where + ',v', rev)
+
html_footer()
+
_re_extract_rev = re.compile(r'^[-+]+ [^\t]+\t([^\t]+)\t((\d+\.)+\d+)$')
_re_extract_info = re.compile(r'@@ \-([0-9]+).*\+([0-9]+).*@@(.*)')
_re_extract_diff = re.compile(r'^([-+ ])(.*)')
@@ -2026,11 +1908,17 @@
(cfg.options.diff_font_face, cfg.options.diff_font_size)
left_row = right_row = 0
+ # this will be set to true if any changes are found
+ changes_seen = 0
+
while 1:
line = fp.readline()
if not line:
break
+ # we've seen some kind of change
+ changes_seen = 1
+
if line[:2] == '@@':
match = _re_extract_info.match(line)
print '<tr bgcolor="%s"><td width="50%">' % cfg.colors.diff_heading
@@ -2046,6 +1934,11 @@
state = 'dump'
left_col = [ ]
right_col = [ ]
+ elif line[0] == '\\':
+ # \ No newline at end of file
+ flush_diff_rows(state, left_col, right_col)
+ left_col = [ ]
+ right_col = [ ]
else:
match = _re_extract_diff.match(line)
line = spaced_html_text(match.group(2))
@@ -2072,10 +1965,11 @@
left_col = [ ]
right_col = [ ]
- flush_diff_rows(state, left_col, right_col)
- if not state:
+ if changes_seen:
+ flush_diff_rows(state, left_col, right_col)
+ else:
print '<tr><td colspan=2> </td></tr>'
- print '<tr bgcolor="%s"><td colspan=2 align=center><b>- No viewable change -</b></td></tr>' % (cfg.colors.diff_empty)
+ print '<tr bgcolor="%s"><td colspan=2 align=center><br><b>- No changes -</b><br> </td></tr>' % (cfg.colors.diff_empty)
print '</table><br><hr noshade width="100%">'
print '<table border=0 cellpadding=10><tr><td>'
@@ -2168,45 +2062,51 @@
rev2 = r2[:idx]
sym2 = r2[idx+1:]
- ### check rev1, rev2 for well-formed-ness (security reasons)
-
if revcmp(rev1, rev2) > 0:
rev1, rev2 = rev2, rev1
sym1, sym2 = sym2, sym1
human_readable = 0
+ unified = 0
+
+ args = [ ]
+
format = query_dict['diff_format']
if format == 'c':
- diff_type = '-c'
+ args.append('-c')
diff_name = 'Context diff'
elif format == 's':
- diff_type = '--side-by-side --width=164'
+ args.append('--side-by-side')
+ args.append('--width=164')
diff_name = 'Side by Side'
elif format == 'H':
- diff_type = '--unified=15'
+ args.append('--unified=15')
diff_name = 'Long Human readable'
human_readable = 1
+ unified = 1
elif format == 'h':
- diff_type = '-u'
+ args.append('-u')
diff_name = 'Human readable'
human_readable = 1
+ unified = 1
elif format == 'u':
- diff_type = '-u'
+ args.append('-u')
diff_name = 'Unidiff'
+ unified = 1
else:
error('Diff format %s not understood' % format, '400 Bad arguments')
if human_readable:
if cfg.options.hr_funout:
- diff_type = diff_type + ' -p'
+ args.append('-p')
if cfg.options.hr_ignore_white:
- diff_type = diff_type + ' -w'
+ args.append('-w')
if cfg.options.hr_ignore_keyword_subst:
- diff_type = diff_type + ' -kk'
+ args.append('-kk')
- fp = os.popen("%srcsdiff %s '-r%s' '-r%s' '%s' 2>&1" %
- (cfg.general.rcs_path, diff_type, rev1, rev2, cvs_filename),
- 'r')
+ args[len(args):] = ['-r' + rev1, '-r' + rev2, cvs_filename]
+ fp = popen.popen(cfg.general.rcs_path + 'rcsdiff', args, 'r')
+
if human_readable:
http_header()
human_readable_diff(request, fp, rev1, rev2, sym1, sym2)
@@ -2214,7 +2114,7 @@
http_header('text/plain')
- if diff_type == '-u':
+ if unified:
f1 = '--- ' + cvsroot
f2 = '+++ ' + cvsroot
else:
@@ -2238,9 +2138,13 @@
print line[:-1]
def handle_config():
+ global cfg
+ cfg = config.Config()
+ cfg.set_defaults()
+
# load in configuration information from the config file
- ### allow changes and paths here...??
- cfg.load_config('viewcvs.conf')
+ pathname = CONF_PATHNAME or 'viewcvs.conf'
+ cfg.load_config(pathname, os.environ.get('HTTP_HOST'))
global default_settings
default_settings = {
@@ -2280,7 +2184,7 @@
redirect(url + '/' + request.qmark_query)
# check the forbidden list
- if request.module in cfg.general.forbidden:
+ if cfg.is_forbidden(request.module):
error('Access to "%s" is forbidden.' % request.module, '403 Forbidden')
if isdir: