1
0
mirror of https://github.com/vitalif/viewvc-4intranet synced 2019-04-16 04:14:59 +03:00

Compare commits

..

22 Commits
V0_5 ... V0_6

Author SHA1 Message Date
(no author)
44d91085ac This commit was manufactured by cvs2svn to create tag 'V0_6'.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/tags/V0_6@145 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-07-25 12:16:33 +00:00
gstein
4ddb89e053 add the 0.6 references
add (missing) refs to who.html and license-1.html


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@144 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-07-25 12:16:33 +00:00
gstein
c9e782c881 rolling
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@143 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-07-25 12:02:21 +00:00
gstein
7d8f036490 not sure that I like the clickable_path parameters, but this is the quickest
way right now to ensure we get the "/" handled properly.
[ will need future review ]


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@142 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-07-25 12:00:26 +00:00
gstein
02bad3fbe3 duh :-)
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@141 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-07-25 11:59:22 +00:00
gstein
892364776e add trailing CR to the error message. use hasattr() rather than "in dir()"
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@139 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-07-02 00:44:05 +00:00
jpaint
8996332950 write error message to stderr instead of printing it so it will show
up in webserver logs


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@138 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-07-01 17:04:48 +00:00
jpaint
eb214de775 check MySQLdb for Timestamp() and friends; exit with a informitive error
of they don't exist.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@137 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-07-01 17:01:45 +00:00
jpaint
5ca77ced3c fix error -- use lowercase "dbi"
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@136 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-07-01 16:53:36 +00:00
jpaint
91d2ebfc64 move the dbi abstraction to dbi.py; I'll deal with various errors and
incompatibilities in different versions of MySQLdb here...


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@135 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-06-27 03:09:52 +00:00
gstein
ee7abc077a some minor tweaks from a while back
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@134 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-06-26 06:39:50 +00:00
gstein
087db42096 some stuff that had accumulated
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@133 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-06-26 06:39:06 +00:00
jpaint
028b54de90 modifications to accept the comma/command-seperated multiple query
strings


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@132 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-06-11 23:12:48 +00:00
jpaint
cb4f496b99 moved query object from lib/database.py to lib/query.py; expanded query
object to accept multiple directories,  repositories, authors, and files;
it also does regular expressions (optionally) now

Note: this commit doesn't leave the database in a very useable state,
      I'll fix that very soon.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@131 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-06-11 16:47:37 +00:00
jpaint
573293bbfc let people know Python 1.5.2 is a requirement for the SQL database part
of ViewCVS


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@130 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-06-01 23:04:48 +00:00
gstein
e6080c8366 execvp() can raise an exception, which means we never get to the sys.exit()
call. by wrapping it with try/except, we can ensure that get there.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@129 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-05-25 06:57:02 +00:00
gstein
346c91205b oops. parts[0] doesn't always exist.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@128 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-05-22 08:56:44 +00:00
gstein
3828e969de two changes:
1) try to fix a problem on FreeBSD where it seems the parent process is
     not waiting for the child (piped) processes to complete.
  2) handle the checkout_magic_path better -- it was showing up as the
     request.module and getting caught up in the 'forbidden' processing.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@127 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-05-22 08:48:07 +00:00
gstein
1a821bda9e add pipe_cmds() to set up a sequence of processes/pipes
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@126 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-05-22 08:46:04 +00:00
gstein
ddd782a950 The daylight flag should be zero since these values are in UTC. Problem
found by Chris Meyer <cmeyer@gatan.com>


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@125 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-05-17 08:01:53 +00:00
gstein
f2dffceb6d fix bug found by Nick.T.Reinking@supervalu.com
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@124 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-05-17 06:56:11 +00:00
gstein
8810ab6c57 some fixes for Python 1.5(.1) compatibility. problems found by the help of
Bruce Langlois <bruce@acsp.com>


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@123 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-05-17 06:42:49 +00:00
11 changed files with 438 additions and 226 deletions

View File

@@ -74,6 +74,8 @@ 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

18
TODO
View File

@@ -14,19 +14,19 @@ 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
KNOWN BUGS
----------

View File

