mirror of
https://github.com/vitalif/viewvc-4intranet
synced 2019-04-16 04:14:59 +03:00
Compare commits
115 Commits
CVSDB_PROD
...
V0_7
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 | ||
![]() |
4ddb89e053 | ||
![]() |
c9e782c881 | ||
![]() |
7d8f036490 | ||
![]() |
02bad3fbe3 | ||
![]() |
892364776e | ||
![]() |
8996332950 | ||
![]() |
eb214de775 | ||
![]() |
5ca77ced3c | ||
![]() |
91d2ebfc64 | ||
![]() |
ee7abc077a | ||
![]() |
087db42096 | ||
![]() |
028b54de90 | ||
![]() |
cb4f496b99 | ||
![]() |
573293bbfc | ||
![]() |
e6080c8366 | ||
![]() |
346c91205b | ||
![]() |
3828e969de | ||
![]() |
1a821bda9e | ||
![]() |
ddd782a950 | ||
![]() |
f2dffceb6d | ||
![]() |
8810ab6c57 | ||
![]() |
b21597d96d | ||
![]() |
c553401843 | ||
![]() |
ee8a705131 | ||
![]() |
665d46a59d | ||
![]() |
15d7883bb0 | ||
![]() |
6ed78e918e | ||
![]() |
5463d8ff1b | ||
![]() |
934209abb9 | ||
![]() |
a19338dd0a | ||
![]() |
ee6c5f5da3 | ||
![]() |
43d6d82b34 | ||
![]() |
7ad684b001 | ||
![]() |
25e4e5d18a | ||
![]() |
2719168ddc | ||
![]() |
31272b7cea | ||
![]() |
eb940183e3 | ||
![]() |
33356d5a30 | ||
![]() |
7ba05d4b43 | ||
![]() |
083de4d0c7 | ||
![]() |
cbe1f36d8a | ||
![]() |
efa434d7fa | ||
![]() |
7fc9018074 | ||
![]() |
a7fe838521 | ||
![]() |
a1e7108d76 | ||
![]() |
867883e525 | ||
![]() |
2f376b11e3 | ||
![]() |
07763938a3 | ||
![]() |
182aeaa7fa | ||
![]() |
75d59eb292 | ||
![]() |
18048c0a80 | ||
![]() |
fb262791a1 | ||
![]() |
b2d9bf88a1 | ||
![]() |
8e94cc5775 | ||
![]() |
e8c829df09 | ||
![]() |
b892974ffe | ||
![]() |
78022b0e38 | ||
![]() |
17c4db41dc | ||
![]() |
8070b6bba1 | ||
![]() |
c8ebba802e | ||
![]() |
5b24650755 | ||
![]() |
8b9a143376 | ||
![]() |
741a3daefe |
125
INSTALL
125
INSTALL
@@ -9,20 +9,32 @@ INSTALLING VIEWCVS
|
||||
You need to have RCS installed. Specifically, "rlog", "rcsdiff",
|
||||
and "co". This script was tested against RedHat's rcs-5.7-10.rpm
|
||||
|
||||
Note, that the viewcvs.cgi-script needs to have READ-ONLY, physical
|
||||
access to the repository (or a copy of it). Therefore, rsh/ssh or
|
||||
Note, that the viewcvs.cgi script needs to have READ-ONLY, physical
|
||||
access to the CVS repository (or a copy of it). Therefore, rsh/ssh or
|
||||
pserver access doesn't work yet.
|
||||
|
||||
2) Copy viewcvs.cgi to the cgi-script location of your web server.
|
||||
For the checkin database to work, you will need MySQL >= 3.22,
|
||||
and the Python DBAPI 2.0 module, MySQLdb. This was tested with
|
||||
MySQLdb 1.12.
|
||||
|
||||
If Python is not located in /usr/local/bin, then you'll need to
|
||||
edit the first line of viewcvs.cgi.
|
||||
2) Installation is handled by the ./viewcvs-install script. Run this
|
||||
script and you will be prompted for a installation root path.
|
||||
The default is /usr/local/viewcvs. The installer sets the install
|
||||
path in some of the files, and ViewCVS cannot be moved to a
|
||||
different path after the install.
|
||||
|
||||
3) Copy viewcvs.conf.dist to the same directory and RENAME it to
|
||||
viewcvs.conf
|
||||
Note: while 'root' is usually required to create /usr/local/viewcvs,
|
||||
ViewCVS does not have to be installed as root, nor does it run as root.
|
||||
It is just as valid to place ViewCVS in a home directory, too.
|
||||
|
||||
4) Edit viewcvs.conf for your specific configuration. In particular,
|
||||
examine the following configuration options:
|
||||
Note: viewcvs-install will create directories if needed. It will
|
||||
prompt before overwriting files that may have been modified (such
|
||||
as viewcvs.conf), thus making it safe to install over the top of
|
||||
a previous installation. It will always overwrite program files,
|
||||
however.
|
||||
|
||||
3) Edit <install-root>viewcvs.conf for your specific configuration.
|
||||
In particular, examine the following configuration options:
|
||||
|
||||
cvs_roots
|
||||
default_root
|
||||
@@ -32,11 +44,98 @@ INSTALLING VIEWCVS
|
||||
There are some other options that are usually nice to change. See
|
||||
viewcvs.conf for more information.
|
||||
|
||||
5) That's it. Try it out.
|
||||
4) The CGI programs are in <install-root>/cgi/. You can symlink to this
|
||||
directory from somewhere in your published HTTP server path if your
|
||||
webserver is configured to follow symbolic links. You can also copy
|
||||
the installed <install-root>/cgi/*.cgi scripts after the install
|
||||
(unlike the other files in ViewCVS, the CGI scripts can be moved).
|
||||
|
||||
Warning: ViewCVS has not been tested on web servers operating on the
|
||||
Win32 platform.
|
||||
NOTE: for security reasons, it is not advisable to install ViewCVS
|
||||
directly into your published HTTP directory tree (due to the MySQL
|
||||
passwords in viewcvs.conf).
|
||||
|
||||
5) That's it for repository browsing. Instructions for getting the
|
||||
SQL checkin database working are below.
|
||||
|
||||
|
||||
WARNING: ViewCVS has not been tested on web servers operating on the
|
||||
Win32 platform.
|
||||
|
||||
|
||||
|
||||
SQL Checkin Database
|
||||
--------------------
|
||||
|
||||
This feature is a clone of the Mozilla Project's Bonsai database. It
|
||||
catalogs every commit in the CVS repository into a SQL database. In fact,
|
||||
the databases are 100% compatible.
|
||||
|
||||
Various queries can be preformed on the database. After installing ViewCVS,
|
||||
there are some additional steps required to get the database working.
|
||||
|
||||
1) You need MySQL >= 3.22, and the Python module MySQLdb >= 1.12 installed.
|
||||
Python 1.5.2 is REQUIRED by MySQLdb, therefore to use this part of
|
||||
ViewCVS you must be useing Python 1.5.2.
|
||||
|
||||
2) You need to create a MySQL user who has permission to create databases.
|
||||
Optionally, you can create a second user with read-only access to the
|
||||
database.
|
||||
|
||||
3) Run the <install-root>/make-database script. It will prompt you for
|
||||
your MySQL user, password, and the name of database you want to
|
||||
create. The database name defaults to "ViewCVS". This script creates
|
||||
the database and sets up the empty tables. If you run this on a
|
||||
existing ViewCVS database, you will loose all your data!
|
||||
|
||||
4) Edit your <install-root>/viewcvs.conf file. There is a [cvsdb]
|
||||
section. You will need to set:
|
||||
|
||||
|
||||
host = # MySQL database server host
|
||||
database_name = # the name of the database you created with
|
||||
# make-database
|
||||
user = # the read/write database user
|
||||
passwd = # password for read/write database user
|
||||
readonly_user = # the readonly database user -- it's pretty
|
||||
# safe to use the read/write user here
|
||||
readonly_passwd = # password for the readonly user
|
||||
|
||||
5) Two programs are provided for updating the checkin database,
|
||||
cvsdbadmin and loginfo-handler. They serve two different purposes.
|
||||
The cvsdbadmin program walks through your CVS repository and adds
|
||||
every commit in every file. This is commonly used for initalizing
|
||||
the database from a repository which has been in use. The
|
||||
loginfo-handler script is executed by the CVS server's CVSROOT/loginfo
|
||||
system upon each commit. It makes real-time updates to the checkin
|
||||
database as commits are made to the repository.
|
||||
|
||||
To build a database of all the commits in the CVS repository /home/cvs,
|
||||
invoke: "./cvsdbadmin rebuild /home/cvs". If you want to update
|
||||
the checkin database, invoke: "./cvsdbadmin update /home/cvs". The
|
||||
update mode checks to see if a commit is already in the database,
|
||||
and only adds it if it is abscent.
|
||||
|
||||
To get real-time updates, you'll want to checkout the CVSROOT module
|
||||
from your CVS repository and edit CVSROOT/loginfo. Add the line:
|
||||
|
||||
ALL (echo %{sVv}; cat) | <install-root>/loginfo-handler
|
||||
|
||||
If you have other scripts invoked by CVSROOT/loginfo, you will want
|
||||
to make sure to change any running under the "DEFAULT" keyword to
|
||||
"ALL" like the loginfo handler, and probably carefully read the
|
||||
execution rules for CVSROOT/loginfo from the CVS manual.
|
||||
|
||||
6) You may want to modify the HTML template files:
|
||||
|
||||
<install-root>/html-templates/queryformtemplate.html
|
||||
<install-root>/html-templates/querytemplate.html
|
||||
|
||||
They're used by the queryform.cgi and query.cgi scripts generate
|
||||
HTML output. At some point, viewcvs.cgi, query.cgi, and queryform.cgi
|
||||
will use the same mechanism for HTML generation, but not yet.
|
||||
|
||||
7) You should be ready to go. Load up the queryform.cgi script and give
|
||||
it a try.
|
||||
|
||||
|
||||
IF YOU HAVE PROBLEMS ...
|
||||
@@ -75,7 +174,7 @@ If you've trouble to make viewcvs.cgi work:
|
||||
|
||||
o check the ViewCVS home page:
|
||||
|
||||
http://www.lyra.org/greg/python/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:
|
||||
|
25
TODO
25
TODO
@@ -14,25 +14,26 @@ TODO ITEMS
|
||||
*) committing with a specific revision number:
|
||||
http://mailman.lyra.org/pipermail/viewcvs/2000q1/000008.html
|
||||
|
||||
*) add an "allowed" modules. process the forbidden modules first. any
|
||||
match will throw it out immediately. if an allowed is present, then
|
||||
disallow any module unless it is present in the allowed list.
|
||||
|
||||
*) provide a virtual host capability. consider multiple hosts which
|
||||
map a particular Location to the same directory (i.e. the same
|
||||
ViewCVS script and config file). provide for a way to have global
|
||||
options plus vhost-specific options.
|
||||
|
||||
*) add capability similar to cvs2cl.pl:
|
||||
http://mailman.lyra.org/pipermail/viewcvs/2000q2/000050.html
|
||||
suggestion from Chris Meyer <cmeyer@gatan.com>.
|
||||
|
||||
*) add a tree view of the directory structure (and files?)
|
||||
|
||||
*) include a ConfigParser.py to help older Python installations
|
||||
|
||||
*) add a check for the rcs programs/paths to viewcvs-install. clarify the
|
||||
dependency on RCS in the docs.
|
||||
|
||||
*) 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.
|
||||
|
||||
|
216
cgi/query.cgi
216
cgi/query.cgi
@@ -1,89 +1,206 @@
|
||||
#!/usr/bin/python
|
||||
# -*- Mode: python -*-
|
||||
#
|
||||
# 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/
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
# CGI script to process and display queries to CVSdb
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
# Copyright (C) 2000 Jay Painter. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth below:
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# 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 script is part of the ViewCVS package. More information can be
|
||||
# found at http://viewcvs.sourceforge.net/.
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
# For tracking purposes, this software is identified by:
|
||||
# $Id$
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
## BOOTSTRAP
|
||||
import sys, os, string
|
||||
_viewcvs_root = string.strip(open("/etc/viewcvs/root", "r").read())
|
||||
sys.path.append(os.path.join(_viewcvs_root, "lib"))
|
||||
##
|
||||
#########################################################################
|
||||
#
|
||||
# INSTALL-TIME CONFIGURATION
|
||||
#
|
||||
# These values will be set during the installation process. During
|
||||
# development, they will remain None.
|
||||
#
|
||||
|
||||
LIBRARY_DIR = None
|
||||
CONF_PATHNAME = None
|
||||
HTML_TEMPLATE_DIR = None
|
||||
|
||||
# Adjust sys.path to include our library directory
|
||||
import sys
|
||||
|
||||
if LIBRARY_DIR:
|
||||
sys.path.insert(0, LIBRARY_DIR)
|
||||
else:
|
||||
sys.path[:0] = ['../lib'] # any other places to look?
|
||||
|
||||
#########################################################################
|
||||
|
||||
import os
|
||||
import string
|
||||
import cgi
|
||||
import time
|
||||
|
||||
import cvsdbapi
|
||||
|
||||
import cgi, time, cvsdbapi
|
||||
|
||||
## tuple of alternating row colors
|
||||
Colors = ("#ccccee", "#ffffff")
|
||||
|
||||
|
||||
def HTMLHeader():
|
||||
print "Content-type: text/html"
|
||||
print
|
||||
|
||||
## returns a tuple-list (mod-str, string)
|
||||
def listparse_string(str):
|
||||
return_list = []
|
||||
|
||||
cmd = ""
|
||||
temp = ""
|
||||
escaped = 0
|
||||
state = "eat leading whitespace"
|
||||
|
||||
for c in str:
|
||||
|
||||
## handle escaped charactors
|
||||
if not escaped and c == "\\":
|
||||
escaped = 1
|
||||
continue
|
||||
|
||||
## strip leading white space
|
||||
if state == "eat leading whitespace":
|
||||
if c in string.whitespace:
|
||||
continue
|
||||
else:
|
||||
state = "get command or data"
|
||||
|
||||
## parse to '"' or ","
|
||||
if state == "get command or data":
|
||||
|
||||
## just add escaped charactors
|
||||
if escaped:
|
||||
escaped = 0
|
||||
temp = temp + c
|
||||
continue
|
||||
|
||||
## the data is in quotes after the command
|
||||
elif c == "\"":
|
||||
cmd = temp
|
||||
temp = ""
|
||||
state = "get quoted data"
|
||||
continue
|
||||
|
||||
## this tells us there was no quoted data, therefore no
|
||||
## command; add the command and start over
|
||||
elif c == ",":
|
||||
## strip ending whitespace on un-quoted data
|
||||
temp = string.rstrip(temp)
|
||||
return_list.append( ("", temp) )
|
||||
temp = ""
|
||||
state = "eat leading whitespace"
|
||||
continue
|
||||
|
||||
## record the data
|
||||
else:
|
||||
temp = temp + c
|
||||
continue
|
||||
|
||||
## parse until ending '"'
|
||||
if state == "get quoted data":
|
||||
|
||||
## just add escaped charactors
|
||||
if escaped:
|
||||
escaped = 0
|
||||
temp = temp + c
|
||||
continue
|
||||
|
||||
## look for ending '"'
|
||||
elif c == "\"":
|
||||
return_list.append( (cmd, temp) )
|
||||
cmd = ""
|
||||
temp = ""
|
||||
state = "eat comma after quotes"
|
||||
continue
|
||||
|
||||
## record the data
|
||||
else:
|
||||
temp = temp + c
|
||||
continue
|
||||
|
||||
|
||||
## parse until ","
|
||||
if state == "eat comma after quotes":
|
||||
if c in string.whitespace:
|
||||
continue
|
||||
|
||||
elif c == ",":
|
||||
state = "eat leading whitespace"
|
||||
continue
|
||||
|
||||
else:
|
||||
print "format error"
|
||||
sys.exit(1)
|
||||
|
||||
if cmd or temp:
|
||||
return_list.append( (cmd, temp) )
|
||||
|
||||
return return_list
|
||||
|
||||
|
||||
def decode_command(cmd):
|
||||
if cmd == "r":
|
||||
return "regex"
|
||||
elif cmd == "l":
|
||||
return "like"
|
||||
else:
|
||||
return "exact"
|
||||
|
||||
|
||||
def FormToCheckinQuery(form):
|
||||
query = cvsdbapi.CreateCheckinQuery()
|
||||
|
||||
if form.has_key("repository"):
|
||||
temp = form["repository"].value
|
||||
query.SetRepository(temp)
|
||||
for cmd, str in listparse_string(temp):
|
||||
cmd = decode_command(cmd)
|
||||
query.SetRepository(str, cmd)
|
||||
|
||||
if form.has_key("branch"):
|
||||
temp = form["branch"].value
|
||||
query.SetBranch(temp)
|
||||
for cmd, str in listparse_string(temp):
|
||||
cmd = decode_command(cmd)
|
||||
query.SetBranch(str, cmd)
|
||||
|
||||
if form.has_key("directory"):
|
||||
temp = form["directory"].value
|
||||
query.SetDirectory(temp)
|
||||
for cmd, str in listparse_string(temp):
|
||||
cmd = decode_command(cmd)
|
||||
query.SetDirectory(str, cmd)
|
||||
|
||||
if form.has_key("file"):
|
||||
temp = form["file"].value
|
||||
query.SetFile(temp)
|
||||
for cmd, str in listparse_string(temp):
|
||||
cmd = decode_command(cmd)
|
||||
query.SetFile(str, cmd)
|
||||
|
||||
if form.has_key("who"):
|
||||
temp = form["who"].value
|
||||
query.SetAuthor(temp)
|
||||
for cmd, str in listparse_string(temp):
|
||||
cmd = decode_command(cmd)
|
||||
query.SetAuthor(str, cmd)
|
||||
|
||||
if form.has_key("sortby"):
|
||||
temp = form["sortby"].value
|
||||
if temp == "date":
|
||||
query.SetSortMethod(query.SORT_DATE)
|
||||
query.SetSortMethod("date")
|
||||
elif temp == "author":
|
||||
query.SetSortMethod(query.SORT_AUTHOR)
|
||||
query.SetSortMethod("author")
|
||||
else:
|
||||
query.SetSortMethod(query.SORT_FILE)
|
||||
query.SetSortMethod("file")
|
||||
|
||||
if form.has_key("date"):
|
||||
temp = form["date"].value
|
||||
@@ -180,10 +297,9 @@ class HTMLTemplate:
|
||||
|
||||
|
||||
def Main():
|
||||
HTMLHeader()
|
||||
print "Content-type: text/html\n\n"
|
||||
|
||||
template_path = os.path.join(
|
||||
_viewcvs_root, "html-templates", "querytemplate.html")
|
||||
template_path = os.path.join(HTML_TEMPLATE_DIR, "querytemplate.html")
|
||||
template = HTMLTemplate(template_path)
|
||||
template.Print1()
|
||||
|
||||
|
@@ -1,46 +1,42 @@
|
||||
#!/usr/bin/python
|
||||
# -*- Mode: python -*-
|
||||
#
|
||||
# nothing here yet!
|
||||
# Copyright (C) 2000-2001 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
# Copyright (C) 2000 Jay Painter. 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.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth below:
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# 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.
|
||||
# Contact information:
|
||||
# Greg Stein, PO Box 760, Palo Alto, CA, 94302
|
||||
# gstein@lyra.org, http://viewcvs.sourceforge.net/
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
# For tracking purposes, this software is identified by:
|
||||
# $Id$
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
## BOOTSTRAP
|
||||
import sys, os, string
|
||||
_viewcvs_root = string.strip(open("/etc/viewcvs/root", "r").read())
|
||||
sys.path.append(os.path.join(_viewcvs_root, "lib"))
|
||||
##
|
||||
#########################################################################
|
||||
#
|
||||
# INSTALL-TIME CONFIGURATION
|
||||
#
|
||||
# These values will be set during the installation process. During
|
||||
# development, they will remain None.
|
||||
#
|
||||
|
||||
LIBRARY_DIR = None
|
||||
CONF_PATHNAME = None
|
||||
HTML_TEMPLATE_DIR = None
|
||||
|
||||
# Adjust sys.path to include our library directory
|
||||
import sys
|
||||
|
||||
if LIBRARY_DIR:
|
||||
sys.path.insert(0, LIBRARY_DIR)
|
||||
else:
|
||||
sys.path[:0] = ['../lib'] # any other places to look?
|
||||
|
||||
#########################################################################
|
||||
|
||||
import os, string
|
||||
|
||||
|
||||
def HTMLHeader():
|
||||
@@ -50,8 +46,7 @@ def HTMLHeader():
|
||||
|
||||
def Main():
|
||||
HTMLHeader()
|
||||
template_path = os.path.join(
|
||||
_viewcvs_root, "html-templates", "queryformtemplate.html")
|
||||
template_path = os.path.join(HTML_TEMPLATE_DIR, "queryformtemplate.html")
|
||||
print open(template_path, "r").read()
|
||||
|
||||
|
||||
|
2128
cgi/viewcvs.cgi
2128
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/
|
||||
#
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
# long_intro
|
||||
# repository_info
|
||||
#
|
||||
# use_enscript
|
||||
#
|
||||
# For Python source colorization:
|
||||
#
|
||||
# py2html_path
|
||||
@@ -94,19 +96,55 @@ main_title = CVS Repository
|
||||
# This should contain a list of modules in the repository that should not be
|
||||
# displayed (by default or by explicit path specification).
|
||||
#
|
||||
# This configuration can be a simple list of modules, or it can get quite
|
||||
# complex:
|
||||
#
|
||||
# *) The "!" can be used before a module to explicitly state that it
|
||||
# is NOT forbidden. Whenever this form is seen, then all modules will
|
||||
# be forbidden unless one of the "!" modules match.
|
||||
#
|
||||
# *) Shell-style "glob" expressions may be used. "*" will match any
|
||||
# sequence of zero or more characters, "?" will match any single
|
||||
# character, "[seq]" will match any character in seq, and "[!seq]"
|
||||
# will match any character not in seq.
|
||||
#
|
||||
# *) Tests are performed in sequence. The first match will terminate the
|
||||
# testing. This allows for more complex allow/deny patterns.
|
||||
#
|
||||
# Tests are case-sensitive.
|
||||
#
|
||||
forbidden =
|
||||
# forbidden = example
|
||||
# forbidden = example1, example2
|
||||
|
||||
# Some examples:
|
||||
#
|
||||
# Disallow "example" but allow all others:
|
||||
# forbidden = example
|
||||
#
|
||||
# Disallow "example1" and "example2" but allow all others:
|
||||
# forbidden = example1, example2
|
||||
#
|
||||
# Allow *only* "example1" and "example2":
|
||||
# forbidden = !example1, !example2
|
||||
#
|
||||
# Forbid modules starting with "x":
|
||||
# forbidden = x*
|
||||
#
|
||||
# Allow modules starting with "x" but no others:
|
||||
# forbidden = !x*
|
||||
#
|
||||
# Allow "xml", forbid other modules starting with "x", and allow the rest:
|
||||
# forbidden = !xml, x*, !*
|
||||
#
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
[cvsdb]
|
||||
|
||||
host = master
|
||||
database_name = bonsai
|
||||
user = root
|
||||
passwd = ogieta50
|
||||
readonly_user = root
|
||||
readonly_passwd = ogieta50
|
||||
#host = localhost
|
||||
#database_name = ViewCVS
|
||||
#user =
|
||||
#passwd =
|
||||
#readonly_user =
|
||||
#readonly_passwd =
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
[images]
|
||||
@@ -202,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
|
||||
@@ -214,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>.
|
||||
@@ -224,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>
|
||||
@@ -307,7 +345,7 @@ hr_ignore_keyword_subst = 1
|
||||
# NOTE: this requires rw-access to the CVSROOT/history file, and rw-access to
|
||||
# the subdirectory to place the lock... so you maybe don't want it
|
||||
# WARNING: this is not yet implemented!!
|
||||
allow_annotate = 0
|
||||
allow_annotate = 1
|
||||
|
||||
# allow pretty-printed version of files
|
||||
allow_markup = 1
|
||||
@@ -378,4 +416,59 @@ diff_font_size = -1
|
||||
# the width of the textinput in the request-diff-form
|
||||
input_text_size = 12
|
||||
|
||||
# should we use 'enscript' for syntax coloring?
|
||||
use_enscript = 0
|
||||
|
||||
#
|
||||
# if the enscript program is not on the path, set this value
|
||||
# Note: there should be a trailing slash
|
||||
#
|
||||
enscript_path =
|
||||
# enscript_path = /usr/bin/
|
||||
|
||||
#
|
||||
# ViewCVS has its own set of mappings from filename extensions and filenames
|
||||
# to languages. If the language is not supported by enscript, then it can
|
||||
# be listed here to disable the use of enscript.
|
||||
#
|
||||
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
|
||||
|
||||
# vhost1 = glob1, glob2
|
||||
# vhost2 = glob3, glob4
|
||||
|
||||
# [vhost1-section]
|
||||
# option = value
|
||||
# [vhost1-othersection]
|
||||
# option = value
|
||||
# [vhost2-section]
|
||||
# option = value
|
||||
|
||||
#
|
||||
# Here is an example:
|
||||
#
|
||||
# [vhosts]
|
||||
# lyra = *lyra.org
|
||||
#
|
||||
# [lyra-general]
|
||||
# forbidden = hideme
|
||||
#
|
||||
# [lyra-options]
|
||||
# show_logs = 0
|
||||
#
|
||||
# Note that "lyra" is the "canonical" name for all hosts in the lyra.org
|
||||
# domain. This canonical name is then used within the additional, vhost-
|
||||
# specific sections to override specific values in the common sections.
|
||||
#
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
576
lib/blame.py
Normal file
576
lib/blame.py
Normal file
@@ -0,0 +1,576 @@
|
||||
#!/usr/local/bin/python
|
||||
# -*-python-*-
|
||||
#
|
||||
# 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://viewcvs.sourceforge.net/license-1.html.
|
||||
#
|
||||
# Contact information:
|
||||
# Greg Stein, PO Box 760, Palo Alto, CA, 94302
|
||||
# gstein@lyra.org, http://viewcvs.sourceforge.net/
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
# blame.py: Annotate each line of a CVS file with its author,
|
||||
# revision #, date, etc.
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
# This software is being maintained as part of the ViewCVS project.
|
||||
# Information is available at:
|
||||
# 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
|
||||
# information about Bonsai can be found at
|
||||
# http://www.mozilla.org/bonsai.html
|
||||
#
|
||||
# cvsblame.pl, in turn, was based on Scott Furman's cvsblame script
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
|
||||
import string
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
import math
|
||||
import cgi
|
||||
import rcsparse
|
||||
|
||||
class CVSParser(rcsparse.Sink):
|
||||
# Precompiled regular expressions
|
||||
trunk_rev = re.compile('^[0-9]+\\.[0-9]+$')
|
||||
last_branch = re.compile('(.*)\\.[0-9]+')
|
||||
is_branch = re.compile('(.*)\\.0\\.([0-9]+)')
|
||||
d_command = re.compile('^d(\d+)\\s(\\d+)')
|
||||
a_command = re.compile('^a(\d+)\\s(\\d+)')
|
||||
|
||||
SECONDS_PER_DAY = 86400
|
||||
|
||||
def __init__(self):
|
||||
self.Reset()
|
||||
|
||||
def Reset(self):
|
||||
self.last_revision = {}
|
||||
self.prev_revision = {}
|
||||
self.revision_date = {}
|
||||
self.revision_author = {}
|
||||
self.revision_branches = {}
|
||||
self.next_delta = {}
|
||||
self.prev_delta = {}
|
||||
self.tag_revision = {}
|
||||
self.revision_symbolic_name = {}
|
||||
self.timestamp = {}
|
||||
self.revision_ctime = {}
|
||||
self.revision_age = {}
|
||||
self.revision_log = {}
|
||||
self.revision_deltatext = {}
|
||||
self.revision_map = []
|
||||
self.lines_added = {}
|
||||
self.lines_removed = {}
|
||||
|
||||
# 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.
|
||||
def map_tag_to_revision(self, tag_or_revision):
|
||||
try:
|
||||
revision = self.tag_revision[tag_or_revision]
|
||||
match = self.is_branch.match(revision)
|
||||
if match:
|
||||
branch = match.group(1) + '.' + match.group(2)
|
||||
if self.last_revision.get(branch):
|
||||
return self.last_revision[branch]
|
||||
else:
|
||||
return match.group(1)
|
||||
else:
|
||||
return revision
|
||||
except:
|
||||
return ''
|
||||
|
||||
# Construct an ordered list of ancestor revisions to the given
|
||||
# revision, starting with the immediate ancestor and going back
|
||||
# to the primordial revision (1.1).
|
||||
#
|
||||
# Note: The generated path does not traverse the tree the same way
|
||||
# that the individual revision deltas do. In particular,
|
||||
# the path traverses the tree "backwards" on branches.
|
||||
def ancestor_revisions(self, revision):
|
||||
ancestors = []
|
||||
revision = self.prev_revision.get(revision)
|
||||
while revision:
|
||||
ancestors.append(revision)
|
||||
revision = self.prev_revision.get(revision)
|
||||
|
||||
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):
|
||||
path = []
|
||||
add_lines_remaining = 0
|
||||
start_line = 0
|
||||
count = 0
|
||||
while revision:
|
||||
path.append(revision)
|
||||
revision = self.prev_delta.get(revision)
|
||||
path.reverse()
|
||||
path = path[1:] # Get rid of head revision
|
||||
|
||||
text = self.deltatext_split(self.head_revision)
|
||||
|
||||
# Iterate, applying deltas to previous revision
|
||||
for revision in path:
|
||||
adjust = 0
|
||||
diffs = self.deltatext_split(revision)
|
||||
self.lines_added[revision] = 0
|
||||
self.lines_removed[revision] = 0
|
||||
lines_added_now = 0
|
||||
lines_removed_now = 0
|
||||
|
||||
for command in diffs:
|
||||
dmatch = self.d_command.match(command)
|
||||
amatch = self.a_command.match(command)
|
||||
if add_lines_remaining > 0:
|
||||
# Insertion lines from a prior "a" command
|
||||
text.insert(start_line + adjust, command)
|
||||
add_lines_remaining = add_lines_remaining - 1
|
||||
adjust = adjust + 1
|
||||
elif dmatch:
|
||||
# "d" - Delete command
|
||||
start_line = string.atoi(dmatch.group(1))
|
||||
count = string.atoi(dmatch.group(2))
|
||||
begin = start_line + adjust - 1
|
||||
del text[begin:begin + count]
|
||||
adjust = adjust - count
|
||||
lines_removed_now = lines_removed_now + count
|
||||
elif amatch:
|
||||
# "a" - Add command
|
||||
start_line = string.atoi(amatch.group(1))
|
||||
count = string.atoi(amatch.group(2))
|
||||
add_lines_remaining = count
|
||||
lines_added_now = lines_added_now + count
|
||||
else:
|
||||
raise RuntimeError, 'Error parsing diff commands'
|
||||
|
||||
self.lines_added[revision] = self.lines_added[revision] + lines_added_now
|
||||
self.lines_removed[revision] = self.lines_removed[revision] + lines_removed_now
|
||||
return text
|
||||
|
||||
def set_head_revision(self, revision):
|
||||
self.head_revision = revision
|
||||
|
||||
def set_principal_branch(self, branch_name):
|
||||
self.principal_branch = branch_name
|
||||
|
||||
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
|
||||
|
||||
### actually, this is a bit bogus... a rev can have multiple names
|
||||
self.revision_symbolic_name[revision] = name
|
||||
|
||||
def set_comment(self, comment):
|
||||
self.file_description = comment
|
||||
|
||||
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.
|
||||
#
|
||||
# The following dicts are created, keyed by revision number:
|
||||
# self.revision_date -- e.g. "96.02.23.00.21.52"
|
||||
# self.timestamp -- seconds since 12:00 AM, Jan 1, 1970 GMT
|
||||
# self.revision_author -- e.g. "tom"
|
||||
# self.revision_branches -- descendant branch revisions, separated by spaces,
|
||||
# e.g. "1.21.4.1 1.21.2.6.1"
|
||||
# self.prev_revision -- revision number of previous *ancestor* in RCS tree.
|
||||
# Traversal of this array occurs in the direction
|
||||
# of the primordial (1.1) revision.
|
||||
# self.prev_delta -- revision number of previous revision which forms
|
||||
# the basis for the edit commands in this revision.
|
||||
# This causes the tree to be traversed towards the
|
||||
# trunk when on a branch, and towards the latest trunk
|
||||
# revision when on the trunk.
|
||||
# self.next_delta -- revision number of next "delta". Inverts prev_delta.
|
||||
#
|
||||
# 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
|
||||
|
||||
#self.revision_date[revision] = date
|
||||
self.timestamp[revision] = timestamp
|
||||
|
||||
# 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
|
||||
if is_trunk_revision:
|
||||
self.prev_revision[revision] = next
|
||||
else:
|
||||
self.prev_revision[next] = revision
|
||||
|
||||
# Construct associative arrays containing info about individual revisions.
|
||||
#
|
||||
# The following associative arrays are created, keyed by revision number:
|
||||
# revision_log -- log message
|
||||
# revision_deltatext -- Either the complete text of the revision,
|
||||
# in the case of the head revision, or the
|
||||
# encoded delta between this revision and another.
|
||||
# The delta is either with respect to the successor
|
||||
# revision if this revision is on the trunk or
|
||||
# relative to its immediate predecessor if this
|
||||
# revision is on a branch.
|
||||
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
|
||||
# opt_m - time since modified
|
||||
# Args out: revision_map
|
||||
# timestamp
|
||||
# revision_deltatext
|
||||
|
||||
# CheckHidden(rcs_pathname);
|
||||
try:
|
||||
rcsfile = open(rcs_pathname, 'r')
|
||||
except:
|
||||
raise RuntimeError, ('error: %s appeared to be under CVS control, ' +
|
||||
'but the RCS file is inaccessible.') % rcs_pathname
|
||||
|
||||
rcsparse.Parser().parse(rcsfile, self)
|
||||
rcsfile.close()
|
||||
|
||||
if opt_rev in [None, '', 'HEAD']:
|
||||
# Explicitly specified topmost revision in tree
|
||||
revision = self.head_revision
|
||||
else:
|
||||
# Symbolic tag or specific revision number specified.
|
||||
revision = self.map_tag_to_revision(opt_rev)
|
||||
if revision == '':
|
||||
raise RuntimeError, 'error: -r: No such revision: ' + opt_rev
|
||||
|
||||
# The primordial revision is not always 1.1! Go find it.
|
||||
primordial = revision
|
||||
while self.prev_revision.get(primordial):
|
||||
primordial = self.prev_revision[primordial]
|
||||
|
||||
# Don't display file at all, if -m option is specified and no
|
||||
# changes have been made in the specified file.
|
||||
if opt_m_timestamp and self.timestamp[revision] < opt_m_timestamp:
|
||||
return ''
|
||||
|
||||
# Figure out how many lines were in the primordial, i.e. version 1.1,
|
||||
# check-in by moving backward in time from the head revision to the
|
||||
# first revision.
|
||||
line_count = 0
|
||||
if self.revision_deltatext.get(self.head_revision):
|
||||
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 = self.deltatext_split(rev)
|
||||
for command in diffs:
|
||||
dmatch = self.d_command.match(command)
|
||||
amatch = self.a_command.match(command)
|
||||
if skip > 0:
|
||||
# Skip insertion lines from a prior "a" command
|
||||
skip = skip - 1
|
||||
elif dmatch:
|
||||
# "d" - Delete command
|
||||
start_line = string.atoi(dmatch.group(1))
|
||||
count = string.atoi(dmatch.group(2))
|
||||
line_count = line_count - count
|
||||
elif amatch:
|
||||
# "a" - Add command
|
||||
start_line = string.atoi(amatch.group(1))
|
||||
count = string.atoi(amatch.group(2))
|
||||
skip = count;
|
||||
line_count = line_count + count
|
||||
else:
|
||||
raise RuntimeError, 'error: illegal RCS file'
|
||||
|
||||
rev = self.prev_revision.get(rev)
|
||||
|
||||
# Now, play the delta edit commands *backwards* from the primordial
|
||||
# revision forward, but rather than applying the deltas to the text of
|
||||
# each revision, apply the changes to an array of revision numbers.
|
||||
# This creates a "revision map" -- an array where each element
|
||||
# represents a line of text in the given revision but contains only
|
||||
# the revision number in which the line was introduced rather than
|
||||
# the line text itself.
|
||||
#
|
||||
# Note: These are backward deltas for revisions on the trunk and
|
||||
# forward deltas for branch revisions.
|
||||
|
||||
# Create initial revision map for primordial version.
|
||||
self.revision_map = [primordial] * line_count
|
||||
|
||||
ancestors = [revision, ] + self.ancestor_revisions(revision)
|
||||
ancestors = ancestors[:-1] # Remove "1.1"
|
||||
last_revision = primordial
|
||||
ancestors.reverse()
|
||||
for revision in ancestors:
|
||||
is_trunk_revision = self.trunk_rev.match(revision) is not None
|
||||
|
||||
if is_trunk_revision:
|
||||
diffs = self.deltatext_split(last_revision)
|
||||
|
||||
# Revisions on the trunk specify deltas that transform a
|
||||
# revision into an earlier revision, so invert the translation
|
||||
# of the 'diff' commands.
|
||||
for command in diffs:
|
||||
if skip > 0:
|
||||
skip = skip - 1
|
||||
else:
|
||||
dmatch = self.d_command.match(command)
|
||||
amatch = self.a_command.match(command)
|
||||
if dmatch:
|
||||
start_line = string.atoi(dmatch.group(1))
|
||||
count = string.atoi(dmatch.group(2))
|
||||
temp = []
|
||||
while count > 0:
|
||||
temp.append(revision)
|
||||
count = count - 1
|
||||
self.revision_map = (self.revision_map[:start_line - 1] +
|
||||
temp + self.revision_map[start_line - 1:])
|
||||
elif amatch:
|
||||
start_line = string.atoi(amatch.group(1))
|
||||
count = string.atoi(amatch.group(2))
|
||||
del self.revision_map[start_line:start_line + count]
|
||||
skip = count
|
||||
else:
|
||||
raise RuntimeError, 'Error parsing diff commands'
|
||||
|
||||
else:
|
||||
# Revisions on a branch are arranged backwards from those on
|
||||
# the trunk. They specify deltas that transform a revision
|
||||
# into a later revision.
|
||||
adjust = 0
|
||||
diffs = self.deltatext_split(revision)
|
||||
for command in diffs:
|
||||
if skip > 0:
|
||||
skip = skip - 1
|
||||
else:
|
||||
dmatch = self.d_command.match(command)
|
||||
amatch = self.a_command.match(command)
|
||||
if dmatch:
|
||||
start_line = string.atoi(dmatch.group(1))
|
||||
count = string.atoi(dmatch.group(2))
|
||||
del self.revision_map[start_line + adjust - 1:start_line + adjust - 1 + count]
|
||||
adjust = adjust - count
|
||||
elif amatch:
|
||||
start_line = string.atoi(amatch.group(1))
|
||||
count = string.atoi(amatch.group(2))
|
||||
skip = count
|
||||
temp = []
|
||||
while count > 0:
|
||||
temp.append(revision)
|
||||
count = count - 1
|
||||
self.revision_map = (self.revision_map[:start_line + adjust] +
|
||||
temp + self.revision_map[start_line + adjust:])
|
||||
adjust = adjust + skip
|
||||
else:
|
||||
raise RuntimeError, 'Error parsing diff commands'
|
||||
|
||||
last_revision = revision
|
||||
|
||||
return revision
|
||||
|
||||
|
||||
re_includes = re.compile('\\#(\\s*)include(\\s*)"(.*?)"')
|
||||
re_filename = re.compile('(.*[\\\\/])?(.+)')
|
||||
|
||||
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.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), url, incfile)
|
||||
return text
|
||||
|
||||
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)
|
||||
text = parser.extract_revision(revision)
|
||||
if len(text) != count:
|
||||
raise RuntimeError, 'Internal consistency error'
|
||||
|
||||
match = re_filename.match(rcs_path)
|
||||
if not match:
|
||||
raise RuntimeError, 'Unable to parse filename'
|
||||
file_head = match.group(1)
|
||||
file_tail = match.group(2)
|
||||
|
||||
open_table_tag = '<table border=0 cellpadding=0 cellspacing=0 width="100%">'
|
||||
startOfRow = '<tr><td colspan=3%s><pre>'
|
||||
endOfRow = '</td></tr>'
|
||||
|
||||
print open_table_tag + (startOfRow % '')
|
||||
|
||||
if count == 0:
|
||||
count = 1
|
||||
|
||||
line_num_width = int(math.log10(count)) + 1
|
||||
revision_width = 3
|
||||
author_width = 5
|
||||
line = 0
|
||||
usedlog = {}
|
||||
usedlog[revision] = 1
|
||||
old_revision = 0
|
||||
row_color = ''
|
||||
lines_in_table = 0
|
||||
inMark = 0
|
||||
rev_count = 0
|
||||
|
||||
for revision in parser.revision_map:
|
||||
thisline = text[line]
|
||||
line = line + 1
|
||||
usedlog[revision] = 1
|
||||
line_in_table = lines_in_table + 1
|
||||
|
||||
# Escape HTML meta-characters
|
||||
thisline = cgi.escape(thisline)
|
||||
|
||||
# Add a link to traverse to included files
|
||||
if 1: # opt_includes
|
||||
thisline = link_includes(thisline, root, file_head, sticky)
|
||||
|
||||
output = ''
|
||||
|
||||
# Highlight lines
|
||||
#mark_cmd;
|
||||
#if (defined($mark_cmd = $mark_line{$line}) and mark_cmd != 'end':
|
||||
# output = output + endOfRow + '<tr><td bgcolor=LIGHTGREEN width="100%"><pre>'
|
||||
# inMark = 1
|
||||
|
||||
if old_revision != revision and line != 1:
|
||||
if row_color == '':
|
||||
row_color = ' bgcolor="#e7e7e7"'
|
||||
else:
|
||||
row_color = ''
|
||||
|
||||
if not inMark:
|
||||
if lines_in_table > 100:
|
||||
output = output + endOfRow + '</table>' + open_table_tag + (startOfRow % row_color)
|
||||
lines_in_table = 0
|
||||
else:
|
||||
output = output + endOfRow + (startOfRow % row_color)
|
||||
|
||||
elif lines_in_table > 200 and not inMark:
|
||||
output = output + endOfRow + '</table>' + open_table_tag + (startOfRow % row_color)
|
||||
lines_in_table = 0
|
||||
|
||||
output = output + "<a name=%d></a>" % (line, )
|
||||
if 1: # opt_line_nums
|
||||
output = output + ('%%%dd' % (line_num_width, )) % (line, )
|
||||
|
||||
if old_revision != revision or rev_count > 20:
|
||||
revision_width = max(revision_width, len(revision))
|
||||
|
||||
if parser.prev_revision.get(revision):
|
||||
fname = file_tail[:-2] # strip the ",v"
|
||||
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)
|
||||
output = output + ">"
|
||||
else:
|
||||
output = output + " "
|
||||
parser.prev_revision[revision] = ''
|
||||
|
||||
author = parser.revision_author[revision]
|
||||
# $author =~ s/%.*$//;
|
||||
author_width = max(author_width, len(author))
|
||||
output = output + ('%%-%ds ' % (author_width, )) % (author, )
|
||||
output = output + revision
|
||||
if parser.prev_revision.get(revision):
|
||||
output = output + '</a>'
|
||||
output = output + (' ' * (revision_width - len(revision) + 1))
|
||||
|
||||
old_revision = revision
|
||||
rev_count = 0
|
||||
else:
|
||||
output = output + ' ' + (' ' * (author_width + revision_width))
|
||||
rev_count = rev_count + 1
|
||||
|
||||
output = output + thisline
|
||||
|
||||
# Close the highlighted section
|
||||
#if (defined $mark_cmd and mark_cmd != 'begin'):
|
||||
# chop($output)
|
||||
# output = output + endOfRow + (startOfRow % row_color)
|
||||
# inMark = 0
|
||||
|
||||
print output
|
||||
print endOfRow + '</table>'
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 3:
|
||||
print 'USAGE: %s cvsroot rcs-file' % sys.argv[0]
|
||||
sys.exit(1)
|
||||
make_html(sys.argv[1], sys.argv[2])
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
48
lib/cfg.py
48
lib/cfg.py
@@ -1,48 +0,0 @@
|
||||
# -*- Mode: python -*-
|
||||
# -----------------------------------------------------------------------
|
||||
# Copyright (C) 2000 Jay Painter. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth below:
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
# For tracking purposes, this software is identified by:
|
||||
# $Id$
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
class EmptyNameSpace:
|
||||
"Placehoder empty class for nesting namespaces."
|
||||
pass
|
||||
|
||||
|
||||
|
||||
## [cvsdb]
|
||||
cvsdb = EmptyNameSpace()
|
||||
cvsdb.host = 'master'
|
||||
cvsdb.database_name = 'bonsai'
|
||||
cvsdb.user = 'root'
|
||||
cvsdb.passwd = 'ogieta50'
|
||||
cvsdb.readonly_user = 'root'
|
||||
cvsdb.readonly_passwd = 'ogieta50'
|
@@ -1,36 +1,17 @@
|
||||
# -*- Mode: python -*-
|
||||
# -----------------------------------------------------------------------
|
||||
# Copyright (C) 2000 Jay Painter. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth below:
|
||||
# Copyright (C) 2000-2001 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://viewcvs.sourceforge.net/license-1.html.
|
||||
#
|
||||
# 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.
|
||||
# Contact information:
|
||||
# Greg Stein, PO Box 760, Palo Alto, CA, 94302
|
||||
# gstein@lyra.org, http://viewcvs.sourceforge.net/
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
# For tracking purposes, this software is identified by:
|
||||
# $Id$
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
import os
|
||||
|
||||
|
@@ -1,13 +1,28 @@
|
||||
# -*- Mode: python -*-
|
||||
#
|
||||
# ### add license stuff
|
||||
# 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/
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
# compat.py: compatibility functions for operation across Python 1.5.x
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
|
||||
import urllib
|
||||
import string
|
||||
import time
|
||||
import re
|
||||
import os
|
||||
|
||||
|
||||
#
|
||||
# urllib.urlencode() is new to Python 1.5.2
|
||||
@@ -23,18 +38,31 @@ 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
|
||||
#
|
||||
if hasattr(time, 'strptime'):
|
||||
def cvs_strptime(timestr):
|
||||
'Parse a CVS-style date/time value.'
|
||||
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 cvs_strptime(timestr):
|
||||
'Parse a CVS-style date/time value.'
|
||||
matches = _re_rev_date.match(timestr).groups()
|
||||
return tuple(map(int, matches)) + (0, 1, -1)
|
||||
cvs_strptime.__doc__ = 'Parse a CVS-style date/time value.'
|
||||
return tuple(map(int, matches)) + (0, 1, 0)
|
||||
|
||||
#
|
||||
# os.makedirs() is new to Python 1.5.2
|
||||
#
|
||||
try:
|
||||
makedirs = os.makedirs
|
||||
except AttributeError:
|
||||
def makedirs(path, mode=0777):
|
||||
head, tail = os.path.split(path)
|
||||
if head and tail and not os.path.exists(head):
|
||||
makedirs(head, mode)
|
||||
os.mkdir(path, mode)
|
||||
|
118
lib/config.py
118
lib/config.py
@@ -1,13 +1,27 @@
|
||||
# -*- Mode: python -*-
|
||||
#
|
||||
# ### add license stuff
|
||||
# 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/
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
# config.py: configuration utilities
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
|
||||
import sys
|
||||
import os
|
||||
import string
|
||||
import ConfigParser
|
||||
import fnmatch
|
||||
|
||||
|
||||
#########################################################################
|
||||
@@ -28,41 +42,71 @@ import ConfigParser
|
||||
|
||||
class Config:
|
||||
_sections = ('general', 'images', 'options', 'colors', 'text', 'cvsdb')
|
||||
_force_multi_value = ('cvs_roots', 'forbidden', 'even_odd')
|
||||
_force_multi_value = ('cvs_roots', 'forbidden', 'even_odd',
|
||||
'disable_enscript_lang')
|
||||
|
||||
def __init__(self):
|
||||
for section in self._sections:
|
||||
setattr(self, section, _sub_config())
|
||||
|
||||
def load_config(self, fname):
|
||||
def load_config(self, fname, vhost=None):
|
||||
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
|
||||
if parser.has_section(section):
|
||||
self._process_section(parser, section, section)
|
||||
|
||||
sc = getattr(self, section)
|
||||
if vhost:
|
||||
self._process_vhost(parser, vhost)
|
||||
|
||||
for opt in parser.options(section):
|
||||
value = parser.get(section, opt)
|
||||
if opt in self._force_multi_value or section == 'images':
|
||||
value = map(string.strip, string.split(value, ','))
|
||||
else:
|
||||
try:
|
||||
value = int(value)
|
||||
except ValueError:
|
||||
pass
|
||||
def _process_section(self, parser, section, subcfg_name):
|
||||
sc = getattr(self, subcfg_name)
|
||||
|
||||
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)
|
||||
for opt in parser.options(section):
|
||||
value = parser.get(section, opt)
|
||||
if opt in self._force_multi_value or subcfg_name == 'images':
|
||||
value = map(string.strip, filter(None, 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)
|
||||
|
||||
def _process_vhost(self, parser, vhost):
|
||||
canon_vhost = self._find_canon_vhost(parser, vhost)
|
||||
if not canon_vhost:
|
||||
# none of the vhost sections matched
|
||||
return
|
||||
|
||||
cv = canon_vhost + '-'
|
||||
lcv = len(cv)
|
||||
for section in parser.sections():
|
||||
if section[:lcv] == cv:
|
||||
self._process_section(parser, section, section[lcv:])
|
||||
|
||||
def _find_canon_vhost(self, parser, vhost):
|
||||
vhost = string.lower(vhost)
|
||||
|
||||
for canon_vhost in parser.options('vhosts'):
|
||||
value = parser.get('vhosts', canon_vhost)
|
||||
patterns = map(string.lower, map(string.strip,
|
||||
filter(None, string.split(value, ','))))
|
||||
for pat in patterns:
|
||||
if fnmatch.fnmatchcase(vhost, pat):
|
||||
return canon_vhost
|
||||
|
||||
return None
|
||||
|
||||
def set_defaults(self):
|
||||
"Set some default values in the configuration."
|
||||
@@ -142,6 +186,10 @@ class Config:
|
||||
self.options.diff_font_face = 'Helvetica,Arial'
|
||||
self.options.diff_font_size = -1
|
||||
self.options.input_text_size = 12
|
||||
self.options.use_enscript = 0
|
||||
self.options.enscript_path = ''
|
||||
self.options.disable_enscript_lang = ()
|
||||
self.options.allow_tar = 0
|
||||
|
||||
self.text.long_intro = """\
|
||||
<p>
|
||||
@@ -158,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
|
||||
@@ -169,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>.
|
||||
@@ -181,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>
|
||||
@@ -204,6 +252,19 @@ class Config:
|
||||
</p>
|
||||
"""
|
||||
|
||||
def is_forbidden(self, module):
|
||||
if not module:
|
||||
return 0
|
||||
default = 0
|
||||
for pat in self.general.forbidden:
|
||||
if pat[0] == '!':
|
||||
default = 1
|
||||
if fnmatch.fnmatchcase(module, pat[1:]):
|
||||
return 0
|
||||
elif fnmatch.fnmatchcase(module, pat):
|
||||
return 1
|
||||
return default
|
||||
|
||||
class _sub_config:
|
||||
def get_image(self, which):
|
||||
text = '[%s]' % string.upper(which)
|
||||
@@ -212,3 +273,10 @@ class _sub_config:
|
||||
return '<img src="%s" alt="%s" border=0 width=%s height=%s>' % \
|
||||
(path, text, width, height)
|
||||
return text
|
||||
|
||||
if not hasattr(sys, 'hexversion'):
|
||||
# Python 1.5 or 1.5.1. fix the syntax for ConfigParser options.
|
||||
import regex
|
||||
ConfigParser.option_cre = regex.compile('^\([-A-Za-z0-9._]+\)\(:\|['
|
||||
+ string.whitespace
|
||||
+ ']*=\)\(.*\)$')
|
||||
|
@@ -1,45 +1,44 @@
|
||||
# -*- Mode: python -*-
|
||||
# -----------------------------------------------------------------------
|
||||
# Copyright (C) 2000 Jay Painter. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth below:
|
||||
# Copyright (C) 2000-2001 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://viewcvs.sourceforge.net/license-1.html.
|
||||
#
|
||||
# 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.
|
||||
# Contact information:
|
||||
# Greg Stein, PO Box 760, Palo Alto, CA, 94302
|
||||
# gstein@lyra.org, http://viewcvs.sourceforge.net/
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
# For tracking purposes, this software is identified by:
|
||||
# $Id$
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
import os, cfg, database, rlog, commit
|
||||
#########################################################################
|
||||
#
|
||||
# INSTALL-TIME CONFIGURATION
|
||||
#
|
||||
# These values will be set during the installation process. During
|
||||
# development, they will remain None.
|
||||
#
|
||||
|
||||
CONF_PATHNAME = None
|
||||
|
||||
#########################################################################
|
||||
|
||||
import os
|
||||
|
||||
import database
|
||||
import query
|
||||
import rlog
|
||||
import commit
|
||||
import config
|
||||
|
||||
## error
|
||||
error = 'cvsdbapi error'
|
||||
|
||||
## database
|
||||
CreateCheckinDatabase = database.CreateCheckinDatabase
|
||||
CreateCheckinQuery = database.CreateCheckinQuery
|
||||
CreateCheckinQuery = query.CreateCheckinQuery
|
||||
|
||||
## rlog
|
||||
GetRLogData = rlog.GetRLogData
|
||||
@@ -52,6 +51,11 @@ PrintCommit = commit.PrintCommit
|
||||
gCheckinDatabase = None
|
||||
gCheckinDatabaseReadOnly = None
|
||||
|
||||
## load configuration file, the data is used globally here
|
||||
cfg = config.Config()
|
||||
cfg.set_defaults()
|
||||
cfg.load_config(CONF_PATHNAME)
|
||||
|
||||
|
||||
def ConnectDatabaseReadOnly():
|
||||
global gCheckinDatabaseReadOnly
|
||||
|
243
lib/database.py
243
lib/database.py
@@ -1,44 +1,24 @@
|
||||
# -*- Mode: python -*-
|
||||
# -----------------------------------------------------------------------
|
||||
# Copyright (C) 2000 Jay Painter. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth below:
|
||||
# Copyright (C) 2000-2001 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://viewcvs.sourceforge.net/license-1.html.
|
||||
#
|
||||
# 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.
|
||||
# Contact information:
|
||||
# Greg Stein, PO Box 760, Palo Alto, CA, 94302
|
||||
# gstein@lyra.org, http://viewcvs.sourceforge.net/
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
# For tracking purposes, this software is identified by:
|
||||
# $Id$
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
import os, sys, string, time
|
||||
|
||||
## imports from the database API; we re-assign the namespace here so it
|
||||
## is easier to switch databases
|
||||
import MySQLdb
|
||||
DBI = MySQLdb
|
||||
import os
|
||||
import sys
|
||||
import string
|
||||
import time
|
||||
|
||||
import dbi
|
||||
from commit import CreateCommit, PrintCommit
|
||||
|
||||
|
||||
@@ -46,17 +26,19 @@ from commit import CreateCommit, PrintCommit
|
||||
## of the CheckinDatabase class
|
||||
|
||||
sqlBase = 'SELECT checkins.type, checkins.ci_when,checkins. whoid, checkins.repositoryid, checkins.dirid, checkins.fileid, checkins.revision, checkins.stickytag, checkins.branchid, checkins.addedlines, checkins.removedlines, checkins.descid FROM %s WHERE %s %s'
|
||||
sqlRepository = '(checkins.repositoryid=repositories.id AND repositories.repository="%s")'
|
||||
sqlBranch = '(checkins.branchid=branches.id AND branches.branch="%s")'
|
||||
sqlDirectory = '(checkins.dirid=dirs.id AND dirs.dir LIKE "%s%%")'
|
||||
sqlFile = '(checkins.fileid=files.id AND files.file="%s")'
|
||||
sqlFromDate ='(checkins.ci_when>="%s")'
|
||||
sqlToDate = '(checkins.ci_when<="%s")'
|
||||
sqlAuthor = '(checkins.whoid=people.id AND people.who="%s")'
|
||||
|
||||
sqlRepository = '(checkins.repositoryid = repositories.id AND repositories.repository %s "%s")'
|
||||
sqlBranch = '(checkins.branchid = branches.id AND branches.branch %s "%s")'
|
||||
sqlDirectory = '(checkins.dirid = dirs.id AND dirs.dir %s "%s")'
|
||||
sqlFile = '(checkins.fileid = files.id AND files.file %s "%s")'
|
||||
sqlAuthor = '(checkins.whoid = people.id AND people.who %s "%s")'
|
||||
|
||||
sqlFromDate ='(checkins.ci_when >= "%s")'
|
||||
sqlToDate = '(checkins.ci_when <= "%s")'
|
||||
sqlSortByDate = 'ORDER BY checkins.ci_when DESC'
|
||||
sqlSortByAuthor = 'ORDER BY checkins.whoid'
|
||||
sqlSortByFile = 'ORDER BY checkins.fileid'
|
||||
sqlExcludeVersionFiles = '(checkins.fileid=files.id AND files.file NOT LIKE "%%.ver")'
|
||||
sqlExcludeVersionFiles = '(checkins.fileid = files.id AND files.file NOT LIKE "%%.ver")'
|
||||
sqlCheckCommit = 'SELECT * FROM checkins WHERE checkins.repositoryid=%s AND checkins.dirid=%s AND checkins.fileid=%s AND checkins.revision=%s'
|
||||
|
||||
## CheckinDatabase provides all interfaces needed to the SQL database
|
||||
@@ -77,7 +59,8 @@ class CheckinDatabase:
|
||||
self.dbDescriptionIDCache = {}
|
||||
|
||||
def Connect(self):
|
||||
self.dbConn = self.SQLConnect()
|
||||
self.dbConn = dbi.connect(
|
||||
self.dbHost, self.dbUser, self.dbPasswd, self.dbDatabase)
|
||||
|
||||
def SQLGetID(self, table, field, identifier, auto_set):
|
||||
sql = 'SELECT id FROM %s x WHERE x.%s="%s"' % (
|
||||
@@ -258,7 +241,7 @@ class CheckinDatabase:
|
||||
## module to do the conversion
|
||||
temp = time.localtime(commit.GetTime())
|
||||
|
||||
dbCI_When = DBI.Timestamp(
|
||||
dbCI_When = dbi.Timestamp(
|
||||
temp[0], temp[1], temp[2], temp[3], temp[4], temp[5])
|
||||
|
||||
dbWhoID = self.GetAuthorID(commit.GetAuthor())
|
||||
@@ -281,46 +264,74 @@ class CheckinDatabase:
|
||||
cursor = self.dbConn.cursor()
|
||||
cursor.execute(sql, sqlArguments)
|
||||
|
||||
def SQLQueryListString(self, sqlString, query_entry_list):
|
||||
sqlList = []
|
||||
|
||||
for query_entry in query_entry_list:
|
||||
|
||||
## figure out the correct match type
|
||||
if query_entry.match == "exact":
|
||||
match = "="
|
||||
elif query_entry.match == "like":
|
||||
match = "LIKE"
|
||||
elif query_entry.match == "regex":
|
||||
match = "REGEXP"
|
||||
|
||||
sqlList.append(sqlString % (match, query_entry.data))
|
||||
|
||||
return "(%s)" % (string.join(sqlList, " OR "))
|
||||
|
||||
def CreateSQLQueryString(self, query):
|
||||
tableList = ['checkins']
|
||||
condList = []
|
||||
|
||||
tableList.append('files')
|
||||
|
||||
## XXX: this is to exclude .ver files -- RN specific hack --JMP
|
||||
tableList.append("files")
|
||||
condList.append(sqlExcludeVersionFiles)
|
||||
|
||||
if query.repository:
|
||||
tableList.append('repositories')
|
||||
condList.append(sqlRepository % (query.repository))
|
||||
|
||||
if query.branch:
|
||||
tableList.append('branches')
|
||||
condList.append(sqlBranch % (query.branch))
|
||||
if len(query.repository_list):
|
||||
tableList.append("repositories")
|
||||
condList.append(
|
||||
self.SQLQueryListString(sqlRepository, query.repository_list))
|
||||
|
||||
if len(query.branch_list):
|
||||
tableList.append("branches")
|
||||
condList.append(
|
||||
self.SQLQueryListString(sqlBranch, query.branch_list))
|
||||
|
||||
if len(query.directory_list):
|
||||
tableList.append("dirs")
|
||||
condList.append(
|
||||
self.SQLQueryListString(sqlDirectory, query.directory_list))
|
||||
|
||||
if len(query.file_list):
|
||||
tableList.append("files")
|
||||
condList.append(
|
||||
self.SQLQueryListString(sqlFile, query.file_list))
|
||||
|
||||
if len(query.author_list):
|
||||
tableList.append("people")
|
||||
condList.append(
|
||||
self.SQLQueryListString(sqlAuthor, query.author_list))
|
||||
|
||||
if query.from_date:
|
||||
condList.append(sqlFromDate % (str(query.from_date)))
|
||||
|
||||
if query.to_date:
|
||||
condList.append(sqlToDate % (str(query.to_date)))
|
||||
|
||||
if query.author:
|
||||
tableList.append('people')
|
||||
condList.append(sqlAuthor % (query.author))
|
||||
|
||||
if query.directory:
|
||||
tableList.append('dirs')
|
||||
condList.append(sqlDirectory % (query.directory))
|
||||
|
||||
if query.file:
|
||||
#tableList.append('files')
|
||||
condList.append(sqlFile % (query.file))
|
||||
|
||||
if query.sort == query.SORT_DATE:
|
||||
if query.sort == "date":
|
||||
order_by = sqlSortByDate
|
||||
elif query.sort == query.SORT_AUTHOR:
|
||||
elif query.sort == "author":
|
||||
order_by = sqlSortByAuthor
|
||||
elif query.sort == query.SORT_FILE:
|
||||
elif query.sort == "file":
|
||||
order_by = sqlSortByFile
|
||||
|
||||
## exclude duplicates from the table list
|
||||
for table in tableList[:]:
|
||||
while tableList.count(table) > 1:
|
||||
tableList.remove(table)
|
||||
|
||||
sql = sqlBase % (
|
||||
string.join(tableList, ', '),
|
||||
string.join(condList, ' AND '),
|
||||
@@ -390,106 +401,8 @@ class CheckinDatabase:
|
||||
return None
|
||||
|
||||
return commit
|
||||
|
||||
|
||||
class MySQLCheckinDatabase(CheckinDatabase):
|
||||
def SQLConnect(self):
|
||||
return MySQLdb.connect(
|
||||
host = self.dbHost,
|
||||
user = self.dbUser,
|
||||
passwd = self.dbPasswd,
|
||||
db = self.dbDatabase)
|
||||
|
||||
|
||||
## CheckinDatabaseQueryData is a object which contains the search parameters
|
||||
## for a query to the CheckinDatabase
|
||||
|
||||
class CheckinDatabaseQuery:
|
||||
|
||||
SORT_DATE = 0
|
||||
SORT_AUTHOR = 1
|
||||
SORT_FILE = 2
|
||||
SORT_CHANGESIZE = 3
|
||||
|
||||
def __init__(self):
|
||||
## repository to query
|
||||
self.repository = None
|
||||
|
||||
## branch
|
||||
self.branch = None
|
||||
|
||||
## directory to seach
|
||||
self.directory = None
|
||||
|
||||
## file to search for
|
||||
self.file = None
|
||||
|
||||
## sorting method
|
||||
self.sort = CheckinDatabaseQuery.SORT_DATE;
|
||||
|
||||
## author to search for
|
||||
self.author = None
|
||||
|
||||
## date range in DBI 2.0 timedate objects
|
||||
self.from_date = None
|
||||
self.to_date = None
|
||||
|
||||
## list of commits -- filled in by CVS query
|
||||
self.commit_list = []
|
||||
|
||||
## commit_cb provides a callback for commits as they
|
||||
## are added
|
||||
self.commit_cb = None
|
||||
|
||||
def SetRepository(self, repository):
|
||||
self.repository = repository
|
||||
|
||||
def SetBranch(self, branch):
|
||||
self.branch = branch
|
||||
|
||||
def SetDirectory(self, directory):
|
||||
self.directory = directory
|
||||
|
||||
def SetFile(self, file):
|
||||
self.file = file
|
||||
|
||||
def SetSortMethod(self, sort):
|
||||
self.sort = sort
|
||||
|
||||
def SetAuthor(self, author):
|
||||
self.author = author
|
||||
|
||||
def SetFromDateObject(self, date):
|
||||
self.from_date = date
|
||||
|
||||
def SetToDateObject(self, date):
|
||||
self.to_date = date
|
||||
|
||||
def SetFromDateHoursAgo(self, hours_ago):
|
||||
ticks = time.time() - (3600 * hours_ago)
|
||||
self.from_date = DBI.TimestampFromTicks(ticks)
|
||||
|
||||
def SetFromDateDaysAgo(self, days_ago):
|
||||
ticks = time.time() - (86400 * days_ago)
|
||||
self.from_date = DBI.TimestampFromTicks(ticks)
|
||||
|
||||
def SetToDateDaysAgo(self, days_ago):
|
||||
ticks = time.time() - (86400 * days_ago)
|
||||
self.to_date = DBI.TimestampFromTicks(ticks)
|
||||
|
||||
def AddCommit(self, commit):
|
||||
self.commit_list.append(commit)
|
||||
if self.commit_cb:
|
||||
self.commit_cb(commit)
|
||||
|
||||
def SetCommitCB(self, callback):
|
||||
self.commit_cb = callback
|
||||
|
||||
|
||||
## entrypoints
|
||||
|
||||
def CreateCheckinDatabase(host, user, passwd, database):
|
||||
return MySQLCheckinDatabase(host, user, passwd, database)
|
||||
|
||||
def CreateCheckinQuery():
|
||||
return CheckinDatabaseQuery()
|
||||
return CheckinDatabase(host, user, passwd, database)
|
||||
|
71
lib/dbi.py
Normal file
71
lib/dbi.py
Normal file
@@ -0,0 +1,71 @@
|
||||
# -*- Mode: python -*-
|
||||
#
|
||||
# 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/
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
|
||||
import sys
|
||||
import MySQLdb
|
||||
|
||||
|
||||
dbi_error = "dbi error"
|
||||
|
||||
|
||||
## make some checks in MySQLdb
|
||||
_no_datetime = """\
|
||||
ERROR: Your version of MySQLdb requires the mxDateTime module
|
||||
for the Timestamp() and TimestampFromTicks() methods.
|
||||
You will need to install mxDateTime to use the ViewCVS
|
||||
database.
|
||||
"""
|
||||
|
||||
if not hasattr(MySQLdb, "Timestamp") or \
|
||||
not hasattr(MySQLdb, "TimestampFromTicks"):
|
||||
sys.stderr.write(_no_datetime)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
class Cursor:
|
||||
def __init__(self, mysql_cursor):
|
||||
self.__cursor = mysql_cursor
|
||||
|
||||
def execute(self, *args):
|
||||
apply(self.__cursor.execute, args)
|
||||
|
||||
def fetchone(self):
|
||||
try:
|
||||
row = self.__cursor.fetchone()
|
||||
except IndexError:
|
||||
row = None
|
||||
|
||||
return row
|
||||
|
||||
|
||||
class Connection:
|
||||
def __init__(self, host, user, passwd, db):
|
||||
self.__mysql = MySQLdb.connect(
|
||||
host=host, user=user, passwd=passwd, db=db)
|
||||
|
||||
def cursor(self):
|
||||
return Cursor(self.__mysql.cursor())
|
||||
|
||||
|
||||
def Timestamp(year, month, date, hour, minute, second):
|
||||
return MySQLdb.Timestamp(year, month, date, hour, minute, second)
|
||||
|
||||
|
||||
def TimestampFromTicks(ticks):
|
||||
return MySQLdb.TimestampFromTicks(ticks)
|
||||
|
||||
|
||||
def connect(host, user, passwd, db):
|
||||
return Connection(host, user, passwd, db)
|
187
lib/popen.py
Normal file
187
lib/popen.py
Normal file
@@ -0,0 +1,187 @@
|
||||
#
|
||||
# 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/
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
# popen.py: a replacement for os.popen()
|
||||
#
|
||||
# This implementation of popen() provides a cmd + args calling sequence,
|
||||
# rather than a system() type of convention. The shell facilities are not
|
||||
# available, but that implies we can avoid worrying about shell hacks in
|
||||
# the arguments.
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
def popen(cmd, args, mode, capture_err=1):
|
||||
# flush the stdio buffers since we are about to change the FD under them
|
||||
sys.stdout.flush()
|
||||
sys.stderr.flush()
|
||||
|
||||
r, w = os.pipe()
|
||||
pid = os.fork()
|
||||
if pid:
|
||||
# in the parent
|
||||
|
||||
# close the descriptor that we don't need and return the other one.
|
||||
if mode == 'r':
|
||||
os.close(w)
|
||||
return _pipe(os.fdopen(r, 'r'), pid)
|
||||
os.close(r)
|
||||
return _pipe(os.fdopen(w, 'w'), pid)
|
||||
|
||||
# in the child
|
||||
|
||||
# we'll need /dev/null for the discarded I/O
|
||||
null = os.open('/dev/null', os.O_RDWR)
|
||||
|
||||
if mode == 'r':
|
||||
# hook stdout/stderr to the "write" channel
|
||||
os.dup2(w, 1)
|
||||
# "close" stdin; the child shouldn't use it
|
||||
### this isn't quite right... we may want the child to read from stdin
|
||||
os.dup2(null, 0)
|
||||
# what to do with errors?
|
||||
if capture_err:
|
||||
os.dup2(w, 2)
|
||||
else:
|
||||
os.dup2(null, 2)
|
||||
else:
|
||||
# hook stdin to the "read" channel
|
||||
os.dup2(r, 0)
|
||||
# "close" stdout/stderr; the child shouldn't use them
|
||||
### this isn't quite right... we may want the child to write to these
|
||||
os.dup2(null, 1)
|
||||
os.dup2(null, 2)
|
||||
|
||||
# don't need these FDs any more
|
||||
os.close(null)
|
||||
os.close(r)
|
||||
os.close(w)
|
||||
|
||||
# the stdin/stdout/stderr are all set up. exec the target
|
||||
try:
|
||||
os.execvp(cmd, (cmd,) + tuple(args))
|
||||
except:
|
||||
pass
|
||||
|
||||
# crap. shouldn't be here.
|
||||
sys.exit(127)
|
||||
|
||||
def pipe_cmds(cmds):
|
||||
# flush the stdio buffers since we are about to change the FD under them
|
||||
sys.stdout.flush()
|
||||
sys.stderr.flush()
|
||||
|
||||
prev_r, parent_w = os.pipe()
|
||||
|
||||
null = os.open('/dev/null', os.O_RDWR)
|
||||
|
||||
for cmd in cmds[:-1]:
|
||||
r, w = os.pipe()
|
||||
pid = os.fork()
|
||||
if not pid:
|
||||
# in the child
|
||||
|
||||
# hook up stdin to the "read" channel
|
||||
os.dup2(prev_r, 0)
|
||||
|
||||
# hook up stdout to the output channel
|
||||
os.dup2(w, 1)
|
||||
|
||||
# toss errors
|
||||
os.dup2(null, 2)
|
||||
|
||||
# close these extra descriptors
|
||||
os.close(prev_r)
|
||||
os.close(parent_w)
|
||||
os.close(null)
|
||||
os.close(r)
|
||||
os.close(w)
|
||||
|
||||
# time to run the command
|
||||
try:
|
||||
os.execvp(cmd[0], cmd)
|
||||
except:
|
||||
pass
|
||||
|
||||
sys.exit(127)
|
||||
|
||||
# in the parent
|
||||
|
||||
# we don't need these any more
|
||||
os.close(prev_r)
|
||||
os.close(w)
|
||||
|
||||
# the read channel of this pipe will feed into to the next command
|
||||
prev_r = r
|
||||
|
||||
# no longer needed
|
||||
os.close(null)
|
||||
|
||||
# done with most of the commands. set up the last command to write to stdout
|
||||
pid = os.fork()
|
||||
if not pid:
|
||||
# in the child (the last command)
|
||||
|
||||
# hook up stdin to the "read" channel
|
||||
os.dup2(prev_r, 0)
|
||||
|
||||
# close these extra descriptors
|
||||
os.close(prev_r)
|
||||
os.close(parent_w)
|
||||
|
||||
# run the last command
|
||||
try:
|
||||
os.execvp(cmds[-1][0], cmds[-1])
|
||||
except:
|
||||
pass
|
||||
|
||||
sys.exit(127)
|
||||
|
||||
# not needed any more
|
||||
os.close(prev_r)
|
||||
|
||||
# write into the first pipe, wait on the final process
|
||||
return _pipe(os.fdopen(parent_w, 'w'), pid)
|
||||
|
||||
|
||||
class _pipe:
|
||||
"Wrapper for a file which can wait() on a child process at close time."
|
||||
|
||||
def __init__(self, file, child_pid):
|
||||
self.file = file
|
||||
self.child_pid = child_pid
|
||||
|
||||
def eof(self):
|
||||
pid, status = os.waitpid(self.child_pid, os.WNOHANG)
|
||||
if pid:
|
||||
self.file.close()
|
||||
self.file = None
|
||||
return status
|
||||
return None
|
||||
|
||||
def close(self):
|
||||
if self.file:
|
||||
self.file.close()
|
||||
self.file = None
|
||||
return os.waitpid(self.child_pid, 0)[1]
|
||||
return None
|
||||
|
||||
def __getattr__(self, name):
|
||||
return getattr(self.file, name)
|
||||
|
||||
def __del__(self):
|
||||
if self.file:
|
||||
self.close()
|
102
lib/query.py
Normal file
102
lib/query.py
Normal file
@@ -0,0 +1,102 @@
|
||||
# -*- Mode: python -*-
|
||||
#
|
||||
# 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/
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
|
||||
import time
|
||||
import dbi
|
||||
|
||||
|
||||
## QueryEntry holds data on one match-type in the SQL database
|
||||
## match is: "exact", "like", or "regex"
|
||||
|
||||
class QueryEntry:
|
||||
def __init__(self, data, match):
|
||||
self.data = data
|
||||
self.match = match
|
||||
|
||||
|
||||
## CheckinDatabaseQueryData is a object which contains the search parameters
|
||||
## for a query to the CheckinDatabase
|
||||
|
||||
class CheckinDatabaseQuery:
|
||||
def __init__(self):
|
||||
## sorting
|
||||
self.sort = "date"
|
||||
|
||||
## repository to query
|
||||
self.repository_list = []
|
||||
self.branch_list = []
|
||||
self.directory_list = []
|
||||
self.file_list = []
|
||||
self.author_list = []
|
||||
|
||||
## date range in DBI 2.0 timedate objects
|
||||
self.from_date = None
|
||||
self.to_date = None
|
||||
|
||||
## list of commits -- filled in by CVS query
|
||||
self.commit_list = []
|
||||
|
||||
## commit_cb provides a callback for commits as they
|
||||
## are added
|
||||
self.commit_cb = None
|
||||
|
||||
def SetRepository(self, repository, match = "exact"):
|
||||
self.repository_list.append(QueryEntry(repository, match))
|
||||
|
||||
def SetBranch(self, branch, match = "exact"):
|
||||
self.branch_list.append(QueryEntry(branch, match))
|
||||
|
||||
def SetDirectory(self, directory, match = "exact"):
|
||||
self.directory_list.append(QueryEntry(directory, match))
|
||||
|
||||
def SetFile(self, file, match = "exact"):
|
||||
self.file_list.append(QueryEntry(file, match))
|
||||
|
||||
def SetAuthor(self, author, match = "exact"):
|
||||
self.author_list.append(QueryEntry(author, match))
|
||||
|
||||
def SetSortMethod(self, sort):
|
||||
self.sort = sort
|
||||
|
||||
def SetFromDateObject(self, ticks):
|
||||
self.from_date = dbi.TimestampFromTicks(ticks)
|
||||
|
||||
def SetToDateObject(self, ticks):
|
||||
self.to_date = dbi.TimestampFromTicks(ticks)
|
||||
|
||||
def SetFromDateHoursAgo(self, hours_ago):
|
||||
ticks = time.time() - (3600 * hours_ago)
|
||||
self.from_date = dbi.TimestampFromTicks(ticks)
|
||||
|
||||
def SetFromDateDaysAgo(self, days_ago):
|
||||
ticks = time.time() - (86400 * days_ago)
|
||||
self.from_date = dbi.TimestampFromTicks(ticks)
|
||||
|
||||
def SetToDateDaysAgo(self, days_ago):
|
||||
ticks = time.time() - (86400 * days_ago)
|
||||
self.to_date = dbi.TimestampFromTicks(ticks)
|
||||
|
||||
def AddCommit(self, commit):
|
||||
self.commit_list.append(commit)
|
||||
if self.commit_cb:
|
||||
self.commit_cb(commit)
|
||||
|
||||
def SetCommitCB(self, callback):
|
||||
self.commit_cb = callback
|
||||
|
||||
|
||||
## entrypoints
|
||||
def CreateCheckinQuery():
|
||||
return CheckinDatabaseQuery()
|
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()
|
60
lib/rlog.py
60
lib/rlog.py
@@ -1,38 +1,37 @@
|
||||
# -*- Mode: python -*-
|
||||
# -----------------------------------------------------------------------
|
||||
# Copyright (C) 2000 Jay Painter. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth below:
|
||||
# Copyright (C) 2000-2001 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://viewcvs.sourceforge.net/license-1.html.
|
||||
#
|
||||
# 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.
|
||||
# Contact information:
|
||||
# Greg Stein, PO Box 760, Palo Alto, CA, 94302
|
||||
# gstein@lyra.org, http://viewcvs.sourceforge.net/
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
# For tracking purposes, this software is identified by:
|
||||
# $Id$
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
import os, sys, string, time, re
|
||||
#########################################################################
|
||||
#
|
||||
# INSTALL-TIME CONFIGURATION
|
||||
#
|
||||
# These values will be set during the installation process. During
|
||||
# development, they will remain None.
|
||||
#
|
||||
|
||||
CONF_PATHNAME = None
|
||||
|
||||
#########################################################################
|
||||
|
||||
import os, sys, string, re, time, config
|
||||
|
||||
## load configuration file, the data is used globally here
|
||||
cfg = config.Config()
|
||||
cfg.set_defaults()
|
||||
cfg.load_config(CONF_PATHNAME)
|
||||
|
||||
|
||||
## RLogOutputParser uses the output of rlog to build a list of Commit
|
||||
## objects describing all the checkins from a given RCS file; this
|
||||
@@ -107,7 +106,8 @@ class RLog:
|
||||
if self.date:
|
||||
arg_list.append('-d%s' % (self.date))
|
||||
|
||||
self.cmd = 'rlog %s "%s"' % (string.join(arg_list), self.filename)
|
||||
temp = os.path.join(cfg.general.rcs_path, "rlog")
|
||||
self.cmd = '%s %s "%s"' % (temp, string.join(arg_list), self.filename)
|
||||
self.rlog = os.popen(self.cmd, 'r')
|
||||
|
||||
def fix_filename(self, filename):
|
||||
@@ -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,49 +1,46 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- Mode: python -*-
|
||||
#
|
||||
# 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/
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
# administrative program for CVSdb; this is primarily
|
||||
# used to add/rebuild CVS repositories to the database
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
# Copyright (C) 2000 Jay Painter. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth below:
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
# For tracking purposes, this software is identified by:
|
||||
# $Id$
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
## BOOTSTRAP
|
||||
import sys, os, string
|
||||
_viewcvs_root = string.strip(open("/etc/viewcvs/root", "r").read())
|
||||
sys.path.append(os.path.join(_viewcvs_root, "lib"))
|
||||
##
|
||||
#########################################################################
|
||||
#
|
||||
# INSTALL-TIME CONFIGURATION
|
||||
#
|
||||
# These values will be set during the installation process. During
|
||||
# development, they will remain None.
|
||||
#
|
||||
|
||||
import cvsdbapi
|
||||
LIBRARY_DIR = None
|
||||
CONF_PATHNAME = None
|
||||
|
||||
# Adjust sys.path to include our library directory
|
||||
import sys
|
||||
|
||||
if LIBRARY_DIR:
|
||||
sys.path.insert(0, LIBRARY_DIR)
|
||||
else:
|
||||
sys.path[:0] = ['../lib'] # any other places to look?
|
||||
|
||||
#########################################################################
|
||||
|
||||
import os, string, cvsdbapi
|
||||
|
||||
|
||||
def UpdateFile(db, repository, path):
|
||||
|
@@ -1,48 +1,45 @@
|
||||
#!/usr/bin/python
|
||||
# -*- Mode: python -*-
|
||||
#
|
||||
# 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/
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
# updates SQL database with new commit records
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
# Copyright (C) 2000 Jay Painter. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth below:
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
# For tracking purposes, this software is identified by:
|
||||
# $Id$
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
## BOOTSTRAP
|
||||
import sys, os, string
|
||||
_viewcvs_root = string.strip(open("/etc/viewcvs/root", "r").read())
|
||||
sys.path.append(os.path.join(_viewcvs_root, "lib"))
|
||||
##
|
||||
#########################################################################
|
||||
#
|
||||
# INSTALL-TIME CONFIGURATION
|
||||
#
|
||||
# These values will be set during the installation process. During
|
||||
# development, they will remain None.
|
||||
#
|
||||
|
||||
import getopt, re, cvsdbapi
|
||||
LIBRARY_DIR = None
|
||||
CONF_PATHNAME = None
|
||||
|
||||
# Adjust sys.path to include our library directory
|
||||
import sys
|
||||
|
||||
if LIBRARY_DIR:
|
||||
sys.path.insert(0, LIBRARY_DIR)
|
||||
else:
|
||||
sys.path[:0] = ['../lib'] # any other places to look?
|
||||
|
||||
#########################################################################
|
||||
|
||||
import os, string, getopt, re, cvsdbapi
|
||||
|
||||
DEBUG_FLAG = 0
|
||||
|
||||
@@ -132,7 +129,7 @@ def ProcessLoginfo(repository, stdin_list):
|
||||
if len(list) == 4:
|
||||
if list[1] == '-' and list[2] == 'New' and list[3] == 'directory':
|
||||
debug('new directory')
|
||||
exit(0, file_hash, 1)
|
||||
return
|
||||
|
||||
## each file in the file list _should_ be of the form:
|
||||
## file-name,<old-ver>,<new-ver>
|
||||
|
146
tools/make-database
Executable file
146
tools/make-database
Executable file
@@ -0,0 +1,146 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- Mode: python -*-
|
||||
#
|
||||
# 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/
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
# administrative program for CVSdb; creates a clean database in
|
||||
# MySQL 3.22 or later
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
import os, sys, string
|
||||
|
||||
INTRO_TEXT = """\
|
||||
This script creates the database and tables in MySQL used by the ViewCVS
|
||||
checkin database. You will be prompted for: database user, database user
|
||||
password, and database name. This script will use mysql to create the
|
||||
database for you. You will then need to set the appropriate parameters
|
||||
in your viewcvs.conf file under the [cvsdb] section.
|
||||
"""
|
||||
|
||||
DATABASE_SCRIPT="""\
|
||||
DROP DATABASE IF EXISTS <dbname>;
|
||||
CREATE DATABASE <dbname>;
|
||||
|
||||
USE <dbname>;
|
||||
|
||||
DROP TABLE IF EXISTS branches;
|
||||
CREATE TABLE branches (
|
||||
id mediumint(9) DEFAULT '0' NOT NULL auto_increment,
|
||||
branch varchar(64) binary DEFAULT '' NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE branch (branch)
|
||||
);
|
||||
|
||||
DROP TABLE IF EXISTS checkins;
|
||||
CREATE TABLE checkins (
|
||||
type enum('Change','Add','Remove'),
|
||||
ci_when datetime DEFAULT '0000-00-00 00:00:00' NOT NULL,
|
||||
whoid mediumint(9) DEFAULT '0' NOT NULL,
|
||||
repositoryid mediumint(9) DEFAULT '0' NOT NULL,
|
||||
dirid mediumint(9) DEFAULT '0' NOT NULL,
|
||||
fileid mediumint(9) DEFAULT '0' NOT NULL,
|
||||
revision varchar(32) binary DEFAULT '' NOT NULL,
|
||||
stickytag varchar(255) binary DEFAULT '' NOT NULL,
|
||||
branchid mediumint(9) DEFAULT '0' NOT NULL,
|
||||
addedlines int(11) DEFAULT '0' NOT NULL,
|
||||
removedlines int(11) DEFAULT '0' NOT NULL,
|
||||
descid mediumint(9),
|
||||
UNIQUE repositoryid (repositoryid,dirid,fileid,revision),
|
||||
KEY ci_when (ci_when),
|
||||
KEY whoid (whoid),
|
||||
KEY repositoryid_2 (repositoryid),
|
||||
KEY dirid (dirid),
|
||||
KEY fileid (fileid),
|
||||
KEY branchid (branchid)
|
||||
);
|
||||
|
||||
DROP TABLE IF EXISTS descs;
|
||||
CREATE TABLE descs (
|
||||
id mediumint(9) DEFAULT '0' NOT NULL auto_increment,
|
||||
description text,
|
||||
hash bigint(20) DEFAULT '0' NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
KEY hash (hash)
|
||||
);
|
||||
|
||||
DROP TABLE IF EXISTS dirs;
|
||||
CREATE TABLE dirs (
|
||||
id mediumint(9) DEFAULT '0' NOT NULL auto_increment,
|
||||
dir varchar(128) binary DEFAULT '' NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE dir (dir)
|
||||
);
|
||||
|
||||
DROP TABLE IF EXISTS files;
|
||||
CREATE TABLE files (
|
||||
id mediumint(9) DEFAULT '0' NOT NULL auto_increment,
|
||||
file varchar(128) binary DEFAULT '' NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE file (file)
|
||||
);
|
||||
|
||||
DROP TABLE IF EXISTS people;
|
||||
CREATE TABLE people (
|
||||
id mediumint(9) DEFAULT '0' NOT NULL auto_increment,
|
||||
who varchar(32) binary DEFAULT '' NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE who (who)
|
||||
);
|
||||
|
||||
DROP TABLE IF EXISTS repositories;
|
||||
CREATE TABLE repositories (
|
||||
id mediumint(9) DEFAULT '0' NOT NULL auto_increment,
|
||||
repository varchar(64) binary DEFAULT '' NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE repository (repository)
|
||||
);
|
||||
|
||||
DROP TABLE IF EXISTS tags;
|
||||
CREATE TABLE tags (
|
||||
repositoryid mediumint(9) DEFAULT '0' NOT NULL,
|
||||
branchid mediumint(9) DEFAULT '0' NOT NULL,
|
||||
dirid mediumint(9) DEFAULT '0' NOT NULL,
|
||||
fileid mediumint(9) DEFAULT '0' NOT NULL,
|
||||
revision varchar(32) binary DEFAULT '' NOT NULL,
|
||||
UNIQUE repositoryid (repositoryid,dirid,fileid,branchid,revision),
|
||||
KEY repositoryid_2 (repositoryid),
|
||||
KEY dirid (dirid),
|
||||
KEY fileid (fileid),
|
||||
KEY branchid (branchid)
|
||||
);
|
||||
"""
|
||||
|
||||
if __name__ == "__main__":
|
||||
print INTRO_TEXT
|
||||
|
||||
user = raw_input("MySQL User: ")
|
||||
passwd = raw_input("MySQL Password: ")
|
||||
dbase = raw_input("ViewCVS Database Name [default: ViewCVS]: ")
|
||||
if not dbase:
|
||||
dbase = "ViewCVS"
|
||||
|
||||
cmd = "{ mysql --user=%s --password=%s ; } 2>&1" % (user, passwd)
|
||||
dscript = string.replace(DATABASE_SCRIPT, "<dbname>", dbase)
|
||||
|
||||
mysql = os.popen(cmd, "w")
|
||||
mysql.write(dscript)
|
||||
status = mysql.close()
|
||||
|
||||
if status:
|
||||
print "[ERROR] the database did not create sucessfully."
|
||||
sys.exit(1)
|
||||
|
||||
print "Database created successfully."
|
||||
sys.exit(0)
|
||||
|
39
tools/make-release
Executable file
39
tools/make-release
Executable file
@@ -0,0 +1,39 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# 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/
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
# make-release: internal tool for creating ViewCVS releases
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
|
||||
if [ "x$1" = "x" ]; then
|
||||
echo "USAGE: $0 tagname target-directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if test -e $2; then
|
||||
echo "ERROR: must remove $2 first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 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 $2/website/license-1.html $2/LICENSE.html
|
||||
rm -r $2/website
|
||||
rm $2/tools/make-release
|
||||
|
||||
echo 'Done.'
|
180
viewcvs-install
Executable file
180
viewcvs-install
Executable file
@@ -0,0 +1,180 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- Mode: python -*-
|
||||
#
|
||||
# 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/
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
# install script for viewcvs -- temporary?
|
||||
#
|
||||
# ### this will eventually be replaced by autoconf plus tools. an
|
||||
# ### interactive front-end to ./configure may be provided.
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
|
||||
import os
|
||||
import sys
|
||||
import string
|
||||
import re
|
||||
import traceback
|
||||
import py_compile
|
||||
|
||||
# get access to our library modules
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(sys.argv[0]), 'lib'))
|
||||
|
||||
import compat
|
||||
|
||||
|
||||
## installer text
|
||||
INFO_TEXT = """\
|
||||
|
||||
This is the ViewCVS installer. It will allow you to choose the install
|
||||
path for ViewCVS. You will now be asked some installation questions.
|
||||
Defaults are given in square brackets. Just hit [Enter] if a default
|
||||
is okay.
|
||||
"""
|
||||
|
||||
## installer defaults
|
||||
ROOT_DIR = "/usr/local/viewcvs-dev"
|
||||
|
||||
|
||||
## list of files for installation
|
||||
## tuple (source path, destination path, install mode, true/false flag for
|
||||
## search-and-replace, flag for prompt before replace, compile_it)
|
||||
##
|
||||
|
||||
FILE_INFO_LIST = [
|
||||
("cgi/viewcvs.cgi", "cgi/viewcvs.cgi", 0755, 1, 0, 0),
|
||||
("cgi/queryform.cgi", "cgi/queryform.cgi", 0755, 1, 0, 0),
|
||||
("cgi/query.cgi", "cgi/query.cgi", 0755, 1, 0, 0),
|
||||
|
||||
("cgi/viewcvs.conf.dist", "viewcvs.conf", 0644, 0, 1, 0),
|
||||
|
||||
("lib/PyFontify.py", "lib/PyFontify.py", 0644, 0, 0, 1),
|
||||
("lib/blame.py", "lib/blame.py", 0644, 0, 0, 1),
|
||||
("lib/commit.py", "lib/commit.py", 0644, 0, 0, 1),
|
||||
("lib/compat.py", "lib/compat.py", 0644, 0, 0, 1),
|
||||
("lib/config.py", "lib/config.py", 0644, 0, 0, 1),
|
||||
("lib/cvsdbapi.py", "lib/cvsdbapi.py", 0644, 1, 0, 1),
|
||||
("lib/database.py", "lib/database.py", 0644, 0, 0, 1),
|
||||
("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/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),
|
||||
("tools/make-database", "make-database", 0755, 1, 0, 0),
|
||||
|
||||
("html-templates/queryformtemplate.html",
|
||||
"html-templates/queryformtemplate.html", 0644, 0, 1, 0),
|
||||
("html-templates/querytemplate.html",
|
||||
"html-templates/querytemplate.html", 0644, 0, 1, 0),
|
||||
]
|
||||
|
||||
|
||||
def Error(text, etype=None, evalue=None):
|
||||
print
|
||||
print "[ERROR] %s" % text
|
||||
if etype:
|
||||
print '[ERROR] ',
|
||||
traceback.print_exception(etype, evalue, None, file=sys.stdout)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def MkDir(path):
|
||||
try:
|
||||
compat.makedirs(path)
|
||||
except os.error, e:
|
||||
if e[0] == 17:
|
||||
# EEXIST: file exists
|
||||
return
|
||||
if e[0] == 13:
|
||||
# EACCES: permission denied
|
||||
Error("You do not have permission to create directory %s" % path)
|
||||
Error("Unknown error creating directory %s" % path, OSError, e)
|
||||
|
||||
|
||||
|
||||
def SetOnePath(contents, var, value):
|
||||
pattern = re.compile('^' + var + r'\s*=\s*.*$', re.MULTILINE)
|
||||
repl = '%s = "%s"' % (var, os.path.join(ROOT_DIR, value))
|
||||
return re.sub(pattern, repl, contents)
|
||||
|
||||
def SetPythonPaths(contents):
|
||||
if contents[:2] == '#!':
|
||||
shbang = '#!' + sys.executable
|
||||
contents = re.sub('^#![^\n]*', shbang, contents)
|
||||
contents = SetOnePath(contents, 'LIBRARY_DIR', 'lib')
|
||||
contents = SetOnePath(contents, 'CONF_PATHNAME', 'viewcvs.conf')
|
||||
contents = SetOnePath(contents, 'HTML_TEMPLATE_DIR', 'html-templates')
|
||||
return contents
|
||||
|
||||
|
||||
def InstallFile(src_path, dest_path, mode, set_python_paths, prompt_replace,
|
||||
compile_it):
|
||||
dest_path = os.path.join(ROOT_DIR, dest_path)
|
||||
|
||||
if prompt_replace and os.path.exists(dest_path):
|
||||
temp = raw_input("File %s exists, overwright? [y/N]: " % (dest_path))
|
||||
if not temp or string.lower(temp[0]) != "y":
|
||||
return
|
||||
|
||||
try:
|
||||
contents = open(src_path, "r").read()
|
||||
except IOError, e:
|
||||
Error(str(e))
|
||||
|
||||
if set_python_paths:
|
||||
contents = SetPythonPaths(contents)
|
||||
|
||||
## write the file to the destination location
|
||||
path, basename = os.path.split(dest_path)
|
||||
MkDir(path)
|
||||
|
||||
try:
|
||||
open(dest_path, "w").write(contents)
|
||||
except IOError, e:
|
||||
if e[0] == 13:
|
||||
# EACCES: permission denied
|
||||
Error("You do not have permission to write file %s" % dest_path)
|
||||
Error("Unknown error writing file %s" % dest_path, IOError, e)
|
||||
|
||||
os.chmod(dest_path, mode)
|
||||
|
||||
if compile_it:
|
||||
py_compile.compile(dest_path)
|
||||
|
||||
|
||||
## MAIN
|
||||
if __name__ == "__main__":
|
||||
print INFO_TEXT
|
||||
|
||||
## get the install path
|
||||
temp = raw_input("Installation Path [%s]: " % ROOT_DIR)
|
||||
temp = string.strip(temp)
|
||||
if len(temp):
|
||||
ROOT_DIR = temp
|
||||
|
||||
## install the files
|
||||
print
|
||||
print "Installing ViewCVS to:", ROOT_DIR
|
||||
|
||||
for args in FILE_INFO_LIST:
|
||||
print " ", args[0]
|
||||
apply(InstallFile, args)
|
||||
|
||||
print
|
||||
print "Installation Complete"
|
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,56 +5,75 @@
|
||||
</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://linux.fh-heilbronn.de/~zeller/cgi/cvsweb.cgi"><i>cvsweb</i></a>
|
||||
<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
|
||||
revisions of files. It can display diffs between versions and
|
||||
show selections of files based on tags or branches.
|
||||
show selections of files based on tags or branches. In addition,
|
||||
ViewCVS has "annotation" or "blame" support, and the beginnings
|
||||
of Bonsai-like query facilities.
|
||||
</p>
|
||||
<p>
|
||||
ViewCVS is currently at version 0.4. It was a
|
||||
port of the cvsweb script, but has had numerous cleanups and other
|
||||
modifications, based on some of Python's strengths. There is
|
||||
still some "badness" in there, but I've been working on flushing
|
||||
that out, along with a few new features. The
|
||||
functionality of ViewCVS is equal to that of cvsweb, minus the
|
||||
"annotation" support. Annotation requires read/write access to
|
||||
the CVS repository (at this time), and I believe that is a
|
||||
Bad Thing to do. One of my tasks will be eliminating the
|
||||
read/write requirement.
|
||||
ViewCVS is currently at version 0.6. It was a port of the cvsweb
|
||||
script, but has had numerous cleanups and other modifications,
|
||||
based on some of Python's strengths. There is still some minor
|
||||
"badness" remaining from the Perl code, but I've been working on
|
||||
flushing that out, while adding new features. Currently, the
|
||||
functionality of ViewCVS surpasses that of cvsweb.
|
||||
</p>
|
||||
<p>
|
||||
The software is available for download:
|
||||
</p>
|
||||
<blockquote>
|
||||
<a href="viewcvs-0.4.tar.gz">Version 0.4 of ViewCVS</a>
|
||||
<a href="viewcvs-0.6.tar.gz">Version 0.6 of ViewCVS</a>
|
||||
</blockquote>
|
||||
<p>
|
||||
Of course, it is also available through ViewCVS itself:
|
||||
</p>
|
||||
<blockquote>
|
||||
<a href="/cgi-bin/viewcvs.cgi/gjspy/viewcvs/">http://www.lyra.org/cgi-bin/viewcvs.cgi/gjspy/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>
|
||||
ViewCVS requires <strong>Python 1.5</strong> (which has been out
|
||||
for a couple years and is readily available for your favorite
|
||||
operating system).
|
||||
operating system). If you choose to use the SQL Checkin Database
|
||||
feature, then you must use <strong>Python 1.5.2</strong> and the
|
||||
<a href="http://dustman.net/andy/python/MySQLdb"><i>MySQLdb
|
||||
module</i></a> installed.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
[ <a href="../">up to Python software</a> ]
|
||||
[ <a href="../../">up to Greg's page</a> ]
|
||||
ViewCVS has been developed by the <a href="who.html">ViewCVS
|
||||
Group</a> and is made available under a
|
||||
<a href="license-1.html">BSD-type license</a>.
|
||||
</p>
|
||||
|
||||
<hr width="75%">
|
||||
@@ -66,16 +85,46 @@
|
||||
<a href="http://mailman.lyra.org/mailman/listinfo/viewcvs">ViewCVS
|
||||
mailing list</a>.
|
||||
</p>
|
||||
<p>
|
||||
A <a href="http://mailman.lyra.org/mailman/listinfo/viewcvs-dev">mailing
|
||||
list for ViewCVS developers</a> is also available.
|
||||
</p>
|
||||
|
||||
<hr width="75%">
|
||||
<h2>Additional features over cvsweb</h2>
|
||||
|
||||
<p>
|
||||
<strong>New to version 0.5:</strong>
|
||||
</p>
|
||||
<ul>
|
||||
<li>Colorization for Python files</li>
|
||||
<li>Better reporting for unreadable files</li>
|
||||
<li>More robust when given varying <code>rcsdiff</code> or
|
||||
<code>rlog</code> outputs</li>
|
||||
<li>Hard breaks in human-readable diffs</li>
|
||||
<li>
|
||||
Colorization for many file types via <code>enscript</code>.
|
||||
</li>
|
||||
<li>
|
||||
Bonsai-like query features.
|
||||
</li>
|
||||
<li>
|
||||
Annotation/blame support against a <strong>read-only</strong>
|
||||
repository.
|
||||
</li>
|
||||
<li>
|
||||
Configuration on a per-virtual-host basis. This allows you
|
||||
to share the configuration file and ViewCVS installation
|
||||
across virtual hosts, yet still be able to fine-tune the
|
||||
options when necessary.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
Other improvements over cvsweb:
|
||||
</p>
|
||||
<ul>
|
||||
<li>Better reporting for unreadable files.</li>
|
||||
<li>
|
||||
More robust when given varying <code>rcsdiff</code> or
|
||||
<code>rlog</code> outputs.
|
||||
</li>
|
||||
<li>Hard breaks in human-readable diffs.</li>
|
||||
<li>
|
||||
The configuration file is optional (you can change the values
|
||||
right in the CGI script and avoid the config file, if you so
|
||||
@@ -85,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>
|
||||
@@ -93,6 +152,7 @@
|
||||
<ul>
|
||||
<li>UI streamlining/simplification</li>
|
||||
<li>Integration with CVS checkin auto-mail scripts</li>
|
||||
<li>Tighter integration the query features</li>
|
||||
<li>
|
||||
<i>Suggestions? Send mail to the
|
||||
<a href="mailto:viewcvs@lyra.org">viewcvs@lyra.org</a>
|
||||
@@ -105,14 +165,29 @@
|
||||
Longer term:
|
||||
</p>
|
||||
<ul>
|
||||
<li>Annotation ("blame") support (similar to cvsweb)</li>
|
||||
<li>Integration with an indexer such as LXR</li>
|
||||
<li>Additional colorizers</li>
|
||||
</ul>
|
||||
|
||||
<hr width="75%">
|
||||
<h2>Colorization of Python files</h2>
|
||||
<h2>Colorization of files</h2>
|
||||
<p>
|
||||
ViewCVS can make use of the <code>enscript</code> program to
|
||||
colorize files in the CVS repository. If <code>enscript</code>
|
||||
is present on your system, then set the
|
||||
<code>use_enscript</code> option in the
|
||||
<code>viewcvs.conf</code> configuration file. If necessary,
|
||||
update the <code>enscript_path</code> option to point to your
|
||||
installation directory. ... That's it! Now, as you view files
|
||||
through ViewCVS, they will be colored.
|
||||
</p>
|
||||
|
||||
<h3>Colorization of Python files</h3>
|
||||
|
||||
<p>
|
||||
ViewCVS currently comes with a builtin colorizer for Python
|
||||
source files. This may go away, given the new
|
||||
<code>enscript</code> support...
|
||||
</p>
|
||||
<p>
|
||||
Christophe Pelte suggested this feature: colorize Python source
|
||||
files using
|
||||
@@ -130,7 +205,7 @@
|
||||
<address><a href="mailto:gstein@lyra.org">Greg Stein</a></address>
|
||||
<!-- Created: Fri Dec 3 02:51:37 PST 1999 -->
|
||||
<!-- hhmts start -->
|
||||
Last modified: Fri Mar 24 03:12:34 PST 2000
|
||||
Last modified: Fri May 11 14:22:36 PDT 2001
|
||||
<!-- hhmts end -->
|
||||
</body>
|
||||
</html>
|
||||
|
83
website/license-1.html
Normal file
83
website/license-1.html
Normal file
@@ -0,0 +1,83 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
|
||||
<html>
|
||||
<head>
|
||||
<title>ViewCVS License Agreement (v1)</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>ViewCVS License Agreement (v1)</h1>
|
||||
|
||||
<p>
|
||||
The following text constitutes the license agreement for the
|
||||
<a href="./">ViewCVS</a> software. It
|
||||
is an agreement between
|
||||
<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-2001 The ViewCVS Group. All rights reserved.
|
||||
</b>
|
||||
</p>
|
||||
<p>
|
||||
By using ViewCVS, you agree to the terms and conditions set
|
||||
forth below:
|
||||
</p>
|
||||
<p>
|
||||
Redistribution and use in source and binary forms, with or
|
||||
without modification, are permitted provided that the following
|
||||
conditions are met:
|
||||
</p>
|
||||
<ol>
|
||||
<li>
|
||||
<p>
|
||||
Redistributions of source code must retain the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer.
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
|
||||
<hr>
|
||||
<address><a href="mailto:gstein@lyra.org">Greg Stein</a></address>
|
||||
<!-- Created: Mon May 8 19:01:27 PDT 2000 -->
|
||||
<!-- hhmts start -->
|
||||
Last modified: Sat May 12 15:53:33 PDT 2001
|
||||
<!-- hhmts end -->
|
||||
</body>
|
||||
</html>
|
32
website/who.html
Normal file
32
website/who.html
Normal file
@@ -0,0 +1,32 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
|
||||
<html>
|
||||
<head>
|
||||
<title>The ViewCVS Group</title>
|
||||
</head>
|
||||
|
||||
<body background="/images/chalk.jpg">
|
||||
<h1>The ViewCVS Group</h1>
|
||||
|
||||
<p>
|
||||
The ViewCVS Group is an informal group of people working on and
|
||||
developing the ViewCVS package. The current set of members are:
|
||||
</p>
|
||||
<ul>
|
||||
<li><a href="http://www.lyra.org/greg/"><b>Greg Stein</b></a></li>
|
||||
<li>Jay Painter</li>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
Please note that the <a href="./">ViewCVS</a> package is offered
|
||||
under a BSD-type license, which is detailed on the
|
||||
<a href="license-1.html">ViewCVS License</a> page.
|
||||
</p>
|
||||
|
||||
<hr>
|
||||
<address><a href="mailto:gstein@lyra.org">Greg Stein</a></address>
|
||||
<!-- Created: Mon May 8 19:08:58 PDT 2000 -->
|
||||
<!-- hhmts start -->
|
||||
Last modified: Fri May 11 14:10:54 PDT 2001
|
||||
<!-- hhmts end -->
|
||||
</body>
|
||||
</html>
|
Reference in New Issue
Block a user