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

Compare commits

..

1 Commits
V0_6 ... V0_5

Author SHA1 Message Date
(no author)
99216ae1da This commit was manufactured by cvs2svn to create tag 'V0_5'.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/tags/V0_5@140 8cb11bc2-c004-0410-86c3-e597b4017df7
2000-07-02 00:44:05 +00:00
11 changed files with 228 additions and 440 deletions

View File

@@ -74,8 +74,6 @@ 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,164 +43,49 @@ else:
#########################################################################
import os
import string
import cgi
import time
import cvsdbapi
import os, string, cgi, time, cvsdbapi
## tuple of alternating row colors
Colors = ("#ccccee", "#ffffff")
## 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 HTMLHeader():
print "Content-type: text/html"
print
def FormToCheckinQuery(form):
query = cvsdbapi.CreateCheckinQuery()
if form.has_key("repository"):
temp = form["repository"].value
for cmd, str in listparse_string(temp):
cmd = decode_command(cmd)
query.SetRepository(str, cmd)
query.SetRepository(temp)
if form.has_key("branch"):
temp = form["branch"].value
for cmd, str in listparse_string(temp):
cmd = decode_command(cmd)
query.SetBranch(str, cmd)
query.SetBranch(temp)
if form.has_key("directory"):
temp = form["directory"].value
for cmd, str in listparse_string(temp):
cmd = decode_command(cmd)
query.SetDirectory(str, cmd)
query.SetDirectory(temp)
if form.has_key("file"):
temp = form["file"].value
for cmd, str in listparse_string(temp):
cmd = decode_command(cmd)
query.SetFile(str, cmd)
query.SetFile(temp)
if form.has_key("who"):
temp = form["who"].value
for cmd, str in listparse_string(temp):
cmd = decode_command(cmd)
query.SetAuthor(str, cmd)
query.SetAuthor(temp)
if form.has_key("sortby"):
temp = form["sortby"].value
if temp == "date":
query.SetSortMethod("date")
query.SetSortMethod(query.SORT_DATE)
elif temp == "author":
query.SetSortMethod("author")
query.SetSortMethod(query.SORT_AUTHOR)
else:
query.SetSortMethod("file")
query.SetSortMethod(query.SORT_FILE)
if form.has_key("date"):
temp = form["date"].value
@@ -297,7 +182,7 @@ class HTMLTemplate:
def Main():
print "Content-type: text/html\n\n"
HTMLHeader()
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://stud.fh-heilbronn.de/~zeller/cgi/cvsweb.cgi/
# http://linux.fh-heilbronn.de/~zeller/cgi/cvsweb.cgi
#
# -----------------------------------------------------------------------
#
__version__ = '0.6'
__version__ = '0.5'
#########################################################################
#
@@ -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,17 +113,15 @@ 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 parts and parts[0] == checkout_magic_path:
if where[:len(checkout_magic_path)] == checkout_magic_path:
self.has_checkout_magic = 1
del parts[0]
where = where[len(checkout_magic_path):]
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)
@@ -276,7 +274,7 @@ def toggle_query(query_dict, which, value=None):
return '?' + query
return ''
def clickable_path(request, path, leaf_is_link, leaf_is_file, drop_leaf):
def clickable_path(request, path, leaf_is_link, drop_leaf):
if path == '/':
# this should never happen - chooseCVSRoot() is
# intended to do this
@@ -288,19 +286,13 @@ def clickable_path(request, path, leaf_is_link, leaf_is_file, 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]
is_leaf = i == len(parts) - 1
if not is_leaf or leaf_is_link:
if is_leaf and leaf_is_file:
slash = ''
else:
slash = '/'
if i < len(parts) - 1 or leaf_is_link:
### should we be encoding/quoting the URL stuff? (probably...)
s = s + ' / <a href="%s%s%s%s#dirlist">%s</a>' % \
(request.script_name, where, slash, request.qmark_query, parts[i])
s = s + ' / <a href="%s%s/%s#dirlist">%s</a>' % \
(request.script_name, where, request.qmark_query, parts[i])
else:
s = s + ' / ' + parts[i]
@@ -342,7 +334,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)
@@ -455,7 +447,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, 0))
(html_icon('dir'), clickable_path(request, path, 1, 0))
print '</tr></table>'
def markup_stream_default(fp):
@@ -492,20 +484,15 @@ def markup_stream_python(fp):
def markup_stream_enscript(lang, fp):
sys.stdout.flush()
enscript = popen.pipe_cmds([(cfg.options.enscript_path + 'enscript',
'--color', '-W', 'html', '-E' + lang, '-o',
'-', '-'),
('sed', '-n', '/^<PRE>$/,/<\\/PRE>$/p')])
cmd = "%senscript --color -W html -E%s -o - - 2> /dev/null " \
"| sed -n '/^<PRE>$/,/<\\/PRE>$/p'" % \
(cfg.options.enscript_path, lang,)
enscript = os.popen(cmd, "w")
while 1:
chunk = fp.read(CHUNK_SIZE)
if not chunk:
if fp.eof() is None:
time.sleep(1)
continue
break
enscript.write(chunk)
enscript.close()
markup_streamers = {
@@ -588,7 +575,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, 1, 0), '</b>'
print 'File:', clickable_path(request, where, 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')
@@ -625,9 +612,6 @@ 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):
@@ -940,7 +924,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
@@ -977,9 +961,9 @@ def get_logs(full_name, files, view_tag):
###
### more work for later...
status = rlog.close()
if status:
raise 'error during rlog: '+hex(status)
if rlog.close():
### what to do?
pass
return fileinfo, alltags.keys()
@@ -1024,7 +1008,7 @@ def view_directory(request):
if not hideattic:
try:
attic_files = os.listdir(full_name + '/Attic')
except os.error:
except OSError:
pass
else:
### filter for just RCS files?
@@ -1062,8 +1046,7 @@ def view_directory(request):
#choose_cvsroot()
pass
else:
print '<p>Current directory: <b>', \
clickable_path(request, where, 0, 0, 0), '</b>'
print '<p>Current directory: <b>', clickable_path(request, where, 0, 0), '</b>'
if view_tag:
print '<p>Current tag: <b>', view_tag, '</b>'
@@ -1275,7 +1258,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 ' \
@@ -1665,20 +1648,12 @@ def view_log(request):
html_header('CVS log for %s' % where)
up_where = re.sub(_re_up_path, '', where)
### 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
filename = os.path.basename(full_name[:-2]) # drop the ",v"
back_url = request.script_name + '/' + urllib.quote(up_where) + \
request.qmark_query
print html_link(html_icon('back'), back_url + '#' + filename)
### use drop_leaf
print '<b>Up to %s</b><p>' % clickable_path(request, up_where, 1, 0, 0)
print '<b>Up to %s</b><p>' % clickable_path(request, up_where, 1, 0)
print '<a href="#diff">Request diff between arbitrary revisions</a>'
print '<hr noshade>'
@@ -2247,9 +2222,9 @@ def main():
try:
main()
except SystemExit, e:
# don't catch SystemExit (caused by sys.exit()). propagate the exit code
sys.exit(e[0])
except SystemExit:
# don't stop on a SystemExit (caused by sys.exit())
pass
except:
info = sys.exc_info()
html_header('Python Exception Occurred')