@@ -43,49 +43,164 @@ else:
#########################################################################
import os, string, cgi, time, cvsdbapi
import os
import string
import cgi
import time
import 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
@@ -182,7 +297,7 @@ class HTMLTemplate:
def Main():
HTMLHeader()
print "Content-type: text/html\n\n"
template_path = os.path.join(HTML_TEMPLATE_DIR, "querytemplate.html")
template = HTMLTemplate(template_path)

View File

@@ -21,12 +21,12 @@
# derived from software by Bill Fenner, with additional modifications by
# Henrik Nordstrom and Ken Coar). The cvsweb distribution can be found
# on Zeller's site:
# http://linux.fh-heilbronn.de/~zeller/cgi/cvsweb.cgi
# http://stud.fh-heilbronn.de/~zeller/cgi/cvsweb.cgi/
#
# -----------------------------------------------------------------------
#
__version__ = '0.5'
__version__ = '0.6'
#########################################################################
#
@@ -69,7 +69,7 @@ import popen
#########################################################################
checkout_magic_path = '~checkout~/'
checkout_magic_path = '~checkout~'
viewcvs_mime_type = 'text/vnd.viewcvs-markup'
# put here the variables we need in order to hold our state - they will be
@@ -113,15 +113,17 @@ class Request:
# clean it up. this removes duplicate '/' characters and any that may
# exist at the front or end of the path.
parts = filter(None, string.split(where, '/'))
where = string.join(parts, '/')
# does it have the magic checkout prefix?
if where[:len(checkout_magic_path)] == checkout_magic_path:
if parts and parts[0] == checkout_magic_path:
self.has_checkout_magic = 1
where = where[len(checkout_magic_path):]
del parts[0]
else:
self.has_checkout_magic = 0
# put it back together
where = string.join(parts, '/')
script_name = os.environ['SCRIPT_NAME'] ### clean this up?
if where:
url = script_name + '/' + urllib.quote(where)
@@ -274,7 +276,7 @@ def toggle_query(query_dict, which, value=None):
return '?' + query
return ''
def clickable_path(request, path, leaf_is_link, drop_leaf):
def clickable_path(request, path, leaf_is_link, leaf_is_file, drop_leaf):
if path == '/':
# this should never happen - chooseCVSRoot() is
# intended to do this
@@ -286,13 +288,19 @@ def clickable_path(request, path, leaf_is_link, drop_leaf):
if drop_leaf:
del parts[-1]
leaf_is_link = 1
leaf_is_file = 0
where = ''
for i in range(len(parts)):
where = where + '/' + parts[i]
if i < len(parts) - 1 or leaf_is_link:
is_leaf = i == len(parts) - 1
if not is_leaf or leaf_is_link:
if is_leaf and leaf_is_file:
slash = ''
else:
slash = '/'
### should we be encoding/quoting the URL stuff? (probably...)
s = s + ' / <a href="%s%s/%s#dirlist">%s</a>' % \
(request.script_name, where, request.qmark_query, parts[i])
s = s + ' / <a href="%s%s%s%s#dirlist">%s</a>' % \
(request.script_name, where, slash, request.qmark_query, parts[i])
else:
s = s + ' / ' + parts[i]
@@ -334,7 +342,7 @@ def html_log(log):
def download_url(request, url, revision, mime_type):
if cfg.options.checkout_magic and mime_type != viewcvs_mime_type:
url = '%s/%s%s/%s' % \
url = '%s/%s/%s/%s' % \
(request.script_name, checkout_magic_path,
os.path.dirname(request.where), url)
@@ -447,7 +455,7 @@ def navigate_header(request, swhere, path, filename, rev, title):
'%s%s#rev%s' % (swhere, request.qmark_query, rev)),
html_icon('file'))
print '<td align=right>%s <b>Up to %s</b></td>' % \
(html_icon('dir'), clickable_path(request, path, 1, 0))
(html_icon('dir'), clickable_path(request, path, 1, 0, 0))
print '</tr></table>'
def markup_stream_default(fp):
@@ -484,15 +492,20 @@ def markup_stream_python(fp):
def markup_stream_enscript(lang, fp):
sys.stdout.flush()
cmd = "%senscript --color -W html -E%s -o - - 2> /dev/null " \
"| sed -n '/^<PRE>$/,/<\\/PRE>$/p'" % \
(cfg.options.enscript_path, lang,)
enscript = os.popen(cmd, "w")
enscript = popen.pipe_cmds([(cfg.options.enscript_path + 'enscript',
'--color', '-W', 'html', '-E' + lang, '-o',
'-', '-'),
('sed', '-n', '/^<PRE>$/,/<\\/PRE>$/p')])
while 1:
chunk = fp.read(CHUNK_SIZE)
if not chunk:
if fp.eof() is None:
time.sleep(1)
continue
break
enscript.write(chunk)
enscript.close()
markup_streamers = {
@@ -575,7 +588,7 @@ def markup_stream(request, fp, revision, mime_type):
navigate_header(request, request.url, pathname, filename, revision, 'view')
print '<hr noshade>'
print '<table width="100&#37;"><tr><td bgcolor="%s">' % cfg.colors.markup_log
print 'File:', clickable_path(request, where, 1, 0), '</b>'
print 'File:', clickable_path(request, where, 1, 1, 0), '</b>'
download_link(request, file_url, revision, '(download)')
if not request.default_text_plain:
download_link(request, file_url, revision, '(as text)', 'text/plain')
@@ -612,6 +625,9 @@ def markup_stream(request, fp, revision, mime_type):
markup_stream_enscript(lang, fp)
else:
markup_stream_default(fp)
status = fp.close()
if status:
raise error, 'pipe error status: %d' % status
html_footer()
def get_file_data(full_name):
@@ -924,7 +940,7 @@ def process_rlog_output(rlog, full_name, view_tag, fileinfo, alltags):
def get_logs(full_name, files, view_tag):
if len(files) == 0:
return { }, { }
return { }, [ ]
fileinfo = { }
alltags = { # all the tags seen in the files of this dir
@@ -961,9 +977,9 @@ def get_logs(full_name, files, view_tag):
###
### more work for later...
if rlog.close():
### what to do?
pass
status = rlog.close()
if status:
raise 'error during rlog: '+hex(status)
return fileinfo, alltags.keys()
@@ -1008,7 +1024,7 @@ def view_directory(request):
if not hideattic:
try:
attic_files = os.listdir(full_name + '/Attic')
except OSError:
except os.error:
pass
else:
### filter for just RCS files?
@@ -1046,7 +1062,8 @@ def view_directory(request):
#choose_cvsroot()
pass
else:
print '<p>Current directory: <b>', clickable_path(request, where, 0, 0), '</b>'
print '<p>Current directory: <b>', \
clickable_path(request, where, 0, 0, 0), '</b>'
if view_tag:
print '<p>Current tag: <b>', view_tag, '</b>'
@@ -1258,7 +1275,7 @@ def view_directory(request):
print '</table>'
if num_files and not num_displayed:
print '<p><b>NOTE:</b> There are %d files, but none match the current' \
print '<p><b>NOTE:</b> There are %d files, but none match the current ' \
'tag (%s)' % (num_files, view_tag)
if unreadable:
print '<hr size=1 noshade><b>NOTE:</b> One or more files were ' \
@@ -1648,12 +1665,20 @@ def view_log(request):
html_header('CVS log for %s' % where)
up_where = re.sub(_re_up_path, '', where)
filename = os.path.basename(full_name[:-2]) # drop the ",v"
### whoops. this sometimes/always? does not have the ",v"
assert full_name[-2:] != ',v', 'please report this error to viewcvs@lyra.org'
#filename = os.path.basename(full_name[:-2]) # drop the ",v"
filename = os.path.basename(full_name)
### try: "./" + query + "#" + filename
back_url = request.script_name + '/' + urllib.quote(up_where) + \
request.qmark_query
print html_link(html_icon('back'), back_url + '#' + filename)
print '<b>Up to %s</b><p>' % clickable_path(request, up_where, 1, 0)
### use drop_leaf
print '<b>Up to %s</b><p>' % clickable_path(request, up_where, 1, 0, 0)
print '<a href="#diff">Request diff between arbitrary revisions</a>'
print '<hr noshade>'
@@ -2222,9 +2247,9 @@ def main():
try:
main()
except SystemExit:
# don't stop on a SystemExit (caused by sys.exit())
pass
except SystemExit, e:
# don't catch SystemExit (caused by sys.exit()). propagate the exit code
sys.exit(e[0])
except:
info = sys.exc_info()
html_header('Python Exception Occurred')

View File

@@ -45,14 +45,15 @@ except AttributeError:
#
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

View File

@@ -272,3 +272,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
+ ']*=\)\(.*\)$')

View File

@@ -25,14 +25,20 @@ CONF_PATHNAME = None
#########################################################################
import os, database, rlog, commit, config
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

View File

@@ -13,13 +13,12 @@
# -----------------------------------------------------------------------
#
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
@@ -27,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
@@ -58,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"' % (
@@ -239,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())
@@ -262,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 '),
@@ -371,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)

