mirror of
https://github.com/vitalif/viewvc-4intranet
synced 2019-04-16 04:14:59 +03:00
Compare commits
52 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
f7920e796c | ||
![]() |
02f2243cc7 | ||
![]() |
8766cf00ca | ||
![]() |
ba039d5a8f | ||
![]() |
007ad51b2c | ||
![]() |
c3d12bb0c8 | ||
![]() |
d0de5e7ad0 | ||
![]() |
4a00a0bb4f | ||
![]() |
abb4a468fc | ||
![]() |
cc3807f964 | ||
![]() |
3bff6b7378 | ||
![]() |
fc6d80e2fb | ||
![]() |
425ae2e8ec | ||
![]() |
c2a29c83cd | ||
![]() |
799816c21f | ||
![]() |
e3dbdb4fcc | ||
![]() |
c28afba169 | ||
![]() |
d790f07d2c | ||
![]() |
edf252fb2f | ||
![]() |
6942dd6b4b | ||
![]() |
a75f32fc3c | ||
![]() |
e243a0e1ad | ||
![]() |
755e4e74c3 | ||
![]() |
a46b9dd3ba | ||
![]() |
ab987dc6fa | ||
![]() |
a6ac1d1892 | ||
![]() |
945cb91e38 | ||
![]() |
262f3569c7 | ||
![]() |
1b358fc88d | ||
![]() |
455faa1fe6 | ||
![]() |
01facd113b | ||
![]() |
67daa9e5e2 | ||
![]() |
03a5620947 | ||
![]() |
424521d40e | ||
![]() |
1eeb39f264 | ||
![]() |
7aa5c33589 | ||
![]() |
60236803e4 | ||
![]() |
a26cfff0ad | ||
![]() |
f0c34a715d | ||
![]() |
4cb1282b70 | ||
![]() |
f34424ca20 | ||
![]() |
bc6851ea6f | ||
![]() |
3b10a1763f | ||
![]() |
329d20633a | ||
![]() |
5251065e6d | ||
![]() |
3d6f71ce00 | ||
![]() |
1a69430064 | ||
![]() |
050ad1f8fe | ||
![]() |
3965ce9221 | ||
![]() |
12003f27b5 | ||
![]() |
b0b1b5603e | ||
![]() |
2b8cc9d34c |
2
INSTALL
2
INSTALL
@@ -174,7 +174,7 @@ If you've trouble to make viewcvs.cgi work:
|
||||
|
||||
o check the ViewCVS home page:
|
||||
|
||||
http://www.lyra.org/viewcvs/
|
||||
http://viewcvs.sourceforge.net/
|
||||
|
||||
o review the ViewCVS mailing list archive to see if somebody else had
|
||||
the same problem, and it was solved:
|
||||
|
7
TODO
7
TODO
@@ -27,12 +27,13 @@ TODO ITEMS
|
||||
|
||||
*) add a page that describes how to reach anonymous CVS for ViewCVS
|
||||
|
||||
*) have a "check" mode that verifies binaries are available on rcs_path
|
||||
|
||||
-> alternately (probably?): use rcsparse rather than external tools
|
||||
|
||||
|
||||
KNOWN BUGS
|
||||
----------
|
||||
*) from Harri Pasanen, Feb 7: when hideattic is set, files added in a
|
||||
branch are not visible.
|
||||
|
||||
*) from Dieter Deyke, Jan 12: if the CVS revisions differ by just a
|
||||
keyword, then the diff output chokes.
|
||||
|
||||
|
@@ -1,22 +1,22 @@
|
||||
#!/usr/bin/python
|
||||
# -*- Mode: python -*-
|
||||
#
|
||||
# Copyright (C) 2000 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 2000-2001 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
# distribution or at http://viewcvs.sourceforge.net/license-1.html.
|
||||
#
|
||||
# Contact information:
|
||||
# Greg Stein, PO Box 760, Palo Alto, CA, 94302
|
||||
# gstein@lyra.org, http://www.lyra.org/viewcvs/
|
||||
# gstein@lyra.org, http://viewcvs.sourceforge.net/
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
# CGI script to process and display queries to CVSdb
|
||||
#
|
||||
# This script is part of the ViewCVS package. More information can be
|
||||
# found at http://www.lyra.org/viewcvs/.
|
||||
# found at http://viewcvs.sourceforge.net/.
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
|
@@ -1,15 +1,15 @@
|
||||
#!/usr/bin/python
|
||||
# -*- Mode: python -*-
|
||||
#
|
||||
# Copyright (C) 2000 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 2000-2001 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
# distribution or at http://viewcvs.sourceforge.net/license-1.html.
|
||||
#
|
||||
# Contact information:
|
||||
# Greg Stein, PO Box 760, Palo Alto, CA, 94302
|
||||
# gstein@lyra.org, http://www.lyra.org/viewcvs/
|
||||
# gstein@lyra.org, http://viewcvs.sourceforge.net/
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
|
2231
cgi/viewcvs.cgi
2231
cgi/viewcvs.cgi
File diff suppressed because it is too large
Load Diff
@@ -3,7 +3,7 @@
|
||||
# Configuration file for ViewCVS
|
||||
#
|
||||
# Information on ViewCVS is located at the following web site:
|
||||
# http://www.lyra.org/viewcvs/
|
||||
# http://viewcvs.sourceforge.net/
|
||||
#
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
@@ -240,7 +240,7 @@ long_intro =
|
||||
</p>
|
||||
<p>
|
||||
This script
|
||||
(<a href="http://www.lyra.org/viewcvs/">ViewCVS</a>)
|
||||
(<a href="http://viewcvs.sourceforge.net/">ViewCVS</a>)
|
||||
has been written by Greg Stein
|
||||
<<a href="mailto:gstein@lyra.org">gstein@lyra.org</a>>
|
||||
based on the
|
||||
@@ -252,7 +252,7 @@ long_intro =
|
||||
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/viewcvs/">ViewCVS distribution
|
||||
<a href="http://viewcvs.sourceforge.net/">ViewCVS distribution
|
||||
site</a>.
|
||||
Please send any suggestions, comments, etc. to
|
||||
<a href="mailto:gstein@lyra.org">Greg Stein</a>.
|
||||
@@ -262,9 +262,9 @@ doc_info =
|
||||
<h3>CVS Documentation</h3>
|
||||
<blockquote>
|
||||
<p>
|
||||
<a href="http://cvsbook.red-bean.com/">Karl Fogel's CVS book</a><br>
|
||||
<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>
|
||||
@@ -434,6 +434,12 @@ enscript_path =
|
||||
disable_enscript_lang =
|
||||
# disable_enscript_lang = perl, cpp
|
||||
|
||||
#
|
||||
# ViewCVS can generate tarball from a repository on the fly.
|
||||
#
|
||||
allow_tar = 0
|
||||
# allow_tar = 1
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
[vhosts]
|
||||
### DOC
|
||||
|
339
lib/blame.py
339
lib/blame.py
@@ -1,16 +1,16 @@
|
||||
#!/usr/local/bin/python
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 2000 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 2000-2001 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 2000 Curt Hagenlocher <curt@hagenlocher.org>
|
||||
#
|
||||
# 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.
|
||||
# distribution or at http://viewcvs.sourceforge.net/license-1.html.
|
||||
#
|
||||
# Contact information:
|
||||
# Greg Stein, PO Box 760, Palo Alto, CA, 94302
|
||||
# gstein@lyra.org, http://www.lyra.org/viewcvs/
|
||||
# gstein@lyra.org, http://viewcvs.sourceforge.net/
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
@@ -21,7 +21,7 @@
|
||||
#
|
||||
# This software is being maintained as part of the ViewCVS project.
|
||||
# Information is available at:
|
||||
# http://www.lyra.org/viewcvs/
|
||||
# http://viewcvs.sourceforge.net/
|
||||
#
|
||||
# This file is based on the cvsblame.pl portion of the Bonsai CVS tool,
|
||||
# developed by Steve Lamm for Netscape Communications Corporation. More
|
||||
@@ -40,17 +40,10 @@ import re
|
||||
import time
|
||||
import math
|
||||
import cgi
|
||||
import rcsparse
|
||||
|
||||
path_sep = os.path.normpath('/')[-1]
|
||||
|
||||
class CVSParser:
|
||||
class CVSParser(rcsparse.Sink):
|
||||
# Precompiled regular expressions
|
||||
nonws_token = re.compile('^([^;@][^;\\s]*)\\s*')
|
||||
semic_token = re.compile('^;\\s*')
|
||||
rcsen_token = re.compile('^@([^@]*)')
|
||||
undo_escape = re.compile('@@')
|
||||
single_at = re.compile('([^@]|^)@([^@]|$)')
|
||||
rcs_tree = re.compile('^\\d')
|
||||
trunk_rev = re.compile('^[0-9]+\\.[0-9]+$')
|
||||
last_branch = re.compile('(.*)\\.[0-9]+')
|
||||
is_branch = re.compile('(.*)\\.0\\.([0-9]+)')
|
||||
@@ -63,9 +56,6 @@ class CVSParser:
|
||||
self.Reset()
|
||||
|
||||
def Reset(self):
|
||||
self.line_buffer = ''
|
||||
self.rcsfile = None
|
||||
self.debug = 0
|
||||
self.last_revision = {}
|
||||
self.prev_revision = {}
|
||||
self.revision_date = {}
|
||||
@@ -73,7 +63,6 @@ class CVSParser:
|
||||
self.revision_branches = {}
|
||||
self.next_delta = {}
|
||||
self.prev_delta = {}
|
||||
self.feof = 0
|
||||
self.tag_revision = {}
|
||||
self.revision_symbolic_name = {}
|
||||
self.timestamp = {}
|
||||
@@ -85,69 +74,6 @@ class CVSParser:
|
||||
self.lines_added = {}
|
||||
self.lines_removed = {}
|
||||
|
||||
# Get the next token from the RCS file
|
||||
def get_token(self):
|
||||
# Erase all-whitespace lines
|
||||
while len(self.line_buffer) == 0:
|
||||
self.line_buffer = self.rcsfile.readline()
|
||||
if self.line_buffer == '':
|
||||
raise RuntimeError, 'EOF'
|
||||
self.line_buffer = string.lstrip(self.line_buffer)
|
||||
|
||||
# A string of non-whitespace characters is a token
|
||||
match = self.nonws_token.match(self.line_buffer)
|
||||
if match:
|
||||
self.line_buffer = self.nonws_token.sub('', self.line_buffer)
|
||||
return match.group(1)
|
||||
|
||||
# ...and so is a single semicolon
|
||||
if self.semic_token.match(self.line_buffer):
|
||||
self.line_buffer = self.semic_token.sub('', self.line_buffer)
|
||||
return ';'
|
||||
|
||||
# ...or an RCS-encoded string that starts with an @ character
|
||||
match = self.rcsen_token.match(self.line_buffer)
|
||||
self.line_buffer = self.rcsen_token.sub('', self.line_buffer)
|
||||
token = match.group(1)
|
||||
|
||||
# Detect single @ character used to close RCS-encoded string
|
||||
while string.find(self.line_buffer, '@') < 0 or not self.single_at.search(self.line_buffer):
|
||||
token = token + self.line_buffer
|
||||
self.line_buffer = self.rcsfile.readline()
|
||||
if self.line_buffer == '':
|
||||
raise RuntimeError, 'EOF'
|
||||
|
||||
# Retain the remainder of the line after the terminating @ character
|
||||
i = string.rindex(self.line_buffer, '@')
|
||||
token = token + self.line_buffer[:i]
|
||||
self.line_buffer = self.line_buffer[i+1:]
|
||||
|
||||
# Undo escape-coding of @ characters.
|
||||
token = self.undo_escape.sub('@', token)
|
||||
|
||||
# Digest any extra blank lines
|
||||
while len(self.line_buffer) == 0 or self.line_buffer == '\n':
|
||||
self.line_buffer = self.rcsfile.readline()
|
||||
if self.line_buffer == '':
|
||||
self.feof = 1
|
||||
break
|
||||
|
||||
if token[-1:] == '\n':
|
||||
token = token[:-1]
|
||||
|
||||
return token
|
||||
|
||||
# Try to match the next token from the input buffer
|
||||
def match_token(self, match):
|
||||
token = self.get_token()
|
||||
if token != match:
|
||||
raise RuntimeError, ('Unexpected parsing error in RCS file.\n' +
|
||||
'Expected token: %s, but saw: %s' % (match, token))
|
||||
|
||||
# Push RCS token back into the input buffer.
|
||||
def unget_token(self, token):
|
||||
self.line_buffer = token + " " + self.line_buffer
|
||||
|
||||
# Map a tag to a numerical revision number. The tag can be a symbolic
|
||||
# branch tag, a symbolic revision tag, or an ordinary numerical
|
||||
# revision number.
|
||||
@@ -182,6 +108,13 @@ class CVSParser:
|
||||
|
||||
return ancestors
|
||||
|
||||
# Split deltatext specified by rev to each line.
|
||||
def deltatext_split(self, rev):
|
||||
lines = string.split(self.revision_deltatext[rev], '\n')
|
||||
if lines[-1] == '':
|
||||
del lines[-1]
|
||||
return lines
|
||||
|
||||
# Extract the given revision from the digested RCS file.
|
||||
# (Essentially the equivalent of cvs up -rXXX)
|
||||
def extract_revision(self, revision):
|
||||
@@ -195,12 +128,12 @@ class CVSParser:
|
||||
path.reverse()
|
||||
path = path[1:] # Get rid of head revision
|
||||
|
||||
text = string.split(self.revision_deltatext[self.head_revision], '\n')
|
||||
text = self.deltatext_split(self.head_revision)
|
||||
|
||||
# Iterate, applying deltas to previous revision
|
||||
for revision in path:
|
||||
adjust = 0
|
||||
diffs = string.split(self.revision_deltatext[revision], '\n')
|
||||
diffs = self.deltatext_split(revision)
|
||||
self.lines_added[revision] = 0
|
||||
self.lines_removed[revision] = 0
|
||||
lines_added_now = 0
|
||||
@@ -235,49 +168,25 @@ class CVSParser:
|
||||
self.lines_removed[revision] = self.lines_removed[revision] + lines_removed_now
|
||||
return text
|
||||
|
||||
def parse_rcs_admin(self):
|
||||
while 1:
|
||||
# Read initial token at beginning of line
|
||||
token = self.get_token()
|
||||
def set_head_revision(self, revision):
|
||||
self.head_revision = revision
|
||||
|
||||
# We're done once we reach the description of the RCS tree
|
||||
if self.rcs_tree.match(token):
|
||||
self.unget_token(token)
|
||||
return
|
||||
def set_principal_branch(self, branch_name):
|
||||
self.principal_branch = branch_name
|
||||
|
||||
# print "token:", token
|
||||
def define_tag(self, name, revision):
|
||||
# Create an associate array that maps from tag name to
|
||||
# revision number and vice-versa.
|
||||
self.tag_revision[name] = revision
|
||||
|
||||
if token == "head":
|
||||
self.head_revision = self.get_token()
|
||||
self.get_token() # Eat semicolon
|
||||
elif token == "branch":
|
||||
self.principal_branch = self.get_token()
|
||||
self.get_token() # Eat semicolon
|
||||
elif token == "symbols":
|
||||
# Create an associate array that maps from tag name to
|
||||
# revision number and vice-versa.
|
||||
while 1:
|
||||
tag = self.get_token()
|
||||
if tag == ';':
|
||||
break
|
||||
(tag_name, tag_rev) = string.split(tag, ':')
|
||||
self.tag_revision[tag_name] = tag_rev
|
||||
self.revision_symbolic_name[tag_rev] = tag_name
|
||||
elif token == "comment":
|
||||
self.file_description = self.get_token()
|
||||
self.get_token() # Eat semicolon
|
||||
### actually, this is a bit bogus... a rev can have multiple names
|
||||
self.revision_symbolic_name[revision] = name
|
||||
|
||||
# Ignore all these other fields - We don't care about them.
|
||||
elif token in ("locks", "strict", "expand", "access"):
|
||||
while 1:
|
||||
tag = self.get_token()
|
||||
if tag == ';':
|
||||
break
|
||||
else:
|
||||
pass
|
||||
# warn("Unexpected RCS token: $token\n")
|
||||
def set_comment(self, comment):
|
||||
self.file_description = comment
|
||||
|
||||
raise RuntimeError, "Unexpected EOF";
|
||||
def set_description(self, description):
|
||||
self.rcs_file_description = description
|
||||
|
||||
# Construct dicts that represent the topology of the RCS tree
|
||||
# and other arrays that contain info about individual revisions.
|
||||
@@ -301,91 +210,47 @@ class CVSParser:
|
||||
# Also creates self.last_revision, keyed by a branch revision number, which
|
||||
# indicates the latest revision on a given branch,
|
||||
# e.g. self.last_revision{"1.2.8"} == 1.2.8.5
|
||||
def define_revision(self, revision, timestamp, author, state,
|
||||
branches, next):
|
||||
self.tag_revision[revision] = revision
|
||||
branch = self.last_branch.match(revision).group(1)
|
||||
self.last_revision[branch] = revision
|
||||
|
||||
def parse_rcs_tree(self):
|
||||
while 1:
|
||||
revision = self.get_token()
|
||||
#self.revision_date[revision] = date
|
||||
self.timestamp[revision] = timestamp
|
||||
|
||||
# End of RCS tree description ?
|
||||
if revision == 'desc':
|
||||
self.unget_token(revision)
|
||||
return
|
||||
# Pretty print the date string
|
||||
ltime = time.localtime(self.timestamp[revision])
|
||||
formatted_date = time.strftime("%d %b %Y %H:%M", ltime)
|
||||
self.revision_ctime[revision] = formatted_date
|
||||
|
||||
# Save age
|
||||
self.revision_age[revision] = ((time.time() - self.timestamp[revision])
|
||||
/ self.SECONDS_PER_DAY)
|
||||
|
||||
# save author
|
||||
self.revision_author[revision] = author
|
||||
|
||||
# ignore the state
|
||||
|
||||
# process the branch information
|
||||
branch_text = ''
|
||||
for branch in branches:
|
||||
self.prev_revision[branch] = revision
|
||||
self.next_delta[revision] = branch
|
||||
self.prev_delta[branch] = revision
|
||||
branch_text = branch_text + branch + ''
|
||||
self.revision_branches[revision] = branch_text
|
||||
|
||||
# process the "next revision" information
|
||||
if next:
|
||||
self.next_delta[revision] = next
|
||||
self.prev_delta[next] = revision
|
||||
is_trunk_revision = self.trunk_rev.match(revision) is not None
|
||||
|
||||
self.tag_revision[revision] = revision
|
||||
branch = self.last_branch.match(revision).group(1)
|
||||
self.last_revision[branch] = revision
|
||||
|
||||
# Parse date
|
||||
self.match_token('date')
|
||||
date = self.get_token()
|
||||
self.revision_date[revision] = date
|
||||
self.match_token(';')
|
||||
|
||||
# Convert date into timestamp
|
||||
date_fields = string.split(date, '.') + ['0', '0', '0']
|
||||
date_fields = map(string.atoi, date_fields)
|
||||
if date_fields[0] < 100:
|
||||
date_fields[0] = date_fields[0] + 1900
|
||||
self.timestamp[revision] = time.mktime(date_fields)
|
||||
|
||||
# Pretty print the date string
|
||||
ltime = time.localtime(self.timestamp[revision])
|
||||
formatted_date = time.strftime("%d %b %Y %H:%M", ltime)
|
||||
self.revision_ctime[revision] = formatted_date
|
||||
|
||||
# Save age
|
||||
self.revision_age[revision] = (
|
||||
(time.time() - self.timestamp[revision]) / self.SECONDS_PER_DAY)
|
||||
|
||||
# Parse author
|
||||
self.match_token('author')
|
||||
author = self.get_token()
|
||||
self.revision_author[revision] = author
|
||||
self.match_token(';')
|
||||
|
||||
# Parse state
|
||||
self.match_token('state')
|
||||
while self.get_token() != ';':
|
||||
pass
|
||||
|
||||
# Parse branches
|
||||
self.match_token('branches')
|
||||
branches = ''
|
||||
while 1:
|
||||
token = self.get_token()
|
||||
if token == ';':
|
||||
break
|
||||
self.prev_revision[token] = revision
|
||||
self.prev_delta[token] = revision
|
||||
branches = branches + token + ' '
|
||||
self.revision_branches[revision] = branches
|
||||
|
||||
# Parse revision of next delta in chain
|
||||
self.match_token('next')
|
||||
next = ''
|
||||
token = self.get_token()
|
||||
if token != ';':
|
||||
next = token
|
||||
self.get_token() # Eat semicolon
|
||||
self.next_delta[revision] = next
|
||||
self.prev_delta[next] = revision
|
||||
if is_trunk_revision:
|
||||
self.prev_revision[revision] = next
|
||||
else:
|
||||
self.prev_revision[next] = revision
|
||||
|
||||
if self.debug >= 3:
|
||||
print "<pre>revision =", revision
|
||||
print "date = ", date
|
||||
print "author = ", author
|
||||
print "branches = ", branches
|
||||
print "next = ", next + "</pre>\n"
|
||||
|
||||
def parse_rcs_description(self):
|
||||
self.match_token('desc')
|
||||
self.rcs_file_description = self.get_token()
|
||||
if is_trunk_revision:
|
||||
self.prev_revision[revision] = next
|
||||
else:
|
||||
self.prev_revision[next] = revision
|
||||
|
||||
# Construct associative arrays containing info about individual revisions.
|
||||
#
|
||||
@@ -398,36 +263,9 @@ class CVSParser:
|
||||
# revision if this revision is on the trunk or
|
||||
# relative to its immediate predecessor if this
|
||||
# revision is on a branch.
|
||||
def parse_rcs_deltatext(self):
|
||||
while not self.feof:
|
||||
revision = self.get_token()
|
||||
if self.debug >= 3:
|
||||
print "Reading delta for revision:", revision
|
||||
self.match_token('log')
|
||||
self.revision_log[revision] = self.get_token()
|
||||
self.match_token('text')
|
||||
self.revision_deltatext[revision] = self.get_token()
|
||||
|
||||
def parse_rcs_file(self):
|
||||
if self.debug >= 2:
|
||||
print "Reading RCS admin..."
|
||||
self.parse_rcs_admin()
|
||||
if self.debug >= 2:
|
||||
print "Reading RCS revision tree topology..."
|
||||
self.parse_rcs_tree()
|
||||
|
||||
if self.debug >= 3:
|
||||
print "<pre>Keys:\n"
|
||||
for i in self.tag_revision.keys():
|
||||
print "yoyuo %s: %s" % (i, self.tag_revision[i])
|
||||
print "</pre>"
|
||||
|
||||
self.parse_rcs_description()
|
||||
if self.debug >= 2:
|
||||
print "Reading RCS revision deltas..."
|
||||
self.parse_rcs_deltatext()
|
||||
if self.debug >= 2:
|
||||
print "Done reading RCS file..."
|
||||
def set_revision_info(self, revision, log, text):
|
||||
self.revision_log[revision] = log
|
||||
self.revision_deltatext[revision] = text
|
||||
|
||||
def parse_cvs_file(self, rcs_pathname, opt_rev = None, opt_m_timestamp = None):
|
||||
# Args in: opt_rev - requested revision
|
||||
@@ -438,13 +276,13 @@ class CVSParser:
|
||||
|
||||
# CheckHidden(rcs_pathname);
|
||||
try:
|
||||
self.rcsfile = open(rcs_pathname, 'r')
|
||||
rcsfile = open(rcs_pathname, 'r')
|
||||
except:
|
||||
raise RuntimeError, ('error: %s appeared to be under CVS control, ' +
|
||||
'but the RCS file is inaccessible.') % rcs_pathname
|
||||
|
||||
self.parse_rcs_file()
|
||||
self.rcsfile.close()
|
||||
rcsparse.Parser().parse(rcsfile, self)
|
||||
rcsfile.close()
|
||||
|
||||
if opt_rev in [None, '', 'HEAD']:
|
||||
# Explicitly specified topmost revision in tree
|
||||
@@ -470,14 +308,14 @@ class CVSParser:
|
||||
# first revision.
|
||||
line_count = 0
|
||||
if self.revision_deltatext.get(self.head_revision):
|
||||
tmp_array = string.split(self.revision_deltatext[self.head_revision], '\n')
|
||||
tmp_array = self.deltatext_split(self.head_revision)
|
||||
line_count = len(tmp_array)
|
||||
|
||||
skip = 0
|
||||
|
||||
rev = self.prev_revision.get(self.head_revision)
|
||||
while rev:
|
||||
diffs = string.split(self.revision_deltatext[rev], '\n')
|
||||
diffs = self.deltatext_split(rev)
|
||||
for command in diffs:
|
||||
dmatch = self.d_command.match(command)
|
||||
amatch = self.a_command.match(command)
|
||||
@@ -522,7 +360,7 @@ class CVSParser:
|
||||
is_trunk_revision = self.trunk_rev.match(revision) is not None
|
||||
|
||||
if is_trunk_revision:
|
||||
diffs = string.split(self.revision_deltatext[last_revision], '\n')
|
||||
diffs = self.deltatext_split(last_revision)
|
||||
|
||||
# Revisions on the trunk specify deltas that transform a
|
||||
# revision into an earlier revision, so invert the translation
|
||||
@@ -555,7 +393,7 @@ class CVSParser:
|
||||
# the trunk. They specify deltas that transform a revision
|
||||
# into a later revision.
|
||||
adjust = 0
|
||||
diffs = string.split(self.revision_deltatext[revision], '\n')
|
||||
diffs = self.deltatext_split(revision)
|
||||
for command in diffs:
|
||||
if skip > 0:
|
||||
skip = skip - 1
|
||||
@@ -589,21 +427,24 @@ class CVSParser:
|
||||
re_includes = re.compile('\\#(\\s*)include(\\s*)"(.*?)"')
|
||||
re_filename = re.compile('(.*[\\\\/])?(.+)')
|
||||
|
||||
def link_includes(text, root, rcs_path):
|
||||
def link_includes(text, root, rcs_path, sticky = None):
|
||||
match = re_includes.match(text)
|
||||
if match:
|
||||
incfile = match.group(3)
|
||||
for rel_path in ('', 'Attic', '..'):
|
||||
trial_root = os.path.join(rcs_path, rel_path)
|
||||
file = os.path.normpath('%s%s%s%s%s,v' % (root, path_sep, trial_root, path_sep, incfile))
|
||||
file = os.path.join(root, trial_root)
|
||||
file = os.path.normpath(os.path.join(file, incfile + ',v'))
|
||||
if os.access(file, os.F_OK):
|
||||
url = os.path.join(rel_path, incfile)
|
||||
if sticky:
|
||||
url = url + '?' + sticky
|
||||
return '#%sinclude%s"<a href="%s">%s</a>"' % \
|
||||
(match.group(1), match.group(2),
|
||||
os.path.join(rel_path, incfile), incfile)
|
||||
(match.group(1), match.group(2), url, incfile)
|
||||
return text
|
||||
|
||||
def make_html(root, rcs_path, opt_rev = None):
|
||||
filename = root + path_sep + rcs_path
|
||||
def make_html(root, rcs_path, opt_rev = None, sticky = None):
|
||||
filename = os.path.join(root, rcs_path)
|
||||
parser = CVSParser()
|
||||
revision = parser.parse_cvs_file(filename, opt_rev)
|
||||
count = len(parser.revision_map)
|
||||
@@ -649,7 +490,7 @@ def make_html(root, rcs_path, opt_rev = None):
|
||||
|
||||
# Add a link to traverse to included files
|
||||
if 1: # opt_includes
|
||||
thisline = link_includes(thisline, root, file_head)
|
||||
thisline = link_includes(thisline, root, file_head, sticky)
|
||||
|
||||
output = ''
|
||||
|
||||
@@ -685,9 +526,11 @@ def make_html(root, rcs_path, opt_rev = None):
|
||||
|
||||
if parser.prev_revision.get(revision):
|
||||
fname = file_tail[:-2] # strip the ",v"
|
||||
### need the sticky options! need cvsroot if not-default
|
||||
output = output + ' <a href="%s.diff?r1=%s&r2=%s"' % \
|
||||
(fname, parser.prev_revision[revision], revision)
|
||||
url = '%s.diff?r1=%s&r2=%s' % \
|
||||
(fname, parser.prev_revision[revision], revision)
|
||||
if sticky:
|
||||
url = url + '&' + sticky
|
||||
output = output + ' <a href="%s"' % (url, )
|
||||
if 0: # use_layers
|
||||
output = output + " onmouseover='return log(event,\"%s\",\"%s\");'" % (
|
||||
parser.prev_revision[revision], revision)
|
||||
|
@@ -1,14 +1,14 @@
|
||||
# -*- Mode: python -*-
|
||||
#
|
||||
# Copyright (C) 2000 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 2000-2001 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
# distribution or at http://viewcvs.sourceforge.net/license-1.html.
|
||||
#
|
||||
# Contact information:
|
||||
# Greg Stein, PO Box 760, Palo Alto, CA, 94302
|
||||
# gstein@lyra.org, http://www.lyra.org/viewcvs/
|
||||
# gstein@lyra.org, http://viewcvs.sourceforge.net/
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
|
@@ -1,14 +1,14 @@
|
||||
# -*- Mode: python -*-
|
||||
#
|
||||
# Copyright (C) 2000 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 2000-2001 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
# distribution or at http://viewcvs.sourceforge.net/license-1.html.
|
||||
#
|
||||
# Contact information:
|
||||
# Greg Stein, PO Box 760, Palo Alto, CA, 94302
|
||||
# gstein@lyra.org, http://www.lyra.org/viewcvs/
|
||||
# gstein@lyra.org, http://viewcvs.sourceforge.net/
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
@@ -38,7 +38,7 @@ except AttributeError:
|
||||
keyvalue = [ ]
|
||||
for key, value in dict.items():
|
||||
keyvalue.append(quote(key) + '=' + quote(str(value)))
|
||||
return '?' + string.join(keyvalue, '&')
|
||||
return string.join(keyvalue, '&')
|
||||
|
||||
#
|
||||
# time.strptime() is new to Python 1.5.2
|
||||
|
@@ -1,14 +1,14 @@
|
||||
# -*- Mode: python -*-
|
||||
#
|
||||
# Copyright (C) 2000 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 2000-2001 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
# distribution or at http://viewcvs.sourceforge.net/license-1.html.
|
||||
#
|
||||
# Contact information:
|
||||
# Greg Stein, PO Box 760, Palo Alto, CA, 94302
|
||||
# gstein@lyra.org, http://www.lyra.org/viewcvs/
|
||||
# gstein@lyra.org, http://viewcvs.sourceforge.net/
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
@@ -189,6 +189,7 @@ class Config:
|
||||
self.options.use_enscript = 0
|
||||
self.options.enscript_path = ''
|
||||
self.options.disable_enscript_lang = ()
|
||||
self.options.allow_tar = 0
|
||||
|
||||
self.text.long_intro = """\
|
||||
<p>
|
||||
@@ -205,7 +206,7 @@ class Config:
|
||||
</p>
|
||||
<p>
|
||||
This script
|
||||
(<a href="http://www.lyra.org/viewcvs/">ViewCVS</a>)
|
||||
(<a href="http://viewcvs.sourceforge.net/">ViewCVS</a>)
|
||||
has been written by Greg Stein
|
||||
<<a href="mailto:gstein@lyra.org">gstein@lyra.org</a>>
|
||||
based on the
|
||||
@@ -216,7 +217,7 @@ class Config:
|
||||
<a href="http://www.opensource.org/licenses/bsd-license.html">BSD-License</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/viewcvs/">ViewCVS distribution
|
||||
<a href="http://viewcvs.sourceforge.net/">ViewCVS distribution
|
||||
site</a>.
|
||||
Please send any suggestions, comments, etc. to
|
||||
<a href="mailto:gstein@lyra.org">Greg Stein</a>.
|
||||
@@ -228,9 +229,9 @@ class Config:
|
||||
<h3>CVS Documentation</h3>
|
||||
<blockquote>
|
||||
<p>
|
||||
<a href="http://cvsbook.red-bean.com/">Karl Fogel's CVS book</a><br>
|
||||
<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>
|
||||
|
@@ -1,14 +1,14 @@
|
||||
# -*- Mode: python -*-
|
||||
#
|
||||
# Copyright (C) 2000 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 2000-2001 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
# distribution or at http://viewcvs.sourceforge.net/license-1.html.
|
||||
#
|
||||
# Contact information:
|
||||
# Greg Stein, PO Box 760, Palo Alto, CA, 94302
|
||||
# gstein@lyra.org, http://www.lyra.org/viewcvs/
|
||||
# gstein@lyra.org, http://viewcvs.sourceforge.net/
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
|
@@ -1,14 +1,14 @@
|
||||
# -*- Mode: python -*-
|
||||
#
|
||||
# Copyright (C) 2000 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 2000-2001 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
# distribution or at http://viewcvs.sourceforge.net/license-1.html.
|
||||
#
|
||||
# Contact information:
|
||||
# Greg Stein, PO Box 760, Palo Alto, CA, 94302
|
||||
# gstein@lyra.org, http://www.lyra.org/viewcvs/
|
||||
# gstein@lyra.org, http://viewcvs.sourceforge.net/
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
|
@@ -1,14 +1,14 @@
|
||||
# -*- Mode: python -*-
|
||||
#
|
||||
# Copyright (C) 2000 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 2000-2001 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
# distribution or at http://viewcvs.sourceforge.net/license-1.html.
|
||||
#
|
||||
# Contact information:
|
||||
# Greg Stein, PO Box 760, Palo Alto, CA, 94302
|
||||
# gstein@lyra.org, http://www.lyra.org/viewcvs/
|
||||
# gstein@lyra.org, http://viewcvs.sourceforge.net/
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
|
@@ -1,13 +1,13 @@
|
||||
#
|
||||
# Copyright (C) 2000 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 2000-2001 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
# distribution or at http://viewcvs.sourceforge.net/license-1.html.
|
||||
#
|
||||
# Contact information:
|
||||
# Greg Stein, PO Box 760, Palo Alto, CA, 94302
|
||||
# gstein@lyra.org, http://www.lyra.org/viewcvs/
|
||||
# gstein@lyra.org, http://viewcvs.sourceforge.net/
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
|
@@ -1,14 +1,14 @@
|
||||
# -*- Mode: python -*-
|
||||
#
|
||||
# Copyright (C) 2000 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 2000-2001 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
# distribution or at http://viewcvs.sourceforge.net/license-1.html.
|
||||
#
|
||||
# Contact information:
|
||||
# Greg Stein, PO Box 760, Palo Alto, CA, 94302
|
||||
# gstein@lyra.org, http://www.lyra.org/viewcvs/
|
||||
# gstein@lyra.org, http://viewcvs.sourceforge.net/
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
|
441
lib/rcsparse.py
Normal file
441
lib/rcsparse.py
Normal file
@@ -0,0 +1,441 @@
|
||||
#
|
||||
# Copyright (C) 2000-2001 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# 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://viewcvs.sourceforge.net/license-1.html.
|
||||
#
|
||||
# Contact information:
|
||||
# Greg Stein, PO Box 760, Palo Alto, CA, 94302
|
||||
# gstein@lyra.org, http://viewcvs.sourceforge.net/
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
# This software is being maintained as part of the ViewCVS project.
|
||||
# Information is available at:
|
||||
# http://viewcvs.sourceforge.net/
|
||||
#
|
||||
# This file was originally based on portions of the blame.py script by
|
||||
# Curt Hagenlocher.
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
import string
|
||||
import time
|
||||
|
||||
|
||||
class _TokenStream:
|
||||
token_term = string.whitespace + ';'
|
||||
|
||||
# the algorithm is about the same speed for any CHUNK_SIZE chosen.
|
||||
# grab a good-sized chunk, but not too large to overwhelm memory.
|
||||
CHUNK_SIZE = 100000
|
||||
|
||||
# CHUNK_SIZE = 5 # for debugging, make the function grind...
|
||||
|
||||
def __init__(self, file):
|
||||
self.rcsfile = file
|
||||
self.idx = 0
|
||||
self.buf = self.rcsfile.read(self.CHUNK_SIZE)
|
||||
if self.buf == '':
|
||||
raise RuntimeError, 'EOF'
|
||||
|
||||
def get(self):
|
||||
"Get the next token from the RCS file."
|
||||
|
||||
# Note: we can afford to loop within Python, examining individual
|
||||
# characters. For the whitespace and tokens, the number of iterations
|
||||
# is typically quite small. Thus, a simple iterative loop will beat
|
||||
# out more complex solutions.
|
||||
|
||||
buf = self.buf
|
||||
idx = self.idx
|
||||
|
||||
while 1:
|
||||
if idx == len(buf):
|
||||
buf = self.rcsfile.read(self.CHUNK_SIZE)
|
||||
if buf == '':
|
||||
# signal EOF by returning None as the token
|
||||
del self.buf # so we fail if get() is called again
|
||||
return None
|
||||
idx = 0
|
||||
|
||||
if buf[idx] not in string.whitespace:
|
||||
break
|
||||
|
||||
idx = idx + 1
|
||||
|
||||
if buf[idx] == ';':
|
||||
self.buf = buf
|
||||
self.idx = idx + 1
|
||||
return ';'
|
||||
|
||||
if buf[idx] != '@':
|
||||
end = idx + 1
|
||||
token = ''
|
||||
while 1:
|
||||
# find token characters in the current buffer
|
||||
while end < len(buf) and buf[end] not in self.token_term:
|
||||
end = end + 1
|
||||
token = token + buf[idx:end]
|
||||
|
||||
if end < len(buf):
|
||||
# we stopped before the end, so we have a full token
|
||||
idx = end
|
||||
break
|
||||
|
||||
# we stopped at the end of the buffer, so we may have a partial token
|
||||
buf = self.rcsfile.read(self.CHUNK_SIZE)
|
||||
idx = end = 0
|
||||
|
||||
self.buf = buf
|
||||
self.idx = idx
|
||||
return token
|
||||
|
||||
# a "string" which starts with the "@" character. we'll skip it when we
|
||||
# search for content.
|
||||
idx = idx + 1
|
||||
|
||||
chunks = [ ]
|
||||
|
||||
while 1:
|
||||
if idx == len(buf):
|
||||
idx = 0
|
||||
buf = self.rcsfile.read(self.CHUNK_SIZE)
|
||||
if buf == '':
|
||||
raise RuntimeError, 'EOF'
|
||||
i = string.find(buf, '@', idx)
|
||||
if i == -1:
|
||||
chunks.append(buf[idx:])
|
||||
idx = len(buf)
|
||||
continue
|
||||
if i == len(buf) - 1:
|
||||
chunks.append(buf[idx:i])
|
||||
idx = 0
|
||||
buf = '@' + self.rcsfile.read(self.CHUNK_SIZE)
|
||||
if buf == '@':
|
||||
raise RuntimeError, 'EOF'
|
||||
continue
|
||||
if buf[i + 1] == '@':
|
||||
chunks.append(buf[idx:i+1])
|
||||
idx = i + 2
|
||||
continue
|
||||
|
||||
chunks.append(buf[idx:i])
|
||||
|
||||
self.buf = buf
|
||||
self.idx = i + 1
|
||||
|
||||
return string.join(chunks, '')
|
||||
|
||||
# _get = get
|
||||
# def get(self):
|
||||
token = self._get()
|
||||
print 'T:', `token`
|
||||
return token
|
||||
|
||||
def match(self, match):
|
||||
"Try to match the next token from the input buffer."
|
||||
|
||||
token = self.get()
|
||||
if token != match:
|
||||
raise RuntimeError, ('Unexpected parsing error in RCS file.\n' +
|
||||
'Expected token: %s, but saw: %s' % (match, token))
|
||||
|
||||
def unget(self, token):
|
||||
"Put this token back, for the next get() to return."
|
||||
|
||||
# Override the class' .get method with a function which clears the
|
||||
# overridden method then returns the pushed token. Since this function
|
||||
# will not be looked up via the class mechanism, it should be a "normal"
|
||||
# function, meaning it won't have "self" automatically inserted.
|
||||
# Therefore, we need to pass both self and the token thru via defaults.
|
||||
|
||||
# note: we don't put this into the input buffer because it may have been
|
||||
# @-unescaped already.
|
||||
|
||||
def give_it_back(self=self, token=token):
|
||||
del self.get
|
||||
return token
|
||||
|
||||
self.get = give_it_back
|
||||
|
||||
class Parser:
|
||||
|
||||
def parse_rcs_admin(self):
|
||||
while 1:
|
||||
# Read initial token at beginning of line
|
||||
token = self.ts.get()
|
||||
|
||||
# We're done once we reach the description of the RCS tree
|
||||
if token[0] in string.digits:
|
||||
self.ts.unget(token)
|
||||
return
|
||||
|
||||
if token == "head":
|
||||
self.sink.set_head_revision(self.ts.get())
|
||||
self.ts.match(';')
|
||||
elif token == "branch":
|
||||
self.sink.set_principal_branch(self.ts.get())
|
||||
self.ts.match(';')
|
||||
elif token == "symbols":
|
||||
while 1:
|
||||
tag = self.ts.get()
|
||||
if tag == ';':
|
||||
break
|
||||
(tag_name, tag_rev) = string.split(tag, ':')
|
||||
self.sink.define_tag(tag_name, tag_rev)
|
||||
elif token == "comment":
|
||||
self.sink.set_comment(self.ts.get())
|
||||
self.ts.match(';')
|
||||
|
||||
# Ignore all these other fields - We don't care about them. Also chews
|
||||
# up "newphrase".
|
||||
elif token in ("locks", "strict", "expand", "access"):
|
||||
while 1:
|
||||
tag = self.ts.get()
|
||||
if tag == ';':
|
||||
break
|
||||
else:
|
||||
pass
|
||||
# warn("Unexpected RCS token: $token\n")
|
||||
|
||||
raise RuntimeError, "Unexpected EOF";
|
||||
|
||||
def parse_rcs_tree(self):
|
||||
while 1:
|
||||
revision = self.ts.get()
|
||||
|
||||
# End of RCS tree description ?
|
||||
if revision == 'desc':
|
||||
self.ts.unget(revision)
|
||||
return
|
||||
|
||||
# Parse date
|
||||
self.ts.match('date')
|
||||
date = self.ts.get()
|
||||
self.ts.match(';')
|
||||
|
||||
# Convert date into timestamp
|
||||
date_fields = string.split(date, '.') + ['0', '0', '0']
|
||||
date_fields = map(string.atoi, date_fields)
|
||||
if date_fields[0] < 100:
|
||||
date_fields[0] = date_fields[0] + 1900
|
||||
timestamp = time.mktime(tuple(date_fields))
|
||||
|
||||
# Parse author
|
||||
self.ts.match('author')
|
||||
author = self.ts.get()
|
||||
self.ts.match(';')
|
||||
|
||||
# Parse state
|
||||
self.ts.match('state')
|
||||
state = ''
|
||||
while 1:
|
||||
token = self.ts.get()
|
||||
if token == ';':
|
||||
break
|
||||
state = state + token + ' '
|
||||
state = state[:-1] # toss the trailing space
|
||||
|
||||
# Parse branches
|
||||
self.ts.match('branches')
|
||||
branches = [ ]
|
||||
while 1:
|
||||
token = self.ts.get()
|
||||
if token == ';':
|
||||
break
|
||||
branches.append(token)
|
||||
|
||||
# Parse revision of next delta in chain
|
||||
self.ts.match('next')
|
||||
next = self.ts.get()
|
||||
if next == ';':
|
||||
next = None
|
||||
else:
|
||||
self.ts.match(';')
|
||||
|
||||
# there are some files with extra tags in them. for example:
|
||||
# owner 640;
|
||||
# group 15;
|
||||
# permissions 644;
|
||||
# hardlinks @configure.in@;
|
||||
# this is "newphrase" in RCSFILE(5). we just want to skip over these.
|
||||
while 1:
|
||||
token = self.ts.get()
|
||||
if token == 'desc' or token[0] in string.digits:
|
||||
self.ts.unget(token)
|
||||
break
|
||||
# consume everything up to the semicolon
|
||||
while self.ts.get() != ';':
|
||||
pass
|
||||
|
||||
self.sink.define_revision(revision, timestamp, author, state, branches,
|
||||
next)
|
||||
|
||||
def parse_rcs_description(self):
|
||||
self.ts.match('desc')
|
||||
self.sink.set_description(self.ts.get())
|
||||
|
||||
def parse_rcs_deltatext(self):
|
||||
while 1:
|
||||
revision = self.ts.get()
|
||||
if revision is None:
|
||||
# EOF
|
||||
break
|
||||
self.ts.match('log')
|
||||
log = self.ts.get()
|
||||
### need to add code to chew up "newphrase"
|
||||
self.ts.match('text')
|
||||
text = self.ts.get()
|
||||
self.sink.set_revision_info(revision, log, text)
|
||||
|
||||
def parse(self, file, sink):
|
||||
self.ts = _TokenStream(file)
|
||||
self.sink = sink
|
||||
|
||||
self.parse_rcs_admin()
|
||||
self.parse_rcs_tree()
|
||||
|
||||
# many sinks want to know when the tree has been completed so they can
|
||||
# do some work to prep for the arrival of the deltatext
|
||||
self.sink.tree_completed()
|
||||
|
||||
self.parse_rcs_description()
|
||||
self.parse_rcs_deltatext()
|
||||
|
||||
# easiest for us to tell the sink it is done, rather than worry about
|
||||
# higher level software doing it.
|
||||
self.sink.parse_completed()
|
||||
|
||||
self.ts = self.sink = None
|
||||
|
||||
|
||||
class Sink:
|
||||
def set_head_revision(self, revision):
|
||||
pass
|
||||
def set_principal_branch(self, branch_name):
|
||||
pass
|
||||
def define_tag(self, name, revision):
|
||||
pass
|
||||
def set_comment(self, comment):
|
||||
pass
|
||||
def set_description(self, description):
|
||||
pass
|
||||
def define_revision(self, revision, timestamp, author, state,
|
||||
branches, next):
|
||||
pass
|
||||
def set_revision_info(self, revision, log, text):
|
||||
pass
|
||||
def tree_completed(self):
|
||||
pass
|
||||
def parse_completed(self):
|
||||
pass
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
#
|
||||
# TESTING AND DEBUGGING TOOLS
|
||||
#
|
||||
|
||||
class DebugSink:
|
||||
def set_head_revision(self, revision):
|
||||
print 'head:', revision
|
||||
|
||||
def set_principal_branch(self, branch_name):
|
||||
print 'branch:', branch_name
|
||||
|
||||
def define_tag(self, name, revision):
|
||||
print 'tag:', name, '=', revision
|
||||
|
||||
def set_comment(self, comment):
|
||||
print 'comment:', comment
|
||||
|
||||
def set_description(self, description):
|
||||
print 'description:', description
|
||||
|
||||
def define_revision(self, revision, timestamp, author, state,
|
||||
branches, next):
|
||||
print 'revision:', revision
|
||||
print ' timestamp:', timestamp
|
||||
print ' author:', author
|
||||
print ' state:', state
|
||||
print ' branches:', branches
|
||||
print ' next:', next
|
||||
|
||||
def set_revision_info(self, revision, log, text):
|
||||
print 'revision:', revision
|
||||
print ' log:', log
|
||||
print ' text:', text[:100], '...'
|
||||
|
||||
class DumpSink:
|
||||
"""Dump all the parse information directly to stdout.
|
||||
|
||||
The output is relatively unformatted and untagged. It is intended as a
|
||||
raw dump of the data in the RCS file. A copy can be saved, then changes
|
||||
made to the parsing engine, then a comparison of the new output against
|
||||
the old output.
|
||||
"""
|
||||
def __init__(self):
|
||||
global sha
|
||||
import sha
|
||||
|
||||
def set_head_revision(self, revision):
|
||||
print revision
|
||||
|
||||
def set_principal_branch(self, branch_name):
|
||||
print branch_name
|
||||
|
||||
def define_tag(self, name, revision):
|
||||
print name, revision
|
||||
|
||||
def set_comment(self, comment):
|
||||
print comment
|
||||
|
||||
def set_description(self, description):
|
||||
print description
|
||||
|
||||
def define_revision(self, revision, timestamp, author, state,
|
||||
branches, next):
|
||||
print revision, timestamp, author, state, branches, next
|
||||
|
||||
def set_revision_info(self, revision, log, text):
|
||||
print revision, sha.new(log).hexdigest(), sha.new(text).hexdigest()
|
||||
|
||||
def tree_completed(self):
|
||||
print 'tree_completed'
|
||||
|
||||
def parse_completed(self):
|
||||
print 'parse_completed'
|
||||
|
||||
def dump_file(fname):
|
||||
Parser().parse(open(fname), DumpSink())
|
||||
|
||||
def time_file(fname):
|
||||
import time
|
||||
p = Parser().parse
|
||||
f = open(fname)
|
||||
s = Sink()
|
||||
t = time.time()
|
||||
p(f, s)
|
||||
t = time.time() - t
|
||||
print t
|
||||
|
||||
def _usage():
|
||||
print 'This is normally a module for importing, but it has a couple'
|
||||
print 'features for testing as an executable script.'
|
||||
print 'USAGE: %s COMMAND filename,v' % sys.argv[0]
|
||||
print ' where COMMAND is one of:'
|
||||
print ' dump: filename is "dumped" to stdout'
|
||||
print ' time: filename is parsed with the time written to stdout'
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
if len(sys.argv) != 3:
|
||||
usage()
|
||||
if sys.argv[1] == 'dump':
|
||||
dump_file(sys.argv[2])
|
||||
elif sys.argv[1] == 'time':
|
||||
time_file(sys.argv[2])
|
||||
else:
|
||||
usage()
|
10
lib/rlog.py
10
lib/rlog.py
@@ -1,14 +1,14 @@
|
||||
# -*- Mode: python -*-
|
||||
#
|
||||
# Copyright (C) 2000 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 2000-2001 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
# distribution or at http://viewcvs.sourceforge.net/license-1.html.
|
||||
#
|
||||
# Contact information:
|
||||
# Greg Stein, PO Box 760, Palo Alto, CA, 94302
|
||||
# gstein@lyra.org, http://www.lyra.org/viewcvs/
|
||||
# gstein@lyra.org, http://viewcvs.sourceforge.net/
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
@@ -245,7 +245,9 @@ class RLogOutputParser:
|
||||
def parse_one_rlog_entry(self):
|
||||
## revision line/first line
|
||||
line = self.rlog.readline()
|
||||
if not line:
|
||||
# Since FreeBSD's rlog outputs extra "---...---\n" before
|
||||
# "===...===\n", _rlog_end may be occured here.
|
||||
if not line or line == _rlog_end:
|
||||
return None
|
||||
|
||||
## revision
|
||||
|
2526
lib/viewcvs.py
Normal file
2526
lib/viewcvs.py
Normal file
File diff suppressed because it is too large
Load Diff
139
tests/timelog.py
Normal file
139
tests/timelog.py
Normal file
@@ -0,0 +1,139 @@
|
||||
|
||||
import time
|
||||
import string
|
||||
import profile
|
||||
|
||||
import rcsparse
|
||||
import viewcvs
|
||||
|
||||
def lines_changed(delta):
|
||||
idx = 0
|
||||
added = deleted = 0
|
||||
while idx < len(delta):
|
||||
op = delta[idx]
|
||||
i = string.find(delta, ' ', idx + 1)
|
||||
j = string.find(delta, '\n', i + 1)
|
||||
line = int(delta[idx+1:i])
|
||||
count = int(delta[i+1:j])
|
||||
idx = j + 1
|
||||
if op == 'd':
|
||||
deleted = deleted + count
|
||||
else: # 'a' for adding text
|
||||
added = added + count
|
||||
# skip new text
|
||||
while count > 0:
|
||||
nl = string.find(delta, '\n', idx)
|
||||
assert nl > 0, 'missing a newline in the delta in the RCS file'
|
||||
idx = nl + 1
|
||||
count = count - 1
|
||||
return added, deleted
|
||||
|
||||
class FetchSink(rcsparse.Sink):
|
||||
def __init__(self, which_rev=None):
|
||||
self.head = self.branch = ''
|
||||
self.tags = { }
|
||||
self.meta = { }
|
||||
self.revs = [ ]
|
||||
self.base = { }
|
||||
self.entries = { }
|
||||
self.which = which_rev
|
||||
|
||||
def set_head_revision(self, revision):
|
||||
self.head = revision
|
||||
|
||||
def set_principal_branch(self, branch_name):
|
||||
self.branch = branch_name
|
||||
|
||||
def define_tag(self, name, revision):
|
||||
self.tags[name] = revision
|
||||
|
||||
def define_revision(self, revision, timestamp, author, state,
|
||||
branches, next):
|
||||
self.meta[revision] = (timestamp, author, state)
|
||||
self.base[next] = revision
|
||||
for b in branches:
|
||||
self.base[b] = revision
|
||||
|
||||
def set_revision_info(self, revision, log, text):
|
||||
timestamp, author, state = self.meta[revision]
|
||||
entry = viewcvs.LogEntry(revision, int(timestamp) - time.timezone, author,
|
||||
state, None, log)
|
||||
|
||||
# .revs is "order seen" and .entries is for random access
|
||||
self.revs.append(entry)
|
||||
self.entries[revision] = entry
|
||||
|
||||
if revision != self.head:
|
||||
added, deleted = lines_changed(text)
|
||||
if string.count(revision, '.') == 1:
|
||||
# on the trunk. reverse delta.
|
||||
changed = '+%d -%d' % (deleted, added)
|
||||
self.entries[self.base[revision]].changed = changed
|
||||
else:
|
||||
# on a branch. forward delta.
|
||||
changed = '+%d -%d' % (added, deleted)
|
||||
self.entries[revision].changed = changed
|
||||
|
||||
def parse_completed(self):
|
||||
if self.which:
|
||||
self.revs = [ self.entries[self.which] ]
|
||||
|
||||
def fetch_log2(full_name, which_rev=None):
|
||||
sink = FetchSink(which_rev)
|
||||
rcsparse.Parser().parse(open(full_name), sink)
|
||||
return sink.head, sink.branch, sink.tags, sink.revs
|
||||
|
||||
def compare_fetch(full_name, which_rev=None):
|
||||
d1 = viewcvs.fetch_log(full_name, which_rev)
|
||||
d2 = fetch_log2(full_name, which_rev)
|
||||
if d1[:3] != d2[:3]:
|
||||
print 'd1:', d1[:3]
|
||||
print 'd2:', d2[:3]
|
||||
return
|
||||
if len(d1[3]) != len(d2[3]):
|
||||
print 'len(d1[3])=%d len(d2[3])=%d' % (len(d1[3]), len(d2[3]))
|
||||
return
|
||||
def sort_func(e, f):
|
||||
return cmp(e.rev, f.rev)
|
||||
d1[3].sort(sort_func)
|
||||
d2[3].sort(sort_func)
|
||||
import pprint
|
||||
for i in range(len(d1[3])):
|
||||
if vars(d1[3][i]) != vars(d2[3][i]):
|
||||
pprint.pprint((i, vars(d1[3][i]), vars(d2[3][i])))
|
||||
|
||||
def time_fetch(full_name, which_rev=None):
|
||||
t = time.time()
|
||||
viewcvs.fetch_log(full_name, which_rev)
|
||||
t1 = time.time() - t
|
||||
t = time.time()
|
||||
fetch_log2(full_name, which_rev)
|
||||
t2 = time.time() - t
|
||||
print t1, t2
|
||||
|
||||
def profile_fetch(full_name, which_rev=None):
|
||||
p = profile.Profile()
|
||||
def many_calls(*args):
|
||||
for i in xrange(10):
|
||||
apply(fetch_log2, args)
|
||||
p.runcall(many_calls, full_name, which_rev)
|
||||
p.print_stats()
|
||||
|
||||
def varysize(full_name, which_rev=None):
|
||||
def one_run(n, *args):
|
||||
rcsparse._TokenStream.CHUNK_SIZE = n
|
||||
t = time.time()
|
||||
for i in xrange(5):
|
||||
apply(fetch_log2, args)
|
||||
print n, time.time() - t
|
||||
|
||||
#one_run(2020, full_name, which_rev)
|
||||
#one_run(4070, full_name, which_rev)
|
||||
#one_run(8170, full_name, which_rev)
|
||||
#one_run(8192, full_name, which_rev)
|
||||
#one_run(16384, full_name, which_rev)
|
||||
one_run(32740, full_name, which_rev)
|
||||
one_run(65500, full_name, which_rev)
|
||||
one_run(100000, full_name, which_rev)
|
||||
one_run(200000, full_name, which_rev)
|
||||
one_run(500000, full_name, which_rev)
|
@@ -1,15 +1,15 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- Mode: python -*-
|
||||
#
|
||||
# Copyright (C) 2000 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 2000-2001 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
# distribution or at http://viewcvs.sourceforge.net/license-1.html.
|
||||
#
|
||||
# Contact information:
|
||||
# Greg Stein, PO Box 760, Palo Alto, CA, 94302
|
||||
# gstein@lyra.org, http://www.lyra.org/viewcvs/
|
||||
# gstein@lyra.org, http://viewcvs.sourceforge.net/
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
|
@@ -1,15 +1,15 @@
|
||||
#!/usr/bin/python
|
||||
# -*- Mode: python -*-
|
||||
#
|
||||
# Copyright (C) 2000 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 2000-2001 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
# distribution or at http://viewcvs.sourceforge.net/license-1.html.
|
||||
#
|
||||
# Contact information:
|
||||
# Greg Stein, PO Box 760, Palo Alto, CA, 94302
|
||||
# gstein@lyra.org, http://www.lyra.org/viewcvs/
|
||||
# gstein@lyra.org, http://viewcvs.sourceforge.net/
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
|
@@ -1,15 +1,15 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- Mode: python -*-
|
||||
#
|
||||
# Copyright (C) 2000 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 2000-2001 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
# distribution or at http://viewcvs.sourceforge.net/license-1.html.
|
||||
#
|
||||
# Contact information:
|
||||
# Greg Stein, PO Box 760, Palo Alto, CA, 94302
|
||||
# gstein@lyra.org, http://www.lyra.org/viewcvs/
|
||||
# gstein@lyra.org, http://viewcvs.sourceforge.net/
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
|
@@ -1,14 +1,14 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (C) 2000 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 2000-2001 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
# distribution or at http://viewcvs.sourceforge.net/license-1.html.
|
||||
#
|
||||
# Contact information:
|
||||
# Greg Stein, PO Box 760, Palo Alto, CA, 94302
|
||||
# gstein@lyra.org, http://www.lyra.org/viewcvs/
|
||||
# gstein@lyra.org, http://viewcvs.sourceforge.net/
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
@@ -18,22 +18,22 @@
|
||||
#
|
||||
|
||||
if [ "x$1" = "x" ]; then
|
||||
echo "USAGE: $0 target-directory"
|
||||
echo "USAGE: $0 tagname target-directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# grab a copy of the CVS repository (export requires a tag, so just use 'co')
|
||||
rm -r $1
|
||||
echo 'Checking out into:' $1
|
||||
cvs -d /home/cvsroot co -d $1 -kv -P -R viewcvs
|
||||
if test -e $2; then
|
||||
echo "ERROR: must remove $2 first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo 'Cleaning up...'
|
||||
# clean out the CVS crap
|
||||
find $1 -name CVS | xargs rm -r
|
||||
# grab a copy of the CVS repository
|
||||
echo 'Checking out into:' $2
|
||||
cvs -d :pserver:anonymous@cvs.viewcvs.sourceforge.net:/cvsroot/viewcvs export -r $1 -d $2 viewcvs
|
||||
|
||||
# various shifting, cleanup
|
||||
mv $1/website/license-1.html $1/LICENSE.html
|
||||
rm -r $1/website
|
||||
rm $1/tools/make-release
|
||||
mv $2/website/license-1.html $2/LICENSE.html
|
||||
rm -r $2/website
|
||||
rm $2/tools/make-release
|
||||
|
||||
echo 'Done.'
|
||||
|
@@ -1,15 +1,15 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- Mode: python -*-
|
||||
#
|
||||
# Copyright (C) 2000 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 2000-2001 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
# distribution or at http://viewcvs.sourceforge.net/license-1.html.
|
||||
#
|
||||
# Contact information:
|
||||
# Greg Stein, PO Box 760, Palo Alto, CA, 94302
|
||||
# gstein@lyra.org, http://www.lyra.org/viewcvs/
|
||||
# gstein@lyra.org, http://viewcvs.sourceforge.net/
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
@@ -69,8 +69,10 @@ FILE_INFO_LIST = [
|
||||
("lib/dbi.py", "lib/dbi.py", 0644, 0, 0, 1),
|
||||
("lib/popen.py", "lib/popen.py", 0644, 0, 0, 1),
|
||||
("lib/py2html.py", "lib/py2html.py", 0644, 0, 0, 1),
|
||||
("lib/rlog.py", "lib/rlog.py", 0644, 1, 0, 1),
|
||||
("lib/query.py", "lib/query.py", 0644, 1, 0, 1),
|
||||
("lib/rcsparse.py", "lib/rcsparse.py", 0644, 1, 0, 1),
|
||||
("lib/rlog.py", "lib/rlog.py", 0644, 1, 0, 1),
|
||||
("lib/viewcvs.py", "lib/viewcvs.py", 0644, 1, 0, 1),
|
||||
|
||||
("tools/loginfo-handler", "loginfo-handler", 0755, 1, 0, 0),
|
||||
("tools/cvsdbadmin", "cvsdbadmin", 0755, 1, 0, 0),
|
||||
|
BIN
website/images/chalk.jpg
Normal file
BIN
website/images/chalk.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 755 B |
@@ -5,16 +5,33 @@
|
||||
</head>
|
||||
|
||||
<body background="/images/chalk.jpg">
|
||||
<h1>ViewCVS: Viewing CVS Repositories</h1>
|
||||
|
||||
<table width="100%" cellspacing=5>
|
||||
<tr>
|
||||
<td>
|
||||
<h1>ViewCVS: Viewing CVS Repositories</h1>
|
||||
</td>
|
||||
<td align=center valign=top bgcolor="white" width="1%">
|
||||
<b>Quickstart:</b>
|
||||
<a href="viewcvs-0.6.tar.gz">download</a>
|
||||
</td>
|
||||
<td width="1%"><a href="http://sourceforge.net/"><img border=0
|
||||
src="http://sourceforge.net/sflogo.php?group_id=18760&type=1"></a><br><a href="http://sourceforge.net/projects/viewcvs/">ViewCVS project page</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p>
|
||||
The ViewCVS software was inspired by
|
||||
<a href="http://stud.fh-heilbronn.de/~zeller/cgi/cvsweb.cgi/"><i>cvsweb</i></a>
|
||||
(by <a href="mailto:zeller@think.de">Henner Zeller</a>).
|
||||
I wanted to make some changes and updates, but cvsweb was
|
||||
implemented in Perl (and rather poorly, IMO). So I undertook the
|
||||
implemented in Perl. While I can manage some Perl, cvsweb was
|
||||
rather unmaintainable for me. So I undertook the
|
||||
task to convert the software to
|
||||
<a href="http://www.python.org/"><i>Python</i></a>.
|
||||
<a href="http://www.python.org/"><i>Python</i></a>. As a result,
|
||||
I've actually been able to go <em>way</em> beyond the simple
|
||||
changes that I had envisioned.
|
||||
</p>
|
||||
<p>
|
||||
ViewCVS can browse directories, change logs, and specific
|
||||
@@ -41,7 +58,7 @@
|
||||
Of course, it is also available through ViewCVS itself:
|
||||
</p>
|
||||
<blockquote>
|
||||
<a href="/cgi-bin/viewcvs.cgi/viewcvs/">http://www.lyra.org/cgi-bin/viewcvs.cgi/viewcvs/</a>
|
||||
<a href="http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/viewcvs/">http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/viewcvs/</a>
|
||||
</blockquote>
|
||||
|
||||
<p>
|
||||
@@ -117,6 +134,16 @@
|
||||
<li>
|
||||
Directories with a large number of files can be viewed.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Security</strong>: ViewCVS only requires read access
|
||||
to the CVS repository (not read/write). With the correct
|
||||
security partitioning, this means that even if ViewCVS were to
|
||||
be subverted, your source code is safe. Further, ViewCVS does
|
||||
not use any <code>system()</code> or <code>popen()</code>
|
||||
calls, which are very susceptible to abuse.
|
||||
<br>
|
||||
<small>(cvsweb had a hole due to a popen() call)</small>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
@@ -174,19 +201,11 @@
|
||||
you want to use this feature.
|
||||
</p>
|
||||
|
||||
<hr width="75%">
|
||||
<h3>Other links</h3>
|
||||
|
||||
<p>
|
||||
[ <a href="/greg/">Greg's page</a> ]
|
||||
[ <a href="/greg/python/">other Python software</a> ]
|
||||
</p>
|
||||
|
||||
<hr>
|
||||
<address><a href="mailto:gstein@lyra.org">Greg Stein</a></address>
|
||||
<!-- Created: Fri Dec 3 02:51:37 PST 1999 -->
|
||||
<!-- hhmts start -->
|
||||
Last modified: Tue Jul 25 05:14:15 PDT 2000
|
||||
Last modified: Fri May 11 14:22:36 PDT 2001
|
||||
<!-- hhmts end -->
|
||||
</body>
|
||||
</html>
|
||||
|
@@ -14,12 +14,20 @@
|
||||
<a href="who.html">The ViewCVS
|
||||
Group</a> and the users of ViewCVS.
|
||||
</p>
|
||||
<p>
|
||||
<small>
|
||||
<em>
|
||||
Note: the copyright years were updated on May 12, 2001. No
|
||||
other changes were made to the license.
|
||||
</em>
|
||||
</small>
|
||||
</p>
|
||||
|
||||
<hr>
|
||||
|
||||
<p>
|
||||
<b>
|
||||
Copyright © 1999-2000 The ViewCVS Group. All rights reserved.
|
||||
Copyright © 1999-2001 The ViewCVS Group. All rights reserved.
|
||||
</b>
|
||||
</p>
|
||||
<p>
|
||||
@@ -69,7 +77,7 @@
|
||||
<address><a href="mailto:gstein@lyra.org">Greg Stein</a></address>
|
||||
<!-- Created: Mon May 8 19:01:27 PDT 2000 -->
|
||||
<!-- hhmts start -->
|
||||
Last modified: Mon May 8 19:28:37 PDT 2000
|
||||
Last modified: Sat May 12 15:53:33 PDT 2001
|
||||
<!-- hhmts end -->
|
||||
</body>
|
||||
</html>
|
||||
|
@@ -12,7 +12,7 @@
|
||||
developing the ViewCVS package. The current set of members are:
|
||||
</p>
|
||||
<ul>
|
||||
<li><a href="/greg/"><b>Greg Stein</b></a></li>
|
||||
<li><a href="http://www.lyra.org/greg/"><b>Greg Stein</b></a></li>
|
||||
<li>Jay Painter</li>
|
||||
</ul>
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
<address><a href="mailto:gstein@lyra.org">Greg Stein</a></address>
|
||||
<!-- Created: Mon May 8 19:08:58 PDT 2000 -->
|
||||
<!-- hhmts start -->
|
||||
Last modified: Mon May 8 19:14:45 PDT 2000
|
||||
Last modified: Fri May 11 14:10:54 PDT 2001
|
||||
<!-- hhmts end -->
|
||||
</body>
|
||||
</html>
|
||||
|
Reference in New Issue
Block a user