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

Compare commits

..

1 Commits

Author SHA1 Message Date
cmpilato
1f399bbd6d Tag the 1.1.11 final release.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/tags/1.1.11@2573 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-05-17 12:27:03 +00:00
59 changed files with 292 additions and 679 deletions

28
CHANGES
View File

@@ -1,28 +1,3 @@
Version 1.1.14 (released 12-Jun-2012)
* fix annotation of svn files with non-URI-safe paths (issue #504)
* handle file:/// Subversion rootpaths as local roots (issue #446)
* fix bug caused by trying to case-normalize anon usernames (issue #505)
* speed up log handling by reusing tokenization results (issue #506)
* add support for custom review log markup rules (issue #429)
Version 1.1.13 (released 23-Jan-2012)
* fix svndbadmin failure on deleted paths under Subversion 1.7 (issue #499)
* fix annotation of files in svn roots with non-URI-safe paths
* fix stray annotation warning in markup display of images
* more gracefully handle attempts to display binary content (issue #501)
Version 1.1.12 (released 03-Nov-2011)
* fix path display in patch and certain diff views (issue #485)
* fix broken cvsdb glob searching (issue 486)
* allow svn revision specifiers to have leading r's (issue #441, #448)
* allow environmental override of configuration location (issue #494)
* fix exception HTML-escaping non-string data under WSGI (issue #454)
* add links to root logs from roots view (issue #470)
* use Pygments lexer-guessing functionality (issue #495)
Version 1.1.11 (released 17-May-2011) Version 1.1.11 (released 17-May-2011)
* security fix: remove user-reachable override of cvsdb row limit * security fix: remove user-reachable override of cvsdb row limit
@@ -31,7 +6,7 @@ Version 1.1.11 (released 17-May-2011)
* fix stack trace when asked to checkout a directory (issue #478) * fix stack trace when asked to checkout a directory (issue #478)
* improve memory usage and speed of revision log markup (issue #477) * improve memory usage and speed of revision log markup (issue #477)
* fix broken annotation view in CVS keyword-bearing files (issue #479) * fix broken annotation view in CVS keyword-bearing files (issue #479)
* warn users when query results are incomplete (issue #433) * warn users when query results are incomplete (issue #443)
* avoid parsing errors on RCS newphrases in the admin section (issue #483) * avoid parsing errors on RCS newphrases in the admin section (issue #483)
* make rlog parsing code more robust in certain error cases (issue #444) * make rlog parsing code more robust in certain error cases (issue #444)
@@ -46,6 +21,7 @@ Version 1.1.9 (released 18-Feb-2011)
* make revision log "extra pages" count configurable * make revision log "extra pages" count configurable
* fix Subversion 1.4.x revision log compatibility code regression * fix Subversion 1.4.x revision log compatibility code regression
* display sanitized error when authzfile is malformed * display sanitized error when authzfile is malformed
* handle file:/// Subversion rootpaths as local roots (issue #446)
* restore markup of URLs in file contents (issue #455) * restore markup of URLs in file contents (issue #455)
* optionally display last-committed metadata in roots view (issue #457) * optionally display last-committed metadata in roots view (issue #457)

55
INSTALL
View File

@@ -34,7 +34,7 @@ Congratulations on getting this far. :-)
(http://www.python.org/) (http://www.python.org/)
* Subversion, Version Control System, 1.3.1 or later * Subversion, Version Control System, 1.3.1 or later
(binary installation and Python bindings) (binary installation and Python bindings)
(http://subversion.apache.org/) (http://subversion.tigris.org/)
Optional: Optional:
@@ -176,24 +176,7 @@ APACHE CONFIGURATION
or /etc/local. Use the vendor documentation or the find utility if or /etc/local. Use the vendor documentation or the find utility if
in doubt. in doubt.
2) Depending on how your Apache configuration is setup by default, you 2) Configure Apache to expose ViewVC to users at the URL of your choice.
might need to explicitly allow high-level access to the ViewVC
install location.
<Directory <VIEWVC_INSTALLATION_DIRECTORY>>
Order allow,deny
Allow from all
</Directory>
For example, if ViewVC is installed in /usr/local/viewvc-1.0 on
your system:
<Directory /usr/local/viewvc-1.0>
Order allow,deny
Allow from all
</Directory>
3) Configure Apache to expose ViewVC to users at the URL of your choice.
ViewVC provides several different ways to do this. Choose one of ViewVC provides several different ways to do this. Choose one of
the following methods: the following methods:
@@ -204,13 +187,13 @@ APACHE CONFIGURATION
The ScriptAlias directive is very useful for pointing The ScriptAlias directive is very useful for pointing
directly to the viewvc.cgi script. Simply insert a line containing directly to the viewvc.cgi script. Simply insert a line containing
ScriptAlias /viewvc <VIEWVC_INSTALLATION_DIRECTORY>/bin/cgi/viewvc.cgi ScriptAlias /viewvc <VIEWVC_INSTALLATION_DIRECTORY>/bin/cgi/viewvc.cgi
into your httpd.conf file. Choose the location in httpd.conf where into your httpd.conf file. Choose the location in httpd.conf where
also the other ScriptAlias lines reside. Some examples: also the other ScriptAlias lines reside. Some examples:
ScriptAlias /viewvc /usr/local/viewvc-1.0/bin/cgi/viewvc.cgi ScriptAlias /viewvc /usr/local/viewvc-1.0/bin/cgi/viewvc.cgi
ScriptAlias /query /usr/local/viewvc-1.0/bin/cgi/query.cgi ScriptAlias /query /usr/local/viewvc-1.0/bin/cgi/query.cgi
---------------------------------------- ----------------------------------------
METHOD B: CGI mode in cgi-bin directory METHOD B: CGI mode in cgi-bin directory
@@ -218,10 +201,6 @@ APACHE CONFIGURATION
Copy the CGI scripts from Copy the CGI scripts from
<VIEWVC_INSTALLATION_DIRECTORY>/bin/cgi/*.cgi <VIEWVC_INSTALLATION_DIRECTORY>/bin/cgi/*.cgi
to the /cgi-bin/ directory configured in your httpd.conf file. to the /cgi-bin/ directory configured in your httpd.conf file.
You can override configuration file location using:
SetEnv VIEWVC_CONF_PATHNAME /etc/viewvc.conf
------------------------------------------ ------------------------------------------
METHOD C: CGI mode in ExecCGI'd directory METHOD C: CGI mode in ExecCGI'd directory
@@ -231,8 +210,8 @@ APACHE CONFIGURATION
to the directory of your choosing in the Document Root adding the following to the directory of your choosing in the Document Root adding the following
Apache directives for the directory in httpd.conf or an .htaccess file: Apache directives for the directory in httpd.conf or an .htaccess file:
Options +ExecCGI Options +ExecCGI
AddHandler cgi-script .cgi AddHandler cgi-script .cgi
Note: For this to work mod_cgi has to be loaded. And for the .htaccess file Note: For this to work mod_cgi has to be loaded. And for the .htaccess file
to be effective, "AllowOverride All" or "AllowOverride Options FileInfo" to be effective, "AllowOverride All" or "AllowOverride Options FileInfo"
@@ -248,10 +227,6 @@ APACHE CONFIGURATION
In httpd.conf, make sure that "AllowOverride All" or at least In httpd.conf, make sure that "AllowOverride All" or at least
"AllowOverride FileInfo Options" are enabled for the directory "AllowOverride FileInfo Options" are enabled for the directory
you copied the files to. you copied the files to.
You can override configuration file location using:
SetEnv VIEWVC_CONF_PATHNAME /etc/viewvc.conf
Note: If you are using Mod_Python under Apache 1.3 the tarball generation Note: If you are using Mod_Python under Apache 1.3 the tarball generation
feature may not work because it uses multithreading. This works fine feature may not work because it uses multithreading. This works fine
@@ -303,6 +278,13 @@ APACHE CONFIGURATION
ScriptAlias /viewvc /usr/local/viewvc/bin/wsgi/viewvc.fcgi ScriptAlias /viewvc /usr/local/viewvc/bin/wsgi/viewvc.fcgi
ScriptAlias /query /usr/local/viewvc/bin/wsgi/query.fcgi ScriptAlias /query /usr/local/viewvc/bin/wsgi/query.fcgi
3) Restart Apache.
The commands to do this vary. "httpd -k restart" and "apache -k
restart" are two common variants. On RedHat Linux it is done using
the command "/sbin/service httpd restart" and on SuSE Linux it is
done with "rcapache restart". Other systems use "apachectl restart".
4) [Optional] Add access control. 4) [Optional] Add access control.
In your httpd.conf you can control access to certain modules by In your httpd.conf you can control access to certain modules by
@@ -324,14 +306,7 @@ APACHE CONFIGURATION
http://<server_name>/viewvc/~checkout~/<module_name> http://<server_name>/viewvc/~checkout~/<module_name>
http://<server_name>/viewvc/<module_name>.tar.gz?view=tar http://<server_name>/viewvc/<module_name>.tar.gz?view=tar
5) Restart Apache. 5) Optional: Protect your ViewVC instance from server-whacking webcrawlers.
The commands to do this vary. "httpd -k restart" and "apache -k
restart" are two common variants. On RedHat Linux it is done using
the command "/sbin/service httpd restart" and on SuSE Linux it is
done with "rcapache restart". Other systems use "apachectl restart".
6) Optional: Protect your ViewVC instance from server-whacking webcrawlers.
As ViewVC is a web-based application which each page containing various As ViewVC is a web-based application which each page containing various
links to other pages and views, you can expect your server's performance links to other pages and views, you can expect your server's performance

View File

@@ -15,7 +15,7 @@
<blockquote> <blockquote>
<p><strong>Copyright &copy; 1999-2012 The ViewCVS Group. All rights <p><strong>Copyright &copy; 1999-2011 The ViewCVS Group. All rights
reserved.</strong></p> reserved.</strong></p>
<p>By using ViewVC, you agree to the terms and conditions set forth <p>By using ViewVC, you agree to the terms and conditions set forth
@@ -62,7 +62,6 @@
<li>March 18, 2009 &mdash; copyright years updated</li> <li>March 18, 2009 &mdash; copyright years updated</li>
<li>March 29, 2010 &mdash; copyright years updated</li> <li>March 29, 2010 &mdash; copyright years updated</li>
<li>February 18, 2011 &mdash; copyright years updated</li> <li>February 18, 2011 &mdash; copyright years updated</li>
<li>January 23, 2012 &mdash; copyright years updated</li>
</ul> </ul>
</body> </body>

View File

@@ -3,7 +3,7 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # 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 ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -3,7 +3,7 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # 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 ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # 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 ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # 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 ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # 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 ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # 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 ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2009 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # 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 ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # 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 ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2010 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # 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 ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # 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 ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2010 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # 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 ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 2004-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 2004-2008 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 2004-2007 James Henstridge # Copyright (C) 2004-2007 James Henstridge
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
@@ -153,7 +153,7 @@ class SvnRev:
fsroot = self._get_root_for_rev(rev) fsroot = self._get_root_for_rev(rev)
# find changes in the revision # find changes in the revision
editor = svn.repos.ChangeCollector(repo.fs, fsroot) editor = svn.repos.RevisionChangeCollector(repo.fs, rev)
e_ptr, e_baton = svn.delta.make_editor(editor) e_ptr, e_baton = svn.delta.make_editor(editor)
svn.repos.svn_repos_replay(fsroot, e_ptr, e_baton) svn.repos.svn_repos_replay(fsroot, e_ptr, e_baton)
@@ -164,33 +164,21 @@ class SvnRev:
continue continue
# deal with the change types we handle # deal with the change types we handle
action = None
base_root = None base_root = None
base_path = change.base_path
if change.base_path: if change.base_path:
base_root = self._get_root_for_rev(change.base_rev) base_root = self._get_root_for_rev(change.base_rev)
# figure out what kind of change this is, and get a diff if not change.path:
# object for it. note that prior to 1.4 Subversion's
# bindings didn't give us change.action, but that's okay
# because back then deleted paths always had a change.path
# of None.
if hasattr(change, 'action') \
and change.action == svn.repos.CHANGE_ACTION_DELETE:
action = 'remove'
elif not change.path:
action = 'remove' action = 'remove'
elif change.added: elif change.added:
action = 'add' action = 'add'
else: else:
action = 'change' action = 'change'
if action == 'remove': diffobj = svn.fs.FileDiff(base_root and base_root or None,
diffobj = svn.fs.FileDiff(base_root, base_path, None, None) base_root and change.base_path or None,
else: change.path and fsroot or None,
diffobj = svn.fs.FileDiff(base_root, base_path, change.path and change.path or None)
fsroot, change.path)
diff_fp = diffobj.get_pipe() diff_fp = diffobj.get_pipe()
plus, minus = _get_diff_counts(diff_fp) plus, minus = _get_diff_counts(diff_fp)
self.changes.append((path, action, plus, minus)) self.changes.append((path, action, plus, minus))
@@ -300,7 +288,7 @@ def usage():
located at REPOS-PATH. located at REPOS-PATH.
Usage: 1. %s [-v] rebuild REPOS-PATH Usage: 1. %s [-v] rebuild REPOS-PATH
2. %s [-v] update REPOS-PATH [REV[:REV2]] [--force] 2. %s [-v] update REPOS-PATH [REV:[REV2]] [--force]
3. %s [-v] purge REPOS-PATH 3. %s [-v] purge REPOS-PATH
1. Rebuild the commit database information for the repository located 1. Rebuild the commit database information for the repository located

View File

@@ -1,7 +1,7 @@
#!/usr/bin/python #!/usr/bin/python
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2010 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # 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 ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2010 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # 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 ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,7 +1,7 @@
#!/usr/bin/python #!/usr/bin/python
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2010 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # 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 ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2009 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # 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 ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -86,8 +86,8 @@
## cvs_roots: Specifies each of the CVS roots on your system and ## cvs_roots: Specifies each of the CVS roots on your system and
## assigns names to them. Each root should be given by a "name: path" ## assigns names to them. Each root should be given by a "name: path"
## value (where the path is an absolute filesystem path). Multiple roots ## value. Multiple roots should be separated by commas and can be
## should be separated by commas and can be placed on separate lines. ## placed on separate lines.
## ##
## Example: ## Example:
## cvs_roots = cvsroot: /opt/cvs/repos1, ## cvs_roots = cvsroot: /opt/cvs/repos1,
@@ -97,13 +97,8 @@
## svn_roots: Specifies each of the Subversion roots (repositories) on ## svn_roots: Specifies each of the Subversion roots (repositories) on
## your system and assigns names to them. Each root should be given by ## your system and assigns names to them. Each root should be given by
## a "name: path" value (where the path is an absolute filesystem path). ## a "name: path" value. Multiple roots should be separated by commas
## Multiple roots should be separated by commas and can be placed on ## and can be placed on separate lines.
## separate lines.
##
## NOTE: ViewVC offers *experimental* support for displaying remote
## Subversion repositories. Simply use the repository's URL instead
## of a local filesystem path when defining the root.
## ##
## Example: ## Example:
## svn_roots = svnrepos: /opt/svn/, ## svn_roots = svnrepos: /opt/svn/,
@@ -114,8 +109,7 @@
## root_parents: Specifies a list of directories under which any ## root_parents: Specifies a list of directories under which any
## number of repositories may reside. You can specify multiple root ## number of repositories may reside. You can specify multiple root
## parents separated by commas or new lines, each of which is of the ## parents separated by commas or new lines, each of which is of the
## form "path: type" (where the type is either "cvs" or "svn", and ## form "path: type" (where type is either "cvs" or "svn").
## the path is an absolute filesystem path).
## ##
## Rather than force you to add a new entry to 'cvs_roots' or ## Rather than force you to add a new entry to 'cvs_roots' or
## 'svn_roots' each time you create a new repository, ViewVC rewards ## 'svn_roots' each time you create a new repository, ViewVC rewards
@@ -351,43 +345,7 @@
## allowed_views: List the ViewVC views which are enabled. Views not ## allowed_views: List the ViewVC views which are enabled. Views not
## in this comma-delited list will not be served (or, will return an ## in this comma-delited list will not be served (or, will return an
## error on attempted access). ## error on attempted access).
## ## Possible values: "annotate", "co", "diff", "markup", "roots", "tar"
## Valid items for this list include: "annotate", "co", "diff", "markup",
## "roots", "tar".
##
## ----------+---------------------------------------------------------
## VIEW | DESCRIPTION
## ----------+---------------------------------------------------------
## annotate | The 'annotate' view shows the contents of a single
## | revision of a versioned file in exactly the same way as
## | the markup view, but with additional line-by-line
## | change attribution (the revision number, author, etc.
## | the most recent edit to that line of text as of the
## | displayed version).
## ----------+---------------------------------------------------------
## co | The 'co' (aka "checkout" or "download") view isn't
## | really a branded view at all, but allows for direct
## | downloading of the contents of a single revision of a
## | versioned file.
## ----------+---------------------------------------------------------
## diff | The 'diff' view displays line-based differences between
## | two revisions of a versioned file in a variety of
## | different user-selectable formats.
## ----------+---------------------------------------------------------
## markup | The 'markup' view shows the contents of a single
## | revision of a versioned file, with syntax highlighting
## | where possible and enabled. It can also optionally
## | show change log information for that revision of the
## | file.
## ----------+---------------------------------------------------------
## roots | The 'roots' view is a simple listing of the various
## | repositories which ViewVC has been configured to serve
## | to users.
## ----------+---------------------------------------------------------
## tar | The 'tar' view isn't a branded view, but generates
## | a GNU Tar archive file containing a single versioned
## | directory and its contents (recursively).
## ----------+---------------------------------------------------------
## ##
#allowed_views = annotate, diff, markup, roots #allowed_views = annotate, diff, markup, roots
@@ -433,26 +391,6 @@
## ##
#mangle_email_addresses = 0 #mangle_email_addresses = 0
## custom_log_formatting: Specifies mappings of regular expressions to
## substitution format strings used to URL-ize strings found in
## revision log messages. Multiple mappings (specified as
## REGEXP:FORMATSTRING) may be defined, separated by commas.
##
## NOTE: Due to a limitation of the configuration format, commas may
## not be used in the regular expression portion of each mapping.
## Commas "in the raw" may not be used in the format string portion,
## either, but you can probably use the URI-encoded form of the comma
## ("%2C") instead with no ill side-effects. If you must specify a
## colon character in either the regular expression or the format
## string, escape it with a preceding backslash ("\:").
##
## Example:
## custom_log_formatting =
## artf[0-9]+ : http://example.com/tracker?id=\0,
## issue ([0-9]+) : http://example.com/bug?id=\1&opts=full%2csortby=id
##
#custom_log_formatting =
## default_file_view: "log", "co", or "markup" ## default_file_view: "log", "co", or "markup"
## Controls whether the default view for file URLs is a checkout view or ## Controls whether the default view for file URLs is a checkout view or
## a log view. "log" is the default for backwards compatibility with old ## a log view. "log" is the default for backwards compatibility with old
@@ -665,13 +603,7 @@
#short_log_len = 80 #short_log_len = 80
## enable_syntax_coloration: Should we colorize known file content ## enable_syntax_coloration: Should we colorize known file content
## syntaxes? ## syntaxes? [Requires Pygments Python module]
##
## NOTE: This feature requires the Pygments Python module
## (http://pygments.org) and works only when ViewVC can determine the
## MIME content type of the file whose contents it wishes to colorize.
## Use the 'mime_types_files' configuration option to specify MIME
## type mapping files useful for making that determination.
## ##
#enable_syntax_coloration = 1 #enable_syntax_coloration = 1

View File

@@ -1,6 +1,6 @@
<html> <html>
<head> <head>
<title>ViewVC 1.1 Template Authoring Guide</title> <title>ViewVC 1.0 Template Authoring Guide</title>
<style> <style>
body { body {
background-color: rgb(180,193,205); background-color: rgb(180,193,205);
@@ -38,13 +38,13 @@ td {
</head> </head>
<body> <body>
<h1>ViewVC 1.1 Template Authoring Guide</h1> <h1>ViewVC 1.0 Template Authoring Guide</h1>
<div class="h2"> <div class="h2">
<h2 id="introduction">Introduction</h2> <h2 id="introduction">Introduction</h2>
<p>This document represents an (unfinished) attempt at providing <p>This document represents an (unfinished) attempt at providing
instructions for how to customize ViewVC's HTML output via documentation for how to customize ViewVC 1.0-dev's HTML output via
modification of its templates.</p> modification of its templates.</p>
</div> </div>
@@ -802,11 +802,6 @@ td {
<td>String</td> <td>String</td>
<td>This is a URL for the markup view of the left file.</td> <td>This is a URL for the markup view of the left file.</td>
</tr> </tr>
<tr class="varlevel1">
<td class="varname">patch_href</td>
<td>String</td>
<td>URL of the patch view for the file.</td>
</tr>
<tr class="varlevel1"> <tr class="varlevel1">
<td class="varname">raw_diff</td> <td class="varname">raw_diff</td>
<td>String</td> <td>String</td>
@@ -2183,12 +2178,6 @@ td {
<td>String</td> <td>String</td>
<td>Log message of last modification to the root.</td> <td>Log message of last modification to the root.</td>
</tr> </tr>
<tr class="varlevel2">
<td class="varname">roots.log_href</td>
<td>String</td>
<td>URL of log revision view for the top-most (root) directory of
the root (repository).</td>
</tr>
<tr class="varlevel2"> <tr class="varlevel2">
<td class="varname">roots.name</td> <td class="varname">roots.name</td>
<td>String</td> <td>String</td>

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2009 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # 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 ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2010 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 2000 Curt Hagenlocher <curt@hagenlocher.org> # Copyright (C) 2000 Curt Hagenlocher <curt@hagenlocher.org>
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
@@ -74,8 +74,7 @@ class HTMLBlameSource:
self.path_parts = path_parts self.path_parts = path_parts
self.diff_url = diff_url self.diff_url = diff_url
self.include_url = include_url self.include_url = include_url
self.annotation, self.revision = self.repos.annotate(path_parts, opt_rev, self.annotation, self.revision = self.repos.annotate(path_parts, opt_rev)
True)
def __getitem__(self, idx): def __getitem__(self, idx):
item = self.annotation.__getitem__(idx) item = self.annotation.__getitem__(idx)

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2007 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # 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 ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2011 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # 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 ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC
@@ -112,7 +112,6 @@ class Config:
_force_multi_value = ( _force_multi_value = (
# Configuration values with multiple, comma-separated values. # Configuration values with multiple, comma-separated values.
'allowed_views', 'allowed_views',
'custom_log_formatting',
'cvs_roots', 'cvs_roots',
'kv_files', 'kv_files',
'languages', 'languages',
@@ -399,7 +398,6 @@ class Config:
self.options.allowed_views = ['annotate', 'diff', 'markup', 'roots'] self.options.allowed_views = ['annotate', 'diff', 'markup', 'roots']
self.options.authorizer = None self.options.authorizer = None
self.options.mangle_email_addresses = 0 self.options.mangle_email_addresses = 0
self.options.custom_log_formatting = []
self.options.default_file_view = "log" self.options.default_file_view = "log"
self.options.http_expiration_time = 600 self.options.http_expiration_time = 600
self.options.generate_etags = 1 self.options.generate_etags = 1

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2011 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # 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 ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC
@@ -352,17 +352,9 @@ class CheckinDatabase:
match = " LIKE " match = " LIKE "
elif query_entry.match == "glob": elif query_entry.match == "glob":
match = " REGEXP " match = " REGEXP "
# Use fnmatch to translate the glob into a regular # use fnmatch to translate the glob into a regexp
# expression. Sadly, we have to account for the fact
# that in Python 2.6, fnmatch.translate() started
# sticking '\Z(?ms)' at the end of the regular
# expression instead of just '$', and doesn't prepend
# the '^'.
data = fnmatch.translate(data) data = fnmatch.translate(data)
if data[0] != '^': if data[0] != '^': data = '^' + data
data = '^' + data
if data[-7:] == '\Z(?ms)':
data = data[:-7] + '$'
elif query_entry.match == "regex": elif query_entry.match == "regex":
match = " REGEXP " match = " REGEXP "
elif query_entry.match == "notregex": elif query_entry.match == "notregex":

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # 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 ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # 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 ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2010 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # 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 ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2009 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # 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 ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2010 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # 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 ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2010 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # 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 ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC
@@ -233,6 +233,9 @@ class WsgiServer(Server):
self.header(status='301 Moved') self.header(status='301 Moved')
self._wsgi_write('This document is located <a href="%s">here</a>.' % url) self._wsgi_write('This document is located <a href="%s">here</a>.' % url)
def escape(self, s, quote = None):
return cgi.escape(s, quote)
def getenv(self, name, value=None): def getenv(self, name, value=None):
return self._environ.get(name, value) return self._environ.get(name, value)

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 2006-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 2006-2010 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # 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 ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 2006-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 2006-2010 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # 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 ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 2008-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 2008-2010 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # 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 ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 2006-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 2006-2011 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # 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 ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC
@@ -34,9 +34,9 @@ class ViewVCAuthorizer(vcauth.GenericViewVCAuthorizer):
# See if the admin wants us to do case normalization of usernames. # See if the admin wants us to do case normalization of usernames.
self.force_username_case = params.get('force_username_case') self.force_username_case = params.get('force_username_case')
if self.force_username_case == "upper": if self.force_username_case == "upper":
self.username = username and string.upper(username) or username self.username = username.upper()
elif self.force_username_case == "lower": elif self.force_username_case == "lower":
self.username = username and string.lower(username) or username self.username = username.lower()
elif not self.force_username_case: elif not self.force_username_case:
self.username = username self.username = username
else: else:

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2011 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # 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 ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC
@@ -170,19 +170,12 @@ class Repository:
Return value is a python file object Return value is a python file object
""" """
def annotate(self, path_parts, rev, include_text=False): def annotate(self, path_parts, rev):
"""Return a list of Annotation object, sorted by their """Return a list of annotate file content lines and a revision.
"line_number" components, which describe the lines of given
version of a file.
The file path is specified as a list of components, relative to The result is a list of Annotation objects, sorted by their
the root of the repository. e.g. ["subdir1", "subdir2", "filename"] line_number components.
"""
rev is the revision of the item to return information about.
If include_text is true, populate the Annotation objects' "text"
members with the corresponding line of file content; otherwise,
leave that member set to None."""
def revinfo(self, rev): def revinfo(self, rev):
"""Return information about a global revision """Return information about a global revision

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # 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 ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC
@@ -14,7 +14,6 @@ import os.path
def canonicalize_rootpath(rootpath): def canonicalize_rootpath(rootpath):
assert os.path.isabs(rootpath)
return os.path.normpath(rootpath) return os.path.normpath(rootpath)
@@ -23,7 +22,6 @@ def expand_root_parent(parent_path):
# "CVSROOT/config" is added the set of returned roots. Or, if the # "CVSROOT/config" is added the set of returned roots. Or, if the
# PARENT_PATH itself contains a child "CVSROOT/config", then all its # PARENT_PATH itself contains a child "CVSROOT/config", then all its
# subdirectories are returned as roots. # subdirectories are returned as roots.
assert os.path.isabs(parent_path)
roots = {} roots = {}
subpaths = os.listdir(parent_path) subpaths = os.listdir(parent_path)
cvsroot = os.path.exists(os.path.join(parent_path, "CVSROOT", "config")) cvsroot = os.path.exists(os.path.join(parent_path, "CVSROOT", "config"))

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2011 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # 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 ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC
@@ -332,13 +332,13 @@ class BinCVSRepository(BaseCVSRepository):
args = rcs_args args = rcs_args
return popen.popen(cmd, args, mode, capture_err) return popen.popen(cmd, args, mode, capture_err)
def annotate(self, path_parts, rev=None, include_text=False): def annotate(self, path_parts, rev=None):
if self.itemtype(path_parts, rev) != vclib.FILE: # does auth-check if self.itemtype(path_parts, rev) != vclib.FILE: # does auth-check
raise vclib.Error("Path '%s' is not a file." raise vclib.Error("Path '%s' is not a file."
% (string.join(path_parts, "/"))) % (string.join(path_parts, "/")))
from vclib.ccvs import blame from vclib.ccvs import blame
source = blame.BlameSource(self.rcsfile(path_parts, 1), rev, include_text) source = blame.BlameSource(self.rcsfile(path_parts, 1), rev)
return source, source.revision return source, source.revision
def revinfo(self, rev): def revinfo(self, rev):

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2011 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 2000 Curt Hagenlocher <curt@hagenlocher.org> # Copyright (C) 2000 Curt Hagenlocher <curt@hagenlocher.org>
# #
# By using this file, you agree to the terms and conditions set forth in # By using this file, you agree to the terms and conditions set forth in
@@ -414,7 +414,7 @@ class CVSParser(rcsparse.Sink):
class BlameSource: class BlameSource:
def __init__(self, rcs_file, opt_rev=None, include_text=False): def __init__(self, rcs_file, opt_rev=None):
# Parse the CVS file # Parse the CVS file
parser = CVSParser() parser = CVSParser()
revision = parser.parse_cvs_file(rcs_file, opt_rev) revision = parser.parse_cvs_file(rcs_file, opt_rev)
@@ -428,7 +428,6 @@ class BlameSource:
self.lines = lines self.lines = lines
self.num_lines = count self.num_lines = count
self.parser = parser self.parser = parser
self.include_text = include_text
# keep track of where we are during an iteration # keep track of where we are during an iteration
self.idx = -1 self.idx = -1
@@ -448,8 +447,6 @@ class BlameSource:
line_number = idx + 1 line_number = idx + 1
author = self.parser.revision_author[rev] author = self.parser.revision_author[rev]
thisline = self.lines[idx] thisline = self.lines[idx]
if not self.include_text:
thisline = None
### TODO: Put a real date in here. ### TODO: Put a real date in here.
item = vclib.Annotation(thisline, line_number, rev, prev_rev, author, None) item = vclib.Annotation(thisline, line_number, rev, prev_rev, author, None)
self.last = item self.last = item

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2011 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # 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 ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC
@@ -142,11 +142,11 @@ class CCVSRepository(BaseCVSRepository):
return vclib._diff_fp(temp1, temp2, info1, info2, return vclib._diff_fp(temp1, temp2, info1, info2,
self.utilities.diff or 'diff', diff_args) self.utilities.diff or 'diff', diff_args)
def annotate(self, path_parts, rev=None, include_text=False): def annotate(self, path_parts, rev=None):
if self.itemtype(path_parts, rev) != vclib.FILE: # does auth-check if self.itemtype(path_parts, rev) != vclib.FILE: # does auth-check
raise vclib.Error("Path '%s' is not a file." raise vclib.Error("Path '%s' is not a file."
% (string.join(path_parts, "/"))) % (string.join(path_parts, "/")))
source = blame.BlameSource(self.rcsfile(path_parts, 1), rev, include_text) source = blame.BlameSource(self.rcsfile(path_parts, 1), rev)
return source, source.revision return source, source.revision
def revinfo(self, rev): def revinfo(self, rev):

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # 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 ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2011 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # 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 ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # 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 ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # 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 ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # 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 ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2011 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # 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 ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC
@@ -19,46 +19,26 @@ import urllib
_re_url = re.compile('^(http|https|file|svn|svn\+[^:]+)://') _re_url = re.compile('^(http|https|file|svn|svn\+[^:]+)://')
def _canonicalize_path(path):
import svn.core
try:
return svn.core.svn_path_canonicalize(path)
except AttributeError: # svn_path_canonicalize() appeared in 1.4.0 bindings
# There's so much more that we *could* do here, but if we're
# here at all its because there's a really old Subversion in
# place, and those older Subversion versions cared quite a bit
# less about the specifics of path canonicalization.
if re.search(_re_url, path):
return path.rstrip('/')
else:
return os.path.normpath(path)
def canonicalize_rootpath(rootpath): def canonicalize_rootpath(rootpath):
# Try to canonicalize the rootpath using Subversion semantics. try:
rootpath = _canonicalize_path(rootpath) import svn.core
return svn.core.svn_path_canonicalize(rootpath)
# ViewVC's support for local repositories is more complete and more except:
# performant than its support for remote ones, so if we're on a if os.name == 'posix':
# Unix-y system and we have a file:/// URL, convert it to a local rootpath_lower = rootpath.lower()
# path instead. if rootpath_lower in ['file://localhost',
if os.name == 'posix': 'file://localhost/',
rootpath_lower = rootpath.lower() 'file://',
if rootpath_lower in ['file://localhost', 'file:///'
'file://localhost/', ]:
'file://', return '/'
'file:///' if rootpath_lower.startswith('file://localhost/'):
]: return os.path.normpath(urllib.unquote(rootpath[16:]))
return '/' elif rootpath_lower.startswith('file:///'):
if rootpath_lower.startswith('file://localhost/'): return os.path.normpath(urllib.unquote(rootpath[7:]))
rootpath = os.path.normpath(urllib.unquote(rootpath[16:])) if re.search(_re_url, rootpath):
elif rootpath_lower.startswith('file:///'): return rootpath.rstrip('/')
rootpath = os.path.normpath(urllib.unquote(rootpath[7:])) return os.path.normpath(rootpath)
# Ensure that we have an absolute path (or URL), and return.
if not re.search(_re_url, rootpath):
assert os.path.isabs(rootpath)
return rootpath
def expand_root_parent(parent_path): def expand_root_parent(parent_path):
@@ -68,7 +48,6 @@ def expand_root_parent(parent_path):
else: else:
# Any subdirectories of PARENT_PATH which themselves have a child # Any subdirectories of PARENT_PATH which themselves have a child
# "format" are returned as roots. # "format" are returned as roots.
assert os.path.isabs(parent_path)
subpaths = os.listdir(parent_path) subpaths = os.listdir(parent_path)
for rootname in subpaths: for rootname in subpaths:
rootpath = os.path.join(parent_path, rootname) rootpath = os.path.join(parent_path, rootname)

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2011 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # 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 ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC
@@ -20,10 +20,7 @@ import re
import tempfile import tempfile
import time import time
import urllib import urllib
from svn_repos import Revision, SVNChangedPath, _datestr_to_date, \ from svn_repos import Revision, SVNChangedPath, _datestr_to_date, _compare_paths, _path_parts, _cleanup_path, _rev2optrev, _fix_subversion_exception, _split_revprops
_compare_paths, _path_parts, _cleanup_path, \
_rev2optrev, _fix_subversion_exception, \
_split_revprops, _canonicalize_path
from svn import core, delta, client, wc, ra from svn import core, delta, client, wc, ra
@@ -341,7 +338,7 @@ class RemoteSubversionRepository(vclib.Repository):
_rev2optrev(rev), 0, self.ctx) _rev2optrev(rev), 0, self.ctx)
return pairs and pairs[0][1] or {} return pairs and pairs[0][1] or {}
def annotate(self, path_parts, rev, include_text=False): def annotate(self, path_parts, rev):
path = self._getpath(path_parts) path = self._getpath(path_parts)
if self.itemtype(path_parts, rev) != vclib.FILE: # does auth-check if self.itemtype(path_parts, rev) != vclib.FILE: # does auth-check
raise vclib.Error("Path '%s' is not a file." % path) raise vclib.Error("Path '%s' is not a file." % path)
@@ -355,13 +352,12 @@ class RemoteSubversionRepository(vclib.Repository):
prev_rev = None prev_rev = None
if revision > 1: if revision > 1:
prev_rev = revision - 1 prev_rev = revision - 1
if not include_text: blame_data.append(vclib.Annotation(line, line_no+1, revision, prev_rev,
line = None
blame_data.append(vclib.Annotation(line, line_no + 1, revision, prev_rev,
author, None)) author, None))
client.svn_client_blame(url, _rev2optrev(1), _rev2optrev(rev), client.svn_client_blame(url, _rev2optrev(1), _rev2optrev(rev),
_blame_cb, self.ctx) _blame_cb, self.ctx)
return blame_data, rev return blame_data, rev
def revinfo(self, rev): def revinfo(self, rev):
@@ -411,11 +407,8 @@ class RemoteSubversionRepository(vclib.Repository):
if rev is None or rev == 'HEAD': if rev is None or rev == 'HEAD':
return self.youngest return self.youngest
try: try:
if type(rev) == type(''):
while rev[0] == 'r':
rev = rev[1:]
rev = int(rev) rev = int(rev)
except: except ValueError:
raise vclib.InvalidRevision(rev) raise vclib.InvalidRevision(rev)
if (rev < 0) or (rev > self.youngest): if (rev < 0) or (rev > self.youngest):
raise vclib.InvalidRevision(rev) raise vclib.InvalidRevision(rev)
@@ -424,8 +417,7 @@ class RemoteSubversionRepository(vclib.Repository):
def _geturl(self, path=None): def _geturl(self, path=None):
if not path: if not path:
return self.rootpath return self.rootpath
path = self.rootpath + '/' + urllib.quote(path) return self.rootpath + '/' + urllib.quote(path, "/*~")
return _canonicalize_path(path)
def _get_dirents(self, path, rev): def _get_dirents(self, path, rev):
"""Return a 2-type of dirents and locks, possibly reading/writing """Return a 2-type of dirents and locks, possibly reading/writing

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2011 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # 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 ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC
@@ -22,7 +22,6 @@ import time
import tempfile import tempfile
import popen import popen
import re import re
import urllib
from svn import fs, repos, core, client, delta from svn import fs, repos, core, client, delta
@@ -43,14 +42,7 @@ def _fix_subversion_exception(e):
e.apr_err = e[1] e.apr_err = e[1]
if not hasattr(e, 'message'): if not hasattr(e, 'message'):
e.message = e[0] e.message = e[0]
### Pre-1.4 Subversion doesn't have svn_path_canonicalize()
def _canonicalize_path(path):
try:
return core.svn_path_canonicalize(path)
except AttributeError:
return path
def _allow_all(root, path, pool): def _allow_all(root, path, pool):
"""Generic authz_read_func that permits access to all paths""" """Generic authz_read_func that permits access to all paths"""
return 1 return 1
@@ -116,16 +108,11 @@ def _rev2optrev(rev):
def _rootpath2url(rootpath, path): def _rootpath2url(rootpath, path):
rootpath = os.path.abspath(rootpath) rootpath = os.path.abspath(rootpath)
drive, rootpath = os.path.splitdrive(rootpath) if rootpath and rootpath[0] != '/':
rootpath = '/' + rootpath
if os.sep != '/': if os.sep != '/':
rootpath = string.replace(rootpath, os.sep, '/') rootpath = string.replace(rootpath, os.sep, '/')
rootpath = urllib.quote(rootpath) return 'file://' + string.join([rootpath, path], "/")
path = urllib.quote(path)
if drive:
url = 'file:///' + drive + rootpath + '/' + path
else:
url = 'file://' + rootpath + '/' + path
return _canonicalize_path(url)
# Given a dictionary REVPROPS of revision properties, pull special # Given a dictionary REVPROPS of revision properties, pull special
@@ -295,11 +282,10 @@ class FileContentsPipe:
class BlameSource: class BlameSource:
def __init__(self, local_url, rev, first_rev, include_text, config_dir): def __init__(self, local_url, rev, first_rev, config_dir):
self.idx = -1 self.idx = -1
self.first_rev = first_rev self.first_rev = first_rev
self.blame_data = [] self.blame_data = []
self.include_text = include_text
ctx = client.svn_client_create_context() ctx = client.svn_client_create_context()
core.svn_config_ensure(config_dir) core.svn_config_ensure(config_dir)
@@ -320,8 +306,6 @@ class BlameSource:
prev_rev = None prev_rev = None
if rev > self.first_rev: if rev > self.first_rev:
prev_rev = rev - 1 prev_rev = rev - 1
if not self.include_text:
text = None
self.blame_data.append(vclib.Annotation(text, line_no + 1, rev, self.blame_data.append(vclib.Annotation(text, line_no + 1, rev,
prev_rev, author, None)) prev_rev, author, None))
@@ -537,7 +521,7 @@ class LocalSubversionRepository(vclib.Repository):
fsroot = self._getroot(rev) fsroot = self._getroot(rev)
return fs.node_proplist(fsroot, path) return fs.node_proplist(fsroot, path)
def annotate(self, path_parts, rev, include_text=False): def annotate(self, path_parts, rev):
path = self._getpath(path_parts) path = self._getpath(path_parts)
path_type = self.itemtype(path_parts, rev) # does auth-check path_type = self.itemtype(path_parts, rev) # does auth-check
if path_type != vclib.FILE: if path_type != vclib.FILE:
@@ -548,8 +532,8 @@ class LocalSubversionRepository(vclib.Repository):
{'svn_cross_copies': 1}) {'svn_cross_copies': 1})
youngest_rev, youngest_path = history[0] youngest_rev, youngest_path = history[0]
oldest_rev, oldest_path = history[-1] oldest_rev, oldest_path = history[-1]
source = BlameSource(_rootpath2url(self.rootpath, path), youngest_rev, source = BlameSource(_rootpath2url(self.rootpath, path),
oldest_rev, include_text, self.config_dir) youngest_rev, oldest_rev, self.config_dir)
return source, youngest_rev return source, youngest_rev
def revinfo(self, rev): def revinfo(self, rev):
@@ -825,11 +809,8 @@ class LocalSubversionRepository(vclib.Repository):
if rev is None or rev == 'HEAD': if rev is None or rev == 'HEAD':
return self.youngest return self.youngest
try: try:
if type(rev) == type(''):
while rev[0] == 'r':
rev = rev[1:]
rev = int(rev) rev = int(rev)
except: except ValueError:
raise vclib.InvalidRevision(rev) raise vclib.InvalidRevision(rev)
if (rev < 0) or (rev > self.youngest): if (rev < 0) or (rev > self.youngest):
raise vclib.InvalidRevision(rev) raise vclib.InvalidRevision(rev)

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2011 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # 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 ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC
@@ -14,7 +14,7 @@
# #
# ----------------------------------------------------------------------- # -----------------------------------------------------------------------
__version__ = '1.1.14' __version__ = '1.1.11'
# this comes from our library; measure the startup time # this comes from our library; measure the startup time
import debug import debug
@@ -1099,29 +1099,6 @@ _re_rewrite_email = re.compile('([-a-zA-Z0-9_.\+]+)@'
# Matches revision references # Matches revision references
_re_rewrite_svnrevref = re.compile(r'\b(r|rev #?|revision #?)([0-9]+)\b') _re_rewrite_svnrevref = re.compile(r'\b(r|rev #?|revision #?)([0-9]+)\b')
class ViewVCHtmlFormatterTokens:
def __init__(self, tokens):
self.tokens = tokens
def get_result(self, maxlen=0):
"""Format the tokens per the registered set of formatters, and
limited to MAXLEN visible characters (or unlimited if MAXLEN is
0). Return a 3-tuple containing the formatted result string, the
number of visible characters in the result string, and a boolean
flag indicating whether or not S was truncated."""
out = ''
out_len = 0
for token in self.tokens:
chunk, chunk_len = token.converter(token.match, token.userdata, maxlen)
out = out + chunk
out_len = out_len + chunk_len
if maxlen:
maxlen = maxlen - chunk_len
if maxlen <= 0:
return out, out_len, 1
return out, out_len, 0
class ViewVCHtmlFormatter: class ViewVCHtmlFormatter:
"""Format a string as HTML-encoded output with customizable markup """Format a string as HTML-encoded output with customizable markup
rules, for example turning strings that look like URLs into anchor links. rules, for example turning strings that look like URLs into anchor links.
@@ -1207,30 +1184,6 @@ class ViewVCHtmlFormatter:
sapi.escape(trunc_s)), \ sapi.escape(trunc_s)), \
len(trunc_s) len(trunc_s)
def format_custom_url(self, mobj, userdata, maxlen=0):
"""Return a 2-tuple containing:
- the text represented by MatchObject MOBJ, formatted as an
linkified URL created by substituting match groups 0-9 into
USERDATA (which is a format string that uses \N to
represent the substitution locations) and with no more than
MAXLEN characters in the non-HTML-tag portions. If MAXLEN
is 0, there is no maximum.
- the number of characters returned.
"""
format = userdata
text = mobj.group(0)
url = format
for i in range(9):
try:
repl = mobj.group(i)
except:
repl = ''
url = url.replace('\%d' % (i), repl)
trunc_s = maxlen and text[:maxlen] or text
return '<a href="%s">%s</a>' % (sapi.escape(url),
sapi.escape(trunc_s)), \
len(trunc_s)
def format_text(self, s, unused, maxlen=0): def format_text(self, s, unused, maxlen=0):
"""Return a 2-tuple containing: """Return a 2-tuple containing:
- the text S, HTML-escaped, containing no more than MAXLEN - the text S, HTML-escaped, containing no more than MAXLEN
@@ -1263,14 +1216,20 @@ class ViewVCHtmlFormatter:
""" """
out = '' out = ''
out_len = 0 out_len = 0
tokens = self.tokenize_text(s) for token in self._tokenize_text(s):
return tokens.get_result() chunk, chunk_len = token.converter(token.match, token.userdata, maxlen)
out = out + chunk
out_len = out_len + chunk_len
if maxlen:
maxlen = maxlen - chunk_len
if maxlen <= 0:
return out, out_len, 1
return out, out_len, 0
def tokenize_text(self, s): def _entity_encode(self, s):
"""Return a ViewVCHtmlFormatterTokens object containing the tokens return string.join(map(lambda x: '&#%d;' % (ord(x)), s), '')
created when parsing the string S. Callers can use that object's
get_result() function to retrieve HTML-formatted text. def _tokenize_text(self, s):
"""
tokens = [] tokens = []
# We could just have a "while s:" here instead of "for line: while # We could just have a "while s:" here instead of "for line: while
# line:", but for really large log messages with heavy # line:", but for really large log messages with heavy
@@ -1317,79 +1276,36 @@ class ViewVCHtmlFormatter:
converter=self.format_text, converter=self.format_text,
userdata=None)) userdata=None))
line = '' line = ''
return ViewVCHtmlFormatterTokens(tokens) return tokens
def _entity_encode(self, s):
return string.join(map(lambda x: '&#%d;' % (ord(x)), s), '')
class LogFormatter: def format_log(request, log, maxlen=0, htmlize=1):
def __init__(self, request, log): if not log:
self.request = request return log
self.log = log or ''
self.tokens = None
self.cache = {} # (maxlen, htmlize) => resulting_log
def get(self, maxlen=0, htmlize=1): cfg = request.cfg
cfg = self.request.cfg if htmlize:
lf = ViewVCHtmlFormatter()
# Prefer the cache. lf.add_formatter(_re_rewrite_url, lf.format_url)
if self.cache.has_key((maxlen, htmlize)): if request.roottype == 'svn':
return self.cache[(maxlen, htmlize)] def revision_to_url(rev):
return request.get_url(view_func=view_revision,
# If we are HTML-izing... params={'revision': rev},
if htmlize: escape=1)
# ...and we don't yet have ViewVCHtmlFormatter() object tokens... lf.add_formatter(_re_rewrite_svnrevref, lf.format_svnrevref,
if not self.tokens: revision_to_url)
# ... then get them. if cfg.options.mangle_email_addresses == 2:
lf = ViewVCHtmlFormatter() lf.add_formatter(_re_rewrite_email, lf.format_email_truncated)
elif cfg.options.mangle_email_addresses == 1:
# Rewrite URLs. lf.add_formatter(_re_rewrite_email, lf.format_email_obfuscated)
lf.add_formatter(_re_rewrite_url, lf.format_url)
# Rewrite Subversion revision references.
if self.request.roottype == 'svn':
def revision_to_url(rev):
return self.request.get_url(view_func=view_revision,
params={'revision': rev},
escape=1)
lf.add_formatter(_re_rewrite_svnrevref, lf.format_svnrevref,
revision_to_url)
# Rewrite email addresses.
if cfg.options.mangle_email_addresses == 2:
lf.add_formatter(_re_rewrite_email, lf.format_email_truncated)
elif cfg.options.mangle_email_addresses == 1:
lf.add_formatter(_re_rewrite_email, lf.format_email_obfuscated)
else:
lf.add_formatter(_re_rewrite_email, lf.format_email)
# Add custom rewrite handling per configuration.
for rule in cfg.options.custom_log_formatting:
rule = rule.replace('\\:', '\x01')
regexp, format = map(lambda x: x.strip(), rule.split(':', 1))
regexp = regexp.replace('\x01', ':')
format = format.replace('\x01', ':')
lf.add_formatter(re.compile(regexp), lf.format_custom_url, format)
# Tokenize the log message.
self.tokens = lf.tokenize_text(self.log)
# Use our formatter to ... you know ... format.
log, log_len, truncated = self.tokens.get_result(maxlen)
result_log = log + (truncated and '&hellip;' or '')
# But if we're not HTML-izing...
else: else:
# ...then do much more simplistic transformations as necessary. lf.add_formatter(_re_rewrite_email, lf.format_email)
if cfg.options.mangle_email_addresses == 2: log, log_len, truncated = lf.get_result(log, maxlen)
log = re.sub(_re_rewrite_email, r'\1@...', log) return log + (truncated and '&hellip;' or '')
result_log = maxlen and log[:maxlen] or log else:
if cfg.options.mangle_email_addresses == 2:
# In either case, populate the cache and return the results. log = re.sub(_re_rewrite_email, r'\1@...', log)
self.cache[(maxlen, htmlize)] = result_log return maxlen and log[:maxlen] or log
return result_log
_time_desc = { _time_desc = {
1 : 'second', 1 : 'second',
@@ -1631,36 +1547,33 @@ def markup_escaped_urls(s):
return "<a href=\"%s\">%s</a>" % (unescaped_url, url) return "<a href=\"%s\">%s</a>" % (unescaped_url, url)
return re.sub(_re_rewrite_escaped_url, _url_repl, s) return re.sub(_re_rewrite_escaped_url, _url_repl, s)
def markup_stream_pygments(request, cfg, blame_data, fp, filename,
def markup_stream(request, cfg, blame_data, file_lines, filename, mime_type, encoding):
mime_type, encoding, colorize): # Determine if we should use Pygments to highlight our output.
"""Return the contents of a versioned file as a list of # Reasons not to include a) being told not to by the configuration,
vclib.Annotation objects, each representing one line of the file's # b) not being able to import the Pygments modules, and c) Pygments
contents. Use BLAME_DATA as the annotation information for the file # not having a lexer for our file's format.
if provided. Use FILE_LINES as the lines of file content text blame_source = []
themselves. MIME_TYPE is the MIME content type of the file; if blame_data:
ENCODING is its character encoding. If COLORIZE is true, attempt to for i in blame_data:
apply syntax coloration to the file contents, and use the i.text = sapi.escape(i.text)
HTML-marked-up results as the text in the return vclib.Annotation i.diff_href = None
objects.""" if i.prev_rev:
i.diff_href = request.get_url(view_func=view_diff,
# Nothing to mark up? So be it. params={'r1': i.prev_rev,
if not file_lines: 'r2': i.rev},
return [] escape=1, partial=1)
blame_source.append(i)
# Determine if we should (and can) use Pygments to highlight our blame_data = blame_source
# output. Reasons not to include a) being told not to by the lexer = None
# configuration, b) not being able to import the Pygments modules, use_pygments = cfg.options.enable_syntax_coloration
# and c) Pygments not having a lexer for our file's format. try:
pygments_lexer = None
if colorize:
from pygments import highlight from pygments import highlight
from pygments.formatters import HtmlFormatter from pygments.formatters import HtmlFormatter
from pygments.lexers import ClassNotFound, \ from pygments.lexers import ClassNotFound, \
get_lexer_by_name, \ get_lexer_by_name, \
get_lexer_for_mimetype, \ get_lexer_for_mimetype, \
get_lexer_for_filename, \ get_lexer_for_filename
guess_lexer
if not encoding: if not encoding:
encoding = 'guess' encoding = 'guess'
if cfg.options.detect_encoding: if cfg.options.detect_encoding:
@@ -1669,50 +1582,50 @@ def markup_stream(request, cfg, blame_data, file_lines, filename,
encoding = 'chardet' encoding = 'chardet'
except (SyntaxError, ImportError): except (SyntaxError, ImportError):
pass pass
try:
# First, see if there's a Pygments lexer associated with MIME_TYPE. lexer = get_lexer_for_mimetype(mime_type,
if mime_type: encoding=encoding,
tabsize=cfg.options.tabsize,
stripnl=False)
except ClassNotFound:
try: try:
pygments_lexer = get_lexer_for_mimetype(mime_type, lexer = get_lexer_for_filename(filename,
encoding=encoding, encoding=encoding,
tabsize=cfg.options.tabsize, tabsize=cfg.options.tabsize,
stripnl=False) stripnl=False)
except ClassNotFound: except ClassNotFound:
pygments_lexer = None use_pygments = 0
except ImportError:
use_pygments = 0
# If we've no lexer thus far, try to find one based on the FILENAME. # If we aren't going to be highlighting anything, just return the
if not pygments_lexer: # BLAME_SOURCE. If there's no blame_source, we'll generate a fake
try: # one from the file contents we fetch with PATH and REV.
pygments_lexer = get_lexer_for_filename(filename, if not use_pygments:
encoding=encoding, if blame_source:
tabsize=cfg.options.tabsize, class BlameSourceTabsizeWrapper:
stripnl=False) def __init__(self, blame_source, tabsize):
except ClassNotFound: self.blame_source = blame_source
pygments_lexer = None self.tabsize = cfg.options.tabsize
def __getitem__(self, idx):
# Still no lexer? If we've reason to believe this is a text item = self.blame_source.__getitem__(idx)
# file, try to guess the lexer based on the file's content. item.text = string.expandtabs(item.text, self.tabsize)
if not pygments_lexer and is_text(mime_type) and file_lines: item.text = markup_escaped_urls(item.text)
try: return item
pygments_lexer = guess_lexer(file_lines[0]) return BlameSourceTabsizeWrapper(blame_source, cfg.options.tabsize)
except ClassNotFound: else:
pygments_lexer = None lines = []
line_no = 0
# If we aren't highlighting, just return an amalgamation of the while 1:
# BLAME_DATA (if any) and the FILE_LINES. line = fp.readline()
if not pygments_lexer: if not line:
lines = [] break
for i in range(len(file_lines)): line_no = line_no + 1
line = file_lines[i] line = sapi.escape(string.expandtabs(line, cfg.options.tabsize))
line = sapi.escape(string.expandtabs(line, cfg.options.tabsize)) line = markup_escaped_urls(line)
line = markup_escaped_urls(line) item = vclib.Annotation(line, line_no, None, None, None, None)
if blame_data: item.diff_href = None
blame_item = blame_data[i] lines.append(item)
blame_item.text = line
else:
blame_item = vclib.Annotation(line, i + 1, None, None, None, None)
blame_item.diff_href = None
lines.append(blame_item)
return lines return lines
# If we get here, we're highlighting something. # If we get here, we're highlighting something.
@@ -1736,9 +1649,8 @@ def markup_stream(request, cfg, blame_data, file_lines, filename,
item.diff_href = None item.diff_href = None
self.blame_data.append(item) self.blame_data.append(item)
self.line_no = self.line_no + 1 self.line_no = self.line_no + 1
ps = PygmentsSink(blame_source)
ps = PygmentsSink(blame_data) highlight(fp.read(), lexer,
highlight(string.join(file_lines, ''), pygments_lexer,
HtmlFormatter(nowrap=True, HtmlFormatter(nowrap=True,
classprefix="pygments-", classprefix="pygments-",
encoding='utf-8'), ps) encoding='utf-8'), ps)
@@ -1777,8 +1689,7 @@ def get_itemprops(request, path_parts, rev):
propnames.sort() propnames.sort()
props = [] props = []
for name in propnames: for name in propnames:
lf = LogFormatter(request, itemprops[name]) value = format_log(request, itemprops[name])
value = lf.get(maxlen=0, htmlize=1)
undisplayable = ezt.boolean(0) undisplayable = ezt.boolean(0)
# skip non-utf8 property names # skip non-utf8 property names
try: try:
@@ -1837,71 +1748,34 @@ def markup_or_annotate(request, is_annotate):
fp.close() fp.close()
if check_freshness(request, None, revision, weak=1): if check_freshness(request, None, revision, weak=1):
return return
if is_annotate: annotation = 'binary'
annotation = 'binary'
image_src_href = request.get_url(view_func=view_checkout, image_src_href = request.get_url(view_func=view_checkout,
params={'revision': rev}, escape=1) params={'revision': rev}, escape=1)
# Not a viewable image. # Not a viewable image.
else: else:
blame_data = None blame_source = None
# If this was an annotation request, try to annotate this file.
# If something goes wrong, that's okay -- we'll gracefully revert
# to a plain markup display.
if is_annotate: if is_annotate:
# Try to annotate this file, but don't croak if we fail.
try: try:
blame_source, revision = request.repos.annotate(path, rev, False) blame_source, revision = request.repos.annotate(path, rev)
annotation = 'annotated'
if check_freshness(request, None, revision, weak=1): if check_freshness(request, None, revision, weak=1):
return return
# Create BLAME_DATA list from BLAME_SOURCE, adding diff_href
# items to each relevant "line".
blame_data = []
for item in blame_source:
item.diff_href = None
if item.prev_rev:
item.diff_href = request.get_url(view_func=view_diff,
params={'r1': item.prev_rev,
'r2': item.rev},
escape=1, partial=1)
blame_data.append(item)
annotation = 'annotated'
except vclib.NonTextualFileContents: except vclib.NonTextualFileContents:
annotation = 'binary' annotation = 'binary'
except: except:
annotation = 'error' annotation = 'error'
# Grab the file contents.
fp, revision = request.repos.openfile(path, rev, {'cvs_oldkeywords' : 1}) fp, revision = request.repos.openfile(path, rev, {'cvs_oldkeywords' : 1})
if check_freshness(request, None, revision, weak=1): if check_freshness(request, None, revision, weak=1):
fp.close() fp.close()
return return
file_lines = fp.readlines() lines = markup_stream_pygments(request, cfg, blame_source, fp,
path[-1], mime_type, encoding)
fp.close() fp.close()
# Do we have a differing number of file content lines and data = common_template_data(request, revision)
# annotation items? That's no good. Call it an error and don't
# bother attempting the annotation display.
if blame_data and (len(file_lines) != len(blame_data)):
annotation = 'error'
blame_data = None
# Try to markup the file contents/annotation. If we get an error
# and we were colorizing the stream, try once more without the
# colorization enabled.
colorize = cfg.options.enable_syntax_coloration
try:
lines = markup_stream(request, cfg, blame_data, file_lines,
path[-1], mime_type, encoding, colorize)
except:
if colorize:
lines = markup_stream(request, cfg, blame_data, file_lines,
path[-1], mime_type, encoding, False)
else:
raise debug.ViewVCException('Error displaying file contents',
'500 Internal Server Error')
data = common_template_data(request, revision, mime_type)
data.merge(ezt.TemplateData({ data.merge(ezt.TemplateData({
'mime_type' : mime_type, 'mime_type' : mime_type,
'log' : None, 'log' : None,
@@ -1929,12 +1803,10 @@ def markup_or_annotate(request, is_annotate):
revs = request.repos.itemlog(path, revision, vclib.SORTBY_REV, revs = request.repos.itemlog(path, revision, vclib.SORTBY_REV,
0, 1, options) 0, 1, options)
entry = revs[-1] entry = revs[-1]
lf = LogFormatter(request, entry.log)
data['date'] = make_time_string(entry.date, cfg) data['date'] = make_time_string(entry.date, cfg)
data['author'] = entry.author data['author'] = entry.author
data['changed'] = entry.changed data['changed'] = entry.changed
data['log'] = lf.get(maxlen=0, htmlize=1) data['log'] = format_log(request, entry.log)
data['size'] = entry.size data['size'] = entry.size
if entry.date is not None: if entry.date is not None:
@@ -2050,27 +1922,20 @@ def view_roots(request):
rootnames = allroots.keys() rootnames = allroots.keys()
rootnames.sort(icmp) rootnames.sort(icmp)
for rootname in rootnames: for rootname in rootnames:
root_path, root_type, lastmod = allroots[rootname]
href = request.get_url(view_func=view_directory, href = request.get_url(view_func=view_directory,
where='', pathtype=vclib.DIR, where='', pathtype=vclib.DIR,
params={'root': rootname}, escape=1) params={'root': rootname}, escape=1)
if root_type == vclib.SVN: lastmod = allroots[rootname][2]
log_href = request.get_url(view_func=view_log,
where='', pathtype=vclib.DIR,
params={'root': rootname}, escape=1)
else:
log_href = None
roots.append(_item(name=request.server.escape(rootname), roots.append(_item(name=request.server.escape(rootname),
type=root_type, type=allroots[rootname][1],
path=root_path, path=allroots[rootname][0],
author=lastmod and lastmod.author or None, author=lastmod and lastmod.author or None,
ago=lastmod and lastmod.ago or None, ago=lastmod and lastmod.ago or None,
date=lastmod and lastmod.date or None, date=lastmod and lastmod.date or None,
log=lastmod and lastmod.log or None, log=lastmod and lastmod.log or None,
short_log=lastmod and lastmod.short_log or None, short_log=lastmod and lastmod.short_log or None,
rev=lastmod and lastmod.rev or None, rev=lastmod and lastmod.rev or None,
href=href, href=href))
log_href=log_href))
data = common_template_data(request) data = common_template_data(request)
data.merge(ezt.TemplateData({ data.merge(ezt.TemplateData({
@@ -2170,11 +2035,9 @@ def view_directory(request):
row.date = make_time_string(file.date, cfg) row.date = make_time_string(file.date, cfg)
row.ago = html_time(request, file.date) row.ago = html_time(request, file.date)
if cfg.options.show_logs: if cfg.options.show_logs:
debug.t_start("dirview_logformat") row.log = format_log(request, file.log)
lf = LogFormatter(request, file.log) row.short_log = format_log(request, file.log,
row.log = lf.get(maxlen=0, htmlize=1) maxlen=cfg.options.short_log_len)
row.short_log = lf.get(maxlen=cfg.options.short_log_len, htmlize=1)
debug.t_end("dirview_logformat")
row.lockinfo = file.lockinfo row.lockinfo = file.lockinfo
row.anchor = request.server.escape(file.name) row.anchor = request.server.escape(file.name)
row.name = request.server.escape(file.name) row.name = request.server.escape(file.name)
@@ -2551,6 +2414,7 @@ def view_log(request):
entry.ago = None entry.ago = None
if rev.date is not None: if rev.date is not None:
entry.ago = html_time(request, rev.date, 1) entry.ago = html_time(request, rev.date, 1)
entry.log = format_log(request, rev.log or '')
entry.size = rev.size entry.size = rev.size
entry.lockinfo = rev.lockinfo entry.lockinfo = rev.lockinfo
entry.branch_point = None entry.branch_point = None
@@ -2558,9 +2422,6 @@ def view_log(request):
entry.orig_path = None entry.orig_path = None
entry.copy_path = None entry.copy_path = None
lf = LogFormatter(request, rev.log or '')
entry.log = lf.get(maxlen=0, htmlize=1)
entry.view_href = None entry.view_href = None
entry.download_href = None entry.download_href = None
entry.download_text_href = None entry.download_text_href = None
@@ -3125,8 +2986,7 @@ class DiffSource:
class DiffSequencingError(Exception): class DiffSequencingError(Exception):
pass pass
def diff_parse_headers(fp, diff_type, path1, path2, rev1, rev2, def diff_parse_headers(fp, diff_type, rev1, rev2, sym1=None, sym2=None):
sym1=None, sym2=None):
date1 = date2 = log_rev1 = log_rev2 = flag = None date1 = date2 = log_rev1 = log_rev2 = flag = None
header_lines = [] header_lines = []
@@ -3155,15 +3015,15 @@ def diff_parse_headers(fp, diff_type, path1, path2, rev1, rev2,
if match: if match:
date1 = match.group(1) date1 = match.group(1)
log_rev1 = match.group(2) log_rev1 = match.group(2)
line = '%s%s\t%s\t%s%s\n' % (f1, path1, date1, log_rev1, if sym1:
sym1 and ' ' + sym1 or '') line = line[:-1] + ' %s\n' % sym1
elif line[:len(f2)] == f2: elif line[:len(f2)] == f2:
match = _re_extract_rev.match(line) match = _re_extract_rev.match(line)
if match: if match:
date2 = match.group(1) date2 = match.group(1)
log_rev2 = match.group(2) log_rev2 = match.group(2)
line = '%s%s\t%s\t%s%s\n' % (f2, path2, date2, log_rev2, if sym2:
sym2 and ' ' + sym2 or '') line = line[:-1] + ' %s\n' % sym2
parsing = 0 parsing = 0
elif line[:3] == 'Bin': elif line[:3] == 'Bin':
flag = _RCSDIFF_IS_BINARY flag = _RCSDIFF_IS_BINARY
@@ -3291,11 +3151,8 @@ def view_patch(request):
raise debug.ViewVCException('Invalid path(s) or revision(s) passed ' raise debug.ViewVCException('Invalid path(s) or revision(s) passed '
'to diff', '400 Bad Request') 'to diff', '400 Bad Request')
path_left = _path_join(p1) date1, date2, flag, headers = diff_parse_headers(fp, diff_type, rev1, rev2,
path_right = _path_join(p2) sym1, sym2)
date1, date2, flag, headers = diff_parse_headers(fp, diff_type,
path_left, path_right,
rev1, rev2, sym1, sym2)
server_fp = get_writeready_server_file(request, 'text/plain') server_fp = get_writeready_server_file(request, 'text/plain')
server_fp.write(headers) server_fp.write(headers)
@@ -3391,7 +3248,6 @@ def view_diff(request):
changes = [] changes = []
if fp: if fp:
date1, date2, flag, headers = diff_parse_headers(fp, diff_type, date1, date2, flag, headers = diff_parse_headers(fp, diff_type,
path_left, path_right,
rev1, rev2, sym1, sym2) rev1, rev2, sym1, sym2)
if human_readable: if human_readable:
if flag is not None: if flag is not None:
@@ -3409,8 +3265,7 @@ def view_diff(request):
fvi = get_file_view_info(request, path_left, rev1) fvi = get_file_view_info(request, path_left, rev1)
left = _item(date=make_time_string(log_entry1.date, cfg), left = _item(date=make_time_string(log_entry1.date, cfg),
author=log_entry1.author, author=log_entry1.author,
log=LogFormatter(request, log=format_log(request, log_entry1.log),
log_entry1.log).get(maxlen=0, htmlize=1),
size=log_entry1.size, size=log_entry1.size,
ago=ago1, ago=ago1,
path=path_left, path=path_left,
@@ -3426,8 +3281,7 @@ def view_diff(request):
fvi = get_file_view_info(request, path_right, rev2) fvi = get_file_view_info(request, path_right, rev2)
right = _item(date=make_time_string(log_entry2.date, cfg), right = _item(date=make_time_string(log_entry2.date, cfg),
author=log_entry2.author, author=log_entry2.author,
log=LogFormatter(request, log=format_log(request, log_entry2.log),
log_entry2.log).get(maxlen=0, htmlize=1),
size=log_entry2.size, size=log_entry2.size,
ago=ago2, ago=ago2,
path=path_right, path=path_right,
@@ -3675,8 +3529,7 @@ def view_revision(request):
propnames.sort() propnames.sort()
props = [] props = []
for name in propnames: for name in propnames:
lf = LogFormatter(request, revprops[name]) value = format_log(request, revprops[name])
value = lf.get(maxlen=0, htmlize=1)
undisplayable = ezt.boolean(0) undisplayable = ezt.boolean(0)
# skip non-utf8 property names # skip non-utf8 property names
try: try:
@@ -3799,14 +3652,13 @@ def view_revision(request):
escape=1) escape=1)
jump_rev_action, jump_rev_hidden_values = \ jump_rev_action, jump_rev_hidden_values = \
request.get_form(params={'revision': None}) request.get_form(params={'revision': None})
lf = LogFormatter(request, msg)
data = common_template_data(request) data = common_template_data(request)
data.merge(ezt.TemplateData({ data.merge(ezt.TemplateData({
'rev' : str(rev), 'rev' : str(rev),
'author' : author, 'author' : author,
'date' : date_str, 'date' : date_str,
'log' : lf.get(maxlen=0, htmlize=1), 'log' : format_log(request, msg),
'properties' : props, 'properties' : props,
'ago' : date is not None and html_time(request, date, 1) or None, 'ago' : date is not None and html_time(request, date, 1) or None,
'changes' : changes, 'changes' : changes,
@@ -4164,10 +4016,9 @@ def build_commit(request, files, max_files, dir_strip, format):
commit.log = None commit.log = None
commit.short_log = None commit.short_log = None
else: else:
lf = LogFormatter(request, desc) commit.log = format_log(request, desc, 0, format != 'rss')
htmlize = (format != 'rss') commit.short_log = format_log(request, desc, cfg.options.short_log_len,
commit.log = lf.get(maxlen=0, htmlize=htmlize) format != 'rss')
commit.short_log = lf.get(maxlen=cfg.options.short_log_len, htmlize=htmlize)
commit.author = request.server.escape(author) commit.author = request.server.escape(author)
commit.rss_date = make_rss_time_string(date, request.cfg) commit.rss_date = make_rss_time_string(date, request.cfg)
if request.roottype == 'svn': if request.roottype == 'svn':
@@ -4461,9 +4312,8 @@ def list_roots(request):
date, author, msg, revprops, changes = repos.revinfo(youngest_rev) date, author, msg, revprops, changes = repos.revinfo(youngest_rev)
date_str = make_time_string(date, cfg) date_str = make_time_string(date, cfg)
ago = html_time(request, date) ago = html_time(request, date)
lf = LogFormatter(request, msg) log = format_log(request, msg)
log = lf.get(maxlen=0, htmlize=1) short_log = format_log(request, msg, maxlen=cfg.options.short_log_len)
short_log = lf.get(maxlen=cfg.options.short_log_len, htmlize=1)
lastmod = _item(ago=ago, author=author, date=date_str, log=log, lastmod = _item(ago=ago, author=author, date=date_str, log=log,
short_log=short_log, rev=str(youngest_rev)) short_log=short_log, rev=str(youngest_rev))
except: except:
@@ -4555,35 +4405,14 @@ def locate_root(cfg, rootname):
return None, None return None, None
def load_config(pathname=None, server=None): def load_config(pathname=None, server=None):
"""Load the ViewVC configuration file. SERVER is the server object
that will be using this configuration. Consult the environment for
the variable VIEWVC_CONF_PATHNAME and VIEWCVS_CONF_PATHNAME (its
legacy name) and, if set, use its value as the path of the
configuration file; otherwise, use PATHNAME (if provided). Failing
all else, use a hardcoded default configuration path."""
debug.t_start('load-config') debug.t_start('load-config')
# See if the environment contains overrides to the configuration if pathname is None:
# path. If we have a SERVER object, consult its environment; use pathname = (os.environ.get("VIEWVC_CONF_PATHNAME")
# the OS environment otherwise. or os.environ.get("VIEWCVS_CONF_PATHNAME")
env_pathname = None or os.path.join(os.path.dirname(os.path.dirname(__file__)),
if server is not None: "viewvc.conf"))
env_pathname = (server.getenv("VIEWVC_CONF_PATHNAME")
or server.getenv("VIEWCVS_CONF_PATHNAME"))
else:
env_pathname = (os.environ.get("VIEWVC_CONF_PATHNAME")
or os.environ.get("VIEWCVS_CONF_PATHNAME"))
# Try to find the configuration pathname by searching these ordered
# locations: the environment, the passed-in PATHNAME, the hard-coded
# default.
pathname = (env_pathname
or pathname
or os.path.join(os.path.dirname(os.path.dirname(__file__)),
"viewvc.conf"))
# Load the configuration!
cfg = config.Config() cfg = config.Config()
cfg.set_defaults() cfg.set_defaults()
cfg.load_config(pathname, server and server.getenv("HTTP_HOST")) cfg.load_config(pathname, server and server.getenv("HTTP_HOST"))

View File

@@ -1,6 +1,6 @@
# -*-python-*- # -*-python-*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2007 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # 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 ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -92,15 +92,13 @@ numbers, and not literal):
Also, drop a copy of the archive files into the root directory of Also, drop a copy of the archive files into the root directory of
the viewvc.org website (unversioned). the viewvc.org website (unversioned).
15. Update the Tigris.org website (^/trunk/www/index.html) to refer to 15. On trunk, update the websites (both the viewvc.org/ and www/ ones)
the new release files and commit. to refer to the new release files, and copy the CHANGES for the
new release into trunk's CHANGES file.
svn ci -m "Bump latest advertised release." 16. Edit the file 'lib/viewvc.py' again, incrementing the patch number
assigned to the __version__ variable. Add a new empty block in
16. Back on the release branch, edit the file 'lib/viewvc.py' again, the branch's CHANGES file. Commit your changes:
incrementing the patch number assigned to the __version__
variable. Add a new empty block in the branch's CHANGES file.
Commit your changes:
svn ci -m "Begin a new release cycle." svn ci -m "Begin a new release cycle."
@@ -115,6 +113,3 @@ numbers, and not literal):
18. Send to the announce@ list a message explaining all the cool new 18. Send to the announce@ list a message explaining all the cool new
features, and post similar announcements to other places interested features, and post similar announcements to other places interested
in this sort of stuff, such as Freshmeat (http://www.freshmeat.net). in this sort of stuff, such as Freshmeat (http://www.freshmeat.net).
19. Merge CHANGES for this release into the CHANGES file for newer
release lines and commit.

View File

@@ -27,7 +27,7 @@
[roots.name]</a> [roots.name]</a>
</td> </td>
[is cfg.options.show_roots_lastmod "1"] [is cfg.options.show_roots_lastmod "1"]
<td style="width:20">&nbsp;[if-any roots.log_href]<a href="[roots.log_href]">[roots.rev]</a>[else][roots.rev][end]</td> <td style="width:20">&nbsp;[roots.rev]</td>
<td style="width:20">&nbsp;[roots.ago]</td> <td style="width:20">&nbsp;[roots.ago]</td>
<td style="width:20">&nbsp;[roots.author]</td> <td style="width:20">&nbsp;[roots.author]</td>
<td style="width:20">&nbsp;[roots.short_log]</td> <td style="width:20">&nbsp;[roots.short_log]</td>

View File

@@ -1,6 +1,6 @@
#!/bin/sh #!/bin/sh
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2007 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # 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 ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,5 +1,5 @@
/* /*
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # 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 ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,5 +1,5 @@
/* /*
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2007 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # 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 ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,5 +1,5 @@
/* /*
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # 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 ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,5 +1,5 @@
/* /*
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # 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 ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- Mode: python -*- # -*- Mode: python -*-
# #
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved. # Copyright (C) 1999-2009 The ViewCVS Group. All Rights Reserved.
# #
# By using this file, you agree to the terms and conditions set forth in # 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 ViewVC # the LICENSE.html file which can be found at the top level of the ViewVC
@@ -209,8 +209,6 @@ def install_file(src_path, dst_path, mode, subst_path_vars,
temp = raw_input("Do you want to [O]verwrite, [D]o " temp = raw_input("Do you want to [O]verwrite, [D]o "
"not overwrite, or [V]iew " "not overwrite, or [V]iew "
"differences? ") "differences? ")
if len(temp) == 0:
continue
temp = string.lower(temp[0]) temp = string.lower(temp[0])
if temp == "v" and ext not in BINARY_FILE_EXTS: if temp == "v" and ext not in BINARY_FILE_EXTS:
print """ print """