View File

@@ -22,8 +22,13 @@
#
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:
@@ -45,6 +50,7 @@ def popen(cmd, args, mode, capture_err=1):
# 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:
@@ -55,6 +61,7 @@ def popen(cmd, args, mode, capture_err=1):
# 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)
@@ -64,11 +71,91 @@ def popen(cmd, args, mode, capture_err=1):
os.close(w)
# the stdin/stdout/stderr are all set up. exec the target
os.execvp(cmd, (cmd,) + tuple(args))
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."
@@ -77,10 +164,20 @@ class _pipe:
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):
self.file.close()
self.file = None
return os.waitpid(self.child_pid, 0)[1] or None
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)

View File

@@ -44,7 +44,7 @@ is okay.
"""
## installer defaults
ROOT_DIR = "/usr/local/viewcvs"
ROOT_DIR = "/usr/local/viewcvs-dev"
## list of files for installation
@@ -66,9 +66,11 @@ FILE_INFO_LIST = [
("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/rlog.py", "lib/rlog.py", 0644, 1, 0, 1),
("lib/query.py", "lib/query.py", 0644, 1, 0, 1),
("tools/loginfo-handler", "loginfo-handler", 0755, 1, 0, 0),
("tools/cvsdbadmin", "cvsdbadmin", 0755, 1, 0, 0),
@@ -93,11 +95,11 @@ def Error(text, etype=None, evalue=None):
def MkDir(path):
try:
compat.makedirs(path)
except OSError, e:
if e.errno == 17:
except os.error, e:
if e[0] == 17:
# EEXIST: file exists
return
if e.errno == 13:
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)
@@ -143,7 +145,7 @@ def InstallFile(src_path, dest_path, mode, set_python_paths, prompt_replace,
try:
open(dest_path, "w").write(contents)
except IOError, e:
if e.errno == 13:
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)

View File

@@ -9,7 +9,7 @@
<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
@@ -24,18 +24,18 @@
of Bonsai-like query facilities.
</p>
<p>
ViewCVS is currently at version 0.5. It was a port of the cvsweb
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" in there, but I've been working on flushing that out,
while adding new features. Currently, the functionality of ViewCVS
surpasses that of cvsweb.
"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.5.tar.gz">Version 0.5 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:
@@ -47,12 +47,16 @@
<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="/greg/">Greg's page</a> ]
[ <a href="/greg/python/">other Python software</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&#37;">
@@ -72,8 +76,32 @@
<hr width="75&#37;">
<h2>Additional features over cvsweb</h2>
<p>
<strong>New to version 0.5:</strong>
</p>
<ul>
<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>Colorization for many file types via <code>enscript</code>.</li>
<li>Better reporting for unreadable files.</li>
<li>
More robust when given varying <code>rcsdiff</code> or
@@ -89,17 +117,6 @@
<li>
Directories with a large number of files can be viewed.
</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>
@@ -157,11 +174,19 @@
you want to use this feature.
</p>
<hr width="75&#37;">
<h3>Other links</h3>
<p>
[ <a href="/greg/">Greg's page</a> ]
[ <a href="/greg/python/">other Python software</a> ]
</p>
<hr>
<address><a href="mailto:gstein@lyra.org">Greg Stein</a></address>
<!-- Created: Fri Dec 3 02:51:37 PST 1999 -->
<!-- hhmts start -->
Last modified: Fri May 12 03:53:51 PDT 2000
Last modified: Tue Jul 25 05:14:15 PDT 2000
<!-- hhmts end -->
</body>
</html>