View File

@@ -45,15 +45,14 @@ 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, 0)
return tuple(map(int, matches)) + (0, 1, -1)
cvs_strptime.__doc__ = 'Parse a CVS-style date/time value.'
#
# os.makedirs() is new to Python 1.5.2

View File

@@ -272,10 +272,3 @@ 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,20 +25,14 @@ CONF_PATHNAME = None
#########################################################################
import os
import database
import query
import rlog
import commit
import config
import os, database, rlog, commit, config
## error
error = 'cvsdbapi error'
## database
CreateCheckinDatabase = database.CreateCheckinDatabase
CreateCheckinQuery = query.CreateCheckinQuery
CreateCheckinQuery = database.CreateCheckinQuery
## rlog
GetRLogData = rlog.GetRLogData

View File

@@ -13,12 +13,13 @@
# -----------------------------------------------------------------------
#
import os
import sys
import string
import time
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 dbi
from commit import CreateCommit, PrintCommit
@@ -26,19 +27,17 @@ 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 "%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")'
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")'
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
@@ -59,8 +58,7 @@ class CheckinDatabase:
self.dbDescriptionIDCache = {}
def Connect(self):
self.dbConn = dbi.connect(
self.dbHost, self.dbUser, self.dbPasswd, self.dbDatabase)
self.dbConn = self.SQLConnect()
def SQLGetID(self, table, field, identifier, auto_set):
sql = 'SELECT id FROM %s x WHERE x.%s="%s"' % (
@@ -241,7 +239,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())
@@ -264,73 +262,45 @@ 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 = []
## XXX: this is to exclude .ver files -- RN specific hack --JMP
tableList.append("files")
tableList.append('files')
condList.append(sqlExcludeVersionFiles)
if query.repository:
tableList.append('repositories')
condList.append(sqlRepository % (query.repository))
if len(query.repository_list):
tableList.append("repositories")
condList.append(
self.SQLQueryListString(sqlRepository, query.repository_list))
if query.branch:
tableList.append('branches')
condList.append(sqlBranch % (query.branch))
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.sort == "date":
order_by = sqlSortByDate
elif query.sort == "author":
order_by = sqlSortByAuthor
elif query.sort == "file":
order_by = sqlSortByFile
if query.author:
tableList.append('people')
condList.append(sqlAuthor % (query.author))
## exclude duplicates from the table list
for table in tableList[:]:
while tableList.count(table) > 1:
tableList.remove(table)
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:
order_by = sqlSortByDate
elif query.sort == query.SORT_AUTHOR:
order_by = sqlSortByAuthor
elif query.sort == query.SORT_FILE:
order_by = sqlSortByFile
sql = sqlBase % (
string.join(tableList, ', '),
@@ -401,8 +371,106 @@ 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 CheckinDatabase(host, user, passwd, database)
return MySQLCheckinDatabase(host, user, passwd, database)
def CreateCheckinQuery():
return CheckinDatabaseQuery()

View File

@@ -22,13 +22,8 @@
#
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:
@@ -50,7 +45,6 @@ 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:
@@ -61,7 +55,6 @@ 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)
@@ -71,91 +64,11 @@ def popen(cmd, args, mode, capture_err=1):
os.close(w)
# the stdin/stdout/stderr are all set up. exec the target
try:
os.execvp(cmd, (cmd,) + tuple(args))
except:
pass
os.execvp(cmd, (cmd,) + tuple(args))
# 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."
@@ -164,20 +77,10 @@ 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):
if self.file:
self.file.close()
self.file = None
return os.waitpid(self.child_pid, 0)[1]
return None
self.file.close()
self.file = None
return os.waitpid(self.child_pid, 0)[1] or 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-dev"
ROOT_DIR = "/usr/local/viewcvs"
## list of files for installation
@@ -66,11 +66,9 @@ 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),
@@ -95,11 +93,11 @@ def Error(text, etype=None, evalue=None):
def MkDir(path):
try:
compat.makedirs(path)
except os.error, e:
if e[0] == 17:
except OSError, e:
if e.errno == 17:
# EEXIST: file exists
return
if e[0] == 13:
if e.errno == 13:
# EACCES: permission denied
Error("You do not have permission to create directory %s" % path)
Error("Unknown error creating directory %s" % path, OSError, e)
@@ -145,7 +143,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[0] == 13:
if e.errno == 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://stud.fh-heilbronn.de/~zeller/cgi/cvsweb.cgi/"><i>cvsweb</i></a>
<a href="http://linux.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.6. It was a port of the cvsweb
ViewCVS is currently at version 0.5. 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.
"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.
</p>
<p>
The software is available for download:
</p>
<blockquote>
<a href="viewcvs-0.6.tar.gz">Version 0.6 of ViewCVS</a>
<a href="viewcvs-0.5.tar.gz">Version 0.5 of ViewCVS</a>
</blockquote>
<p>
Of course, it is also available through ViewCVS itself:
@@ -47,16 +47,12 @@
<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). 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.
operating system).
</p>
<p>
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>.
[ <a href="/greg/">Greg's page</a> ]
[ <a href="/greg/python/">other Python software</a> ]
</p>
<hr width="75&#37;">
@@ -76,32 +72,8 @@
<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
@@ -117,6 +89,17 @@
<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>
@@ -174,19 +157,11 @@
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: Tue Jul 25 05:14:15 PDT 2000
Last modified: Fri May 12 03:53:51 PDT 2000
<!-- hhmts end -->
</body>
</html>